Fixes for static movement and RR.

This commit is contained in:
Justin Marshall
2026-05-29 22:11:02 -07:00
parent 5dc650a63b
commit de259a7017
2 changed files with 96 additions and 8 deletions
+78 -4
View File
@@ -660,6 +660,7 @@ enum PipelineMode
static void QD3D12_FlushQueuedBatches();
static PipelineMode PickPipeline(bool useTex0, bool useTex1);
static Mat4 CurrentMVP();
static Mat4 QD3D12_ProjectionWithoutAutomaticJitter(const Mat4& projection);
struct RetiredResource
{
@@ -2189,7 +2190,9 @@ static bool QD3D12_UpdateCameraInfoFromCurrentMatrices()
if (g_gl.projStack.empty() || g_gl.modelStack.empty())
return false;
const float* glProjection = g_gl.projStack.back().m;
const Mat4 unjitteredProjection = QD3D12_ProjectionWithoutAutomaticJitter(g_gl.projStack.back());
const float* glProjection = unjitteredProjection.m;
const float* rasterProjection = g_gl.projStack.back().m;
const float* modelView = g_gl.modelStack.back().m;
const float* modelToWorld = g_gl.modelMatrix.m;
const UINT renderWidth = g_currentWindow ? g_currentWindow->renderWidth : 0;
@@ -2201,7 +2204,7 @@ static bool QD3D12_UpdateCameraInfoFromCurrentMatrices()
g_qd3d12AutoCamera.cachedRenderWidth == renderWidth &&
g_qd3d12AutoCamera.cachedRenderHeight == renderHeight &&
g_gl.cameraState.valid &&
memcmp(g_qd3d12AutoCamera.cachedProjection, glProjection, sizeof(g_qd3d12AutoCamera.cachedProjection)) == 0 &&
memcmp(g_qd3d12AutoCamera.cachedProjection, rasterProjection, sizeof(g_qd3d12AutoCamera.cachedProjection)) == 0 &&
memcmp(g_qd3d12AutoCamera.cachedModelView, modelView, sizeof(g_qd3d12AutoCamera.cachedModelView)) == 0 &&
memcmp(g_qd3d12AutoCamera.cachedModelToWorld, modelToWorld, sizeof(g_qd3d12AutoCamera.cachedModelToWorld)) == 0)
{
@@ -2331,7 +2334,7 @@ static bool QD3D12_UpdateCameraInfoFromCurrentMatrices()
g_qd3d12AutoCamera.cachedMotionHistoryReset = g_gl.motionHistoryReset;
g_qd3d12AutoCamera.cachedRenderWidth = renderWidth;
g_qd3d12AutoCamera.cachedRenderHeight = renderHeight;
QD3D12_MatrixCopy(g_qd3d12AutoCamera.cachedProjection, glProjection);
QD3D12_MatrixCopy(g_qd3d12AutoCamera.cachedProjection, rasterProjection);
QD3D12_MatrixCopy(g_qd3d12AutoCamera.cachedModelView, modelView);
QD3D12_MatrixCopy(g_qd3d12AutoCamera.cachedModelToWorld, modelToWorld);
return true;
@@ -8586,7 +8589,7 @@ static sl::Result QD3D12_SetStreamlineCommonConstants(sl::FrameToken& frameToken
memcpy(&consts.clipToCameraView, g_gl.cameraState.clipToView.m, sizeof(float) * 16);
memcpy(&consts.clipToPrevClip, g_gl.cameraState.clipToPrevClip.m, sizeof(float) * 16);
memcpy(&consts.prevClipToClip, g_gl.cameraState.prevClipToClip.m, sizeof(float) * 16);
consts.jitterOffset = { g_gl.jitterX, g_gl.jitterY };
consts.jitterOffset = { -g_gl.jitterX, -g_gl.jitterY };
// The G-buffer stores motion as previous UV minus current UV after removing
// projection jitter, so it is already in normalized screen space.
consts.mvecScale = { 1.0f, 1.0f };
@@ -8959,6 +8962,74 @@ static void QD3D12_ConfigureDLSSFrameGeneration(QD3D12Window&, void*) {}
static void QD3D12_LogDLSSFrameGenerationPresentResult() {}
#endif
static float QD3D12_Halton(uint32_t index, uint32_t base)
{
float result = 0.0f;
float fraction = 1.0f / (float)base;
while (index > 0u)
{
result += (float)(index % base) * fraction;
index /= base;
fraction /= (float)base;
}
return result;
}
static bool QD3D12_ShouldUseAutomaticProjectionJitter(const QD3D12Window& w)
{
return !w.isPbuffer &&
QD3D12_AllowsGamePresentationFeatures(w) &&
g_gl.upscalerBackend == QD3D12_UPSCALER_DLSS &&
QD3D12_WantsDLSSRayReconstruction();
}
static void QD3D12_UpdateAutomaticProjectionJitter(QD3D12Window& w)
{
if (!QD3D12_ShouldUseAutomaticProjectionJitter(w))
{
g_gl.jitterX = 0.0f;
g_gl.jitterY = 0.0f;
return;
}
const uint32_t jitterIndex = (uint32_t)(g_gl.frameSerial % 8u) + 1u;
constexpr float kRRMicroJitterPixels = 0.18f;
g_gl.jitterX = (QD3D12_Halton(jitterIndex, 2u) - 0.5f) * kRRMicroJitterPixels;
g_gl.jitterY = (QD3D12_Halton(jitterIndex, 3u) - 0.5f) * kRRMicroJitterPixels;
}
static void QD3D12_ApplyAutomaticProjectionJitter(Mat4& projection)
{
if (!g_currentWindow || g_gl.matrixMode != GL_PROJECTION)
return;
if (fabsf(g_gl.jitterX) <= 1.0e-6f && fabsf(g_gl.jitterY) <= 1.0e-6f)
return;
if (!QD3D12_IsPerspectiveProjectionCM(projection.m))
return;
const float renderWidth = (float)std::max<UINT>(1u, g_currentWindow->renderWidth);
const float renderHeight = (float)std::max<UINT>(1u, g_currentWindow->renderHeight);
projection.m[8] += (2.0f * g_gl.jitterX) / renderWidth;
projection.m[9] += (2.0f * g_gl.jitterY) / renderHeight;
}
static Mat4 QD3D12_ProjectionWithoutAutomaticJitter(const Mat4& projection)
{
Mat4 unjittered = projection;
if (!g_currentWindow)
return unjittered;
if (fabsf(g_gl.jitterX) <= 1.0e-6f && fabsf(g_gl.jitterY) <= 1.0e-6f)
return unjittered;
if (!QD3D12_IsPerspectiveProjectionCM(unjittered.m))
return unjittered;
const float renderWidth = (float)std::max<UINT>(1u, g_currentWindow->renderWidth);
const float renderHeight = (float)std::max<UINT>(1u, g_currentWindow->renderHeight);
unjittered.m[8] -= (2.0f * g_gl.jitterX) / renderWidth;
unjittered.m[9] -= (2.0f * g_gl.jitterY) / renderHeight;
return unjittered;
}
#if defined(QD3D12_ENABLE_FFX)
struct QD3D12FfxState
{
@@ -12348,6 +12419,7 @@ void QD3D12_BeginFrame()
g_gl.deferScreenSpaceParticlesToEndFrame = false;
g_gl.deferredScreenSpaceParticleTlas.Reset();
QD3D12_UpdateViewportState();
QD3D12_UpdateAutomaticProjectionJitter(w);
FrameResources& fr = w.frames[w.frameIndex];
QD3D12_CHECK(fr.cmdAlloc->Reset());
@@ -15656,6 +15728,7 @@ void APIENTRY glLoadMatrixf(const GLfloat* m)
auto& top = QD3D12_CurrentMatrixStack().back();
memcpy(top.m, m, sizeof(top.m));
QD3D12_ApplyAutomaticProjectionJitter(top);
}
void APIENTRY glGetIntegerv(GLenum pname, GLint* params)
@@ -15970,6 +16043,7 @@ void APIENTRY glFrustum(GLdouble left, GLdouble right,
auto& topMat = QD3D12_CurrentMatrixStack().back();
topMat = Mat4::Multiply(topMat, proj);
QD3D12_ApplyAutomaticProjectionJitter(topMat);
}
void APIENTRY glDepthFunc(GLenum func)
+18 -4
View File
@@ -3689,6 +3689,12 @@ float2 Rand2(inout uint rng)
return float2(Rand(rng), Rand(rng));
}
float2 RayReconstructionTemporalDisk(uint2 pixel, uint sampleIndex, float radius)
{
uint rng = InitRng(pixel, gFrameIndex, sampleIndex);
return ConcentricSampleDisk(Rand2(rng)) * radius;
}
float3 SampleCosineWorld(float3 N, inout uint rng)
{
float3 tangent, bitangent;
@@ -3953,6 +3959,8 @@ float3 PathTraceDirectPointLight(uint2 pixel, float3 worldPos, float3 N, float3
// Use a deterministic low-discrepancy pattern instead. With a single sample,
// use the light center so default point lights are hard-shadowed and stable.
float areaRadius = (sampleCount > 1u && Lgt.samples > 1u) ? max(GetPointLightMaxRadius(Lgt) * 0.03, 0.12) : 0.0;
if (gEnableDenoiser == 0u && Lgt.samples != 0u)
areaRadius = max(areaRadius, min(max(GetPointLightMaxRadius(Lgt) * 0.006, 0.04), 1.25));
float rand = Hash12((float2)pixel + worldPos.xy + float2(worldPos.z, centerDist));
float3 diffuseAccum = 0.0;
@@ -3962,7 +3970,9 @@ float3 PathTraceDirectPointLight(uint2 pixel, float3 worldPos, float3 N, float3
for (uint s = 0u; s < sampleCount; ++s)
{
float2 disk = float2(0.0, 0.0);
if (areaRadius > 0.0)
if (areaRadius > 0.0 && gEnableDenoiser == 0u)
disk = RayReconstructionTemporalDisk(pixel, 0xA11E00u + s, areaRadius);
else if (areaRadius > 0.0)
disk = ConcentricSampleDisk(Hammersley2D(s, sampleCount, rand)) * areaRadius;
float3 sampleLightPos = Lgt.position + tangent * disk.x + bitangent * disk.y;
@@ -4039,6 +4049,8 @@ float3 PathTraceDirectRectLight(uint2 pixel, float3 worldPos, float3 N, float3 V
for (uint s = 0u; s < sampleCount; ++s)
{
float2 uv = StableRectLightSampleUV(s, sampleCount);
if (gEnableDenoiser == 0u)
uv = clamp(uv + RayReconstructionTemporalDisk(pixel, 0xEC7000u + s, 0.18), -0.85, 0.85);
float3 sampleLightPos =
Lgt.position +
@@ -4178,10 +4190,12 @@ float3 EstimateSingleLightVolumetricScattering(uint2 pixel, float3 cameraPos, fl
stepCount = min(max(stepCount, 8u), 32u);
float stepLen = viewDist / (float)stepCount;
// Do not frame-jitter the march. This renderer has no temporal GI history,
// so varying the volume sample positions every frame creates visible sparkle.
// A centered deterministic slice is stable and the spatial denoiser can smooth it.
float jitter = 0.5;
if (gEnableDenoiser == 0u)
{
uint jitterRng = InitRng(pixel, gFrameIndex, 0x701u);
jitter = lerp(0.42, 0.58, Rand(jitterRng));
}
float density = EstimateVolumeDensityFromLight(Lgt);
float anisotropy = (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_SPOT) ? 0.55 : 0.35;