From 2003cc8aaa31b4b64463f9129853f775bbd53741 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sun, 8 Mar 2026 14:50:14 -0700 Subject: [PATCH] FSR2: de-jitter scene sampling, fix loading screen progress FSR2 temporal upscaling: - De-jitter scene color sampling (outUV - jitterUV) for frame-to-frame consistency, eliminating the primary source of temporal jitter - Remove luminance instability dampening (was causing excessive blur) - Simplify to uniform 8% blend (de-jittered values are consistent) - Gamma 2.0 for moderate neighborhood clamping - Motion vector dead zone: zero sub-0.01px motion from float precision noise Loading screen: - Reduce tile load radius from 3 to 2 (25 tiles) for faster loading - Process one tile per iteration for smooth progress bar updates --- assets/shaders/fsr2_accumulate.comp.glsl | 64 +++++++++-------------- assets/shaders/fsr2_accumulate.comp.spv | Bin 19720 -> 18288 bytes src/core/application.cpp | 10 ++-- 3 files changed, 29 insertions(+), 45 deletions(-) diff --git a/assets/shaders/fsr2_accumulate.comp.glsl b/assets/shaders/fsr2_accumulate.comp.glsl index 9684b7c3..756945f0 100644 --- a/assets/shaders/fsr2_accumulate.comp.glsl +++ b/assets/shaders/fsr2_accumulate.comp.glsl @@ -81,8 +81,15 @@ void main() { vec2 outUV = (vec2(outPixel) + 0.5) * pc.displaySize.zw; - // Bicubic upsampling with anti-ringing: sharp without edge halos - vec3 currentColor = sampleBicubic(sceneColor, outUV, pc.internalSize.xy); + // De-jitter: the scene was rendered with sub-pixel jitter, effectively + // shifting the internal image by jitterUV. Sampling at (outUV - jitterUV) + // undoes this shift, reconstructing the scene at the output pixel's true + // unjittered position. This makes the sampled value consistent across + // frames, eliminating the primary source of temporal jitter. + vec2 jitterUV = pc.jitterOffset.xy * 0.5; + vec2 dejitteredUV = outUV - jitterUV; + + vec3 currentColor = sampleBicubic(sceneColor, dejitteredUV, pc.internalSize.xy); if (pc.params.x > 0.5) { imageStore(historyOutput, outPixel, vec4(currentColor, 1.0)); @@ -114,18 +121,18 @@ void main() { vec3 historyColor = texture(historyInput, historyUV).rgb; - // Neighborhood clamping in YCoCg space with wide gamma. - // Wide gamma (3.0) prevents jitter-chasing: the clamp box only catches - // truly stale history (disocclusion), not normal jitter variation. + // Neighborhood clamping in YCoCg space at de-jittered positions. + // De-jittered neighborhood is stable across frames, preventing + // the clamp box from chasing jitter. vec3 s0 = rgbToYCoCg(currentColor); - vec3 s1 = rgbToYCoCg(texture(sceneColor, outUV + vec2(-texelSize.x, 0.0)).rgb); - vec3 s2 = rgbToYCoCg(texture(sceneColor, outUV + vec2( texelSize.x, 0.0)).rgb); - vec3 s3 = rgbToYCoCg(texture(sceneColor, outUV + vec2(0.0, -texelSize.y)).rgb); - vec3 s4 = rgbToYCoCg(texture(sceneColor, outUV + vec2(0.0, texelSize.y)).rgb); - vec3 s5 = rgbToYCoCg(texture(sceneColor, outUV + vec2(-texelSize.x, -texelSize.y)).rgb); - vec3 s6 = rgbToYCoCg(texture(sceneColor, outUV + vec2( texelSize.x, -texelSize.y)).rgb); - vec3 s7 = rgbToYCoCg(texture(sceneColor, outUV + vec2(-texelSize.x, texelSize.y)).rgb); - vec3 s8 = rgbToYCoCg(texture(sceneColor, outUV + vec2( texelSize.x, texelSize.y)).rgb); + vec3 s1 = rgbToYCoCg(texture(sceneColor, dejitteredUV + vec2(-texelSize.x, 0.0)).rgb); + vec3 s2 = rgbToYCoCg(texture(sceneColor, dejitteredUV + vec2( texelSize.x, 0.0)).rgb); + vec3 s3 = rgbToYCoCg(texture(sceneColor, dejitteredUV + vec2(0.0, -texelSize.y)).rgb); + vec3 s4 = rgbToYCoCg(texture(sceneColor, dejitteredUV + vec2(0.0, texelSize.y)).rgb); + vec3 s5 = rgbToYCoCg(texture(sceneColor, dejitteredUV + vec2(-texelSize.x, -texelSize.y)).rgb); + vec3 s6 = rgbToYCoCg(texture(sceneColor, dejitteredUV + vec2( texelSize.x, -texelSize.y)).rgb); + vec3 s7 = rgbToYCoCg(texture(sceneColor, dejitteredUV + vec2(-texelSize.x, texelSize.y)).rgb); + vec3 s8 = rgbToYCoCg(texture(sceneColor, dejitteredUV + vec2( texelSize.x, texelSize.y)).rgb); vec3 m1 = s0 + s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8; vec3 m2 = s0*s0 + s1*s1 + s2*s2 + s3*s3 + s4*s4 + s5*s5 + s6*s6 + s7*s7 + s8*s8; @@ -133,10 +140,7 @@ void main() { vec3 variance = max(m2 / 9.0 - mean * mean, vec3(0.0)); vec3 stddev = sqrt(variance); - // Tighter clamp (gamma 1.5) catches slightly misaligned history that - // causes doubling. With jitter-aware blending providing stability, - // the clamp can be tight without causing jitter-chasing. - float gamma = 1.5; + float gamma = 2.0; vec3 boxMin = mean - gamma * stddev; vec3 boxMax = mean + gamma * stddev; @@ -146,28 +150,10 @@ void main() { float clampDist = length(historyYCoCg - clampedHistory); - // Jitter-aware sample weighting: compute how close the current frame's - // jittered sample fell to this output pixel. Close samples are high quality - // (blend aggressively for fast convergence), distant samples are low quality - // (blend minimally to avoid visible jitter). - vec2 jitterPx = pc.jitterOffset.xy * 0.5 * pc.internalSize.xy; - vec2 internalPos = outUV * pc.internalSize.xy; - vec2 subPixelOffset = fract(internalPos) - 0.5; - vec2 sampleDelta = subPixelOffset - jitterPx; - float dist2 = dot(sampleDelta, sampleDelta); - float sampleQuality = exp(-dist2 * 3.0); - float baseBlend = mix(0.02, 0.20, sampleQuality); - - // Luminance instability: when current frame differs significantly from - // history, it may be aliased/flickering content. Reduce blend to prevent - // oscillation, especially for small distant features. - float lumCurrent = dot(currentColor, vec3(0.299, 0.587, 0.114)); - float lumHistory = dot(historyColor, vec3(0.299, 0.587, 0.114)); - float lumDelta = abs(lumCurrent - lumHistory) / max(max(lumCurrent, lumHistory), 0.01); - float stability = 1.0 - clamp(lumDelta * 3.0, 0.0, 0.7); - baseBlend *= stability; - - float blendFactor = baseBlend; + // With de-jittered sampling, the reconstructed value is consistent across + // frames, so a uniform blend rate works without causing visible jitter. + // 8% gives ~28 frames for 90% convergence (~0.5s at 60fps). + float blendFactor = 0.08; // Disocclusion: large clamp distance → rapidly replace stale history blendFactor = mix(blendFactor, 0.60, clamp(clampDist * 5.0, 0.0, 1.0)); diff --git a/assets/shaders/fsr2_accumulate.comp.spv b/assets/shaders/fsr2_accumulate.comp.spv index 640fab16dcae1df79e369f65c1959a9d2e37e5ff..2ea3acb515e67eced0532dde96d59d0168498a15 100644 GIT binary patch literal 18288 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 diff --git a/src/core/application.cpp b/src/core/application.cpp index f9ac557c..d6ae3065 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -4042,7 +4042,7 @@ void Application::loadOnlineWorldTerrain(uint32_t mapId, float x, float y, float // then restore the full radius after entering the game. // This matches WoW's behavior: load quickly, stream the rest in-game. const int savedLoadRadius = 4; - terrainMgr->setLoadRadius(1); + terrainMgr->setLoadRadius(2); // 5x5=25 tiles — balance between spawn hitches and load time terrainMgr->setUnloadRadius(7); // Trigger tile streaming for surrounding area @@ -4080,11 +4080,9 @@ void Application::loadOnlineWorldTerrain(uint32_t mapId, float x, float y, float // Trigger new streaming — enqueue tiles for background workers terrainMgr->update(*camera, 0.016f); - // Process ALL available ready tiles per iteration — batches GPU - // uploads into a single command buffer + fence wait instead of - // one fence per tile. Loading screen still updates between - // iterations while workers parse more tiles. - terrainMgr->processAllReadyTiles(); + // Process ONE tile per iteration so the progress bar updates + // smoothly between tiles instead of stalling on large batches. + terrainMgr->processOneReadyTile(); int remaining = terrainMgr->getRemainingTileCount(); int loaded = terrainMgr->getLoadedTileCount();