From 3ee973a483759ca1352f1289ba415e91ffedd50b Mon Sep 17 00:00:00 2001 From: DoubleGate Date: Sun, 2 Aug 2026 03:37:16 -0400 Subject: [PATCH] docs: interlace needs a rule `extract` cannot express, and Mesen2 is no longer flaky Probed and withdrawn. Two findings. GOOD NEWS: the recorded Mesen2 nondeterminism is gone. Three consecutive Mesen2 runs and two snes9x runs of an interlace scene produced identical results, so "Mesen2 alternates between both field parities run-to-run on one build" no longer reproduces -- almost certainly the emu.setInput defect fixed earlier. "Run any field-dependent capture twice per host" can be retired as the REASON interlace is blocked. THE BLOCKER: the two hosts emit different WIDTHS for the same interlace scene. snes9x renders a single field at 256x224; Mesen2 doubles both dimensions to 512x478. Hi-res worked because both emitted 512 wide and differed only in height, which HiResEven absorbs by deriving the row step from the observed height. Here no single (xstep, ystep) pair describes both reductions, so `extract` as designed -- one declared rule per scene, every host applying the same reduction -- is insufficient. ADR 0013 records the three options and argues against the tempting one: a generic "downsample whatever you get to 256x224" rule would subsume HiResEven but give up the declared-not-inferred property, and would then silently accept a 256-wide frame for a scene that must be 512. If interlace is worth covering, a per-host reduction table is the honest shape. Docs only; the probe scene is not in the tree. Co-Authored-By: Claude Opus 5 (1M context) --- CHANGELOG.md | 18 +++++++++ .../0013-accuracysnes-framebuffer-oracle.md | 40 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab1b98a2..68420729 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- **Interlace scenes: the recorded Mesen2 nondeterminism is GONE, but interlace is blocked for a + different reason.** Probed and withdrawn. Three consecutive Mesen2 runs and two snes9x runs of an + interlace scene produced identical results, so the standing note that "Mesen2 alternates between + both field parities run-to-run on one build" no longer reproduces — that was almost certainly the + `emu.setInput` defect fixed earlier, and "run any field-dependent capture twice per host" can be + retired as the *reason* interlace is blocked. + + The actual blocker is that the two hosts emit different **widths** for the same interlace scene: + snes9x `256x224`, Mesen2 `512x478`. Hi-res worked because both emitted 512 wide and differed only + in height, which `HiResEven` absorbs by deriving the row step from the observed height. Here no + single `(xstep, ystep)` pair describes both reductions, so `extract` as designed — one declared + rule per scene, every host applying the same reduction — is insufficient. + + `docs/adr/0013` records the three options and argues against the tempting one: a generic + "downsample whatever you get to 256x224" rule would subsume `HiResEven` but give up the + declared-not-inferred property, and would then silently accept a 256-wide frame for a scene that + must be 512. + - **`C10.04` attempted and withdrawn: it is blocked by the same exclusion that unblocked `C5.15`.** A Mode 5 scene with `MOSAIC = $01` was written against the control the working rules require — the same canvas with mosaic off — and hashed **identically to it on both references** diff --git a/docs/adr/0013-accuracysnes-framebuffer-oracle.md b/docs/adr/0013-accuracysnes-framebuffer-oracle.md index ff438d48..940e727f 100644 --- a/docs/adr/0013-accuracysnes-framebuffer-oracle.md +++ b/docs/adr/0013-accuracysnes-framebuffer-oracle.md @@ -265,3 +265,43 @@ a state no picture can show; an unshowable scene hashes stably and every emulato The control scene is what caught it, and its doc comment said in advance what an equal hash would mean. **Write the control first; it is the only thing that distinguishes "the references agree" from "nothing happened".** + +### Interlace needs a rule the current design cannot express + +Probed 2026-08-02, scene withdrawn. Two findings, and the second is the blocker. + +**The good news: Mesen2 is no longer nondeterministic here.** The earlier note that "Mesen2 +alternates between both field parities run-to-run on one build" no longer reproduces — three +consecutive runs of an interlace scene produced the identical result, as did two of snes9x. That +nondeterminism was almost certainly the `emu.setInput` defect fixed earlier, and the standing advice +to "run any field-dependent capture twice per host" can be retired as the *reason* interlace is +blocked. It is blocked for a different reason. + +**The blocker: the two hosts emit different WIDTHS for the same interlace scene.** + +| scene kind | snes9x | Mesen2 | +|---|---|---| +| ordinary | `256x224` | `256x239` | +| Mode 5 hi-res | `512x224` | `512x478` | +| **interlace** | **`256x224`** | **`512x478`** | + +Hi-res worked because both hosts emitted **512 wide** and differed only in height, which `HiResEven` +absorbs by deriving the row step from the observed height. Interlace does not: snes9x renders a +single field at 256 wide while Mesen2 doubles both dimensions. `HiResEven`'s `width == 512` assertion +cannot hold for both, and no single `(xstep, ystep)` pair describes both reductions. + +So `extract` as designed — one declared rule per scene, each host applying the same reduction — is +insufficient for interlace. The options, none of them free: + +1. A `Downsample` rule that reduces *whatever* the host emits to 256x224 by deriving both steps from + the observed geometry. General, and it subsumes `HiResEven` — but it gives up the + **declared-not-inferred** property that is the whole reason `extract` catches a core rendering + 256 wide when it should render 512. +2. A per-host reduction table, so a rule can say "snes9x: as-is; Mesen2: halve both". Keeps the + declaration honest at the cost of the hosts no longer being interchangeable. +3. Declare the expected geometry per rule *and* per host, and reject anything else — the strictest, + and the most to maintain. + +Option 1 is the tempting one and should be resisted on its own: it would make the hi-res case +silently accept a 256-wide frame for a scene that must be 512. If interlace is worth covering, option +2 is the honest shape.