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,54 @@
|
||||
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")
|
||||
Reference in New Issue
Block a user