feat(VR): add DLSS Enhancer with DLSSperf and subrect blend#2096
feat(VR): add DLSS Enhancer with DLSSperf and subrect blend#2096YtzyFvra wants to merge 4 commits into
Conversation
📝 WalkthroughWalkthroughIntroduces a comprehensive DLSS Enhancer VR feature system with companion DLSSperf performance optimization. Adds GPU compute shaders for temporal smoothing and subrect blending, a multi-module DLSS enhancement subsystem managing VR per-eye rendering via Default/Faster/Extreme modes, a DLSSperf render-target-size hook system for VR display/render resolution management, VR subrect UV controller utilities, and broad integration hooks into upscaling and rendering pipelines to enable dynamic DLSS routing and resource management. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
No actionable suggestions for changed features. |
There was a problem hiding this comment.
Pull request overview
Adds a new VR-only DLSS “enhancer” path to Community Shaders, introducing sub-rect DLSS rendering controls plus an optional DLSSperf mode that hooks VR render target allocation to reduce VRAM/bandwidth while preserving full display-resolution DLSS output.
Changes:
- Introduces
DlssEnhancerFeature(settings/UI/persistence) and aDlssEnhancerpipeline (modes/ops/params/bridge) to drive Streamline DLSS with subrect + blend. - Adds
DLSSperfsub-feature: hooks VR RT sizing, creates a private display-res output texture, and intercepts parts of post-processing to keep the pipeline functional at mixed resolutions. - Updates Upscaling/Streamline/RCAS integrations to support the enhancer route and new output dimension handling.
Reviewed changes
Copilot reviewed 35 out of 35 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/XSEPlugin.cpp | Increases trampoline allocation size for additional hooks/scanners. |
| src/State.h | Marks DLSSENHANCER as a “special feature”. |
| src/Hooks.cpp | Adds DLSSperf viewport correction hook + RT creation interception and boot sequence call. |
| src/Globals.h | Registers new global feature instances (DlssEnhancer, DLSSperf). |
| src/Globals.cpp | Adds a Draw hook to correct fade overlay viewport under DLSSperf. |
| src/Features/Upscaling/Streamline.h | Extends DLSS evaluation/options API to include output height. |
| src/Features/Upscaling/Streamline.cpp | Adds enhancer mvecScale handling, optional mask tagging, and enhancer-aware option routing. |
| src/Features/Upscaling/RCAS/RCAS.h | Adds sharpen overload that takes explicit width/height. |
| src/Features/Upscaling/RCAS/RCAS.cpp | Routes existing sharpen call through the new explicit-dimension overload. |
| src/Features/Upscaling.h | Adds “active DLSS settings” accessors for enhancer routing. |
| src/Features/Upscaling.cpp | Adds enhancer execution route + DLSSperf post handling and jitter adjustments. |
| src/Features/Subrect/Subrect.h | New Subrect controller API/types for UV/pixel regions and presets. |
| src/Features/Subrect/Subrect.cpp | Implements preset persistence + ImGui drag editor + UV→pixel conversion. |
| src/Features/DLSSperf.h | New DLSSperf feature interface and hook/intercept state. |
| src/Features/DLSSperf.cpp | Implements VR RT-size hook, post interception, downscale, and shader hook installs. |
| src/Features/DlssEnhancerFeature.h | New feature definition: settings, UI, latching, and capability checks. |
| src/Features/DlssEnhancerFeature.cpp | Implements settings UI, serialization, clamping, and cache clearing. |
| src/Features/DlssEnhancer/Preprocess.h | Declares enhancer preprocess stage for optional texture encoding. |
| src/Features/DlssEnhancer/Preprocess.cpp | Implements enhancer preprocess compute dispatch to generate DLSS hints/MV copies. |
| src/Features/DlssEnhancer/Postprocess.h | Declares enhancer DLSS postprocess stage (sharpening). |
| src/Features/DlssEnhancer/Postprocess.cpp | Implements enhancer-aware sharpening, including DLSSperf testTexture path. |
| src/Features/DlssEnhancer/Params.h | Declares unified VR DLSS parameter resolution for mode pipelines. |
| src/Features/DlssEnhancer/Params.cpp | Resolves render/display dimensions, textures, subrect UV, and jitter. |
| src/Features/DlssEnhancer/Ops.h | Declares reusable GPU ops for enhancer pipelines (stretch/temporal/blend/etc.). |
| src/Features/DlssEnhancer/Modes.cpp | Implements Default/Faster/Extreme mode pipelines and routing. |
| src/Features/DlssEnhancer/Core.h | Declares enhancer resource pool and execution entry points. |
| src/Features/DlssEnhancer/Core.cpp | Implements ops/resource management, temporal smoothing, and subrect blending dispatch. |
| src/Features/DlssEnhancer/Bridge.h | Declares bridge API used by Upscaling/Streamline/Hooks. |
| src/Features/DlssEnhancer/Bridge.cpp | Implements route decision, settings forwarding, and boot-time latch/hook installation. |
| src/Feature.cpp | Registers DlssEnhancerFeature in the global feature list. |
| features/DLSSperf/Shaders/DLSSperf/BilinearCopyPS.hlsl | Adds DLSSperf downscale shader used before post chain. |
| features/DLSS Enhancer/Shaders/Features/SubrectStretchCS.hlsl | Adds periphery stretch compute shader for subrect pipeline. |
| features/DLSS Enhancer/Shaders/Features/SubrectBlendCS.hlsl | Adds feather/dither blend compute shader for subrect compositing. |
| features/DLSS Enhancer/Shaders/Features/PeripheryTemporalSmoothCS.hlsl | Adds temporal smoothing compute shader for stretched periphery AA. |
| features/DLSS Enhancer/Shaders/Features/DLSSENHANCER.ini | Adds feature metadata/version ini. |
| auto& enhancer = globals::features::dlssEnhancer; | ||
| enhancer.LatchEnabled(); | ||
| enhancer.LatchQualityMode(); | ||
| if (!globals::features::dlssPerf.IsHookActive() && enhancer.settings.enablePerf) { |
There was a problem hiding this comment.
BootSequence() installs the DLSSperf render-target-size hook whenever settings.enablePerf is set, even if DlssEnhancer itself is disabled at boot. That means a config with enabled=0 but enablePerf=1 will still shrink engine RT allocations, potentially breaking rendering while the enhancer route is inactive. Gate DLSSperf installation on the enhancer being enabled-at-boot (and ideally runtime-supported) before calling LatchPerfEnabled() / InstallRenderTargetSizeHook().
| if (!globals::features::dlssPerf.IsHookActive() && enhancer.settings.enablePerf) { | |
| const bool routeActiveAtBoot = enhancer.IsActive(); | |
| if (routeActiveAtBoot && !globals::features::dlssPerf.IsHookActive() && enhancer.settings.enablePerf) { |
|
|
||
| uint64_t ComputeSubrectUVHash(const Subrect::UVRegion& uv, uint32_t mode) | ||
| { | ||
| uint64_t h = 0; | ||
| auto mix = [&](uint64_t v) { h ^= v + 0x9e3779b97f4a7c15ULL + (h << 12) + (h >> 4); }; | ||
| mix(std::hash<float>{}(uv.x)); | ||
| mix(std::hash<float>{}(uv.y)); | ||
| mix(std::hash<float>{}(uv.w)); | ||
| mix(std::hash<float>{}(uv.h)); |
There was a problem hiding this comment.
ComputeSubrectUVHash() uses std::hash but this translation unit doesn't include <functional>. This can fail to compile on toolchains that don't pull std::hash in transitively. Add the proper standard header include.
| // Distance from nearest edge of the subrect (in pixels) | ||
| float distL = (float)srcPos.x; | ||
| float distR = (float)(SubWidth - 1 - srcPos.x); | ||
| float distT = (float)srcPos.y; | ||
| float distB = (float)(SubHeight - 1 - srcPos.y); |
There was a problem hiding this comment.
edgeDist is computed from srcPos (which includes SrcOffsetX). When SrcOffsetX is non-zero (Extreme strip mode), left/right edge distances become incorrect (can even go negative), so the feather/dither band won't be applied correctly for the offset eye. Compute edge distances in subrect-local coordinates (based on tid.x/tid.y), not the absolute srcPos within the strip texture.
| // Distance from nearest edge of the subrect (in pixels) | |
| float distL = (float)srcPos.x; | |
| float distR = (float)(SubWidth - 1 - srcPos.x); | |
| float distT = (float)srcPos.y; | |
| float distB = (float)(SubHeight - 1 - srcPos.y); | |
| // Distance from nearest edge of the logical subrect (in pixels). | |
| // Use subrect-local coordinates from tid, not srcPos, because srcPos.x | |
| // includes SrcOffsetX in Extreme strip mode. | |
| float distL = (float)tid.x; | |
| float distR = (float)(SubWidth - 1 - tid.x); | |
| float distT = (float)tid.y; | |
| float distB = (float)(SubHeight - 1 - tid.y); |
| // Save current D3D VP | ||
| UINT numVP = 1; | ||
| D3D11_VIEWPORT savedVP; | ||
| This->RSGetViewports(&numVP, &savedVP); |
There was a problem hiding this comment.
savedVP is left uninitialized before RSGetViewports. If the device context has 0 viewports set, RSGetViewports can return numVP=0 without writing savedVP, and the subsequent restore will set an undefined viewport. Initialize savedVP to {} and/or only restore when numVP > 0.
| #include "Hooks.h" | ||
|
|
||
| #include "ShaderTools/BSShaderHooks.h" |
There was a problem hiding this comment.
The file appears to have a UTF-8 BOM character before the first #include (�feff), which can introduce subtle tooling / formatting issues and shows up in diffs as an extra character. Consider saving this file as UTF-8 without BOM to keep encoding consistent with the rest of the codebase.
There was a problem hiding this comment.
Actionable comments posted: 10
🧹 Nitpick comments (5)
src/Features/DlssEnhancer/Postprocess.h (1)
11-11: Mark this API[[nodiscard]]to prevent silent failure handling gaps.This method reports success/failure;
[[nodiscard]]will force call sites to explicitly handle the result.♻️ Proposed change
- static bool ApplyDlssSharpening(Upscaling& upscaling); + [[nodiscard]] static bool ApplyDlssSharpening(Upscaling& upscaling);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Features/DlssEnhancer/Postprocess.h` at line 11, Mark the ApplyDlssSharpening API as [[nodiscard]] so callers cannot ignore its boolean success/failure return; update the declaration of ApplyDlssSharpening(Upscaling& upscaling) in Postprocess.h to include the [[nodiscard]] attribute and adjust any related forward declarations if present so the attribute is visible to callers.features/DLSSperf/Shaders/DLSSperf/BilinearCopyPS.hlsl (1)
18-21: Avoid per-pixelGetDimensions()in this hot shader path.Pass inverse source size via a small cbuffer instead of querying dimensions per pixel.
♻️ Proposed shader-side change
+cbuffer DownscaleCB : register(b0) +{ + float2 InvSrcSize; + float2 _pad; +}; + float4 main(PS_INPUT input) : SV_Target { - float2 srcSize; - SourceTex.GetDimensions(srcSize.x, srcSize.y); - float2 texelSize = 1.0 / srcSize; + float2 texelSize = InvSrcSize;(Then populate
InvSrcSizeonce from CPU before draw.)
Based on learnings: Applies tosrc/Features/**/*.{h,hpp,cpp,hlsl}— consider GPU workload and runtime performance impact.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@features/DLSSperf/Shaders/DLSSperf/BilinearCopyPS.hlsl` around lines 18 - 21, The shader currently calls SourceTex.GetDimensions(srcSize.x, srcSize.y) in the pixel shader which queries dimensions per-pixel; instead add a small cbuffer containing the precomputed inverse source size (e.g., float2 InvSrcSize) and replace the GetDimensions + texelSize calculation with using InvSrcSize directly (update InvSrcSize once on the CPU before each draw). Modify the HLSL to remove SourceTex.GetDimensions usage and read InvSrcSize, and update the CPU-side draw setup to fill the cbuffer so the pixel shader uses the cached inverse size.src/Globals.cpp (1)
354-395: Consider adding a comment explaining the magic numberVertexCount == 30.The hook relies on intercepting draws with exactly 30 vertices to identify the scene-transition fade overlay. While the comment explains the purpose, documenting why 30 vertices specifically identifies this overlay would help future maintainers understand if this assumption could break with engine changes or mod conflicts.
Also, the hook reads
dlssPerf.IsHookActive()anddlssPerf.IsPostChainDone()without synchronization. Per the context snippets,postChainDoneis a simpleboolmodified byEndPostInterceptand cleared byPlayerViewRender_Hook. Since D3D calls are typically single-threaded on the render thread, this should be safe, but consider adding a brief comment confirming single-threaded access assumptions.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Globals.cpp` around lines 354 - 395, Add a short comment in ID3D11DeviceContext_Draw::thunk explaining why VertexCount == 30 is used to detect the scene-transition fade overlay (e.g., that the engine issues a Draw of 30 verts for the fade overlay in current engine versions and that this is a brittle heuristic that may break with engine changes or mods). Also add a brief note next to the dlssPerf.IsHookActive() / dlssPerf.IsPostChainDone() checks (and/or mention the postChainDone flag modified by EndPostIntercept and cleared by PlayerViewRender_Hook) that access is intended to be render-thread single-threaded and therefore unsynchronized reads are acceptable; mention that if this assumption changes a lock or atomic should be used.src/Features/DlssEnhancerFeature.h (1)
141-145: Replace hard-coded enum clamp limits with enum-derived bounds.Using
2u/1uhere is brittle; when new enum values are added, getters will silently cap to outdated ranges.♻️ Suggested refactor
- DlssMode GetDlssMode() const { return (DlssMode)std::min(settings.dlssMode, 2u); } - StretchMode GetStretchMode() const { return (StretchMode)std::min(settings.stretchMode, 2u); } - SharpenMode GetSharpenMode() const { return (SharpenMode)std::min(settings.sharpenMode, 1u); } - PeripheryAAMode GetPeripheryAAMode() const { return (PeripheryAAMode)std::min(settings.peripheryAAMode, 1u); } - SubrectBlendMode GetSubrectBlendMode() const { return (SubrectBlendMode)std::min(settings.subrectBlendMode, 2u); } + static constexpr uint kDlssModeMax = static_cast<uint>(DlssMode::kExtreme); + static constexpr uint kStretchModeMax = static_cast<uint>(StretchMode::kGaussianBlur); + static constexpr uint kSharpenModeMax = static_cast<uint>(SharpenMode::kNone); + static constexpr uint kPeripheryAAModeMax = static_cast<uint>(PeripheryAAMode::kTemporalSmooth); + static constexpr uint kSubrectBlendModeMax = static_cast<uint>(SubrectBlendMode::kDither); + + DlssMode GetDlssMode() const { return static_cast<DlssMode>(std::min(settings.dlssMode, kDlssModeMax)); } + StretchMode GetStretchMode() const { return static_cast<StretchMode>(std::min(settings.stretchMode, kStretchModeMax)); } + SharpenMode GetSharpenMode() const { return static_cast<SharpenMode>(std::min(settings.sharpenMode, kSharpenModeMax)); } + PeripheryAAMode GetPeripheryAAMode() const { return static_cast<PeripheryAAMode>(std::min(settings.peripheryAAMode, kPeripheryAAModeMax)); } + SubrectBlendMode GetSubrectBlendMode() const { return static_cast<SubrectBlendMode>(std::min(settings.subrectBlendMode, kSubrectBlendModeMax)); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Features/DlssEnhancerFeature.h` around lines 141 - 145, The getters currently clamp using hard-coded literals (2u/1u); replace those literals with enum-derived bounds by converting the enum sentinel (e.g., a Last/Count/Max enumerator you add to DlssMode, StretchMode, SharpenMode, PeripheryAAMode, SubrectBlendMode if it doesn't exist) to the underlying unsigned type and use that value in the clamp/min logic (or std::clamp) when casting settings.<field> back to the enum in GetDlssMode/GetStretchMode/GetSharpenMode/GetPeripheryAAMode/GetSubrectBlendMode so the range automatically updates when the enums change. Ensure you add a clear sentinel (like Enum::Count or Enum::Max) to each enum if missing before using it.src/Features/DlssEnhancer/Ops.h (1)
25-37: Unify failure propagation forEnsure*resource allocation APIs.These helpers are
void, so callers cannot explicitly branch on allocation/setup failure. Returning status (like yourPreparePerEyeInputs/FinalizePerEyeOutputs) would make graceful degradation safer.♻️ Suggested API direction
- void EnsureVRIntermediateTextures(...); - void EnsureVRSubrectTextures(...); - void EnsureFasterOutputTextures(...); - void EnsureExtremeStripTextures(...); - void EnsureVRRenderSBS(...); - void EnsureTemporalResources(...); + bool EnsureVRIntermediateTextures(...); + bool EnsureVRSubrectTextures(...); + bool EnsureFasterOutputTextures(...); + bool EnsureExtremeStripTextures(...); + bool EnsureVRRenderSBS(...); + bool EnsureTemporalResources(...);Then make call sites short-circuit to fallback path when
false.As per coding guidelines: "Include proper resource management and graceful degradation for DirectX 11 resources and user input validation to prevent crashes from malformed configurations".
Also applies to: 66-67
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Features/DlssEnhancer/Ops.h` around lines 25 - 37, Change the Ensure* helpers from void to returning a status (bool) so callers can detect allocation/setup failures: update the signatures of EnsureVRIntermediateTextures, EnsureVRSubrectTextures, EnsureFasterOutputTextures, EnsureExtremeStripTextures, and EnsureVRRenderSBS to return bool, implement internal checks to return false on any resource creation/validation failure, and mirror the pattern used by PreparePerEyeInputs/FinalizePerEyeOutputs; then update all call sites to short-circuit into the graceful-degradation/fallback path when these functions return false (propagate failure or switch to the fallback renderer), ensuring resource cleanup and input validation are performed before returning false.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@features/DLSS` Enhancer/Shaders/Features/SubrectBlendCS.hlsl:
- Around line 36-46: The edge distance is computed in source texture space using
srcPos (which includes SrcOffsetX) causing distR to go negative when
SrcOffsetX!=0; change the distance math to use the local subrect coordinate tid
(or a local uint2 like localPos = tid.xy) instead of srcPos so
distL/distR/distT/distB and edgeDist are computed relative to 0..SubWidth-1 /
0..SubHeight-1; update the calculations that reference distL, distR, distT,
distB and edgeDist (currently around srcPos usage) to use the local coordinate
so feathering is correct when SrcOffsetX/DstOffsetX are non-zero.
In `@features/DLSS` Enhancer/Shaders/Features/SubrectStretchCS.hlsl:
- Around line 49-53: Clamp the runtime-configured BlurRadius before use and add
a zero-radius fast path to avoid unnecessary texture fetches: in
SubrectStretchCS.hlsl where texelSize, center, and step are computed, clamp
BlurRadius to a safe range (e.g., max >= 0 and an upper limit) and if the
clamped value is <= a small epsilon treat it as zero — bypass the 9-tap Gaussian
(use only the center sample) instead of computing step and performing nine
texture fetches; ensure references to BlurRadius, texelSize, center, and step
are updated to use the clamped value and the conditional fast path.
In `@src/Features/DlssEnhancer/Core.cpp`:
- Around line 178-224: PreparePerEyeInputs currently just copies subresources
into Core::vrIntermediate* and therefore leaves HMD hidden-area pixels intact;
after the loop that CopySubresourceRegion into Core::vrIntermediateColorIn[i]
and Core::vrIntermediateDepth[i] (inside PreparePerEyeInputs) you must reapply
the same HMD hidden-area clear used in the legacy VR path (the logic in
Upscaling.cpp that clears color where depth == 0). Implement a per-eye
hidden-area clear step that reads Core::vrIntermediateDepth[i] (or uses the same
helper used by Upscaling.cpp) and clears/zeros the corresponding pixels in
Core::vrIntermediateColorIn[i] so Streamline/DLSS don’t get hidden-area garbage;
place this directly after copying into
vrIntermediateColorIn[i]/vrIntermediateDepth[i] in PreparePerEyeInputs and
ensure it runs for both eyes and respects reactive/transparency handling as
before.
In `@src/Features/DlssEnhancer/Modes.cpp`:
- Around line 75-80: The code currently computes per-eye subrect sizes using
p.leftUV for both eyes; change the calculations to use the eyeUVs array per-eye
(i.e., use eyeUVs[i]->w / ->h or &p.leftUV / &p.rightUV) when computing subInW,
subInH, subOutW, subOutH so each eye uses its own UV region; apply the same fix
in the other occurrences noted (around the blocks at original lines 146-149 and
207-210) so all executors derive sizes from the corresponding Subrect::UVRegion
(p.leftUV or p.rightUV) rather than always p.leftUV.
In `@src/Features/DlssEnhancer/Params.cpp`:
- Around line 41-47: The assignment to p.colorDstUAV uses
globals::game::renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN].UAV
without checking for null; update the default path in Params.cpp so you first
retrieve and null-check that UAV (from
renderTargets[RE::RENDER_TARGETS::kMAIN].UAV) before assigning to p.colorDstUAV,
and only assign if non-null (or set an explicit safe fallback and/or comment the
required caller contract); keep the existing
dlssPerf.IsHookActive()/dlssPerf.GetTestTexture() branch unchanged.
In `@src/Features/DlssEnhancer/Preprocess.cpp`:
- Around line 39-67: The code binds upscaling.motionVectorCopyTexture->uav into
UAV slot 2 without validating it; update the preprocess path to check
upscaling.motionVectorCopyTexture is non-null before adding it to the
ID3D11UnorderedAccessView* uavs array when upscaleMethod ==
Upscaling::UpscaleMethod::kDLSS, and if it is null either (a) set the third slot
to nullptr and skip operations that require the motion-vector-copy, or (b) abort
the DLSS-specific bind/dispatch with a graceful return/log message; locate
references in Preprocess.cpp to upscaling.motionVectorCopyTexture, the uavs
array creation, and the Upscaling::UpscaleMethod::kDLSS branch to implement the
guard and corresponding error/log handling.
In `@src/Features/DLSSperf.cpp`:
- Around line 100-110: When CreateUnorderedAccessView for testTextureUAV fails,
tear down the DLSSperf output route by clearing the related resources and state
so callers fall back to the kMAIN path: reset/clear testTextureUAV and
testTexture (or call the existing teardown function for DLSSperf output route if
present), ensure GetTestTextureUAV() will return null after this, and log the
failure; update the block around CreateUnorderedAccessView in DLSSperf.cpp (the
section creating testTextureUAV) to perform these clears/teardown on FAILED(hr)
so later bind calls don’t silently drop display-res output.
In `@src/Features/Upscaling/Streamline.cpp`:
- Around line 577-597: slSetTag(vp, tags, tagCount, context) is called without
checking its return code which can leave incomplete bindings and cause
slEvaluateFeature to run with stale state; update the code around slSetTag to
capture its return value, check for failure (non-success), log an error message
including the return code and relevant identifiers (e.g., vp, tagCount), and
abort/return early (or propagate an error) so that slEvaluateFeature is not
called when slSetTag fails; reference the slSetTag call and ensure the control
flow prevents calling slEvaluateFeature on failure.
In `@src/Hooks.cpp`:
- Around line 1097-1118: The current fixed 0x2500 scan window can overrun into
adjacent functions or miss late call sites; replace the hard-coded span with a
runtime-determined function range by using relocation-based boundaries (e.g.,
use REL::RelocationID(...).address() for the BSShaderRenderTargets::Create start
and the next known relocation ID for its end or use
REL::RelocateMember/Relocation helpers) and then loop from funcStart to funcEnd;
keep references to BSShaderRenderTargets_Create::engineCreateRT, the
REL::RelocationID(...) used for funcBase, DLSSperf_CreateRT_Thunk and
BSShaderRenderTargets_Create::s_rtCallSitesPatched so you still write the
trampoline.write_call<5> for matching call targets but only within the actual
function body determined at runtime.
---
Nitpick comments:
In `@features/DLSSperf/Shaders/DLSSperf/BilinearCopyPS.hlsl`:
- Around line 18-21: The shader currently calls
SourceTex.GetDimensions(srcSize.x, srcSize.y) in the pixel shader which queries
dimensions per-pixel; instead add a small cbuffer containing the precomputed
inverse source size (e.g., float2 InvSrcSize) and replace the GetDimensions +
texelSize calculation with using InvSrcSize directly (update InvSrcSize once on
the CPU before each draw). Modify the HLSL to remove SourceTex.GetDimensions
usage and read InvSrcSize, and update the CPU-side draw setup to fill the
cbuffer so the pixel shader uses the cached inverse size.
In `@src/Features/DlssEnhancer/Ops.h`:
- Around line 25-37: Change the Ensure* helpers from void to returning a status
(bool) so callers can detect allocation/setup failures: update the signatures of
EnsureVRIntermediateTextures, EnsureVRSubrectTextures,
EnsureFasterOutputTextures, EnsureExtremeStripTextures, and EnsureVRRenderSBS to
return bool, implement internal checks to return false on any resource
creation/validation failure, and mirror the pattern used by
PreparePerEyeInputs/FinalizePerEyeOutputs; then update all call sites to
short-circuit into the graceful-degradation/fallback path when these functions
return false (propagate failure or switch to the fallback renderer), ensuring
resource cleanup and input validation are performed before returning false.
In `@src/Features/DlssEnhancer/Postprocess.h`:
- Line 11: Mark the ApplyDlssSharpening API as [[nodiscard]] so callers cannot
ignore its boolean success/failure return; update the declaration of
ApplyDlssSharpening(Upscaling& upscaling) in Postprocess.h to include the
[[nodiscard]] attribute and adjust any related forward declarations if present
so the attribute is visible to callers.
In `@src/Features/DlssEnhancerFeature.h`:
- Around line 141-145: The getters currently clamp using hard-coded literals
(2u/1u); replace those literals with enum-derived bounds by converting the enum
sentinel (e.g., a Last/Count/Max enumerator you add to DlssMode, StretchMode,
SharpenMode, PeripheryAAMode, SubrectBlendMode if it doesn't exist) to the
underlying unsigned type and use that value in the clamp/min logic (or
std::clamp) when casting settings.<field> back to the enum in
GetDlssMode/GetStretchMode/GetSharpenMode/GetPeripheryAAMode/GetSubrectBlendMode
so the range automatically updates when the enums change. Ensure you add a clear
sentinel (like Enum::Count or Enum::Max) to each enum if missing before using
it.
In `@src/Globals.cpp`:
- Around line 354-395: Add a short comment in ID3D11DeviceContext_Draw::thunk
explaining why VertexCount == 30 is used to detect the scene-transition fade
overlay (e.g., that the engine issues a Draw of 30 verts for the fade overlay in
current engine versions and that this is a brittle heuristic that may break with
engine changes or mods). Also add a brief note next to the
dlssPerf.IsHookActive() / dlssPerf.IsPostChainDone() checks (and/or mention the
postChainDone flag modified by EndPostIntercept and cleared by
PlayerViewRender_Hook) that access is intended to be render-thread
single-threaded and therefore unsynchronized reads are acceptable; mention that
if this assumption changes a lock or atomic should be used.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 3bc4ae3f-c5d4-48b5-8c93-f67eecc2fef7
📒 Files selected for processing (35)
features/DLSS Enhancer/Shaders/Features/DLSSENHANCER.inifeatures/DLSS Enhancer/Shaders/Features/PeripheryTemporalSmoothCS.hlslfeatures/DLSS Enhancer/Shaders/Features/SubrectBlendCS.hlslfeatures/DLSS Enhancer/Shaders/Features/SubrectStretchCS.hlslfeatures/DLSSperf/Shaders/DLSSperf/BilinearCopyPS.hlslsrc/Feature.cppsrc/Features/DLSSperf.cppsrc/Features/DLSSperf.hsrc/Features/DlssEnhancer/Bridge.cppsrc/Features/DlssEnhancer/Bridge.hsrc/Features/DlssEnhancer/Core.cppsrc/Features/DlssEnhancer/Core.hsrc/Features/DlssEnhancer/Modes.cppsrc/Features/DlssEnhancer/Ops.hsrc/Features/DlssEnhancer/Params.cppsrc/Features/DlssEnhancer/Params.hsrc/Features/DlssEnhancer/Postprocess.cppsrc/Features/DlssEnhancer/Postprocess.hsrc/Features/DlssEnhancer/Preprocess.cppsrc/Features/DlssEnhancer/Preprocess.hsrc/Features/DlssEnhancerFeature.cppsrc/Features/DlssEnhancerFeature.hsrc/Features/Subrect/Subrect.cppsrc/Features/Subrect/Subrect.hsrc/Features/Upscaling.cppsrc/Features/Upscaling.hsrc/Features/Upscaling/RCAS/RCAS.cppsrc/Features/Upscaling/RCAS/RCAS.hsrc/Features/Upscaling/Streamline.cppsrc/Features/Upscaling/Streamline.hsrc/Globals.cppsrc/Globals.hsrc/Hooks.cppsrc/State.hsrc/XSEPlugin.cpp
| uint2 srcPos = uint2(tid.x + SrcOffsetX, tid.y); | ||
| uint2 dstPos = uint2(tid.x + DstOffsetX, tid.y + DstOffsetY); | ||
|
|
||
| float4 dlss = SrcTex.Load(int3(srcPos, 0)); | ||
|
|
||
| // Distance from nearest edge of the subrect (in pixels) | ||
| float distL = (float)srcPos.x; | ||
| float distR = (float)(SubWidth - 1 - srcPos.x); | ||
| float distT = (float)srcPos.y; | ||
| float distB = (float)(SubHeight - 1 - srcPos.y); | ||
| float edgeDist = min(min(distL, distR), min(distT, distB)); |
There was a problem hiding this comment.
Compute feather distance in local subrect space, not srcPos.
SrcOffsetX is intentionally non-zero for Extreme mode, but Lines 42-45 use srcPos for edge distance. That makes distR negative once the source read is offset, so the strip stays in the feather band and blends incorrectly against the background. The distance math should use tid.xy (or another local 0..SubWidth/Height coordinate), not the source texture offset.
Suggested fix
- uint2 srcPos = uint2(tid.x + SrcOffsetX, tid.y);
- uint2 dstPos = uint2(tid.x + DstOffsetX, tid.y + DstOffsetY);
+ uint2 localPos = tid.xy;
+ uint2 srcPos = uint2(localPos.x + SrcOffsetX, localPos.y);
+ uint2 dstPos = uint2(localPos.x + DstOffsetX, localPos.y + DstOffsetY);
float4 dlss = SrcTex.Load(int3(srcPos, 0));
// Distance from nearest edge of the subrect (in pixels)
- float distL = (float)srcPos.x;
- float distR = (float)(SubWidth - 1 - srcPos.x);
- float distT = (float)srcPos.y;
- float distB = (float)(SubHeight - 1 - srcPos.y);
+ float distL = (float)localPos.x;
+ float distR = (float)(SubWidth - 1 - localPos.x);
+ float distT = (float)localPos.y;
+ float distB = (float)(SubHeight - 1 - localPos.y);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| uint2 srcPos = uint2(tid.x + SrcOffsetX, tid.y); | |
| uint2 dstPos = uint2(tid.x + DstOffsetX, tid.y + DstOffsetY); | |
| float4 dlss = SrcTex.Load(int3(srcPos, 0)); | |
| // Distance from nearest edge of the subrect (in pixels) | |
| float distL = (float)srcPos.x; | |
| float distR = (float)(SubWidth - 1 - srcPos.x); | |
| float distT = (float)srcPos.y; | |
| float distB = (float)(SubHeight - 1 - srcPos.y); | |
| float edgeDist = min(min(distL, distR), min(distT, distB)); | |
| uint2 localPos = tid.xy; | |
| uint2 srcPos = uint2(localPos.x + SrcOffsetX, localPos.y); | |
| uint2 dstPos = uint2(localPos.x + DstOffsetX, localPos.y + DstOffsetY); | |
| float4 dlss = SrcTex.Load(int3(srcPos, 0)); | |
| // Distance from nearest edge of the subrect (in pixels) | |
| float distL = (float)localPos.x; | |
| float distR = (float)(SubWidth - 1 - localPos.x); | |
| float distT = (float)localPos.y; | |
| float distB = (float)(SubHeight - 1 - localPos.y); | |
| float edgeDist = min(min(distL, distR), min(distT, distB)); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@features/DLSS` Enhancer/Shaders/Features/SubrectBlendCS.hlsl around lines 36
- 46, The edge distance is computed in source texture space using srcPos (which
includes SrcOffsetX) causing distR to go negative when SrcOffsetX!=0; change the
distance math to use the local subrect coordinate tid (or a local uint2 like
localPos = tid.xy) instead of srcPos so distL/distR/distT/distB and edgeDist are
computed relative to 0..SubWidth-1 / 0..SubHeight-1; update the calculations
that reference distL, distR, distT, distB and edgeDist (currently around srcPos
usage) to use the local coordinate so feathering is correct when
SrcOffsetX/DstOffsetX are non-zero.
| // Gaussian blur 3x3: 9-tap weighted average around center | ||
| float2 texelSize = float2(1.0 / (float)SrcWidth, 1.0 / (float)SrcHeight); | ||
| float2 center = float2(srcU, srcV); | ||
| float2 step = texelSize * BlurRadius; | ||
|
|
There was a problem hiding this comment.
Clamp blur radius and add a zero-radius fast path.
BlurRadius is runtime-configured; clamp it before use, and skip 9-tap blur when radius is effectively zero to avoid wasted texture fetches.
🛠️ Proposed change
} else if (StretchMode == 2) {
// Gaussian blur 3x3: 9-tap weighted average around center
float2 texelSize = float2(1.0 / (float)SrcWidth, 1.0 / (float)SrcHeight);
float2 center = float2(srcU, srcV);
- float2 step = texelSize * BlurRadius;
+ float safeRadius = (BlurRadius > 0.0) ? min(BlurRadius, 8.0) : 0.0;
+ if (safeRadius <= 1e-4) {
+ color = SrcTex.SampleLevel(BilinearSampler, center, 0);
+ } else {
+ float2 step = texelSize * safeRadius;
// Gaussian weights for 3x3 kernel (sigma ~ 0.85 * radius)
// Center=4, Edge=2, Corner=1, sum=16
float4 sum = SrcTex.SampleLevel(BilinearSampler, center, 0) * 4.0;
sum += SrcTex.SampleLevel(BilinearSampler, center + float2(-step.x, 0), 0) * 2.0;
sum += SrcTex.SampleLevel(BilinearSampler, center + float2(step.x, 0), 0) * 2.0;
sum += SrcTex.SampleLevel(BilinearSampler, center + float2(0, -step.y), 0) * 2.0;
sum += SrcTex.SampleLevel(BilinearSampler, center + float2(0, step.y), 0) * 2.0;
sum += SrcTex.SampleLevel(BilinearSampler, center + float2(-step.x, -step.y), 0);
sum += SrcTex.SampleLevel(BilinearSampler, center + float2(step.x, -step.y), 0);
sum += SrcTex.SampleLevel(BilinearSampler, center + float2(-step.x, step.y), 0);
sum += SrcTex.SampleLevel(BilinearSampler, center + float2(step.x, step.y), 0);
color = sum * (1.0 / 16.0);
+ }
} else {As per coding guidelines: include user input validation and graceful degradation for malformed configurations.
Based on learnings: Applies to src/Features/**/*.{h,hpp,cpp,hlsl} — consider GPU workload and runtime performance impact.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@features/DLSS` Enhancer/Shaders/Features/SubrectStretchCS.hlsl around lines
49 - 53, Clamp the runtime-configured BlurRadius before use and add a
zero-radius fast path to avoid unnecessary texture fetches: in
SubrectStretchCS.hlsl where texelSize, center, and step are computed, clamp
BlurRadius to a safe range (e.g., max >= 0 and an upper limit) and if the
clamped value is <= a small epsilon treat it as zero — bypass the 9-tap Gaussian
(use only the center sample) instead of computing step and performing nine
texture fetches; ensure references to BlurRadius, texelSize, center, and step
are updated to use the clamped value and the conditional fast path.
| const Subrect::UVRegion* eyeUVs[2] = { &p.leftUV, &p.rightUV }; | ||
| uint32_t subInW = std::max<uint32_t>(1, (uint32_t)(p.eyeWidthIn * p.leftUV.w)); | ||
| uint32_t subInH = std::max<uint32_t>(1, (uint32_t)(p.eyeHeightIn * p.leftUV.h)); | ||
| uint32_t subOutW = std::max<uint32_t>(1, (uint32_t)(p.eyeWidthOut * p.leftUV.w)); | ||
| uint32_t subOutH = std::max<uint32_t>(1, (uint32_t)(p.eyeHeightOut * p.leftUV.h)); | ||
|
|
There was a problem hiding this comment.
Don't size both eyes from leftUV.
VRDlssParams carries leftUV and rightUV, and the subrect controller persists right_uv, but all three executors derive subIn* / subOut* from p.leftUV. Any preset with a different right-eye width or height will crop and evaluate eye 1 with the wrong extent.
Also applies to: 146-149, 207-210
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Features/DlssEnhancer/Modes.cpp` around lines 75 - 80, The code currently
computes per-eye subrect sizes using p.leftUV for both eyes; change the
calculations to use the eyeUVs array per-eye (i.e., use eyeUVs[i]->w / ->h or
&p.leftUV / &p.rightUV) when computing subInW, subInH, subOutW, subOutH so each
eye uses its own UV region; apply the same fix in the other occurrences noted
(around the blocks at original lines 146-149 and 207-210) so all executors
derive sizes from the corresponding Subrect::UVRegion (p.leftUV or p.rightUV)
rather than always p.leftUV.
| p.colorDstUAV = globals::game::renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN].UAV; | ||
| if (dlssPerf.IsHookActive()) { | ||
| if (auto* testTex = dlssPerf.GetTestTexture()) { | ||
| p.colorDst = testTex; | ||
| p.colorDstUAV = dlssPerf.GetTestTextureUAV(); | ||
| } | ||
| } |
There was a problem hiding this comment.
Add null check for kMAIN UAV before assignment.
Line 41 directly accesses renderTargets[RE::RENDER_TARGETS::kMAIN].UAV without verifying it's non-null. While the DLSSperf path at lines 43-46 correctly null-checks the test texture, the default path could assign a null UAV if the render target isn't fully initialized.
🛡️ Proposed fix to add defensive null check
p.colorSrc = upscalingTexture;
p.colorDst = upscalingTexture;
- p.colorDstUAV = globals::game::renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN].UAV;
+ auto& mainRT = globals::game::renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN];
+ p.colorDstUAV = mainRT.UAV; // May be null if RT not ready; caller should validate
if (dlssPerf.IsHookActive()) {
if (auto* testTex = dlssPerf.GetTestTexture()) {
p.colorDst = testTex;
p.colorDstUAV = dlssPerf.GetTestTextureUAV();
}
}Alternatively, if the caller always validates colorDstUAV before use, add a comment documenting that contract.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| p.colorDstUAV = globals::game::renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN].UAV; | |
| if (dlssPerf.IsHookActive()) { | |
| if (auto* testTex = dlssPerf.GetTestTexture()) { | |
| p.colorDst = testTex; | |
| p.colorDstUAV = dlssPerf.GetTestTextureUAV(); | |
| } | |
| } | |
| auto& mainRT = globals::game::renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN]; | |
| p.colorDstUAV = mainRT.UAV; // May be null if RT not ready; caller should validate | |
| if (dlssPerf.IsHookActive()) { | |
| if (auto* testTex = dlssPerf.GetTestTexture()) { | |
| p.colorDst = testTex; | |
| p.colorDstUAV = dlssPerf.GetTestTextureUAV(); | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Features/DlssEnhancer/Params.cpp` around lines 41 - 47, The assignment to
p.colorDstUAV uses
globals::game::renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN].UAV
without checking for null; update the default path in Params.cpp so you first
retrieve and null-check that UAV (from
renderTargets[RE::RENDER_TARGETS::kMAIN].UAV) before assigning to p.colorDstUAV,
and only assign if non-null (or set an explicit safe fallback and/or comment the
required caller contract); keep the existing
dlssPerf.IsHookActive()/dlssPerf.GetTestTexture() branch unchanged.
| // UAV for testTexture | ||
| { | ||
| D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc{}; | ||
| uavDesc.Format = desc.Format; | ||
| uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D; | ||
| uavDesc.Texture2D.MipSlice = 0; | ||
| hr = device->CreateUnorderedAccessView(testTexture.get(), &uavDesc, testTextureUAV.put()); | ||
| if (FAILED(hr)) { | ||
| logger::error("[DLSSperf] Failed to create testTexture UAV: {:#x}", (uint32_t)hr); | ||
| } | ||
| } |
There was a problem hiding this comment.
Disable the DLSSperf output route if testTextureUAV creation fails.
The rest of this PR still routes display-res composition through GetTestTextureUAV() when the hook is active. If UAV creation fails here, the subrect stretch/blend passes later bind a null destination UAV and silently drop the display-res output. Tear the route down or clear testTexture here so callers fall back to the normal kMAIN path. As per coding guidelines, "Include proper resource management and graceful degradation for DirectX 11 resources and user input validation to prevent crashes from malformed configurations".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Features/DLSSperf.cpp` around lines 100 - 110, When
CreateUnorderedAccessView for testTextureUAV fails, tear down the DLSSperf
output route by clearing the related resources and state so callers fall back to
the kMAIN path: reset/clear testTextureUAV and testTexture (or call the existing
teardown function for DLSSperf output route if present), ensure
GetTestTextureUAV() will return null after this, and log the failure; update the
block around CreateUnorderedAccessView in DLSSperf.cpp (the section creating
testTextureUAV) to perform these clears/teardown on FAILED(hr) so later bind
calls don’t silently drop display-res output.
| // --- DLSS Enhancer route (VR-only) --- | ||
| if (IsDlssEnhancerRouteActive()) { | ||
| if (upscaleMethod != UpscaleMethod::kDLSS) { | ||
| logger::error("[DLSSENHANCER] Enhancer route active but method is {}, skipping frame", (int)upscaleMethod); | ||
| return; | ||
| } | ||
|
|
||
| auto& enhancer = globals::features::dlssEnhancer; | ||
|
|
||
| if (enhancer.IsAnyEncodeEnabled()) { | ||
| if (!DlssEnhancer::Preprocess::EncodeUpscalingTextures(*this)) { | ||
| logger::error("[DLSSENHANCER] Encode preprocess failed; skipping frame"); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| auto& main = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN]; | ||
| auto& depth = renderer->GetDepthStencilData().depthStencils[RE::RENDER_TARGETS_DEPTHSTENCIL::kMAIN]; | ||
| auto& motionVector = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMOTION_VECTOR]; | ||
|
|
||
| ID3D11Resource* mvec = enhancer.IsEncodeMVDilation() ? motionVectorCopyTexture->resource.get() : motionVector.texture; | ||
| ID3D11Resource* reactive = enhancer.IsEncodeReactiveMask() ? reactiveMaskTexture->resource.get() : nullptr; | ||
| ID3D11Resource* transparency = enhancer.IsEncodeTransparencyMask() ? transparencyCompositionMaskTexture->resource.get() : nullptr; | ||
|
|
||
| state->BeginPerfEvent("Upscaling (DLSS Enhancer)"); | ||
| DlssEnhancer::Core::ExecuteVRDlssCore(streamline, | ||
| main.texture, depth.texture, | ||
| reactive, transparency, mvec); | ||
| state->EndPerfEvent(); | ||
| return; |
There was a problem hiding this comment.
Check ExecuteVRDlssCore() before exiting the frame.
If ExecuteVRDlssCore() fails, this branch still returns and skips the legacy DLSS path, so the frame proceeds with stale / non-upscaled contents. Only early-return on success; otherwise fall through to the existing fallback path.
Suggested fix
state->BeginPerfEvent("Upscaling (DLSS Enhancer)");
- DlssEnhancer::Core::ExecuteVRDlssCore(streamline,
+ const bool executed = DlssEnhancer::Core::ExecuteVRDlssCore(streamline,
main.texture, depth.texture,
reactive, transparency, mvec);
state->EndPerfEvent();
- return;
+ if (executed) {
+ return;
+ }
+
+ logger::error("[DLSSENHANCER] Core execution failed; falling back to legacy DLSS path");
}| // Build tag array dynamically — reactive and transparency masks are optional hints | ||
| sl::ResourceTag tags[6]; | ||
| uint32_t tagCount = 0; | ||
| tags[tagCount++] = { &colorInRes, sl::kBufferTypeScalingInputColor, sl::ResourceLifecycle::eOnlyValidNow, &extentIn }; | ||
| tags[tagCount++] = { &colorOutRes, sl::kBufferTypeScalingOutputColor, sl::ResourceLifecycle::eOnlyValidNow, &extentOut }; | ||
| tags[tagCount++] = { &depthRes, sl::kBufferTypeDepth, sl::ResourceLifecycle::eValidUntilPresent, &extentIn }; | ||
| tags[tagCount++] = { &mvecRes, sl::kBufferTypeMotionVectors, sl::ResourceLifecycle::eValidUntilPresent, &extentIn }; | ||
|
|
||
| sl::Resource reactiveMaskRes; | ||
| if (reactiveMask) { | ||
| reactiveMaskRes = { sl::ResourceType::eTex2d, reactiveMask, 0 }; | ||
| tags[tagCount++] = { &reactiveMaskRes, sl::kBufferTypeBiasCurrentColorHint, sl::ResourceLifecycle::eValidUntilPresent, &extentIn }; | ||
| } | ||
|
|
||
| sl::Resource transparencyMaskRes; | ||
| if (transparencyMask) { | ||
| transparencyMaskRes = { sl::ResourceType::eTex2d, transparencyMask, 0 }; | ||
| tags[tagCount++] = { &transparencyMaskRes, sl::kBufferTypeTransparencyHint, sl::ResourceLifecycle::eValidUntilPresent, &extentIn }; | ||
| } | ||
|
|
||
| slSetTag(vp, tags, _countof(tags), context); | ||
| slSetTag(vp, tags, tagCount, context); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, check the file size and structure
wc -l src/Features/Upscaling/Streamline.cppRepository: doodlum/skyrim-community-shaders
Length of output: 117
🏁 Script executed:
# Read the specific lines mentioned and surrounding context
sed -n '570,605p' src/Features/Upscaling/Streamline.cppRepository: doodlum/skyrim-community-shaders
Length of output: 1610
🏁 Script executed:
# Find what function contains these lines
sed -n '500,597p' src/Features/Upscaling/Streamline.cpp | head -100Repository: doodlum/skyrim-community-shaders
Length of output: 4373
🏁 Script executed:
# Search for other slSetTag calls in the codebase to see how they handle results
rg 'slSetTag' --type cpp --type hRepository: doodlum/skyrim-community-shaders
Length of output: 323
🏁 Script executed:
# Search for slSetTag function pointer definition and related declarations
rg 'PFun_slSetTag|slSetTag.*Result' --type h --type cpp -A 2 -B 2Repository: doodlum/skyrim-community-shaders
Length of output: 1153
🏁 Script executed:
# Look at lines after 597 to see the full flow
sed -n '597,630p' src/Features/Upscaling/Streamline.cppRepository: doodlum/skyrim-community-shaders
Length of output: 1328
🏁 Script executed:
# Check if there's a return type annotation or documentation for slSetTag
rg 'typedef.*PFun_slSetTag|PFun_slSetTag\*' --type h -A 5Repository: doodlum/skyrim-community-shaders
Length of output: 569
🏁 Script executed:
# Search for the typedef definition of PFun_slSetTag
rg 'typedef.*slSetTag|PFun_slSetTag\s*=' --type h --type cpp -B 3 -A 3Repository: doodlum/skyrim-community-shaders
Length of output: 58
🏁 Script executed:
# Look for sl namespace definitions or includes that might define the function signatures
fd -e h -e hpp | xargs grep -l 'sl::Result.*slSetTag\|PFun_slSetTag' 2>/dev/nullRepository: doodlum/skyrim-community-shaders
Length of output: 111
🏁 Script executed:
# Search more broadly for sl::Result patterns to confirm the Streamline API convention
rg 'sl::Result' src/Features/Upscaling/Streamline.cpp | head -20Repository: doodlum/skyrim-community-shaders
Length of output: 840
🏁 Script executed:
# Look at the Streamline.h header to find the typedef and any documentation
cat src/Features/Upscaling/Streamline.h | head -150Repository: doodlum/skyrim-community-shaders
Length of output: 3629
🏁 Script executed:
# Search for any includes or external declarations about slSetTag
rg -i 'slSetTag' src/Features/Upscaling/ --type h --type cpp -B 5 -A 2Repository: doodlum/skyrim-community-shaders
Length of output: 2419
🏁 Script executed:
# Confirm slSetTag is from the external SL header files by looking for NVIDIA Streamline documentation patterns
rg 'typedef.*Result.*\(' src/Features/Upscaling/Streamline.cpp | head -5Repository: doodlum/skyrim-community-shaders
Length of output: 58
🏁 Script executed:
# Check the actual signature pattern by examining how other PFun functions are being used
sed -n '1,100p' src/Features/Upscaling/Streamline.cpp | grep -A 5 'PFun_'Repository: doodlum/skyrim-community-shaders
Length of output: 58
🏁 Script executed:
# Search for NVIDIA Streamline sl.h to see if we can find documentation about slSetTag return type
rg 'sl::Result' src/Features/Upscaling/Streamline.cpp -B 2 -A 2 | head -40Repository: doodlum/skyrim-community-shaders
Length of output: 2032
🏁 Script executed:
# Get a complete picture of all Streamline API call error handling patterns
rg 'sl(Init|Shutdown|SetTag|EvaluateFeature|SetFeatureLoaded|GetFeatureFunction|PCLSetMarker|DLSSSetOptions|GetFeatureRequirements)' src/Features/Upscaling/Streamline.cpp -B 1 -A 2Repository: doodlum/skyrim-community-shaders
Length of output: 5014
Check and log slSetTag result before evaluating DLSS.
Line 597 calls slSetTag without checking the return value, despite every other Streamline API call in this file doing so (slGetFeatureRequirements, slSetFeatureLoaded, slGetFeatureFunction, slPCLSetMarker, slEvaluateFeature all check results). If tagging fails, slEvaluateFeature proceeds with incomplete resource bindings from a previous frame or eye, making the failure hard to diagnose.
Suggested fix
- slSetTag(vp, tags, tagCount, context);
+ const sl::Result tagResult = slSetTag(vp, tags, tagCount, context);
+ if (tagResult != sl::Result::eOk) {
+ logger::error(
+ "[Streamline] slSetTag failed{}: {}",
+ globals::game::isVR ? std::format(" for eye {}", eyeIndex) : "",
+ magic_enum::enum_name(tagResult));
+ return;
+ }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Features/Upscaling/Streamline.cpp` around lines 577 - 597, slSetTag(vp,
tags, tagCount, context) is called without checking its return code which can
leave incomplete bindings and cause slEvaluateFeature to run with stale state;
update the code around slSetTag to capture its return value, check for failure
(non-success), log an error message including the return code and relevant
identifiers (e.g., vp, tagCount), and abort/return early (or propagate an error)
so that slEvaluateFeature is not called when slSetTag fails; reference the
slSetTag call and ensure the control flow prevents calling slEvaluateFeature on
failure.
| // DLSSperf: runtime scan of BSShaderRenderTargets::Create for remaining (unhooked) | ||
| // CreateRenderTarget call sites. The 6 sites patched by write_thunk_call above | ||
| // (Main, Normals, NormalsSwap, MotionVectors, RefractionNormals, UnderwaterMask) | ||
| // have been redirected and won't match engineCreateRT — only unhooked sites hit. | ||
| if (BSShaderRenderTargets_Create::engineCreateRT) { | ||
| auto funcBase = REL::RelocationID(100458, 107175).address(); | ||
| auto createRTAddr = reinterpret_cast<uintptr_t>(BSShaderRenderTargets_Create::engineCreateRT); | ||
| auto& trampoline = SKSE::GetTrampoline(); | ||
|
|
||
| for (size_t offset = 0; offset < 0x2500; offset++) { | ||
| auto callAddr = funcBase + offset; | ||
| if (*reinterpret_cast<uint8_t*>(callAddr) == 0xE8) { | ||
| int32_t rel = *reinterpret_cast<int32_t*>(callAddr + 1); | ||
| uintptr_t target = callAddr + 5 + rel; | ||
| if (target == createRTAddr) { | ||
| trampoline.write_call<5>(callAddr, BSShaderRenderTargets_Create::DLSSperf_CreateRT_Thunk); | ||
| BSShaderRenderTargets_Create::s_rtCallSitesPatched++; | ||
| } | ||
| } | ||
| } | ||
| BSShaderRenderTargets_Create::s_rtCallSitesPatched += 6; | ||
| } |
There was a problem hiding this comment.
Avoid patching a fixed 0x2500-byte window here.
This scan is not bounded to the actual BSShaderRenderTargets::Create body. On another runtime, it can miss late CreateRenderTarget calls or rewrite an identical call in the next function if it also targets engineCreateRT. This hook path is too version-sensitive for a hard-coded byte span.
As per coding guidelines, "Ensure code changes work across SE/AE/VR Skyrim variants using runtime detection and REL::RelocateMember() patterns for compatibility".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Hooks.cpp` around lines 1097 - 1118, The current fixed 0x2500 scan window
can overrun into adjacent functions or miss late call sites; replace the
hard-coded span with a runtime-determined function range by using
relocation-based boundaries (e.g., use REL::RelocationID(...).address() for the
BSShaderRenderTargets::Create start and the next known relocation ID for its end
or use REL::RelocateMember/Relocation helpers) and then loop from funcStart to
funcEnd; keep references to BSShaderRenderTargets_Create::engineCreateRT, the
REL::RelocationID(...) used for funcBase, DLSSperf_CreateRT_Thunk and
BSShaderRenderTargets_Create::s_rtCallSitesPatched so you still write the
trampoline.write_call<5> for matching call targets but only within the actual
function body determined at runtime.
|
✅ A pre-release build is available for this PR: |
|
|
||
| // Features that are more special then others | ||
| std::unordered_map<std::string, bool> specialFeatures = { | ||
| { "DLSSENHANCER", false }, |
There was a problem hiding this comment.
Nothing is allowed to be a special feature. You must respect the feature boundary including hooking from the feature.
doodlum
left a comment
There was a problem hiding this comment.
this looks to be a real awesome addition to the project. if it can fix dlss at high resolutions whilst being more efficient that's going to be extremely useful.
| // CreateRenderTarget call sites. The 6 sites patched by write_thunk_call above | ||
| // (Main, Normals, NormalsSwap, MotionVectors, RefractionNormals, UnderwaterMask) | ||
| // have been redirected and won't match engineCreateRT — only unhooked sites hit. | ||
| if (BSShaderRenderTargets_Create::engineCreateRT) { |
| globals::features::hdrDisplay.RedirectFramebuffer(); | ||
|
|
||
| func(a_this, a3, a_target, a_4, a_5); | ||
| // DLSSperf: hybrid Post — engine runs steps 1-8 (1k pyramid/bloom/exposure), |
| return; | ||
| } | ||
|
|
||
| // --- Legacy upscaling path (upstream) --- |
|
|
||
| if (upscaleMethod != UpscaleMethod::kNONE && upscaleMethod != UpscaleMethod::kTAA) { | ||
| float resolutionScaleBase = 1.0f / ffxFsr3GetUpscaleRatioFromQualityMode((FfxFsr3QualityMode)settings.qualityMode); | ||
| // Phase 2: DLSSperf forces ratio=1 — RTs are already at RenderRes, no DRS scaling needed |
There was a problem hiding this comment.
why are you touching this
| // Post-chain-done flag: set true after EndPostIntercept, cleared at | ||
| // PlayerView end by PlayerViewRender_Hook. When true, UpdateViewPort | ||
| // hook expands VP to displayRes so draws after the Post chain | ||
| // (UI合成, scene fade, submit prep) use correct 3k VP. |
There was a problem hiding this comment.
why is this a separate feature. features should not be dependent on other features.
DLSS Enhancer: intercepts and reconfigures DLSS at runtime for VR, enabling periphery temporal smoothing and subrect-aware blending. DLSSperf: hybrid post-processing pipeline that lets the engine run bloom/exposure at internal resolution while DLSS upscales. Includes Subrect utility for VR viewport cropping and shared presets. VR-only for now; flat-screen adaptation is straightforward.
Automated formatting by clang-format, prettier, and other hooks. See https://pre-commit.ci for details.
574a4cc to
7e98dbe
Compare
There was a problem hiding this comment.
Actionable comments posted: 7
♻️ Duplicate comments (1)
src/Features/DlssEnhancer/Preprocess.cpp (1)
39-67:⚠️ Potential issue | 🔴 Critical
motionVectorCopyTexturestill not validated despite prior fix claim.The past review comment flagged this exact issue and is marked addressed in commits e965e37..7e98dbe, but the guard at Line 39 still omits
motionVectorCopyTexture, and Line 66 continues to dereferenceupscaling.motionVectorCopyTexture->uav.get()on the DLSS path. If that resource is null, this crashes. Please add the check:Proposed fix
- if (!upscaling.upscalingDataCB || !upscaling.reactiveMaskTexture || !upscaling.transparencyCompositionMaskTexture) { + if (!upscaling.upscalingDataCB || + !upscaling.reactiveMaskTexture || + !upscaling.transparencyCompositionMaskTexture || + !upscaling.motionVectorCopyTexture || + !upscaling.motionVectorCopyTexture->uav) { logger::error("[DLSSENHANCER] Missing preprocess resources"); return false; }As per coding guidelines: "Include proper resource management and graceful degradation for DirectX 11 resources and user input validation to prevent crashes from malformed configurations".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Features/DlssEnhancer/Preprocess.cpp` around lines 39 - 67, The guard that checks required preprocess resources must also validate upscaling.motionVectorCopyTexture when DLSS is selected to avoid dereferencing a null UAV; update the existing null-check that currently tests upscaling.upscalingDataCB, upscaling.reactiveMaskTexture, and upscaling.transparencyCompositionMaskTexture to include upscaling.motionVectorCopyTexture when upscaleMethod == Upscaling::UpscaleMethod::kDLSS, and ensure the creation of the uavs array only dereferences upscaling.motionVectorCopyTexture->uav.get() after that check (or substitute nullptr/fallback if DLSS is not available), referencing the symbols upscaling.motionVectorCopyTexture, Upscaling::UpscaleMethod::kDLSS, and the uavs array construction to locate where to modify the code.
🧹 Nitpick comments (5)
src/Features/DlssEnhancer/Preprocess.cpp (1)
63-67: Dead ternary branch — method is already guaranteed DLSS.The early-return at Line 30-33 ensures
upscaleMethod == kDLSSby the time this executes, so the ternary at Line 66 always picks the DLSS arm. Either drop the conditional for clarity or, if you intend to later re-enable non-DLSS routes here, note that theGetEnhancerEncodeTexturesCShelper also unconditionally injects theDLSSdefine (Line 15) regardless ofmethodIndex, so the per-method cache slot is misleading.Suggested simplification
ID3D11UnorderedAccessView* uavs[3] = { upscaling.reactiveMaskTexture->uav.get(), upscaling.transparencyCompositionMaskTexture->uav.get(), - upscaleMethod == Upscaling::UpscaleMethod::kDLSS ? upscaling.motionVectorCopyTexture->uav.get() : nullptr + upscaling.motionVectorCopyTexture->uav.get() };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Features/DlssEnhancer/Preprocess.cpp` around lines 63 - 67, The ternary in the uavs array is dead because earlier code guarantees upscaleMethod == Upscaling::UpscaleMethod::kDLSS; replace the conditional with the direct DLSS UAV (use upscaling.motionVectorCopyTexture->uav.get()) so the third element is unambiguous, and also update related logic so per-method cache slots reflect actual method use (inspect GetEnhancerEncodeTexturesCS which currently injects a DLSS define unconditionally and make that define conditional on methodIndex or adjust cache key to avoid misleading per-method caching).CMakeLists.txt (1)
5-10: PR title: consider shortening to fit the repo guideline.The current title is descriptive, but over the 50-character title guideline. Consider
feat(vr): add dlss enhancer; addImplements #...if there is a tracking issue.As per coding guidelines, “Format: type(scope): description” and “Length: 50 characters limit for title”.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@CMakeLists.txt` around lines 5 - 10, PR title exceeds the 50-character guideline: change the pull request title to the required "type(scope): description" format and shorten it (suggestion: "feat(vr): add dlss enhancer"); if there's an associated tracking issue include "Implements #<issue-number>" in the title or PR description; no code changes required inside the CMakeLists project(...) block (CommunityShaders, VERSION 1.4.11 remain unchanged).src/Features/DLSSperf.cpp (2)
251-303: No exception-safe restore of swapped engine struct pointers.In
TonemapRender_Hook::thunk(and similarlyUIPassDispatch_Hook::thunk) you rewritekmainRT.SRV,kmainCopyRT.SRV, and 16 entries ofkmainDS.views/readOnlyViews(plus the VR viewport), then callfunc(...)and restore. If anything belowfuncthrows or long-jumps, the engine is left permanently pointed attestTextureSRV/fakeDSV, which corrupts every subsequent frame until shutdown.Since these are engine-owned globals, consider a small scope-exit / RAII guard around the swap so restoration is guaranteed:
🛡️ Sketch
auto restore = stl::scope_exit([&] { kmainRT.SRV = dlssPerf.savedKMainSRV; kmainCopyRT.SRV = dlssPerf.savedKMainCopySRV; for (int i = 0; i < 8; i++) kmainDS.views[i] = dlssPerf.savedKMainViews[i]; for (int i = 0; i < 8; i++) kmainDS.readOnlyViews[i] = dlssPerf.savedKMainReadOnlyViews[i]; dlssPerf.savedKMainSRV = nullptr; dlssPerf.savedKMainCopySRV = nullptr; }); // ...do swap, then call func...As per coding guidelines: "Include proper resource management and graceful degradation for DirectX 11 resources and user input validation to prevent crashes from malformed configurations".
Also applies to: 385-454
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Features/DLSSperf.cpp` around lines 251 - 303, TonemapRender_Hook::thunk currently swaps engine-owned pointers (kmainRT.SRV, kmainCopyRT.SRV, and kmainDS.views/readOnlyViews) and restores them only after calling func(...), which can leak if func throws/long-jumps; wrap the swap+call+restore in an exception-safe RAII guard (e.g., stl::scope_exit or a small local RestoreGuard) that captures references to dlssPerf.savedKMainSRV, dlssPerf.savedKMainCopySRV, dlssPerf.savedKMainViews[], dlssPerf.savedKMainReadOnlyViews[], kmainRT, kmainCopyRT, and kmainDS and performs the exact restore logic on destruction so restoration is guaranteed even if func throws; apply the same pattern to UIPassDispatch_Hook::thunk and ensure saved* members are nulled inside the guard after restore.
529-640:DownscaleToKMainsaves/restores only a subset of the pipeline state.The helper saves RT slot 0, VP, blend/DS/RS state, and GS/HS/DS shaders, but does not save/restore the IA state it rewrites (input layout, vertex/index buffers, primitive topology) nor the VS/PS shaders or PS SRV/sampler slot 0 it overwrites. Because this runs right before the engine post chain, and the engine re-binds IA + shaders per draw, this is benign in practice today — but it's fragile: any future call site that runs mid-pass will silently corrupt the following draw.
Consider either (a) saving the remaining IA/VS/PS/sampler state for symmetry, or (b) documenting the invariant in the header that this may only be called when the pipeline is about to be fully re-bound.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Features/DLSSperf.cpp` around lines 529 - 640, DownscaleToKMain currently overwrites IA, VS/PS and PS resource/sampler slot 0 without saving/restoring them; update the function to symmetrically save and restore the remaining pipeline state it touches: capture current input layout, vertex buffer(s), index buffer, primitive topology (e.g., savedInputLayout, savedVB(s), savedIB, savedTopology), the bound VS and PS (savedVS, savedPS), and PS slot 0 SRV and sampler (savedPSSRV0, savedPSSampler0) before calling IASet*/VSSetShader/PSSetShader/PSSetShaderResources/PSSetSamplers, then restore those saved objects after drawing and Release() any COM pointers you acquired; alternatively, if you prefer not to add state plumbing, add a clear header comment on DownscaleToKMain documenting the invariant that it must only be called when the pipeline will be fully rebound.src/Features/DlssEnhancer/Core.cpp (1)
87-103: Deduplicate the R32_TYPELESS depth intermediate pattern.This depth texture + R32_FLOAT SRV block is repeated verbatim in
EnsureVRSubrectTextures(142–158) andEnsureExtremeStripTextures(383–398). Extracting a small helper (e.g.CreateDepthIntermediate(w, h, name)) would tighten the three copies and make format/bind-flag changes less error-prone.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Features/DlssEnhancer/Core.cpp` around lines 87 - 103, The repeated pattern creating a typeless R32 depth texture and R32_FLOAT SRV in EnsureVRSubrectTextures and EnsureExtremeStripTextures should be extracted into a small helper (e.g. CreateDepthIntermediate) to avoid duplication and make future format/bind-flag changes safe; implement a function CreateDepthIntermediate(int w, int h, const std::string &name) that builds the D3D11_TEXTURE2D_DESC (Width, Height, MipLevels=1, ArraySize=1, Format=DXGI_FORMAT_R32_TYPELESS, SampleDesc.Count=1, Usage=D3D11_USAGE_DEFAULT, BindFlags=D3D11_BIND_SHADER_RESOURCE), constructs a Texture2D, calls Util::SetResourceName on texture->resource.get(), creates the SRV with D3D11_SHADER_RESOURCE_VIEW_DESC (Format=DXGI_FORMAT_R32_FLOAT, ViewDimension=TEXTURE2D, Texture2D.MipLevels=1) via Texture2D::CreateSRV, and replace the existing inline blocks in EnsureVRSubrectTextures and EnsureExtremeStripTextures to call Core::CreateDepthIntermediate (or Core::CreateDepthIntermediate returning a unique_ptr<Texture2D>) and assign into Core::vrIntermediateDepth[i] (or the appropriate target).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@CMakeLists.txt`:
- Around line 97-102: Replace the hardcoded vcpkg triplet include path used in
target_include_directories for ${PROJECT_NAME} with the include dirs provided by
the DirectX-Headers imported target; either link the imported target
(Microsoft::DirectX-Headers) into ${PROJECT_NAME} via
target_link_libraries(${PROJECT_NAME} PRIVATE Microsoft::DirectX-Headers) or use
target_include_directories(${PROJECT_NAME} PRIVATE
$<TARGET_PROPERTY:Microsoft::DirectX-Headers,INTERFACE_INCLUDE_DIRECTORIES>) so
the include path is taken from the imported target instead of the fixed
"${CMAKE_BINARY_DIR}/vcpkg_installed/x64-windows-static-md/include/directx".
In `@src/Features/DlssEnhancer/Core.cpp`:
- Around line 659-674: The Map call for Core::vrSubrectBlendCB is unchecked and
may leave mapped.pData invalid; update the block that maps
Core::vrSubrectBlendCB so you capture the HRESULT from context->Map and wrap the
writes to BlendCB (DstOffsetX, DstOffsetY, SubWidth, SubHeight, BlendMode,
FeatherWidth, FrameIndex, SrcOffsetX, DitherStrength and the s_blendFrameIdx
increment) and the context->Unmap call inside a SUCCEEDED(hr) guard (same
pattern used at the earlier Map checks), so that you only dereference
mapped.pData and call Unmap when Map succeeds.
- Around line 436-450: StretchDRSBothEyes currently unconditionally dereferences
Core::vrRenderSBS->srv.get() when srcOverride is null; add a nullity guard so if
srcOverride is null and Core::vrRenderSBS is null or
Core::vrRenderSBS->srv.get() is null you log an error/warning (use the same
logging facility/pattern used elsewhere in this file) and return early without
calling StretchDRSToFullEye. Update the start of StretchDRSBothEyes to check
Core::vrRenderSBS and Core::vrRenderSBS->srv.get(), choose src only if non-null,
otherwise log and exit the function to avoid crashes.
- Around line 264-290: The lazy-init for vrSubrectStretch resources can leave
vrSubrectStretchCS attached while CreateBuffer/CreateSamplerState fails,
preventing retries; update the init in Core:: (the block that checks
Core::vrSubrectStretchCS and creates vrSubrectStretchCB/vrSubrectStretchSampler)
to either (A) reset Core::vrSubrectStretchCS (release/clear the shader handle)
when any Create* call fails so the next call will re-enter the init path, or (B)
change the guard to require all three handles to be null before initializing
(i.e., gate on !Core::vrSubrectStretchCS && !Core::vrSubrectStretchCB &&
!Core::vrSubrectStretchSampler); apply the same fix to the TemporalSmoothSBS
init block that touches its compute shader, constant buffer and sampler so
failed partial initialization can be retried.
- Around line 12-52: The CreateTextureFromSource helper currently forwards
srcDesc.Format directly into CreateSRV/CreateUAV which throws for typeless
formats; update CreateTextureFromSource to sanitize typeless formats before
creating views: detect common typeless enums (e.g.,
DXGI_FORMAT_R8G8B8A8_TYPELESS, R16G16_TYPELESS, R32_TYPELESS, etc.) and map them
to sane default view formats (e.g., R8G8B8A8_UNORM, R16G16_FLOAT or R32_FLOAT
respectively), store the result in a local viewFormat variable and use that for
srvDesc.Format and uavDesc.Format; keep srcDesc.Format for the texture creation
itself, and log or assert if an unmapped typeless format is encountered so
callers can supply an explicit view format if needed (refer to symbols:
CreateTextureFromSource, srcDesc.Format, srvDesc, uavDesc, CreateSRV, CreateUAV,
Texture2D).
In `@src/Features/DlssEnhancer/Preprocess.cpp`:
- Around line 70-75: GetEnhancerEncodeTexturesCS may return null after you've
already bound a constant buffer, 4 SRVs and 3 UAVs to the CS stage, so you must
unbind those resources before the early return to avoid leaking CS bindings;
either (preferred) move the GetEnhancerEncodeTexturesCS call above any CS state
setup so you only bind after successful compilation, or (alternative) before the
early return after logger::error and state->EndPerfEvent(), call the matching
cleanup calls (ID3D11DeviceContext::CSSetConstantBuffers, ::CSSetShaderResources
with null SRVs, and ::CSSetUnorderedAccessViews with null UAVs) to clear the 1
CB, 4 SRVs and 3 UAVs that were previously bound (mirror the existing cleanup
block used later in this function).
In `@src/Utils/Subrect/Subrect.cpp`:
- Around line 52-62: The function Subrect::PixelRegion UVToPixelRegion should
guard against zero-sized dimensions: add an early check in UVToPixelRegion that
if eyeWidth == 0 || eyeHeight == 0 it returns an empty/default
Subrect::PixelRegion (e.g. {}), to avoid underflow from expressions like
eyeWidth - 1; update callers such as GetLeftEyePixelRegion only if they rely on
non-empty regions, but the primary fix is the defensive early-return in
UVToPixelRegion to validate eyeWidth/eyeHeight before any arithmetic.
---
Duplicate comments:
In `@src/Features/DlssEnhancer/Preprocess.cpp`:
- Around line 39-67: The guard that checks required preprocess resources must
also validate upscaling.motionVectorCopyTexture when DLSS is selected to avoid
dereferencing a null UAV; update the existing null-check that currently tests
upscaling.upscalingDataCB, upscaling.reactiveMaskTexture, and
upscaling.transparencyCompositionMaskTexture to include
upscaling.motionVectorCopyTexture when upscaleMethod ==
Upscaling::UpscaleMethod::kDLSS, and ensure the creation of the uavs array only
dereferences upscaling.motionVectorCopyTexture->uav.get() after that check (or
substitute nullptr/fallback if DLSS is not available), referencing the symbols
upscaling.motionVectorCopyTexture, Upscaling::UpscaleMethod::kDLSS, and the uavs
array construction to locate where to modify the code.
---
Nitpick comments:
In `@CMakeLists.txt`:
- Around line 5-10: PR title exceeds the 50-character guideline: change the pull
request title to the required "type(scope): description" format and shorten it
(suggestion: "feat(vr): add dlss enhancer"); if there's an associated tracking
issue include "Implements #<issue-number>" in the title or PR description; no
code changes required inside the CMakeLists project(...) block
(CommunityShaders, VERSION 1.4.11 remain unchanged).
In `@src/Features/DlssEnhancer/Core.cpp`:
- Around line 87-103: The repeated pattern creating a typeless R32 depth texture
and R32_FLOAT SRV in EnsureVRSubrectTextures and EnsureExtremeStripTextures
should be extracted into a small helper (e.g. CreateDepthIntermediate) to avoid
duplication and make future format/bind-flag changes safe; implement a function
CreateDepthIntermediate(int w, int h, const std::string &name) that builds the
D3D11_TEXTURE2D_DESC (Width, Height, MipLevels=1, ArraySize=1,
Format=DXGI_FORMAT_R32_TYPELESS, SampleDesc.Count=1, Usage=D3D11_USAGE_DEFAULT,
BindFlags=D3D11_BIND_SHADER_RESOURCE), constructs a Texture2D, calls
Util::SetResourceName on texture->resource.get(), creates the SRV with
D3D11_SHADER_RESOURCE_VIEW_DESC (Format=DXGI_FORMAT_R32_FLOAT,
ViewDimension=TEXTURE2D, Texture2D.MipLevels=1) via Texture2D::CreateSRV, and
replace the existing inline blocks in EnsureVRSubrectTextures and
EnsureExtremeStripTextures to call Core::CreateDepthIntermediate (or
Core::CreateDepthIntermediate returning a unique_ptr<Texture2D>) and assign into
Core::vrIntermediateDepth[i] (or the appropriate target).
In `@src/Features/DlssEnhancer/Preprocess.cpp`:
- Around line 63-67: The ternary in the uavs array is dead because earlier code
guarantees upscaleMethod == Upscaling::UpscaleMethod::kDLSS; replace the
conditional with the direct DLSS UAV (use
upscaling.motionVectorCopyTexture->uav.get()) so the third element is
unambiguous, and also update related logic so per-method cache slots reflect
actual method use (inspect GetEnhancerEncodeTexturesCS which currently injects a
DLSS define unconditionally and make that define conditional on methodIndex or
adjust cache key to avoid misleading per-method caching).
In `@src/Features/DLSSperf.cpp`:
- Around line 251-303: TonemapRender_Hook::thunk currently swaps engine-owned
pointers (kmainRT.SRV, kmainCopyRT.SRV, and kmainDS.views/readOnlyViews) and
restores them only after calling func(...), which can leak if func
throws/long-jumps; wrap the swap+call+restore in an exception-safe RAII guard
(e.g., stl::scope_exit or a small local RestoreGuard) that captures references
to dlssPerf.savedKMainSRV, dlssPerf.savedKMainCopySRV,
dlssPerf.savedKMainViews[], dlssPerf.savedKMainReadOnlyViews[], kmainRT,
kmainCopyRT, and kmainDS and performs the exact restore logic on destruction so
restoration is guaranteed even if func throws; apply the same pattern to
UIPassDispatch_Hook::thunk and ensure saved* members are nulled inside the guard
after restore.
- Around line 529-640: DownscaleToKMain currently overwrites IA, VS/PS and PS
resource/sampler slot 0 without saving/restoring them; update the function to
symmetrically save and restore the remaining pipeline state it touches: capture
current input layout, vertex buffer(s), index buffer, primitive topology (e.g.,
savedInputLayout, savedVB(s), savedIB, savedTopology), the bound VS and PS
(savedVS, savedPS), and PS slot 0 SRV and sampler (savedPSSRV0, savedPSSampler0)
before calling
IASet*/VSSetShader/PSSetShader/PSSetShaderResources/PSSetSamplers, then restore
those saved objects after drawing and Release() any COM pointers you acquired;
alternatively, if you prefer not to add state plumbing, add a clear header
comment on DownscaleToKMain documenting the invariant that it must only be
called when the pipeline will be fully rebound.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 9894c526-d966-40aa-bbaf-c88933b1f887
📒 Files selected for processing (37)
CMakeLists.txtfeatures/DLSS Enhancer/Shaders/Features/DLSSENHANCER.inifeatures/DLSS Enhancer/Shaders/Features/PeripheryTemporalSmoothCS.hlslfeatures/DLSS Enhancer/Shaders/Features/SubrectBlendCS.hlslfeatures/DLSS Enhancer/Shaders/Features/SubrectStretchCS.hlslfeatures/DLSSperf/Shaders/DLSSperf/BilinearCopyPS.hlslsrc/Feature.cppsrc/Features/DLSSperf.cppsrc/Features/DLSSperf.hsrc/Features/DlssEnhancer/Bridge.cppsrc/Features/DlssEnhancer/Bridge.hsrc/Features/DlssEnhancer/Core.cppsrc/Features/DlssEnhancer/Core.hsrc/Features/DlssEnhancer/Modes.cppsrc/Features/DlssEnhancer/Ops.hsrc/Features/DlssEnhancer/Params.cppsrc/Features/DlssEnhancer/Params.hsrc/Features/DlssEnhancer/Postprocess.cppsrc/Features/DlssEnhancer/Postprocess.hsrc/Features/DlssEnhancer/Preprocess.cppsrc/Features/DlssEnhancer/Preprocess.hsrc/Features/DlssEnhancerFeature.cppsrc/Features/DlssEnhancerFeature.hsrc/Features/Upscaling.cppsrc/Features/Upscaling.hsrc/Features/Upscaling/RCAS/RCAS.cppsrc/Features/Upscaling/RCAS/RCAS.hsrc/Features/Upscaling/Streamline.cppsrc/Features/Upscaling/Streamline.hsrc/Globals.cppsrc/Globals.hsrc/Hooks.cppsrc/State.hsrc/Utils/Subrect/Subrect.cppsrc/Utils/Subrect/Subrect.hsrc/XSEPlugin.cppvcpkg.json
✅ Files skipped from review due to trivial changes (11)
- features/DLSS Enhancer/Shaders/Features/DLSSENHANCER.ini
- src/XSEPlugin.cpp
- src/Feature.cpp
- vcpkg.json
- src/State.h
- src/Features/DlssEnhancer/Postprocess.h
- src/Features/DlssEnhancer/Preprocess.h
- src/Features/Upscaling.h
- src/Features/Upscaling/RCAS/RCAS.cpp
- src/Features/DLSSperf.h
- src/Features/DlssEnhancer/Modes.cpp
🚧 Files skipped from review as they are similar to previous changes (14)
- src/Features/Upscaling/RCAS/RCAS.h
- src/Features/Upscaling/Streamline.h
- src/Features/DlssEnhancer/Bridge.h
- src/Features/DlssEnhancer/Params.cpp
- features/DLSS Enhancer/Shaders/Features/PeripheryTemporalSmoothCS.hlsl
- features/DLSS Enhancer/Shaders/Features/SubrectBlendCS.hlsl
- src/Features/DlssEnhancer/Bridge.cpp
- src/Features/DlssEnhancer/Postprocess.cpp
- src/Features/DlssEnhancer/Params.h
- features/DLSS Enhancer/Shaders/Features/SubrectStretchCS.hlsl
- src/Hooks.cpp
- src/Features/DlssEnhancerFeature.cpp
- src/Features/Upscaling.cpp
- src/Features/DlssEnhancer/Core.h
| target_include_directories( | ||
| ${PROJECT_NAME} | ||
| BEFORE | ||
| PRIVATE | ||
| "${CMAKE_BINARY_DIR}/vcpkg_installed/x64-windows-static-md/include/directx" | ||
| ) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Triplet / vcpkg install-dir references:"
rg -n -C2 'VCPKG_TARGET_TRIPLET|VCPKG_INSTALLED_DIR|vcpkg_installed|x64-windows-static-md'
echo
echo "DirectX-Headers include/target usage:"
rg -n -C3 'directx-headers|Microsoft::DirectX-Headers|include/directx'Repository: doodlum/skyrim-community-shaders
Length of output: 2132
🏁 Script executed:
cat CMakePresets.json | head -50Repository: doodlum/skyrim-community-shaders
Length of output: 1623
🏁 Script executed:
rg -n 'VCPKG_TARGET_TRIPLET|"name":\s*"' CMakePresets.json | head -30Repository: doodlum/skyrim-community-shaders
Length of output: 569
🏁 Script executed:
rg -n 'Microsoft::DirectX-Headers' --type cmakeRepository: doodlum/skyrim-community-shaders
Length of output: 129
🏁 Script executed:
cat CMakePresets.json | grep -A 5 '"inherits"'Repository: doodlum/skyrim-community-shaders
Length of output: 543
🏁 Script executed:
rg -n 'configurePresets|inherits' CMakePresets.json | head -40Repository: doodlum/skyrim-community-shaders
Length of output: 239
🏁 Script executed:
# Check if there are any documentation comments about the DirectX path
rg -B5 -A2 'CMAKE_BINARY_DIR.*vcpkg_installed.*directx' CMakeLists.txtRepository: doodlum/skyrim-community-shaders
Length of output: 333
Use the DirectX-Headers imported target instead of hardcoding the vcpkg triplet path.
Line 101 hardcodes the include path directly to the x64-windows-static-md triplet from CMakePresets.json. This couples the CMakeLists.txt to the specific triplet value, requiring manual updates if VCPKG_TARGET_TRIPLET changes or if someone uses a custom vcpkg install root. Since find_package(directx-headers CONFIG REQUIRED) already succeeds, extract the include directory from the Microsoft::DirectX-Headers imported target instead.
🛠️ Proposed fix
find_package(directx-headers CONFIG REQUIRED)
+get_target_property(
+ DIRECTX_HEADERS_INCLUDE_DIRS
+ Microsoft::DirectX-Headers
+ INTERFACE_INCLUDE_DIRECTORIES
+)
+if(NOT DIRECTX_HEADERS_INCLUDE_DIRS)
+ message(FATAL_ERROR "Microsoft::DirectX-Headers did not expose include directories")
+endif()
target_include_directories(
${PROJECT_NAME}
BEFORE
PRIVATE
- "${CMAKE_BINARY_DIR}/vcpkg_installed/x64-windows-static-md/include/directx"
+ ${DIRECTX_HEADERS_INCLUDE_DIRS}
)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@CMakeLists.txt` around lines 97 - 102, Replace the hardcoded vcpkg triplet
include path used in target_include_directories for ${PROJECT_NAME} with the
include dirs provided by the DirectX-Headers imported target; either link the
imported target (Microsoft::DirectX-Headers) into ${PROJECT_NAME} via
target_link_libraries(${PROJECT_NAME} PRIVATE Microsoft::DirectX-Headers) or use
target_include_directories(${PROJECT_NAME} PRIVATE
$<TARGET_PROPERTY:Microsoft::DirectX-Headers,INTERFACE_INCLUDE_DIRECTORIES>) so
the include path is taken from the imported target instead of the fixed
"${CMAKE_BINARY_DIR}/vcpkg_installed/x64-windows-static-md/include/directx".
| eastl::unique_ptr<Texture2D> CreateTextureFromSource(ID3D11Resource* src, uint32_t width, uint32_t height, | ||
| bool copyBindFlags, bool createSRV, bool createUAV, const char* name) | ||
| { | ||
| D3D11_TEXTURE2D_DESC srcDesc; | ||
| static_cast<ID3D11Texture2D*>(src)->GetDesc(&srcDesc); | ||
|
|
||
| D3D11_TEXTURE2D_DESC desc = {}; | ||
| desc.Width = width; | ||
| desc.Height = height; | ||
| desc.MipLevels = 1; | ||
| desc.ArraySize = 1; | ||
| desc.Format = srcDesc.Format; | ||
| desc.SampleDesc.Count = 1; | ||
| desc.Usage = D3D11_USAGE_DEFAULT; | ||
| desc.BindFlags = copyBindFlags ? srcDesc.BindFlags : (D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS); | ||
|
|
||
| auto tex = eastl::make_unique<Texture2D>(desc); | ||
|
|
||
| if (name) { | ||
| Util::SetResourceName(tex->resource.get(), name); | ||
| } | ||
|
|
||
| if (createSRV) { | ||
| D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; | ||
| srvDesc.Format = srcDesc.Format; | ||
| srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; | ||
| srvDesc.Texture2D.MostDetailedMip = 0; | ||
| srvDesc.Texture2D.MipLevels = 1; | ||
| tex->CreateSRV(srvDesc); | ||
| } | ||
|
|
||
| if (createUAV) { | ||
| D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; | ||
| uavDesc.Format = srcDesc.Format; | ||
| uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D; | ||
| uavDesc.Texture2D.MipSlice = 0; | ||
| tex->CreateUAV(uavDesc); | ||
| } | ||
|
|
||
| return tex; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check whether CreateSRV/CreateUAV on Texture2D log or otherwise signal failure.
fd -t f 'Texture2D*' -e h -e hpp -e cpp | xargs rg -n -C3 'CreateSRV|CreateUAV'Repository: doodlum/skyrim-community-shaders
Length of output: 36828
🏁 Script executed:
# Find Texture2D class definition to confirm the implementation
rg -n "class Texture2D" -A 15Repository: doodlum/skyrim-community-shaders
Length of output: 1842
🏁 Script executed:
# Check if DX::ThrowIfFailed is defined and what it does
rg -n "ThrowIfFailed" -B 2 -A 2 | head -50Repository: doodlum/skyrim-community-shaders
Length of output: 2803
🏁 Script executed:
# Look at usage of CreateTextureFromSource in DlssEnhancer/Core.cpp to see if there's error handling
rg -n "CreateTextureFromSource" src/Features/DlssEnhancer/Core.cpp -B 2 -A 5Repository: doodlum/skyrim-community-shaders
Length of output: 5307
Handle typeless source formats in texture view creation.
srcDesc.Format used verbatim in SRV/UAV creation will cause exceptions, not silent failures, when the source is typeless (e.g., R8G8B8A8_TYPELESS). The CreateSRV/CreateUAV calls use DX::ThrowIfFailed(), which throws on CreateShaderResourceView/CreateUnorderedAccessView failures; unhandled exceptions will crash rather than leave null views.
Consider either (a) asserting/logging when srcDesc.Format is typeless and requiring callers to pass an explicit view format, or (b) mapping typeless formats to a reasonable typed view format inside this helper (e.g., R8G8B8A8_TYPELESS → R8G8B8A8_UNORM).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Features/DlssEnhancer/Core.cpp` around lines 12 - 52, The
CreateTextureFromSource helper currently forwards srcDesc.Format directly into
CreateSRV/CreateUAV which throws for typeless formats; update
CreateTextureFromSource to sanitize typeless formats before creating views:
detect common typeless enums (e.g., DXGI_FORMAT_R8G8B8A8_TYPELESS,
R16G16_TYPELESS, R32_TYPELESS, etc.) and map them to sane default view formats
(e.g., R8G8B8A8_UNORM, R16G16_FLOAT or R32_FLOAT respectively), store the result
in a local viewFormat variable and use that for srvDesc.Format and
uavDesc.Format; keep srcDesc.Format for the texture creation itself, and log or
assert if an unmapped typeless format is encountered so callers can supply an
explicit view format if needed (refer to symbols: CreateTextureFromSource,
srcDesc.Format, srvDesc, uavDesc, CreateSRV, CreateUAV, Texture2D).
| if (!Core::vrSubrectStretchCS) { | ||
| Core::vrSubrectStretchCS.attach((ID3D11ComputeShader*)Util::CompileShader(L"Data/Shaders/Features/SubrectStretchCS.hlsl", {}, "cs_5_0")); | ||
|
|
||
| D3D11_BUFFER_DESC cbDesc = {}; | ||
| cbDesc.ByteWidth = 48; | ||
| cbDesc.Usage = D3D11_USAGE_DYNAMIC; | ||
| cbDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; | ||
| cbDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; | ||
| if (FAILED(globals::d3d::device->CreateBuffer(&cbDesc, nullptr, Core::vrSubrectStretchCB.put()))) { | ||
| logger::error("[DLSSENHANCER] Failed to create SubrectStretch constant buffer"); | ||
| return; | ||
| } | ||
|
|
||
| D3D11_SAMPLER_DESC sampDesc = {}; | ||
| sampDesc.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; | ||
| sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; | ||
| sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; | ||
| sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; | ||
| if (FAILED(globals::d3d::device->CreateSamplerState(&sampDesc, Core::vrSubrectStretchSampler.put()))) { | ||
| logger::error("[DLSSENHANCER] Failed to create SubrectStretch sampler"); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| if (!Core::vrSubrectStretchCS || !Core::vrSubrectStretchCB || !Core::vrSubrectStretchSampler) { | ||
| return; | ||
| } |
There was a problem hiding this comment.
Partial lazy-init can cache a broken state permanently.
If CreateBuffer or CreateSamplerState fails after vrSubrectStretchCS has been attached, the outer if (!Core::vrSubrectStretchCS) gate is no longer entered on subsequent calls, so the CB/sampler never get retried and the guard at lines 288–290 silently returns forever. The same pattern exists in TemporalSmoothSBS at lines 504–527.
Either reset vrSubrectStretchCS (and the matching temporal CS) on failure, or gate the init block on all three handles being null.
🛡️ Example fix
if (!Core::vrSubrectStretchCS) {
Core::vrSubrectStretchCS.attach((ID3D11ComputeShader*)Util::CompileShader(L"Data/Shaders/Features/SubrectStretchCS.hlsl", {}, "cs_5_0"));
D3D11_BUFFER_DESC cbDesc = {};
cbDesc.ByteWidth = 48;
cbDesc.Usage = D3D11_USAGE_DYNAMIC;
cbDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
cbDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
if (FAILED(globals::d3d::device->CreateBuffer(&cbDesc, nullptr, Core::vrSubrectStretchCB.put()))) {
logger::error("[DLSSENHANCER] Failed to create SubrectStretch constant buffer");
+ Core::vrSubrectStretchCS = nullptr;
return;
}
...
if (FAILED(globals::d3d::device->CreateSamplerState(&sampDesc, Core::vrSubrectStretchSampler.put()))) {
logger::error("[DLSSENHANCER] Failed to create SubrectStretch sampler");
+ Core::vrSubrectStretchCS = nullptr;
+ Core::vrSubrectStretchCB = nullptr;
return;
}
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Features/DlssEnhancer/Core.cpp` around lines 264 - 290, The lazy-init for
vrSubrectStretch resources can leave vrSubrectStretchCS attached while
CreateBuffer/CreateSamplerState fails, preventing retries; update the init in
Core:: (the block that checks Core::vrSubrectStretchCS and creates
vrSubrectStretchCB/vrSubrectStretchSampler) to either (A) reset
Core::vrSubrectStretchCS (release/clear the shader handle) when any Create* call
fails so the next call will re-enter the init path, or (B) change the guard to
require all three handles to be null before initializing (i.e., gate on
!Core::vrSubrectStretchCS && !Core::vrSubrectStretchCB &&
!Core::vrSubrectStretchSampler); apply the same fix to the TemporalSmoothSBS
init block that touches its compute shader, constant buffer and sampler so
failed partial initialization can be retried.
| void StretchDRSBothEyes(ID3D11UnorderedAccessView* dstUAV, uint32_t eyeWidthOut, uint32_t eyeHeightOut, | ||
| uint32_t eyeWidthIn, uint32_t eyeHeightIn, uint32_t renderW, uint32_t renderH, | ||
| ID3D11ShaderResourceView* srcOverride) | ||
| { | ||
| auto* src = srcOverride ? srcOverride : Core::vrRenderSBS->srv.get(); | ||
| for (uint32_t i = 0; i < 2; ++i) { | ||
| uint32_t dstX = (i == 1) ? eyeWidthOut : 0; | ||
| uint32_t srcX = (i == 1) ? eyeWidthIn : 0; | ||
| StretchDRSToFullEye( | ||
| src, dstUAV, | ||
| dstX, eyeWidthOut, eyeHeightOut, | ||
| srcX, renderW, renderH, | ||
| eyeWidthIn, eyeHeightIn); | ||
| } | ||
| } |
There was a problem hiding this comment.
Guard against null vrRenderSBS when no srcOverride is supplied.
Core::vrRenderSBS->srv.get() is dereferenced unconditionally if srcOverride == nullptr. If the caller forgot to invoke SnapshotSBS first, or vrRenderSBS creation failed upstream, this crashes. Add a nullity guard (log + early-return) to degrade gracefully, consistent with the rest of this file's pattern.
🛡️ Proposed fix
ID3D11ShaderResourceView* srcOverride)
{
+ if (!srcOverride && (!Core::vrRenderSBS || !Core::vrRenderSBS->srv)) {
+ logger::error("[DLSSENHANCER] StretchDRSBothEyes called before SnapshotSBS");
+ return;
+ }
auto* src = srcOverride ? srcOverride : Core::vrRenderSBS->srv.get();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Features/DlssEnhancer/Core.cpp` around lines 436 - 450,
StretchDRSBothEyes currently unconditionally dereferences
Core::vrRenderSBS->srv.get() when srcOverride is null; add a nullity guard so if
srcOverride is null and Core::vrRenderSBS is null or
Core::vrRenderSBS->srv.get() is null you log an error/warning (use the same
logging facility/pattern used elsewhere in this file) and return early without
calling StretchDRSToFullEye. Update the start of StretchDRSBothEyes to check
Core::vrRenderSBS and Core::vrRenderSBS->srv.get(), choose src only if non-null,
otherwise log and exit the function to avoid crashes.
| // Update CB | ||
| { | ||
| D3D11_MAPPED_SUBRESOURCE mapped; | ||
| context->Map(Core::vrSubrectBlendCB.get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); | ||
| BlendCB* cb = (BlendCB*)mapped.pData; | ||
| cb->DstOffsetX = dstOffsetX; | ||
| cb->DstOffsetY = dstOffsetY; | ||
| cb->SubWidth = subWidth; | ||
| cb->SubHeight = subHeight; | ||
| cb->BlendMode = (blendMode == DlssEnhancerFeature::SubrectBlendMode::kDither) ? 1 : 0; | ||
| cb->FeatherWidth = enhancer.settings.subrectFeatherWidth; | ||
| cb->FrameIndex = s_blendFrameIdx++; | ||
| cb->SrcOffsetX = srcOffsetX; | ||
| cb->DitherStrength = enhancer.settings.subrectDitherStrength; | ||
| context->Unmap(Core::vrSubrectBlendCB.get(), 0); | ||
| } |
There was a problem hiding this comment.
Unchecked Map return value — potential crash on failure.
Unlike the SUCCEEDED(context->Map(...)) checks at lines 293 and 534, this call ignores the HRESULT. If Map fails, mapped.pData is undefined and the subsequent cb->... writes dereference garbage. Guard the writes with SUCCEEDED(...) and only Unmap on success.
🛡️ Proposed fix
- {
- D3D11_MAPPED_SUBRESOURCE mapped;
- context->Map(Core::vrSubrectBlendCB.get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
- BlendCB* cb = (BlendCB*)mapped.pData;
- cb->DstOffsetX = dstOffsetX;
- ...
- cb->DitherStrength = enhancer.settings.subrectDitherStrength;
- context->Unmap(Core::vrSubrectBlendCB.get(), 0);
- }
+ {
+ D3D11_MAPPED_SUBRESOURCE mapped{};
+ if (FAILED(context->Map(Core::vrSubrectBlendCB.get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped))) {
+ logger::error("[DLSSENHANCER] Failed to map SubrectBlend CB");
+ return;
+ }
+ BlendCB* cb = (BlendCB*)mapped.pData;
+ cb->DstOffsetX = dstOffsetX;
+ ...
+ cb->DitherStrength = enhancer.settings.subrectDitherStrength;
+ context->Unmap(Core::vrSubrectBlendCB.get(), 0);
+ }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Features/DlssEnhancer/Core.cpp` around lines 659 - 674, The Map call for
Core::vrSubrectBlendCB is unchecked and may leave mapped.pData invalid; update
the block that maps Core::vrSubrectBlendCB so you capture the HRESULT from
context->Map and wrap the writes to BlendCB (DstOffsetX, DstOffsetY, SubWidth,
SubHeight, BlendMode, FeatherWidth, FrameIndex, SrcOffsetX, DitherStrength and
the s_blendFrameIdx increment) and the context->Unmap call inside a
SUCCEEDED(hr) guard (same pattern used at the earlier Map checks), so that you
only dereference mapped.pData and call Unmap when Map succeeds.
| ID3D11ComputeShader* cs = GetEnhancerEncodeTexturesCS(upscaling, upscaleMethod); | ||
| if (!cs) { | ||
| state->EndPerfEvent(); | ||
| logger::error("[DLSSENHANCER] Failed to get encode compute shader"); | ||
| return false; | ||
| } |
There was a problem hiding this comment.
Leaked CS bindings on shader-compile failure path.
If GetEnhancerEncodeTexturesCS returns null, this early-returns after already having bound the constant buffer (Line 58), 4 SRVs (Line 61), and 3 UAVs (Line 68) to the compute stage — none of which get unbound. Subsequent compute passes will observe stale views bound to CS, and D3D debug layer will likely warn on hazards. Run the same null-cleanup block (Lines 80-86) before returning, or restructure to compile the CS before any binding.
Suggested fix
ID3D11ComputeShader* cs = GetEnhancerEncodeTexturesCS(upscaling, upscaleMethod);
if (!cs) {
+ ID3D11ShaderResourceView* nullViews[4] = {};
+ context->CSSetShaderResources(0, ARRAYSIZE(nullViews), nullViews);
+ ID3D11UnorderedAccessView* nullUavs[3] = {};
+ context->CSSetUnorderedAccessViews(0, ARRAYSIZE(nullUavs), nullUavs, nullptr);
+ ID3D11Buffer* nullBuffer = nullptr;
+ context->CSSetConstantBuffers(0, 1, &nullBuffer);
state->EndPerfEvent();
logger::error("[DLSSENHANCER] Failed to get encode compute shader");
return false;
}Or hoist the GetEnhancerEncodeTexturesCS call above Line 50 so no state is set up before compilation is known to have succeeded (also avoids paying binding cost on repeated failure).
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ID3D11ComputeShader* cs = GetEnhancerEncodeTexturesCS(upscaling, upscaleMethod); | |
| if (!cs) { | |
| state->EndPerfEvent(); | |
| logger::error("[DLSSENHANCER] Failed to get encode compute shader"); | |
| return false; | |
| } | |
| ID3D11ComputeShader* cs = GetEnhancerEncodeTexturesCS(upscaling, upscaleMethod); | |
| if (!cs) { | |
| ID3D11ShaderResourceView* nullViews[4] = {}; | |
| context->CSSetShaderResources(0, ARRAYSIZE(nullViews), nullViews); | |
| ID3D11UnorderedAccessView* nullUavs[3] = {}; | |
| context->CSSetUnorderedAccessViews(0, ARRAYSIZE(nullUavs), nullUavs, nullptr); | |
| ID3D11Buffer* nullBuffer = nullptr; | |
| context->CSSetConstantBuffers(0, 1, &nullBuffer); | |
| state->EndPerfEvent(); | |
| logger::error("[DLSSENHANCER] Failed to get encode compute shader"); | |
| return false; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Features/DlssEnhancer/Preprocess.cpp` around lines 70 - 75,
GetEnhancerEncodeTexturesCS may return null after you've already bound a
constant buffer, 4 SRVs and 3 UAVs to the CS stage, so you must unbind those
resources before the early return to avoid leaking CS bindings; either
(preferred) move the GetEnhancerEncodeTexturesCS call above any CS state setup
so you only bind after successful compilation, or (alternative) before the early
return after logger::error and state->EndPerfEvent(), call the matching cleanup
calls (ID3D11DeviceContext::CSSetConstantBuffers, ::CSSetShaderResources with
null SRVs, and ::CSSetUnorderedAccessViews with null UAVs) to clear the 1 CB, 4
SRVs and 3 UAVs that were previously bound (mirror the existing cleanup block
used later in this function).
| Subrect::PixelRegion UVToPixelRegion(const Subrect::UVRegion& uv, uint32_t eyeWidth, uint32_t eyeHeight) | ||
| { | ||
| Subrect::PixelRegion result; | ||
| result.x = std::min<uint32_t>(eyeWidth - 1, static_cast<uint32_t>(uv.x * eyeWidth)); | ||
| result.y = std::min<uint32_t>(eyeHeight - 1, static_cast<uint32_t>(uv.y * eyeHeight)); | ||
| result.w = std::max<uint32_t>(1, static_cast<uint32_t>(uv.w * eyeWidth)); | ||
| result.h = std::max<uint32_t>(1, static_cast<uint32_t>(uv.h * eyeHeight)); | ||
| result.w = std::min<uint32_t>(result.w, eyeWidth - result.x); | ||
| result.h = std::min<uint32_t>(result.h, eyeHeight - result.y); | ||
| return result; | ||
| } |
There was a problem hiding this comment.
Guard against zero-size eye dimensions to prevent uint32_t underflow.
If eyeWidth == 0 or eyeHeight == 0 (e.g. fullTextureWidth == 1 hits fullTextureWidth / 2 == 0 in GetLeftEyePixelRegion), eyeWidth - 1 / eyeHeight - 1 wraps to 0xFFFFFFFF and the clamp on the next two lines then produces garbage. Callers currently pass VR-sized textures so this is a defensive nit, but since the coding guidelines ask for "user input validation to prevent crashes from malformed configurations", an early if (eyeWidth == 0 || eyeHeight == 0) return {}; would make the helper robust.
As per coding guidelines: "Include proper resource management and graceful degradation for DirectX 11 resources and user input validation to prevent crashes from malformed configurations".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Utils/Subrect/Subrect.cpp` around lines 52 - 62, The function
Subrect::PixelRegion UVToPixelRegion should guard against zero-sized dimensions:
add an early check in UVToPixelRegion that if eyeWidth == 0 || eyeHeight == 0 it
returns an empty/default Subrect::PixelRegion (e.g. {}), to avoid underflow from
expressions like eyeWidth - 1; update callers such as GetLeftEyePixelRegion only
if they rely on non-empty regions, but the primary fix is the defensive
early-return in UVToPixelRegion to validate eyeWidth/eyeHeight before any
arithmetic.
d6b1b58 to
d3c2bd7
Compare
Automated formatting by clang-format, prettier, and other hooks. See https://pre-commit.ci for details.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
src/Utils/Subrect/Subrect.cpp (1)
52-60:⚠️ Potential issue | 🟡 MinorGuard zero-sized eye dimensions before subtracting.
eyeWidth - 1andeyeHeight - 1underflow when either dimension is zero. This was already flagged previously; the helper still needs the early return.Proposed fix
Subrect::PixelRegion UVToPixelRegion(const Subrect::UVRegion& uv, uint32_t eyeWidth, uint32_t eyeHeight) { + if (eyeWidth == 0 || eyeHeight == 0) { + return {}; + } + Subrect::PixelRegion result; result.x = std::min<uint32_t>(eyeWidth - 1, static_cast<uint32_t>(uv.x * eyeWidth));Read-only verification:
#!/bin/bash # Confirm all current callers and whether any can pass dimensions derived from division. rg -n -C3 'UVToPixelRegion\s*\(|GetLeftEyePixelRegion\s*\(|GetStereoPixelRegions\s*\(' --type=cpp --type=h --type=hppAs per coding guidelines: “Include proper resource management and graceful degradation for DirectX 11 resources and user input validation to prevent crashes from malformed configurations”.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Utils/Subrect/Subrect.cpp` around lines 52 - 60, The function UVToPixelRegion can underflow when eyeWidth or eyeHeight is zero; add an early guard at the top of UVToPixelRegion that checks if eyeWidth == 0 || eyeHeight == 0 and immediately returns a PixelRegion with x=0,y=0,w=0,h=0 (or otherwise documented empty region) to avoid the subtraction/underflow; update callers such as GetLeftEyePixelRegion/GetStereoPixelRegions if they expect non-empty regions to handle this empty-region return appropriately.
🧹 Nitpick comments (1)
src/Utils/Subrect/Subrect.h (1)
1-1: PR title suggestion.The current title is descriptive, but it exceeds the 50-character guideline and uses an uppercase scope. Consider:
feat(vr): add dlss enhancer. If this implements a tracked feature, addImplements #<issue>.As per coding guidelines: “Format: type(scope): description” and “Length: 50 characters limit for title, 72 for body”.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Utils/Subrect/Subrect.h` at line 1, Update the PR title to follow the repository commit/PR convention "type(scope): description" with a lowercase scope, keep the title under 50 characters, and add "Implements #<issue>" in the body if this implements a tracked issue; e.g., replace the current title with something like "feat(vr): add dlss enhancer" and ensure the PR body fits the 72-character line limit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/Features/DlssEnhancerFeature.h`:
- Around line 31-32: The header DlssEnhancerFeature.h uses std::clamp and
std::min but doesn't include <algorithm>; add `#include` <algorithm> to the top of
DlssEnhancerFeature.h so the use of std::clamp (around the clamp call near line
130) and std::min (the comparisons around lines 141–145) are valid without
relying on transitive includes from Feature.h or Subrect.h.
- Around line 137-139: The hook installation currently checks
settings.enablePerf directly and must instead be gated by the parent feature's
latched enable state and runtime support; update the BootSequence() hook-install
condition that calls InstallRenderTargetSizeHook() to use the DLSS Enhancer's
guarded API (e.g., call ShouldEnablePerfAtBoot() or IsPerfEnabled() after
LatchPerfEnabled()) so the hook is only installed when the parent feature is
enabledAtBoot and runtime-supported; also audit callers that read
settings.enablePerf and change them to query the guarded method
(IsPerfEnabled()/ShouldEnablePerfAtBoot()) instead.
In `@src/Utils/Subrect/Subrect.h`:
- Around line 1-3: The header Subrect.h is not self-contained: it uses uint32_t,
std::string, json, and DX11 interface pointer types but only includes <vector>;
update Subrect.h to include the proper headers and/or forward-declare types so
it compiles standalone—specifically add <cstdint> (or <stdint.h>) for uint32_t,
<string> for std::string, include or forward-declare nlohmann::json (or add a
local using json = nlohmann::json;) for json usage, and forward-declare the
required DX11 COM interfaces (e.g., ID3D11Device, ID3D11Texture2D, etc.) or
include the appropriate DirectX headers where those pointer types are used;
ensure the changes reference the existing Subrect.h declarations so the header
becomes self-contained without relying on transitive includes.
---
Duplicate comments:
In `@src/Utils/Subrect/Subrect.cpp`:
- Around line 52-60: The function UVToPixelRegion can underflow when eyeWidth or
eyeHeight is zero; add an early guard at the top of UVToPixelRegion that checks
if eyeWidth == 0 || eyeHeight == 0 and immediately returns a PixelRegion with
x=0,y=0,w=0,h=0 (or otherwise documented empty region) to avoid the
subtraction/underflow; update callers such as
GetLeftEyePixelRegion/GetStereoPixelRegions if they expect non-empty regions to
handle this empty-region return appropriately.
---
Nitpick comments:
In `@src/Utils/Subrect/Subrect.h`:
- Line 1: Update the PR title to follow the repository commit/PR convention
"type(scope): description" with a lowercase scope, keep the title under 50
characters, and add "Implements #<issue>" in the body if this implements a
tracked issue; e.g., replace the current title with something like "feat(vr):
add dlss enhancer" and ensure the PR body fits the 72-character line limit.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: abe668a3-62b1-4d67-bc10-858a5c3c5788
📒 Files selected for processing (3)
src/Features/DlssEnhancerFeature.hsrc/Utils/Subrect/Subrect.cppsrc/Utils/Subrect/Subrect.h
| #include "Feature.h" | ||
| #include "Utils/Subrect/Subrect.h" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check whether DlssEnhancerFeature.h currently gets <algorithm> only transitively.
rg -n -C2 '#include\s+<algorithm>|DlssEnhancerFeature\.h|std::clamp|std::min' --type=cpp --type=h --type=hppRepository: doodlum/skyrim-community-shaders
Length of output: 103
🏁 Script executed:
#!/bin/bash
# Find <algorithm> includes and std::clamp/std::min usage with correct file type flags
rg -n -C2 '#include\s+<algorithm>|std::clamp|std::min' --type cpp --type h 2>/dev/null | head -100Repository: doodlum/skyrim-community-shaders
Length of output: 5905
🏁 Script executed:
#!/bin/bash
# Read DlssEnhancerFeature.h to check includes and usage
find . -name "DlssEnhancerFeature.h" -type fRepository: doodlum/skyrim-community-shaders
Length of output: 112
🏁 Script executed:
#!/bin/bash
# Check Feature.h to see if it includes <algorithm>
find . -name "Feature.h" -type fRepository: doodlum/skyrim-community-shaders
Length of output: 91
🏁 Script executed:
#!/bin/bash
cat -n ./src/Features/DlssEnhancerFeature.h | head -150Repository: doodlum/skyrim-community-shaders
Length of output: 7656
🏁 Script executed:
#!/bin/bash
cat -n ./src/Feature.h | head -50Repository: doodlum/skyrim-community-shaders
Length of output: 1958
🏁 Script executed:
#!/bin/bash
cat -n ./src/Utils/Subrect/Subrect.h | head -30Repository: doodlum/skyrim-community-shaders
Length of output: 635
Include <algorithm> for std::clamp and std::min usage.
This header uses std::clamp (line 130) and std::min (lines 141-145) without directly including <algorithm>. Avoid relying on transitive includes from Feature.h or Subrect.h.
Proposed fix
`#include` "Feature.h"
`#include` "Utils/Subrect/Subrect.h"
+
+#include <algorithm>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| #include "Feature.h" | |
| #include "Utils/Subrect/Subrect.h" | |
| `#include` "Feature.h" | |
| `#include` "Utils/Subrect/Subrect.h" | |
| `#include` <algorithm> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Features/DlssEnhancerFeature.h` around lines 31 - 32, The header
DlssEnhancerFeature.h uses std::clamp and std::min but doesn't include
<algorithm>; add `#include` <algorithm> to the top of DlssEnhancerFeature.h so the
use of std::clamp (around the clamp call near line 130) and std::min (the
comparisons around lines 141–145) are valid without relying on transitive
includes from Feature.h or Subrect.h.
| // DLSSperf sub-feature: latched at first boot, change requires restart | ||
| bool IsPerfEnabled() const { return perfEnabledAtBoot; } | ||
| void LatchPerfEnabled() { perfEnabledAtBoot = (settings.enablePerf != 0); } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find DLSSperf hook installation gates and confirm they include the parent enabled/runtime checks.
rg -n -C5 'InstallRenderTargetSizeHook|LatchPerfEnabled|settings\.enablePerf|IsPerfEnabled|ShouldEnablePerfAtBoot' --type=cpp --type=h --type=hppRepository: doodlum/skyrim-community-shaders
Length of output: 103
🏁 Script executed:
#!/bin/bash
# Search for DLSSperf-related code with correct syntax
rg -n -C5 'LatchPerfEnabled|IsPerfEnabled|InstallRenderTargetSizeHook|ShouldEnablePerfAtBoot' --type c --type hRepository: doodlum/skyrim-community-shaders
Length of output: 2080
🏁 Script executed:
#!/bin/bash
# Find the DlssEnhancerFeature header file
fd -e h -e hpp 'DlssEnhancer' -x cat {} -nRepository: doodlum/skyrim-community-shaders
Length of output: 8315
🏁 Script executed:
#!/bin/bash
# Look for Bridge.cpp and hook installation code
fd 'Bridge' --type f -x head -50 {}Repository: doodlum/skyrim-community-shaders
Length of output: 3333
🏁 Script executed:
#!/bin/bash
# Check IsRuntimeSupported() implementation
rg -n 'IsRuntimeSupported' src/Features/DlssEnhancerFeature.h -A 3Repository: doodlum/skyrim-community-shaders
Length of output: 381
🏁 Script executed:
#!/bin/bash
# Verify InstallRenderTargetSizeHook is only called from Bridge.cpp
rg -n 'InstallRenderTargetSizeHook' --type c --type hRepository: doodlum/skyrim-community-shaders
Length of output: 139
🏁 Script executed:
#!/bin/bash
# Find IsRuntimeSupported implementation
rg -n 'bool.*IsRuntimeSupported' --type c -A 5Repository: doodlum/skyrim-community-shaders
Length of output: 493
🏁 Script executed:
#!/bin/bash
# Find InstallRenderTargetSizeHook implementation
rg -n 'InstallRenderTargetSizeHook' --type c -A 10Repository: doodlum/skyrim-community-shaders
Length of output: 853
🏁 Script executed:
#!/bin/bash
# Search for cpp files containing DlssEnhancerFeature or DLSSperf implementations
fd -e cpp | grep -i 'dlss\|perf' | head -20Repository: doodlum/skyrim-community-shaders
Length of output: 521
🏁 Script executed:
#!/bin/bash
# Search in Feature-related cpp files for IsRuntimeSupported implementation
rg -n 'IsRuntimeSupported.*{' --type cRepository: doodlum/skyrim-community-shaders
Length of output: 181
🏁 Script executed:
#!/bin/bash
# Search for IsRuntimeSupported implementation in cpp files
rg -n 'IsRuntimeSupported' src/Features/DlssEnhancerFeature.cpp -A 5Repository: doodlum/skyrim-community-shaders
Length of output: 522
🏁 Script executed:
#!/bin/bash
# Also check Bridge.cpp for any additional context around hook installation
rg -n 'BootSequence\|InstallRenderTargetSizeHook' src/Features/DlssEnhancer/Bridge.cpp -B 5 -A 10Repository: doodlum/skyrim-community-shaders
Length of output: 58
🏁 Script executed:
#!/bin/bash
# Find BootSequence in Bridge.cpp
cat src/Features/DlssEnhancer/Bridge.cpp | head -100Repository: doodlum/skyrim-community-shaders
Length of output: 2403
🏁 Script executed:
#!/bin/bash
# Check what line BootSequence is at in Bridge.cpp
rg -n 'BootSequence' src/Features/DlssEnhancer/Bridge.cppRepository: doodlum/skyrim-community-shaders
Length of output: 120
Gate DLSSperf hook installation on the parent feature enable state.
BootSequence() in Bridge.cpp installs InstallRenderTargetSizeHook() based only on settings.enablePerf without checking enabledAtBoot. This means disabling the DLSS Enhancer feature will not prevent DLSSperf from shrinking engine render targets. The hook installation condition (line 40) should also verify that the parent feature is enabled and runtime-supported before proceeding.
Suggested API-side guard
// DLSSperf sub-feature: latched at first boot, change requires restart
bool IsPerfEnabled() const { return perfEnabledAtBoot; }
- void LatchPerfEnabled() { perfEnabledAtBoot = (settings.enablePerf != 0); }
+ bool ShouldEnablePerfAtBoot() const { return enabledAtBoot && IsRuntimeSupported() && settings.enablePerf != 0; }
+ void LatchPerfEnabled() { perfEnabledAtBoot = ShouldEnablePerfAtBoot(); }In Bridge.cpp's BootSequence(), use the guarded method:
- if (!globals::features::dlssPerf.IsHookActive() && enhancer.settings.enablePerf) {
+ if (!globals::features::dlssPerf.IsHookActive() && enhancer.ShouldEnablePerfAtBoot()) {Callers that install the hook should use ShouldEnablePerfAtBoot() or IsPerfEnabled() after latching, rather than reading settings.enablePerf directly.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Features/DlssEnhancerFeature.h` around lines 137 - 139, The hook
installation currently checks settings.enablePerf directly and must instead be
gated by the parent feature's latched enable state and runtime support; update
the BootSequence() hook-install condition that calls
InstallRenderTargetSizeHook() to use the DLSS Enhancer's guarded API (e.g., call
ShouldEnablePerfAtBoot() or IsPerfEnabled() after LatchPerfEnabled()) so the
hook is only installed when the parent feature is enabledAtBoot and
runtime-supported; also audit callers that read settings.enablePerf and change
them to query the guarded method (IsPerfEnabled()/ShouldEnablePerfAtBoot())
instead.
| #pragma once | ||
|
|
||
| #include <vector> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find direct includes of Subrect.h and nearby json alias definitions.
rg -n -C3 '#include\s+"Utils/Subrect/Subrect\.h"|using\s+json|typedef\s+.*json' --type=cpp --type=h --type=hppRepository: doodlum/skyrim-community-shaders
Length of output: 103
🏁 Script executed:
# Read the full Subrect.h header to see current includes and type usage
cat -n src/Utils/Subrect/Subrect.hRepository: doodlum/skyrim-community-shaders
Length of output: 1866
🏁 Script executed:
# Search for json alias/typedef/using definitions in the codebase
rg -n 'using\s+json|typedef\s+.*json' --type=c --type=hRepository: doodlum/skyrim-community-shaders
Length of output: 625
🏁 Script executed:
# Find files that include Subrect.h and check if they work
rg -l 'Subrect\.h' --type=c --type=h | head -10Repository: doodlum/skyrim-community-shaders
Length of output: 110
🏁 Script executed:
# Check for nlohmann or json includes in the codebase to understand json definition
rg -n '#include.*json' --type=c --type=h | head -20Repository: doodlum/skyrim-community-shaders
Length of output: 851
🏁 Script executed:
# Check DlssEnhancerFeature.h to see what it includes (explaining why current code compiles)
cat -n src/Features/DlssEnhancerFeature.h | head -50Repository: doodlum/skyrim-community-shaders
Length of output: 2893
🏁 Script executed:
# Check if PCH.h is included and brings in all necessary types
rg -n '#include.*PCH|#include.*pch' --type=c --type=h src/Features/DlssEnhancerFeature.hRepository: doodlum/skyrim-community-shaders
Length of output: 58
🏁 Script executed:
# Search for D3D11 forward declarations or includes across the codebase
rg -n 'ID3D11ShaderResourceView|ID3D11Texture2D' --type=c --type=h | grep -E '(forward|declare|include)' | head -10Repository: doodlum/skyrim-community-shaders
Length of output: 58
Make this header self-contained.
Subrect.h uses uint32_t, std::string, json, and DX11 interface pointer types but only includes <vector>. Missing includes and forward declarations can cause compilation failures if this header is included in translation units without the same PCH/transitive include chain.
Proposed include/forward-declaration fix
`#pragma` once
+#include <cstdint>
+#include <string>
`#include` <vector>
+#include <nlohmann/json.hpp>
+
+struct ID3D11ShaderResourceView;
+struct ID3D11Texture2D;Alternatively, add a local using json = nlohmann::json; declaration after the includes if you prefer not to expose the external dependency directly.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| #pragma once | |
| #include <vector> | |
| `#pragma` once | |
| `#include` <cstdint> | |
| `#include` <string> | |
| `#include` <vector> | |
| `#include` <nlohmann/json.hpp> | |
| struct ID3D11ShaderResourceView; | |
| struct ID3D11Texture2D; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Utils/Subrect/Subrect.h` around lines 1 - 3, The header Subrect.h is not
self-contained: it uses uint32_t, std::string, json, and DX11 interface pointer
types but only includes <vector>; update Subrect.h to include the proper headers
and/or forward-declare types so it compiles standalone—specifically add
<cstdint> (or <stdint.h>) for uint32_t, <string> for std::string, include or
forward-declare nlohmann::json (or add a local using json = nlohmann::json;) for
json usage, and forward-declare the required DX11 COM interfaces (e.g.,
ID3D11Device, ID3D11Texture2D, etc.) or include the appropriate DirectX headers
where those pointer types are used; ensure the changes reference the existing
Subrect.h declarations so the header becomes self-contained without relying on
transitive includes.
…P-B) First slice of the DlssEnhancer foveated-DLSS framework, decomposed from community-shaders#2096. These four modules form the leaf interface layer: - Bridge.h/cpp — single point of contact for Upscaling/Streamline. Settings forwarding, route activity check, boot latches, mvec scale. Drops the original PR's DLSSperf install path (PR-2 owns DLSSperf). Drops the kExtreme branch in ComputeMvecScale (kExtreme deferred). - Params.h/cpp — VRDlssParams::Resolve(). Builds the per-frame parameter block for Mode dispatchers from current global state. MVP-B drops the DLSSperf-aware DisplayRes/RenderRes split and testTexture rerouting; that's a PR-2 + PR-3 integration follow-up. Migrated to PR-1's Util::Subrect API: GetUV() == primary/left, GetRightEyeUV() == mirrored right. - Preprocess.h/cpp — DlssEnhancer::Preprocess::EncodeUpscalingTextures. Mirrors Upscaling's encode pass with a DLSS-specific shader define, invoked only when the DlssEnhancer route runs. Lift-as-is. - Postprocess.h/cpp — DlssEnhancer::Postprocess::ApplyDlssSharpening. RCAS sharpen pass for the route. Drops the DLSSperf-aware testTexture variant (deferred to PR-2 + PR-3 integration). These files reference DlssEnhancerFeature, Core, Modes, Ops which haven't landed yet — won't compile in isolation. Build gate is at the end of the lift sequence. Decomposed from community-shaders#2096 (PR-3 MVP-B of 4); see DLSS-PR-PLAN.md. Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Lifts Core.h, Core.cpp, and Ops.h from community-shaders#2096 with the MVP-B cuts applied surgically: Removed (deferred to PR-3b): - vrExtremeStrip* fields and EnsureExtremeStripTextures (kExtreme mode) - vrTemporalHistory[2], EnsureTemporalResources, TemporalSmoothSBS and the periphery temporal-smooth CS resources - vrSubrectBlendCS, BlendCB, blend-feather/dither dispatch — replaced by a plain CopySubresourceRegion in Ops::BlendSubrectToOutput - ExecuteExtremeMode private declaration - ClearResources/ClearShaderCache references to all of the above Migrated for PR-1 compatibility: - Subrect:: → Util::Subrect:: (UVRegion field types, ComputeSubrectUVHash parameter) - #include path Utils/Subrect/Subrect.h → Utils/Subrect.h Bot-flagged Major bug fixed during the lift: - CodeRabbit Major (Core.cpp:178-224 in original PR): the per-eye inputs path failed to reapply the HMD hidden-area clear, letting garbage from the HMD's optical mask bleed into DLSS history. Fixed in Core::PreparePerEyeInputs by calling Upscaling::ClearHMDMask per-eye after the SBS->per-eye CopySubresourceRegion. Decomposed from community-shaders#2096 (PR-3 MVP-B of 4); see DLSS-PR-PLAN.md. Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Lifts Modes.cpp with cuts and the bot-flagged Major bug fixed: Removed (deferred to PR-3b): - ExecuteExtremeMode body (~70 LOC, asymmetric strip mode) - kExtreme switch arm in router ExecuteVRDlssCore - Periphery temporal-smooth blocks in both Default and Faster mode bodies (depended on Core's removed EnsureTemporalResources + TemporalSmoothSBS) Migrated for PR-1 compatibility: - Subrect:: -> Util::Subrect:: (3 sites in mode bodies) - Added #include for Utils/Subrect.h - BlendSubrectToOutput callsites updated to MVP-B signature (drops dstUAV arg now that the function is plain CopySubresourceRegion) Bot-flagged Major bug fixed (CodeRabbit Major @ Modes.cpp:80,146,207 of original PR): - Per-eye subrect dimensions (subInW/H/subOutW/H) were computed once outside the eye loop using p.leftUV.w/h, so the right eye used left-eye dimensions even when rightUV differed. Moved inside the per-eye loop using eyeUVs[i]->w/h. Allocator calls (EnsureVRSubrectTextures, EnsureFasterOutputTextures) still use left-eye dimensions for the shared texture set, with a comment citing PR-1's auto-mirrored stereo Subrect (leftUV.w == rightUV.w) as the constraint that keeps this safe in MVP-B. Also fixes Postprocess.cpp's sharpness lookup to read from Upscaling::Settings::sharpnessDLSS directly (the PR called GetActiveSharpnessDLSS() which doesn't exist on dev — that helper came in via the deferred per-route override layer). Output: Modes.cpp 214 LOC (was 276). Decomposed from community-shaders#2096 (PR-3 MVP-B of 4); see DLSS-PR-PLAN.md. Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The Feature class with UI, plus the one compute shader MVP-B keeps. Lifted DlssEnhancerFeature with cuts: - Removed enums: SubrectBlendMode, PeripheryAAMode (entirely) - Removed DlssMode::kExtreme entry (kDefault, kFaster only) - Removed Settings fields: peripheryAAMode, peripheryTemporalAlpha, subrectBlendMode, subrectFeatherWidth, subrectDitherStrength, peripheryBlurRadius, enablePerf - Removed accessors: GetPeripheryAAMode, GetSubrectBlendMode, IsPerfEnabled, LatchPerfEnabled, perfEnabledAtBoot - Removed ImGui sections for all of the above + DLSSperf hook status - ClampPresetToMode keeps Faster constraints, drops Extreme branch Kept (MVP-B mode set requires these): - StretchMode (all 3: Bilinear/Point/GaussianBlur — used by SubrectStretchCS for the periphery stretch step) - SharpenMode (all entries; Postprocess uses kNone vs others) - subrectController (Util::Subrect::Controller, PR-1 stereo API) - IsActive/IsLoaded/LatchEnabled/LatchQualityMode and the boot-time quality snapshot + GetActive*** accessors Migrated for PR-1: - #include path Utils/Subrect/Subrect.h -> Utils/Subrect.h - Subrect::Controller -> Util::Subrect::Controller - subrectController.GetLeftEyeUV() -> subrectController.GetUV() (PR-1's API: GetUV() doubles as left-eye in stereo mode) PostPostLoad opts the controller into stereo mode and seeds a small preset menu for Default/Faster: Full Eye [0, 0, 1.0, 1.0 ] Center 75% (foveal) [0.125, 0.125, 0.75, 0.75] Center 50% (foveal) [0.25, 0.25, 0.5, 0.5 ] For symmetric crops centered at x=0.5, the auto-mirror produces the same UV as the seed — left==right by construction. GetShortName returns "DLSSENHANCER" (uppercase) to match the features/DLSS Enhancer/Shaders/Features/DLSSENHANCER.ini filename; FindFeatureByShortName + version-issue lookup are filename-keyed. Also lifted as-is: - features/DLSS Enhancer/Shaders/Features/SubrectStretchCS.hlsl (3-mode stretch: Bilinear / Point / GaussianBlur 3x3) - features/DLSS Enhancer/Shaders/Features/DLSSENHANCER.ini The SubrectBlendCS shader is intentionally NOT lifted — the periphery blend collapses to a plain CopySubresourceRegion in Ops::BlendSubrectToOutput, so the shader (and its bot-flagged feather edge bug) is deferred to PR-3b. Sizes: DlssEnhancerFeature.h 128 LOC (was 163), .cpp 312 (was 405). References to globals::features::dlssEnhancer become resolvable once the next commit registers the instance. Decomposed from community-shaders#2096 (PR-3 MVP-B of 4); see DLSS-PR-PLAN.md. Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The framework lifted in the previous commits is now reachable. Globals: - DlssEnhancerFeature forward decl + extern + instance - Feature::GetFeatureList() registry add so settings load/save and draw flow through the standard Feature iteration Upscaling.cpp short-circuit: - DlssEnhancer::Bridge::IsRouteActive() at top of the kDLSS branch in Upscale(). Invokes Preprocess::EncodeUpscalingTextures + Core:: ExecuteVRDlssCore. On any failure falls through to dev's standard streamline.Upscale so users always get DLSS output, with a one-shot warn log when fallback fires. - Sharpening dispatch routes through Postprocess::ApplyDlssSharpening when the route is active; dev's ApplySharpening otherwise. Hooks.cpp: - BSShaderRenderTargets::Create::thunk calls DlssEnhancer::Bridge:: BootSequence() after globals::ReInit + State::Setup. Latches enabledAtBoot + qualityModeAtBoot before the first frame. Two follow-up fixes required during the wire: - Streamline::EvaluateDLSS signature drift: dev takes 11 args (...extentIn, extentOut, outputWidth); the PR's calls passed 12 (extra outputHeight). Dropped the trailing height at all 3 callsites in Modes.cpp. - Core.cpp had one stale reference to the removed Settings field peripheryBlurRadius. Replaced with a fixed 1.0f for the GaussianBlur stretch path; per-user blur radius deferred to PR-3b. Build status: plugin C++ compile passes (MSBuild ClCompile exit 0). FFX shader-permutation crash on this machine still blocks full link (environmental, same as PR-2; reproduces on clean upstream/dev). Decomposed from community-shaders#2096 (PR-3 MVP-B of 4); see DLSS-PR-PLAN.md. Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds an opt-in stereo extension to the screenshot's subrect controller
so stereo-aware consumers (the DLSS Enhancer subrect framework) can
track a mirrored right-eye UV alongside the existing primary UV without
forking the util. Mono callers (ScreenshotFeature) are unaffected:
SaveSettings emits bit-identical JSON, LoadSettings round-trips the
legacy schema, and the DrawEditor signature is unchanged.
New API:
- SetStereoEnabled(bool) / IsStereoEnabled()
- GetRightEyeUV() — folds onto primary UV in mono mode
- GetStereoPixelRegions(fullW, fullH) — resolves both eyes against
per-eye half-width for SBS textures
- Preset gains an optional rightUV{} field
In stereo mode, drag/slider edits auto-mirror the primary UV to the
right eye around x=0.5 (HMD nose-side overlap symmetry). LoadSettings
mirrors left → right only for the legacy upgrade case (CropX/Y/W/H
present, no CropRight*), so a seeded preset's explicit right-eye UV
survives a fresh load.
The header is now self-contained (forward-declares D3D11 types,
includes nlohmann/json), so a test target can compile it without the
plugin PCH.
Adds a C++ unit test target (tests/cpp) covering mono back-compat,
stereo round-trip, legacy upgrade mirror, and per-eye pixel-region
math. 9 test cases, 33 assertions. Reuses Catch2 via FetchContent
dedup with tests/shaders. New CMake target run_cpp_tests.
Decomposed from community-shaders#2096 (PR-1 of 4); see DLSS-PR-PLAN.md.
Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds an opt-in stereo extension to the screenshot's subrect controller
so stereo-aware consumers (the DLSS Enhancer subrect framework) can
track a mirrored right-eye UV alongside the existing primary UV without
forking the util. Mono callers (ScreenshotFeature) are unaffected:
SaveSettings emits bit-identical JSON, LoadSettings round-trips the
legacy schema, and the DrawEditor signature is unchanged.
New API:
- SetStereoEnabled(bool) / IsStereoEnabled()
- GetRightEyeUV() — folds onto primary UV in mono mode
- GetStereoPixelRegions(fullW, fullH) — resolves both eyes against
per-eye half-width for SBS textures
- Preset gains an optional rightUV{} field
In stereo mode, drag/slider edits auto-mirror the primary UV to the
right eye around x=0.5 (HMD nose-side overlap symmetry). LoadSettings
mirrors left → right only for the legacy upgrade case (CropX/Y/W/H
present, no CropRight*), so a seeded preset's explicit right-eye UV
survives a fresh load.
The header is now self-contained (forward-declares D3D11 types,
includes nlohmann/json), so a test target can compile it without the
plugin PCH.
Adds a C++ unit test target (tests/cpp) covering mono back-compat,
stereo round-trip, legacy upgrade mirror, and per-eye pixel-region
math. 9 test cases, 33 assertions. Reuses Catch2 via FetchContent
dedup with tests/shaders. New CMake target run_cpp_tests.
Decomposed from community-shaders#2096 (PR-1 of 4); see DLSS-PR-PLAN.md.
Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The Feature class with UI, plus the one compute shader MVP-B keeps. Lifted DlssEnhancerFeature with cuts: - Removed enums: SubrectBlendMode, PeripheryAAMode (entirely) - Removed DlssMode::kExtreme entry (kDefault, kFaster only) - Removed Settings fields: peripheryAAMode, peripheryTemporalAlpha, subrectBlendMode, subrectFeatherWidth, subrectDitherStrength, peripheryBlurRadius, enablePerf - Removed accessors: GetPeripheryAAMode, GetSubrectBlendMode, IsPerfEnabled, LatchPerfEnabled, perfEnabledAtBoot - Removed ImGui sections for all of the above + DLSSperf hook status - ClampPresetToMode keeps Faster constraints, drops Extreme branch Kept (MVP-B mode set requires these): - StretchMode (all 3: Bilinear/Point/GaussianBlur — used by SubrectStretchCS for the periphery stretch step) - SharpenMode (all entries; Postprocess uses kNone vs others) - subrectController (Util::Subrect::Controller, PR-1 stereo API) - IsActive/IsLoaded/LatchEnabled/LatchQualityMode and the boot-time quality snapshot + GetActive*** accessors Migrated for PR-1: - #include path Utils/Subrect/Subrect.h -> Utils/Subrect.h - Subrect::Controller -> Util::Subrect::Controller - subrectController.GetLeftEyeUV() -> subrectController.GetUV() (PR-1's API: GetUV() doubles as left-eye in stereo mode) PostPostLoad opts the controller into stereo mode and seeds a small preset menu for Default/Faster: Full Eye [0, 0, 1.0, 1.0 ] Center 75% (foveal) [0.125, 0.125, 0.75, 0.75] Center 50% (foveal) [0.25, 0.25, 0.5, 0.5 ] For symmetric crops centered at x=0.5, the auto-mirror produces the same UV as the seed — left==right by construction. GetShortName returns "DLSSENHANCER" (uppercase) to match the features/DLSS Enhancer/Shaders/Features/DLSSENHANCER.ini filename; FindFeatureByShortName + version-issue lookup are filename-keyed. Also lifted as-is: - features/DLSS Enhancer/Shaders/Features/SubrectStretchCS.hlsl (3-mode stretch: Bilinear / Point / GaussianBlur 3x3) - features/DLSS Enhancer/Shaders/Features/DLSSENHANCER.ini The SubrectBlendCS shader is intentionally NOT lifted — the periphery blend collapses to a plain CopySubresourceRegion in Ops::BlendSubrectToOutput, so the shader (and its bot-flagged feather edge bug) is deferred to PR-3b. Sizes: DlssEnhancerFeature.h 128 LOC (was 163), .cpp 312 (was 405). References to globals::features::dlssEnhancer become resolvable once the next commit registers the instance. Decomposed from community-shaders#2096 (PR-3 MVP-B of 4); see DLSS-PR-PLAN.md. Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The framework lifted in the previous commits is now reachable. Globals: - DlssEnhancerFeature forward decl + extern + instance - Feature::GetFeatureList() registry add so settings load/save and draw flow through the standard Feature iteration Upscaling.cpp short-circuit: - DlssEnhancer::Bridge::IsRouteActive() at top of the kDLSS branch in Upscale(). Invokes Preprocess::EncodeUpscalingTextures + Core:: ExecuteVRDlssCore. On any failure falls through to dev's standard streamline.Upscale so users always get DLSS output, with a one-shot warn log when fallback fires. - Sharpening dispatch routes through Postprocess::ApplyDlssSharpening when the route is active; dev's ApplySharpening otherwise. Hooks.cpp: - BSShaderRenderTargets::Create::thunk calls DlssEnhancer::Bridge:: BootSequence() after globals::ReInit + State::Setup. Latches enabledAtBoot + qualityModeAtBoot before the first frame. Two follow-up fixes required during the wire: - Streamline::EvaluateDLSS signature drift: dev takes 11 args (...extentIn, extentOut, outputWidth); the PR's calls passed 12 (extra outputHeight). Dropped the trailing height at all 3 callsites in Modes.cpp. - Core.cpp had one stale reference to the removed Settings field peripheryBlurRadius. Replaced with a fixed 1.0f for the GaussianBlur stretch path; per-user blur radius deferred to PR-3b. Build status: plugin C++ compile passes (MSBuild ClCompile exit 0). FFX shader-permutation crash on this machine still blocks full link (environmental, same as PR-2; reproduces on clean upstream/dev). Decomposed from community-shaders#2096 (PR-3 MVP-B of 4); see DLSS-PR-PLAN.md. Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The two compute shaders deferred from PR-3 MVP-B: - SubrectBlendCS: feather/dither-blend DLSS subrect output back onto the stretched periphery in kMAIN. Replaces MVP-B's plain CopySubresourceRegion when the user picks Feather or Dither blend mode in DlssEnhancer settings. Bot-flagged Major bug fixed during the lift (CodeRabbit + Copilot on the original PR): the previous edge-distance math used srcPos (= tid.xy + SrcOffsetX) for distL/distR, but in Extreme strip mode SrcOffsetX != 0 makes srcPos.x exceed SubWidth, driving distR negative and breaking the feather band entirely — the strip would never blend correctly with the background. Fixed by computing edge distances in dispatch-local tid.xy space (always [0, SubWidth-1]) regardless of the source-side offset. Inline comment cites the finding for future readers. - PeripheryTemporalSmoothCS: ping-pong reprojected-history smooth on the SBS render-res texture, applied before StretchDRS so the periphery has temporal AA that DLSS doesn't provide. Lifted as-is. These shaders are not yet wired into Core/Modes/UI — that's the next PR-3b commit. Lifting them first to keep the diff readable. Decomposed from community-shaders#2096 (PR-3b of the decomposition); see DLSS-PR-PLAN.md. Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Restores the periphery temporal smooth + feather/dither blend + Extreme strip resource scaffolding that PR-3 MVP-B deferred. Each piece is the original PR's code adapted to our base (Util::Subrect instead of Subrect:: namespace, no DLSSperf references). Restored in Core.h: - vrExtremeStrip* fields (color in/out, depth, mvec, reactive, transparency + strip dimensions) for Modes::ExecuteExtremeMode - vrTemporalHistory[2] + vrMvecSRV/Owner + vrTemporalHistoryW/H + vrTemporalFrameIdx + vrTemporalHistoryValid (history + cache) - vrTemporalSmoothCS + vrTemporalSmoothCB + vrTemporalSmoothSampler - vrSubrectBlendCS + vrSubrectBlendCB + vrBlendSrcSRV/Owner - ExecuteExtremeMode private static decl (body lifted in next commit) Restored in Core.cpp: - EnsureExtremeStripTextures (allocates the merged-strip set) - EnsureTemporalResources + TemporalSmoothSBS (ping-pong reproject history + dispatch the PeripheryTemporalSmoothCS shader) - BlendSubrectToOutput restored to its full feather/dither version, with the gating retained: kHardCopy short-circuits to a plain CopySubresourceRegion (the MVP-B fast path); other modes lazy-init the SubrectBlendCS shader and dispatch with feather/dither params read from Settings. - BlendCB struct + s_blendFrameIdx static for the CS path - ClearResources + ClearShaderCache expanded to release the restored resources cleanly Restored in Ops.h: - EnsureExtremeStripTextures, EnsureTemporalResources, TemporalSmoothSBS, BlendSubrectToOutput (with restored dstUAV parameter) declarations Restored in Modes.cpp: - BlendSubrectToOutput callsites in Default + Faster updated to pass p.colorDstUAV (the restored dstUAV signature) Stale fix unwound: - Core.cpp's stretch CS dispatch now reads enhSettings.peripheryBlurRadius again (PR-3 MVP-B had hardcoded 1.0f as a stub). The Settings field itself is restored in the DlssEnhancerFeature commit. Resource creation now sets D3D11 names via Util::SetResourceName per the CLAUDE.md convention. Restored resources use the "DlssEnhancer::" prefix so they're identifiable in RenderDoc. References to DlssEnhancerFeature::SubrectBlendMode::kHardCopy and the Settings fields peripheryBlurRadius/peripheryTemporalAlpha/ subrectFeatherWidth/subrectDitherStrength are expected unresolved until the next commit restores them in DlssEnhancerFeature. Decomposed from community-shaders#2096 (PR-3b of the decomposition); see DLSS-PR-PLAN.md. Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…(PR-3b)
Three changes to Modes.cpp:
1. Router gains a kExtreme switch arm dispatching to the new
ExecuteExtremeMode body (the framework method was already
declared in Core.h's restored sections).
2. Default + Faster mode bodies regain the periphery temporal-smooth
call before StretchDRSBothEyes:
if PeripheryAAMode == kTemporalSmooth:
EnsureTemporalResources + TemporalSmoothSBS → stretchSrc
StretchDRSBothEyes(..., stretchSrc)
Restored from PR-3 MVP-B's stub-out (where stretchSrc was
hardcoded nullptr because TemporalSmooth was deferred).
3. New ExecuteExtremeMode body — combined-strip mode that merges
both eyes' subrects horizontally and runs DLSS once on the strip.
Adapted from community-shaders#2096 with three deliberate fixes:
a. Per-eye subrect sizing: same fix pattern Default + Faster got
in PR-3 MVP-B (CodeRabbit Major @ original PR Modes.cpp:80).
The CopySubresourceRegion box dimensions inside the per-eye
loop and the BlendSubrectToOutput sizes use eyeUVs[i]->w/h
(per-eye), not p.leftUV.w/h. The strip allocation still uses
left-eye dims (auto-mirror constraint, same as elsewhere).
b. Streamline::EvaluateDLSS signature: drop the trailing
outputHeight arg (dev's signature takes outputWidth only).
Matches the same fix Default + Faster needed in MVP-B.
c. Stale Subrect:: namespace renamed to Util::Subrect:: per the
PR-1 stereo Subrect API.
The blend-back step uses srcOffsetX = i * subOutW so each eye reads
its half of the strip's output. SubrectBlendCS's edge-distance math
was fixed in commit 569d1fe2d (PR-3b shader lift) to use tid.xy
local coords — the precise reason this strip mode was unsafe in the
original PR.
Decomposed from community-shaders#2096 (PR-3b of the decomposition); see
DLSS-PR-PLAN.md.
Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Re-adds the foveated/blend/periphery surface that PR-3 MVP-B
deferred. After this commit, all the symbols Core.cpp/Modes.cpp
have been referencing for the past three commits resolve.
Enums restored in DlssEnhancerFeature.h:
- DlssMode::kExtreme = 2 (combined-strip mode that pairs with
Modes::ExecuteExtremeMode restored in 6dca8556c)
- SubrectBlendMode { kHardCopy=0, kFeather=1, kDither=2 } — drives
Ops::BlendSubrectToOutput's gating: kHardCopy fast-paths to
CopySubresourceRegion, others dispatch SubrectBlendCS
- PeripheryAAMode { kNone=0, kTemporalSmooth=1 } — when kTemporalSmooth
is selected, Default/Faster/Extreme bodies in Modes.cpp invoke
EnsureTemporalResources + TemporalSmoothSBS before stretch
Settings fields restored:
- peripheryAAMode (default kTemporalSmooth)
- peripheryTemporalAlpha (0.16)
- subrectBlendMode (default kDither)
- subrectFeatherWidth (64)
- subrectDitherStrength (1.0)
- peripheryBlurRadius (1.0) — Core.cpp's blur dispatch reads this
again instead of MVP-B's hardcoded 1.0f stub
enablePerf intentionally NOT restored — PR-2 owns DLSSperf via
Upscaling::Settings::enableDLSSperf. Marked with an inline comment.
Accessors:
- GetPeripheryAAMode(), GetSubrectBlendMode()
- GetDlssMode() clamp updated 1u→2u to admit kExtreme
- ClampSettings expanded to clamp the restored fields
UI restored in DrawSettings:
- Mode picker gains "Extreme (not recommended)" alongside
Default/Faster
- ClampPresetToMode admits all presets in kExtreme (matches
original PR's permissive stance)
- New "Periphery AA" combo + alpha slider
- New "Subrect Blend" combo + featherWidth + ditherStrength
sliders
- peripheryBlurRadius slider gated on StretchMode == kGaussianBlur
- No DLSSperf UI (PR-2 owns it on the Upscaling panel)
JSON serialization (NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT)
adds the 6 new fields in struct-declaration order.
Migrations preserved from MVP-B: GetShortName() = "DLSSENHANCER"
(matches .ini), Util::Subrect:: namespace, no DLSSperf.h include.
Decomposed from community-shaders#2096 (PR-3b of the decomposition); see
DLSS-PR-PLAN.md.
Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two follow-ups to wrap PR-3b:
1. Bridge::ComputeMvecScale handles kExtreme:
- Default + Faster: outX = 1 / UV.w (per-eye texture sets)
- Extreme: outX = 1 / (2 * UV.w) — both eyes merged into a strip
of width 2*subOutW; mvec authored at full-eye res must shrink
by 1/(2·w) to land in strip space.
PR-3 MVP-B's stub treated all modes as Default/Faster; that
would mis-scale mvec under Extreme and produce ghosting in the
strip output. Now correct.
2. Quality Mode slider in DlssEnhancerFeature::DrawSettings gains
a "RESTART REQUIRED" banner when settings.qualityMode diverges
from qualityModeAtBoot. Mirrors the existing Enable slider's
warning. The boot latch (Bridge::BootSequence) snapshots
qualityMode once at engine init for downstream RT-allocation
decisions; live changes don't take effect until restart, so
the banner avoids the "the slider doesn't do anything"
confusion.
Build status: plugin C++ compile passes (MSBuild ClCompile, exit 0).
Decomposed from community-shaders#2096 (PR-3b of the decomposition); see
DLSS-PR-PLAN.md.
Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…upled from Bridge)
DLSSperf hooks BSOpenVR::GetRenderTargetSize so all engine render
targets allocate at RenderRes while DLSS writes its upscaled output
to a private DisplayRes texture. The legacy UpscaleRT round-trip
disappears, VRAM/bandwidth drop, and game menus stop being occluded
by the upscaler output. The Post chain hybrid (BeginPostIntercept /
EndPostIntercept) swaps in the DisplayRes texture + a fake DSV
around tonemap/refraction so the HDR pyramid builds from anti-
aliased content.
This commit lifts the standalone files only:
- src/Features/DLSSperf.{h,cpp}
- features/DLSSperf/Shaders/DLSSperf/BilinearCopyPS.hlsl
Decoupling from the original PR's DlssEnhancer::Bridge:
- Quality mode is read directly from Upscaling::Settings at hook-
install time (BSShaderRenderTargets::Create runs after settings
load).
- Scale is computed via ffxFsr3GetUpscaleRatioFromQualityMode, the
same helper Upscaling.cpp uses in ConfigureUpscaling.
Follow-up commits will land the Globals/Hooks/Upscaling integration
hunks and a user-facing enablement toggle.
Decomposed from community-shaders#2096 (PR-2 of 4); see DLSS-PR-PLAN.md.
Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds the dlssPerf instance to globals::features and registers the ID3D11DeviceContext::Draw vfunc-13 detour. The detour is no-op unless DLSSperf's RT-size hook is live AND its post chain just finished, in which case it forces the scene-fade overlay (the characteristic Draw(30)) to render at displayRes — fixing the "black stamp" partial-screen artifact where a renderRes VP would otherwise stamp onto the displayRes kTOTAL. Decoupled extract from community-shaders#2096 (PR-2 of 4); covers only the DLSSperf- specific Globals additions, not the unrelated TruePBR/Screenshot/ RefreshTES refactors that share the PR's diff. Fix vs original PR: zero-init savedVP and skip the restore when numVP=0 (Copilot review finding — RSGetViewports does not write savedVP if no viewport was set). Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds the 3 DLSSperf-specific hook surfaces: 1. BSGraphics::Renderer::UpdateViewPort detour — corrects the engine's viewport whenever it disagrees with our enlarged RT dimensions: compress displayRes→renderRes for normal world/depth draws, expand renderRes→displayRes after the Post chain finishes (UI + submit-prep). No-op until DLSSperf::IsHookActive flips on. 2. BSShaderRenderTargets::Create — gates DLSSperf::InstallRenderTarget SizeHook on Upscaling::Settings::enableDLSSperf (replacement for the PR's DlssEnhancer::Bridge::BootSequence path), enlarges the Color RTs in the post-chain whitelist (kIMAGESPACE_TEMP_COPY/kTOTAL/kMENUBG) to displayRes via DLSSperf_MaybeEnlargeRT, and calls SetupResources manually since DLSSperf isn't a Feature in the registry. 3. Runtime E8 scanner in Hooks::Install that finds every unhooked CreateRenderTarget call site within Create() and re-writes them through DLSSperf_CreateRT_Thunk so enlargement applies universally, not just to the 6 sites with explicit per-target thunks. Also injects DLSSperf_MaybeEnlargeRT into all 6 existing CreateRenderTarget thunks (Main, Normals, NormalsSwap, MotionVectors, RefractionNormals, UnderwaterMask). The new field Upscaling::Settings::enableDLSSperf (added in the next commit) provides the user-facing gate; without it set, none of this code path activates. Decoupled extract from community-shaders#2096 (PR-2 of 4); skips the unrelated TruePBR/ TESObjectLAND/BSLightingShader refactor hunks that share the PR's diff. Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Three Upscaling-side changes complete the DLSSperf wiring: 1. ConfigureUpscaling jitter branch — when DLSSperf::IsHookActive, RTs are already at RenderRes so the DRS-style scale collapses to identity. Jitter is still computed at the real DisplayRes phase ratio so DLSS has enough sub-pixel diversity for the upscale. 2. Main_PostProcessing wrap — replaces the bare engine func() call with a HandlePostProcessing lambda when DLSSperf::ShouldHandlePost. The hybrid Post performs a two-layer struct swap so tonemap + refraction read the DisplayRes testTexture instead of small kMAIN. 3. Upscaling::Settings::enableDLSSperf — new opt-in bool with JSON load/save (added to NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT) and a UI checkbox under Streamline settings, gated on VR + DLSS. Restart required (matches the engine init lifecycle of the hook). DLSSperf.cpp now includes Upscaling.h so the InstallRenderTargetSize Hook can read settings.qualityMode at install time. Plugin C++ compile passes (MSBuild ClCompile target, exit 0). Full link blocked locally by an environmental crash in the FFX shader permutation generator — unrelated to PR-2 changes; reproduces on clean upstream/dev. Decoupled extract from community-shaders#2096 (PR-2 of 4); skips the DlssEnhancer route and per-eye sharpening branches that share the PR's diff. Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…upled from Bridge)
DLSSperf hooks BSOpenVR::GetRenderTargetSize so all engine render
targets allocate at RenderRes while DLSS writes its upscaled output
to a private DisplayRes texture. The legacy UpscaleRT round-trip
disappears, VRAM/bandwidth drop, and game menus stop being occluded
by the upscaler output. The Post chain hybrid (BeginPostIntercept /
EndPostIntercept) swaps in the DisplayRes texture + a fake DSV
around tonemap/refraction so the HDR pyramid builds from anti-
aliased content.
This commit lifts the standalone files only:
- src/Features/DLSSperf.{h,cpp}
- features/DLSSperf/Shaders/DLSSperf/BilinearCopyPS.hlsl
Decoupling from the original PR's DlssEnhancer::Bridge:
- Quality mode is read directly from Upscaling::Settings at hook-
install time (BSShaderRenderTargets::Create runs after settings
load).
- Scale is computed via ffxFsr3GetUpscaleRatioFromQualityMode, the
same helper Upscaling.cpp uses in ConfigureUpscaling.
Follow-up commits will land the Globals/Hooks/Upscaling integration
hunks and a user-facing enablement toggle.
Decomposed from community-shaders#2096 (PR-2 of 4); see DLSS-PR-PLAN.md.
Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds the dlssPerf instance to globals::features and registers the ID3D11DeviceContext::Draw vfunc-13 detour. The detour is no-op unless DLSSperf's RT-size hook is live AND its post chain just finished, in which case it forces the scene-fade overlay (the characteristic Draw(30)) to render at displayRes — fixing the "black stamp" partial-screen artifact where a renderRes VP would otherwise stamp onto the displayRes kTOTAL. Decoupled extract from community-shaders#2096 (PR-2 of 4); covers only the DLSSperf- specific Globals additions, not the unrelated TruePBR/Screenshot/ RefreshTES refactors that share the PR's diff. Fix vs original PR: zero-init savedVP and skip the restore when numVP=0 (Copilot review finding — RSGetViewports does not write savedVP if no viewport was set). Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds the 3 DLSSperf-specific hook surfaces: 1. BSGraphics::Renderer::UpdateViewPort detour — corrects the engine's viewport whenever it disagrees with our enlarged RT dimensions: compress displayRes→renderRes for normal world/depth draws, expand renderRes→displayRes after the Post chain finishes (UI + submit-prep). No-op until DLSSperf::IsHookActive flips on. 2. BSShaderRenderTargets::Create — gates DLSSperf::InstallRenderTarget SizeHook on Upscaling::Settings::enableDLSSperf (replacement for the PR's DlssEnhancer::Bridge::BootSequence path), enlarges the Color RTs in the post-chain whitelist (kIMAGESPACE_TEMP_COPY/kTOTAL/kMENUBG) to displayRes via DLSSperf_MaybeEnlargeRT, and calls SetupResources manually since DLSSperf isn't a Feature in the registry. 3. Runtime E8 scanner in Hooks::Install that finds every unhooked CreateRenderTarget call site within Create() and re-writes them through DLSSperf_CreateRT_Thunk so enlargement applies universally, not just to the 6 sites with explicit per-target thunks. Also injects DLSSperf_MaybeEnlargeRT into all 6 existing CreateRenderTarget thunks (Main, Normals, NormalsSwap, MotionVectors, RefractionNormals, UnderwaterMask). The new field Upscaling::Settings::enableDLSSperf (added in the next commit) provides the user-facing gate; without it set, none of this code path activates. Decoupled extract from community-shaders#2096 (PR-2 of 4); skips the unrelated TruePBR/ TESObjectLAND/BSLightingShader refactor hunks that share the PR's diff. Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Three Upscaling-side changes complete the DLSSperf wiring: 1. ConfigureUpscaling jitter branch — when DLSSperf::IsHookActive, RTs are already at RenderRes so the DRS-style scale collapses to identity. Jitter is still computed at the real DisplayRes phase ratio so DLSS has enough sub-pixel diversity for the upscale. 2. Main_PostProcessing wrap — replaces the bare engine func() call with a HandlePostProcessing lambda when DLSSperf::ShouldHandlePost. The hybrid Post performs a two-layer struct swap so tonemap + refraction read the DisplayRes testTexture instead of small kMAIN. 3. Upscaling::Settings::enableDLSSperf — new opt-in bool with JSON load/save (added to NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT) and a UI checkbox under Streamline settings, gated on VR + DLSS. Restart required (matches the engine init lifecycle of the hook). DLSSperf.cpp now includes Upscaling.h so the InstallRenderTargetSize Hook can read settings.qualityMode at install time. Plugin C++ compile passes (MSBuild ClCompile target, exit 0). Full link blocked locally by an environmental crash in the FFX shader permutation generator — unrelated to PR-2 changes; reproduces on clean upstream/dev. Decoupled extract from community-shaders#2096 (PR-2 of 4); skips the DlssEnhancer route and per-eye sharpening branches that share the PR's diff. Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…upled from Bridge)
DLSSperf hooks BSOpenVR::GetRenderTargetSize so all engine render
targets allocate at RenderRes while DLSS writes its upscaled output
to a private DisplayRes texture. The legacy UpscaleRT round-trip
disappears, VRAM/bandwidth drop, and game menus stop being occluded
by the upscaler output. The Post chain hybrid (BeginPostIntercept /
EndPostIntercept) swaps in the DisplayRes texture + a fake DSV
around tonemap/refraction so the HDR pyramid builds from anti-
aliased content.
This commit lifts the standalone files only:
- src/Features/DLSSperf.{h,cpp}
- features/DLSSperf/Shaders/DLSSperf/BilinearCopyPS.hlsl
Decoupling from the original PR's DlssEnhancer::Bridge:
- Quality mode is read directly from Upscaling::Settings at hook-
install time (BSShaderRenderTargets::Create runs after settings
load).
- Scale is computed via ffxFsr3GetUpscaleRatioFromQualityMode, the
same helper Upscaling.cpp uses in ConfigureUpscaling.
Follow-up commits will land the Globals/Hooks/Upscaling integration
hunks and a user-facing enablement toggle.
Decomposed from community-shaders#2096 (PR-2 of 4); see DLSS-PR-PLAN.md.
Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds the dlssPerf instance to globals::features and registers the ID3D11DeviceContext::Draw vfunc-13 detour. The detour is no-op unless DLSSperf's RT-size hook is live AND its post chain just finished, in which case it forces the scene-fade overlay (the characteristic Draw(30)) to render at displayRes — fixing the "black stamp" partial-screen artifact where a renderRes VP would otherwise stamp onto the displayRes kTOTAL. Decoupled extract from community-shaders#2096 (PR-2 of 4); covers only the DLSSperf- specific Globals additions, not the unrelated TruePBR/Screenshot/ RefreshTES refactors that share the PR's diff. Fix vs original PR: zero-init savedVP and skip the restore when numVP=0 (Copilot review finding — RSGetViewports does not write savedVP if no viewport was set). Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds the 3 DLSSperf-specific hook surfaces: 1. BSGraphics::Renderer::UpdateViewPort detour — corrects the engine's viewport whenever it disagrees with our enlarged RT dimensions: compress displayRes→renderRes for normal world/depth draws, expand renderRes→displayRes after the Post chain finishes (UI + submit-prep). No-op until DLSSperf::IsHookActive flips on. 2. BSShaderRenderTargets::Create — gates DLSSperf::InstallRenderTarget SizeHook on Upscaling::Settings::enableDLSSperf (replacement for the PR's DlssEnhancer::Bridge::BootSequence path), enlarges the Color RTs in the post-chain whitelist (kIMAGESPACE_TEMP_COPY/kTOTAL/kMENUBG) to displayRes via DLSSperf_MaybeEnlargeRT, and calls SetupResources manually since DLSSperf isn't a Feature in the registry. 3. Runtime E8 scanner in Hooks::Install that finds every unhooked CreateRenderTarget call site within Create() and re-writes them through DLSSperf_CreateRT_Thunk so enlargement applies universally, not just to the 6 sites with explicit per-target thunks. Also injects DLSSperf_MaybeEnlargeRT into all 6 existing CreateRenderTarget thunks (Main, Normals, NormalsSwap, MotionVectors, RefractionNormals, UnderwaterMask). The new field Upscaling::Settings::enableDLSSperf (added in the next commit) provides the user-facing gate; without it set, none of this code path activates. Decoupled extract from community-shaders#2096 (PR-2 of 4); skips the unrelated TruePBR/ TESObjectLAND/BSLightingShader refactor hunks that share the PR's diff. Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Three Upscaling-side changes complete the DLSSperf wiring: 1. ConfigureUpscaling jitter branch — when DLSSperf::IsHookActive, RTs are already at RenderRes so the DRS-style scale collapses to identity. Jitter is still computed at the real DisplayRes phase ratio so DLSS has enough sub-pixel diversity for the upscale. 2. Main_PostProcessing wrap — replaces the bare engine func() call with a HandlePostProcessing lambda when DLSSperf::ShouldHandlePost. The hybrid Post performs a two-layer struct swap so tonemap + refraction read the DisplayRes testTexture instead of small kMAIN. 3. Upscaling::Settings::enableDLSSperf — new opt-in bool with JSON load/save (added to NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT) and a UI checkbox under Streamline settings, gated on VR + DLSS. Restart required (matches the engine init lifecycle of the hook). DLSSperf.cpp now includes Upscaling.h so the InstallRenderTargetSize Hook can read settings.qualityMode at install time. Plugin C++ compile passes (MSBuild ClCompile target, exit 0). Full link blocked locally by an environmental crash in the FFX shader permutation generator — unrelated to PR-2 changes; reproduces on clean upstream/dev. Decoupled extract from community-shaders#2096 (PR-2 of 4); skips the DlssEnhancer route and per-eye sharpening branches that share the PR's diff. Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…P-B) First slice of the DlssEnhancer foveated-DLSS framework, decomposed from community-shaders#2096. These four modules form the leaf interface layer: - Bridge.h/cpp — single point of contact for Upscaling/Streamline. Settings forwarding, route activity check, boot latches, mvec scale. Drops the original PR's DLSSperf install path (PR-2 owns DLSSperf). Drops the kExtreme branch in ComputeMvecScale (kExtreme deferred). - Params.h/cpp — VRDlssParams::Resolve(). Builds the per-frame parameter block for Mode dispatchers from current global state. MVP-B drops the DLSSperf-aware DisplayRes/RenderRes split and testTexture rerouting; that's a PR-2 + PR-3 integration follow-up. Migrated to PR-1's Util::Subrect API: GetUV() == primary/left, GetRightEyeUV() == mirrored right. - Preprocess.h/cpp — DlssEnhancer::Preprocess::EncodeUpscalingTextures. Mirrors Upscaling's encode pass with a DLSS-specific shader define, invoked only when the DlssEnhancer route runs. Lift-as-is. - Postprocess.h/cpp — DlssEnhancer::Postprocess::ApplyDlssSharpening. RCAS sharpen pass for the route. Drops the DLSSperf-aware testTexture variant (deferred to PR-2 + PR-3 integration). These files reference DlssEnhancerFeature, Core, Modes, Ops which haven't landed yet — won't compile in isolation. Build gate is at the end of the lift sequence. Decomposed from community-shaders#2096 (PR-3 MVP-B of 4); see DLSS-PR-PLAN.md. Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Lifts Core.h, Core.cpp, and Ops.h from community-shaders#2096 with the MVP-B cuts applied surgically: Removed (deferred to PR-3b): - vrExtremeStrip* fields and EnsureExtremeStripTextures (kExtreme mode) - vrTemporalHistory[2], EnsureTemporalResources, TemporalSmoothSBS and the periphery temporal-smooth CS resources - vrSubrectBlendCS, BlendCB, blend-feather/dither dispatch — replaced by a plain CopySubresourceRegion in Ops::BlendSubrectToOutput - ExecuteExtremeMode private declaration - ClearResources/ClearShaderCache references to all of the above Migrated for PR-1 compatibility: - Subrect:: → Util::Subrect:: (UVRegion field types, ComputeSubrectUVHash parameter) - #include path Utils/Subrect/Subrect.h → Utils/Subrect.h Bot-flagged Major bug fixed during the lift: - CodeRabbit Major (Core.cpp:178-224 in original PR): the per-eye inputs path failed to reapply the HMD hidden-area clear, letting garbage from the HMD's optical mask bleed into DLSS history. Fixed in Core::PreparePerEyeInputs by calling Upscaling::ClearHMDMask per-eye after the SBS->per-eye CopySubresourceRegion. Decomposed from community-shaders#2096 (PR-3 MVP-B of 4); see DLSS-PR-PLAN.md. Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Lifts Modes.cpp with cuts and the bot-flagged Major bug fixed: Removed (deferred to PR-3b): - ExecuteExtremeMode body (~70 LOC, asymmetric strip mode) - kExtreme switch arm in router ExecuteVRDlssCore - Periphery temporal-smooth blocks in both Default and Faster mode bodies (depended on Core's removed EnsureTemporalResources + TemporalSmoothSBS) Migrated for PR-1 compatibility: - Subrect:: -> Util::Subrect:: (3 sites in mode bodies) - Added #include for Utils/Subrect.h - BlendSubrectToOutput callsites updated to MVP-B signature (drops dstUAV arg now that the function is plain CopySubresourceRegion) Bot-flagged Major bug fixed (CodeRabbit Major @ Modes.cpp:80,146,207 of original PR): - Per-eye subrect dimensions (subInW/H/subOutW/H) were computed once outside the eye loop using p.leftUV.w/h, so the right eye used left-eye dimensions even when rightUV differed. Moved inside the per-eye loop using eyeUVs[i]->w/h. Allocator calls (EnsureVRSubrectTextures, EnsureFasterOutputTextures) still use left-eye dimensions for the shared texture set, with a comment citing PR-1's auto-mirrored stereo Subrect (leftUV.w == rightUV.w) as the constraint that keeps this safe in MVP-B. Also fixes Postprocess.cpp's sharpness lookup to read from Upscaling::Settings::sharpnessDLSS directly (the PR called GetActiveSharpnessDLSS() which doesn't exist on dev — that helper came in via the deferred per-route override layer). Output: Modes.cpp 214 LOC (was 276). Decomposed from community-shaders#2096 (PR-3 MVP-B of 4); see DLSS-PR-PLAN.md. Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The Feature class with UI, plus the one compute shader MVP-B keeps. Lifted DlssEnhancerFeature with cuts: - Removed enums: SubrectBlendMode, PeripheryAAMode (entirely) - Removed DlssMode::kExtreme entry (kDefault, kFaster only) - Removed Settings fields: peripheryAAMode, peripheryTemporalAlpha, subrectBlendMode, subrectFeatherWidth, subrectDitherStrength, peripheryBlurRadius, enablePerf - Removed accessors: GetPeripheryAAMode, GetSubrectBlendMode, IsPerfEnabled, LatchPerfEnabled, perfEnabledAtBoot - Removed ImGui sections for all of the above + DLSSperf hook status - ClampPresetToMode keeps Faster constraints, drops Extreme branch Kept (MVP-B mode set requires these): - StretchMode (all 3: Bilinear/Point/GaussianBlur — used by SubrectStretchCS for the periphery stretch step) - SharpenMode (all entries; Postprocess uses kNone vs others) - subrectController (Util::Subrect::Controller, PR-1 stereo API) - IsActive/IsLoaded/LatchEnabled/LatchQualityMode and the boot-time quality snapshot + GetActive*** accessors Migrated for PR-1: - #include path Utils/Subrect/Subrect.h -> Utils/Subrect.h - Subrect::Controller -> Util::Subrect::Controller - subrectController.GetLeftEyeUV() -> subrectController.GetUV() (PR-1's API: GetUV() doubles as left-eye in stereo mode) PostPostLoad opts the controller into stereo mode and seeds a small preset menu for Default/Faster: Full Eye [0, 0, 1.0, 1.0 ] Center 75% (foveal) [0.125, 0.125, 0.75, 0.75] Center 50% (foveal) [0.25, 0.25, 0.5, 0.5 ] For symmetric crops centered at x=0.5, the auto-mirror produces the same UV as the seed — left==right by construction. GetShortName returns "DLSSENHANCER" (uppercase) to match the features/DLSS Enhancer/Shaders/Features/DLSSENHANCER.ini filename; FindFeatureByShortName + version-issue lookup are filename-keyed. Also lifted as-is: - features/DLSS Enhancer/Shaders/Features/SubrectStretchCS.hlsl (3-mode stretch: Bilinear / Point / GaussianBlur 3x3) - features/DLSS Enhancer/Shaders/Features/DLSSENHANCER.ini The SubrectBlendCS shader is intentionally NOT lifted — the periphery blend collapses to a plain CopySubresourceRegion in Ops::BlendSubrectToOutput, so the shader (and its bot-flagged feather edge bug) is deferred to PR-3b. Sizes: DlssEnhancerFeature.h 128 LOC (was 163), .cpp 312 (was 405). References to globals::features::dlssEnhancer become resolvable once the next commit registers the instance. Decomposed from community-shaders#2096 (PR-3 MVP-B of 4); see DLSS-PR-PLAN.md. Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The framework lifted in the previous commits is now reachable. Globals: - DlssEnhancerFeature forward decl + extern + instance - Feature::GetFeatureList() registry add so settings load/save and draw flow through the standard Feature iteration Upscaling.cpp short-circuit: - DlssEnhancer::Bridge::IsRouteActive() at top of the kDLSS branch in Upscale(). Invokes Preprocess::EncodeUpscalingTextures + Core:: ExecuteVRDlssCore. On any failure falls through to dev's standard streamline.Upscale so users always get DLSS output, with a one-shot warn log when fallback fires. - Sharpening dispatch routes through Postprocess::ApplyDlssSharpening when the route is active; dev's ApplySharpening otherwise. Hooks.cpp: - BSShaderRenderTargets::Create::thunk calls DlssEnhancer::Bridge:: BootSequence() after globals::ReInit + State::Setup. Latches enabledAtBoot + qualityModeAtBoot before the first frame. Two follow-up fixes required during the wire: - Streamline::EvaluateDLSS signature drift: dev takes 11 args (...extentIn, extentOut, outputWidth); the PR's calls passed 12 (extra outputHeight). Dropped the trailing height at all 3 callsites in Modes.cpp. - Core.cpp had one stale reference to the removed Settings field peripheryBlurRadius. Replaced with a fixed 1.0f for the GaussianBlur stretch path; per-user blur radius deferred to PR-3b. Build status: plugin C++ compile passes (MSBuild ClCompile exit 0). FFX shader-permutation crash on this machine still blocks full link (environmental, same as PR-2; reproduces on clean upstream/dev). Decomposed from community-shaders#2096 (PR-3 MVP-B of 4); see DLSS-PR-PLAN.md. Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The two compute shaders deferred from PR-3 MVP-B: - SubrectBlendCS: feather/dither-blend DLSS subrect output back onto the stretched periphery in kMAIN. Replaces MVP-B's plain CopySubresourceRegion when the user picks Feather or Dither blend mode in DlssEnhancer settings. Bot-flagged Major bug fixed during the lift (CodeRabbit + Copilot on the original PR): the previous edge-distance math used srcPos (= tid.xy + SrcOffsetX) for distL/distR, but in Extreme strip mode SrcOffsetX != 0 makes srcPos.x exceed SubWidth, driving distR negative and breaking the feather band entirely — the strip would never blend correctly with the background. Fixed by computing edge distances in dispatch-local tid.xy space (always [0, SubWidth-1]) regardless of the source-side offset. Inline comment cites the finding for future readers. - PeripheryTemporalSmoothCS: ping-pong reprojected-history smooth on the SBS render-res texture, applied before StretchDRS so the periphery has temporal AA that DLSS doesn't provide. Lifted as-is. These shaders are not yet wired into Core/Modes/UI — that's the next PR-3b commit. Lifting them first to keep the diff readable. Decomposed from community-shaders#2096 (PR-3b of the decomposition); see DLSS-PR-PLAN.md. Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Restores the periphery temporal smooth + feather/dither blend + Extreme strip resource scaffolding that PR-3 MVP-B deferred. Each piece is the original PR's code adapted to our base (Util::Subrect instead of Subrect:: namespace, no DLSSperf references). Restored in Core.h: - vrExtremeStrip* fields (color in/out, depth, mvec, reactive, transparency + strip dimensions) for Modes::ExecuteExtremeMode - vrTemporalHistory[2] + vrMvecSRV/Owner + vrTemporalHistoryW/H + vrTemporalFrameIdx + vrTemporalHistoryValid (history + cache) - vrTemporalSmoothCS + vrTemporalSmoothCB + vrTemporalSmoothSampler - vrSubrectBlendCS + vrSubrectBlendCB + vrBlendSrcSRV/Owner - ExecuteExtremeMode private static decl (body lifted in next commit) Restored in Core.cpp: - EnsureExtremeStripTextures (allocates the merged-strip set) - EnsureTemporalResources + TemporalSmoothSBS (ping-pong reproject history + dispatch the PeripheryTemporalSmoothCS shader) - BlendSubrectToOutput restored to its full feather/dither version, with the gating retained: kHardCopy short-circuits to a plain CopySubresourceRegion (the MVP-B fast path); other modes lazy-init the SubrectBlendCS shader and dispatch with feather/dither params read from Settings. - BlendCB struct + s_blendFrameIdx static for the CS path - ClearResources + ClearShaderCache expanded to release the restored resources cleanly Restored in Ops.h: - EnsureExtremeStripTextures, EnsureTemporalResources, TemporalSmoothSBS, BlendSubrectToOutput (with restored dstUAV parameter) declarations Restored in Modes.cpp: - BlendSubrectToOutput callsites in Default + Faster updated to pass p.colorDstUAV (the restored dstUAV signature) Stale fix unwound: - Core.cpp's stretch CS dispatch now reads enhSettings.peripheryBlurRadius again (PR-3 MVP-B had hardcoded 1.0f as a stub). The Settings field itself is restored in the DlssEnhancerFeature commit. Resource creation now sets D3D11 names via Util::SetResourceName per the CLAUDE.md convention. Restored resources use the "DlssEnhancer::" prefix so they're identifiable in RenderDoc. References to DlssEnhancerFeature::SubrectBlendMode::kHardCopy and the Settings fields peripheryBlurRadius/peripheryTemporalAlpha/ subrectFeatherWidth/subrectDitherStrength are expected unresolved until the next commit restores them in DlssEnhancerFeature. Decomposed from community-shaders#2096 (PR-3b of the decomposition); see DLSS-PR-PLAN.md. Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…(PR-3b)
Three changes to Modes.cpp:
1. Router gains a kExtreme switch arm dispatching to the new
ExecuteExtremeMode body (the framework method was already
declared in Core.h's restored sections).
2. Default + Faster mode bodies regain the periphery temporal-smooth
call before StretchDRSBothEyes:
if PeripheryAAMode == kTemporalSmooth:
EnsureTemporalResources + TemporalSmoothSBS → stretchSrc
StretchDRSBothEyes(..., stretchSrc)
Restored from PR-3 MVP-B's stub-out (where stretchSrc was
hardcoded nullptr because TemporalSmooth was deferred).
3. New ExecuteExtremeMode body — combined-strip mode that merges
both eyes' subrects horizontally and runs DLSS once on the strip.
Adapted from community-shaders#2096 with three deliberate fixes:
a. Per-eye subrect sizing: same fix pattern Default + Faster got
in PR-3 MVP-B (CodeRabbit Major @ original PR Modes.cpp:80).
The CopySubresourceRegion box dimensions inside the per-eye
loop and the BlendSubrectToOutput sizes use eyeUVs[i]->w/h
(per-eye), not p.leftUV.w/h. The strip allocation still uses
left-eye dims (auto-mirror constraint, same as elsewhere).
b. Streamline::EvaluateDLSS signature: drop the trailing
outputHeight arg (dev's signature takes outputWidth only).
Matches the same fix Default + Faster needed in MVP-B.
c. Stale Subrect:: namespace renamed to Util::Subrect:: per the
PR-1 stereo Subrect API.
The blend-back step uses srcOffsetX = i * subOutW so each eye reads
its half of the strip's output. SubrectBlendCS's edge-distance math
was fixed in commit 569d1fe2d (PR-3b shader lift) to use tid.xy
local coords — the precise reason this strip mode was unsafe in the
original PR.
Decomposed from community-shaders#2096 (PR-3b of the decomposition); see
DLSS-PR-PLAN.md.
Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Re-adds the foveated/blend/periphery surface that PR-3 MVP-B
deferred. After this commit, all the symbols Core.cpp/Modes.cpp
have been referencing for the past three commits resolve.
Enums restored in DlssEnhancerFeature.h:
- DlssMode::kExtreme = 2 (combined-strip mode that pairs with
Modes::ExecuteExtremeMode restored in 6dca8556c)
- SubrectBlendMode { kHardCopy=0, kFeather=1, kDither=2 } — drives
Ops::BlendSubrectToOutput's gating: kHardCopy fast-paths to
CopySubresourceRegion, others dispatch SubrectBlendCS
- PeripheryAAMode { kNone=0, kTemporalSmooth=1 } — when kTemporalSmooth
is selected, Default/Faster/Extreme bodies in Modes.cpp invoke
EnsureTemporalResources + TemporalSmoothSBS before stretch
Settings fields restored:
- peripheryAAMode (default kTemporalSmooth)
- peripheryTemporalAlpha (0.16)
- subrectBlendMode (default kDither)
- subrectFeatherWidth (64)
- subrectDitherStrength (1.0)
- peripheryBlurRadius (1.0) — Core.cpp's blur dispatch reads this
again instead of MVP-B's hardcoded 1.0f stub
enablePerf intentionally NOT restored — PR-2 owns DLSSperf via
Upscaling::Settings::enableDLSSperf. Marked with an inline comment.
Accessors:
- GetPeripheryAAMode(), GetSubrectBlendMode()
- GetDlssMode() clamp updated 1u→2u to admit kExtreme
- ClampSettings expanded to clamp the restored fields
UI restored in DrawSettings:
- Mode picker gains "Extreme (not recommended)" alongside
Default/Faster
- ClampPresetToMode admits all presets in kExtreme (matches
original PR's permissive stance)
- New "Periphery AA" combo + alpha slider
- New "Subrect Blend" combo + featherWidth + ditherStrength
sliders
- peripheryBlurRadius slider gated on StretchMode == kGaussianBlur
- No DLSSperf UI (PR-2 owns it on the Upscaling panel)
JSON serialization (NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT)
adds the 6 new fields in struct-declaration order.
Migrations preserved from MVP-B: GetShortName() = "DLSSENHANCER"
(matches .ini), Util::Subrect:: namespace, no DLSSperf.h include.
Decomposed from community-shaders#2096 (PR-3b of the decomposition); see
DLSS-PR-PLAN.md.
Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two follow-ups to wrap PR-3b:
1. Bridge::ComputeMvecScale handles kExtreme:
- Default + Faster: outX = 1 / UV.w (per-eye texture sets)
- Extreme: outX = 1 / (2 * UV.w) — both eyes merged into a strip
of width 2*subOutW; mvec authored at full-eye res must shrink
by 1/(2·w) to land in strip space.
PR-3 MVP-B's stub treated all modes as Default/Faster; that
would mis-scale mvec under Extreme and produce ghosting in the
strip output. Now correct.
2. Quality Mode slider in DlssEnhancerFeature::DrawSettings gains
a "RESTART REQUIRED" banner when settings.qualityMode diverges
from qualityModeAtBoot. Mirrors the existing Enable slider's
warning. The boot latch (Bridge::BootSequence) snapshots
qualityMode once at engine init for downstream RT-allocation
decisions; live changes don't take effect until restart, so
the banner avoids the "the slider doesn't do anything"
confusion.
Build status: plugin C++ compile passes (MSBuild ClCompile, exit 0).
Decomposed from community-shaders#2096 (PR-3b of the decomposition); see
DLSS-PR-PLAN.md.
Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…P-B) First slice of the DlssEnhancer foveated-DLSS framework, decomposed from community-shaders#2096. These four modules form the leaf interface layer: - Bridge.h/cpp — single point of contact for Upscaling/Streamline. Settings forwarding, route activity check, boot latches, mvec scale. Drops the original PR's DLSSperf install path (PR-2 owns DLSSperf). Drops the kExtreme branch in ComputeMvecScale (kExtreme deferred). - Params.h/cpp — VRDlssParams::Resolve(). Builds the per-frame parameter block for Mode dispatchers from current global state. MVP-B drops the DLSSperf-aware DisplayRes/RenderRes split and testTexture rerouting; that's a PR-2 + PR-3 integration follow-up. Migrated to PR-1's Util::Subrect API: GetUV() == primary/left, GetRightEyeUV() == mirrored right. - Preprocess.h/cpp — DlssEnhancer::Preprocess::EncodeUpscalingTextures. Mirrors Upscaling's encode pass with a DLSS-specific shader define, invoked only when the DlssEnhancer route runs. Lift-as-is. - Postprocess.h/cpp — DlssEnhancer::Postprocess::ApplyDlssSharpening. RCAS sharpen pass for the route. Drops the DLSSperf-aware testTexture variant (deferred to PR-2 + PR-3 integration). These files reference DlssEnhancerFeature, Core, Modes, Ops which haven't landed yet — won't compile in isolation. Build gate is at the end of the lift sequence. Decomposed from community-shaders#2096 (PR-3 MVP-B of 4); see DLSS-PR-PLAN.md. Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Decoupled
DlssEnhancerFeaturefromUpscaling, providing richer subrect DLSS controls and a newDLSSperfsub-feature that hooks engine RT allocation to slash VRAM/bandwidth at high resolutions.Key Advantages
DlssEnhancer — Decoupled subrect DLSS framework
DLSSperf — Bye-bye DRS, hello 6K VR on budget GPUs
BSOpenVR::GetRenderTargetSize→ all engine RTs shrink to RenderRes, DLSS outputs to private DisplayRes textureLimitations
Summary by CodeRabbit
New Features
Enhancements