revert: "perf: optimise DeferredComposite (#2150)"#2232
Conversation
This reverts commit 7f64e55.
📝 WalkthroughWalkthroughThis PR converts the deferred composite pipeline from a graphics-based approach (vertex/pixel shaders) to a compute shader architecture, refactors normal encoding from sqrt-based to octahedral schemes, updates dependent systems to reference the new normal-roughness render target, and removes obsolete state management utilities. Changes
Sequence Diagram(s)sequenceDiagram
participant Deferred as Deferred::Render
participant Dispatch as GPU Dispatch
participant ComputeShader as DeferredCompositeCS
participant SRVs as Input SRVs<br/>(Albedo, Normal, Depth, etc.)
participant UAVs as Output UAVs<br/>(Main, Normals, Motion)
Deferred->>Deferred: Bind SRVs (Albedo, NormalRoughness, Masks, Depth, etc.)
Deferred->>Deferred: Bind StereoOptModeTexture (optional)
Deferred->>Deferred: Bind UAVs (Main, NormalMask, MotionVectors)
Deferred->>Dispatch: Dispatch(screen width/8, screen height/8, 1)
Dispatch->>ComputeShader: Launch threads (dispatchID)
ComputeShader->>SRVs: Sample per-pixel data (albedo, normal, depth)
alt Depth == 1.0
ComputeShader->>ComputeShader: Generate sky motion vectors
else Standard
ComputeShader->>ComputeShader: Compute standard lighting
end
ComputeShader->>ComputeShader: Decode octahedral normal
ComputeShader->>ComputeShader: Sample SSGI, reflections, sky
ComputeShader->>UAVs: Write Main (final color)
ComputeShader->>UAVs: Write NormalTAAMaskSpecular
ComputeShader->>UAVs: Write MotionVectors
Deferred->>Deferred: Unbind compute resources
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.Comment |
Actionable Suggestions
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@package/Shaders/Tests/TestGBuffer.hlsl`:
- Around line 37-59: The tests currently only assert encoded range and decoded
unit length for the sample normals (testNormals) but miss verifying direction
fidelity; update the loop that uses GBuffer::EncodeNormal and
GBuffer::DecodeNormal to also compute the dot product between original and
decoded (e.g., dot(original, decoded)) and assert it exceeds a relaxed threshold
(choose ~0.90–0.97 depending on half precision tolerance) to catch
mirrored/reflected results; add this cosine/dot assertion alongside the existing
length check for variables original and decoded.
In `@src/Deferred.cpp`:
- Around line 356-373: The composite shader reads DepthTexture unguarded in
DeferredCompositeCS but srvs[16] leaves slot t4 null on builds without
dynamicCubemaps, causing bad depth reads; fix by binding the depth SRV into
srvs[4] unconditionally (use Util::GetCurrentSceneDepthSRV(true) for t4) instead
of only when dynamicCubemaps.loaded || REL::Module::IsVR(), and ensure the srvs
array still gracefully uses nullptr for optional textures (reflectance,
envTexture, skylighting, etc.) so resource lifetimes and DX11 binding remain
safe when features are absent.
🪄 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: 5b1b00ef-be67-4da7-9759-36c316185e2a
📒 Files selected for processing (10)
package/Shaders/Common/GBuffer.hlslipackage/Shaders/DeferredCompositeCS.hlslpackage/Shaders/DeferredCompositeVS.hlslpackage/Shaders/Tests/TestGBuffer.hlslsrc/Deferred.cppsrc/Deferred.hsrc/Features/ScreenSpaceGI.cppsrc/Features/SubsurfaceScattering.cppsrc/Features/VRStereoOptimizations.hsrc/Utils/D3DStateBackup.h
💤 Files with no reviewable changes (2)
- package/Shaders/DeferredCompositeVS.hlsl
- src/Utils/D3DStateBackup.h
| // Test behavioral properties of octahedral encoding (not exact numerical accuracy) | ||
| // Half precision + quantization means we check: valid output, normalized, reasonable direction | ||
| half3 testNormals[4] = { | ||
| normalize(half3(1.0h, 1.0h, 1.0h)), | ||
| normalize(half3(-1.0h, 1.0h, 1.0h)), | ||
| normalize(half3(1.0h, -1.0h, 1.0h)), | ||
| normalize(half3(1.0h, 1.0h, -1.0h)) | ||
| }; | ||
|
|
||
| for (int i = 0; i < 4; i++) { | ||
| float3 original = testNormals[i]; | ||
| float2 encoded = GBuffer::EncodeNormal(original); | ||
| float3 decoded = GBuffer::DecodeNormal(encoded); | ||
| half3 original = testNormals[i]; | ||
| half2 encoded = GBuffer::EncodeNormal(original); | ||
| half3 decoded = GBuffer::DecodeNormal(encoded); | ||
|
|
||
| // Check behavioral properties (relaxed for half precision quantization): | ||
| // 1. Encoded values are in valid range [0, 1] | ||
| ASSERT(IsTrue, encoded.x >= 0.0h && encoded.x <= 1.0h); | ||
| ASSERT(IsTrue, encoded.y >= 0.0h && encoded.y <= 1.0h); | ||
|
|
||
| float length = sqrt(decoded.x * decoded.x + decoded.y * decoded.y + decoded.z * decoded.z); | ||
| ASSERT(IsTrue, abs(length - 1.0) < 0.05); | ||
| // 2. Decoded normal is normalized (unit length) | ||
| half length = sqrt(decoded.x * decoded.x + decoded.y * decoded.y + decoded.z * decoded.z); | ||
| ASSERT(IsTrue, abs(length - 1.0h) < 0.02h); // Relaxed tolerance for half precision | ||
| } |
There was a problem hiding this comment.
Keep a direction check for the diagonal roundtrip.
This now only proves “valid range + unit length.” A broken octahedral fold/sign path can still return a normalized but mirrored vector, so these angled cases would pass while reflections drift. Add a cosine/dot threshold against original.
🧪 Suggested assertion
for (int i = 0; i < 4; i++) {
half3 original = testNormals[i];
half2 encoded = GBuffer::EncodeNormal(original);
half3 decoded = GBuffer::DecodeNormal(encoded);
// Check behavioral properties (relaxed for half precision quantization):
// 1. Encoded values are in valid range [0, 1]
ASSERT(IsTrue, encoded.x >= 0.0h && encoded.x <= 1.0h);
ASSERT(IsTrue, encoded.y >= 0.0h && encoded.y <= 1.0h);
+
+ // 1b. Decoded direction should still match the source normal closely.
+ ASSERT(IsTrue, dot(decoded, original) > 0.98h);
// 2. Decoded normal is normalized (unit length)
half length = sqrt(decoded.x * decoded.x + decoded.y * decoded.y + decoded.z * decoded.z);
ASSERT(IsTrue, abs(length - 1.0h) < 0.02h); // Relaxed tolerance for half precision
}📝 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.
| // Test behavioral properties of octahedral encoding (not exact numerical accuracy) | |
| // Half precision + quantization means we check: valid output, normalized, reasonable direction | |
| half3 testNormals[4] = { | |
| normalize(half3(1.0h, 1.0h, 1.0h)), | |
| normalize(half3(-1.0h, 1.0h, 1.0h)), | |
| normalize(half3(1.0h, -1.0h, 1.0h)), | |
| normalize(half3(1.0h, 1.0h, -1.0h)) | |
| }; | |
| for (int i = 0; i < 4; i++) { | |
| float3 original = testNormals[i]; | |
| float2 encoded = GBuffer::EncodeNormal(original); | |
| float3 decoded = GBuffer::DecodeNormal(encoded); | |
| half3 original = testNormals[i]; | |
| half2 encoded = GBuffer::EncodeNormal(original); | |
| half3 decoded = GBuffer::DecodeNormal(encoded); | |
| // Check behavioral properties (relaxed for half precision quantization): | |
| // 1. Encoded values are in valid range [0, 1] | |
| ASSERT(IsTrue, encoded.x >= 0.0h && encoded.x <= 1.0h); | |
| ASSERT(IsTrue, encoded.y >= 0.0h && encoded.y <= 1.0h); | |
| float length = sqrt(decoded.x * decoded.x + decoded.y * decoded.y + decoded.z * decoded.z); | |
| ASSERT(IsTrue, abs(length - 1.0) < 0.05); | |
| // 2. Decoded normal is normalized (unit length) | |
| half length = sqrt(decoded.x * decoded.x + decoded.y * decoded.y + decoded.z * decoded.z); | |
| ASSERT(IsTrue, abs(length - 1.0h) < 0.02h); // Relaxed tolerance for half precision | |
| } | |
| for (int i = 0; i < 4; i++) { | |
| half3 original = testNormals[i]; | |
| half2 encoded = GBuffer::EncodeNormal(original); | |
| half3 decoded = GBuffer::DecodeNormal(encoded); | |
| // Check behavioral properties (relaxed for half precision quantization): | |
| // 1. Encoded values are in valid range [0, 1] | |
| ASSERT(IsTrue, encoded.x >= 0.0h && encoded.x <= 1.0h); | |
| ASSERT(IsTrue, encoded.y >= 0.0h && encoded.y <= 1.0h); | |
| // 1b. Decoded direction should still match the source normal closely. | |
| ASSERT(IsTrue, dot(decoded, original) > 0.98h); | |
| // 2. Decoded normal is normalized (unit length) | |
| half length = sqrt(decoded.x * decoded.x + decoded.y * decoded.y + decoded.z * decoded.z); | |
| ASSERT(IsTrue, abs(length - 1.0h) < 0.02h); // Relaxed tolerance for half precision | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@package/Shaders/Tests/TestGBuffer.hlsl` around lines 37 - 59, The tests
currently only assert encoded range and decoded unit length for the sample
normals (testNormals) but miss verifying direction fidelity; update the loop
that uses GBuffer::EncodeNormal and GBuffer::DecodeNormal to also compute the
dot product between original and decoded (e.g., dot(original, decoded)) and
assert it exceeds a relaxed threshold (choose ~0.90–0.97 depending on half
precision tolerance) to catch mirrored/reflected results; add this cosine/dot
assertion alongside the existing length check for variables original and
decoded.
|
✅ A pre-release build is available for this PR: |
|
the feature audit is spurious. Need to teach it about reverts. |
This reverts commit 7f64e55.
closes #2223
Summary by CodeRabbit
Performance & Optimization
Refactor