Skip to content

feat(VR): add DLSS Enhancer with DLSSperf and subrect blend#2096

Open
YtzyFvra wants to merge 4 commits into
community-shaders:devfrom
YtzyFvra:feature/DLSSenhancer
Open

feat(VR): add DLSS Enhancer with DLSSperf and subrect blend#2096
YtzyFvra wants to merge 4 commits into
community-shaders:devfrom
YtzyFvra:feature/DLSSenhancer

Conversation

@YtzyFvra
Copy link
Copy Markdown
Contributor

@YtzyFvra YtzyFvra commented Apr 12, 2026

Decoupled DlssEnhancerFeature from Upscaling, providing richer subrect DLSS controls and a new DLSSperf sub-feature that hooks engine RT allocation to slash VRAM/bandwidth at high resolutions.

Key Advantages

DlssEnhancer — Decoupled subrect DLSS framework

  • evaluate mode, quality mode, stretch mode, sharpening, periphery AA, and blend mode controls
  • Extensible architecture for future sub-features: per-preset phase control, DLDSR-like supersampling, HVS preprocessing with VRS/MVR/MRS

DLSSperf — Bye-bye DRS, hello 6K VR on budget GPUs

  • Hooks BSOpenVR::GetRenderTargetSize → all engine RTs shrink to RenderRes, DLSS outputs to private DisplayRes texture
  • Eliminates UpscaleRT copy, cuts VRAM/bandwidth proportional to scale ratio
  • Makes Ultra Performance (0.33×) viable at highres(>6K) — high-quality VR on low-end GPUs instead of low-res DLAA
  • Two-layer struct swap keeps post-processing (tonemap, refraction, UI) working correctly at mixed resolutions

Limitations

  • VR only (no SE/AE client available for testing)
  • No FSR adaptation — user feedback indicates non-1.0 FSR presets perform too poorly to justify
  • Post chain still runs at RenderRes via bilinear downsample (visual loss minimal; native DisplayRes post planned)

Summary by CodeRabbit

  • New Features

    • Added DLSS Enhancer system for VR with configurable quality modes and rendering strategies
    • Introduced DLSSperf render optimization for VR environments
    • Added support for subrect blending with feather and dither modes
    • Implemented periphery temporal smoothing with anti-aliasing options
    • Added configurable DLSS sharpening modes
    • Support for motion vector scaling
  • Enhancements

    • Extended DLSS configuration with preset selection and compatibility filtering
    • Enhanced post-processing pipeline for improved visual quality
    • Added interactive subrect region editor for customization

Copilot AI review requested due to automatic review settings April 12, 2026 02:38
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 12, 2026

📝 Walkthrough

Walkthrough

Introduces 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

Cohort / File(s) Summary
DLSS Enhancer Feature Core
src/Features/DlssEnhancerFeature.h, src/Features/DlssEnhancerFeature.cpp
New primary feature struct with enums (DlssMode, StretchMode, SharpenMode, SubrectBlendMode, PeripheryAAMode), persisted Settings, and comprehensive ImGui UI for DLSS mode, quality, preset, sharpness, periphery AA, and subrect blending configuration. Includes boot-time latching, preset compatibility validation, and shader cache management.
DLSS Enhancer Bridge Interface
src/Features/DlssEnhancer/Bridge.h, src/Features/DlssEnhancer/Bridge.cpp
Single external API layer exposing route enablement, live DLSS configuration forwarding (quality/preset/sharpness), boot sequencing, motion-vector scale computation, and quality-to-render-scale mapping.
DLSS Enhancer Core GPU Pipeline
src/Features/DlssEnhancer/Core.h, src/Features/DlssEnhancer/Core.cpp, src/Features/DlssEnhancer/Modes.cpp
Static GPU resource pool and dispatch engine supporting three VR DLSS execution modes (Default: per-eye DLSS; Faster: dual per-eye evaluations; Extreme: combined strip). Implements per-eye input preparation, output finalization, and mode-specific workflows with optional temporal smoothing and subrect blending.
DLSS Enhancer Primitive Operations
src/Features/DlssEnhancer/Ops.h, src/Features/DlssEnhancer/Params.h, src/Features/DlssEnhancer/Params.cpp
Composable GPU operations (texture creation, resource ensuring, per-eye copy, subrect stretching, temporal smoothing, blending) and unified VRDlssParams struct resolved from runtime state and D3D11 resources.
DLSS Enhancer Post-Processing
src/Features/DlssEnhancer/Preprocess.h, src/Features/DlssEnhancer/Preprocess.cpp, src/Features/DlssEnhancer/Postprocess.h, src/Features/DlssEnhancer/Postprocess.cpp
Pre/post-DLSS encode and post-sharpening stages with texture encoding dispatch and DLSS-routed sharpening supporting both main render-target and DLSSperf test-texture paths.
DLSS Enhancer Compute Shaders
features/DLSS Enhancer/Shaders/Features/PeripheryTemporalSmoothCS.hlsl, features/DLSS Enhancer/Shaders/Features/SubrectBlendCS.hlsl, features/DLSS Enhancer/Shaders/Features/SubrectStretchCS.hlsl
GPU compute kernels for periphery temporal accumulation with motion-adaptive blending, subrect feathering/dithering with noise, and render-to-display resolution stretching with optional blur.
DLSSperf VR Optimization Feature
src/Features/DLSSperf.h, src/Features/DLSSperf.cpp
VR performance feature managing render-target sizing hooks, fake depth-stencil resources, and hybrid post-chain interception with DS/SRV swapping to enable 3k DLSS processing and downscaling to 1k main for pyramid building.
DLSSperf Downscale Shader
features/DLSSperf/Shaders/DLSSperf/BilinearCopyPS.hlsl
Pixel shader performing 3×3 box-filter downscaling from higher resolution to lower resolution.
Upscaling Integration
src/Features/Upscaling.h, src/Features/Upscaling.cpp, src/Features/Upscaling/RCAS/RCAS.h, src/Features/Upscaling/RCAS/RCAS.cpp, src/Features/Upscaling/Streamline.h, src/Features/Upscaling/Streamline.cpp
New DLSS Enhancer route activation logic, active parameter getters (quality/preset/sharpness), DLSS-routed pre/post-processing, DLSSperf jitter/viewport compute, and Streamline API expansion to support explicit output height and dynamic reactive/transparency tag building.
VR Subrect Controller
src/Utils/Subrect/Subrect.h, src/Utils/Subrect/Subrect.cpp
Interactive UV-space subrect cropping editor with preset management, stereo pixel-region conversion, left/right eye synchronization, and JSON persistence.
System Integration & Hooks
src/Feature.cpp, src/Globals.h, src/Globals.cpp, src/Hooks.cpp, src/State.h, src/XSEPlugin.cpp
Global feature registration, render-target resizing hooks for DLSSperf, Draw call viewport interception for UI, BSShaderRenderTargets color-RT enlargement, and increased trampoline allocation.
Feature Configuration
features/DLSS Enhancer/Shaders/Features/DLSSENHANCER.ini
Metadata file declaring DLSS Enhancer shader feature version.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • alandtse
  • doodlum
  • davo0411

🐰 A subrect and a stretch, temporal smooth so neat,
DLSSperf's magic dance makes rendering complete,
Per-eye sharpening whispers through the VR night,
Where DLSS Enhancer blooms in stereo light!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main feature additions: DLSS Enhancer with DLSSperf and subrect blend support.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

No actionable suggestions for changed features.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 a DlssEnhancer pipeline (modes/ops/params/bridge) to drive Streamline DLSS with subrect + blend.
  • Adds DLSSperf sub-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) {
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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().

Suggested change
if (!globals::features::dlssPerf.IsHookActive() && enhancer.settings.enablePerf) {
const bool routeActiveAtBoot = enhancer.IsActive();
if (routeActiveAtBoot && !globals::features::dlssPerf.IsHookActive() && enhancer.settings.enablePerf) {

Copilot uses AI. Check for mistakes.
Comment on lines +415 to +423

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));
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +45
// 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);
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
// 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);

Copilot uses AI. Check for mistakes.
Comment thread src/Globals.cpp
Comment on lines +370 to +373
// Save current D3D VP
UINT numVP = 1;
D3D11_VIEWPORT savedVP;
This->RSGetViewports(&numVP, &savedVP);
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment thread src/Hooks.cpp
Comment on lines +1 to 3
#include "Hooks.h"

#include "ShaderTools/BSShaderHooks.h"
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-pixel GetDimensions() 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 InvSrcSize once from CPU before draw.)
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/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 number VertexCount == 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() and dlssPerf.IsPostChainDone() without synchronization. Per the context snippets, postChainDone is a simple bool modified by EndPostIntercept and cleared by PlayerViewRender_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/1u here 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 for Ensure* resource allocation APIs.

These helpers are void, so callers cannot explicitly branch on allocation/setup failure. Returning status (like your PreparePerEyeInputs/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

📥 Commits

Reviewing files that changed from the base of the PR and between f4a7ecc and 574a4cc.

📒 Files selected for processing (35)
  • features/DLSS Enhancer/Shaders/Features/DLSSENHANCER.ini
  • features/DLSS Enhancer/Shaders/Features/PeripheryTemporalSmoothCS.hlsl
  • features/DLSS Enhancer/Shaders/Features/SubrectBlendCS.hlsl
  • features/DLSS Enhancer/Shaders/Features/SubrectStretchCS.hlsl
  • features/DLSSperf/Shaders/DLSSperf/BilinearCopyPS.hlsl
  • src/Feature.cpp
  • src/Features/DLSSperf.cpp
  • src/Features/DLSSperf.h
  • src/Features/DlssEnhancer/Bridge.cpp
  • src/Features/DlssEnhancer/Bridge.h
  • src/Features/DlssEnhancer/Core.cpp
  • src/Features/DlssEnhancer/Core.h
  • src/Features/DlssEnhancer/Modes.cpp
  • src/Features/DlssEnhancer/Ops.h
  • src/Features/DlssEnhancer/Params.cpp
  • src/Features/DlssEnhancer/Params.h
  • src/Features/DlssEnhancer/Postprocess.cpp
  • src/Features/DlssEnhancer/Postprocess.h
  • src/Features/DlssEnhancer/Preprocess.cpp
  • src/Features/DlssEnhancer/Preprocess.h
  • src/Features/DlssEnhancerFeature.cpp
  • src/Features/DlssEnhancerFeature.h
  • src/Features/Subrect/Subrect.cpp
  • src/Features/Subrect/Subrect.h
  • src/Features/Upscaling.cpp
  • src/Features/Upscaling.h
  • src/Features/Upscaling/RCAS/RCAS.cpp
  • src/Features/Upscaling/RCAS/RCAS.h
  • src/Features/Upscaling/Streamline.cpp
  • src/Features/Upscaling/Streamline.h
  • src/Globals.cpp
  • src/Globals.h
  • src/Hooks.cpp
  • src/State.h
  • src/XSEPlugin.cpp

Comment on lines +36 to +46
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));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Suggested change
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.

Comment on lines +49 to +53
// 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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Comment thread src/Features/DlssEnhancer/Core.cpp
Comment on lines +75 to +80
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));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Comment on lines +41 to +47
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();
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
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.

Comment thread src/Features/DlssEnhancer/Preprocess.cpp
Comment thread src/Features/DLSSperf.cpp
Comment on lines +100 to +110
// 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);
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Comment on lines +1699 to +1728
// --- 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;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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");
 	}

Comment on lines +577 to +597
// 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);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, check the file size and structure
wc -l src/Features/Upscaling/Streamline.cpp

Repository: 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.cpp

Repository: 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 -100

Repository: 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 h

Repository: 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 2

Repository: 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.cpp

Repository: 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 5

Repository: 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 3

Repository: 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/null

Repository: 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 -20

Repository: 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 -150

Repository: 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 2

Repository: 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 -5

Repository: 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 -40

Repository: 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 2

Repository: 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.

Comment thread src/Hooks.cpp
Comment on lines +1097 to +1118
// 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;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 12, 2026

✅ A pre-release build is available for this PR:
Download

Comment thread src/State.h

// Features that are more special then others
std::unordered_map<std::string, bool> specialFeatures = {
{ "DLSSENHANCER", false },
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing is allowed to be a special feature. You must respect the feature boundary including hooking from the feature.

Copy link
Copy Markdown
Collaborator

@doodlum doodlum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/Hooks.cpp
// 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) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just detour

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),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is pyramid

return;
}

// --- Legacy upscaling path (upstream) ---
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are you touching this

Comment thread src/Features/DLSSperf.h
// 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.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

english please :D

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this a separate feature. features should not be dependent on other features.

CS Dev and others added 2 commits April 19, 2026 19:49
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.
@YtzyFvra YtzyFvra force-pushed the feature/DLSSenhancer branch from 574a4cc to 7e98dbe Compare April 19, 2026 11:50
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

♻️ Duplicate comments (1)
src/Features/DlssEnhancer/Preprocess.cpp (1)

39-67: ⚠️ Potential issue | 🔴 Critical

motionVectorCopyTexture still 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 dereference upscaling.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 == kDLSS by 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 the GetEnhancerEncodeTexturesCS helper also unconditionally injects the DLSS define (Line 15) regardless of methodIndex, 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; add Implements #... 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 similarly UIPassDispatch_Hook::thunk) you rewrite kmainRT.SRV, kmainCopyRT.SRV, and 16 entries of kmainDS.views/readOnlyViews (plus the VR viewport), then call func(...) and restore. If anything below func throws or long-jumps, the engine is left permanently pointed at testTextureSRV / 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: DownscaleToKMain saves/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) and EnsureExtremeStripTextures (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

📥 Commits

Reviewing files that changed from the base of the PR and between 574a4cc and d6b1b58.

📒 Files selected for processing (37)
  • CMakeLists.txt
  • features/DLSS Enhancer/Shaders/Features/DLSSENHANCER.ini
  • features/DLSS Enhancer/Shaders/Features/PeripheryTemporalSmoothCS.hlsl
  • features/DLSS Enhancer/Shaders/Features/SubrectBlendCS.hlsl
  • features/DLSS Enhancer/Shaders/Features/SubrectStretchCS.hlsl
  • features/DLSSperf/Shaders/DLSSperf/BilinearCopyPS.hlsl
  • src/Feature.cpp
  • src/Features/DLSSperf.cpp
  • src/Features/DLSSperf.h
  • src/Features/DlssEnhancer/Bridge.cpp
  • src/Features/DlssEnhancer/Bridge.h
  • src/Features/DlssEnhancer/Core.cpp
  • src/Features/DlssEnhancer/Core.h
  • src/Features/DlssEnhancer/Modes.cpp
  • src/Features/DlssEnhancer/Ops.h
  • src/Features/DlssEnhancer/Params.cpp
  • src/Features/DlssEnhancer/Params.h
  • src/Features/DlssEnhancer/Postprocess.cpp
  • src/Features/DlssEnhancer/Postprocess.h
  • src/Features/DlssEnhancer/Preprocess.cpp
  • src/Features/DlssEnhancer/Preprocess.h
  • src/Features/DlssEnhancerFeature.cpp
  • src/Features/DlssEnhancerFeature.h
  • src/Features/Upscaling.cpp
  • src/Features/Upscaling.h
  • src/Features/Upscaling/RCAS/RCAS.cpp
  • src/Features/Upscaling/RCAS/RCAS.h
  • src/Features/Upscaling/Streamline.cpp
  • src/Features/Upscaling/Streamline.h
  • src/Globals.cpp
  • src/Globals.h
  • src/Hooks.cpp
  • src/State.h
  • src/Utils/Subrect/Subrect.cpp
  • src/Utils/Subrect/Subrect.h
  • src/XSEPlugin.cpp
  • vcpkg.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

Comment thread CMakeLists.txt Outdated
Comment on lines +97 to +102
target_include_directories(
${PROJECT_NAME}
BEFORE
PRIVATE
"${CMAKE_BINARY_DIR}/vcpkg_installed/x64-windows-static-md/include/directx"
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 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 -50

Repository: doodlum/skyrim-community-shaders

Length of output: 1623


🏁 Script executed:

rg -n 'VCPKG_TARGET_TRIPLET|"name":\s*"' CMakePresets.json | head -30

Repository: doodlum/skyrim-community-shaders

Length of output: 569


🏁 Script executed:

rg -n 'Microsoft::DirectX-Headers' --type cmake

Repository: 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 -40

Repository: 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.txt

Repository: 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".

Comment on lines +12 to +52
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;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 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 15

Repository: 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 -50

Repository: 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 5

Repository: 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_TYPELESSR8G8B8A8_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).

Comment on lines +264 to +290
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;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Comment on lines +436 to +450
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);
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Comment on lines +659 to +674
// 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);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Comment on lines +70 to +75
ID3D11ComputeShader* cs = GetEnhancerEncodeTexturesCS(upscaling, upscaleMethod);
if (!cs) {
state->EndPerfEvent();
logger::error("[DLSSENHANCER] Failed to get encode compute shader");
return false;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
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).

Comment on lines +52 to +62
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;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

@YtzyFvra YtzyFvra force-pushed the feature/DLSSenhancer branch from d6b1b58 to d3c2bd7 Compare April 19, 2026 12:46
Automated formatting by clang-format, prettier, and other hooks.
See https://pre-commit.ci for details.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
src/Utils/Subrect/Subrect.cpp (1)

52-60: ⚠️ Potential issue | 🟡 Minor

Guard zero-sized eye dimensions before subtracting.

eyeWidth - 1 and eyeHeight - 1 underflow 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=hpp

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 - 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, add Implements #<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

📥 Commits

Reviewing files that changed from the base of the PR and between d6b1b58 and 1bee4e9.

📒 Files selected for processing (3)
  • src/Features/DlssEnhancerFeature.h
  • src/Utils/Subrect/Subrect.cpp
  • src/Utils/Subrect/Subrect.h

Comment on lines +31 to +32
#include "Feature.h"
#include "Utils/Subrect/Subrect.h"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 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=hpp

Repository: 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 -100

Repository: 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 f

Repository: 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 f

Repository: doodlum/skyrim-community-shaders

Length of output: 91


🏁 Script executed:

#!/bin/bash
cat -n ./src/Features/DlssEnhancerFeature.h | head -150

Repository: doodlum/skyrim-community-shaders

Length of output: 7656


🏁 Script executed:

#!/bin/bash
cat -n ./src/Feature.h | head -50

Repository: doodlum/skyrim-community-shaders

Length of output: 1958


🏁 Script executed:

#!/bin/bash
cat -n ./src/Utils/Subrect/Subrect.h | head -30

Repository: 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.

Suggested change
#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.

Comment on lines +137 to +139
// DLSSperf sub-feature: latched at first boot, change requires restart
bool IsPerfEnabled() const { return perfEnabledAtBoot; }
void LatchPerfEnabled() { perfEnabledAtBoot = (settings.enablePerf != 0); }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 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=hpp

Repository: 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 h

Repository: 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 {} -n

Repository: 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 3

Repository: 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 h

Repository: doodlum/skyrim-community-shaders

Length of output: 139


🏁 Script executed:

#!/bin/bash
# Find IsRuntimeSupported implementation
rg -n 'bool.*IsRuntimeSupported' --type c -A 5

Repository: doodlum/skyrim-community-shaders

Length of output: 493


🏁 Script executed:

#!/bin/bash
# Find InstallRenderTargetSizeHook implementation
rg -n 'InstallRenderTargetSizeHook' --type c -A 10

Repository: 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 -20

Repository: 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 c

Repository: 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 5

Repository: 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 10

Repository: doodlum/skyrim-community-shaders

Length of output: 58


🏁 Script executed:

#!/bin/bash
# Find BootSequence in Bridge.cpp
cat src/Features/DlssEnhancer/Bridge.cpp | head -100

Repository: 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.cpp

Repository: 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.

Comment on lines +1 to +3
#pragma once

#include <vector>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 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=hpp

Repository: 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.h

Repository: 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=h

Repository: 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 -10

Repository: 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 -20

Repository: 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 -50

Repository: 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.h

Repository: 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 -10

Repository: 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.

Suggested change
#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.

@alandtse alandtse added deferred Defer to next cycle and removed deferred Defer to next cycle labels Apr 29, 2026
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 16, 2026
…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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 16, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 16, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 16, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 16, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 19, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 20, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 22, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 22, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 22, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 22, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 22, 2026
…(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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 22, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 22, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 22, 2026
…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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 22, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 22, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 22, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 22, 2026
…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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 22, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 22, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 22, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 23, 2026
…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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 23, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 23, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 23, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 23, 2026
…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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 23, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 23, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 23, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 23, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 23, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 23, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 23, 2026
…(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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 23, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 23, 2026
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>
alandtse added a commit to alandtse/open-shaders that referenced this pull request May 24, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants