From a27836d77d3370c2ea3aa46f5d805b383e0e0ee9 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Sun, 3 May 2026 00:51:57 -0700 Subject: [PATCH] fix(deferred): restore UAV bind on vanilla normals targets PR #2178 removed the CreateRenderTarget_Normals and CreateRenderTarget_NormalsSwap hooks because the PS-variant deferred composite path from PR #2150 didn't need UAV access on the vanilla kNORMAL_TAAMASK_SSRMASK target. PR #2150 was later reverted, restoring the CS path, but the hook removal stayed. DeferredCompositeCS still binds normals.UAV as UAV1 and writes EncodeNormalVanilla(normalVS) through it so the post-pass vanilla SSAO chain (ISSAORawAO -> ISSAOComposite) sees current-frame normals. Without the hooks, the target's UAV is null, the CS write is silently dropped, and vanilla SSAO samples uninitialized data -- producing hard wedge-shaped shadow artifacts visible to users without Screen Space GI installed (SSGI was masking the issue by force-disabling vanilla SSAO every frame). Restoring the two hooks brings the vanilla SSAO chain back in line with v1.4.11 behavior. The other parts of #2178 already went away with the #2150 revert, so this is the only piece left to undo. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/Hooks.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/Hooks.cpp b/src/Hooks.cpp index eef88299cb..767ab92288 100644 --- a/src/Hooks.cpp +++ b/src/Hooks.cpp @@ -586,6 +586,31 @@ namespace Hooks static inline REL::Relocation func; }; + // kNORMAL_TAAMASK_SSRMASK and its swap need UAV bind because DeferredCompositeCS + // writes vanilla-encoded normals through UAV1 (`normals.UAV` in Deferred::DeferredPasses), + // which feeds the post-pass vanilla SSAO chain (ISSAORawAO -> ISSAOComposite). Without + // these hooks the UAV is null, the CS write is silently dropped, and vanilla SSAO reads + // uninitialized data and produces hard wedge-shaped shadow artifacts. + struct CreateRenderTarget_Normals + { + static void thunk(RE::BSGraphics::Renderer* This, RE::RENDER_TARGETS::RENDER_TARGET a_target, RE::BSGraphics::RenderTargetProperties* a_properties) + { + globals::state->ModifyRenderTarget(a_target, a_properties); + func(This, a_target, a_properties); + } + static inline REL::Relocation func; + }; + + struct CreateRenderTarget_NormalsSwap + { + static void thunk(RE::BSGraphics::Renderer* This, RE::RENDER_TARGETS::RENDER_TARGET a_target, RE::BSGraphics::RenderTargetProperties* a_properties) + { + globals::state->ModifyRenderTarget(a_target, a_properties); + func(This, a_target, a_properties); + } + static inline REL::Relocation func; + }; + struct CreateRenderTarget_MotionVectors { static void thunk(RE::BSGraphics::Renderer* This, RE::RENDER_TARGETS::RENDER_TARGET a_target, RE::BSGraphics::RenderTargetProperties* a_properties) @@ -952,6 +977,8 @@ namespace Hooks logger::info("Hooking BSShaderRenderTargets::Create::CreateRenderTarget(s)"); stl::write_thunk_call(REL::RelocationID(100458, 107175).address() + REL::Relocate(0x3F0, 0x3F3, 0x548)); + stl::write_thunk_call(REL::RelocationID(100458, 107175).address() + REL::Relocate(0x458, 0x45B, 0x5B0)); + stl::write_thunk_call(REL::RelocationID(100458, 107175).address() + REL::Relocate(0x46B, 0x46E, 0x5C3)); stl::write_thunk_call(REL::RelocationID(100458, 107175).address() + REL::Relocate(0x4F0, 0x4EF, 0x64E)); stl::write_thunk_call(REL::RelocationID(100458, 107175).address() + REL::Relocate(0x503, 0x502, 0x661));