Optimization work

This commit is contained in:
Justin Marshall
2026-05-04 18:40:22 -07:00
parent 8ab3b0be16
commit 9e8bf6012c
2 changed files with 93 additions and 53 deletions
+91 -53
View File
@@ -3986,23 +3986,47 @@ float3 EstimatePathTracedIndirectBounce(uint2 pixel, float3 worldPos, float3 N,
return accum * INDIRECT_STRENGTH; return accum * INDIRECT_STRENGTH;
} }
float3 PathTraceLightingSample(uint2 pixel, float3 worldPos, float3 N, float3 V, float3 baseAlbedo, bool isSkeletal, inout uint rng, out float3 specularAccum) float3 ApplyPrimaryDiffusePost(float3 lightingAccum, float ao, float microShadow, bool isSkeletal)
{
lightingAccum *= ao;
lightingAccum *= microShadow;
if (isSkeletal)
lightingAccum *= 1.2;
return max(lightingAccum, 0.0);
}
float3 ApplyPrimarySpecularPost(float3 specularAccum, float ao, bool isSkeletal)
{
specularAccum *= ao;
if (isSkeletal)
specularAccum *= 1.15;
return max(specularAccum, 0.0);
}
float3 PathTraceDeterministicLighting(
uint2 pixel,
float3 worldPos,
float3 N,
float3 V,
float3 baseAlbedo,
bool isSkeletal,
float cavity,
float ao,
float skyVis,
float ambientSkyVis,
out float3 specularAccum)
{ {
specularAccum = 0.0; specularAccum = 0.0;
float cavity = ComputeCavity(pixel, worldPos, N);
float microShadow = lerp(0.75, 1.0, cavity); float microShadow = lerp(0.75, 1.0, cavity);
// Keep the environment / sun shadow path deterministic. The previous // These environment and direct-light terms are deterministic for a given
// path-traced version sampled the sky cone and AO with frame-varying random // pixel. The old RayGen evaluated them once for every SPP, which multiplied
// rays; with DLSS RR enabled that raw 1spp signal was noisy enough to lose // the AO/sky/direct shadow ray budget without adding new samples.
// the old environment shadow shapes. Reuse the stable multi-ray sky and AO
// probes from the original lighting pass, then add only a small stochastic
// sky-bounce term when both extra bounces and an SPP budget are requested.
float ao = ComputeAmbientOcclusion(worldPos, N, pixel);
float skyVis = ComputeSkyVisibility(worldPos, N, pixel);
float ambientSkyVis = TraceStraightUpToSky(worldPos, N);
float upness = saturate(N.z * 0.5 + 0.5); float upness = saturate(N.z * 0.5 + 0.5);
float3 skyColorRGB = float3(0.98, 0.55, 0.35); float3 skyColorRGB = float3(0.98, 0.55, 0.35);
float3 skyColor = skyColorRGB * (0.35 + 0.65 * upness); float3 skyColor = skyColorRGB * (0.35 + 0.65 * upness);
@@ -4011,14 +4035,14 @@ float3 PathTraceLightingSample(uint2 pixel, float3 worldPos, float3 N, float3 V,
lightingAccum += skyColor * (0.70 * skyVis); lightingAccum += skyColor * (0.70 * skyVis);
lightingAccum += ambientSkyVis * (skyColorRGB * 0.15); lightingAccum += ambientSkyVis * (skyColorRGB * 0.15);
if (gMaxBounces > 1u)
{
lightingAccum += EstimatePathTracedIndirectBounce(pixel, worldPos, N, V, baseAlbedo, rng);
}
if (isSkeletal) if (isSkeletal)
lightingAccum += 0.1; lightingAccum += 0.1;
// The current direct-light evaluators are deterministic. Keep the inout RNG
// argument only because the functions share the same signature as stochastic
// helpers; it is not consumed by PathTraceDirect* in the current shader.
uint directRng = InitRng(pixel, 0u, 0xD17EC7u);
[loop] [loop]
for (uint i = 0; i < gLightCount; ++i) for (uint i = 0; i < gLightCount; ++i)
{ {
@@ -4028,32 +4052,23 @@ float3 PathTraceLightingSample(uint2 pixel, float3 worldPos, float3 N, float3 V,
if (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_POINT) if (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_POINT)
{ {
diffuse = PathTraceDirectPointLight(pixel, worldPos, N, V, baseAlbedo, Lgt, rng, spec); diffuse = PathTraceDirectPointLight(pixel, worldPos, N, V, baseAlbedo, Lgt, directRng, spec);
} }
else if (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_SPOT) else if (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_SPOT)
{ {
diffuse = PathTraceDirectSpotLight(worldPos, N, V, baseAlbedo, Lgt, rng, spec); diffuse = PathTraceDirectSpotLight(worldPos, N, V, baseAlbedo, Lgt, directRng, spec);
} }
else if (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_RECT) else if (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_RECT)
{ {
diffuse = PathTraceDirectRectLight(pixel, worldPos, N, V, baseAlbedo, Lgt, rng, spec); diffuse = PathTraceDirectRectLight(pixel, worldPos, N, V, baseAlbedo, Lgt, directRng, spec);
} }
lightingAccum += diffuse; lightingAccum += diffuse;
specularAccum += spec; specularAccum += spec;
} }
lightingAccum *= ao; specularAccum = ApplyPrimarySpecularPost(specularAccum, ao, isSkeletal);
specularAccum *= ao; return ApplyPrimaryDiffusePost(lightingAccum, ao, microShadow, isSkeletal);
lightingAccum *= microShadow;
if (isSkeletal)
{
lightingAccum *= 1.2;
specularAccum *= 1.15;
}
return max(lightingAccum, 0.0);
} }
[shader("raygeneration")] [shader("raygeneration")]
@@ -4093,6 +4108,14 @@ void RayGen()
uint spp = max(gSamplesPerPixel, 1u); uint spp = max(gSamplesPerPixel, 1u);
spp = min(spp, 8u); spp = min(spp, 8u);
// All four of these are deterministic for this pixel. Compute them once,
// then reuse them for every stochastic GI sample.
float cavity = ComputeCavity(pixel, worldPos, N);
float ao = ComputeAmbientOcclusion(worldPos, N, pixel);
float skyVis = ComputeSkyVisibility(worldPos, N, pixel);
float ambientSkyVis = TraceStraightUpToSky(worldPos, N);
float microShadow = lerp(0.75, 1.0, cavity);
float3 reactiveFinalGather = 0.0; float3 reactiveFinalGather = 0.0;
if (gMaxBounces > 1u) if (gMaxBounces > 1u)
{ {
@@ -4109,34 +4132,49 @@ void RayGen()
gatherRng); gatherRng);
} }
float3 colorAccum = 0.0; float3 specularAccum = 0.0;
float3 lightingAccum = PathTraceDeterministicLighting(
pixel,
worldPos,
N,
V,
baseAlbedo,
isSkeletal,
cavity,
ao,
skyVis,
ambientSkyVis,
specularAccum);
[loop] // Only the indirect bounce path uses per-SPP randomness now. Direct lights,
for (uint s = 0; s < spp; ++s) // AO, sky visibility, cavity, and final gather are deterministic and should
// not be re-traced spp times.
if (gMaxBounces > 1u)
{ {
// The current denoiser is spatial, not temporal. Do not use gFrameIndex float3 indirectAccum = 0.0;
// for the primary GI seed or the same pixel flickers forever instead of
// presenting a stable signal for the a-trous pass.
uint rng = InitRng(pixel, 0u, s);
float3 specularAccum = 0.0; [loop]
float3 lightingAccum = PathTraceLightingSample( for (uint s = 0; s < spp; ++s)
pixel, {
worldPos, // The current denoiser is spatial, not temporal. Do not use
N, // gFrameIndex for the primary GI seed or the same pixel flickers
V, // forever instead of presenting a stable signal for the a-trous pass.
baseAlbedo, uint rng = InitRng(pixel, 0u, s);
isSkeletal, indirectAccum += EstimatePathTracedIndirectBounce(
rng, pixel,
specularAccum); worldPos,
N,
V,
baseAlbedo,
rng);
}
float cavity = ComputeCavity(pixel, worldPos, N); float3 indirectLighting = indirectAccum / (float)spp;
float3 albedo = baseAlbedo * cavity; lightingAccum += ApplyPrimaryDiffusePost(indirectLighting, ao, microShadow, isSkeletal);
colorAccum += (albedo * lightingAccum) + specularAccum;
} }
float3 finalColor = colorAccum / (float)spp; float3 albedo = baseAlbedo * cavity;
float3 finalColor = (albedo * lightingAccum) + specularAccum;
if (gMaxBounces > 1u) if (gMaxBounces > 1u)
{ {
+2
View File
@@ -6161,6 +6161,8 @@ static UINT QD3D12_CalcMipCount(int width, int height)
w = std::max<UINT>(1u, w >> 1); w = std::max<UINT>(1u, w >> 1);
h = std::max<UINT>(1u, h >> 1); h = std::max<UINT>(1u, h >> 1);
++levels; ++levels;
if (levels > 2)
break;
} }
return levels; return levels;
} }