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
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
import numpy as np, glob
|
||||
from PIL import Image
|
||||
|
||||
files = sorted(glob.glob("an2/f*.png"))
|
||||
imgs = [Image.open(f) for f in files]
|
||||
print("PIL mode:", imgs[0].mode)
|
||||
idx = [np.asarray(im.convert("P") if im.mode!="P" else im, dtype=np.uint8) for im in imgs]
|
||||
N = len(idx); H,W = idx[0].shape; raw = H*W
|
||||
print(f"frames={N} size={H}x{W} raw={raw} B/frame")
|
||||
|
||||
# --- per-pair change stats on the QUANTIZED stream ---
|
||||
pct = np.array([100.0*np.count_nonzero(idx[i]!=idx[i-1])/raw for i in range(1,N)])
|
||||
print(f"\n--- consecutive change % (24fps, quantized) ---")
|
||||
print(f"mean {pct.mean():.1f} median {np.median(pct):.1f} p10 {np.percentile(pct,10):.1f} p90 {np.percentile(pct,90):.1f} max {pct.max():.1f}")
|
||||
print(f"pairs under 2% changed: {np.count_nonzero(pct<2.0)}/{len(pct)} ({100*np.count_nonzero(pct<2.0)/len(pct):.0f}%)")
|
||||
|
||||
# --- threshold dedupe (twos detection) ---
|
||||
THRESH = 2.0
|
||||
keep=[0]
|
||||
for i in range(1,N):
|
||||
if 100.0*np.count_nonzero(idx[i]!=idx[keep[-1]])/raw >= THRESH:
|
||||
keep.append(i)
|
||||
print(f"\n--- dedupe @ {THRESH}% ---")
|
||||
print(f"unique frames: {len(keep)}/{N} -> effective {len(keep)/12.0:.1f} fps")
|
||||
|
||||
cp = np.array([100.0*np.count_nonzero(idx[keep[j]]!=idx[keep[j-1]])/raw for j in range(1,len(keep))])
|
||||
print(f"unique-pair change %: mean {cp.mean():.1f} median {np.median(cp):.1f} p90 {np.percentile(cp,90):.1f} max {cp.max():.1f}")
|
||||
|
||||
def encode_size(a,b,gap=4):
|
||||
total=0
|
||||
for y in range(a.shape[0]):
|
||||
ra,rb=a[y],b[y]
|
||||
d=np.nonzero(ra!=rb)[0]
|
||||
if len(d)==0: continue
|
||||
spans=[]; s=d[0]; p=d[0]
|
||||
for x in d[1:]:
|
||||
if x-p>gap: spans.append((s,p)); s=x
|
||||
p=x
|
||||
spans.append((s,p))
|
||||
total+=2
|
||||
for s0,e0 in spans:
|
||||
seg=rb[s0:e0+1]; total+=2
|
||||
i2=0
|
||||
while i2<len(seg):
|
||||
r=1
|
||||
while i2+r<len(seg) and seg[i2+r]==seg[i2] and r<127: r+=1
|
||||
total += 2 if r>=3 else r
|
||||
i2+=r
|
||||
return total
|
||||
|
||||
sz=np.array([encode_size(idx[keep[j-1]],idx[keep[j]]) for j in range(1,len(keep))])
|
||||
fps_eff=len(keep)/12.0
|
||||
rate=sz.mean()*fps_eff
|
||||
print(f"\n--- codec estimate ---")
|
||||
print(f"delta bytes: mean {sz.mean():.0f} median {np.median(sz):.0f} p90 {np.percentile(sz,90):.0f} max {sz.max():.0f}")
|
||||
print(f"ratio vs raw: {raw/sz.mean():.1f}:1")
|
||||
print(f"stream rate : {rate/1024:.1f} KB/s")
|
||||
print(f"22min video : {rate*22*60/1048576:.0f} MB")
|
||||
px=cp.mean()/100*raw
|
||||
print(f"blit cost : ~{px*6.5/1000:.0f}k cycles/frame (budget 833k)")
|
||||
print(f"p90 blit : ~{np.percentile(cp,90)/100*raw*6.5/1000:.0f}k cycles")
|
||||
Reference in New Issue
Block a user