Skip to content

feat(vr-dlss): VR DLSS viewport scaling with periphery TAA#1983

Closed
vrnord wants to merge 16 commits into
community-shaders:devfrom
vrnord:vr-dlss-periphery-taa
Closed

feat(vr-dlss): VR DLSS viewport scaling with periphery TAA#1983
vrnord wants to merge 16 commits into
community-shaders:devfrom
vrnord:vr-dlss-periphery-taa

Conversation

@vrnord
Copy link
Copy Markdown
Contributor

@vrnord vrnord commented Mar 16, 2026

Summary

  • VR DLSS viewport scaling: Process only a configurable central crop of each eye through DLSS, reducing GPU cost by up to 50%
  • Periphery TAA: Apply native TAA to the peripheral region outside the DLSS crop, then bilinear upscale to fill the full eye
  • Feathered composite: Smooth blend at DLSS crop boundary to hide the quality transition
  • Nasal crop offset: Shift the DLSS crop toward the nose where visual acuity is highest

Depends on: #1982 (VR stereo reprojection quality improvements)

Details

VR Upscaling Shaders (2917a5f)

New HLSL shaders for the DLSS viewport scaling pipeline:

  • DLSSCompositePS.hlsl — format-converting fullscreen copy (R11G11B10F → R16G16B16A16F)
  • DepthUpscalePS.hlsl — nearest-neighbor depth upscale preserving depth discontinuities
  • ForceAlphaCS.hlsl — sets alpha to 1.0 for DLSS input (DLSS requires alpha channel)
  • VRPeripheryFillCS.hlsl — bilinear upscale from render-res to display-res for periphery region

Viewport Scaling & Feathered Composite (1c1bb29)

Core viewport scaling infrastructure:

  • vrDlssViewportScale setting (0.5–1.0): fraction of each eye processed by DLSS
  • vrDlssCropOffsetX (0.0–0.3): nasal offset to center DLSS on high-acuity region
  • Per-eye intermediate texture management (crop extraction, upscale, composite)
  • Feathered pixel shader composite with alpha blending at crop boundary (technique based on PureDark's Skyrim-Upscaler VR, MIT license)

Streamline Per-Eye DLSS (6a0e00b)

Extends Streamline DLSS integration for VR per-eye evaluation:

  • Separate DLSS evaluation for each eye with independent viewports
  • Per-eye jitter, motion vectors, depth, and reactive mask extraction
  • Camera pinhole offset adjustment for asymmetric crop positions

Periphery TAA via TAAReorder (92139c9)

Post-conductor hook system that captures vanilla TAA output for the periphery:

  • Hooks into Skyrim's post-processing pipeline to intercept TAA'd frames
  • Captures per-eye TAA content before DLSS replaces the central crop
  • Provides temporally stable periphery that blends seamlessly with DLSS center
  • Based on PureDark's technique from Skyrim-Upscaler VR (MIT license)

Attribution

The feathered composite and TAAReorder post-conductor techniques are based on PureDark's Skyrim-Upscaler VR (MIT license).

Test plan

  • VR + DLSS: Enable viewport scaling (0.7), verify DLSS applies to central crop only
  • VR + DLSS: Verify periphery shows TAA'd content (no shimmer/aliasing)
  • VR + DLSS: Check feathered blend at crop boundary (no visible seam)
  • VR + DLSS: Test nasal offset slider shifts crop toward nose
  • VR + DLSS: Measure frame time improvement vs full-frame DLSS
  • VR without DLSS: Verify no regressions when DLSS is disabled
  • Flatrim: Confirm no regressions (VR code paths gated)
  • Build: Verify ALL preset compiles cleanly (SE/AE/VR)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added VR stereo rendering optimization with per-pixel classification and reprojection for improved performance.
    • Introduced VR-specific upscaling options with periphery handling and feathered compositing.
    • Added contrast adaptive sharpening post-processing for VR output.
  • Enhancements

    • Improved parallax offset calculation and alpha testing for better material rendering.
    • Enhanced MIP bias controls for VR foliage and distant objects.
    • Optimized depth upscaling and display resolution mapping for VR viewports.
  • Bug Fixes

    • Fixed depth-stencil state handling for proper VR stereo mask preservation.

vrnord and others added 8 commits March 15, 2026 23:23
…g, and DLSS fix

- Hardware stencil-based Eye 1 pixel culling for forward passes
- StereoBlend lateral reprojection from Eye 0 to Eye 1
- AMD CAS post-TAA sharpening with strength slider
- RGB fringe suppression for tree alpha test artifacts
- Fix UpscaleDepth to use upstream dev version (prevents DLSS crash)
- Mip LOD bias control for VR foliage

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Streamline proxy wrapping disrupts VR compositor frame pacing.
DLSS works without wrapped interfaces; only frame generation needs them.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace nearest-neighbor integer Load() with hardware bilinear
SampleLevel() for color reads during Eye 1 reprojection. Fixes visible
"distortion ridges" at iso-depth contours where floor(offset) jumps
by 1 pixel.

Motion vectors remain integer-sampled via Load() — DLSS requires
discrete motion data. The decoupled approach eliminates visual
artifacts without affecting temporal accumulation.

- Add SampleReprojectedColor() helper using GetDimensions() + UV math
- Bind linear sampler at s0 in overwrite mode (lazy-created)
- Clamp UVs to active DRS viewport bounds

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Store parallax occlusion mapping (POM) depth offset in Reflectance.w
so the stereo reprojection shader can account for parallax displacement
when reprojecting Eye 0 → Eye 1. Without this, POM surfaces show
incorrect stereo depth.

- Enable alpha writes on RT[5] (REFLECTANCE) in deferred blend states
- Output pixelOffset to Reflectance.w in Lighting.hlsl (EMAT+PARALLAX)
- Initialize pixelOffset=0 in ExtendedMaterials GetParallaxCoords
- Add pomDepthScale setting and debugPOMDepth visualization
- Disable CAS (force 0) pending proper integration; hide UI slider

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix HLSL/C++ struct mismatch in CPMSettings.pad0: change float1 → bool
in HLSL and float → uint in C++ to match the bool fields preceding it.
Required after merging VR MIP bias fields with upstream SH ambient
fields in SharedDataCB.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reset CommonLibSSE-NG submodule to upstream 4.7.1 (23bf5512).
Fix SharedDataCB struct padding: float pad0 → float4 pad0 to match
HLSL cbuffer layout (float2 + implicit 8-byte gap before float4
AmbientSHR) and ensure sizeof is a multiple of 16.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Automated formatting by clang-format, prettier, and other hooks.
See https://pre-commit.ci for details.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 16, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 943792c0-71db-44af-84cd-d8442d2f9d62

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Implements comprehensive VR stereo rendering optimizations and DLSS/upscaling integration, introducing stencil-based eye-culling, reprojection, CAS sharpening, per-eye DLSS upscaling with feathered compositing, TAA reordering, and VR-specific MIP bias adjustments across shaders and C++ rendering pipeline.

Changes

Cohort / File(s) Summary
Extended Materials Parallax
features/Extended Materials/Shaders/ExtendedMaterials/ExtendedMaterials.hlsli
Initialize pixelOffset to 0 and adjust parallax offset calculation using lerp with nearBlendToFar factor.
HMD Mask Clearing & Fallback
features/Upscaling/Shaders/Upscaling/ClearHMDMaskCS.hlsl
Add coordinate scaling, fallback texture binding, per-eye offset/dimension tracking, and conditional depth-based color blending instead of direct black writes.
DLSS Composite & Upscale Shaders
features/Upscaling/Shaders/Upscaling/DLSSCompositePS.hlsl, DepthUpscalePS.hlsl
Introduce pixel shader for format-converting fullscreen compositing with optional bilinear upscaling and depth buffer upscaling shader with point sampling and SBS stereo clamping.
Feathered Compositing Shaders
features/Upscaling/Shaders/Upscaling/FeatheredCompositeCS.hlsl, FeatheredCompositePS.hlsl
Add compute and pixel shader variants for feathered crop blending between DLSS center and periphery with distance-based alpha falloff.
Periphery Post-Processing Shaders
features/Upscaling/Shaders/Upscaling/ForceAlphaCS.hlsl, VRPeripheryFillCS.hlsl
Add compute shaders for forcing alpha=1.0 on UI and bilinearly upscaling periphery from render to display resolution per-eye.
VR Stereo Optimization Shaders & Config
features/VR Stereo Optimizations/Shaders/Features/VRStereoOptimizations.ini, package/Shaders/VRStereoOptimizations/*, package/Shaders/VR/CASCS.hlsl
Introduce stencil classification, reprojection, CAS sharpening, and debug visualization shaders for per-pixel stereo mode determination and eye-1 culling via depth/edge detection.
Shared Rendering Data & VR Utilities
package/Shaders/Common/SharedData.hlsli, package/Shaders/Common/VR.hlsli
Add VR MIP bias fields (VRMipBias, VRMipBiasNearDist, VRMipBiasFarDist, VRMipBiasMode, VRAlphaTestThreshold) and per-eye jitter offset in NDC space.
Deferred Rendering VR Integration
package/Shaders/DeferredCompositeCS.hlsl, src/Deferred.cpp
Add early-exit logic for right eye when stereo optimization is active; bind mode texture SRV and dispatch stencil writes; move stereo blending post-composite.
Foliage & Grass VR MIP Bias
package/Shaders/DistantTree.hlsl, package/Shaders/Lighting.hlsl, package/Shaders/RunGrass.hlsl
Integrate depth-gated VR MIP bias adjustments for foliage sampling, alpha testing, and fringe suppression; apply per-eye alpha reference modifications.
VR Stereo Blend Enhancement
package/Shaders/VR/StereoBlendCS.hlsl, src/Features/VR/StereoBlend.cpp, src/Features/VR.h, src/Features/VR/SettingsUI.cpp
Introduce STEREO_OVERWRITE path with per-pixel mode handling, depth reprojection, POM adjustment, debug visualization modes; add stereoBlendOverwriteCS and linear sampler resources; expand StereoBlendCB with edge tint and debug parameters.
VR Post-Processing
package/Shaders/VR/VRPostProcessCS.hlsl
Add bilateral blending compute shader for 2x supersampling with depth-weighted cross-eye reprojection and mode-based pixel classification.
VR Stereo Optimizations Feature
src/Features/VRStereoOptimizations.h, src/Features/VRStereoOptimizations.cpp
Implement complete stereo optimization feature with settings, shader compilation, stencil dispatch, reprojection, CAS post-processing, DSS caching, and comprehensive UI controls.
TAA Reordering VR Integration
src/Features/TAAReorder.h, src/Features/TAAReorder.cpp
Introduce hook-based pipeline for TAA/DLSS interleaving: capture Phase 2A, post-process, evaluate DLSS, paste center crop to per-eye submit with optional feathered compositing.
Upscaling VR Enhancement
src/Features/Upscaling.h, src/Features/Upscaling.cpp, src/Features/Upscaling/Streamline.h, src/Features/Upscaling/Streamline.cpp
Add VR viewport scaling, periphery TAA, nasal crop offset, feather width settings; per-eye DLSS paths; periphery fill/feathered composite; expand ClearHMDMask and Streamline signatures for height, eye0Only, and colorSourceOverride parameters.
Extended Materials Settings
src/Features/ExtendedMaterials.h
Change padding member from float array to single uint in Settings struct.
Feature & Global Integration
src/Feature.cpp, src/Globals.h, src/Globals.cpp
Include VRStereoOptimizations feature; add D3D11 context hooks for depth-stencil state override and stencil-clear flag stripping when VR stereo optimization is active.
Shader Constant Buffer Updates
package/Shaders/VRStereoOptimizations/cbuffers.hlsli, src/State.h, src/State.cpp
Define shared VR stereo optimization parameters cbuffer with frame dimensions, stereo mode, thresholds, edge parameters, and blend distance; populate SharedDataCB VR fields from stereo optimization settings.

Sequence Diagram(s)

sequenceDiagram
    participant CPU as CPU / C++ Renderer
    participant Phase1 as Phase 1: Stencil<br/>Classification & Write
    participant Phase2 as Phase 2: Depth-Stencil<br/>Cache & Modified DSS
    participant Phase3 as Phase 3:<br/>Reprojection
    participant PostTAA as Post-TAA:<br/>CAS Sharpening
    participant DeferredComp as DeferredComposite<br/>(VR_STEREO_OPT)

    CPU->>Phase1: DispatchStencil()<br/>(eye 0 depth analysis)
    Phase1->>Phase1: Classify pixels:<br/>MODE_DISOCCLUDED,<br/>MODE_EDGE,<br/>MODE_MAIN,<br/>MODE_FULL_BLEND
    Phase1->>Phase1: ExecuteStencilWritePass<br/>(eye 1 stencil buffer)
    
    CPU->>Phase2: GetOrCreateModifiedDSS<br/>(stencil NOT_EQUAL ref=1)
    Phase2->>Phase2: Clone & cache DSS<br/>for eye 1 skip

    CPU->>DeferredComp: Bind ModeTexture SRV<br/>for right eye early-exit
    DeferredComp->>DeferredComp: If eyeIndex==1 &&<br/>mode in {1,2}:<br/>early-return
    
    CPU->>Phase3: DispatchReprojection<br/>(eye 0→eye 1)
    Phase3->>Phase3: Read MODE_MAIN pixels<br/>from eye 1
    Phase3->>Phase3: Reproject to eye 0<br/>coordinates & sample
    Phase3->>Phase3: Write eye 0 color<br/>to eye 1 render target
    
    CPU->>PostTAA: ApplyCAS<br/>(post-TAA output)
    PostTAA->>PostTAA: Adaptive sharpening<br/>via cross/diagonal<br/>neighbor analysis
    PostTAA->>PostTAA: Write sharpened<br/>result to render target
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly Related PRs

Suggested Reviewers

  • alandtse
  • jiayev

🐰 Whiskers twitch with delight as the warren hops through VR's stereo maze,
Stencils and shaders dance in perfect phase,
Each eye sees true, no culling haze,
DLSS crops bloom where feathering plays,
And TAA reorders through rendering's craze!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 24.27% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat(vr-dlss): VR DLSS viewport scaling with periphery TAA' clearly and concisely describes the main feature being implemented—a VR-specific DLSS viewport scaling system with peripheral TAA support. It accurately reflects the core objective without being vague or off-topic.

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

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

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 16, 2026

Actionable Suggestions

  • Hair Specular (jiayev): Needs version bump to 1-0-4
  • Light Limit Fix (Alan Tse): Needs version bump to 3-0-3
  • Linear Lighting (jiayev): Needs version bump to 1-0-1
  • Performance Overlay (Dlizzio): Needs version bump to 1-0-1
  • Screen Space GI (Igor Alan Albuquerque de Sousa): Needs version bump to 4-0-2
  • Sky Sync (Skrubby Skrub In A Shrub): Needs version bump to 1-0-1
  • Skylighting (jiayev): Needs version bump to 1-2-4
  • Terrain Variation (Igor Alan Albuquerque de Sousa): Needs version bump to 1-0-2
  • Unified Water (Dlizzio): Needs version bump to 1-0-1
  • Upscaling (jiayev): Needs version bump to 1-2-1
  • VR (Dlizzio): Needs version bump to 1-0-2
  • Weather Editor (Dlizzio): Needs version bump to 1-0-1

@github-actions
Copy link
Copy Markdown

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 16

Caution

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

⚠️ Outside diff range comments (2)
features/Extended Materials/Shaders/ExtendedMaterials/ExtendedMaterials.hlsli (1)

500-514: ⚠️ Potential issue | 🟡 Minor

Potential discontinuity between blended and fallback pixelOffset values.

Line 500 now blends pixelOffset toward 0.5 as nearBlendToFar approaches 1.0, but line 513 sets pixelOffset = 0 when the computation block is skipped entirely. This creates a discontinuity at the transition boundary (when nearBlendToFar >= 1.0), which could cause a visible seam or "pop" at the parallax fade-out distance.

Consider updating line 513 to pixelOffset = 0.5; for consistency with the blend target.

Proposed fix
 `#if` defined(LANDSCAPE)
 		weights[0] = input.LandBlendWeights1.x;
 		weights[1] = input.LandBlendWeights1.y;
 		weights[2] = input.LandBlendWeights1.z;
 		weights[3] = input.LandBlendWeights1.w;
 		weights[4] = input.LandBlendWeights2.x;
 		weights[5] = input.LandBlendWeights2.y;
 `#endif`

-		pixelOffset = 0;
+		pixelOffset = 0.5;
 		return coords;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@features/Extended`
Materials/Shaders/ExtendedMaterials/ExtendedMaterials.hlsli around lines 500 -
514, The fallback branch sets pixelOffset = 0 which creates a discontinuity
versus the blended path that lerps pixelOffset toward 0.5 as nearBlendToFar → 1;
update the fallback assignment to pixelOffset = 0.5 so the value matches the
blend target (change the final pixelOffset = 0 to pixelOffset = 0.5) — keep the
return coords as-is so the function continues returning coords while ensuring
pixelOffset, nearBlendToFar, and the blended computation (viewDirTS,
parallaxAmount) remain continuous at the transition.
src/Features/VR/SettingsUI.cpp (1)

326-345: ⚠️ Potential issue | 🟡 Minor

Explain the two new debug views in the tooltip.

Overwrite and Overwrite Eye1 are now selectable, but the help text still ends at Edge Detection, so the last two entries have no in-UI explanation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Features/VR/SettingsUI.cpp` around lines 326 - 345, The tooltip text for
the Debug View Combo (debugModes array and StereoBlendDebugMode) stops at "Edge
Detection" and lacks explanations for the new "Overwrite" and "Overwrite Eye1"
modes; update the ImGui::Text tooltip block inside the
Util::HoverTooltipWrapper() so it appends clear, concise descriptions for
"Overwrite" (force use of blended/override color for both eyes) and "Overwrite
Eye1" (force override for eye 1 only), matching the style of the existing
entries so users can understand the behavior of these two modes.
🧹 Nitpick comments (10)
features/Upscaling/Shaders/Upscaling/DepthUpscalePS.hlsl (1)

35-41: Unused constant buffer member InvSourceDim.

InvSourceDim is declared but never used in this shader. If it's not needed for future expansion, consider removing it to reduce constant buffer size.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@features/Upscaling/Shaders/Upscaling/DepthUpscalePS.hlsl` around lines 35 -
41, The cbuffer DepthUpscaleCB declares InvSourceDim but it is never referenced;
remove the unused member to shrink the constant buffer. Edit the DepthUpscaleCB
definition in DepthUpscalePS.hlsl and delete the line declaring float2
InvSourceDim (or replace with a comment placeholder if you expect future use),
then rebuild and run shader validation to ensure member layout changes don't
break any constant buffer bindings that reference DepthUpscaleCB elsewhere.
features/Upscaling/Shaders/Upscaling/FeatheredCompositeCS.hlsl (1)

24-28: Minor: Edge distance calculation differs from PS version.

The CS uses CropW - 1 - cropLocal.x for distRight, so the rightmost pixel has distRight = 0. The PS version (FeatheredCompositePS.hlsl) uses (CropOrigin.x + CropSize.x) - pixelPos.x, giving the rightmost pixel distRight = 1.

This creates a 1-pixel feathering inconsistency at the right/bottom edges between the two paths. If both shaders are intended to produce identical results, consider aligning the formulas.

🔧 Option to align with PS version
-	float distRight = (float)(CropW - 1 - cropLocal.x);
+	float distRight = (float)(CropW - cropLocal.x);
-	float distBottom = (float)(CropH - 1 - cropLocal.y);
+	float distBottom = (float)(CropH - cropLocal.y);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@features/Upscaling/Shaders/Upscaling/FeatheredCompositeCS.hlsl` around lines
24 - 28, The right/bottom edge distance math in the compute shader uses "CropW -
1 - cropLocal.x" and "CropH - 1 - cropLocal.y" (variables distRight and
distBottom), which yields 0 at the rightmost/bottommost pixel and differs from
the pixel shader’s "(CropOrigin + CropSize) - pixelPos" behavior; update
distRight and distBottom to use "CropW - cropLocal.x" and "CropH - cropLocal.y"
(preserving the float casts) so the right/bottom edges produce 1 like
FeatheredCompositePS.hlsl, keeping the rest of the distFromEdge/min logic
unchanged.
features/Upscaling/Shaders/Upscaling/VRPeripheryFillCS.hlsl (1)

1-4: PR metadata tweak: add explicit issue keyword (and optionally shorten title).

Consider adding Implements #1983`` in the PR description for automatic issue linkage; optionally tighten the title to conventional length/style.

As per coding guidelines, feature PRs should include GitHub keywords like "Implements #123", and conventional-commit titles should follow the documented format/length.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@features/Upscaling/Shaders/Upscaling/VRPeripheryFillCS.hlsl` around lines 1 -
4, Update the PR metadata to include the GitHub issue keyword by adding
"Implements `#1983`" to the PR description and, if desired, shorten the PR title
to conform to conventional-commit length/style; reference this change to the
shader added in VRPeripheryFillCS.hlsl so reviewers can link the feature
implementation to the issue and ensure automatic issue linkage.
package/Shaders/DistantTree.hlsl (1)

1-1: Consider tightening PR metadata to match repo convention.

Suggested title (≤50 chars): feat(vr-dlss): add periphery taa compositing
Also add explicit issue linkage in PR body, e.g. Implements #1983``.

As per coding guidelines, "**/*: provide suggestions for Conventional Commit Titles ... Length: 50 characters limit ... and Issue References using keywords like Implements #123``."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package/Shaders/DistantTree.hlsl` at line 1, Update the PR metadata to follow
repo conventions: change the PR title to a concise Conventional Commit style
(suggested: "feat(vr-dlss): add periphery taa compositing") and add an explicit
issue linkage in the PR body using a keyword like "Implements `#1983`"; ensure the
PR description references the relevant shader file (Shaders/DistantTree.hlsl) so
reviewers can correlate the change.
package/Shaders/VRStereoOptimizations/StencilWritePS.hlsl (1)

36-50: Whitelist the modes that should write stencil.

The comment says only MODE_MAIN and MODE_EDGE are meant to survive, but the current blacklist will also write stencil for any future or legacy mode value that isn't MODE_DISOCCLUDED or MODE_FULL_BLEND. An allow-list keeps the pass aligned with its contract and prevents accidental Eye 1 culling if another classification value reappears.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package/Shaders/VRStereoOptimizations/StencilWritePS.hlsl` around lines 36 -
50, Replace the current blacklist approach with an explicit allow-list so only
MODE_MAIN and MODE_EDGE write the stencil: in the shader code that inspects the
mode variable (currently comparing to MODE_DISOCCLUDED and MODE_FULL_BLEND),
change the logic to discard unless mode == MODE_MAIN or mode == MODE_EDGE;
preserve the existing sky/HMD depth checks (DepthTexture[modeCoord]) and their
discards but ensure they run only after confirming the mode is allowed. This
ensures only the intended symbols MODE_MAIN and MODE_EDGE (and the depth checks
using DepthTexture and modeCoord) reach stencil-writing.
package/Shaders/Common/SharedData.hlsli (1)

60-60: Type change from float1 to bool in CPMSettings.

The pad0 field changed from float to bool. In HLSL, bool is typically 4 bytes in cbuffers, so this should be compatible. However, ensure the corresponding C++ struct uses uint (not bool) for the padding field to maintain size consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package/Shaders/Common/SharedData.hlsli` at line 60, CPMSettings's pad0 was
changed in HLSL from float to bool; update the matching C++ struct field (the
pad0 member in the CPMSettings struct) to use an unsigned int type (e.g.,
uint32_t or unsigned int) instead of bool so the CPU-side layout and size match
the HLSL cbuffer bool (4 bytes) and maintain proper packing/alignment with other
members defined in SharedData.hlsli.
package/Shaders/VR/VRPostProcessCS.hlsl (1)

101-106: Consider documenting or parameterizing the depth tolerance.

The hardcoded 0.02 in the depth agreement calculation is a magic number that affects blend quality. Consider either:

  1. Documenting why 0.02 was chosen
  2. Moving it to the constant buffer for tuning
- float depthAgreement = 1.0 - saturate(abs(centerDepth - otherDepth) / maxDepth / 0.02);
+ // 0.02 = 2% relative depth tolerance for bilateral weight
+ float depthAgreement = 1.0 - saturate(abs(centerDepth - otherDepth) / maxDepth / 0.02);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package/Shaders/VR/VRPostProcessCS.hlsl` around lines 101 - 106, The depth
agreement uses a hardcoded tolerance (0.02) in VRPostProcessCS.hlsl which makes
tuning difficult; replace this magic number with a named parameter (e.g.,
DepthTolerance) exposed via the shader constant buffer or add a clearly
commented constant at top of the file, then use that symbol in the
depthAgreement calculation (the lines computing maxDepth, depthAgreement, and
blendWeight) so the tolerance can be documented and adjusted without changing
code.
package/Shaders/RunGrass.hlsl (1)

876-885: Duplicate VR alpha test logic—consider extracting to a shared macro or function.

This VR alpha test adjustment duplicates the pattern from lines 505-514. If the offset value (0.1) changes, both locations must be updated.

♻️ Optional: Extract VR alpha test to a shared helper

Consider adding a helper function in Common/VR.hlsli:

float Stereo::GetVRAlphaTestRef(float alphaRef, uint eyeIndex)
{
    return alphaRef - eyeIndex * 0.1;  // or use SharedData::VRAlphaTestThreshold
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package/Shaders/RunGrass.hlsl` around lines 876 - 885, The VR alpha-test
logic duplicates code that adjusts AlphaTestRefRS by Stereo::GetEyeIndexPS;
extract this into a shared helper (e.g., Stereo::GetVRAlphaTestRef or a
SharedData::VRAlphaTestThreshold-aware helper placed in Common/VR.hlsli) and
replace the local calculation in this block (references: AlphaTestRefRS,
Stereo::GetEyeIndexPS, diffuseAlpha) with a single call to that helper so the
adjustment (currently eyeIndex * 0.1) is defined in one place.
src/Features/Upscaling.h (1)

188-194: Add 16-byte alignment guarantee for HLSL constant buffer compatibility.

DlssCompositeCB is used as a GPU constant buffer but lacks the alignas(16) specifier and static_assert validation that VRStereoOptParams in VRStereoOptimizations.h correctly uses. HLSL cbuffers require 16-byte alignment.

♻️ Proposed fix
-	struct DlssCompositeCB
+	struct alignas(16) DlssCompositeCB
 	{
 		float2 DynResScale;  // renderRes / displayRes per-eye
 		float2 EyeOffset;    // (i * eyeWidth, 0)
 		float2 SrcTexSize;   // full texture dimensions
 		float2 pad;
 	};
+	static_assert(sizeof(DlssCompositeCB) % 16 == 0, "DlssCompositeCB must be 16-byte aligned for HLSL cbuffer.");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Features/Upscaling.h` around lines 188 - 194, DlssCompositeCB is used as
a GPU constant buffer but lacks 16-byte alignment; add alignas(16) to the
DlssCompositeCB declaration and include a compile-time check similar to
VRStereoOptParams (e.g., a static_assert verifying alignof(DlssCompositeCB) ==
16 and/or that sizeof(DlssCompositeCB) is a multiple of 16) to ensure HLSL
cbuffer compatibility.
src/Features/VRStereoOptimizations.h (1)

228-228: DSS cache keyed on raw pointers assumes original states remain valid.

The cache uses the original ID3D11DepthStencilState* as a lookup key. If an external component releases a cached DSS, subsequent lookups could return stale entries pointing to freed memory (dangling reference in the map values, not immediate UB from the key itself).

Consider documenting this assumption, or clearing the cache in Reset() if DSS lifetimes are not guaranteed to outlive the feature.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Features/VRStereoOptimizations.h` at line 228, The dssCache currently
uses raw ID3D11DepthStencilState* as the map key which can become invalid if
external code releases those objects; either ensure the cache owns strong
references or purge entries on reset. Fix by updating the cache to use
winrt::com_ptr<ID3D11DepthStencilState> (or another owning handle) as the
key/value so the cache holds a reference and prevents dangling pointers, or, if
changing key type is undesirable, add logic to clear dssCache in the class
Reset() method to remove any potentially freed entries and document the lifetime
assumption for ID3D11DepthStencilState objects in VRStereoOptimizations.h.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@features/Upscaling/Shaders/Upscaling/DLSSCompositePS.hlsl`:
- Around line 28-36: The bilinear mapping is using corner coordinates so the
upscale is shifted by half a texel; in main(VS_OUTPUT input) explicitly map
pixel centers before scaling: convert input.Position.xy to center space
(subtract 0.5), apply EyeOffset adjustments and DynResScale, then convert back
to texel/UV space (add 0.5) before dividing by SrcTexSize and calling
Source.SampleLevel with LinearSampler; additionally, when sampling a packed
side-by-side (SBS) source, clamp srcPos.x to the active eye region to prevent
seam bleed (use EyeOffset/X-region/active-eye logic around srcPos.x prior to
computing srcUV).

In `@package/Shaders/Common/VR.hlsli`:
- Around line 630-636: The shared VR vertex helper (GetVRVSOutput / the block
using a_eyeIndex) currently applies a permanent right-eye-only screen-space
jitter by adding kJitterNDC to vsout.VRPosition.xy; remove that global
modification and instead expose an opt-in path: delete or guard the if
(a_eyeIndex == 1) { static const float2 kJitterNDC ... vsout.VRPosition.xy +=
... } from the shared helper and provide either a boolean parameter (e.g., bool
enableEyeJitter) or a separate helper/overload that applies the jitter only when
explicitly requested by a material/pass that needs alpha-tested edge
stabilization, ensuring other callers of GetVRVSOutput are unaffected.

In `@package/Shaders/Lighting.hlsl`:
- Around line 3097-3105: The fringe-suppression block that modifies
baseColor.rgb is currently run after lighting so it never affects
material.BaseColor, color, or outputAlbedo (and thus
psout.Diffuse/psout.Albedo); move or apply this correction earlier so the
corrected baseColor feeds into the lighting/albedo path. Specifically, either
relocate the if (vrFoliageBias < 0) { ... } block to immediately after baseColor
is sampled/derived (before material.BaseColor/color/outputAlbedo are computed)
or replace uses of uncorrected baseColor when computing material.BaseColor,
color, and outputAlbedo with the saturated, bias-scaled version so psout.Diffuse
and psout.Albedo use the corrected RGB. Ensure you reference and update
baseColor, material.BaseColor, color, outputAlbedo, psout.Diffuse, and
psout.Albedo accordingly.
- Around line 3200-3205: The code currently only writes pixelOffset into
psout.Reflectance under the EMAT && (PARALLAX || LANDSCAPE) guard which causes
TRUE_PBR deferred permutations to lose the per-pixel height; update the
conditional so that when PBRParallax has populated pixelOffset (i.e. TRUE_PBR
permutations) it is preserved — for example extend the preprocessor check to
include TRUE_PBR (or remove the PARALLAX-only restriction) so psout.Reflectance
= float4(indirectLobeWeights.specular, (pixelOffset > 0.0) ?
saturate(pixelOffset) : 0.0); is emitted for TRUE_PBR as well; reference
psout.Reflectance, pixelOffset, TRUE_PBR, EMAT, PARALLAX, and LANDSCAPE to
locate the change.

In `@package/Shaders/VR/CASCS.hlsl`:
- Around line 37-47: Border samples are currently read out of bounds using sp +
int2(...) before Source.Load; clamp each neighbor coordinate to the valid
texture extent so edge pixels duplicate/clamp instead of sampling off-image.
Change the nine fetches to compute a clamped coordinate (e.g. neighbor =
clamp(sp + int2(dx,dy), int2(0,0), texSize - int2(1,1))) and pass that clamped
int2 into Source.Load; use DTid/sp to form the base and the existing Source.Load
calls (Source.Load, DTid, sp) to locate and replace the unsafe offsets. Ensure
you obtain the texture dimensions (texSize/texDim via GetDimensions or the
existing size variable) for the clamp bounds.

In `@package/Shaders/VRStereoOptimizations/StencilCS.hlsl`:
- Around line 19-20: The shader currently uses a hardcoded
kDisocclusionThreshold constant instead of the value populated by
VRStereoOptimizations::UpdateConstantBuffer; replace uses of
kDisocclusionThreshold (including the comparisons around lines 79-81) with the
DisocclusionThreshold value read from the constant buffer (cbuffer) so the
shader reads the runtime-configured DisocclusionThreshold set by
UpdateConstantBuffer; locate the cbuffer declaration (where
DisocclusionThreshold is defined) and reference that variable in the comparisons
instead of the static constant.

In `@src/Deferred.cpp`:
- Around line 279-283: When VR is active the code calls
globals::features::vrStereoOptimizations.DispatchStencil() unconditionally; add
the same feature-loaded/enabled guard used in other VR stereo paths (e.g., the
checks around the calls at lines near the other DispatchStencil/optimization
usages) so that you only call
globals::features::vrStereoOptimizations.DispatchStencil() when
globals::features::vrStereoOptimizations.isLoaded() (or the equivalent
loaded/enabled flag) returns true; modify the VR block that wraps
DispatchStencil to first check that vrStereoOptimizations is loaded/enabled
before invoking DispatchStencil().

In `@src/Features/TAAReorder.cpp`:
- Around line 145-157: Reset the per-frame readiness flags at the top of the
hook before any early-return or capture logic so stale true values don't persist
across frames; specifically set g_postPPReady (and the analogous pre/post flags
used elsewhere such as g_prePPReady and any other *_Ready booleans) to false
before calling EnsurePostPPCopy / EnsurePrePPCopy or performing CopyResource,
then let the existing code set them to true only when a valid capture/paste
completes (affecting the blocks around EnsurePostPPCopy, EnsurePrePPCopy,
g_postPPCopy, and their equivalents at the other mentioned locations).
- Around line 119-120: g_diagCounter is never updated so shouldLog always true;
update the diagnostic gate by incrementing or toggling g_diagCounter at the
start/end of the frame or pass where logging is intended (e.g., increment at
frame start and decrement at frame end) and use that counter in
ShouldReorderTAA/shouldLog checks to limit logging; apply the change around the
entry/exit points for ExecutePassHook, BSImagespaceShaderHook, and SubmitHook so
the gate advances once per pass/frame, and ensure the updates to g_diagCounter
are thread-safe if hooks can be invoked concurrently.
- Around line 239-253: Clamp and validate the VR DLSS config values before using
them to compute rectangles: ensure upscaling.settings.vrDlssViewportScale is
clamped to a reasonable (e.g. >0 and <=1) range,
upscaling.settings.vrDlssCropOffsetX is clamped to a safe [-1,1] (or narrower)
range, and upscaling.settings.vrDlssFeatherWidth is clamped to [0,
maxFeatherRatio]; then recompute centerW/centerH, baseCenterX/centerY,
nasalShift and featherPixels using the validated values (these variables appear
where screenSize, eyeW, eyeH, centerW, centerH, baseCenterX, centerY,
nasalShift, featherPixels and FinalizePerEyeOutputs are used); apply the same
validation in the other occurrences noted (around the other diffs at the
referenced spots) so derived crop boxes never exceed eye bounds and math remains
safe.
- Around line 60-73: EnsurePostPPCopy must guard against failed DirectX
allocations: check the HRESULT returned from
globals::d3d::device->CreateTexture2D and CreateShaderResourceView and only set
g_postPPCopy/g_postPPCopySRV when those calls succeed; if either fails, log the
error, release/clear the corresponding smart pointers and do not call
CopyResource(g_postPPCopy.get(), postTex) later. Specifically, update
EnsurePostPPCopy to capture and test HRESULTs from
CreateTexture2D/CreateShaderResourceView, verify g_postPPCopy is non-null before
creating the SRV and before any CopyResource call, and ensure CopyResource usage
(and any subsequent resource-dependent code) early-exits or skips when
g_postPPCopy is null to avoid dereferencing a null texture.

In `@src/Features/Upscaling.cpp`:
- Around line 69-75: The hook hk_D3D11CreateDeviceAndSwapChainUpscaling()
currently reads globals::game::isVR which may be uninitialized at hook time;
replace that cached check with a runtime VR detection call (use
REL::Module::IsVR() or the project's runtime-detection helper) when deciding to
set pSwapChainDesc->SwapEffect and shouldProxy/interface-wrapping logic, and
apply the same change to the other similar branches (the block around lines
referenced in the review) so all device-creation code uses runtime detection
instead of globals::game::isVR to handle SE/AE/VR variants safely.

In `@src/Features/Upscaling/Streamline.cpp`:
- Around line 610-624: The log branch detects periphery TAA via
upscaling.settings.vrPeripheryTAA but FillPeriphery still uses the
default/previous source (vrIntermediateColorIn), so periphery is not coming from
the TAA SRV; update the call to upscaling.FillPeriphery(...) in the
viewportScaling branch to pass the TAA per-eye SRV (upscaling.vrTAAdPerEye[i])
when upscaling.settings.vrPeripheryTAA is true (otherwise pass the
existing/default SRV), ensuring FillPeriphery receives the correct source;
reference FillPeriphery and upscaling.vrTAAdPerEye to locate the change and add
a conditional argument or overload as needed.

In `@src/Features/VRStereoOptimizations.cpp`:
- Around line 173-182: The code arms stencil culling even when the stencil-write
DSV is missing; modify DispatchStencil() to check the actual resource
(stencilWriteReadOnlyDSV or the created DSV handle from
SetupResources/CreateDepthStencilView) before setting stencilActive = true and
before writing NOT_EQUAL marks, and if the DSV is absent skip arming and log a
warning; also ensure the later hook that injects NOT_EQUAL stencil tests
verifies stencilActive (or the DSV presence) before applying tests to avoid
using an uninitialized stencil buffer.
- Around line 44-89: In VRStereoOptimizations::LoadSettings the code calls
o_json["..."].get<>() after only contains(), which can throw on wrong types and
accepts out-of-range values; update each branch (StereoMode,
DisocclusionDepthThreshold, QualityJitterOffset, FoveatedRegionRadius,
FoveatedRegionCenterX/Y, UseEyeTracking, Debug*, FullBlendDistance, MipBiasMode,
MipLodBias, MipBiasNearDist, MipBiasFarDist, AlphaTestThreshold, etc.) to first
check the JSON type (e.g., is_number()/is_boolean()/is_string()), then read and
validate/clamp the value to acceptable ranges (e.g., centers 0–1, radii >=0,
mipBiasMode within allowed enum set, thresholds within sensible bounds); on
invalid type or out-of-range value, fall back to the existing settings.*
defaults (or clamp) and emit a warning/log entry instead of allowing an
exception so State::Load() degrades gracefully. Ensure CASStrength remains
forced to 0.0f as before.

In `@src/State.h`:
- Around line 213-218: The struct has a packing mismatch: C++ declares float4
pad0 while HLSL uses float2 pad0 causing AmbientSHR/AmbientSHG/AmbientSHB
offsets to diverge; fix by making the padding identical in both languages—either
change the C++ pad0 to float2 to match HLSL or change the HLSL pad0 to float4 to
match C++; then update the inline comment near
VRMipBias/VRMipBiasNearDist/VRMipBiasFarDist/VRMipBiasMode/VRAlphaTestThreshold/pad0
to reflect the exact bytes of padding and verify the total bytes before
AmbientSHR are the same in C++ and HLSL.

---

Outside diff comments:
In `@features/Extended`
Materials/Shaders/ExtendedMaterials/ExtendedMaterials.hlsli:
- Around line 500-514: The fallback branch sets pixelOffset = 0 which creates a
discontinuity versus the blended path that lerps pixelOffset toward 0.5 as
nearBlendToFar → 1; update the fallback assignment to pixelOffset = 0.5 so the
value matches the blend target (change the final pixelOffset = 0 to pixelOffset
= 0.5) — keep the return coords as-is so the function continues returning coords
while ensuring pixelOffset, nearBlendToFar, and the blended computation
(viewDirTS, parallaxAmount) remain continuous at the transition.

In `@src/Features/VR/SettingsUI.cpp`:
- Around line 326-345: The tooltip text for the Debug View Combo (debugModes
array and StereoBlendDebugMode) stops at "Edge Detection" and lacks explanations
for the new "Overwrite" and "Overwrite Eye1" modes; update the ImGui::Text
tooltip block inside the Util::HoverTooltipWrapper() so it appends clear,
concise descriptions for "Overwrite" (force use of blended/override color for
both eyes) and "Overwrite Eye1" (force override for eye 1 only), matching the
style of the existing entries so users can understand the behavior of these two
modes.

---

Nitpick comments:
In `@features/Upscaling/Shaders/Upscaling/DepthUpscalePS.hlsl`:
- Around line 35-41: The cbuffer DepthUpscaleCB declares InvSourceDim but it is
never referenced; remove the unused member to shrink the constant buffer. Edit
the DepthUpscaleCB definition in DepthUpscalePS.hlsl and delete the line
declaring float2 InvSourceDim (or replace with a comment placeholder if you
expect future use), then rebuild and run shader validation to ensure member
layout changes don't break any constant buffer bindings that reference
DepthUpscaleCB elsewhere.

In `@features/Upscaling/Shaders/Upscaling/FeatheredCompositeCS.hlsl`:
- Around line 24-28: The right/bottom edge distance math in the compute shader
uses "CropW - 1 - cropLocal.x" and "CropH - 1 - cropLocal.y" (variables
distRight and distBottom), which yields 0 at the rightmost/bottommost pixel and
differs from the pixel shader’s "(CropOrigin + CropSize) - pixelPos" behavior;
update distRight and distBottom to use "CropW - cropLocal.x" and "CropH -
cropLocal.y" (preserving the float casts) so the right/bottom edges produce 1
like FeatheredCompositePS.hlsl, keeping the rest of the distFromEdge/min logic
unchanged.

In `@features/Upscaling/Shaders/Upscaling/VRPeripheryFillCS.hlsl`:
- Around line 1-4: Update the PR metadata to include the GitHub issue keyword by
adding "Implements `#1983`" to the PR description and, if desired, shorten the PR
title to conform to conventional-commit length/style; reference this change to
the shader added in VRPeripheryFillCS.hlsl so reviewers can link the feature
implementation to the issue and ensure automatic issue linkage.

In `@package/Shaders/Common/SharedData.hlsli`:
- Line 60: CPMSettings's pad0 was changed in HLSL from float to bool; update the
matching C++ struct field (the pad0 member in the CPMSettings struct) to use an
unsigned int type (e.g., uint32_t or unsigned int) instead of bool so the
CPU-side layout and size match the HLSL cbuffer bool (4 bytes) and maintain
proper packing/alignment with other members defined in SharedData.hlsli.

In `@package/Shaders/DistantTree.hlsl`:
- Line 1: Update the PR metadata to follow repo conventions: change the PR title
to a concise Conventional Commit style (suggested: "feat(vr-dlss): add periphery
taa compositing") and add an explicit issue linkage in the PR body using a
keyword like "Implements `#1983`"; ensure the PR description references the
relevant shader file (Shaders/DistantTree.hlsl) so reviewers can correlate the
change.

In `@package/Shaders/RunGrass.hlsl`:
- Around line 876-885: The VR alpha-test logic duplicates code that adjusts
AlphaTestRefRS by Stereo::GetEyeIndexPS; extract this into a shared helper
(e.g., Stereo::GetVRAlphaTestRef or a SharedData::VRAlphaTestThreshold-aware
helper placed in Common/VR.hlsli) and replace the local calculation in this
block (references: AlphaTestRefRS, Stereo::GetEyeIndexPS, diffuseAlpha) with a
single call to that helper so the adjustment (currently eyeIndex * 0.1) is
defined in one place.

In `@package/Shaders/VR/VRPostProcessCS.hlsl`:
- Around line 101-106: The depth agreement uses a hardcoded tolerance (0.02) in
VRPostProcessCS.hlsl which makes tuning difficult; replace this magic number
with a named parameter (e.g., DepthTolerance) exposed via the shader constant
buffer or add a clearly commented constant at top of the file, then use that
symbol in the depthAgreement calculation (the lines computing maxDepth,
depthAgreement, and blendWeight) so the tolerance can be documented and adjusted
without changing code.

In `@package/Shaders/VRStereoOptimizations/StencilWritePS.hlsl`:
- Around line 36-50: Replace the current blacklist approach with an explicit
allow-list so only MODE_MAIN and MODE_EDGE write the stencil: in the shader code
that inspects the mode variable (currently comparing to MODE_DISOCCLUDED and
MODE_FULL_BLEND), change the logic to discard unless mode == MODE_MAIN or mode
== MODE_EDGE; preserve the existing sky/HMD depth checks
(DepthTexture[modeCoord]) and their discards but ensure they run only after
confirming the mode is allowed. This ensures only the intended symbols MODE_MAIN
and MODE_EDGE (and the depth checks using DepthTexture and modeCoord) reach
stencil-writing.

In `@src/Features/Upscaling.h`:
- Around line 188-194: DlssCompositeCB is used as a GPU constant buffer but
lacks 16-byte alignment; add alignas(16) to the DlssCompositeCB declaration and
include a compile-time check similar to VRStereoOptParams (e.g., a static_assert
verifying alignof(DlssCompositeCB) == 16 and/or that sizeof(DlssCompositeCB) is
a multiple of 16) to ensure HLSL cbuffer compatibility.

In `@src/Features/VRStereoOptimizations.h`:
- Line 228: The dssCache currently uses raw ID3D11DepthStencilState* as the map
key which can become invalid if external code releases those objects; either
ensure the cache owns strong references or purge entries on reset. Fix by
updating the cache to use winrt::com_ptr<ID3D11DepthStencilState> (or another
owning handle) as the key/value so the cache holds a reference and prevents
dangling pointers, or, if changing key type is undesirable, add logic to clear
dssCache in the class Reset() method to remove any potentially freed entries and
document the lifetime assumption for ID3D11DepthStencilState objects in
VRStereoOptimizations.h.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e0b31891-b61f-425e-a255-f9fdc8bf76c0

📥 Commits

Reviewing files that changed from the base of the PR and between 47e3a79 and 3a155ea.

📒 Files selected for processing (42)
  • features/Extended Materials/Shaders/ExtendedMaterials/ExtendedMaterials.hlsli
  • features/Upscaling/Shaders/Upscaling/ClearHMDMaskCS.hlsl
  • features/Upscaling/Shaders/Upscaling/DLSSCompositePS.hlsl
  • features/Upscaling/Shaders/Upscaling/DepthUpscalePS.hlsl
  • features/Upscaling/Shaders/Upscaling/FeatheredCompositeCS.hlsl
  • features/Upscaling/Shaders/Upscaling/FeatheredCompositePS.hlsl
  • features/Upscaling/Shaders/Upscaling/ForceAlphaCS.hlsl
  • features/Upscaling/Shaders/Upscaling/VRPeripheryFillCS.hlsl
  • features/VR Stereo Optimizations/Shaders/Features/VRStereoOptimizations.ini
  • package/Shaders/Common/SharedData.hlsli
  • package/Shaders/Common/VR.hlsli
  • package/Shaders/DeferredCompositeCS.hlsl
  • package/Shaders/DistantTree.hlsl
  • package/Shaders/Lighting.hlsl
  • package/Shaders/RunGrass.hlsl
  • package/Shaders/VR/CASCS.hlsl
  • package/Shaders/VR/StereoBlendCS.hlsl
  • package/Shaders/VR/VRPostProcessCS.hlsl
  • package/Shaders/VRStereoOptimizations/ReprojectionCS.hlsl
  • package/Shaders/VRStereoOptimizations/StencilCS.hlsl
  • package/Shaders/VRStereoOptimizations/StencilWritePS.hlsl
  • package/Shaders/VRStereoOptimizations/StencilWriteVS.hlsl
  • package/Shaders/VRStereoOptimizations/cbuffers.hlsli
  • src/Deferred.cpp
  • src/Feature.cpp
  • src/Features/ExtendedMaterials.h
  • src/Features/TAAReorder.cpp
  • src/Features/TAAReorder.h
  • src/Features/Upscaling.cpp
  • src/Features/Upscaling.h
  • src/Features/Upscaling/Streamline.cpp
  • src/Features/Upscaling/Streamline.h
  • src/Features/VR.cpp
  • src/Features/VR.h
  • src/Features/VR/SettingsUI.cpp
  • src/Features/VR/StereoBlend.cpp
  • src/Features/VRStereoOptimizations.cpp
  • src/Features/VRStereoOptimizations.h
  • src/Globals.cpp
  • src/Globals.h
  • src/State.cpp
  • src/State.h

Comment on lines +28 to +36
float4 main(VS_OUTPUT input) : SV_Target
{
// Map display-res pixel position to render-res source position.
// Subtract eye offset, scale to render-res, add eye offset back.
float2 localPos = input.Position.xy - EyeOffset;
float2 srcLocal = localPos * DynResScale;
float2 srcPos = srcLocal + EyeOffset;
float2 srcUV = srcPos / SrcTexSize;
return Source.SampleLevel(LinearSampler, srcUV, 0);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix the bilinear source-coordinate mapping before shipping this.

input.Position.xy is already in destination pixel-center space, so localPos * DynResScale maps corners instead of centers and shifts the whole upscale by half a texel. That will slightly blur/misalign the DLSS/periphery composite. Map centers explicitly before converting to UV; if this samples a packed SBS texture, clamp srcPos.x to the active eye as well to avoid seam bleed.

🐛 Proposed fix
-	float2 localPos = input.Position.xy - EyeOffset;
-	float2 srcLocal = localPos * DynResScale;
+	float2 localPos = input.Position.xy - EyeOffset;
+	float2 srcLocal = (localPos - float2(0.5, 0.5)) * DynResScale + float2(0.5, 0.5);
 	float2 srcPos = srcLocal + EyeOffset;
 	float2 srcUV = srcPos / SrcTexSize;
 	return Source.SampleLevel(LinearSampler, srcUV, 0);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
float4 main(VS_OUTPUT input) : SV_Target
{
// Map display-res pixel position to render-res source position.
// Subtract eye offset, scale to render-res, add eye offset back.
float2 localPos = input.Position.xy - EyeOffset;
float2 srcLocal = localPos * DynResScale;
float2 srcPos = srcLocal + EyeOffset;
float2 srcUV = srcPos / SrcTexSize;
return Source.SampleLevel(LinearSampler, srcUV, 0);
float4 main(VS_OUTPUT input) : SV_Target
{
// Map display-res pixel position to render-res source position.
// Subtract eye offset, scale to render-res, add eye offset back.
float2 localPos = input.Position.xy - EyeOffset;
float2 srcLocal = (localPos - float2(0.5, 0.5)) * DynResScale + float2(0.5, 0.5);
float2 srcPos = srcLocal + EyeOffset;
float2 srcUV = srcPos / SrcTexSize;
return Source.SampleLevel(LinearSampler, srcUV, 0);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@features/Upscaling/Shaders/Upscaling/DLSSCompositePS.hlsl` around lines 28 -
36, The bilinear mapping is using corner coordinates so the upscale is shifted
by half a texel; in main(VS_OUTPUT input) explicitly map pixel centers before
scaling: convert input.Position.xy to center space (subtract 0.5), apply
EyeOffset adjustments and DynResScale, then convert back to texel/UV space (add
0.5) before dividing by SrcTexSize and calling Source.SampleLevel with
LinearSampler; additionally, when sampling a packed side-by-side (SBS) source,
clamp srcPos.x to the active eye region to prevent seam bleed (use
EyeOffset/X-region/active-eye logic around srcPos.x prior to computing srcUV).

Comment on lines +630 to +636
// Hardcoded ~0.75px diagonal jitter for Eye 1 stereo edge supersampling.
// Larger offset increases chance of different alpha test outcomes between eyes
// (tree branches vs sky). NDC for 6304x3088 SBS reference; scales with resolution.
if (a_eyeIndex == 1) {
static const float2 kJitterNDC = float2(1.68e-4, -3.44e-4);
vsout.VRPosition.xy += kJitterNDC * vsout.VRPosition.w;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Keep this out of the shared VR vertex helper.

This adds a permanent right-eye-only screen-space shift to every pass that calls GetVRVSOutput, not just the alpha-tested edge cases mentioned in the comment. The Y component in particular creates binocular vertical disparity across the whole frame, which is likely to show up as blur/eye strain in-headset rather than a localized coverage win. Please move this behind a dedicated opt-in path or restrict it to the specific material/pass you're trying to stabilize.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package/Shaders/Common/VR.hlsli` around lines 630 - 636, The shared VR vertex
helper (GetVRVSOutput / the block using a_eyeIndex) currently applies a
permanent right-eye-only screen-space jitter by adding kJitterNDC to
vsout.VRPosition.xy; remove that global modification and instead expose an
opt-in path: delete or guard the if (a_eyeIndex == 1) { static const float2
kJitterNDC ... vsout.VRPosition.xy += ... } from the shared helper and provide
either a boolean parameter (e.g., bool enableEyeJitter) or a separate
helper/overload that applies the jitter only when explicitly requested by a
material/pass that needs alpha-tested edge stabilization, ensuring other callers
of GetVRVSOutput are unaffected.

Comment on lines +3097 to +3105
// Suppress RGB fringe contamination from negative MIP bias.
// Low-alpha texels near the foliage boundary have bright padding bleeding into RGB.
// Alpha is a direct proxy for contamination — low alpha = more padding contribution.
// Scale correction by bias strength so close-range (no bias) textures are untouched.
if (vrFoliageBias < 0) {
float biasStrength = saturate(vrFoliageBias / min(SharedData::VRMipBias, -0.001));
float fringeScale = 5.0; // higher = more aggressive fringe suppression
baseColor.rgb *= saturate(alpha * lerp(1.0, fringeScale, biasStrength));
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Apply the foliage fringe suppression before lighting.

By this point material.BaseColor, color, and outputAlbedo have already been derived from the uncorrected baseColor. Scaling baseColor.rgb here does not reach psout.Diffuse or psout.Albedo, so the new fringe fix is effectively inert for the final shaded color.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package/Shaders/Lighting.hlsl` around lines 3097 - 3105, The
fringe-suppression block that modifies baseColor.rgb is currently run after
lighting so it never affects material.BaseColor, color, or outputAlbedo (and
thus psout.Diffuse/psout.Albedo); move or apply this correction earlier so the
corrected baseColor feeds into the lighting/albedo path. Specifically, either
relocate the if (vrFoliageBias < 0) { ... } block to immediately after baseColor
is sampled/derived (before material.BaseColor/color/outputAlbedo are computed)
or replace uses of uncorrected baseColor when computing material.BaseColor,
color, and outputAlbedo with the saturated, bias-scaled version so psout.Diffuse
and psout.Albedo use the corrected RGB. Ensure you reference and update
baseColor, material.BaseColor, color, outputAlbedo, psout.Diffuse, and
psout.Albedo accordingly.

Comment thread package/Shaders/Lighting.hlsl Outdated
Comment on lines +3200 to +3205
# if defined(EMAT) && (defined(PARALLAX) || defined(LANDSCAPE))
psout.Reflectance = float4(indirectLobeWeights.specular,
(pixelOffset > 0.0) ? saturate(pixelOffset) : 0.0);
# else
psout.Reflectance = float4(indirectLobeWeights.specular, 0.0);
# endif
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Keep pixelOffset in deferred TRUE_PBR parallax outputs.

PBRParallax still fills pixelOffset above, but this new guard only exports it for PARALLAX and LANDSCAPE. Non-landscape TRUE_PBR permutations therefore always emit 0.0 here, so downstream deferred passes lose the per-pixel height offset.

Suggested fix
-#		if defined(EMAT) && (defined(PARALLAX) || defined(LANDSCAPE))
+#		if defined(EMAT) && (defined(PARALLAX) || defined(LANDSCAPE) || defined(TRUE_PBR))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# if defined(EMAT) && (defined(PARALLAX) || defined(LANDSCAPE))
psout.Reflectance = float4(indirectLobeWeights.specular,
(pixelOffset > 0.0) ? saturate(pixelOffset) : 0.0);
# else
psout.Reflectance = float4(indirectLobeWeights.specular, 0.0);
# endif
# if defined(EMAT) && (defined(PARALLAX) || defined(LANDSCAPE) || defined(TRUE_PBR))
psout.Reflectance = float4(indirectLobeWeights.specular,
(pixelOffset > 0.0) ? saturate(pixelOffset) : 0.0);
# else
psout.Reflectance = float4(indirectLobeWeights.specular, 0.0);
# endif
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package/Shaders/Lighting.hlsl` around lines 3200 - 3205, The code currently
only writes pixelOffset into psout.Reflectance under the EMAT && (PARALLAX ||
LANDSCAPE) guard which causes TRUE_PBR deferred permutations to lose the
per-pixel height; update the conditional so that when PBRParallax has populated
pixelOffset (i.e. TRUE_PBR permutations) it is preserved — for example extend
the preprocessor check to include TRUE_PBR (or remove the PARALLAX-only
restriction) so psout.Reflectance = float4(indirectLobeWeights.specular,
(pixelOffset > 0.0) ? saturate(pixelOffset) : 0.0); is emitted for TRUE_PBR as
well; reference psout.Reflectance, pixelOffset, TRUE_PBR, EMAT, PARALLAX, and
LANDSCAPE to locate the change.

Comment thread package/Shaders/VR/CASCS.hlsl Outdated
Comment on lines +37 to +47
// Fetch 3x3 neighborhood
int2 sp = int2(DTid.xy);
float3 a = Source.Load(int3(sp + int2(-1, -1), 0)).rgb;
float3 b = Source.Load(int3(sp + int2(0, -1), 0)).rgb;
float3 c = Source.Load(int3(sp + int2(1, -1), 0)).rgb;
float3 d = Source.Load(int3(sp + int2(-1, 0), 0)).rgb;
float3 e = Source.Load(int3(sp, 0)).rgb;
float3 f = Source.Load(int3(sp + int2(1, 0), 0)).rgb;
float3 g = Source.Load(int3(sp + int2(-1, 1), 0)).rgb;
float3 h = Source.Load(int3(sp + int2(0, 1), 0)).rgb;
float3 i = Source.Load(int3(sp + int2(1, 1), 0)).rgb;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Clamp the 3x3 neighborhood at the image border.

The first/last row and column currently fetch neighbors outside the source extent, so the sharpen weights for border pixels are built from off-image samples. Clamp sp + offset to [0, texDim - 1] or fall back to a copy on the border.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package/Shaders/VR/CASCS.hlsl` around lines 37 - 47, Border samples are
currently read out of bounds using sp + int2(...) before Source.Load; clamp each
neighbor coordinate to the valid texture extent so edge pixels duplicate/clamp
instead of sampling off-image. Change the nine fetches to compute a clamped
coordinate (e.g. neighbor = clamp(sp + int2(dx,dy), int2(0,0), texSize -
int2(1,1))) and pass that clamped int2 into Source.Load; use DTid/sp to form the
base and the existing Source.Load calls (Source.Load, DTid, sp) to locate and
replace the unsafe offsets. Ensure you obtain the texture dimensions
(texSize/texDim via GetDimensions or the existing size variable) for the clamp
bounds.

Comment on lines 69 to 75
// Use better swap effect to prevent tearing and improve performance
pSwapChainDesc->SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
// But don't change it for VR as it can affect frame pacing with the VR compositor
if (!globals::game::isVR)
pSwapChainDesc->SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;

bool shouldProxy = !globals::game::isVR;
if (shouldProxy)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use REL::Module::IsVR() inside the device-creation hook.

hk_D3D11CreateDeviceAndSwapChainUpscaling() can run before globals::ReInit() populates globals::game::isVR. Using the cached flag here can still send VR startup through the non-VR flip-discard/proxy/interface-wrapping path, which is exactly the judder path these branches are trying to avoid.

Suggested fix
-	// Use better swap effect to prevent tearing and improve performance
-	// But don't change it for VR as it can affect frame pacing with the VR compositor
-	if (!globals::game::isVR)
+	const bool isVR = REL::Module::IsVR();
+
+	// Use better swap effect to prevent tearing and improve performance
+	// But don't change it for VR as it can affect frame pacing with the VR compositor
+	if (!isVR)
 		pSwapChainDesc->SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
 
-	bool shouldProxy = !globals::game::isVR;
+	bool shouldProxy = !isVR;
@@
-		if (!globals::game::isVR) {
+		if (!isVR) {
 			upscaling.UpgradeBackendInterface((void**)&(*ppDevice));
 			upscaling.UpgradeBackendInterface((void**)&(*ppSwapChain));
 		}

Based on learnings: Applies to src/**/*.{h,hpp,cpp} : Ensure code changes work across SE/AE/VR Skyrim variants using runtime detection and REL::RelocateMember() patterns for compatibility

Also applies to: 151-159

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Features/Upscaling.cpp` around lines 69 - 75, The hook
hk_D3D11CreateDeviceAndSwapChainUpscaling() currently reads globals::game::isVR
which may be uninitialized at hook time; replace that cached check with a
runtime VR detection call (use REL::Module::IsVR() or the project's
runtime-detection helper) when deciding to set pSwapChainDesc->SwapEffect and
shouldProxy/interface-wrapping logic, and apply the same change to the other
similar branches (the block around lines referenced in the review) so all
device-creation code uses runtime detection instead of globals::game::isVR to
handle SE/AE/VR variants safely.

Comment on lines +610 to +624
// Periphery TAA diagnostic
if (TAAReorder::g_diagCounter == 0 && viewportScaling && upscaling.settings.vrPeripheryTAA) {
logger::info("[TAAReorder] Periphery TAA: vrTAAdPerEye[0]={}, g_initialized={} (TAA injected at display RT level)",
(void*)upscaling.vrTAAdPerEye[0].get(), TAAReorder::g_initialized);
}

uint32_t eyeCount = eye0Only ? 1 : 2;
for (uint32_t i = 0; i < eyeCount; ++i) {
sl::ViewportHandle vp = (i == 1) ? viewportRight : viewport;
sl::Extent extentIn{ 0, 0, eyeWidthIn, eyeHeightIn };
sl::Extent extentOut{ 0, 0, eyeWidthOut, eyeHeightOut };

if (viewportScaling) {
// Pre-fill composition target with bilinear upscale of full render-res eye.
// DLSS output is pasted on top in FinalizePerEyeOutputs.
upscaling.FillPeriphery(i, eyeWidthIn, eyeHeightIn, eyeWidthOut, eyeHeightOut);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Pass the TAA periphery SRV into FillPeriphery() when that mode is enabled.

This branch only logs vrTAAdPerEye; the actual fill still defaults to vrIntermediateColorIn, so the periphery continues to come from the pre-TAA DLSS input. As written, vrPeripheryTAA doesn't change the composite source.

Suggested fix
 		for (uint32_t i = 0; i < eyeCount; ++i) {
 			sl::ViewportHandle vp = (i == 1) ? viewportRight : viewport;
 
 			if (viewportScaling) {
+				ID3D11ShaderResourceView* peripherySRV = nullptr;
+				uint32_t peripherySrcWidth = eyeWidthIn;
+				uint32_t peripherySrcHeight = eyeHeightIn;
+
+				if (upscaling.settings.vrPeripheryTAA && upscaling.vrTAAdPerEye[i]) {
+					peripherySRV = upscaling.vrTAAdPerEye[i]->srv.get();
+					peripherySrcWidth = eyeWidthOut;
+					peripherySrcHeight = eyeHeightOut;
+				}
+
 				// Pre-fill composition target with bilinear upscale of full render-res eye.
 				// DLSS output is pasted on top in FinalizePerEyeOutputs.
-				upscaling.FillPeriphery(i, eyeWidthIn, eyeHeightIn, eyeWidthOut, eyeHeightOut);
+				upscaling.FillPeriphery(i, peripherySrcWidth, peripherySrcHeight, eyeWidthOut, eyeHeightOut, peripherySRV);
 			}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Features/Upscaling/Streamline.cpp` around lines 610 - 624, The log branch
detects periphery TAA via upscaling.settings.vrPeripheryTAA but FillPeriphery
still uses the default/previous source (vrIntermediateColorIn), so periphery is
not coming from the TAA SRV; update the call to upscaling.FillPeriphery(...) in
the viewportScaling branch to pass the TAA per-eye SRV
(upscaling.vrTAAdPerEye[i]) when upscaling.settings.vrPeripheryTAA is true
(otherwise pass the existing/default SRV), ensuring FillPeriphery receives the
correct source; reference FillPeriphery and upscaling.vrTAAdPerEye to locate the
change and add a conditional argument or overload as needed.

Comment on lines +44 to +89
void VRStereoOptimizations::LoadSettings(json& o_json)
{
if (o_json.contains("StereoMode"))
settings.stereoMode = o_json["StereoMode"].get<StereoMode>();
if (o_json.contains("DisocclusionDepthThreshold"))
settings.disocclusionDepthThreshold = o_json["DisocclusionDepthThreshold"].get<float>();
if (o_json.contains("QualityJitterOffset"))
settings.qualityJitterOffset = o_json["QualityJitterOffset"].get<float>();
if (o_json.contains("FoveatedRegionRadius"))
settings.foveatedRegionRadius = o_json["FoveatedRegionRadius"].get<float>();
if (o_json.contains("FoveatedRegionCenterX"))
settings.foveatedRegionCenterX = o_json["FoveatedRegionCenterX"].get<float>();
if (o_json.contains("FoveatedRegionCenterY"))
settings.foveatedRegionCenterY = o_json["FoveatedRegionCenterY"].get<float>();
if (o_json.contains("UseEyeTracking"))
settings.useEyeTracking = o_json["UseEyeTracking"].get<bool>();
if (o_json.contains("DebugVisualization"))
settings.debugVisualization = o_json["DebugVisualization"].get<bool>();
if (o_json.contains("DebugSkipMerge"))
settings.debugSkipMerge = o_json["DebugSkipMerge"].get<bool>();
if (o_json.contains("DebugForceAllStencil"))
settings.debugForceAllStencil = o_json["DebugForceAllStencil"].get<bool>();
if (o_json.contains("DebugForceAllReprojectCS"))
settings.debugForceAllReprojectCS = o_json["DebugForceAllReprojectCS"].get<bool>();
if (o_json.contains("DebugDepthMap"))
settings.debugDepthMap = o_json["DebugDepthMap"].get<bool>();
if (o_json.contains("FullBlendDistance"))
settings.fullBlendDistance = o_json["FullBlendDistance"].get<float>();
if (o_json.contains("MipBiasMode"))
settings.mipBiasMode = o_json["MipBiasMode"].get<int>();
// Backwards compat: old bool EnableMipBias -> mode 2 (Distant Trees)
else if (o_json.contains("EnableMipBias") && o_json["EnableMipBias"].get<bool>())
settings.mipBiasMode = 2;
if (o_json.contains("MipLodBias"))
settings.mipLodBias = o_json["MipLodBias"].get<float>();
if (o_json.contains("MipBiasNearDist"))
settings.mipBiasNearDist = o_json["MipBiasNearDist"].get<float>();
if (o_json.contains("MipBiasFarDist"))
settings.mipBiasFarDist = o_json["MipBiasFarDist"].get<float>();
// CAS disabled for now — ignore saved value
// if (o_json.contains("CASStrength"))
// settings.casStrength = o_json["CASStrength"].get<float>();
settings.casStrength = 0.0f;
if (o_json.contains("AlphaTestThreshold"))
settings.alphaTestThreshold = o_json["AlphaTestThreshold"].get<float>();
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Harden the JSON load path for the new settings.

These branches only check contains(). A wrong JSON type here will throw on get<>() and fail the whole feature load from State::Load(), and the new numeric fields are currently accepted out of range.

Suggested hardening
-	if (o_json.contains("DisocclusionDepthThreshold"))
-		settings.disocclusionDepthThreshold = o_json["DisocclusionDepthThreshold"].get<float>();
+	if (auto it = o_json.find("DisocclusionDepthThreshold"); it != o_json.end() && it->is_number())
+		settings.disocclusionDepthThreshold = std::clamp(it->get<float>(), 0.001f, 0.1f);
@@
-	if (o_json.contains("MipBiasMode"))
-		settings.mipBiasMode = o_json["MipBiasMode"].get<int>();
+	if (auto it = o_json.find("MipBiasMode"); it != o_json.end() && it->is_number_integer())
+		settings.mipBiasMode = std::clamp(it->get<int>(), 0, 2);
@@
-	if (o_json.contains("AlphaTestThreshold"))
-		settings.alphaTestThreshold = o_json["AlphaTestThreshold"].get<float>();
+	if (auto it = o_json.find("AlphaTestThreshold"); it != o_json.end() && it->is_number())
+		settings.alphaTestThreshold = std::clamp(it->get<float>(), 0.0f, 1.0f);
+
+	settings.mipBiasNearDist = std::max(settings.mipBiasNearDist, 0.0f);
+	settings.mipBiasFarDist = std::max(settings.mipBiasFarDist, settings.mipBiasNearDist);

As per coding guidelines: Include proper resource management and graceful degradation for DirectX 11 resources and user input validation to prevent crashes from malformed configurations

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Features/VRStereoOptimizations.cpp` around lines 44 - 89, In
VRStereoOptimizations::LoadSettings the code calls o_json["..."].get<>() after
only contains(), which can throw on wrong types and accepts out-of-range values;
update each branch (StereoMode, DisocclusionDepthThreshold, QualityJitterOffset,
FoveatedRegionRadius, FoveatedRegionCenterX/Y, UseEyeTracking, Debug*,
FullBlendDistance, MipBiasMode, MipLodBias, MipBiasNearDist, MipBiasFarDist,
AlphaTestThreshold, etc.) to first check the JSON type (e.g.,
is_number()/is_boolean()/is_string()), then read and validate/clamp the value to
acceptable ranges (e.g., centers 0–1, radii >=0, mipBiasMode within allowed enum
set, thresholds within sensible bounds); on invalid type or out-of-range value,
fall back to the existing settings.* defaults (or clamp) and emit a warning/log
entry instead of allowing an exception so State::Load() degrades gracefully.
Ensure CASStrength remains forced to 0.0f as before.

Comment on lines +173 to +182
auto& depthData = renderer->GetDepthStencilData().depthStencils[RE::RENDER_TARGETS_DEPTHSTENCIL::kMAIN];
if (depthData.views[0] && depthData.texture) {
D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc{};
depthData.views[0]->GetDesc(&dsvDesc);
dsvDesc.Flags = D3D11_DSV_READ_ONLY_DEPTH;

DX::ThrowIfFailed(device->CreateDepthStencilView(depthData.texture, &dsvDesc, stencilWriteReadOnlyDSV.put()));
} else {
logger::warn("[VRStereoOptimizations] Could not create read-only DSV: depth stencil data not available");
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't arm stencil culling when the stencil-write DSV is unavailable.

SetupResources() already logs that stencilWriteReadOnlyDSV can be missing. DispatchStencil() still proceeds and sets stencilActive = true, so the later hook starts injecting NOT_EQUAL stencil tests against a buffer that never received this frame's marks.

Suggested guard
-	if (!stencilCS || !stencilWriteVS || !stencilWritePS || !texPerPixelMode || !paramsCB)
+	if (!stencilCS || !stencilWriteVS || !stencilWritePS || !texPerPixelMode || !paramsCB ||
+	    !stencilWriteReadOnlyDSV || !stencilWriteDSS || !stencilWriteRS)
 		return;

As per coding guidelines: Include proper resource management and graceful degradation for DirectX 11 resources and user input validation to prevent crashes from malformed configurations

Also applies to: 400-407, 452-456

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Features/VRStereoOptimizations.cpp` around lines 173 - 182, The code arms
stencil culling even when the stencil-write DSV is missing; modify
DispatchStencil() to check the actual resource (stencilWriteReadOnlyDSV or the
created DSV handle from SetupResources/CreateDepthStencilView) before setting
stencilActive = true and before writing NOT_EQUAL marks, and if the DSV is
absent skip arming and log a warning; also ensure the later hook that injects
NOT_EQUAL stencil tests verifies stencilActive (or the DSV presence) before
applying tests to avoid using an uninitialized stencil buffer.

Comment thread src/State.h
Comment on lines +213 to +218
float VRMipBias;
float VRMipBiasNearDist;
float VRMipBiasFarDist;
uint VRMipBiasMode; // 0=Off, 1=All Textures, 2=Distant Trees only
float VRAlphaTestThreshold; // Alpha test threshold for VR TREE_ANIM (0 = use vanilla)
float4 pad0; // HLSL: float2 + implicit 8-byte gap before float4 AmbientSHR
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: C++ and HLSL padding mismatch will cause data corruption.

C++ declares float4 pad0 (16 bytes), but HLSL declares float2 pad0 (8 bytes). This mismatch will cause AmbientSHR, AmbientSHG, and AmbientSHB to be read from incorrect offsets in shaders, resulting in corrupted ambient lighting.

The comment mentions "implicit 8-byte gap" but HLSL cbuffer packing does not automatically insert gaps—it packs sequentially within 16-byte boundaries.

🐛 Fix: Make C++ and HLSL padding match

Either change C++ to match HLSL:

- float4 pad0;                 // HLSL: float2 + implicit 8-byte gap before float4 AmbientSHR
+ float2 pad0;                 // Must match HLSL
+ float2 _pad1;                // Explicit padding to align AmbientSHR

Or change HLSL to match C++:

- float2 pad0;
+ float4 pad0;

The total padding before AmbientSHR must be identical in both C++ and HLSL (likely needs to be 8 or 12 bytes depending on current offset).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/State.h` around lines 213 - 218, The struct has a packing mismatch: C++
declares float4 pad0 while HLSL uses float2 pad0 causing
AmbientSHR/AmbientSHG/AmbientSHB offsets to diverge; fix by making the padding
identical in both languages—either change the C++ pad0 to float2 to match HLSL
or change the HLSL pad0 to float4 to match C++; then update the inline comment
near
VRMipBias/VRMipBiasNearDist/VRMipBiasFarDist/VRMipBiasMode/VRAlphaTestThreshold/pad0
to reflect the exact bytes of padding and verify the total bytes before
AmbientSHR are the same in C++ and HLSL.

@alandtse
Copy link
Copy Markdown
Collaborator

Taking to draft until #1982 is merged.

@alandtse alandtse marked this pull request as draft March 16, 2026 06:55
vrnord and others added 5 commits March 16, 2026 10:33
…hanges

Review fixes:
- DeferredCompositeCS: only early-out MODE_MAIN (mode==2), not MODE_EDGE
- CASCS: add max(mxRGB, 1e-4) guard against division by zero
- StereoBlendCS: clamp POM-corrected depth to prevent div-by-zero
- StencilCS: use cbuffer DisocclusionThreshold instead of hardcoded constant
- VR.hlsli: make Eye 1 jitter resolution-adaptive via FrameBuffer::BufferDim
- Lighting.hlsl: include TRUE_PBR in pixelOffset export guard
- ExtendedMaterials.hlsli: saturate() clamp on pixelOffset output
- VRStereoOptimizations.cpp: add DeactivateStencil() to early-exit paths
- VRStereoOptimizations.cpp: add null check for depthSRV
- Globals.cpp: scope ClearDepthStencilView hook to main scene DSV only
- StereoBlend.cpp: use terrain-aware Util::GetCurrentSceneDepthSRV()

Revert Upscaling.cpp/h to upstream dev — all DLSS viewport scaling,
TAAReorder, and periphery TAA code belongs exclusively in PR2.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add HLSL shaders for the VR DLSS periphery pipeline:

- ClearHMDMaskCS: Enhanced with fallback texture support and
  configurable depth/color region offsets for viewport scaling
- DLSSCompositePS: Format-converting fullscreen copy (point-sample)
  for pasting DLSS output onto the submit texture
- DepthUpscalePS: Depth buffer upscaling with point sampling to
  preserve depth discontinuities at object edges
- FeatheredCompositeCS/PS: Feathered alpha blend at DLSS crop boundary
  using smoothstep falloff (CS fallback + PS with hardware blend)
- ForceAlphaCS: Sets alpha=1.0 on submit texture to fix Scaleform UI
  rendering after DLSS writes non-opaque alpha values
- VRPeripheryFillCS: Bilinear upscale from render-res to display-res
  for the peripheral region outside the DLSS crop

Based in part on techniques from PureDark's Skyrim-Upscaler VR
(MIT license: https://github.com/PureDark/Skyrim-Upscaler)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…posite

Implement DLSS viewport scaling with configurable crop region:

- Add vrDlssCropOffsetX setting (0.0-0.3) to shift DLSS crop toward
  the nasal edge where visual acuity is highest
- Add vrDlssFeatherWidth setting for smooth blend at crop boundary
  (currently disabled pending fix, default 0.0)
- Implement feathered composite via pixel shader with SrcAlpha blend
  (preserves periphery TAA quality) with CS fallback path
- Create RTV on vrFinalOutput for PS-based compositing
- Fix stencil clear value (0xFF → 0x00) for VR depth upscale path
- Add eye0Only parameter to FinalizePerEyeOutputs for single-eye mode
- Clear stencil marks before periphery TAA to prevent interference
- Set TAA high-frequency response (fTAAHighFreq=1.0) during periphery
  pass for improved sharpness

Based in part on techniques from PureDark's Skyrim-Upscaler VR
(MIT license: https://github.com/PureDark/Skyrim-Upscaler)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Refactor Streamline DLSS to support per-eye evaluation in VR:

- Split CheckFrameConstants into per-eye loop with viewport[i] offsets
- Add crop-aware subrect calculation when viewport scaling is active
- Compute cameraPinholeOffset for asymmetric crop (nasal offset)
- Support per-eye intermediate textures for color, depth, motion
  vectors, reactive mask, and transparency mask inputs
- Add colorSourceOverride parameter for TAAReorder post-conductor flow
- Fix jitter scaling to use crop dimensions when viewport scaling

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement periphery TAA for VR DLSS viewport scaling by hooking into
the BSImagespaceShader render pipeline at the post-conductor level.

When DLSS processes only a center crop of each eye, the peripheral
region needs temporal anti-aliasing from the native TAA pass. This
module reorders the pipeline so TAA runs on the full frame while DLSS
processes the crop, then composites the DLSS center onto the TAA'd
periphery.

Hook architecture (all RVAs are SkyrimVR.exe):
- BSImagespaceShaderHook (0x132C827): wraps conductor + Phase 5 TAA,
  evaluates DLSS on Phase 2A output copy after func() returns
- ExecutePassHook (0x012D2540): captures Phase 2A output to
  g_postPPCopy for DLSS color source
- ConductorCallHook (0x1325086): tracks conductor state
- ForceTAASetter/TAAStateMachine: pass-through TAA control hooks
- DepthStencilRegHook (0x00DC79D0): diagnostic logging for depth
  target registration
- SubmitHook (0x00C53920): diagnostic logging for VR frame submission

Based on PureDark's BSImagespaceShader_Hook_VR approach from
Skyrim-Upscaler VR (MIT license:
https://github.com/PureDark/Skyrim-Upscaler).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vrnord vrnord force-pushed the vr-dlss-periphery-taa branch from 3a155ea to 78e003c Compare March 16, 2026 16:33
pre-commit-ci Bot and others added 3 commits March 16, 2026 16:34
Automated formatting by clang-format, prettier, and other hooks.
See https://pre-commit.ci for details.
Screen Space Shadows: reduced sample count for right eye when
VRStereoOptimizations is active. StereoSync toggle exposed in UI.

SSGI: bypass stereoSync when VRStereoOptimizations is active to prevent
eye 0 contamination from sparse eye 1 data. Mode texture early-out
in compute passes for culled eye 1 pixels.

Remove dead CASCS.hlsl (CAS feature fully removed).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Automated formatting by clang-format, prettier, and other hooks.
See https://pre-commit.ci for details.
vrnord added a commit to vrnord/skyrim-community-shaders-Stereo-Optimizations that referenced this pull request Mar 22, 2026
Process a configurable central crop through DLSS, reducing GPU cost.
Periphery filled with bilinear upscale or TAA'd content via conductor.
Feathered composite at crop boundary hides quality transition.
Nasal crop offset shifts DLSS region toward nose for higher acuity.

Key components:
- Per-eye DLSS evaluation with viewport-scaled projection matrices
- ClearHMDMask zeroes hidden area mesh pixels before DLSS
- VRPeripheryFillCS bilinear upscales render-res to display-res
- FeatheredCompositePS alpha-blends DLSS crop onto periphery
- TAAReorder conductor architecture for periphery TAA
- REL::Module::IsVR() in device-creation hook (timing fix)
- Dead code removed (vrTAAdPerEye, vrPreTAACopy, BILINEAR_UPSCALE)

Based on PureDark's Skyrim-Upscaler VR conductor architecture (MIT).
Depends on: feat(vr): VR stereo reprojection optimizations
Replaces PR community-shaders#1983.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vrnord added a commit to vrnord/skyrim-community-shaders-Stereo-Optimizations that referenced this pull request Mar 23, 2026
Process a configurable central crop through DLSS, reducing GPU cost.
Periphery filled with bilinear upscale or TAA'd content via conductor.
Feathered composite at crop boundary hides quality transition.
Nasal crop offset shifts DLSS region toward nose for higher acuity.

Key components:
- Per-eye DLSS evaluation with viewport-scaled projection matrices
- ClearHMDMask zeroes hidden area mesh pixels before DLSS
- VRPeripheryFillCS bilinear upscales render-res to display-res
- FeatheredCompositePS alpha-blends DLSS crop onto periphery
- TAAReorder conductor architecture for periphery TAA
- REL::Module::IsVR() in device-creation hook (timing fix)
- Dead code removed (vrTAAdPerEye, vrPreTAACopy, BILINEAR_UPSCALE)

Based on PureDark's Skyrim-Upscaler VR conductor architecture (MIT).
Depends on: feat(vr): VR stereo reprojection optimizations
Replaces PR community-shaders#1983.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vrnord
Copy link
Copy Markdown
Contributor Author

vrnord commented Mar 23, 2026

Superseded by #2003 (VR DLSS viewport scaling with periphery TAA).
Depends on #2002 (core stereo optimizations).

All review feedback addressed in the split PRs.

@vrnord vrnord closed this Mar 23, 2026
vrnord added a commit to vrnord/skyrim-community-shaders-Stereo-Optimizations that referenced this pull request Mar 23, 2026
Process a configurable central crop through DLSS, reducing GPU cost.
Periphery filled with bilinear upscale or TAA'd content via conductor.
Feathered composite at crop boundary hides quality transition.
Nasal crop offset shifts DLSS region toward nose for higher acuity.

Key components:
- Per-eye DLSS evaluation with viewport-scaled projection matrices
- ClearHMDMask zeroes hidden area mesh pixels before DLSS
- VRPeripheryFillCS bilinear upscales render-res to display-res
- FeatheredCompositePS alpha-blends DLSS crop onto periphery
- TAAReorder conductor architecture for periphery TAA
- REL::Module::IsVR() in device-creation hook (timing fix)
- Bounds guards in ClearHMDMaskCS and ForceAlphaCS
- Per-frame flag resets in TAAReorder
- Complete pipeline state save/restore in BSImagespaceShaderHook
- Crop parameter clamping before Streamline constants

Based on PureDark's Skyrim-Upscaler VR conductor architecture (MIT).
Depends on: feat(vr): VR stereo reprojection optimizations
Replaces PR community-shaders#1983.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vrnord added a commit to vrnord/skyrim-community-shaders-Stereo-Optimizations that referenced this pull request Mar 23, 2026
Process a configurable central crop through DLSS, reducing GPU cost.
Periphery filled with bilinear upscale or TAA'd content via conductor.
Feathered composite at crop boundary hides quality transition.
Nasal crop offset shifts DLSS region toward nose for higher acuity.

Key components:
- Per-eye DLSS evaluation with viewport-scaled projection matrices
- ClearHMDMask zeroes hidden area mesh pixels before DLSS
- VRPeripheryFillCS bilinear upscales render-res to display-res
- FeatheredCompositePS alpha-blends DLSS crop onto periphery
- TAAReorder conductor architecture for periphery TAA
- REL::Module::IsVR() in device-creation hook (timing fix)
- Bounds guards in ClearHMDMaskCS and ForceAlphaCS
- Per-frame flag resets in TAAReorder
- Complete pipeline state save/restore in BSImagespaceShaderHook
- Crop parameter clamping before Streamline constants

Based on PureDark's Skyrim-Upscaler VR conductor architecture (MIT).
Depends on: feat(vr): VR stereo reprojection optimizations
Replaces PR community-shaders#1983.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vrnord added a commit to vrnord/skyrim-community-shaders-Stereo-Optimizations that referenced this pull request Mar 23, 2026
Process a configurable central crop through DLSS, reducing GPU cost.
Periphery filled with bilinear upscale or TAA'd content via conductor.
Feathered composite at crop boundary hides quality transition.
Nasal crop offset shifts DLSS region toward nose for higher acuity.

Key components:
- Per-eye DLSS evaluation with viewport-scaled projection matrices
- ClearHMDMask zeroes hidden area mesh pixels before DLSS
- VRPeripheryFillCS bilinear upscales render-res to display-res
- FeatheredCompositePS alpha-blends DLSS crop onto periphery
- TAAReorder conductor architecture for periphery TAA
- REL::Module::IsVR() in device-creation hook (timing fix)
- Bounds guards in ClearHMDMaskCS and ForceAlphaCS
- Per-frame flag resets in TAAReorder
- Complete pipeline state save/restore in BSImagespaceShaderHook
- Crop parameter clamping before Streamline constants

Based on PureDark's Skyrim-Upscaler VR conductor architecture (MIT).
Depends on: feat(vr): VR stereo reprojection optimizations
Replaces PR community-shaders#1983.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vrnord added a commit to vrnord/skyrim-community-shaders-Stereo-Optimizations that referenced this pull request Mar 23, 2026
Process a configurable central crop through DLSS, reducing GPU cost.
Periphery filled with bilinear upscale or TAA'd content via conductor.
Feathered composite at crop boundary hides quality transition.
Nasal crop offset shifts DLSS region toward nose for higher acuity.

Key components:
- Per-eye DLSS evaluation with viewport-scaled projection matrices
- ClearHMDMask zeroes hidden area mesh pixels before DLSS
- VRPeripheryFillCS bilinear upscales render-res to display-res
- FeatheredCompositePS alpha-blends DLSS crop onto periphery
- TAAReorder conductor architecture for periphery TAA
- REL::Module::IsVR() in device-creation hook (timing fix)
- Bounds guards in ClearHMDMaskCS and ForceAlphaCS
- Per-frame flag resets in TAAReorder
- Complete pipeline state save/restore in BSImagespaceShaderHook
- Crop parameter clamping before Streamline constants

Based on PureDark's Skyrim-Upscaler VR conductor architecture (MIT).
Depends on: feat(vr): VR stereo reprojection optimizations
Replaces PR community-shaders#1983.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vrnord added a commit to vrnord/skyrim-community-shaders-Stereo-Optimizations that referenced this pull request Mar 23, 2026
Process a configurable central crop through DLSS, reducing GPU cost.
Periphery filled with bilinear upscale or TAA'd content via conductor.
Feathered composite at crop boundary hides quality transition.
Nasal crop offset shifts DLSS region toward nose for higher acuity.

Key components:
- Per-eye DLSS evaluation with viewport-scaled projection matrices
- ClearHMDMask zeroes hidden area mesh pixels before DLSS
- VRPeripheryFillCS bilinear upscales render-res to display-res
- FeatheredCompositePS alpha-blends DLSS crop onto periphery
- TAAReorder conductor architecture for periphery TAA
- REL::Module::IsVR() in device-creation hook (timing fix)
- Bounds guards in ClearHMDMaskCS and ForceAlphaCS
- Per-frame flag resets in TAAReorder
- Complete pipeline state save/restore in BSImagespaceShaderHook
- Crop parameter clamping before Streamline constants

Based on PureDark's Skyrim-Upscaler VR conductor architecture (MIT).
Depends on: feat(vr): VR stereo reprojection optimizations
Replaces PR community-shaders#1983.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vrnord added a commit to vrnord/skyrim-community-shaders-Stereo-Optimizations that referenced this pull request Mar 24, 2026
Process a configurable central crop through DLSS, reducing GPU cost.
Periphery filled with bilinear upscale or TAA'd content via conductor.
Feathered composite at crop boundary hides quality transition.
Nasal crop offset shifts DLSS region toward nose for higher acuity.

Key components:
- Per-eye DLSS evaluation with viewport-scaled projection matrices
- ClearHMDMask zeroes hidden area mesh pixels before DLSS
- VRPeripheryFillCS bilinear upscales render-res to display-res
- FeatheredCompositePS alpha-blends DLSS crop onto periphery
- TAAReorder conductor architecture for periphery TAA
- REL::Module::IsVR() in device-creation hook (timing fix)
- Bounds guards in ClearHMDMaskCS and ForceAlphaCS
- Per-frame flag resets in TAAReorder
- Complete pipeline state save/restore in BSImagespaceShaderHook
- Crop parameter clamping before Streamline constants

Based on PureDark's Skyrim-Upscaler VR conductor architecture (MIT).
Depends on: feat(vr): VR stereo reprojection optimizations
Replaces PR community-shaders#1983.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vrnord added a commit to vrnord/skyrim-community-shaders-Stereo-Optimizations that referenced this pull request Mar 24, 2026
Process a configurable central crop through DLSS, reducing GPU cost.
Periphery filled with bilinear upscale or TAA'd content via conductor.
Feathered composite at crop boundary hides quality transition.
Nasal crop offset shifts DLSS region toward nose for higher acuity.

Key components:
- Per-eye DLSS evaluation with viewport-scaled projection matrices
- ClearHMDMask zeroes hidden area mesh pixels before DLSS
- VRPeripheryFillCS bilinear upscales render-res to display-res
- FeatheredCompositePS alpha-blends DLSS crop onto periphery
- TAAReorder conductor architecture for periphery TAA
- REL::Module::IsVR() in device-creation hook (timing fix)
- Bounds guards in ClearHMDMaskCS and ForceAlphaCS
- Per-frame flag resets in TAAReorder
- Complete pipeline state save/restore in BSImagespaceShaderHook
- Crop parameter clamping before Streamline constants

Based on PureDark's Skyrim-Upscaler VR conductor architecture (MIT).
Depends on: feat(vr): VR stereo reprojection optimizations
Replaces PR community-shaders#1983.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vrnord added a commit to vrnord/skyrim-community-shaders-Stereo-Optimizations that referenced this pull request Mar 24, 2026
Process a configurable central crop through DLSS, reducing GPU cost.
Periphery filled with bilinear upscale or TAA'd content via conductor.
Feathered composite at crop boundary hides quality transition.
Nasal crop offset shifts DLSS region toward nose for higher acuity.

Key components:
- Per-eye DLSS evaluation with viewport-scaled projection matrices
- ClearHMDMask zeroes hidden area mesh pixels before DLSS
- VRPeripheryFillCS bilinear upscales render-res to display-res
- FeatheredCompositePS alpha-blends DLSS crop onto periphery
- TAAReorder conductor architecture for periphery TAA
- REL::Module::IsVR() in device-creation hook (timing fix)
- Bounds guards in ClearHMDMaskCS and ForceAlphaCS
- Per-frame flag resets in TAAReorder
- Complete pipeline state save/restore in BSImagespaceShaderHook
- Crop parameter clamping before Streamline constants

Based on PureDark's Skyrim-Upscaler VR conductor architecture (MIT).
Depends on: feat(vr): VR stereo reprojection optimizations
Replaces PR community-shaders#1983.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vrnord added a commit to vrnord/skyrim-community-shaders-Stereo-Optimizations that referenced this pull request Mar 25, 2026
Process a configurable central crop through DLSS, reducing GPU cost.
Periphery filled with bilinear upscale or TAA'd content via conductor.
Feathered composite at crop boundary hides quality transition.
Nasal crop offset shifts DLSS region toward nose for higher acuity.

Key components:
- Per-eye DLSS evaluation with viewport-scaled projection matrices
- ClearHMDMask zeroes hidden area mesh pixels before DLSS
- VRPeripheryFillCS bilinear upscales render-res to display-res
- FeatheredCompositePS alpha-blends DLSS crop onto periphery
- TAAReorder conductor architecture for periphery TAA
- REL::Module::IsVR() in device-creation hook (timing fix)
- Bounds guards in ClearHMDMaskCS and ForceAlphaCS
- Per-frame flag resets in TAAReorder
- Complete pipeline state save/restore in BSImagespaceShaderHook
- Crop parameter clamping before Streamline constants

Based on PureDark's Skyrim-Upscaler VR conductor architecture (MIT).
Depends on: feat(vr): VR stereo reprojection optimizations
Replaces PR community-shaders#1983.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vrnord added a commit to vrnord/skyrim-community-shaders-Stereo-Optimizations that referenced this pull request Mar 25, 2026
Process a configurable central crop through DLSS, reducing GPU cost.
Periphery filled with bilinear upscale or TAA'd content via conductor.
Feathered composite at crop boundary hides quality transition.
Nasal crop offset shifts DLSS region toward nose for higher acuity.

Key components:
- Per-eye DLSS evaluation with viewport-scaled projection matrices
- ClearHMDMask zeroes hidden area mesh pixels before DLSS
- VRPeripheryFillCS bilinear upscales render-res to display-res
- FeatheredCompositePS alpha-blends DLSS crop onto periphery
- TAAReorder conductor architecture for periphery TAA
- REL::Module::IsVR() in device-creation hook (timing fix)
- Bounds guards in ClearHMDMaskCS and ForceAlphaCS
- Per-frame flag resets in TAAReorder
- Complete pipeline state save/restore in BSImagespaceShaderHook
- Crop parameter clamping before Streamline constants

Based on PureDark's Skyrim-Upscaler VR conductor architecture (MIT).
Depends on: feat(vr): VR stereo reprojection optimizations
Replaces PR community-shaders#1983.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vrnord added a commit to vrnord/skyrim-community-shaders-Stereo-Optimizations that referenced this pull request Mar 25, 2026
Process a configurable central crop through DLSS, reducing GPU cost.
Periphery filled with bilinear upscale or TAA'd content via conductor.
Feathered composite at crop boundary hides quality transition.
Nasal crop offset shifts DLSS region toward nose for higher acuity.

Key components:
- Per-eye DLSS evaluation with viewport-scaled projection matrices
- ClearHMDMask zeroes hidden area mesh pixels before DLSS
- VRPeripheryFillCS bilinear upscales render-res to display-res
- FeatheredCompositePS alpha-blends DLSS crop onto periphery
- TAAReorder conductor architecture for periphery TAA
- REL::Module::IsVR() in device-creation hook (timing fix)
- Bounds guards in ClearHMDMaskCS and ForceAlphaCS
- Per-frame flag resets in TAAReorder
- Complete pipeline state save/restore in BSImagespaceShaderHook
- Crop parameter clamping before Streamline constants

Based on PureDark's Skyrim-Upscaler VR conductor architecture (MIT).
Depends on: feat(vr): VR stereo reprojection optimizations
Replaces PR community-shaders#1983.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants