Put the frame in a container with no decoder, and find the palette is not free
ROADMAP K2. DLXP1: a 49,664 B record that is 97 sectors exactly, no index and no length word, because a packed record's length is geometry rather than content. 582.0 KB/s, which is what FINDINGS 61.9 predicted to the tenth, and it encodes in 3.3 s because there is no k-means in it. px68k's own x68k/gvram.c renders the container's bytes index-exact with the harness computing no interleave -- the only test that can catch an encoder whose byte order is wrong, since a container round-trips against its own inverse either way. Both negative controls fail as they must. The picture is re-derived against this project's builder rather than PIL's (34.05 dB against 61.9's 34.08) and the GGGGGRRRRRBBBBBI word is charged for the first time in this tree: 0.53 dB, on every row, so it moves no comparison. What the control found is the finding. A packed container on a SCENE palette lands exactly on the codec's ceiling, so the whole +2.31 dB is the per-frame palette and nothing else -- and 231 of 256 entries change every frame, which makes a mismatched paint 12.8 dB worse than the correct pairing, on screen for roughly half of every frame slot if buffer mode does not blank. So B2 now decides which packed CONTAINER ships, not only which player. The fallback is already a flag: --scene-palette --no-palette is 30.79 dB, zero churn, 576.0 KB/s and still +2.07 dB on the shipping codec. 62.5 is priced and is a wash: palette first 20.32 dB, palette last 20.33. Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
This commit is contained in:
@@ -256,6 +256,42 @@ run bounds the model and not the hardware, and "does a real palette register
|
||||
take a byte write" joins the hardware list as B4. A negative answer costs 0.28%
|
||||
of a frame and nothing else (FINDINGS 62).
|
||||
|
||||
**The packed container exists, and the palette that makes it better than the
|
||||
codec is not free after all.** `tools/encoder/dlxp.py` is DLXP1 and `pack.py`
|
||||
writes it: a **49,664 byte record that is 97 sectors exactly**, no record index
|
||||
and no length word — a packed record's length is geometry, so record *i* is at
|
||||
`off + i*rec` and a seek is arithmetic — at **582.0 KB/s**, which is what
|
||||
FINDINGS 61.9 predicted to the tenth, encoded in **3.3 seconds** because there is
|
||||
no k-means in it. **px68k's own `gvram.c` renders the container's bytes
|
||||
index-exact with the harness computing no interleave**, which is the only test
|
||||
that can catch an encoder whose byte order is wrong: a container round-trips
|
||||
against its own inverse either way. And the picture is re-derived against this
|
||||
project's own quantiser rather than PIL's — **34.05 dB against 61.9's 34.08** —
|
||||
with the X68000's `GGGGGRRRRRBBBBBI` word charged for the first time in this
|
||||
tree, 0.53 dB, on every row, so it moves no comparison.
|
||||
|
||||
**What the control found is the finding.** A packed container built on a *scene*
|
||||
palette lands **exactly on the codec's ceiling, 30.79 dB**, so the whole +2.31 dB
|
||||
the packed branch has over that ceiling is **the per-frame palette and nothing
|
||||
else**. And a per-frame palette is not a small delta: **231 of 256 entries change
|
||||
every frame**, and a picture under the neighbouring frame's palette is **12.8 dB
|
||||
worse** than the correct pairing — a wipe on screen for roughly half of every
|
||||
frame slot, forever, *if* buffer mode does not blank the layer. Correct render,
|
||||
the same frame under the next frame's palette, and the 24-bit source; the frame
|
||||
is the one whose mismatch is closest to the mean, so it is not an outlier picked
|
||||
to make the point:
|
||||
|
||||

|
||||
|
||||
It is chroma speckle and a shifted ground rather than a scramble — two
|
||||
median-cut palettes of adjacent frames occupy a similar gamut — which is milder
|
||||
than 12.8 dB sounds and worse than a still can show, because a still does not
|
||||
show it arriving and leaving twelve times a second. So B2 stopped
|
||||
being a question about headroom and became one about **which packed container
|
||||
ships**. The fallback is already a flag: `--scene-palette --no-palette` is
|
||||
30.79 dB, zero churn, **576.0 KB/s**, and still +2.07 dB on the shipping codec as
|
||||
the display renders both (FINDINGS 63).
|
||||
|
||||
**The scene graph is in, and the worst gap between two decision points is
|
||||
zero.** `tools/import/scenegraph.py` imports the arcade scene graph — 40 scenes,
|
||||
516 sequences, 906 input windows — and 5.4% of the game's 612 branch transitions
|
||||
@@ -469,6 +505,15 @@ tools/bench/c68k/ headless px68k C68K harness, a SECOND emulator for every
|
||||
which MAME cannot report. The Makefile's -no-pie and the
|
||||
harness's MAP_32BIT arena are load-bearing: C68K truncates
|
||||
host pointers to 32 bits.
|
||||
tools/bench/gvpack/ the same second-emulator argument for the DISPLAY: it links
|
||||
px68k's real x68k/gvram.c, so the address decode, the R20
|
||||
bit-11 write path, the page-byte selection, the scroll wrap and
|
||||
the index-0 transparency test are px68k's own code.
|
||||
verify_gvpack.py checks the LAYOUT (the harness computes the
|
||||
interleave); verify_dlxp.py checks the CONTAINER, writing a
|
||||
DLXP1 record's bytes into GVRAM verbatim with no interleave
|
||||
computed anywhere, which is the only way to catch an encoder
|
||||
whose byte order is wrong.
|
||||
25 imports nothing itself: it reads the DLXSCENE1 scene
|
||||
table and reports the worst gap between two decision points,
|
||||
what the input layer has to survive, and what both cost in
|
||||
@@ -479,6 +524,11 @@ tools/import/ the ONLY code in this tree coupled to somebody else's source.
|
||||
project's own DLXSCENE1 schema, with the sources' licences and
|
||||
attribution inside it. Nothing is vendored and the output is
|
||||
gitignored derived data.
|
||||
30 is the PACKED container's gate: the format's invariants
|
||||
(round-trip, sectors, the transparency key, the palette word),
|
||||
and the quality re-derivation 61.9 asked for, in both the RGB888
|
||||
domain every encoder PSNR here is quoted in and the GRB555 one a
|
||||
player actually displays.
|
||||
tools/media/ builds docs/img/ from a paced recording run
|
||||
tools/vasm/ vasm m68k assembler, binary plus source tarball
|
||||
tools/encoder/ hybrid VQ encoder and DLX3 container writer.
|
||||
@@ -492,6 +542,12 @@ tools/encoder/ hybrid VQ encoder and DLX3 container writer.
|
||||
record as whole sectors straight into the ring with no window
|
||||
and no bounce copy; dlx.record_lengths() is the one place that
|
||||
rule is applied.
|
||||
dlxp.py and pack.py are the OTHER container -- DLXP1, the
|
||||
decoder-free packed one (FINDINGS 63). Nothing is shared with
|
||||
the codec's writer on purpose: a packed record is a palette and
|
||||
a picture, both geometry, and dlxp.py is the one place the
|
||||
interleave and the 97-sector record are stated. There is no
|
||||
rate control in pack.py because there is no rate lever.
|
||||
dlx.py is the reference DECODER, ground truth for the 68000.
|
||||
24 models the ring with the 68000 owning it: the request
|
||||
queue, the poll-only-when-not-decoding rule and 54.4's frame
|
||||
|
||||
Reference in New Issue
Block a user