Skip to content

feat(VR): add DeepDVC dynamic vibrance#2091

Open
YtzyFvra wants to merge 5 commits into
community-shaders:devfrom
YtzyFvra:feature/dvc
Open

feat(VR): add DeepDVC dynamic vibrance#2091
YtzyFvra wants to merge 5 commits into
community-shaders:devfrom
YtzyFvra:feature/dvc

Conversation

@YtzyFvra
Copy link
Copy Markdown
Contributor

@YtzyFvra YtzyFvra commented Apr 11, 2026

NVIDIA Deep Learning Dynamic Vibrance Control (DeepDVC) via Streamline SDK.
AI-based post-process that enhances color vibrance and dark-area detail while preventing overexposure.
work after tone mapping.
VR-only for now; flat-screen adaptation is straightforward.

Summary by CodeRabbit

New Features

  • Added DeepDVC display enhancement feature in the Display settings menu. Users can toggle the feature on/off, adjust intensity levels, and control saturation boost with persistent settings. The feature is fully integrated with VR support and includes built-in validation checks.

Copilot AI review requested due to automatic review settings April 11, 2026 13:43
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 11, 2026

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Introduces a new DeepDVC feature with configuration persistence, ImGui settings panel, and Streamline integration. The feature includes VR-specific post-tone-mapping shader hooks, texture processing via proxy buffers, and multiple safety checks for missing dependencies. DeepDVC is registered globally and conditionally available based on VR support and Streamline requirements.

Changes

Cohort / File(s) Summary
Configuration & Registration
features/DeepDVC/Shaders/Features/DeepDVC.ini, src/Feature.cpp, src/Globals.cpp, src/Globals.h
Added new INI config file and registered DeepDVC in feature list and global namespace.
DeepDVC Feature Implementation
src/Features/DeepDVC.h, src/Features/DeepDVC.cpp
Implements core DeepDVC feature with lifecycle management (Load/Save/Restore), ImGui settings panel with mode/intensity/saturation controls, and evaluation logic that manages proxy buffers, Streamline integration, and VR shader hooks.
Streamline Integration
src/Features/Upscaling/Streamline.h, src/Features/Upscaling/Streamline.cpp
Extended Streamline feature list to include sl::kFeatureDeepDVC, added feature-detection logic in CheckFeatures(), and bound the DeepDVC options-setter function-pointer in PostDevice().

Sequence Diagram(s)

sequenceDiagram
    participant Game as Game Engine
    participant DVC as DeepDVC Feature
    participant SL as Streamline
    participant D3D as D3D11 Device

    rect rgb(100, 150, 200, 0.5)
    Note over Game,D3D: DeepDVC Evaluate Flow
    Game->>DVC: Evaluate() called
    DVC->>DVC: Check prerequisites<br/>(VR, Streamline, D3D context)
    alt Prerequisites met
        DVC->>DVC: Create/update proxyBuffer UAV
        DVC->>D3D: Copy source texture → proxyBuffer
        DVC->>SL: Register scaling tags
        DVC->>SL: slDeepDVCSetOptions(options)
        DVC->>SL: slEvaluateFeature(kFeatureDeepDVC)
        DVC->>D3D: Copy proxyBuffer → target texture
    else Prerequisites missing
        DVC->>DVC: Log warning, early exit
    end
    end

    rect rgb(150, 100, 200, 0.5)
    Note over Game,D3D: Shader Hook Installation
    Game->>DVC: PostPostLoad() called
    alt Running in VR
        DVC->>DVC: Install vtable hooks<br/>on tone-mapping shaders
        DVC->>DVC: Hooks call Evaluate()
        DVC->>DVC: Log installation complete
    end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • PR #1507: Modifies src/Features/Upscaling/Streamline.{cpp,h} to add DeepDVC feature flags, detection, and function bindings—directly related at code level.
  • PR #2098: Adjusts VR-specific image-space shader hook logic; this PR installs DeepDVC post-tone-mapping hooks for VR, so they share VR rendering path modifications.
  • PR #1076: Follows the same pattern of adding a new feature class, updating Feature::GetFeatureList(), and exporting via globals/Globals.h.

Suggested reviewers

  • alandtse
  • doodlum

Poem

🐰 A feature so deep, through Streamline it flows,
Proxy buffers dance where the tone-mapping goes,
VR shaders now hooked in our shader technique,
DeepDVC arrives, bringing visual mystique! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.81% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(VR): add DeepDVC dynamic vibrance' accurately and specifically describes the main change: adding NVIDIA DeepDVC (a dynamic vibrance control feature) for VR support.

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

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

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

❤️ Share

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

@github-actions
Copy link
Copy Markdown

No actionable suggestions for changed features.

Copy link
Copy Markdown
Contributor

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Features/DeepDVC.cpp`:
- Around line 35-38: DeepDVC::LoadSettings currently assigns settings directly;
after deserialization, validate and clamp fields: ensure the "mode" field is
bounded to [0, modes.size()-1] (if missing or invalid, set to a safe default
like 0), clamp all float parameters to their UI slider ranges (match the same
min/max used by the UI code), and sanitize any other enum/int fields to valid
values; if clamping fails or required keys are missing, restore sensible
defaults in settings and avoid passing an out-of-range mode into the evaluation
path or indexing modes[] so the renderer and evaluation functions use only
validated values.
- Around line 162-176: slEvaluateFeature may be missing or can fail, and in
those cases proxyBuffer is unprocessed and must not overwrite targetBuffer;
change the logic around upscaling.streamline.slEvaluateFeature / sl::Result res
to track success (e.g., bool evalSucceeded = false), set evalSucceeded = true
only when slEvaluateFeature is present and returns sl::Result::eOk, and then
only perform context->CopyResource(targetBuffer, proxyBuffer) when evalSucceeded
&& proxyBuffer && targetBuffer; reference
upscaling.streamline.slEvaluateFeature, sl::Result res, frameToken, inputsEval,
context, proxyBuffer and targetBuffer to locate and update the code.
- Around line 114-121: The proxy recreation check only compares
Width/Height/Format; update the validation in the block that calls
proxyBuffer->GetDesc(&proxyDesc) to compare all Texture2D shape fields
(ArraySize, MipLevels, SampleDesc.Count, SampleDesc.Quality in addition to
Width, Height, Format) against the current desc and release/reset proxyBuffer if
any differ so that CopyResource (used later) always has identical resource
shapes; reference the D3D11_TEXTURE2D_DESC from GetDesc, the local desc
variable, proxyBuffer, and the CopyResource call sites when making the change.

In `@src/Features/Upscaling/Streamline.cpp`:
- Around line 249-262: The DeepDVC feature handling must mirror the Reflex/PCL
pattern: replace direct slIsFeatureLoaded()/slGetFeatureRequirements() usage
with a call to checkFeatureAvailability(sl::kFeatureDeepDVC, featureDeepDVC,
adapterInfo) to validate loading and support, then bind function pointers via
bindFeatureFn(sl::kFeatureDeepDVC, /*target function pointers*/ ) and if
bindFeatureFn or checkFeatureAvailability returns failure clear featureDeepDVC
to false; specifically update the code paths around slIsFeatureLoaded,
slGetFeatureFunction and featureDeepDVC so that any failed availability check or
failed slGetFeatureFunction binding will set featureDeepDVC = false and log the
error consistently (use the existing checkFeatureAvailability and bindFeatureFn
helpers and the featureDeepDVC flag).

In `@src/Hooks.cpp`:
- Around line 234-242: The shared relocation "func" in
BSImagespaceShaderHDRTonemapBlendCinematic_Render means the same write_vfunc
hook type is being installed on two different vtables which causes one thunk to
call the wrong vanilla implementation and stomps the FrameAnnotations hooks; fix
this by creating two distinct hook types/thunks (e.g., separate structs for
Render and Fade) so each vtable slot has its own unique relocation instead of a
shared "func", and move or call globals::features::deepDVC.Evaluate() inside the
existing FrameAnnotations hook path (or chain the FrameAnnotations handler) so
DeepDVC logic is executed by that owner rather than installing duplicate
write_vfunc hooks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a41947a3-f6f1-430e-8b07-f01b87ec9477

📥 Commits

Reviewing files that changed from the base of the PR and between f0a752f and 154f834.

⛔ Files ignored due to path filters (2)
  • features/Upscaling/Shaders/Upscaling/Streamline/nvngx_deepdvc.dll is excluded by !**/*.dll
  • features/Upscaling/Shaders/Upscaling/Streamline/sl.deepdvc.dll is excluded by !**/*.dll
📒 Files selected for processing (9)
  • features/DeepDVC/Shaders/Features/DeepDVC.ini
  • src/Feature.cpp
  • src/Features/DeepDVC.cpp
  • src/Features/DeepDVC.h
  • src/Features/Upscaling/Streamline.cpp
  • src/Features/Upscaling/Streamline.h
  • src/Globals.cpp
  • src/Globals.h
  • src/Hooks.cpp

Comment thread src/Features/DeepDVC.cpp
Comment thread src/Features/DeepDVC.cpp
Comment thread src/Features/DeepDVC.cpp
Comment thread src/Features/Upscaling/Streamline.cpp Outdated
Comment thread src/Hooks.cpp Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 11, 2026

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

Copy link
Copy Markdown
Collaborator

@alandtse alandtse left a comment

Choose a reason for hiding this comment

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

Tested in VR and enabled in AE. I'm seeing a watermark when using this to contact nvidia. NVIDIA-RTX/Streamline#107. We'll need to figure out the license issues before having this. AI comments should also be resolved (some may be invalid). Some initial thoughts.

  1. Should this be a separate feature or should we combine with Upscaling like Reflex? Part of that also turns on if you want to distribute separately or just wanted the feature available. Please note that Nexus does not allow CS mods to collect DP, if that is one of your goals. If DP is important, let us know and we can discuss with you further. It may be easiest to join us on Discord.
  2. The hook in hooks.cpp violates the feature boundary (which we're trying to enforce more). We may need to expose a central hook you can register to. This may be waived because other recent features are still doing it.
  3. It's coming with a separate ini file. All features should use the master config file for individual settings. The DeepDVC.ini is reserved for integrating with CS but isn't meant as a separate storage location.
  4. Not for you necessarily, but we need to use cmake to load the dependencies instead of copying raw dlls in to the repo.

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

♻️ Duplicate comments (3)
src/Features/Upscaling/Streamline.cpp (1)

248-261: ⚠️ Potential issue | 🟠 Major

Handle DeepDVC probe/bind failures the same way as Reflex/PCL.

Line 248 ignores the slIsFeatureLoaded result, and Lines 329-330 bind slDeepDVCSetOptions without clearing/checking the pointer. That can leave featureDeepDVC true while src/Features/DeepDVC.cpp, Lines 18-20 still advertises the feature even though the DeepDVC entry point is unavailable. Please mirror the existing checkFeatureAvailability/bindFeatureFn path here and clear featureDeepDVC if either step fails.

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: 329-331

🤖 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 248 - 261, The DeepDVC
probe/bind path must mirror the checkFeatureAvailability/bindFeatureFn flow: use
the result of slIsFeatureLoaded and
slIsFeatureSupported/slGetFeatureRequirements to decide availability, attempt to
bind the entry point (slDeepDVCSetOptions) and if either the probe or bind step
fails clear featureDeepDVC (set to false) and avoid calling the function
pointer; update the Streamline.cpp logic around
slIsFeatureLoaded/slIsFeatureSupported/slGetFeatureRequirements and the code
that binds slDeepDVCSetOptions so it performs atomic check+bind like
checkFeatureAvailability and bindFeatureFn, logs the failure reason (using
magic_enum::enum_name(result) where applicable), and ensures DeepDVC.cpp does
not advertise the feature when the entry point is unavailable.
src/Features/DeepDVC.cpp (2)

111-117: ⚠️ Potential issue | 🟠 Major

Recreate the proxy on every Texture2D shape change.

Lines 114-117 only compare width, height, and format. ID3D11DeviceContext::CopyResource also requires matching array size, mip count, and sample description. A reallocated target with the same dimensions but different ArraySize, MipLevels, or MSAA state will pass this check and then fail at Lines 139 and 172.

Suggested change
-		if (proxyDesc.Width != desc.Width || proxyDesc.Height != desc.Height || proxyDesc.Format != desc.Format) {
+		if (proxyDesc.Width != desc.Width ||
+			proxyDesc.Height != desc.Height ||
+			proxyDesc.Format != desc.Format ||
+			proxyDesc.ArraySize != desc.ArraySize ||
+			proxyDesc.MipLevels != desc.MipLevels ||
+			proxyDesc.SampleDesc.Count != desc.SampleDesc.Count ||
+			proxyDesc.SampleDesc.Quality != desc.SampleDesc.Quality) {
 			proxyBuffer->Release();
 			proxyBuffer = nullptr;
 		}

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/DeepDVC.cpp` around lines 111 - 117, The proxy recreation check
is incomplete: extend the comparison in the proxyBuffer handling so that before
keeping the existing proxy you also compare proxyDesc.ArraySize,
proxyDesc.MipLevels, and proxyDesc.SampleDesc.Count/Quality against
desc.ArraySize, desc.MipLevels, and desc.SampleDesc to detect MSAA changes; if
any of those differ, Release() and null out proxyBuffer (same as existing
pattern). Update the check around proxyBuffer->GetDesc(...) (references:
proxyBuffer, proxyDesc, desc) to include these additional fields so CopyResource
will not be attempted with mismatched resource shapes.

32-35: ⚠️ Potential issue | 🟠 Major

Clamp deserialized settings before using them.

Line 34 trusts the persisted JSON as-is, but Line 46 indexes modes[settings.mode] and Lines 148-150 forward those values into Streamline. A malformed config can therefore read past modes[] or send invalid DeepDVC parameters. Please clamp mode to [0, 1] and clamp the float settings to the same [0.0f, 1.0f] range exposed by the UI.

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/DeepDVC.cpp` around lines 32 - 35, DeepDVC::LoadSettings
currently assigns settings = o_json without validation; after that assignment
clamp settings.mode to the integer range [0,1] (set to 0 if out of range) and
clamp any float parameters that get forwarded to Streamline to the [0.0f,1.0f]
range (e.g., the specific float fields used later around the Streamline call at
lines ~148-150); implement clamping in LoadSettings (use std::clamp or explicit
checks) so modes[settings.mode] cannot be indexed out-of-bounds and Streamline
always receives normalized floats, and ensure the code falls back to a safe
default if values are invalid.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Features/DeepDVC.cpp`:
- Around line 82-88: DeepDVC::Evaluate() currently reads
upscaling.streamline.frameToken (and then uses frameToken) without first
refreshing it; call EnsureFrameToken() at the start of DeepDVC::Evaluate()
before accessing upscaling.streamline.frameToken (or using local frameToken) so
the cached token is up-to-date even when DLSS/Reflex are disabled; ensure the
call happens before the lines that read context/device/frameToken and before the
early return that checks frameToken.

---

Duplicate comments:
In `@src/Features/DeepDVC.cpp`:
- Around line 111-117: The proxy recreation check is incomplete: extend the
comparison in the proxyBuffer handling so that before keeping the existing proxy
you also compare proxyDesc.ArraySize, proxyDesc.MipLevels, and
proxyDesc.SampleDesc.Count/Quality against desc.ArraySize, desc.MipLevels, and
desc.SampleDesc to detect MSAA changes; if any of those differ, Release() and
null out proxyBuffer (same as existing pattern). Update the check around
proxyBuffer->GetDesc(...) (references: proxyBuffer, proxyDesc, desc) to include
these additional fields so CopyResource will not be attempted with mismatched
resource shapes.
- Around line 32-35: DeepDVC::LoadSettings currently assigns settings = o_json
without validation; after that assignment clamp settings.mode to the integer
range [0,1] (set to 0 if out of range) and clamp any float parameters that get
forwarded to Streamline to the [0.0f,1.0f] range (e.g., the specific float
fields used later around the Streamline call at lines ~148-150); implement
clamping in LoadSettings (use std::clamp or explicit checks) so
modes[settings.mode] cannot be indexed out-of-bounds and Streamline always
receives normalized floats, and ensure the code falls back to a safe default if
values are invalid.

In `@src/Features/Upscaling/Streamline.cpp`:
- Around line 248-261: The DeepDVC probe/bind path must mirror the
checkFeatureAvailability/bindFeatureFn flow: use the result of slIsFeatureLoaded
and slIsFeatureSupported/slGetFeatureRequirements to decide availability,
attempt to bind the entry point (slDeepDVCSetOptions) and if either the probe or
bind step fails clear featureDeepDVC (set to false) and avoid calling the
function pointer; update the Streamline.cpp logic around
slIsFeatureLoaded/slIsFeatureSupported/slGetFeatureRequirements and the code
that binds slDeepDVCSetOptions so it performs atomic check+bind like
checkFeatureAvailability and bindFeatureFn, logs the failure reason (using
magic_enum::enum_name(result) where applicable), and ensures DeepDVC.cpp does
not advertise the feature when the entry point is unavailable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d49b8c12-cca6-4e73-9856-bcd13ba85188

📥 Commits

Reviewing files that changed from the base of the PR and between 154f834 and 5c1a264.

📒 Files selected for processing (3)
  • src/Features/DeepDVC.cpp
  • src/Features/Upscaling/Streamline.cpp
  • src/Hooks.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Hooks.cpp

Comment thread src/Features/DeepDVC.cpp
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.

♻️ Duplicate comments (2)
src/Features/DeepDVC.cpp (2)

123-130: ⚠️ Potential issue | 🟠 Major

Proxy descriptor check is still incomplete — CopyResource can fail.

The comparison at line 126 only covers Width/Height/Format. ID3D11DeviceContext::CopyResource requires the full texture shape to match, so if targetBuffer is reallocated with a different ArraySize, MipLevels, or SampleDesc, the stale proxyBuffer will be kept and both CopyResource calls (lines 151 and 184) will silently fail (or emit D3D errors).

🩹 Suggested fix
-		if (proxyDesc.Width != desc.Width || proxyDesc.Height != desc.Height || proxyDesc.Format != desc.Format) {
+		if (proxyDesc.Width != desc.Width ||
+		    proxyDesc.Height != desc.Height ||
+		    proxyDesc.Format != desc.Format ||
+		    proxyDesc.ArraySize != desc.ArraySize ||
+		    proxyDesc.MipLevels != desc.MipLevels ||
+		    proxyDesc.SampleDesc.Count != desc.SampleDesc.Count ||
+		    proxyDesc.SampleDesc.Quality != desc.SampleDesc.Quality) {
 			proxyBuffer->Release();
 			proxyBuffer = nullptr;
 		}

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/DeepDVC.cpp` around lines 123 - 130, The proxy texture
validation is incomplete: extend the descriptor comparison in the proxyBuffer
check to include ArraySize, MipLevels and the SampleDesc (at least Count and
Quality) in addition to Width/Height/Format so the proxy is released when any of
those differ; update the logic around proxyDesc and desc (the symbols
proxyBuffer, proxyDesc, desc) so mismatched ArraySize/MipLevels/SampleDesc
fields cause proxyBuffer->Release() and proxyBuffer = nullptr to avoid
CopyResource failures (also ensure later CopyResource calls on
targetBuffer/proxyBuffer are guarded by a non-null proxyBuffer).

171-185: ⚠️ Potential issue | 🟠 Major

Don't copy proxyBuffer back to target when evaluation didn't succeed.

If slEvaluateFeature is missing (null function pointer) or returns non-eOk, proxyBuffer still holds the pre-evaluation copy and is unconditionally blitted over targetBuffer at lines 183-184. That's a no-op on the pixels but it's still a guaranteed GPU copy per frame, and more importantly it masks eval failures: any future change that might leave proxyBuffer in an undefined state (e.g. partial writes, transient DLL reload) would corrupt the final image. Gate the copy on a success flag.

🩹 Suggested fix
+	bool evalOk = false;
 	const sl::BaseStructure* inputsEval[] = { &viewport };
 	if (upscaling.streamline.slEvaluateFeature) {
 		sl::Result res = upscaling.streamline.slEvaluateFeature(sl::kFeatureDeepDVC, *frameToken, inputsEval, _countof(inputsEval), context);
 		if (res != sl::Result::eOk) {
 			static int failCount = 0;
 			if (failCount < 100) {
 				logger::warn("[DeepDVC] slEvaluateFeature failed: {}", magic_enum::enum_name(res));
 				failCount++;
 			}
+		} else {
+			evalOk = true;
 		}
 	}
 
-	if (proxyBuffer && targetBuffer) {
+	if (evalOk && proxyBuffer && targetBuffer) {
 		context->CopyResource(targetBuffer, proxyBuffer);
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Features/DeepDVC.cpp` around lines 171 - 185, The code unconditionally
copies proxyBuffer to targetBuffer even when slEvaluateFeature is null or
returns non-eOk; change DeepDVC's logic to track success (e.g., a bool like
evalSucceeded) when upscaling.streamline.slEvaluateFeature is present and
slEvaluateFeature(sl::kFeatureDeepDVC, *frameToken, inputsEval,
_countof(inputsEval), context) returns sl::Result::eOk, and only perform
context->CopyResource(targetBuffer, proxyBuffer) when evalSucceeded is true and
both proxyBuffer and targetBuffer are valid; refer to the slEvaluateFeature
call, sl::kFeatureDeepDVC, frameToken, inputsEval, context, proxyBuffer, and
targetBuffer to locate where to add the flag and gate the copy.
🧹 Nitpick comments (1)
src/Features/Upscaling/Streamline.cpp (1)

116-120: Remove (or wire up) the unused featuresToLoadVR array.

featuresToLoadVR is declared at line 117 but never referenced — pref.featuresToLoad is still set from featuresToLoad on line 119, and numFeaturesToLoad uses _countof(featuresToLoad). Since the two arrays are currently identical, this just adds dead code. Either drop it, or select the appropriate array at runtime based on globals::game::isVR.

♻️ Proposed fix (drop the unused array)
 	sl::Feature featuresToLoad[] = { sl::kFeatureDLSS, sl::kFeatureReflex, sl::kFeaturePCL, sl::kFeatureDeepDVC };
-	sl::Feature featuresToLoadVR[] = { sl::kFeatureDLSS, sl::kFeatureReflex, sl::kFeaturePCL, sl::kFeatureDeepDVC };
🤖 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 116 - 120, The extra
array featuresToLoadVR is declared but never used; either remove it or wire it
up based on runtime VR state. Fix by selecting which array to assign to
pref.featuresToLoad and pref.numFeaturesToLoad: use featuresToLoad when
!globals::game::isVR and featuresToLoadVR when globals::game::isVR (update
pref.featuresToLoad and pref.numFeaturesToLoad accordingly), or simply delete
the unused featuresToLoadVR declaration if VR-specific branching is not needed.
Ensure you reference the existing symbols featuresToLoad, featuresToLoadVR,
pref.featuresToLoad, pref.numFeaturesToLoad, and globals::game::isVR when making
the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/Features/DeepDVC.cpp`:
- Around line 123-130: The proxy texture validation is incomplete: extend the
descriptor comparison in the proxyBuffer check to include ArraySize, MipLevels
and the SampleDesc (at least Count and Quality) in addition to
Width/Height/Format so the proxy is released when any of those differ; update
the logic around proxyDesc and desc (the symbols proxyBuffer, proxyDesc, desc)
so mismatched ArraySize/MipLevels/SampleDesc fields cause proxyBuffer->Release()
and proxyBuffer = nullptr to avoid CopyResource failures (also ensure later
CopyResource calls on targetBuffer/proxyBuffer are guarded by a non-null
proxyBuffer).
- Around line 171-185: The code unconditionally copies proxyBuffer to
targetBuffer even when slEvaluateFeature is null or returns non-eOk; change
DeepDVC's logic to track success (e.g., a bool like evalSucceeded) when
upscaling.streamline.slEvaluateFeature is present and
slEvaluateFeature(sl::kFeatureDeepDVC, *frameToken, inputsEval,
_countof(inputsEval), context) returns sl::Result::eOk, and only perform
context->CopyResource(targetBuffer, proxyBuffer) when evalSucceeded is true and
both proxyBuffer and targetBuffer are valid; refer to the slEvaluateFeature
call, sl::kFeatureDeepDVC, frameToken, inputsEval, context, proxyBuffer, and
targetBuffer to locate where to add the flag and gate the copy.

---

Nitpick comments:
In `@src/Features/Upscaling/Streamline.cpp`:
- Around line 116-120: The extra array featuresToLoadVR is declared but never
used; either remove it or wire it up based on runtime VR state. Fix by selecting
which array to assign to pref.featuresToLoad and pref.numFeaturesToLoad: use
featuresToLoad when !globals::game::isVR and featuresToLoadVR when
globals::game::isVR (update pref.featuresToLoad and pref.numFeaturesToLoad
accordingly), or simply delete the unused featuresToLoadVR declaration if
VR-specific branching is not needed. Ensure you reference the existing symbols
featuresToLoad, featuresToLoadVR, pref.featuresToLoad, pref.numFeaturesToLoad,
and globals::game::isVR when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 26118c8a-195c-448e-b569-5783e530c99a

📥 Commits

Reviewing files that changed from the base of the PR and between 5c1a264 and 864e96f.

📒 Files selected for processing (5)
  • features/DeepDVC/Shaders/Features/DeepDVC.ini
  • src/Features/DeepDVC.cpp
  • src/Features/Upscaling/Streamline.cpp
  • src/FrameAnnotations.cpp
  • src/Hooks.cpp
✅ Files skipped from review due to trivial changes (2)
  • features/DeepDVC/Shaders/Features/DeepDVC.ini
  • src/Hooks.cpp

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.

♻️ Duplicate comments (2)
src/Features/DeepDVC.cpp (2)

171-185: ⚠️ Potential issue | 🟠 Major

Only copy back after a successful DeepDVC evaluation.

If slEvaluateFeature is missing or returns a non-OK result, Line 184 still overwrites targetBuffer with proxyBuffer. Track evaluation success and copy back only when Streamline actually processed the resource.

Suggested change
+	bool evalSucceeded = false;
 	const sl::BaseStructure* inputsEval[] = { &viewport };
 	if (upscaling.streamline.slEvaluateFeature) {
 		sl::Result res = upscaling.streamline.slEvaluateFeature(sl::kFeatureDeepDVC, *frameToken, inputsEval, _countof(inputsEval), context);
 		if (res != sl::Result::eOk) {
 			static int failCount = 0;
 			if (failCount < 100) {
 				logger::warn("[DeepDVC] slEvaluateFeature failed: {}", magic_enum::enum_name(res));
 				failCount++;
 			}
+		} else {
+			evalSucceeded = true;
 		}
 	}
 
-	if (proxyBuffer && targetBuffer) {
+	if (evalSucceeded && proxyBuffer && targetBuffer) {
 		context->CopyResource(targetBuffer, proxyBuffer);
 	}

To verify the current failure path:

#!/bin/bash
# Inspect whether final target copy is gated by slEvaluateFeature success.
sed -n '159,186p' src/Features/DeepDVC.cpp

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/DeepDVC.cpp` around lines 171 - 185, The code always copies
proxyBuffer to targetBuffer even when Streamline evaluation fails or
slEvaluateFeature is missing; modify the logic to track success of
slEvaluateFeature (check that upscaling.streamline.slEvaluateFeature exists and
that sl::Result res == sl::Result::eOk when calling
upscaling.streamline.slEvaluateFeature(sl::kFeatureDeepDVC, *frameToken,
inputsEval, _countof(inputsEval), context)) and only call
context->CopyResource(targetBuffer, proxyBuffer) when that success flag is true
and both proxyBuffer and targetBuffer are valid; ensure the flag is initialized
false, set true on success, and used to gate the CopyResource call.

123-130: ⚠️ Potential issue | 🟠 Major

Compare the full Texture2D shape before reusing proxyBuffer.

Line 126 still only checks width, height, and format. If ArraySize, MipLevels, or SampleDesc changes, the later CopyResource calls can fail because the cached proxy no longer matches the target resource shape.

Suggested change
-		if (proxyDesc.Width != desc.Width || proxyDesc.Height != desc.Height || proxyDesc.Format != desc.Format) {
+		if (proxyDesc.Width != desc.Width ||
+			proxyDesc.Height != desc.Height ||
+			proxyDesc.Format != desc.Format ||
+			proxyDesc.ArraySize != desc.ArraySize ||
+			proxyDesc.MipLevels != desc.MipLevels ||
+			proxyDesc.SampleDesc.Count != desc.SampleDesc.Count ||
+			proxyDesc.SampleDesc.Quality != desc.SampleDesc.Quality) {
 			proxyBuffer->Release();
 			proxyBuffer = nullptr;
 		}

To verify the current state:

#!/bin/bash
# Confirm proxyBuffer shape validation and CopyResource call sites.
sed -n '116,185p' src/Features/DeepDVC.cpp

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/DeepDVC.cpp` around lines 123 - 130, The proxyBuffer reuse check
only compares Width, Height, and Format but must validate the entire
D3D11_TEXTURE2D_DESC to avoid CopyResource failures; update the validation
inside the proxyBuffer block to compare ArraySize, MipLevels and SampleDesc
(Count and Quality) in addition to Width/Height/Format, and also ensure any
BindFlags or MiscFlags that affect compatibility are considered before retaining
proxyBuffer (functions/variables: proxyBuffer, D3D11_TEXTURE2D_DESC, desc,
CopyResource); if any field differs, Release() and null out proxyBuffer as
already done.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/Features/DeepDVC.cpp`:
- Around line 171-185: The code always copies proxyBuffer to targetBuffer even
when Streamline evaluation fails or slEvaluateFeature is missing; modify the
logic to track success of slEvaluateFeature (check that
upscaling.streamline.slEvaluateFeature exists and that sl::Result res ==
sl::Result::eOk when calling
upscaling.streamline.slEvaluateFeature(sl::kFeatureDeepDVC, *frameToken,
inputsEval, _countof(inputsEval), context)) and only call
context->CopyResource(targetBuffer, proxyBuffer) when that success flag is true
and both proxyBuffer and targetBuffer are valid; ensure the flag is initialized
false, set true on success, and used to gate the CopyResource call.
- Around line 123-130: The proxyBuffer reuse check only compares Width, Height,
and Format but must validate the entire D3D11_TEXTURE2D_DESC to avoid
CopyResource failures; update the validation inside the proxyBuffer block to
compare ArraySize, MipLevels and SampleDesc (Count and Quality) in addition to
Width/Height/Format, and also ensure any BindFlags or MiscFlags that affect
compatibility are considered before retaining proxyBuffer (functions/variables:
proxyBuffer, D3D11_TEXTURE2D_DESC, desc, CopyResource); if any field differs,
Release() and null out proxyBuffer as already done.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 64180157-84c1-4a0f-843c-2650bcff4495

📥 Commits

Reviewing files that changed from the base of the PR and between 864e96f and 17a47c2.

📒 Files selected for processing (5)
  • src/Feature.cpp
  • src/Features/DeepDVC.cpp
  • src/Features/Upscaling/Streamline.h
  • src/FrameAnnotations.cpp
  • src/Globals.cpp
✅ Files skipped from review due to trivial changes (1)
  • src/Globals.cpp
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/Feature.cpp
  • src/Features/Upscaling/Streamline.h
  • src/FrameAnnotations.cpp

@YtzyFvra
Copy link
Copy Markdown
Contributor Author

Tested in VR and enabled in AE. I'm seeing a watermark when using this to contact nvidia. NVIDIA-RTX/Streamline#107. We'll need to figure out the license issues before having this. AI comments should also be resolved (some may be invalid). Some initial thoughts.

  1. Should this be a separate feature or should we combine with Upscaling like Reflex? Part of that also turns on if you want to distribute separately or just wanted the feature available. Please note that Nexus does not allow CS mods to collect DP, if that is one of your goals. If DP is important, let us know and we can discuss with you further. It may be easiest to join us on Discord.
  2. The hook in hooks.cpp violates the feature boundary (which we're trying to enforce more). We may need to expose a central hook you can register to. This may be waived because other recent features are still doing it.
  3. It's coming with a separate ini file. All features should use the master config file for individual settings. The DeepDVC.ini is reserved for integrating with CS but isn't meant as a separate storage location.
  4. Not for you necessarily, but we need to use cmake to load the dependencies instead of copying raw dlls in to the repo.

Thanks for the review! All feedback has been addressed in the latest push.

  1. Separate Feature vs Upscaling / DP — No interest in separate distribution or DP, this is purely a PR contribution. Kept it as an independent Feature since it has its own GUI panel and settings. Happy to restructure if preferred.

  2. Hook boundary — Moved out of Hooks.cpp entirely. Now lives inside the FrameAnnotations template thunk via if constexpr, reusing the existing BSImagespaceShader_Render<> vtable hooks for the two Tonemap effects. No vtable slot conflicts.

  3. INI fileDeepDVC.ini now only contains [Info] Version = 1-0-0. Runtime settings go through LoadSettings/SaveSettings with the master config.

  4. CMake dependencies — Happy to help when that migration happens.

  5. AI review comments — Valid ones fixed: settings clamping in LoadSettings, EnsureFrameToken() before reading frameToken, proper slDeepDVCSetOptions bind with null check and failure degradation, hook conflict resolved. The proxy shape check and eval-failure CopyResource comments are false positives.

@alandtse
Copy link
Copy Markdown
Collaborator

Thanks for the review! All feedback has been addressed in the latest push.

  1. Separate Feature vs Upscaling / DP — No interest in separate distribution or DP, this is purely a PR contribution. Kept it as an independent Feature since it has its own GUI panel and settings. Happy to restructure if preferred.

Ok we can leave it separate for now.

  1. Hook boundary — Moved out of Hooks.cpp entirely. Now lives inside the FrameAnnotations template thunk via if constexpr, reusing the existing BSImagespaceShader_Render<> vtable hooks for the two Tonemap effects. No vtable slot conflicts.

Apologies Frame Annotations is also not the best place. Since hooks should chain, can you see if you can keep the hook in DeepDVC.cpp? If our existing lifecycle events don't work, let me know.

Please also check the stray edit in hooks.cpp. We should make sure we don't have random churn in the files.

Lastly, we enter into a RC cycle for the last half of April. We won't be merging new feats until May and will be focusing on perfs and fixes (and other non feats). Thanks for the patience. I know you have other feats pending, so if you want to work on them, we'll continue to review. If you have any interest on contributing during the RC period, we'd appreciate any help you feel comfortable in giving including reviewing other PRs.

Automated formatting by clang-format, prettier, and other hooks.
See https://pre-commit.ci for details.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (1)
src/Features/Upscaling/Streamline.cpp (1)

319-331: ⚠️ Potential issue | 🟡 Minor

Disable DeepDVC if the Streamline binding entry point is unavailable.

When featureDeepDVC is true but slGetFeatureFunction is null, this block silently skips binding and leaves DeepDVC advertised as supported. Clear the flag in that branch so the feature degrades consistently.

Suggested change
 	slDeepDVCSetOptions = nullptr;
 	if (featureDeepDVC && slGetFeatureFunction) {
 		auto fn = (void*)nullptr;
 		const sl::Result bindResult = slGetFeatureFunction(sl::kFeatureDeepDVC, "slDeepDVCSetOptions", fn);
 		if (bindResult == sl::Result::eOk && fn) {
 			slDeepDVCSetOptions = reinterpret_cast<decltype(slDeepDVCSetOptions)>(fn);
 			logger::info("[Streamline] DeepDVC runtime controls are available");
 		} else {
 			logger::warn("[Streamline] DeepDVC options bind failed with {}; DeepDVC will be disabled",
 				magic_enum::enum_name(bindResult));
 			featureDeepDVC = false;
 		}
+	} else if (featureDeepDVC) {
+		logger::warn("[Streamline] slGetFeatureFunction is unavailable; DeepDVC will be disabled");
+		featureDeepDVC = false;
 	}

Please verify with the Streamline package/version used for distribution that missing feature-function binding hides DeepDVC from the UI and skips runtime evaluation.

🤖 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 319 - 331, The code
leaves DeepDVC advertised when featureDeepDVC is true but slGetFeatureFunction
is null; update the branch where slGetFeatureFunction is false to clear
featureDeepDVC and log a warning so the feature is disabled consistently —
specifically modify the block around slDeepDVCSetOptions / slGetFeatureFunction
to set featureDeepDVC = false (and add a logger::warn mentioning
slGetFeatureFunction unavailable) when slGetFeatureFunction is null so DeepDVC
is hidden from the UI and not evaluated at runtime.
🧹 Nitpick comments (1)
src/Features/DeepDVC.h (1)

44-54: Prefer RAII for the owned D3D texture.

proxyBuffer is an owning COM pointer with manual Release() paths in both the destructor and recreation logic. Consider using the project’s existing COM smart-pointer convention to make reset/destruction exception-safe and copy-safe.

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/DeepDVC.h` around lines 44 - 54, The destructor/manual Release
on the raw ID3D11Texture2D* proxyBuffer should be replaced with the project’s
COM smart-pointer convention (e.g., ComPtr/ScopedComPtr/WRL::ComPtr) to ensure
RAII, exception-safety and correct copy/move semantics; change the member
declaration from "ID3D11Texture2D* proxyBuffer" to the smart-pointer type,
remove manual Release() from ~DeepDVC() and any recreation code, and use the
smart pointer’s Reset/Release/Attach methods when replacing the texture; also
include the appropriate smart-pointer header and ensure DeepDVC’s copy/move
constructors/operators follow the project convention (delete or default them) so
resource ownership is well-defined.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Features/DeepDVC.cpp`:
- Line 52: The ImGui usage casts uint32_t to int* unsafely (e.g.,
ImGui::SliderInt("Mode", (int*)&settings.mode)) — replace this pattern by using
a local int temp variable for the slider, call ImGui::SliderInt with &temp,
clamp/validate temp, then assign back to settings.mode (or change settings.mode
to int if you intentionally want signed storage) to avoid unsafe pointer casts;
apply the same fix consistently for settings.mode usage in DeepDVC.cpp and the
analogous patterns in Upscaling, ScreenSpaceGI, SubsurfaceScattering,
ScreenSpaceShadows, and WetnessEffects, or alternatively document/suppress the
pattern if that is the agreed project-wide approach.
- Around line 156-180: Add upfront null checks for the Streamline entry points
and fail-fast when options cannot be applied: ensure
upscaling.streamline.slSetTag is non-null before calling slSetTag(viewport,
inputs, _countof(inputs), context); check
upscaling.streamline.slDeepDVCSetOptions before creating and setting options and
if slDeepDVCSetOptions exists but returns a non-eOk result, do not proceed to
evaluation (return or skip); finally guard
upscaling.streamline.slEvaluateFeature before calling
slEvaluateFeature(sl::kFeatureDeepDVC, *frameToken, inputsEval,
_countof(inputsEval), context). Reference symbols: slSetTag,
slDeepDVCSetOptions, slEvaluateFeature, viewport, inputs, context, options,
upscaling.streamline, frameToken.

In `@src/Features/DeepDVC.h`:
- Line 30: The DeepDVC feature is currently enabled by default via the variable
"mode" in DeepDVC.h; change its default from 1 to 0 so the post-process is off
until users opt in. Locate the "uint32_t mode" declaration in the DeepDVC.h
header and set the initializer to 0 (ensure any comments or documentation in the
file reflect the new default), and run a quick build/test to verify no other
code assumes mode defaults to 1.

In `@src/Features/Upscaling/Streamline.cpp`:
- Around line 116-120: The code currently always assigns the non-VR array to
pref.featuresToLoad causing SE/AE to request kFeatureDeepDVC even though it's
VR-only; update the logic to choose between featuresToLoad and featuresToLoadVR
at runtime (e.g., detect VR variant via the existing runtime detection helper
used in this module or using REL::RelocateMember() pattern if variant-specific
members are used) and set pref.featuresToLoad and pref.numFeaturesToLoad from
the chosen array so that kFeatureDeepDVC is only included when running the VR
build; reference the arrays featuresToLoad and featuresToLoadVR and the fields
pref.featuresToLoad / pref.numFeaturesToLoad and ensure the runtime branch uses
REL::RelocateMember() where required for compatibility across SE/AE/VR.

---

Duplicate comments:
In `@src/Features/Upscaling/Streamline.cpp`:
- Around line 319-331: The code leaves DeepDVC advertised when featureDeepDVC is
true but slGetFeatureFunction is null; update the branch where
slGetFeatureFunction is false to clear featureDeepDVC and log a warning so the
feature is disabled consistently — specifically modify the block around
slDeepDVCSetOptions / slGetFeatureFunction to set featureDeepDVC = false (and
add a logger::warn mentioning slGetFeatureFunction unavailable) when
slGetFeatureFunction is null so DeepDVC is hidden from the UI and not evaluated
at runtime.

---

Nitpick comments:
In `@src/Features/DeepDVC.h`:
- Around line 44-54: The destructor/manual Release on the raw ID3D11Texture2D*
proxyBuffer should be replaced with the project’s COM smart-pointer convention
(e.g., ComPtr/ScopedComPtr/WRL::ComPtr) to ensure RAII, exception-safety and
correct copy/move semantics; change the member declaration from
"ID3D11Texture2D* proxyBuffer" to the smart-pointer type, remove manual
Release() from ~DeepDVC() and any recreation code, and use the smart pointer’s
Reset/Release/Attach methods when replacing the texture; also include the
appropriate smart-pointer header and ensure DeepDVC’s copy/move
constructors/operators follow the project convention (delete or default them) so
resource ownership is well-defined.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 39605255-f4dc-4890-8621-adbb287cc7c5

📥 Commits

Reviewing files that changed from the base of the PR and between 17a47c2 and ecffbe4.

⛔ Files ignored due to path filters (2)
  • features/Upscaling/Shaders/Upscaling/Streamline/nvngx_deepdvc.dll is excluded by !**/*.dll
  • features/Upscaling/Shaders/Upscaling/Streamline/sl.deepdvc.dll is excluded by !**/*.dll
📒 Files selected for processing (8)
  • features/DeepDVC/Shaders/Features/DeepDVC.ini
  • src/Feature.cpp
  • src/Features/DeepDVC.cpp
  • src/Features/DeepDVC.h
  • src/Features/Upscaling/Streamline.cpp
  • src/Features/Upscaling/Streamline.h
  • src/Globals.cpp
  • src/Globals.h
✅ Files skipped from review due to trivial changes (4)
  • src/Globals.cpp
  • src/Feature.cpp
  • src/Globals.h
  • features/DeepDVC/Shaders/Features/DeepDVC.ini
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Features/Upscaling/Streamline.h

Comment thread src/Features/DeepDVC.cpp
Comment thread src/Features/DeepDVC.cpp
Comment thread src/Features/DeepDVC.h Outdated
Comment thread src/Features/Upscaling/Streamline.cpp
Copy link
Copy Markdown
Collaborator

@doodlum doodlum left a comment

Choose a reason for hiding this comment

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

when i tried this is just made things look washed out. maybe there needs to be a contrast slider or something?

Also it brightens up things that are already bright, overexposing them.

Please try looking at my local tonemapping code here instead https://github.com/doodlum/skyrim-community-shaders/tree/ltm-nonsense

@YtzyFvra
Copy link
Copy Markdown
Contributor Author

Thanks for the review! All feedback has been addressed in the latest push.

  1. Separate Feature vs Upscaling / DP — No interest in separate distribution or DP, this is purely a PR contribution. Kept it as an independent Feature since it has its own GUI panel and settings. Happy to restructure if preferred.

Ok we can leave it separate for now.

  1. Hook boundary — Moved out of Hooks.cpp entirely. Now lives inside the FrameAnnotations template thunk via if constexpr, reusing the existing BSImagespaceShader_Render<> vtable hooks for the two Tonemap effects. No vtable slot conflicts.

Apologies Frame Annotations is also not the best place. Since hooks should chain, can you see if you can keep the hook in DeepDVC.cpp? If our existing lifecycle events don't work, let me know.

Please also check the stray edit in hooks.cpp. We should make sure we don't have random churn in the files.

Lastly, we enter into a RC cycle for the last half of April. We won't be merging new feats until May and will be focusing on perfs and fixes (and other non feats). Thanks for the patience. I know you have other feats pending, so if you want to work on them, we'll continue to review. If you have any interest on contributing during the RC period, we'd appreciate any help you feel comfortable in giving including reviewing other PRs.

  1. Hook — Done.
  2. Stray edit — Done.
  3. AI Review — Fixed: slSetTag null check added (40b1cf9).
  4. RC cycle — Understood, happy to help review other PRs.

@YtzyFvra
Copy link
Copy Markdown
Contributor Author

when i tried this is just made things look washed out. maybe there needs to be a contrast slider or something?

Also it brightens up things that are already bright, overexposing them.

Please try looking at my local tonemapping code here instead https://github.com/doodlum/skyrim-community-shaders/tree/ltm-nonsense

Thanks for the feedback.
DeepDVC is a closed-source AI black box with only two exposed parameters (intensity / saturation boost), so there's not much I can tune on my end.
Let me dig into any papers or documentation NVIDIA may have published to see if there's a way to mitigate the overexposure — I'll follow up.

@YtzyFvra
Copy link
Copy Markdown
Contributor Author

when i tried this is just made things look washed out. maybe there needs to be a contrast slider or something?

Also it brightens up things that are already bright, overexposing them.

Please try looking at my local tonemapping code here instead https://github.com/doodlum/skyrim-community-shaders/tree/ltm-nonsense

DVC Technical Background

DVC is a complete black box — no papers or mechanism documentation found, only that it's a deep neural network accelerated by Tensor Cores. NVIDIA's programming guide recommends running it after tonemapping, before film grain, at full resolution (post-SR), accepting only SDR input.

DVC exposes only two parameters: intensity and saturation boost. Comparing input vs output, the intensity parameter likely controls multiple dimensions internally — brightness, chrominance, contrast, grey levels, etc.

Unlike simply adjusting post-processing sliders, DVC operates at a much finer granularity — it doesn't do uniform linear processing as the exposed parameters might suggest. It likely uses attention mechanisms and ViT-based strategies, applying different processing strengths to different regions of different scenes. This makes it better at avoiding localized overexposure and detail loss compared to traditional approaches.

However, DVC can only avoid over-processing when performing its own enhancement — it cannot suppress over-processing introduced by other sources.

This AI filter works correctly in The Witcher and several other titles. I've tested it on vanilla Skyrim and earlier CS builds with no issues. Back in January I used it on the jiayev fork to enhance some PP presets from the channel — for example, certain cinematic presets have terrible shadow detail in specific scenes/weather conditions, and DVC significantly improves them. In bright scenes the effect is subtle; in dark caves it noticeably improves visibility and makes the image more vivid.

LTM Testing

I don't have an SE/AE client, so I tested the LTM branch in VR. After disabling Sky_UpdateColors and Sky_SetDirectionalAmbientColors (which use non-VR addresses), ENB post-processing had incorrect transparency handling scope; with my setup, enabling useeffect darkened the entire image, and all sub-effect toggles and sliders had no effect whatsoever. So I couldn't evaluate LTM's actual results under normal working conditions.

Regarding the "washed out" issue: ruling out other factors, it's most likely double lifting — both LTM and DVC boost shadow detail, so dark areas get lifted twice. Meanwhile, after LTM processing, DVC's further enhancement pushes highlights beyond the ideal range of the ACES Filmic curve, causing overexposure. This problem likely can't be solved from the DVC side.

An Interesting Finding

Some users have reported that CS's DLSS performance is worse than PureDark's. I experimented with moving DLSS execution to after post-processing to save performance (currently DLSSEnhancer uses a rather convoluted method to process post-processing at render resolution before compositing to display resolution to avoid DLSS artifacts — simply moving DLSS after PP like PureDark does would be cleaner and simpler). On vanilla Skyrim, even with SL exposure/HDR flags set correctly, the Quality preset causes highlights to become blurry with slight color shifts. But if DVC runs after post-processing and before DLSS, it saves performance while producing a more visually comfortable result. My guess is that DLSS's training data is based on HDR input, and DVC happens to adjust the LDR output into a form closer to what DLSS expects.

Could this approach be worth trying in CS? Would it conflict with any existing development plans?

@YtzyFvra
Copy link
Copy Markdown
Contributor Author

Thanks for the review! All feedback has been addressed in the latest push.

An New Finding

Some users have reported that CS's DLSS performance is worse than PureDark's. I experimented with moving DLSS execution to after post-processing to save performance (currently DLSSEnhancer uses a rather convoluted method to process post-processing at render resolution before compositing to display resolution to avoid DLSS artifacts — simply moving DLSS after PP like PureDark does would be cleaner and simpler). On vanilla Skyrim, even with SL exposure/HDR flags set correctly, the Quality preset causes highlights to become blurry with slight color shifts. But if DVC runs after post-processing and before DLSS, it saves performance while producing a more visually comfortable result. My guess is that DLSS's training data is based on HDR input, and DVC happens to adjust the LDR output into a form closer to what DLSS expects.

Could this approach be worth trying in CS? Would it conflict with any existing development plans?

And

Is there a Discord channel or thread for VR dev discussion? I'm often busy with work and don't know where to start with the flood of messages. I appreciate what you and VRnord do for VR — where do you usually chat? (doodlum seems to be everywhere XD, but I rarely see you two.)

@doodlum
Copy link
Copy Markdown
Collaborator

doodlum commented Apr 19, 2026

ltm needs to be ported to be separate to enb system, so it modifies vanilla tonemapping system.

moving dlss to after post processing is acceptable for vr. for se/ae it needs to stay as is.

we talk on the official discord server for cs.

@alandtse
Copy link
Copy Markdown
Collaborator

Is there a Discord channel or thread for VR dev discussion? I'm often busy with work and don't know where to start with the flood of messages. I appreciate what you and VRnord do for VR — where do you usually chat? (doodlum seems to be everywhere XD, but I rarely see you two.)

https://discord.com/invite/nkrQybAsyy

Copy link
Copy Markdown
Collaborator

@alandtse alandtse left a comment

Choose a reason for hiding this comment

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

Please note nvidia says we need to talk to the developer relations rep before we include this. Please handle. We can't merge until that's resolved. If they can't provide it to an open source project, then we can't use the dll.

@doodlum fyi.

@alandtse alandtse added the deferred Defer to next cycle label Apr 29, 2026
@alandtse alandtse removed the deferred Defer to next cycle label May 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants