Skip to content

feat(VR): add foveated rendering#44

Merged
alandtse merged 20 commits into
devfrom
pr3-dlss-foveated
May 28, 2026
Merged

feat(VR): add foveated rendering#44
alandtse merged 20 commits into
devfrom
pr3-dlss-foveated

Conversation

@alandtse
Copy link
Copy Markdown
Owner

@alandtse alandtse commented May 25, 2026

Summary

Lifts the foveated subrect rendering path from upstream PR community-shaders#2096 and decomposes it into a focused, ship-ready PR on top of the merged PerfMode base (#24, #42). Renders the visual-center subrect at full upscaler quality while the periphery uses the stretch path — large perceived-quality win per VR-attention dollar at modest cost.

Generic naming, DLSS as the backend. Foveated rendering is an upscaler-agnostic technique (NVIDIA's VRS, Meta Quest eye-tracking foveation, Vive Pro 2's FFR all express the same idea). The original DlssEnhancer naming was historical, not algorithmic. Following PR #42's PerfMode generalization precedent, this PR ships the generic FoveatedRender naming. The concrete implementation is currently DLSS-only.

Architecturally folded into Upscaling (mirrors PerfMode after #42): struct FoveatedRender inside src/Features/Upscaling/, shaders under features/Upscaling/Shaders/Upscaling/FoveatedRender/. Drops the Feature boilerplate and the DLSSENHANCER.ini placeholder. Rebased onto current dev with the DLSSperf → PerfMode rename applied throughout.

Scope & non-scope

  • In scope: Bridge + Params + Pre/Postprocess (Core + Ops), Modes (incl. bot-flagged per-eye fix from the original PR), SubrectStretchCS shader, Upscale + globals wiring. Generic FoveatedRender naming throughout.
  • DLSS-only this PR (implementation boundary, not architectural): three gates remain upscaleMethod == kDLSS because the concrete implementation is DLSS-specific:
    • Preprocess.cpp early-return — non-DLSS path not implemented.
    • Main_PostProcessing sharpening route — DLSS-specific routing through FoveatedRender's SharpenMode.
    • motionVectorCopyTexture UAV ternary — DLSS-specific MV format.
  • Out of scope: SubrectBlendCS, PeripheryTemporalSmoothCS, kExtreme mode, restored Settings/UI surface. Branch pr3b-dlss-foveated-extras prepped for the follow-up stack.

Test plan

  • DevMode off, default off → identical behavior to dev baseline (no foveated path armed). No regressions.
  • VR + DLSS + FoveatedRender enabled → visual-center subrect renders at full DLSS, periphery uses stretch path. No left/right per-eye mismatch (regression test for the bot-flagged fix lifted from feat(VR): add DLSS Enhancer with DLSSperf and subrect blend community-shaders/skyrim-community-shaders#2096).
  • Toggle subrect ratio in UI; sharp region scales without crash; no flicker on slider drag.
  • VR + FSR (no FoveatedRender activation in this PR) → falls through cleanly; PerfMode's FSR support remains unaffected.
  • Flatrim + non-VR builds remain unaffected (DLSS-gated; non-VR paths fall through).
  • CI: vs2026 build, Flatrim + VR shader validation, C++ unit tests, CodeQL.

Co-Authored-By: YtzyFvra 59631290+YtzyFvra@users.noreply.github.com

Summary by CodeRabbit

  • New Features

    • VR-only Foveated DLSS with two execution strategies (default / faster) and optional subrect rendering to improve VR performance.
    • Background stretch sampling modes: bilinear, point, and 9-tap blur; optional periphery debug tint.
    • VR settings UI: boot-latched enable/quality, runtime checks, tuning panel, and persistent settings.
  • Improvements

    • DLSS sharpening routed through the foveated path when active; preprocessing supports masks and motion vectors.
  • Bug Fixes

    • Screenshot preview blend updated to avoid background bleed-through.

Review Change Stack

Recent updates (post-review)

Four commits pushed on top of the review-sweep rebase:

  • fix(VR): foveated route honors DLSSperf and skips in menus — three correctness fixes from interactive testing:
    • DLSSperf + Foveated black-world: Params::Resolve now routes through PerfMode's testTexture so the perf-mode chain is preserved when Foveated is also active.
    • Subrect-region-black: Streamline's SetDLSSOptions / EvaluateDLSS take an outputHeight parameter; the foveated path now passes subOutH so DLSS isn't configured for subOutW × eyeHeightOut when extentOut declares subOutW × subOutH.
    • Menu-region garbage: the foveated route now skips when a menu is open and falls through to standard DLSS.
  • refactor(util): lift OpaquePreviewBlendCallback to Util::Subrect — DRY: ScreenshotFeature and FoveatedRender share the helper instead of duplicating it.
  • refactor(VR): foveated sharpening — sharpnessDLSS=0 is the single disable — drops the redundant SharpenMode setting; sharpnessDLSS == 0 is now the single disable signal.
  • feat(VR): foveated UX polish — visualizer, nasal preset, dead UI removal — debug visualizer (red tint on the cheap-stretched periphery), asymmetric "Nasal Convergence 50%" preset, Enable moved to top level and body greyed-out when disabled, sliders converted to checkboxes where appropriate, beefier stretch-mode tooltips, and dead encoder UI removed (deferred to PR-3b).

Copilot AI review requested due to automatic review settings May 25, 2026 21:33
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 25, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR implements a VR-only FoveatedRender subsystem routing DLSS through per-eye subrects: settings/UI, Bridge, VRDlssParams and Ops, Core GPU resources, two execution modes, DLSS-specific preprocess and sharpen postprocess, a subrect stretch compute shader, and integration into Upscaling with boot-latched wiring.

Changes

VR Foveated DLSS Upscaling

Layer / File(s) Summary
FoveatedRender module and UI
src/Features/Upscaling/FoveatedRender.h, src/Features/Upscaling/FoveatedRender.cpp
Defines the FoveatedRender struct, enums, Settings, JSON save/load, defaults, ClampSettings, activation/runtime accessors, boot-latching, and DrawEnable/DrawSettings UI with subrect editor.
Bridge interface & MV scaling
src/Features/Upscaling/FoveatedRender/Bridge.h, src/Features/Upscaling/FoveatedRender/Bridge.cpp
Exposes route activity check, live quality/preset/sharpness forwarding, BootSequence, ComputeMvecScale, and render-scale mapping for callers.
Params and Ops helpers
src/Features/Upscaling/FoveatedRender/Params.h, src/Features/Upscaling/FoveatedRender/Params.cpp, src/Features/Upscaling/FoveatedRender/Ops.h
Adds VRDlssParams::Resolve and Ops declarations for texture creation/ensuring, per-eye prepare/finalize, SBS snapshot, stretching utilities, blending, and UV+mode hashing.
Core API & resource holders
src/Features/Upscaling/FoveatedRender/Core.h, src/Features/Upscaling/FoveatedRender/Core.cpp
Declares Core entrypoints (ExecuteVRDlssCore, Prepare/Finalize), static inline GPU resource holders (intermediates, subrect/faster textures, stretch CS/CB/sampler) and implements safe texture creation, per-eye intermediate management, Prepare/Finalize copies, SnapshotSBS, compute-shader DRS stretching, Ensure helpers, and ClearResources/ClearShaderCache.
DLSS execution strategies (default & faster)
src/Features/Upscaling/FoveatedRender/Modes.cpp
Implements ExecuteVRDlssCore router with UV/mode hash detection and two modes: default (full-eye or subrect per-eye DLSS with snapshot+stretch) and faster (direct kMAIN evaluation using per-eye extents).
Preprocessing and postprocessing
src/Features/Upscaling/FoveatedRender/Preprocess.h, src/Features/Upscaling/FoveatedRender/Preprocess.cpp, src/Features/Upscaling/FoveatedRender/Postprocess.h, src/Features/Upscaling/FoveatedRender/Postprocess.cpp
Implements DLSS-specific EncodeUpscalingTextures (cached compute shader with DLSS define, required UAV/SRV binding and dispatch) and ApplyDlssSharpening (in-place RCAS sharpening via sharpener UAV and render-target copy-back).
Subrect stretch compute shader
features/Upscaling/Shaders/Upscaling/FoveatedRender/SubrectStretchCS.hlsl
HLSL compute shader (8x8) with StretchCB and main: maps per-eye output pixels to source SBS UVs, supports bilinear, nearest, and 3x3 Gaussian blur sampling modes, and writes to destination UAV with per-eye offset; includes DebugVisualize tint.
Integration into Upscaling & runtime plumbing
src/Features/Upscaling.h, src/Features/Upscaling.cpp, src/Features/Upscaling/Streamline.*, src/Hooks.cpp, src/Features/Upscaling/PerfMode.*
Adds foveatedRender static instance, wires settings UI and nested JSON save/load, delegates lifecycle hooks, routes Upscaling::Upscale through Preprocess + Core when Bridge route active with fallback, uses foveated sharpening when active, updates Streamline APIs to accept optional subrect output height, and latches boot-time state in Hooks.
Subrect util and Screenshot integration
src/Utils/Subrect.h, src/Utils/Subrect.cpp, src/Utils/Subrect_PreviewBlend.cpp, src/Features/ScreenshotFeature.cpp
Introduces Util::Subrect::OpaquePreviewBlendCallback declaration and implementation, documents DrawEditor preview blending behavior, and updates ScreenshotFeature to use the shared callback rather than a local function.

Sequence Diagram

sequenceDiagram
  participant VRApp
  participant Upscaling
  participant FoveatedBridge
  participant Preprocess
  participant FoveatedCore
  participant Streamline
  participant Postprocess
  VRApp->>Upscaling: Upscale(upscalingTexture, depth, reactive, transparency, mvec)
  Upscaling->>FoveatedBridge: IsRouteActive()
  FoveatedBridge-->>Upscaling: true/false
  alt route active
    Upscaling->>Preprocess: EncodeUpscalingTextures()
    Preprocess-->>Upscaling: prepared masks/UAVs
    Upscaling->>FoveatedCore: ExecuteVRDlssCore(streamline, textures)
    FoveatedCore->>FoveatedCore: VRDlssParams::Resolve()
    FoveatedCore->>FoveatedCore: PrepareVRPerEyeInputs()
    FoveatedCore->>Streamline: EvaluateDLSS(left eye)
    Streamline-->>FoveatedCore: left eye DLSS output
    FoveatedCore->>Streamline: EvaluateDLSS(right eye)
    Streamline-->>FoveatedCore: right eye DLSS output
    FoveatedCore->>FoveatedCore: FinalizeVRPerEyeOutputs()
    FoveatedCore-->>Upscaling: final color DST
    Upscaling->>Postprocess: ApplyDlssSharpening()
    Postprocess-->>Upscaling: sharpened output
  else fallback
    Upscaling->>Streamline: streamline.Upscale(...)
    Streamline-->>Upscaling: standard upscale
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • alandtse/open-shaders#23: Extends Util::Subrect APIs used by this PR (stereo/subrect UV handling), tightly related at the API/consumer level.

Poem

🐰 I hop through pixels, soft and bright,

Stretching left and right with shader might.
DLSS dreams in subrected streams,
VR vistas bloom like springtime beams,
A rabbit cheers for rendered light.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(VR): add foveated rendering' clearly and concisely describes the main addition—a new foveated rendering feature for VR. It is specific, directly related to the primary changeset, and follows conventional commit conventions.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pr3-dlss-foveated

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

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

Lifts the DlssEnhancer foveated-subrect path from upstream PR-2096 and decomposes it into an MVP slice that lives as a mode inside Upscaling (mirroring the DLSSperf pattern from #24). When enabled, the visual-center subrect is upscaled at full DLSS quality and the periphery is filled by a cheap compute-shader stretch from a render-resolution snapshot.

Changes:

  • Adds DlssEnhancer as a static inline member of Upscaling with its own settings, subrect controller, UI panel, and JSON round-trip nested under a dlssEnhancer key.
  • Adds the DlssEnhancerImpl namespace (Bridge, Core, Ops, Modes, Params, Preprocess, Postprocess) implementing per-eye DLSS dispatch in Default and Faster modes, with graceful fallback to the standard DLSS path on preflight failure.
  • Adds SubrectStretchCS.hlsl (Bilinear / Point / Gaussian Blur) for periphery stretch, plus a boot latch in BSShaderRenderTargets::Create and route gating wired into Upscaling::Upscale and Main_PostProcessing.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/Hooks.cpp Calls DlssEnhancerImpl::Bridge::BootSequence() after engine RT init to latch enable/quality.
src/Features/Upscaling/DLSSperf.cpp Comment-only namespace rename to DlssEnhancerImpl::Bridge.
src/Features/Upscaling/DlssEnhancer.{h,cpp} New mode class: settings, UI, preset clamping, lifecycle, accessors.
src/Features/Upscaling/DlssEnhancer/Bridge.{h,cpp} Single contact point exposing route-active/quality/sharpness/preset and motion-vector scale.
src/Features/Upscaling/DlssEnhancer/Core.{h,cpp} Owns per-eye/subrect intermediates, stretch CS resources, ensure/finalize helpers, mode dispatch.
src/Features/Upscaling/DlssEnhancer/Modes.cpp Implements Default (per-eye + subrect) and Faster (SBS-extent) DLSS execution strategies.
src/Features/Upscaling/DlssEnhancer/Ops.h Declarations of GPU primitives used by Modes.
src/Features/Upscaling/DlssEnhancer/Params.{h,cpp} VRDlssParams::Resolve builds the per-frame parameter block from global state.
src/Features/Upscaling/DlssEnhancer/Preprocess.{h,cpp} DLSS-only encode pass for reactive/transparency/MV masks.
src/Features/Upscaling/DlssEnhancer/Postprocess.{h,cpp} RCAS sharpening wrapper invoked when the route is active.
src/Features/Upscaling.{h,cpp} Adds dlssEnhancer member, UI tree node, nested settings I/O, and route gating in Upscale() and Main_PostProcessing.
features/Upscaling/Shaders/Upscaling/DlssEnhancer/SubrectStretchCS.hlsl New compute shader implementing Bilinear/Point/Gaussian periphery stretch.

Comment thread src/Features/Upscaling/DlssEnhancer/Core.cpp Outdated
alandtse added a commit that referenced this pull request May 25, 2026
The refactor commit (a9e9d9f) moved the shader from
features/DLSS Enhancer/Shaders/Features/SubrectStretchCS.hlsl
to features/Upscaling/Shaders/Upscaling/DlssEnhancer/SubrectStretchCS.hlsl
but missed updating the runtime CompileShader path in Core.cpp, which still
pointed at Data/Shaders/Features/SubrectStretchCS.hlsl.

With the old path the compile silently fails, vrSubrectStretchCS stays
null, and StretchDRSToFullEye early-returns — the periphery stretch (and
therefore the entire foveated subrect path) produces no output. Caught by
Copilot on PR #44.

Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 25, 2026

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

Copilot AI review requested due to automatic review settings May 26, 2026 03:04
@alandtse alandtse force-pushed the pr3-dlss-foveated branch from 076107b to 48987ba Compare May 26, 2026 03:04
alandtse added a commit that referenced this pull request May 26, 2026
The refactor commit (a9e9d9f) moved the shader from
features/DLSS Enhancer/Shaders/Features/SubrectStretchCS.hlsl
to features/Upscaling/Shaders/Upscaling/DlssEnhancer/SubrectStretchCS.hlsl
but missed updating the runtime CompileShader path in Core.cpp, which still
pointed at Data/Shaders/Features/SubrectStretchCS.hlsl.

With the old path the compile silently fails, vrSubrectStretchCS stays
null, and StretchDRSToFullEye early-returns — the periphery stretch (and
therefore the entire foveated subrect path) produces no output. Caught by
Copilot on PR #44.

Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com>
Copy link
Copy Markdown

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

Copilot reviewed 19 out of 19 changed files in this pull request and generated 3 comments.

Comment thread src/Features/Upscaling/FoveatedRender.cpp
Comment thread src/Features/Upscaling/FoveatedRender.cpp Outdated
Comment thread src/Features/Upscaling/FoveatedRender/Core.cpp
@alandtse alandtse changed the title feat(VR): DlssEnhancer foveated subrect (PR-3 MVP-B) feat(VR): FoveatedRender (DLSS backend, PR-3 MVP-B) May 26, 2026
Copy link
Copy Markdown

@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: 6

🧹 Nitpick comments (3)
src/Features/Upscaling/FoveatedRender/Preprocess.cpp (1)

44-48: 💤 Low value

Consider validating render target SRVs for robustness.

The SRVs from render targets (lines 44-47) are used directly without null checks. While the renderer is expected to be valid at this point, defensive validation would prevent potential crashes from unexpected state. This is a lower-priority concern compared to the motionVectorCopyTexture issue.

Also applies to: 60-61

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Features/Upscaling/FoveatedRender/Preprocess.cpp` around lines 44 - 48,
The render target SRVs retrieved via renderer->GetRuntimeData().renderTargets
(motionVector, temporalAAMask, normals) and
renderer->GetDepthStencilData().depthStencils (depth) are used without null
checks; add defensive validation before usage: verify each SRV/target is
non-null (and has expected format/state) and early-return or fallback if any
check fails to avoid crashes, and apply the same validation for the later uses
referenced around the temporalAAMask/motionVector copy paths (the code that
relies on these SRVs and Util::GetScreenDispatchCount(true)); reference the
symbols motionVector, temporalAAMask, normals, depth,
renderer->GetRuntimeData(), and GetDepthStencilData() when implementing these
checks.
src/Features/Upscaling/FoveatedRender.h (1)

3-20: 💤 Low value

Conventional commit + issue-link wording can be tightened.

Suggested title: feat(vr): add foveated render route wiring
Suggested PR body footer: Implements #44 (or `Closes `#44 if fully complete).

As per coding guidelines "When reviewing PRs, please provide suggestions for Conventional Commit Titles" and "Suggest adding appropriate GitHub keywords".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Features/Upscaling/FoveatedRender.h` around lines 3 - 20, Update the PR's
conventional commit title and PR body footer: change the commit title to
"feat(vr): add foveated render route wiring" and add a footer line in the PR
body like "Implements `#44`" (or "Closes `#44`" if the PR fully resolves the issue).
Apply these changes to the PR/commit metadata for the change described in
src/Features/Upscaling/FoveatedRender.h so it follows the repo's Conventional
Commit and issue-linking guidelines.
src/Features/Upscaling/FoveatedRender/Params.h (1)

1-1: 💤 Low value

Suggest a conventional commit title + issue keyword in PR body.

Current title is close, but to align with repo guidance I’d suggest a shorter conventional form, e.g. feat(vr): add dlss foveated subrect mvp-b, and include an issue keyword like Implements #44`` in the PR description.

As per coding guidelines: "Conventional Commit Titles ... Format: type(scope): description" and "Issue References ... 'Implements #123'".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Features/Upscaling/FoveatedRender/Params.h` at line 1, Update the PR
title to follow Conventional Commits format and add an issue reference in the PR
body: use a short title like "feat(vr): add dlss foveated subrect mvp-b" and add
an issue keyword line such as "Implements `#44`" in the description; reference the
change affecting src/Features/Upscaling/FoveatedRender/Params.h in the PR text
so reviewers know which component (FoveatedRender/Params) the commit touches.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Features/Upscaling/FoveatedRender.cpp`:
- Around line 53-57: Update FoveatedRender::LoadSettings to avoid the const_cast
and to fail-closed when JSON is malformed: call
subrectController.LoadSettings(o_json) (no const_cast) since
Util::Subrect::Controller::LoadSettings already accepts const json&, and wrap
the Settings deserialization so you initialize settings to a safe default
(Settings default ctor) and then attempt to populate it from o_json in a
try/catch (or check types) — on any exception or invalid shape keep the default
settings and then call ClampSettings(); also ensure any JSON->Settings
conversion uses explicit parsing (e.g., json::get or get_to) inside the guarded
block so malformed input doesn’t overwrite defaults.

In `@src/Features/Upscaling/FoveatedRender/Bridge.cpp`:
- Around line 15-27: The three Bridge getters
(FoveatedRenderImpl::Bridge::GetQualityMode, GetPresetDLSS, GetSharpnessDLSS)
currently return raw shared settings even when the route is inactive; update
each to first check IsRouteActive() and if false return the neutral/identity
value by delegating to the FoveatedRender instance's clamped-active getters
(i.e., forward to the FoveatedRender methods that enforce the inactive-route
clamping), otherwise return the active/clamped value as before.

In `@src/Features/Upscaling/FoveatedRender/Core.cpp`:
- Around line 13-18: CreateTextureFromSource currently assumes src is a valid
ID3D11Texture2D and dereferences it directly; change it to validate src is
non-null and to QueryInterface for ID3D11Texture2D (or otherwise check runtime
type) and return nullptr (and log) if QueryInterface fails to avoid crashes on
non-texture resources. In StretchDRSToFullEye, ensure you do not call
CSSetConstantBuffers and Dispatch when context->Map fails: either return early
on Map failure or implement a fallback path that populates the constant buffer
via UpdateSubresource (or a safe default initialization) before binding and
dispatching, so Dispatch never runs with an uninitialized/stale constant buffer.
- Around line 309-334: In StretchDRSToFullEye ensure you abort binding/dispatch
when context->Map(Core::vrSubrectStretchCB.get(), ...) fails: currently the code
only updates the cb when SUCCEEDED(Map) but always proceeds to bind
Core::vrSubrectStretchCB and call Dispatch, so change the control flow to
return/skip the bind+Dispatch if Map fails (or wrap the bind/Dispatch in the
SUCCEEDED(Map) block) so the shader never runs with stale data from
Core::vrSubrectStretchCB.

In `@src/Features/Upscaling/FoveatedRender/Modes.cpp`:
- Around line 92-93: ExecuteDefaultMode and ExecuteFasterMode call
StretchDRSBothEyes with p.colorDstUAV but do not guard against a null UAV;
update both ExecuteDefaultMode and ExecuteFasterMode to check if p.colorDstUAV
is null before calling StretchDRSBothEyes (and likewise ensure
StretchDRSToFullEye does not call CSSetUnorderedAccessViews/Dispatch with a null
UAV), and if null return false immediately so the router will fall back to the
standard DLSS path; reference the functions ExecuteDefaultMode,
ExecuteFasterMode, StretchDRSBothEyes, StretchDRSToFullEye and the UAV variable
p.colorDstUAV when making the change.

In `@src/Features/Upscaling/FoveatedRender/Preprocess.cpp`:
- Around line 39-42: The resource validation misses checking
upscaling.motionVectorCopyTexture, which is later dereferenced when
upscaleMethod == kDLSS; update the existing null-check that tests
upscaling.upscalingDataCB, upscaling.reactiveMaskTexture, and
upscaling.transparencyCompositionMaskTexture to also include
upscaling.motionVectorCopyTexture and return false (with the same logger::error)
if it's null so motionVectorCopyTexture->uav.get() is never dereferenced when
uninitialized.

---

Nitpick comments:
In `@src/Features/Upscaling/FoveatedRender.h`:
- Around line 3-20: Update the PR's conventional commit title and PR body
footer: change the commit title to "feat(vr): add foveated render route wiring"
and add a footer line in the PR body like "Implements `#44`" (or "Closes `#44`" if
the PR fully resolves the issue). Apply these changes to the PR/commit metadata
for the change described in src/Features/Upscaling/FoveatedRender.h so it
follows the repo's Conventional Commit and issue-linking guidelines.

In `@src/Features/Upscaling/FoveatedRender/Params.h`:
- Line 1: Update the PR title to follow Conventional Commits format and add an
issue reference in the PR body: use a short title like "feat(vr): add dlss
foveated subrect mvp-b" and add an issue keyword line such as "Implements `#44`"
in the description; reference the change affecting
src/Features/Upscaling/FoveatedRender/Params.h in the PR text so reviewers know
which component (FoveatedRender/Params) the commit touches.

In `@src/Features/Upscaling/FoveatedRender/Preprocess.cpp`:
- Around line 44-48: The render target SRVs retrieved via
renderer->GetRuntimeData().renderTargets (motionVector, temporalAAMask, normals)
and renderer->GetDepthStencilData().depthStencils (depth) are used without null
checks; add defensive validation before usage: verify each SRV/target is
non-null (and has expected format/state) and early-return or fallback if any
check fails to avoid crashes, and apply the same validation for the later uses
referenced around the temporalAAMask/motionVector copy paths (the code that
relies on these SRVs and Util::GetScreenDispatchCount(true)); reference the
symbols motionVector, temporalAAMask, normals, depth,
renderer->GetRuntimeData(), and GetDepthStencilData() when implementing these
checks.
🪄 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: baeac1e1-7511-4d57-a17e-26e6ceaa3894

📥 Commits

Reviewing files that changed from the base of the PR and between c97626c and c877f85.

📒 Files selected for processing (20)
  • features/Upscaling/Shaders/Upscaling/FoveatedRender/SubrectStretchCS.hlsl
  • src/Features/Upscaling.cpp
  • src/Features/Upscaling.h
  • src/Features/Upscaling/FoveatedRender.cpp
  • src/Features/Upscaling/FoveatedRender.h
  • src/Features/Upscaling/FoveatedRender/Bridge.cpp
  • src/Features/Upscaling/FoveatedRender/Bridge.h
  • src/Features/Upscaling/FoveatedRender/Core.cpp
  • src/Features/Upscaling/FoveatedRender/Core.h
  • src/Features/Upscaling/FoveatedRender/Modes.cpp
  • src/Features/Upscaling/FoveatedRender/Ops.h
  • src/Features/Upscaling/FoveatedRender/Params.cpp
  • src/Features/Upscaling/FoveatedRender/Params.h
  • src/Features/Upscaling/FoveatedRender/Postprocess.cpp
  • src/Features/Upscaling/FoveatedRender/Postprocess.h
  • src/Features/Upscaling/FoveatedRender/Preprocess.cpp
  • src/Features/Upscaling/FoveatedRender/Preprocess.h
  • src/Features/Upscaling/PerfMode.cpp
  • src/Features/Upscaling/PerfMode.h
  • src/Hooks.cpp

Comment thread src/Features/Upscaling/FoveatedRender.cpp
Comment thread src/Features/Upscaling/FoveatedRender/Bridge.cpp Outdated
Comment thread src/Features/Upscaling/FoveatedRender/Core.cpp
Comment thread src/Features/Upscaling/FoveatedRender/Core.cpp Outdated
Comment thread src/Features/Upscaling/FoveatedRender/Modes.cpp
Comment thread src/Features/Upscaling/FoveatedRender/Preprocess.cpp
alandtse added a commit that referenced this pull request May 26, 2026
Ten inline findings from Copilot + CodeRabbit on PR-3 FoveatedRender:

- Upscaling::LoadSettings ordering: FoveatedRender::ClampSettings touches
  sibling presetDLSS, but the subsequent settings=o_json overwrote it.
  Re-call after the JSON re-assign so the cross-feature clamp survives.
- FoveatedRender::LoadSettings: drop unnecessary const_cast<json&> — the
  Subrect::Controller::LoadSettings overload takes const json&.
- Bridge getters (GetQualityMode/GetPresetDLSS/GetSharpnessDLSS): gate on
  IsRouteActive() per the documented neutral/identity contract and forward
  through FoveatedRender::GetActive*() clamped getters when active.
- Core::CreateTextureFromSource: validate src non-null and runtime-type
  via QueryInterface for ID3D11Texture2D instead of blind static_cast.
- Core::StretchDRSToFullEye: null-check kMainUAV; early-return on Map
  failure rather than Dispatch with a stale constant buffer.
- Core::EnsureVRIntermediateTextures: drop stale per-eye reactive /
  transparency intermediates when their source disappears so the next
  PreparePerEyeInputs doesn't read leftover masks.
- Modes::ExecuteDefaultMode (subrect path) + ExecuteFasterMode: null
  guard p.colorDstUAV and return false so the router falls back to
  standard DLSS instead of looping through StretchDRSToFullEye's guard
  every frame.
- Preprocess::EncodeUpscalingTextures: explicit null check for
  motionVectorCopyTexture before binding its UAV in the DLSS branch.

Skipped (with reason on the resolved thread): CodeRabbit's try/catch
hardening for the JSON deserialize — nlohmann throws on malformed input,
the outer LoadSettings already handles it; adding a local catch would
swallow real errors.

Shader path comment was already addressed by 076107b + the
DlssEnhancer→FoveatedRender rename; thread resolved.

Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 26, 2026 04:51
Copy link
Copy Markdown

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

Copilot reviewed 20 out of 20 changed files in this pull request and generated no new comments.

Copy link
Copy Markdown

@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.

🧹 Nitpick comments (1)
src/Features/Upscaling/FoveatedRender.cpp (1)

53-61: 💤 Low value

Consider adding defensive parsing for malformed JSON.

The const_cast removal addresses part of the past feedback. For additional resilience against corrupted config files, consider wrapping the settings = o_json assignment in a try/catch that falls back to default settings on json::exception. Currently, malformed JSON will propagate the exception upward rather than failing closed.

🛡️ Optional hardening
 void FoveatedRender::LoadSettings(const json& o_json)
 {
-	settings = o_json;
+	try {
+		settings = o_json.get<Settings>();
+	} catch (const json::exception&) {
+		logger::warn("[FOVEATED] Malformed settings JSON, using defaults");
+		settings = {};
+	}
 	// Util::Subrect::Controller::LoadSettings takes `const json&` (Subrect.h:68)
 	// so no const_cast is needed — keeping it would imply mutation that never
 	// happens. (Copilot + CodeRabbit on PR `#44`.)
 	subrectController.LoadSettings(o_json);
 	ClampSettings();
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Features/Upscaling/FoveatedRender.cpp` around lines 53 - 61,
FoveatedRender::LoadSettings currently assigns settings = o_json without
guarding against malformed JSON; wrap the assignment and subsequent calls
(settings = o_json; subrectController.LoadSettings(o_json); ClampSettings();) in
a try block and catch json::exception to restore or initialize sane defaults
(e.g., reset settings to default state), log the error, and continue so a
corrupted config fails closed rather than propagating; ensure you reference the
settings member, subrectController.LoadSettings, and ClampSettings in the error
path so state remains consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/Features/Upscaling/FoveatedRender.cpp`:
- Around line 53-61: FoveatedRender::LoadSettings currently assigns settings =
o_json without guarding against malformed JSON; wrap the assignment and
subsequent calls (settings = o_json; subrectController.LoadSettings(o_json);
ClampSettings();) in a try block and catch json::exception to restore or
initialize sane defaults (e.g., reset settings to default state), log the error,
and continue so a corrupted config fails closed rather than propagating; ensure
you reference the settings member, subrectController.LoadSettings, and
ClampSettings in the error path so state remains consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 46e35986-d053-4550-a829-85f08ac22c84

📥 Commits

Reviewing files that changed from the base of the PR and between c877f85 and cf51686.

📒 Files selected for processing (6)
  • src/Features/Upscaling.cpp
  • src/Features/Upscaling/FoveatedRender.cpp
  • src/Features/Upscaling/FoveatedRender/Bridge.cpp
  • src/Features/Upscaling/FoveatedRender/Core.cpp
  • src/Features/Upscaling/FoveatedRender/Modes.cpp
  • src/Features/Upscaling/FoveatedRender/Preprocess.cpp

alandtse added a commit that referenced this pull request May 26, 2026
The refactor commit (a9e9d9f) moved the shader from
features/DLSS Enhancer/Shaders/Features/SubrectStretchCS.hlsl
to features/Upscaling/Shaders/Upscaling/DlssEnhancer/SubrectStretchCS.hlsl
but missed updating the runtime CompileShader path in Core.cpp, which still
pointed at Data/Shaders/Features/SubrectStretchCS.hlsl.

With the old path the compile silently fails, vrSubrectStretchCS stays
null, and StretchDRSToFullEye early-returns — the periphery stretch (and
therefore the entire foveated subrect path) produces no output. Caught by
Copilot on PR #44.

Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com>
@alandtse alandtse force-pushed the pr3-dlss-foveated branch from cf51686 to d96b405 Compare May 26, 2026 06:02
alandtse added a commit that referenced this pull request May 26, 2026
Ten inline findings from Copilot + CodeRabbit on PR-3 FoveatedRender:

- Upscaling::LoadSettings ordering: FoveatedRender::ClampSettings touches
  sibling presetDLSS, but the subsequent settings=o_json overwrote it.
  Re-call after the JSON re-assign so the cross-feature clamp survives.
- FoveatedRender::LoadSettings: drop unnecessary const_cast<json&> — the
  Subrect::Controller::LoadSettings overload takes const json&.
- Bridge getters (GetQualityMode/GetPresetDLSS/GetSharpnessDLSS): gate on
  IsRouteActive() per the documented neutral/identity contract and forward
  through FoveatedRender::GetActive*() clamped getters when active.
- Core::CreateTextureFromSource: validate src non-null and runtime-type
  via QueryInterface for ID3D11Texture2D instead of blind static_cast.
- Core::StretchDRSToFullEye: null-check kMainUAV; early-return on Map
  failure rather than Dispatch with a stale constant buffer.
- Core::EnsureVRIntermediateTextures: drop stale per-eye reactive /
  transparency intermediates when their source disappears so the next
  PreparePerEyeInputs doesn't read leftover masks.
- Modes::ExecuteDefaultMode (subrect path) + ExecuteFasterMode: null
  guard p.colorDstUAV and return false so the router falls back to
  standard DLSS instead of looping through StretchDRSToFullEye's guard
  every frame.
- Preprocess::EncodeUpscalingTextures: explicit null check for
  motionVectorCopyTexture before binding its UAV in the DLSS branch.

Skipped (with reason on the resolved thread): CodeRabbit's try/catch
hardening for the JSON deserialize — nlohmann throws on malformed input,
the outer LoadSettings already handles it; adding a local catch would
swallow real errors.

Shader path comment was already addressed by 076107b + the
DlssEnhancer→FoveatedRender rename; thread resolved.

Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com>
Copy link
Copy Markdown

@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

🧹 Nitpick comments (4)
src/Hooks.cpp (1)

17-17: 💤 Low value

Conventional-commit style and issue-reference polish.

Consider a title like feat(vr): add foveated dlss route (lowercase scope/description, shorter), and add an explicit PR/body reference like Implements #44`` for traceability.

As per coding guidelines: "**/*: provide suggestions for conventional commit titles and issue references when reviewing PRs."

Also applies to: 416-421

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Hooks.cpp` at line 17, Update the PR title and body to follow
conventional-commit style: change the title related to the include of
"Features/Upscaling/FoveatedRender/Bridge.h" to a lowercase scope/description
like "feat(vr): add foveated dlss route" and add an explicit issue/PR reference
line in the PR body such as "Implements `#44`"; apply the same formatting and
issue-reference update to the other related commits/PR descriptions affecting
the same area (lines around the other changes at 416-421) so all entries follow
the guideline.
src/Features/Upscaling/FoveatedRender/Params.cpp (1)

3-7: ⚡ Quick win

Use src/Globals.h as the globals access entrypoint

This file reads globals::state and globals::features, but does not include Globals.h directly. Please switch to #include "../../../Globals.h" (and keep only needed direct dependencies) to match the project’s graphics feature access contract.

As per coding guidelines: "Access feature registry and core systems through src/Globals.h for all graphics features and DirectX 11 device/context access."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Features/Upscaling/FoveatedRender/Params.cpp` around lines 3 - 7, Replace
the direct subsystem headers with the global access header: remove the indirect
includes (e.g., "../../../State.h" and "../../../Utils/Game.h") and instead add
`#include` "../../../Globals.h" in Params.cpp so the file accesses globals::state
and globals::features via the sanctioned entrypoint; keep only any other direct
includes that are truly required by local declarations (e.g., FoveatedRender or
Upscaling types) and rebuild to ensure no unresolved symbols remain.
src/Features/Upscaling/FoveatedRender/Params.h (1)

1-1: PR metadata follow-up: tighten commit title and issue keyword usage

Suggested conventional title (<=50 chars, lowercase scope/desc): feat(vr): add dlss foveated subrect path
Also consider adding an explicit keyword line in the PR body like Implements #44 (or `Addresses `#44) for automation consistency.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Features/Upscaling/FoveatedRender/Params.h` at line 1, Update the PR
commit title to a concise conventional format like "feat(vr): add dlss foveated
subrect path" (<=50 chars, lowercase scope/desc) and add an explicit issue
keyword line in the PR body such as "Implements `#44`" or "Addresses `#44`" for
automation; this is a PR metadata change (the change touches Params.h but the
fix is to adjust the commit/PR metadata, not the code).
src/Features/Upscaling/FoveatedRender/Postprocess.cpp (1)

1-1: PR metadata: tighten title format and add explicit issue keyword.

Suggested conventional title (<=50 chars, lowercase description): feat(vr): add dlss foveated subrect route
Suggested PR body footer: Implements #44``

As per coding guidelines, "Format: type(scope): description" with "lowercase description" and "Suggest adding appropriate GitHub keywords".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Features/Upscaling/FoveatedRender/Postprocess.cpp` at line 1, Update the
PR metadata to follow the conventional commit format: change the PR title to
"feat(vr): add dlss foveated subrect route" (lowercase description, keep within
50 chars) and add the suggested footer "Implements `#44`" to the PR body so the
issue is linked; no code changes required in Postprocess.cpp.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@features/Upscaling/Shaders/Upscaling/FoveatedRender/SubrectStretchCS.hlsl`:
- Around line 35-39: The srcU/srcV computed for sampling (from SrcEyeWidth,
SrcEyeHeight, SrcOffsetX, SrcWidth, SrcHeight) must be clamped to the per-eye
sub-rect bounds to avoid cross-eye bleed when StretchMode == 2 or when using
bilinear SampleLevel; compute minU = SrcOffsetX / SrcWidth and maxU =
(SrcOffsetX + SrcEyeWidth) / SrcWidth, minV = 0 / SrcHeight and maxV =
SrcEyeHeight / SrcHeight (use the corresponding Src* symbols already in the
shader), then clamp srcU = clamp(srcU, minU, maxU) and srcV = clamp(srcV, minV,
maxV) before any SampleLevel calls so the blur/bilinear footprint cannot sample
neighboring SBS eye texels.
- Around line 27-38: The shader performs divisions using
DstWidth/DstHeight/SrcWidth/SrcHeight before any branching, which can produce
NaN/Inf and invalid SourceTex.Load coordinates; add explicit zero-dimension
guards at the top of the main entry (numthreads... main) to early-out when any
of DstWidth, DstHeight, SrcWidth, SrcHeight, SrcEyeWidth, or SrcEyeHeight are
zero, and clamp or fallback to a safe UV (e.g., zero) before computing srcU/srcV
and texelSize so StretchMode branches (including point mode using SrcTex.Load)
never receive invalid coordinates; also run hlslkit-compile/register scan to
verify cbuffer StretchCB : register(b0), SrcTex : t0, BilinearSampler : s0, and
DstTex : u0 do not conflict with other passes.

In `@src/Features/Upscaling/FoveatedRender.h`:
- Around line 105-112: The build fails because FoveatedRender::ClampSettings()
is declared private but Upscaling::LoadSettings() calls
foveatedRender.ClampSettings(); make ClampSettings accessible where it's used by
moving or duplicating its declaration to the public section of the
FoveatedRender class (or change its access specifier to public) so
Upscaling::LoadSettings() can legally call it; update the header declaration for
ClampSettings() (and keep the existing implementation in FoveatedRender) and
leave callers (e.g., Upscaling::LoadSettings() using foveatedRender) unchanged.

In `@src/Features/Upscaling/FoveatedRender/Core.cpp`:
- Around line 451-456: The function StretchDRSBothEyes may dereference a null
snapshot SRV when Core::vrRenderSBS is null or its srv is null; add a guard at
the top of StretchDRSBothEyes that checks Core::vrRenderSBS and
Core::vrRenderSBS->srv (or the resolved src after srcOverride) and early-returns
if neither provides a valid ID3D11ShaderResourceView, so the function degrades
safely instead of dereferencing a null pointer.
- Around line 209-255: PreparePerEyeInputs may call
context->CopySubresourceRegion on null source resources (colorSrc, depthSrc,
mvecSrc, reactiveSrc, transparencySrc) leading to D3D crashes; add null checks
before each per-eye CopySubresourceRegion call in PreparePerEyeInputs to skip
the corresponding copy when the matching input is null (e.g., only call
CopySubresourceRegion for colorSrc if colorSrc != nullptr, likewise for
depthSrc, mvecSrc, and conditionally for reactiveSrc/transparencySrc),
preserving existing per-eye indexing and using the same D3D11_BOX and
Core::vrIntermediate* targets.

In `@src/Features/Upscaling/FoveatedRender/Postprocess.cpp`:
- Around line 33-36: The code dereferences globals::d3d::context and
globals::game::renderer (and then calls
renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN]) without
null checks; add guards that verify globals::d3d::context and
globals::game::renderer are non-null before using them and early-return or
choose a safe fallback path if either is null (e.g., skip postprocess
setup/return false). Apply the same null-check pattern around any later uses of
renderer->GetRuntimeData() and renderTargets accesses to prevent crashes during
init/device-loss edges.

In `@src/Features/Upscaling/FoveatedRender/Preprocess.cpp`:
- Around line 52-69: The code binds temporalAAMask.SRV, normals.SRV,
motionVector.SRV and depth.depthSRV unconditionally before compute dispatch
(context->CSSetShaderResources), which can cause device warnings/crashes if any
are null; add validation that each of temporalAAMask.SRV, normals.SRV,
motionVector.SRV and depth.depthSRV is non-null before calling
context->CSSetShaderResources / proceeding with the dispatch (or
early-return/skip the preprocess); when a required SRV is missing, cleanly end
the perf event (state->BeginPerfEvent / EndPerfEvent) and skip binding/dispatch
and log a concise error so the failure is deterministic.

---

Nitpick comments:
In `@src/Features/Upscaling/FoveatedRender/Params.cpp`:
- Around line 3-7: Replace the direct subsystem headers with the global access
header: remove the indirect includes (e.g., "../../../State.h" and
"../../../Utils/Game.h") and instead add `#include` "../../../Globals.h" in
Params.cpp so the file accesses globals::state and globals::features via the
sanctioned entrypoint; keep only any other direct includes that are truly
required by local declarations (e.g., FoveatedRender or Upscaling types) and
rebuild to ensure no unresolved symbols remain.

In `@src/Features/Upscaling/FoveatedRender/Params.h`:
- Line 1: Update the PR commit title to a concise conventional format like
"feat(vr): add dlss foveated subrect path" (<=50 chars, lowercase scope/desc)
and add an explicit issue keyword line in the PR body such as "Implements `#44`"
or "Addresses `#44`" for automation; this is a PR metadata change (the change
touches Params.h but the fix is to adjust the commit/PR metadata, not the code).

In `@src/Features/Upscaling/FoveatedRender/Postprocess.cpp`:
- Line 1: Update the PR metadata to follow the conventional commit format:
change the PR title to "feat(vr): add dlss foveated subrect route" (lowercase
description, keep within 50 chars) and add the suggested footer "Implements `#44`"
to the PR body so the issue is linked; no code changes required in
Postprocess.cpp.

In `@src/Hooks.cpp`:
- Line 17: Update the PR title and body to follow conventional-commit style:
change the title related to the include of
"Features/Upscaling/FoveatedRender/Bridge.h" to a lowercase scope/description
like "feat(vr): add foveated dlss route" and add an explicit issue/PR reference
line in the PR body such as "Implements `#44`"; apply the same formatting and
issue-reference update to the other related commits/PR descriptions affecting
the same area (lines around the other changes at 416-421) so all entries follow
the guideline.
🪄 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: 518cb0c9-2597-4179-be6a-53e4042242cd

📥 Commits

Reviewing files that changed from the base of the PR and between cf51686 and d96b405.

📒 Files selected for processing (20)
  • features/Upscaling/Shaders/Upscaling/FoveatedRender/SubrectStretchCS.hlsl
  • src/Features/Upscaling.cpp
  • src/Features/Upscaling.h
  • src/Features/Upscaling/FoveatedRender.cpp
  • src/Features/Upscaling/FoveatedRender.h
  • src/Features/Upscaling/FoveatedRender/Bridge.cpp
  • src/Features/Upscaling/FoveatedRender/Bridge.h
  • src/Features/Upscaling/FoveatedRender/Core.cpp
  • src/Features/Upscaling/FoveatedRender/Core.h
  • src/Features/Upscaling/FoveatedRender/Modes.cpp
  • src/Features/Upscaling/FoveatedRender/Ops.h
  • src/Features/Upscaling/FoveatedRender/Params.cpp
  • src/Features/Upscaling/FoveatedRender/Params.h
  • src/Features/Upscaling/FoveatedRender/Postprocess.cpp
  • src/Features/Upscaling/FoveatedRender/Postprocess.h
  • src/Features/Upscaling/FoveatedRender/Preprocess.cpp
  • src/Features/Upscaling/FoveatedRender/Preprocess.h
  • src/Features/Upscaling/PerfMode.cpp
  • src/Features/Upscaling/PerfMode.h
  • src/Hooks.cpp
✅ Files skipped from review due to trivial changes (2)
  • src/Features/Upscaling/PerfMode.cpp
  • src/Features/Upscaling/PerfMode.h

Comment thread src/Features/Upscaling/FoveatedRender.h
Comment thread src/Features/Upscaling/FoveatedRender/Core.cpp
Comment thread src/Features/Upscaling/FoveatedRender/Core.cpp
Comment thread src/Features/Upscaling/FoveatedRender/Postprocess.cpp
Comment thread src/Features/Upscaling/FoveatedRender/Preprocess.cpp
@alandtse alandtse changed the title feat(VR): FoveatedRender (DLSS backend, PR-3 MVP-B) feat(VR): add foveated rendering May 26, 2026
alandtse added a commit that referenced this pull request May 26, 2026
Three correctness fixes uncovered by interactive testing on top of #44.

1. DLSSperf integration in Params::Resolve. The route's dimensions were
   resolved against state->screenSize unconditionally and colorDst aliased
   kMAIN. With PerfMode active, kMAIN is allocated at RenderRes and DLSS
   must write into PerfMode's private DisplayRes testTexture — the route
   would render black world while HUD stayed visible. Mirror
   Streamline::Upscale's plumbing (Streamline.cpp:617-626): when
   perfMode.IsHookActive() && perfMode.GetTestTexture(), use
   perfMode.GetDisplayScreenSize() for eyeWidthOut/Out and route
   colorDst + colorDstUAV through testTexture. Input extents stay at
   renderSize (kMAIN is what the engine wrote).

2. Subrect outputHeight plumbing. SetDLSSOptions took outputWidth from
   the caller but hardcoded outputHeight = displaySize.y. For the
   foveated subrect path this configured DLSS for subOutW x eyeHeightOut
   while extentOut declared subOutW x subOutH — NGX returned zeroed
   output and the subrect region rendered solid black on top of the
   stretched background. The original wiring commit dropped a 12th
   outputHeight arg as redundant (it was, for the standard path). Add
   defaulted (=0) outputHeight to SetDLSSOptions and EvaluateDLSS; 0
   preserves the standard path's full-eye height, non-zero is the
   subrect height the foveated route now passes for both Default and
   Faster mode evaluates.

3. Menu-open skip in Upscaling::Upscale. In menus the world stops
   producing fresh motion vectors and depth but kMAIN keeps changing
   (UI plate composites). The route's subrect DLSS evaluate accumulates
   temporal history against stale neighbourhood data and the subrect
   region renders as visible reconstruction garbage. Standard full-eye
   DLSS is robust to this because it reconstructs across the whole
   image — the foveated crop is what makes the stale-history bleed
   visible. Skip the route when GameIsPaused() || IsMainOrLoadingMenuOpen,
   same predicate dev uses at Upscaling.cpp:1748 for
   ShouldUseFrameGenerationThisFrame, and fall through to standard DLSS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
alandtse added a commit that referenced this pull request May 26, 2026
Round of UX work from interactive testing of #44. Behavioural changes
in this commit are scoped to FoveatedRender; no impact on the standard
DLSS path or DLSSperf.

Layout
- Split DrawSettings into DrawEnable (always-visible top-level header
  + Enable checkbox) and DrawSettings (collapsible body, greyed via
  BeginDisabled when not enabled). Discoverable enable, no buried
  toggle; body knobs visible-but-uneditable so users can inspect
  tooltips before opting in.
- Encoder Tree Node deleted alongside its three Settings fields,
  IsEncode* accessors, and clamp lines (see prior commit's note;
  this is the file-side of that removal).

Defaults / opt-in
- settings.enabled default flipped 1 -> 0. Foveated is opt-in via the
  Enable checkbox + restart; first-launch users no longer surprised by
  the route altering their world render.

Bug fixes
- Preview switched from kMAIN to kVR_FRAMEBUFFER (the final composed
  SBS image the headset sees). kMAIN is mid-pipeline and carries
  composited UI alpha that the default blend interprets as a hole —
  preview looked like a transparency mask. Screenshot picks the same
  RT for the same reason (ScreenshotFeature.cpp:243). Uses the new
  shared Util::Subrect::OpaquePreviewBlendCallback.

UI polish
- Sliders -> Checkboxes for the binary on/off setting (Enable).
- Stretch Mode label fix ("Point (VRS-like)" -> "Point") and the
  shader sub-label kPoint description dropped the VRS reference;
  VRS doesn't ship here yet (deferred to PR-4). Same trim applied
  to the tooltip and the "If you also use VRS or Screenshot, set them
  to the same subrect preset" warning — replaced with a softer
  TextDisabled note mentioning only Screenshot (the only other
  Subrect consumer that exists today).
- Stretch Mode tooltips expanded with When-to-use / Visual-artifact
  pattern for each of Bilinear / Point / Gaussian.

Debug visualizer
- "Visualize regions" runtime checkbox under Subrect Region. Tints
  the cheap-stretched periphery red so the DLSS-reconstructed subrect
  (un-tinted) pops visually. Lets users confirm at a glance where
  DLSS is actually running vs where the cheap stretch is filling, and
  helps verify the subrect lands where the menu preview said it would.
  Implementation: one new uint on FoveatedRender::Settings, one new uint
  on StretchCB (repurposed _pad), one shader line. No new shader, no
  new compute pass, no perf cost when off.

Nasal Convergence preset
- New seeded preset "Nasal Convergence 50%" — asymmetric per-eye UVs
  that bias toward the nose side of each eye, capturing the binocular
  fusion zone where HMD stereo overlap is strongest. Demonstrates the
  Util::Subrect::Preset.rightUV API that already supports
  per-eye-asymmetric crops; previous seeded presets were all centered.
  Note for testing: SeedDefaultPresets is empty-case-only, so existing
  installs with saved CropPresets won't see this until they delete the
  Upscaling.foveatedRender.CropPresets block from SettingsUser.json.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 26, 2026 09:02
Copy link
Copy Markdown

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

Copilot reviewed 26 out of 26 changed files in this pull request and generated 4 comments.

Comment thread src/Features/Upscaling/PerfMode.cpp Outdated
Comment thread src/Features/Upscaling/PerfMode.h Outdated
Comment thread src/Features/Upscaling/FoveatedRender/Modes.cpp Outdated
Comment thread src/Features/Upscaling/FoveatedRender/Modes.cpp Outdated
Copy link
Copy Markdown

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/Features/Upscaling.cpp (2)

620-633: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve preset F during settings load.

FoveatedRender::ClampSettings() now treats presetDLSS == 5 as valid, but Line 624 still resets anything above 4 back to Default before that clamp runs. That makes preset F impossible to persist from config and breaks the new mode-compatibility logic.

Suggested fix
-	if (settings.presetDLSS > 4) {
+	if (settings.presetDLSS > 5) {
 		logger::warn("[Upscaling] Loaded presetDLSS {} out of range, resetting to 0 (Default)", settings.presetDLSS);
 		settings.presetDLSS = 0;
 	}

You should also add the "Preset F" UI label in the combo so a loaded value of 5 has a valid presentation path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Features/Upscaling.cpp` around lines 620 - 633, The code is clamping
settings.presetDLSS > 4 back to 0 which prevents the new preset F (value 5) from
persisting and from being recognized by FoveatedRender::ClampSettings(); remove
or relax that clamp so presetDLSS == 5 is allowed (e.g., only clamp values > 5
or change the comparison to > 5), and update the UI combo/list that displays
preset labels to include a "Preset F" entry at the index corresponding to value
5 so a loaded value of 5 has a valid presentation path.

2401-2412: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Mirror the upscale route gate when choosing the sharpening path.

Line 1982 skips the foveated route while menus are open, but this branch still uses Postprocess::ApplyDlssSharpening() whenever Bridge::IsRouteActive() is true. On those menu frames the standard DLSS path actually ran, so postprocess can sharpen the wrong target path instead of matching the fallback.

Suggested fix
 	if (upscaleMethod == UpscaleMethod::kDLSS) {
+		auto* ui = globals::game::ui;
+		auto* st = globals::state;
+		const bool menuOpen = (ui && ui->GameIsPaused()) || (st && st->IsMainOrLoadingMenuOpen(ui));
+
-		if (FoveatedRenderImpl::Bridge::IsRouteActive()) {
+		if (FoveatedRenderImpl::Bridge::IsRouteActive() && globals::game::isVR && !menuOpen) {
 			FoveatedRenderImpl::Postprocess::ApplyDlssSharpening(upscaling);
 		} else {
 			upscaling.ApplySharpening();
 		}
 	}
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Utils/Subrect_PreviewBlend.cpp`:
- Around line 16-30: The OpaquePreviewBlendCallback currently dereferences
globals::d3d::device and globals::d3d::context without checks; add a fast null
guard at the start of OpaquePreviewBlendCallback that returns early if
globals::d3d::device or globals::d3d::context is null to avoid crashes during
init/teardown, then proceed with creating/setting opaqueBlend only when
device/context are valid; ensure you still only create opaqueBlend once (static
opaqueBlend) and skip OMSetBlendState when context is absent.

---

Outside diff comments:
In `@src/Features/Upscaling.cpp`:
- Around line 620-633: The code is clamping settings.presetDLSS > 4 back to 0
which prevents the new preset F (value 5) from persisting and from being
recognized by FoveatedRender::ClampSettings(); remove or relax that clamp so
presetDLSS == 5 is allowed (e.g., only clamp values > 5 or change the comparison
to > 5), and update the UI combo/list that displays preset labels to include a
"Preset F" entry at the index corresponding to value 5 so a loaded value of 5
has a valid presentation path.
🪄 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: 4dc79bb7-12ec-4956-a283-992dcb16b03b

📥 Commits

Reviewing files that changed from the base of the PR and between d96b405 and 29eac91.

📒 Files selected for processing (14)
  • features/Upscaling/Shaders/Upscaling/FoveatedRender/SubrectStretchCS.hlsl
  • src/Features/ScreenshotFeature.cpp
  • src/Features/Upscaling.cpp
  • src/Features/Upscaling/FoveatedRender.cpp
  • src/Features/Upscaling/FoveatedRender.h
  • src/Features/Upscaling/FoveatedRender/Core.cpp
  • src/Features/Upscaling/FoveatedRender/Modes.cpp
  • src/Features/Upscaling/FoveatedRender/Params.cpp
  • src/Features/Upscaling/FoveatedRender/Postprocess.cpp
  • src/Features/Upscaling/Streamline.cpp
  • src/Features/Upscaling/Streamline.h
  • src/Utils/Subrect.cpp
  • src/Utils/Subrect.h
  • src/Utils/Subrect_PreviewBlend.cpp
✅ Files skipped from review due to trivial changes (1)
  • src/Utils/Subrect.cpp

Comment thread src/Utils/Subrect_PreviewBlend.cpp
alandtse added a commit that referenced this pull request May 27, 2026
- GetRenderScaleForQuality delegates to ffxFsr3GetUpscaleRatioFromQuality­Mode
  instead of duplicating the 1.5/1.7/2.0/3.0 scale table (Upscaling.cpp and
  PerfMode.cpp already query the FFX helper). Falls back to 3.0 on a
  non-finite result.
- Trim past-tense / "the original PR did X" / "MVP-B / deferred to PR-3b"
  comments throughout FoveatedRender across Bridge, Core, Modes, Ops,
  Params, Postprocess — they described absent code, which CLAUDE.md
  explicitly disallows. Load-bearing regression warnings (e.g. the
  encoder-toggle no-op note in FoveatedRender.h) are kept.

No behavior change. Addresses audit findings on PR #44.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
alandtse added a commit that referenced this pull request May 27, 2026
- Bridge::GetQualityModeAtBoot now gates on IsRouteActive() like the
  other Bridge getters; the header contract promised every query
  returns a neutral value when the route is inactive, and this one
  silently leaked the latched value.
- StretchDRSToFullEye init block now resets vrSubrectStretchCS on
  CreateBuffer/CreateSamplerState failure paths. Otherwise the outer
  !vrSubrectStretchCS guard stays false next frame and the failed
  CB/sampler never get retried — Faster mode would be dead until
  game restart.

(Copilot on PR #44.)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 27, 2026 04:11
Copy link
Copy Markdown

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

Copilot reviewed 26 out of 26 changed files in this pull request and generated no new comments.

alandtse and others added 20 commits May 27, 2026 08:25
…P-B)

First slice of the DlssEnhancer foveated-DLSS framework, decomposed
from community-shaders#2096. These four modules form the leaf interface layer:

- Bridge.h/cpp — single point of contact for Upscaling/Streamline.
  Settings forwarding, route activity check, boot latches, mvec scale.
  Drops the original PR's DLSSperf install path (PR-2 owns DLSSperf).
  Drops the kExtreme branch in ComputeMvecScale (kExtreme deferred).

- Params.h/cpp — VRDlssParams::Resolve(). Builds the per-frame
  parameter block for Mode dispatchers from current global state.
  MVP-B drops the DLSSperf-aware DisplayRes/RenderRes split and
  testTexture rerouting; that's a PR-2 + PR-3 integration follow-up.
  Migrated to PR-1's Util::Subrect API: GetUV() == primary/left,
  GetRightEyeUV() == mirrored right.

- Preprocess.h/cpp — DlssEnhancer::Preprocess::EncodeUpscalingTextures.
  Mirrors Upscaling's encode pass with a DLSS-specific shader define,
  invoked only when the DlssEnhancer route runs. Lift-as-is.

- Postprocess.h/cpp — DlssEnhancer::Postprocess::ApplyDlssSharpening.
  RCAS sharpen pass for the route. Drops the DLSSperf-aware testTexture
  variant (deferred to PR-2 + PR-3 integration).

These files reference DlssEnhancerFeature, Core, Modes, Ops which
haven't landed yet — won't compile in isolation. Build gate is at
the end of the lift sequence.

Decomposed from community-shaders#2096 (PR-3 MVP-B of 4); see DLSS-PR-PLAN.md.

Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Lifts Core.h, Core.cpp, and Ops.h from community-shaders#2096 with the MVP-B cuts
applied surgically:

Removed (deferred to PR-3b):
- vrExtremeStrip* fields and EnsureExtremeStripTextures (kExtreme mode)
- vrTemporalHistory[2], EnsureTemporalResources, TemporalSmoothSBS
  and the periphery temporal-smooth CS resources
- vrSubrectBlendCS, BlendCB, blend-feather/dither dispatch — replaced
  by a plain CopySubresourceRegion in Ops::BlendSubrectToOutput
- ExecuteExtremeMode private declaration
- ClearResources/ClearShaderCache references to all of the above

Migrated for PR-1 compatibility:
- Subrect:: → Util::Subrect:: (UVRegion field types,
  ComputeSubrectUVHash parameter)
- #include path Utils/Subrect/Subrect.h → Utils/Subrect.h

Bot-flagged Major bug fixed during the lift:
- CodeRabbit Major (Core.cpp:178-224 in original PR): the per-eye
  inputs path failed to reapply the HMD hidden-area clear, letting
  garbage from the HMD's optical mask bleed into DLSS history. Fixed
  in Core::PreparePerEyeInputs by calling Upscaling::ClearHMDMask
  per-eye after the SBS->per-eye CopySubresourceRegion.

Decomposed from community-shaders#2096 (PR-3 MVP-B of 4); see DLSS-PR-PLAN.md.

Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Lifts Modes.cpp with cuts and the bot-flagged Major bug fixed:

Removed (deferred to PR-3b):
- ExecuteExtremeMode body (~70 LOC, asymmetric strip mode)
- kExtreme switch arm in router ExecuteVRDlssCore
- Periphery temporal-smooth blocks in both Default and Faster mode
  bodies (depended on Core's removed EnsureTemporalResources +
  TemporalSmoothSBS)

Migrated for PR-1 compatibility:
- Subrect:: -> Util::Subrect:: (3 sites in mode bodies)
- Added #include for Utils/Subrect.h
- BlendSubrectToOutput callsites updated to MVP-B signature (drops
  dstUAV arg now that the function is plain CopySubresourceRegion)

Bot-flagged Major bug fixed (CodeRabbit Major @ Modes.cpp:80,146,207
of original PR):
- Per-eye subrect dimensions (subInW/H/subOutW/H) were computed once
  outside the eye loop using p.leftUV.w/h, so the right eye used
  left-eye dimensions even when rightUV differed. Moved inside the
  per-eye loop using eyeUVs[i]->w/h. Allocator calls
  (EnsureVRSubrectTextures, EnsureFasterOutputTextures) still use
  left-eye dimensions for the shared texture set, with a comment
  citing PR-1's auto-mirrored stereo Subrect (leftUV.w == rightUV.w)
  as the constraint that keeps this safe in MVP-B.

Also fixes Postprocess.cpp's sharpness lookup to read from
Upscaling::Settings::sharpnessDLSS directly (the PR called
GetActiveSharpnessDLSS() which doesn't exist on dev — that helper
came in via the deferred per-route override layer).

Output: Modes.cpp 214 LOC (was 276).

Decomposed from community-shaders#2096 (PR-3 MVP-B of 4); see DLSS-PR-PLAN.md.

Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The Feature class with UI, plus the one compute shader MVP-B keeps.

Lifted DlssEnhancerFeature with cuts:
- Removed enums: SubrectBlendMode, PeripheryAAMode (entirely)
- Removed DlssMode::kExtreme entry (kDefault, kFaster only)
- Removed Settings fields: peripheryAAMode, peripheryTemporalAlpha,
  subrectBlendMode, subrectFeatherWidth, subrectDitherStrength,
  peripheryBlurRadius, enablePerf
- Removed accessors: GetPeripheryAAMode, GetSubrectBlendMode,
  IsPerfEnabled, LatchPerfEnabled, perfEnabledAtBoot
- Removed ImGui sections for all of the above + DLSSperf hook status
- ClampPresetToMode keeps Faster constraints, drops Extreme branch

Kept (MVP-B mode set requires these):
- StretchMode (all 3: Bilinear/Point/GaussianBlur — used by
  SubrectStretchCS for the periphery stretch step)
- SharpenMode (all entries; Postprocess uses kNone vs others)
- subrectController (Util::Subrect::Controller, PR-1 stereo API)
- IsActive/IsLoaded/LatchEnabled/LatchQualityMode and the boot-time
  quality snapshot + GetActive*** accessors

Migrated for PR-1:
- #include path Utils/Subrect/Subrect.h -> Utils/Subrect.h
- Subrect::Controller -> Util::Subrect::Controller
- subrectController.GetLeftEyeUV() -> subrectController.GetUV()
  (PR-1's API: GetUV() doubles as left-eye in stereo mode)

PostPostLoad opts the controller into stereo mode and seeds a small
preset menu for Default/Faster:
  Full Eye          [0,    0,    1.0,  1.0 ]
  Center 75% (foveal) [0.125, 0.125, 0.75, 0.75]
  Center 50% (foveal) [0.25,  0.25,  0.5,  0.5 ]
For symmetric crops centered at x=0.5, the auto-mirror produces the
same UV as the seed — left==right by construction.

GetShortName returns "DLSSENHANCER" (uppercase) to match the
features/DLSS Enhancer/Shaders/Features/DLSSENHANCER.ini filename;
FindFeatureByShortName + version-issue lookup are filename-keyed.

Also lifted as-is:
- features/DLSS Enhancer/Shaders/Features/SubrectStretchCS.hlsl
  (3-mode stretch: Bilinear / Point / GaussianBlur 3x3)
- features/DLSS Enhancer/Shaders/Features/DLSSENHANCER.ini

The SubrectBlendCS shader is intentionally NOT lifted — the
periphery blend collapses to a plain CopySubresourceRegion in
Ops::BlendSubrectToOutput, so the shader (and its bot-flagged
feather edge bug) is deferred to PR-3b.

Sizes: DlssEnhancerFeature.h 128 LOC (was 163), .cpp 312 (was 405).

References to globals::features::dlssEnhancer become resolvable
once the next commit registers the instance.

Decomposed from community-shaders#2096 (PR-3 MVP-B of 4); see DLSS-PR-PLAN.md.

Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The framework lifted in the previous commits is now reachable.

Globals:
- DlssEnhancerFeature forward decl + extern + instance
- Feature::GetFeatureList() registry add so settings load/save and
  draw flow through the standard Feature iteration

Upscaling.cpp short-circuit:
- DlssEnhancer::Bridge::IsRouteActive() at top of the kDLSS branch in
  Upscale(). Invokes Preprocess::EncodeUpscalingTextures + Core::
  ExecuteVRDlssCore. On any failure falls through to dev's standard
  streamline.Upscale so users always get DLSS output, with a one-shot
  warn log when fallback fires.
- Sharpening dispatch routes through Postprocess::ApplyDlssSharpening
  when the route is active; dev's ApplySharpening otherwise.

Hooks.cpp:
- BSShaderRenderTargets::Create::thunk calls DlssEnhancer::Bridge::
  BootSequence() after globals::ReInit + State::Setup. Latches
  enabledAtBoot + qualityModeAtBoot before the first frame.

Two follow-up fixes required during the wire:
- Streamline::EvaluateDLSS signature drift: dev takes 11 args
  (...extentIn, extentOut, outputWidth); the PR's calls passed 12
  (extra outputHeight). Dropped the trailing height at all 3
  callsites in Modes.cpp.
- Core.cpp had one stale reference to the removed Settings field
  peripheryBlurRadius. Replaced with a fixed 1.0f for the
  GaussianBlur stretch path; per-user blur radius deferred to PR-3b.

Build status: plugin C++ compile passes (MSBuild ClCompile exit 0).
FFX shader-permutation crash on this machine still blocks full link
(environmental, same as PR-2; reproduces on clean upstream/dev).

Decomposed from community-shaders#2096 (PR-3 MVP-B of 4); see DLSS-PR-PLAN.md.

Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Following the precedent set in PR-2 for DLSSperf: DlssEnhancer is the
second VR-DLSS surface to live entirely inside Upscaling rather than as
a peer Feature. Both share the same "mode of Upscaling, not its own
Feature" architecture.

Structural changes:
- struct DlssEnhancerFeature : Feature  →  struct DlssEnhancer (drop
  Feature inheritance and the GetName/IsCore/GetCategory boilerplate).
- src/Features/DlssEnhancerFeature.{cpp,h}  →
  src/Features/Upscaling/DlssEnhancer.{cpp,h}
- src/Features/DlssEnhancer/*  →  src/Features/Upscaling/DlssEnhancer/*
- features/DLSS Enhancer/Shaders/Features/SubrectStretchCS.hlsl  →
  features/Upscaling/Shaders/Upscaling/DlssEnhancer/SubrectStretchCS.hlsl
- DLSSENHANCER.ini deleted (placeholder version marker; DLSSperf has no
  .ini either).
- Implementation namespace DlssEnhancer  →  DlssEnhancerImpl to avoid
  collision with the new struct name.

Membership / lifecycle:
- globals::features::dlssEnhancer is gone; access via
  globals::features::upscaling.dlssEnhancer (static-inline member of
  Upscaling, sibling to streamline / fidelityFX / rcas / dlssPerf).
- Removed from globals::features registration and Feature::ms_features.
- PostPostLoad, ClearShaderCache, SaveSettings, LoadSettings,
  RestoreDefaultSettings driven from the equivalent Upscaling methods.

Settings dedup:
- DlssEnhancer::Settings drops qualityMode / sharpnessDLSS / presetDLSS
  / streamlineLogLevel (already on Upscaling::Settings — double-stored).
- DlssEnhancer accessors / Bridge.cpp / Postprocess.cpp now read those
  four through upscaling.settings.

UI:
- DlssEnhancer's standalone panel sliders for the dedup'd fields are
  gone; the Upscaling panel covers them for both DLSS paths.
- DlssEnhancer::DrawSettings renders only DlssEnhancer-specific knobs
  (Enable, DLSS Mode, Stretch Mode, Sharpen Mode, MV/Reactive/
  Transparency masks, Subrect editor).
- Called from Upscaling::DrawSettings inside a "Foveated DLSS
  (DlssEnhancer)" TreeNode, gated on isVR.
- "RESTART REQUIRED" warning replaced with Util::Text::RestartNeeded to
  match the cue convention from PR-2.

JSON:
- DlssEnhancer round-trips under "dlssEnhancer" sub-key in Upscaling's
  settings JSON. Upscaling::LoadSettings extracts and erases the
  sub-block before deserializing its own fields, so absence is safe.
The refactor commit (a9e9d9f) moved the shader from
features/DLSS Enhancer/Shaders/Features/SubrectStretchCS.hlsl
to features/Upscaling/Shaders/Upscaling/DlssEnhancer/SubrectStretchCS.hlsl
but missed updating the runtime CompileShader path in Core.cpp, which still
pointed at Data/Shaders/Features/SubrectStretchCS.hlsl.

With the old path the compile silently fails, vrSubrectStretchCS stays
null, and StretchDRSToFullEye early-returns — the periphery stretch (and
therefore the entire foveated subrect path) produces no output. Caught by
Copilot on PR #44.

Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com>
Foveated rendering (high-quality visual-center subrect + stretched
periphery) is an upscaler-agnostic technique — NVIDIA's VRS, Meta's
Quest eye-tracking foveation, Vive Pro 2's FFR all express the same
idea. The DLSS-tied naming was historical, not algorithmic. Following
PR #42's PerfMode generalization precedent, this PR ships the generic
naming with DLSS as the first concrete backend.

Mechanical rename:
- struct DlssEnhancer -> FoveatedRender
- namespace DlssEnhancerImpl -> FoveatedRenderImpl
- Member Upscaling::dlssEnhancer -> foveatedRender
- src/Features/Upscaling/DlssEnhancer{,.{cpp,h}} -> .../FoveatedRender{,.{cpp,h}}
- features/Upscaling/Shaders/Upscaling/DlssEnhancer/ -> .../FoveatedRender/
- Log tag [DLSSENHANCER] -> [FOVEATED]
- Tracy zone + JSON settings key

The DLSS-specific implementation in Modes.cpp / Preprocess.cpp stays
scoped to DLSS this PR. Follow-up PR-3c will:
- Replace Core::ExecuteVRDlssCore(Streamline&, ...) with a backend
  abstraction.
- Implement FSR concrete backend (~150-300 LOC, mostly Modes.cpp).
- Drop the Preprocess non-DLSS early-return; wire FSR's depth-output
  UAV variant of EncodeTexturesCS (already exists in Upscaling.cpp).
- Hoist the Upscaling.cpp route-active gate to fire for kDLSS or kFSR.

No persisted user settings to preserve — PR-3 hasn't shipped.

Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com>
Ten inline findings from Copilot + CodeRabbit on PR-3 FoveatedRender:

- Upscaling::LoadSettings ordering: FoveatedRender::ClampSettings touches
  sibling presetDLSS, but the subsequent settings=o_json overwrote it.
  Re-call after the JSON re-assign so the cross-feature clamp survives.
- FoveatedRender::LoadSettings: drop unnecessary const_cast<json&> — the
  Subrect::Controller::LoadSettings overload takes const json&.
- Bridge getters (GetQualityMode/GetPresetDLSS/GetSharpnessDLSS): gate on
  IsRouteActive() per the documented neutral/identity contract and forward
  through FoveatedRender::GetActive*() clamped getters when active.
- Core::CreateTextureFromSource: validate src non-null and runtime-type
  via QueryInterface for ID3D11Texture2D instead of blind static_cast.
- Core::StretchDRSToFullEye: null-check kMainUAV; early-return on Map
  failure rather than Dispatch with a stale constant buffer.
- Core::EnsureVRIntermediateTextures: drop stale per-eye reactive /
  transparency intermediates when their source disappears so the next
  PreparePerEyeInputs doesn't read leftover masks.
- Modes::ExecuteDefaultMode (subrect path) + ExecuteFasterMode: null
  guard p.colorDstUAV and return false so the router falls back to
  standard DLSS instead of looping through StretchDRSToFullEye's guard
  every frame.
- Preprocess::EncodeUpscalingTextures: explicit null check for
  motionVectorCopyTexture before binding its UAV in the DLSS branch.

Skipped (with reason on the resolved thread): CodeRabbit's try/catch
hardening for the JSON deserialize — nlohmann throws on malformed input,
the outer LoadSettings already handles it; adding a local catch would
swallow real errors.

Shader path comment was already addressed by 076107b + the
DlssEnhancer→FoveatedRender rename; thread resolved.

Co-Authored-By: YtzyFvra <59631290+YtzyFvra@users.noreply.github.com>
Three correctness fixes uncovered by interactive testing on top of #44.

1. DLSSperf integration in Params::Resolve. The route's dimensions were
   resolved against state->screenSize unconditionally and colorDst aliased
   kMAIN. With PerfMode active, kMAIN is allocated at RenderRes and DLSS
   must write into PerfMode's private DisplayRes testTexture — the route
   would render black world while HUD stayed visible. Mirror
   Streamline::Upscale's plumbing (Streamline.cpp:617-626): when
   perfMode.IsHookActive() && perfMode.GetTestTexture(), use
   perfMode.GetDisplayScreenSize() for eyeWidthOut/Out and route
   colorDst + colorDstUAV through testTexture. Input extents stay at
   renderSize (kMAIN is what the engine wrote).

2. Subrect outputHeight plumbing. SetDLSSOptions took outputWidth from
   the caller but hardcoded outputHeight = displaySize.y. For the
   foveated subrect path this configured DLSS for subOutW x eyeHeightOut
   while extentOut declared subOutW x subOutH — NGX returned zeroed
   output and the subrect region rendered solid black on top of the
   stretched background. The original wiring commit dropped a 12th
   outputHeight arg as redundant (it was, for the standard path). Add
   defaulted (=0) outputHeight to SetDLSSOptions and EvaluateDLSS; 0
   preserves the standard path's full-eye height, non-zero is the
   subrect height the foveated route now passes for both Default and
   Faster mode evaluates.

3. Menu-open skip in Upscaling::Upscale. In menus the world stops
   producing fresh motion vectors and depth but kMAIN keeps changing
   (UI plate composites). The route's subrect DLSS evaluate accumulates
   temporal history against stale neighbourhood data and the subrect
   region renders as visible reconstruction garbage. Standard full-eye
   DLSS is robust to this because it reconstructs across the whole
   image — the foveated crop is what makes the stale-history bleed
   visible. Skip the route when GameIsPaused() || IsMainOrLoadingMenuOpen,
   same predicate dev uses at Upscaling.cpp:1748 for
   ShouldUseFrameGenerationThisFrame, and fall through to standard DLSS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ScreenshotFeature and (next commit) FoveatedRender both need the same
opaque-RGB blend state callback around Subrect::DrawEditor's Image draw
— without it, ImGui's default SRC_ALPHA blend interprets the source RT's
non-1 alpha as transparency and the preview looks like a transparency
mask instead of the rendered image. Two copies of the same helper is one
copy too many.

Move to Util::Subrect::OpaquePreviewBlendCallback. Lives in a separate
TU (Subrect_PreviewBlend.cpp) so the unit-test target — which pulls
Subrect.cpp standalone without PCH — doesn't need to link the d3d
singletons. ScreenshotFeature now calls the shared helper; FoveatedRender
adoption lands in the next commit alongside its preview-source fix.

Doc the regression risks in the header so future implementers don't
reinvent it wrong: BlendEnable=FALSE and WriteMask must exclude alpha
(VR menu plate recomposition reads back the alpha channel).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…able

Postprocess::ApplyDlssSharpening had a kNone short-circuit that was
literally redundant with the existing sharpnessDLSS <= 0 check four
lines down — two ways to disable the same RCAS pass. Drop the
SharpenMode check; the existing sharpness slider remains the single
disable signal. Setting/enum removal lands with the rest of the dead
foveated UI in the next commit (same files, want to ship the
sharpnessDLSS-as-single-signal note here cleanly).

Note in passing on why foveated needs its own ApplyDlssSharpening
rather than reusing dev's zero-copy Upscaling::ApplySharpening: the
route writes DLSS output to per-eye intermediates and copies back to
kMAIN/testTexture, never through sharpenerTexture — so dev's zero-copy
path (which reads sharpenerTexture and writes to kMAIN.UAV) has nothing
valid to sharpen. The kMAIN -> sharpenerTexture -> kMAIN round-trip in
Postprocess::ApplyDlssSharpening is the route's accommodation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Round of UX work from interactive testing of #44. Behavioural changes
in this commit are scoped to FoveatedRender; no impact on the standard
DLSS path or DLSSperf.

Layout
- Split DrawSettings into DrawEnable (always-visible top-level header
  + Enable checkbox) and DrawSettings (collapsible body, greyed via
  BeginDisabled when not enabled). Discoverable enable, no buried
  toggle; body knobs visible-but-uneditable so users can inspect
  tooltips before opting in.
- Encoder Tree Node deleted alongside its three Settings fields,
  IsEncode* accessors, and clamp lines (see prior commit's note;
  this is the file-side of that removal).

Defaults / opt-in
- settings.enabled default flipped 1 -> 0. Foveated is opt-in via the
  Enable checkbox + restart; first-launch users no longer surprised by
  the route altering their world render.

Bug fixes
- Preview switched from kMAIN to kVR_FRAMEBUFFER (the final composed
  SBS image the headset sees). kMAIN is mid-pipeline and carries
  composited UI alpha that the default blend interprets as a hole —
  preview looked like a transparency mask. Screenshot picks the same
  RT for the same reason (ScreenshotFeature.cpp:243). Uses the new
  shared Util::Subrect::OpaquePreviewBlendCallback.

UI polish
- Sliders -> Checkboxes for the binary on/off setting (Enable).
- Stretch Mode label fix ("Point (VRS-like)" -> "Point") and the
  shader sub-label kPoint description dropped the VRS reference;
  VRS doesn't ship here yet (deferred to PR-4). Same trim applied
  to the tooltip and the "If you also use VRS or Screenshot, set them
  to the same subrect preset" warning — replaced with a softer
  TextDisabled note mentioning only Screenshot (the only other
  Subrect consumer that exists today).
- Stretch Mode tooltips expanded with When-to-use / Visual-artifact
  pattern for each of Bilinear / Point / Gaussian.

Debug visualizer
- "Visualize regions" runtime checkbox under Subrect Region. Tints
  the cheap-stretched periphery red so the DLSS-reconstructed subrect
  (un-tinted) pops visually. Lets users confirm at a glance where
  DLSS is actually running vs where the cheap stretch is filling, and
  helps verify the subrect lands where the menu preview said it would.
  Implementation: one new uint on FoveatedRender::Settings, one new uint
  on StretchCB (repurposed _pad), one shader line. No new shader, no
  new compute pass, no perf cost when off.

Nasal Convergence preset
- New seeded preset "Nasal Convergence 50%" — asymmetric per-eye UVs
  that bias toward the nose side of each eye, capturing the binocular
  fusion zone where HMD stereo overlap is strongest. Demonstrates the
  Util::Subrect::Preset.rightUV API that already supports
  per-eye-asymmetric crops; previous seeded presets were all centered.
  Note for testing: SeedDefaultPresets is empty-case-only, so existing
  installs with saved CropPresets won't see this until they delete the
  Upscaling.foveatedRender.CropPresets block from SettingsUser.json.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- SubrectStretchCS: zero-dim guard, clamp sample UVs to per-eye texel
  bounds so bilinear/blur taps and point lookups can't bleed across
  the SBS midline.
- Core::PreparePerEyeInputs: null-guard required color/depth/mvec
  sources before CopySubresourceRegion.
- Core::StretchDRSBothEyes: degrade safely when vrRenderSBS snapshot
  is missing instead of dereferencing srv.
- Core::StretchDRSToFullEye: name vrSubrectStretchCS/CB/Sampler for
  RenderDoc, size the CB via sizeof(StretchCB) — struct hoisted to
  namespace scope to drop the magic 48.
- Postprocess::ApplyDlssSharpening: guard context/renderer/
  stateUpdateFlags pointers before dereference.
- Preprocess::EncodeUpscalingTextures: validate all four input SRVs
  before BeginPerfEvent + dispatch so the perf-event lifecycle stays
  balanced and DLSS doesn't sample corrupted masks.
- Rename leftover Enh_* resource debug strings in Core.cpp to
  FoveatedRender_* to match the c877f85 class rename.

Addresses CodeRabbit review comments on PR #44 (UV clamp at
SubrectStretchCS.hlsl:40, null guards at Core.cpp:255/458,
Postprocess.cpp:33, Preprocess.cpp:69) and audit blocker on
missing Util::SetResourceName at Core.cpp:318/328.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- ComputeSubrectUVHash now mixes both eye UVs + mode. Asymmetric
  presets (e.g. Nasal Convergence) change rightUV only; the prior
  left-only hash would skip DLSS resource recreation on the switch.
- ExecuteFasterMode early-return now mirrors ExecuteDefaultMode:
  only the subrect path requires colorDstUAV (Step 3 stretch is
  gated on !p.isFullEye); Faster + Full Eye no longer falls back
  to standard DLSS spuriously.
- Subrect::OpaquePreviewBlendCallback guards device/context, checks
  CreateBlendState HRESULT, and names the resulting blend state for
  RenderDoc.
- Drop redundant 'public:' after 'struct FoveatedRender'.
- Remove sed-renamed historical references to DlssEnhancer in
  PerfMode.cpp/.h — the comments described absent code (per CLAUDE.md
  'comments describe present code, not absent code').

Addresses CodeRabbit/Copilot review on PR #44 (Modes.cpp:36/165,
PerfMode.cpp:11/PerfMode.h:10, Subrect_PreviewBlend.cpp:30) and
audit blocker (CreateBlendState unnamed).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- GetRenderScaleForQuality delegates to ffxFsr3GetUpscaleRatioFromQuality­Mode
  instead of duplicating the 1.5/1.7/2.0/3.0 scale table (Upscaling.cpp and
  PerfMode.cpp already query the FFX helper). Falls back to 3.0 on a
  non-finite result.
- Trim past-tense / "the original PR did X" / "MVP-B / deferred to PR-3b"
  comments throughout FoveatedRender across Bridge, Core, Modes, Ops,
  Params, Postprocess — they described absent code, which CLAUDE.md
  explicitly disallows. Load-bearing regression warnings (e.g. the
  encoder-toggle no-op note in FoveatedRender.h) are kept.

No behavior change. Addresses audit findings on PR #44.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The four quality presets (1.5/1.7/2.0/3.0) are aligned across NVIDIA
DLSS and FFX FSR3 by spec, so all three call sites that translated
qualityMode into a render-scale ratio were calling the same FFX helper
with the same cast/clamp boilerplate.

Centralize on Upscaling::GetQualityModeRatio() so a future reader
doesn't have to chase three identical "DLSS path calls FSR function"
puzzles. Callsites:
- Upscaling.cpp DrawSettings preset label
- Upscaling.cpp restart-needed banner
- Upscaling.cpp viewport projection scale (subrect/full-eye paths)
- FoveatedRender::GetRenderScaleForQuality (now a thin delegate)

PerfMode keeps its direct FFX call because it does fail-closed
validation on the ratio — substituting the helper's 3.0 fallback would
lose the explicit "hook NOT installed" diagnostic for a malformed
qualityMode.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Faster mode now snapshots kMAIN into vrRenderSBS and clears the HMD
hidden-area ring on both eye halves before DLSS reads from the SBS via
extent offsets. Without this DLSS temporally accumulated Skyrim's
default sky clear from the masked-out edge into the visible region on
fast head motion — Default mode had this protection via per-eye
intermediates; Faster bypassed it.

- vrRenderSBS now gets a UAV bind flag (required by ClearHMDMaskCS).
- Snapshot moved from Step 3 to Step 2a — single copy reused for both
  the mask clear and the stretch background.
- ClearHMDMask is called twice with matching depth/color offsets per
  eye. The shader's default contract assumes a per-eye color target
  (colorOffsetX = 0); routing the SBS through it requires overriding
  both offsets together — otherwise the right-eye pass overwrites the
  left half and the right eye never gets cleared.

UX polish:
- Default/Faster tooltip rewritten with When-to-use guidance, the
  cross-eye sampling caveat for Faster, and the J/K-preset auto-clamp
  behavior. Stops being a one-liner — picks the right tradeoff
  language so users can decide.
- Replaced misused TextDisabled with WrappedInfo (state notes) and
  TextWrapped (current-selection descriptions). TextDisabled now only
  appears where something is actually disabled.
- Switching to Faster logs the preset clamp so the change isn't
  silent.

Cost vs prior Faster: one full-SBS color copy (~30-40 MB/frame) plus
two compute dispatches. Still cheaper than Default — depth/MV/reactive/
transparency are still extent-indirect, not per-eye copied.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Bridge::GetQualityModeAtBoot now gates on IsRouteActive() like the
  other Bridge getters; the header contract promised every query
  returns a neutral value when the route is inactive, and this one
  silently leaked the latched value.
- StretchDRSToFullEye init block now resets vrSubrectStretchCS on
  CreateBuffer/CreateSamplerState failure paths. Otherwise the outer
  !vrSubrectStretchCS guard stays false next frame and the failed
  CB/sampler never get retried — Faster mode would be dead until
  game restart.

(Copilot on PR #44.)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The helper's std::clamp lower bound was 1u, which silently forced DLAA
(qualityMode=0, render at display resolution) into Quality (1.5x).
Visible symptom: in-game render region of kMAIN shrunk to 67% — the
viewport projection scale at Upscaling.cpp:1401 divided by 1.5 instead
of 1.0. Affected both flat and VR DLAA users.

The FfxFsr3QualityMode enum header doesn't declare a 0 entry, but the
implementation delegates to FfxFsr3UpscalerQualityMode which maps
NATIVEAA=0 → 1.0f. The pre-refactor callsites used clamp(0u, 4u) and
relied on this delegation; the helper now does the same.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@alandtse alandtse force-pushed the pr3-dlss-foveated branch from 0692c13 to 02d1b51 Compare May 27, 2026 08:25
@alandtse alandtse merged commit 694504c into dev May 28, 2026
19 checks passed
@alandtse alandtse deleted the pr3-dlss-foveated branch May 28, 2026 02:02
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.

2 participants