From 8c0635242b9023c96724354cff59f7cedbc1d405 Mon Sep 17 00:00:00 2001 From: itchyfeetleech <169226071+itchyfeetleech@users.noreply.github.com> Date: Mon, 19 Oct 2026 19:31:40 +1300 Subject: [PATCH] engine: SR reconstruction samples the history once when both candidates have one vector post_m.comp (presets M and L) blends the history along the two most likely of three motion candidates and sampled it once per candidate (five bilinear taps; L also expands each sample to linear light). Where the motion is uniform the two candidates usually have the same vector: the same position, so the same bicubic sample or the same fallback colour (a NaN vector never compares equal, +0 and -0 give the same position). That case now takes the first candidate's value. Every output frame byte-identical (M and L, HDR and LDR; 1280x720 -> 1440p, 1114x626 -> 1080p for the compact tile tier, 1920x1080 -> 1440p for the 15x15 tier, the stress inputs), and so is everything D4R_M_DUMP writes, the history, half history and packed image included. Taking 0.99 times the first sample there instead changes every frame of every input, the reset frames too. Only post_m*.spv / post_l*.spv change. RX 9070 XT, d4r-m 720p -> 1440p, interleaved, reconstruction stage: uniform motion 325 -> 288 us (L 401 -> 354), compact tier at 1114x626 -> 1080p 219 -> 193 (L 275 -> 244); random motion unchanged (377 -> 374, L 404 -> 401). Frame 1570 -> 1541 us (L 2299 -> 2249) on the uniform-motion frames. Signed-off-by: itchyfeetleech <169226071+itchyfeetleech@users.noreply.github.com> --- engine/post_m.comp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/engine/post_m.comp b/engine/post_m.comp index ab02dc6..f0243e2 100644 --- a/engine/post_m.comp +++ b/engine/post_m.comp @@ -214,6 +214,9 @@ void main() // the fallback colour is only fetched where the history sample leaves the image or the history was reset: // the value is the same either way, so computing it in the branch saves the fetch on ordinary pixels const bool out1 = q1.x < 0.0 || q1.y < 0.0 || q1.x > size.x || q1.y > size.y || p.reset != 0u; + // the second candidate often has the first one's vector (where the motion is uniform): the same position, + // so the first one's sample or fallback (a NaN is never equal, and +0 and -0 give the same position) + const bool same = all(equal(second.xy, big.xy)); #if PRESET_L // linear light: a sample's expansion (when the previous exposure is positive) or the colour texel const vec3 h1 = out1 ? texelFetch(colorTex, colourTexel(near), 0).rgb : historyLinear(q1, p.expoPrev == 0.0 ? 1.0 : p.expoPrev); @@ -221,7 +224,9 @@ void main() if (b2 > 0.0) { const vec2 q2 = centre + second.xy; - if (q2.x < 0.0 || q2.y < 0.0 || q2.x > size.x || q2.y > size.y || p.reset != 0u) + if (same) + h2 = h1; + else if (q2.x < 0.0 || q2.y < 0.0 || q2.x > size.x || q2.y > size.y || p.reset != 0u) h2 = out1 ? h1 : texelFetch(colorTex, colourTexel(near), 0).rgb; else h2 = historyLinear(q2, p.expoPrev == 0.0 ? 1.0 : p.expoPrev); } @@ -232,7 +237,9 @@ void main() if (b2 > 0.0) { const vec2 q2 = centre + second.xy; - if (q2.x < 0.0 || q2.y < 0.0 || q2.x > size.x || q2.y > size.y || p.reset != 0u) + if (same) + h2 = h1; + else if (q2.x < 0.0 || q2.y < 0.0 || q2.x > size.x || q2.y > size.y || p.reset != 0u) h2 = out1 ? h1 : compress(e2 * texelFetch(colorTex, colourTexel(near), 0).rgb); else h2 = history(q2); }