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:
+112
@@ -1,3 +1,115 @@
|
||||
# Status & next-session handoff — end of session 31 (2026-08-25)
|
||||
|
||||
## Session 31: the packed container, and the palette that buys 2.31 dB has a price
|
||||
|
||||
**Green light first and last: `./tools/bench/check.sh` was ALL GREEN before any
|
||||
of this (`tmp/check_s31_start.log`) and ALL GREEN after** — the same stages, plus
|
||||
one new one.
|
||||
|
||||
**FINDINGS 63.** Mostly **host arithmetic** over the Blu-ray's own frames; the
|
||||
one rendering check is **px68k's real `x68k/gvram.c`**, linked headless the way
|
||||
`tools/bench/c68k` links its CPU core. No MAME run was needed and no real
|
||||
hardware ran. 47.4 — does buffer mode blank the layer — is still the board
|
||||
question, and this session made it **bigger**.
|
||||
|
||||
**THE HEADLINE. ROADMAP K2 is done.** `tools/encoder/dlxp.py` is the format,
|
||||
`pack.py` the encoder, `tools/analysis/30_packed_container.py` the gate and the
|
||||
re-derivation, `tools/bench/gvpack/verify_dlxp.py` the second-emulator render.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| record | **49,664 B = 97 sectors EXACTLY**, 512 B palette + 49,152 B picture |
|
||||
| index | **none, and none can be needed** — a packed record's length is geometry |
|
||||
| wire | **582.0 KB/s**, exactly what 61.9 predicted |
|
||||
| encode | **3.3 s** for 120 frames, against ~55 s for the codec |
|
||||
| render | **index-exact on px68k, with the harness computing no interleave** |
|
||||
|
||||
**1. THE RE-DERIVATION SESSION 30 ASKED FOR, and it had TWO debts not one**
|
||||
(63.3). 61.9's 34.08 dB was PIL's free 256-colour MEDIANCUT; the unnamed second
|
||||
debt was that **every PSNR this project has quoted — 29.19, 31.33, 34.08 — is
|
||||
RGB888, upstream of the `GGGGGRRRRRBBBBBI` word (23.3)**, and a packed record
|
||||
carries that word and nothing else.
|
||||
|
||||
| | RGB888 | **GRB555** |
|
||||
|---|---:|---:|
|
||||
| CODEC, the gate container (440.4 KB/s) | 29.07 | **28.72** |
|
||||
| 256c scene palette — the codec's CEILING | 31.32 | **30.79** |
|
||||
| PACKED, 254c SCENE palette *(control)* | 31.32 | **30.79** |
|
||||
| **PACKED CONTAINER, 254c PER-FRAME** | **34.05** | **33.10** |
|
||||
|
||||
61.9 survives the real builder: **34.05 against 34.08**. The layout's two
|
||||
reserved entries cost **+0.0003 dB**. The GRB555 word costs 0.53 dB and costs
|
||||
every row, so it moves no comparison.
|
||||
|
||||
**2. THE CONTROL IS THE FINDING UNDER THE HEADLINE.** A packed container with a
|
||||
SCENE palette lands **exactly on the codec's ceiling**, as it must. So the whole
|
||||
+2.31 dB the packed branch has over that ceiling is **the per-frame palette and
|
||||
nothing else** — not the packing, not literal frames, not 254 colours.
|
||||
|
||||
**3. 62.5 IS PRICED, AND THE ORDER IS A WASH** (63.4). Palette first 20.32 dB,
|
||||
palette last 20.33 — 0.01 apart, so the choice can be made on other grounds, and
|
||||
`dlxp.py` makes it a container flag rather than an assumption.
|
||||
|
||||
**4. AND THE MISMATCH IS NOT A WASH.** **231 of 256 palette entries change every
|
||||
frame (90%)**, and a picture under the neighbouring frame's palette is **12.8 dB
|
||||
worse**. It is a wipe, not a flash, and the transfer is 55.2% of a frame slot —
|
||||
so at 12 fps a colour-scrambled region is on screen for roughly half of every
|
||||
slot, forever. **B2 stopped being a headroom question and became a picture
|
||||
question again**: if buffer mode does not blank, it decides not just which
|
||||
player exists (61.6) but **which packed container** does. The codec never had
|
||||
this exposure — its palette is scene-constant, so its tear is old picture
|
||||
against new picture, never old colours against new ones.
|
||||
|
||||
**5. THE FALLBACK IS ALREADY IN THE ENCODER.** `pack.py --scene-palette
|
||||
--no-palette`: **30.79 dB, zero churn, nothing to mismatch, +2.07 dB on the
|
||||
shipping codec as the display renders both, and 576.0 KB/s instead of 582.0.**
|
||||
The per-frame palette is now a **priced** +2.31 dB rather than a free one.
|
||||
|
||||
## HANDOFF — start here
|
||||
|
||||
**THE TREE IS ALL GREEN.** Session 31's work is a commit of its own.
|
||||
|
||||
### The work, in the order it should be done
|
||||
|
||||
**1. K3 — END TO END, OFF THE DISC.** Scene setup — palette, page-1 X-scroll
|
||||
384, priority `vc1 = 0x0002`, R20 bit 11, the 384 zeroed words a row and the
|
||||
letterbox rows — then **one chained DMA a frame** out of a DLXP1 record, with
|
||||
the palette as the 193rd or 1st array entry (63.4 says either). Gate it
|
||||
pixel-exact over the same 120-frame window the decoder is gated on. The
|
||||
container is `tmp/packed_singe.dlxp` and `check.sh` rebuilds it every run.
|
||||
|
||||
**2. RUN BOTH PALETTE ORDERS while K3 is being built.** It is a flag
|
||||
(`--palette-last`), the containers already exist, and 63.4 bounds the cost but
|
||||
cannot decide it — dB over a whole frame is not what an eye sees in a wipe.
|
||||
|
||||
### What is PARKED, so it is not re-derived
|
||||
|
||||
**E7, E4 and C1** (61.8), and **P4a's wiring** for the video path. Do not delete
|
||||
any of it — B2 is unanswered and 48.1's prior leans against packing.
|
||||
|
||||
### Risks that are OURS, not hardware
|
||||
|
||||
1. **A chained transfer has never run back to back at 12 fps.** Unchanged since
|
||||
session 30: run 9 was seven entries, once. A frame is 193 and a second is
|
||||
twelve frames.
|
||||
2. ~~34.08 dB is PIL's MEDIANCUT~~ **CLOSED, 63.3: 34.05 dB against this
|
||||
project's own builder, and the hardware word is charged on top.**
|
||||
3. **The packed player deletes `ring.i`, `xfer.i` and most of `stream.s` from
|
||||
the video path.** A simplification that large usually hides something.
|
||||
4. **B2 sits over all of it, and 63.4 raised what it decides.** B4 still sits
|
||||
over the palette entry specifically, with a small blast radius.
|
||||
|
||||
### Reproducing this session
|
||||
|
||||
./tools/bench/check.sh # ALL GREEN
|
||||
python3 tools/encoder/pack.py tmp/fr_singe tmp/packed_singe.dlxp --nframes 120
|
||||
python3 tools/analysis/30_packed_container.py tmp/packed_singe.dlxp
|
||||
python3 tools/bench/gvpack/verify_dlxp.py tmp/packed_singe.dlxp 0 --controls
|
||||
|
||||
**WHAT IS NEXT.** K3: end to end, off the disc.
|
||||
|
||||
---
|
||||
|
||||
# Status & next-session handoff — end of session 30 (2026-08-25)
|
||||
|
||||
## Session 30: one channel start paints a whole frame, and the palette has no handler to be wrong about
|
||||
|
||||
Reference in New Issue
Block a user