Align the container to the disc, and find the decoder-free packed player fits
Two sessions, unrecorded until now, committed together because their edits share files and cannot be split cleanly after the fact. Session 28 (FINDINGS 60): the container is DLX5 -- every record sector-aligned, 120/120 starting on a boundary where 3/120 did, +0.48% on the wire and zero clocks -- and the ring's release rounds to RECALN so no pad is stranded. Two encoder levers measured and refused: `--spans all` buys +0.19 dB for +67% of the wire, and joint span/lam selection emits byte-identical containers because `lam` never leaves its floor on any of 120 frames. Session 29 (FINDINGS 61): the packed full-frame blit is 27.3% of a 12 fps frame, a channel fills GVRAM in buffer mode off the disc with the CPU halted, and it walks the 1,024 B line stride itself through array chaining. At the 9 clk/B dual-address floor the codec is 110.4% of a frame and a decoder-free packed literal player is 55.2%, at +4.89 dB -- 2.75 dB past a ceiling the codec's scene-wide palette cannot cross. Encoder work is parked; the codec is kept and not built on. check.sh is ALL GREEN before and after, plus one new stage that gates the ORDER of the measured paint costs rather than their values. Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
This commit is contained in:
@@ -189,19 +189,56 @@ payload to **328** — which is an encoder target, entirely inside this project,
|
||||
and measured against the heaviest container the encoder emits rather than
|
||||
against a shipping one (FINDINGS 59.7).
|
||||
|
||||
**A record is not a sector, and the cheapest fix is a re-encode.** 117 of 120
|
||||
records start part way into a 512 B block, and reading whole blocks into the
|
||||
ring corrupts the neighbouring records rather than merely wasting bytes — the
|
||||
block loop reads with no bounds check. PIO absorbs this for free by simply not
|
||||
storing the bytes outside the window, which is a property that disappears the
|
||||
**A record was not a sector, and the fix was a re-encode — it is done.** 117 of
|
||||
120 records used to start part way into a 512 B block, and reading whole blocks
|
||||
into the ring corrupts the neighbouring records rather than merely wasting bytes
|
||||
— the block loop reads with no bounds check. PIO absorbed this for free by
|
||||
simply not storing the bytes outside the window, a property that disappears the
|
||||
moment a DMA channel takes over. Priced three ways: windowed PIO is +1.34% on
|
||||
the wire and cannot be done by a channel at all; a bounce buffer is +1.34% and
|
||||
**+5 clocks on every delivered byte**, 22.4% of a frame; sector-aligning records
|
||||
in the container is **+0.43% and zero clocks**. The last wins on both axes and
|
||||
joins the re-encode bundle (FINDINGS 58.3). **Session 27 made it a
|
||||
precondition rather than a preference**: the transport now *refuses* a windowed
|
||||
read when the data phase is the channel's, so the container has to meet it
|
||||
before the DMAC can sit behind the ring (FINDINGS 59.4).
|
||||
in the container is **+0.43% and zero clocks** (FINDINGS 58.3). Session 27 made
|
||||
it a precondition rather than a preference — the transport *refuses* a windowed
|
||||
read when the data phase is the channel's (59.4) — and **session 28 met it: the
|
||||
container is DLX5, every record is padded to 512 B and the frame stream starts
|
||||
on a sector boundary. 120 of 120 records are aligned, the realised wire cost is
|
||||
+0.48%, and the disc now moves exactly the records** — the bytes off the disc
|
||||
and the bytes into the ring are the same number, which is what check.sh gates on
|
||||
(FINDINGS 60.1).
|
||||
|
||||
**And the player that has no decoder at all fits the budget the codec misses.**
|
||||
256-colour GVRAM throws away the high byte of every word a CPU writes, so a
|
||||
picture byte normally costs two disc bytes — but CRTC R20 bit 11 turns the
|
||||
masking off, and with the two 256-colour pages scrolled apart one word carries
|
||||
two pixels (FINDINGS 46/47). Session 29 measured what that is worth. The packed
|
||||
full-frame blit is **227,553 clocks, 27.3% of a 12 fps frame** — 51% of the
|
||||
unpacked one, and the *same* as the unpacked path's write-only floor, so packing
|
||||
buys back the whole of the source read. A DMA channel fills GVRAM in buffer mode
|
||||
straight off the disc with the CPU halted, and **walks the 1,024-byte line stride
|
||||
itself** through array chaining, so a frame is one channel start and not 192. At
|
||||
the 9 clk/B dual-address floor — the only configuration this machine can be shown
|
||||
to run — **the shipping codec is 110.4% of a frame and a decoder-free packed
|
||||
player is 55.2%.** Decoding 37,585 bytes costs more than not decoding 49,152.
|
||||
|
||||
What it costs is the wire: **576 KB/s, fixed, with no lever** — a codec's bitrate
|
||||
is adjustable and a literal frame's is geometry — against 327 KB/s for the codec
|
||||
at the same floor. So the two open hardware facts changed character: **whether
|
||||
the medium sustains 576 KB/s, and whether buffer mode blanks the layer while it
|
||||
is being written, now decide which player exists** rather than how much headroom
|
||||
one has. The codec cannot take the packing either way: writing 4×4 blocks a byte
|
||||
at a time is **28% dearer** than the shipping shape, and pairing the blocks 128
|
||||
columns apart to get the burst back drops SKIP from 66.3% of blocks to 46.1% of
|
||||
pairs — about **+60% on the bytes**, against a target that needs them 35% lower
|
||||
(FINDINGS 61).
|
||||
|
||||
**So encoder work is PARKED (USER DECISION, session 29).** Not because the codec
|
||||
is wrong, but because its remaining path is a conjunction and the packed one is
|
||||
not. The codec that exists is 440 KB/s and 110.4% of a frame; reaching E7's
|
||||
327 KB/s needs a 35% byte reduction after two of its three levers were measured
|
||||
and found inert (60.4, 60.5), and the reward on success is a design at ~100% of
|
||||
the frame. The packed player is at 55.2% today. **The codec is kept on disk and
|
||||
not built on**, because B2 is unanswered and 48.1's prior leans against packing —
|
||||
if buffer mode blanks, it is the only thing left (48.3).
|
||||
|
||||
**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,
|
||||
@@ -434,7 +471,11 @@ tools/encoder/ hybrid VQ encoder and DLX3 container writer.
|
||||
side. It must match blit.s and decode.s: 11 coarse units of
|
||||
24 px, 11 fine of 2.
|
||||
DLX2 4-byte-aligns every frame record, because an odd move.l
|
||||
is an ADDRESS ERROR on a 68000, not a slow read.
|
||||
is an ADDRESS ERROR on a 68000, not a slow read. DLX5 aligns
|
||||
them to 512 B sectors instead, so a DMA channel can read a
|
||||
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.
|
||||
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