Files
Dragon-s-Lair-X68k/tools/analysis/04_aggregate.py
T
reala-misaki 65112b9305 Session 1: hardware research, content measurement, codec decision, MAME harness
Verified GVRAM is one word-access per pixel in ALL color modes; chose 256-color
256x192 with movem.l bursts (page 1 sacrificed as double-buffer).

Measured 8 scenes from the Blu-ray source: blit costs under 8% of the 12fps
cycle budget, so I/O is the bottleneck, not CPU. Naive delta+RLE reaches only
3.2:1 (365 KB/s, 470MB) -> decision to use 4x4 vector quantization (~30 KB/s).

"Shot on twos" assumption failed: the transfer has zero duplicate frames, so
12fps requires explicit decimation.

Documents three false measurement results and their root causes (per-frame
Floyd-Steinberg dithering, temporal denoise, exact-match dedupe on noisy source).

MAME Lua injection harness works and is reusable for cycle-cost measurement;
the IOCS _B_READ disk benchmark is blocked returning -1.

Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
2026-08-23 11:23:49 -07:00

55 lines
2.5 KiB
Python

import numpy as np, glob, os
from PIL import Image
def enc(a,b,gap=4):
t=0
for y in range(a.shape[0]):
ra,rb=a[y],b[y]; d=np.nonzero(ra!=rb)[0]
if not len(d): continue
sp=[]; s=d[0]; p=d[0]
for x in d[1:]:
if x-p>gap: sp.append((s,p)); s=x
p=x
sp.append((s,p)); t+=2
for s0,e0 in sp:
seg=rb[s0:e0+1]; t+=2; i=0
while i<len(seg):
r=1
while i+r<len(seg) and seg[i+r]==seg[i] and r<127: r+=1
t += 2 if r>=3 else r; i+=r
return t
RAW=256*192; rows=[]
for d in sorted(glob.glob("samp/*")):
s=os.path.basename(d)
fs=sorted(glob.glob(f"{d}/f*.png"))
if len(fs)<10: continue
rgb=[np.asarray(Image.open(x).convert("RGB")) for x in fs]
# one shared palette per scene, no dither (cel art is flat)
samp=np.concatenate([r.reshape(-1,3) for r in rgb[::3]])
ref=Image.fromarray(samp.reshape(-1,1,3)).quantize(colors=256,method=Image.MEDIANCUT,dither=Image.NONE)
idx=[np.asarray(Image.fromarray(r).quantize(palette=ref,dither=Image.NONE),dtype=np.uint8) for r in rgb]
N=len(idx)
# dedupe @0.3%
keep=[0]
for i in range(1,N):
if 100.0*np.count_nonzero(idx[i]!=idx[keep[-1]])/RAW >= 0.3: keep.append(i)
fps=len(keep)/5.0
cp=np.array([100.0*np.count_nonzero(idx[keep[j]]!=idx[keep[j-1]])/RAW for j in range(1,len(keep))])
sz=np.array([enc(idx[keep[j-1]],idx[keep[j]]) for j in range(1,len(keep))])
rows.append((s,N,len(keep),fps,cp.mean(),np.percentile(cp,90),sz.mean(),np.percentile(sz,90),sz.mean()*fps))
print(f"{s}: src={N} uniq={len(keep)} fps={fps:4.1f} chg={cp.mean():5.1f}%/p90 {np.percentile(cp,90):5.1f}% delta={sz.mean():6.0f}B/p90 {np.percentile(sz,90):6.0f} rate={sz.mean()*fps/1024:6.1f}KB/s")
r=np.array([x[3:] for x in rows],dtype=float)
print("\n=== AGGREGATE (8 scenes) ===")
print(f"effective fps after dedupe : {r[:,0].mean():.1f}")
print(f"pixels changed / frame : mean {r[:,1].mean():.1f}% p90 {r[:,2].mean():.1f}%")
print(f"delta frame size : mean {r[:,3].mean():.0f} B p90 {r[:,4].mean():.0f} B")
print(f"compression vs raw : {RAW/r[:,3].mean():.1f}:1")
rate=r[:,5].mean()
print(f"stream rate : {rate/1024:.0f} KB/s")
print(f"22 min of video : {rate*22*60/1048576:.0f} MB")
px=r[:,1].mean()/100*RAW
print(f"blit cost mean : ~{px*6.5/1000:.0f}k cycles (budget 833k @12fps)")
print(f"blit cost p90 : ~{r[:,2].mean()/100*RAW*6.5/1000:.0f}k cycles")