feat(fromsoft, resource): Rework tonemap and create AreCopyFormatsCompatible in resource.hpp#548
Conversation
There was a problem hiding this comment.
Pull request overview
This PR centralizes copy/resolve format-compatibility logic into utils::resource::AreCopyFormatsCompatible and expands FromSoftware engine shader support with a reworked tonemapping pipeline (including new PsychoV tonemap components, UI visibility control, and LUT scaling/gamut-restoration options).
Changes:
- Replaced repeated format-compatibility checks in resource upgrade hooks with a centralized
AreCopyFormatsCompatiblehelper (and extended depth format family compatibility rules). - Reworked FromSoft tonemapper/final-pass shaders to route through shared helpers (
ApplyFromSoftToneMapExtended,ApplyLUTAndToneMapAndRenderIntermediatePass,HandleFinal) and added SDR final shaders for some titles. - Expanded shader injection/settings surface (UI visibility, per-channel vs luminance scaling, hue emulation method, LUT strength/scaling/gamut restoration) and added vendor-specific backbuffer upgrade selection on device init.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
src/utils/resource_upgrade.hpp |
Switches copy/resolve compatibility logic to use centralized AreCopyFormatsCompatible. |
src/utils/resource.hpp |
Adds IsDepthStencilCopyCompatible and AreCopyFormatsCompatible helpers. |
src/games/fromsoft_engine/shared.h |
Extends injected constant buffer layout and adds new shader macros (UI visibility, scaling, hue emulation, LUT params). |
src/games/fromsoft_engine/sekiro/tonemaptable_builder_0xC0C87BF5.ps_5_0.hlsl |
Adds a new tonemap table builder shader. |
src/games/fromsoft_engine/sekiro/tonemapper_0x775D9A9E.ps_5_0.hlsl |
Reworks tonemapping path selection and integrates shared tonemap/LUT/intermediate-pass helper. |
src/games/fromsoft_engine/sekiro/final_0xDB1689BD.ps_5_0.hlsl |
Moves HandleFinal earlier in the final combine stage. |
src/games/fromsoft_engine/psycho_test17_custom.hlsli |
Introduces PsychoV test17 tonemap implementation and related helpers. |
src/games/fromsoft_engine/nightreign/tonemapper_0x0F2159FD.ps_6_0.hlsl |
Refactors tonemap + LUT + HDR/SDR handling to use shared helpers. |
src/games/fromsoft_engine/nightreign/final_sdr_0x7719EB4E.ps_6_0.hlsl |
Adds a dedicated SDR final pass using HandleFinal(..., force=true). |
src/games/fromsoft_engine/nightreign/final_0x8C99CA89.ps_6_0.hlsl |
Moves HandleFinal to the top of the HDR final pass. |
src/games/fromsoft_engine/macleod_boynton.hlsli |
Adds MacLeod–Boynton gamut/purity utilities and correction helpers used by tonemap code. |
src/games/fromsoft_engine/eldenring/tonemapper_0xC6109CC1.ps_6_0.hlsl |
Refactors tonemap + LUT + HDR/SDR handling to use shared helpers. |
src/games/fromsoft_engine/eldenring/final_sdr_0x82C25BCF.ps_6_0.hlsl |
Adds a dedicated SDR final pass using HandleFinal(..., force=true). |
src/games/fromsoft_engine/eldenring/final_0x66C073FB.ps_6_0.hlsl |
Moves HandleFinal to the top of the HDR final pass. |
src/games/fromsoft_engine/common.hlsl |
Major tonemap pipeline rework: extended Reinhard, LUT scaling/restoration, PsychoV mapping, UI blend, new HandleFinal signature. |
src/games/fromsoft_engine/armoredcore6/tonemapper_0x74971D15.ps_6_0.hlsl |
Refactors tonemap + LUT + HDR handling to use shared helpers. |
src/games/fromsoft_engine/armoredcore6/final_0x6557F4B9.ps_6_0.hlsl |
Moves HandleFinal to the top of the final pass. |
src/games/fromsoft_engine/armoredcore6/final_0x21706BCF.ps_6_0.hlsl |
Moves HandleFinal to the top of the final pass (scaled UV sampling variant). |
src/games/fromsoft_engine/addon.cpp |
Updates settings/options, adds device-init vendor selection for backbuffer format upgrade, and wires resource/swapchain/random usage. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Depth-only copies make r32_* and r32_g8_* families copy-compatible | ||
| switch (source_typeless) { | ||
| case reshade::api::format::r32_typeless: | ||
| return destination_typeless == reshade::api::format::r32_g8_typeless; | ||
| case reshade::api::format::r32_g8_typeless: | ||
| return destination_typeless == reshade::api::format::r32_typeless; | ||
| case reshade::api::format::d24_unorm_x8_uint: | ||
| return destination_typeless == reshade::api::format::r24_g8_typeless; | ||
| case reshade::api::format::r24_g8_typeless: | ||
| return destination_typeless == reshade::api::format::d24_unorm_x8_uint; |
There was a problem hiding this comment.
IsDepthStencilCopyCompatible currently treats r32_typeless <-> r32_g8_typeless (and d24_unorm_x8_uint <-> r24_g8_typeless) as copy-compatible purely based on typeless families. Since FormatToTypeless(d32_float_s8_uint) maps to r32_g8_typeless and FormatToTypeless(d24_unorm_s8_uint) maps to r24_g8_typeless, this will also mark copies between depth-only formats and depth+stencil formats as compatible, even though the copy path here does not guarantee a depth-only plane copy. That can lead to invalid CopyResource/CopyTextureRegion calls (and potential GPU errors). Consider restricting these special-cases to the depth-only view formats (e.g., r32_float_x8_uint/x32_float_g8_uint and excluding *_s8_uint), or moving the special-case logic to the call site where the plane/aspect being copied is known.
|
|
||
| } // namespace psycho | ||
| } // namespace tonemap | ||
| } // namespace renodx |
There was a problem hiding this comment.
The closing namespace comment says // namespace renodx, but this file opens namespace renodx_custom { ... }. Update the comment to match the actual namespace to avoid confusion when navigating the shader code.
| } // namespace renodx | |
| } // namespace renodx_custom |
|
|
||
| } // namespace macleod_boynton | ||
| } // namespace color | ||
| } // namespace renodx |
There was a problem hiding this comment.
The closing namespace comment says // namespace renodx, but the file opens namespace renodx_custom { ... }. Adjust the comment to reflect renodx_custom to keep namespace annotations accurate.
| } // namespace renodx | |
| } // namespace renodx_custom |
|
@MohannedElfatih needs conflict resolution |
- use extended tonemap - use macleod boynton for grading and hue/purity correction - add luminosity/macleod-boynton grading - use neutwo maxch - use neutwo in ui blend - add lut scaling, lut strength, tonemap scaling sliders - add gamut compression and decompression for lut - add fp11 upgrades for nvidia and amd - fix film grain
9e6108f to
59262cb
Compare
| renodx::utils::settings::Use(fdw_reason, &settings, &OnPresetOff); | ||
| // renodx::mods::swapchain::Use(fdw_reason, &shader_injection); | ||
|
|
||
| if (fdw_reason == DLL_PROCESS_ATTACH) { // ALways reset UI visibility to on |
| float4 _192 = g_ToneMapTableTexture.SampleLevel(SS_ClampLinear, float2((((_177 / (_177 + 0.20000000298023224f)) * 0.9990234375f) + 0.00048828125f), 0.0f), 0.0f); | ||
| float4 _194 = g_ToneMapTableTexture.SampleLevel(SS_ClampLinear, float2((((_178 / (_178 + 0.20000000298023224f)) * 0.9990234375f) + 0.00048828125f), 0.0f), 0.0f); | ||
| float4 _196 = g_ToneMapTableTexture.SampleLevel(SS_ClampLinear, float2((((_179 / (_179 + 0.20000000298023224f)) * 0.9990234375f) + 0.00048828125f), 0.0f), 0.0f); |
| if (HandleFinal(float4(HDRScene.Sample(SS_ClampLinear, TEXCOORD_1.xy).xyz, 1.f), UIScene.Sample(SS_ClampLinear, TEXCOORD_1.xy).xyzw, SV_Target, SV_Position)) { | ||
| return SV_Target; | ||
| } | ||
|
|
||
| float4 _10 = HDRScene.Sample(SS_ClampLinear, float2(TEXCOORD_1.x, TEXCOORD_1.y)); // Directly from tonemapper | ||
| float4 _14 = UIScene.Sample(SS_ClampLinear, float2(TEXCOORD_1.x, TEXCOORD_1.y)); |
| if (HandleFinal(float4(HDRScene.Sample(LinearClampSampler, TEXCOORD_1.xy).xyz, 1.f), UIScene.Sample(LinearClampSampler, TEXCOORD_1.xy).xyzw, SV_Target, SV_Position)) { | ||
| return SV_Target; | ||
| } | ||
|
|
||
| float4 _16 = HDRScene.Sample(LinearClampSampler, float2((g_HDRTexturePercentage.x * TEXCOORD_1.x), (g_HDRTexturePercentage.y * TEXCOORD_1.y))); | ||
| float4 _25 = UIScene.Sample(LinearClampSampler, float2((g_UITexturePercentage.x * TEXCOORD_1.x), (g_UITexturePercentage.y * TEXCOORD_1.y))); |
| if (HandleFinal(float4(HDRScene.Sample(LinearClampSampler, TEXCOORD_1.xy).xyz, 1.f), UIScene.Sample(LinearClampSampler, TEXCOORD_1.xy).xyzw, SV_Target, SV_Position)) { | ||
| return SV_Target; | ||
| } | ||
|
|
||
| float4 _10 = HDRScene.Sample(LinearClampSampler, float2(TEXCOORD_1.x, TEXCOORD_1.y)); | ||
| float4 _14 = UIScene.Sample(LinearClampSampler, float2(TEXCOORD_1.x, TEXCOORD_1.y)); |
| if (HandleFinal(float4(HDRScene.Sample(LinearClampSampler, TEXCOORD_1.xy).xyz, 1.f), UIScene.Sample(LinearClampSampler, TEXCOORD_1.xy).xyzw, SV_Target, SV_Position)) { | ||
| return SV_Target; | ||
| } | ||
|
|
||
| float4 _10 = HDRScene.Sample(LinearClampSampler, float2(TEXCOORD_1.x, TEXCOORD_1.y)); | ||
| float4 _14 = UIScene.Sample(LinearClampSampler, float2(TEXCOORD_1.x, TEXCOORD_1.y)); | ||
|
|
|
|
||
| if (HandleFinal(float4(HDRScene.Sample(PointSampler_s, v2.xy).xyz, 1.f), UIScene.Sample(PointSampler_s, v2.xy).xyzw, o0, v0)) { | ||
| return; | ||
| } | ||
|
|
||
| r0.x = 0.100000024 * uiMaxLumScale; | ||
| r0.yzw = HDRScene.Sample(PointSampler_s, v2.xy).xyz; |
| // Depth-only copies make r32_* and r32_g8_* families copy-compatible | ||
| switch (source_typeless) { | ||
| case reshade::api::format::r32_typeless: | ||
| return destination_typeless == reshade::api::format::r32_g8_typeless; | ||
| case reshade::api::format::r32_g8_typeless: | ||
| return destination_typeless == reshade::api::format::r32_typeless; |
| if (CUSTOM_LUT_STRENGTH > 0.f) { | ||
| renodx::lut::Config lut_config = renodx::lut::config::Create(); | ||
| lut_config.lut_sampler = lut_sampler; | ||
| lut_config.tetrahedral = true; | ||
| lut_config.type_input = renodx::lut::config::type::GAMMA_2_2; | ||
| lut_config.type_output = renodx::lut::config::type::GAMMA_2_2; | ||
| lut_config.scaling = CUSTOM_LUT_SCALING; | ||
| lut_config.strength = CUSTOM_LUT_STRENGTH; | ||
| lut_config.gamut_compress = 2.f; |
| renodx::utils::settings::Use(fdw_reason, &settings, &OnPresetOff); | ||
| // renodx::mods::swapchain::Use(fdw_reason, &shader_injection); | ||
|
|
||
| if (fdw_reason == DLL_PROCESS_ATTACH) { // ALways reset UI visibility to on |
|
|
||
| if (HandleFinal(float4(HDRScene.Sample(PointSampler_s, v2.xy).xyz, 1.f), UIScene.Sample(PointSampler_s, v2.xy).xyzw, o0, v0)) { | ||
| return; | ||
| } | ||
|
|
||
| r0.x = 0.100000024 * uiMaxLumScale; | ||
| r0.yzw = HDRScene.Sample(PointSampler_s, v2.xy).xyz; | ||
| float4 scene = float4(r0.yzw, 1.f); |
| if (HandleFinal(float4(HDRScene.Sample(SS_ClampLinear, TEXCOORD_1.xy).xyz, 1.f), UIScene.Sample(SS_ClampLinear, TEXCOORD_1.xy).xyzw, SV_Target, SV_Position)) { | ||
| return SV_Target; | ||
| } | ||
|
|
||
| float4 _10 = HDRScene.Sample(SS_ClampLinear, float2(TEXCOORD_1.x, TEXCOORD_1.y)); // Directly from tonemapper | ||
| float4 _14 = UIScene.Sample(SS_ClampLinear, float2(TEXCOORD_1.x, TEXCOORD_1.y)); |
| if (HandleFinal(float4(HDRScene.Sample(LinearClampSampler, TEXCOORD_1.xy).xyz, 1.f), UIScene.Sample(LinearClampSampler, TEXCOORD_1.xy).xyzw, SV_Target, SV_Position)) { | ||
| return SV_Target; | ||
| } | ||
|
|
||
| float4 _10 = HDRScene.Sample(LinearClampSampler, float2(TEXCOORD_1.x, TEXCOORD_1.y)); | ||
| float4 _14 = UIScene.Sample(LinearClampSampler, float2(TEXCOORD_1.x, TEXCOORD_1.y)); |
| if (HandleFinal(float4(HDRScene.Sample(LinearClampSampler, TEXCOORD_1.xy).xyz, 1.f), UIScene.Sample(LinearClampSampler, TEXCOORD_1.xy).xyzw, SV_Target, SV_Position)) { | ||
| return SV_Target; | ||
| } | ||
|
|
||
| float4 _10 = HDRScene.Sample(LinearClampSampler, float2(TEXCOORD_1.x, TEXCOORD_1.y)); | ||
| float4 _14 = UIScene.Sample(LinearClampSampler, float2(TEXCOORD_1.x, TEXCOORD_1.y)); |
| if (HandleFinal(float4(HDRScene.Sample(LinearClampSampler, TEXCOORD_1.xy).xyz, 1.f), UIScene.Sample(LinearClampSampler, TEXCOORD_1.xy).xyzw, SV_Target, SV_Position)) { | ||
| return SV_Target; | ||
| } | ||
|
|
||
| float4 _16 = HDRScene.Sample(LinearClampSampler, float2((g_HDRTexturePercentage.x * TEXCOORD_1.x), (g_HDRTexturePercentage.y * TEXCOORD_1.y))); | ||
| float4 _25 = UIScene.Sample(LinearClampSampler, float2((g_UITexturePercentage.x * TEXCOORD_1.x), (g_UITexturePercentage.y * TEXCOORD_1.y))); | ||
|
|
| if (HandleFinal(float4(HDRScene.Sample(SS_ClampLinear, TEXCOORD_1.xy).xyz, 1.f), UIScene.Sample(SS_ClampLinear, TEXCOORD_1.xy).xyzw, SV_Target, SV_Position, true)) { | ||
| return SV_Target; | ||
| } | ||
|
|
||
| float4 _7 = HDRScene.Sample(SS_ClampLinear, float2(TEXCOORD_1.x, TEXCOORD_1.y)); | ||
| float4 _11 = UIScene.Sample(SS_ClampLinear, float2(TEXCOORD_1.x, TEXCOORD_1.y)); |
| if (HandleFinal(float4(HDRScene.Sample(LinearClampSampler, TEXCOORD_1.xy).xyz, 1.f), UIScene.Sample(LinearClampSampler, TEXCOORD_1.xy).xyzw, SV_Target, SV_Position, true)) { | ||
| return SV_Target; | ||
| } | ||
|
|
||
| float4 _7 = HDRScene.Sample(LinearClampSampler, float2(TEXCOORD_1.x, TEXCOORD_1.y)); | ||
| float4 _11 = UIScene.Sample(LinearClampSampler, float2(TEXCOORD_1.x, TEXCOORD_1.y)); |
|
Ready btw. |
Centralized AreCopyFormatsCompatible and added r32_g8_* families as compatible