Measure the span: the mode survives, and it is an encoder format
FINDINGS 29 priced a literal-span mode at 4*(50 + 4L*9.08) cycles and labelled
the whole section DERIVED. Session 8 step 0 was to measure it before optimising
over the mode set it implies. Two variants in blit.s, one stream per span length
from prep_spans.py, timed by span.lua, driven by span.sh in ~25 s:
v5, handed (x, npix) and left to work the copy out: 97.9/span + 10.459/px
v6, handed an address and a jump displacement: 43.7/span + 9.152/px
29 assumed 50.0/span + 9.080/px
So 29's arithmetic was right about a format nobody had written. The difference
is not tuning: v5 spends ~122 cycles a span computing a destination, dividing
npix into bursts and handling a 0..15 remainder, all of which the encoder knows
at build time. v6's record is {u32 absolute GVRAM address, u16 jump
displacement} into an unrolled chain of 24-pixel copy units -- no loop, no
remainder, no arithmetic -- and it fits 11 span lengths to 0.3%.
Three things that measurement showed and derivation could not:
- The per-pixel cost is a function of REGISTER PRESSURE. FINDINGS 24's 9.08
was a fixed blit with 12 registers free; v5 can spare 8 and pays 10.46; v6
gets 12 back only because the encoder holds the state.
- Short spans die in the remainder path -- a 12-pixel span costs MORE than a
16-pixel one -- and the fix is padding, not avoidance.
- Odd-x alignment is free (259.0 vs 261.8 cycles/span), as a 16-bit bus
implies but nobody had checked.
Re-priced against the unchanged mode maps, sasi: median 74.4% -> 52.0% (29 said
43.0), misses 37 -> 10/120 (29 said 8), 448.0 KB/s. Break-even moved from runs
of 2 blocks to runs of 4. 29.4 survives: a scene cut needs x >= 0.196 of the
frame as spans and the bus allows x <= 0.373, so it fits at 12fps.
All 23 timing configs are also checked pixel-exact, so none of this was timed
against a decoder that quietly skipped work.
FINDINGS 30. Next: lever B, the cost-aware mode decision.
Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Regression test for the 256x256 CRTC mode (docs/FINDINGS 23).
|
||||
|
||||
Checks tmp/snap256/x68000/0000.png against tmp/frame256.bin:
|
||||
Checks a native snapshot (default tmp/snap256/x68000/0000.png, override with
|
||||
argv[1] -- tools/bench/span.lua verifies twelve of them) against
|
||||
tmp/frame256.bin:
|
||||
1. native snapshot is 256x512 -- 256 dots, and 512 active scanlines of a
|
||||
568-line 31.5kHz raster carrying 256 double-scanned graphics rows
|
||||
2. double-scan pairing is (1,2),(3,4),... -- MAME halves the ABSOLUTE
|
||||
@@ -16,7 +18,8 @@ import struct, sys
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
s = np.asarray(Image.open("tmp/snap256/x68000/0000.png").convert("RGB")).astype(int)
|
||||
snap = sys.argv[1] if len(sys.argv) > 1 else "tmp/snap256/x68000/0000.png"
|
||||
s = np.asarray(Image.open(snap).convert("RGB")).astype(int)
|
||||
d = open("tmp/frame256.bin", "rb").read()
|
||||
W, H = struct.unpack(">HH", d[4:8])
|
||||
pal = np.frombuffer(d[8:8+768], np.uint8).reshape(256, 3).astype(int)
|
||||
@@ -53,7 +56,7 @@ if fail:
|
||||
sys.exit(1)
|
||||
|
||||
mse = ((act - pal[idx]) ** 2).mean()
|
||||
print(f"OK 256x512 native, double-scan exact, active {W}x{H} pixel-exact, "
|
||||
print(f"OK {snap}: 256x512 native, double-scan exact, active {W}x{H} pixel-exact, "
|
||||
f"letterbox true black")
|
||||
print(f" palette ceiling vs 24-bit palettised source: "
|
||||
f"{10*np.log10(255**2/mse):.2f} dB ({(I==0).sum()}/256 entries use I=0)")
|
||||
|
||||
Reference in New Issue
Block a user