fix(video): hi-res epoch trusted window 2 -> 16 frames (#338 Stage 2)

The design's now/now-1 trusted window assumed the 3D view is redrawn
every presented frame.  Doom -- the Stage 2 anchor -- runs its engine
at ~10-15Hz double-buffered, so displayed blocks are routinely 3-8
presented frames old and the 2-frame window rejected every one of
them: measured 0.00% supersampled output on E1M1 gameplay, vs 8.68%
with the window widened.  16 frames (~267ms NTSC) covers slow engines
while still bounding the R3 stale-structure class (a false positive
needs a value-coincident dead tag AND 16 frames without a rewrite,
and self-heals at the next real write).  Window comparison measured:
2 -> 0.00%, 16 -> 8.68%, infinity -> 8.68% (16 saturates the benefit).

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
Joseph MattielloandClaude Fable 5 committed 2026-08-08 22:17:04 -04:00
1 parent 1df162dd40
commit 404cb118cd
2 files changed
+19 -5

No files matched your search

+14 -2
View File
@@ -132,9 +132,21 @@ void ShadowFBShutdown(void)
/* Tag layout (design section 3.4):
* bits 0-15 value16 (the stock word the block was derived from)
* bit 16 VALID (so zero-init never false-matches RAM zeros)
* bits 17-24 frame epoch (entry trusted iff epoch == now or now-1)
* bits 17-24 frame epoch (entry trusted iff written within the last
* HIRES_EPOCH_WINDOW presented frames)
*/
#define HIRES_TAG_EPOCH_SHIFT 17
/* Trusted-window size in presented frames (design section 3.4 / R3).
* The design's initial now/now-1 guess assumed the 3D view is redrawn
* every frame; Doom -- the Stage 2 anchor title -- runs its engine at
* ~10-15Hz double-buffered, so the displayed buffer's blocks are
* routinely 3-8 presented frames old and a 2-frame window rejected
* every one of them (measured: 0.00% supersampled output). 16 frames
* (~267ms NTSC) covers slow engines while still bounding the R3
* stale-structure class: a false positive needs RAM to coincidentally
* equal a dead tag AND the word untouched for 16 frames, and it still
* self-heals at the next real write. */
#define HIRES_EPOCH_WINDOW 16
int shadowHiresActive = 0;
int shadowHiresN = 1;
@@ -268,7 +280,7 @@ static const shadowfb_sub *shadow_hires_block(uint32_t addr, uint16_t current16)
if ((tag & 0x1FFFF) != ((uint32_t)current16 | SHADOWFB_TAG_VALID))
return NULL;
ep = (tag >> HIRES_TAG_EPOCH_SHIFT) & 0xFF;
if (ep != hiresEpoch && ep != ((hiresEpoch - 1) & 0xFF))
if (((hiresEpoch - ep) & 0xFF) >= HIRES_EPOCH_WINDOW)
return NULL;
return hiresPageSub[page]
+ word * (uint32_t)shadowHiresN * (uint32_t)shadowHiresN;
+5 -3
View File
@@ -86,9 +86,11 @@ void ShadowFBLineFromRAM(int idx, uint32_t srcAddr, uint16_t value16);
*
* Coherence is the same value-check-at-read scheme as the 1x shadow,
* PLUS a frame-epoch field in the tag (design section 3.4): an entry
* is only trusted when its epoch is the current or previous frame,
* which bounds the stale-structure artifact class that the 1x
* bounded-error argument does not cover at Nx (design section 5.4).
* is only trusted when it was written within the last few presented
* frames (HIRES_EPOCH_WINDOW in shadowfb.c -- sized for slow
* double-buffered engines like Doom's ~15Hz renderer), which bounds
* the stale-structure artifact class that the 1x bounded-error
* argument does not cover at Nx (design section 5.4).
*
* Stage 1 semantics: every writer stores the stock pixel replicated
* N*N times (box replication), so output is bit-exactly the Nx box