ec82764bef
RTL (GS rasterizer, EE core stub, platform bridge, LPDDR4B path), sim regression (272 TBs), docs, and tooling. Copyrighted PS2 content (BIOS, game code, GS dumps, and all dump-derived textures/traces) is excluded via .gitignore and stays local. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
91 lines
4.5 KiB
Python
91 lines
4.5 KiB
Python
#!/usr/bin/env python3
|
|
"""retroDE_ps2 — Ch351 step 1: isolate reciprocal quantization from S1 under-interpolation banding.
|
|
|
|
Compares the RTL FB dump (sh3_real_fb_out.mem, written by tb_top_psmct32_sh3_real_draw_demo) against TWO host
|
|
references over the identical crop geometry:
|
|
FLOAT — ideal perspective (the Codex pixel-diff oracle).
|
|
RECIP-8b — RTL-FAITHFUL: fixed-point vertex attrs + the 8-bit gs_reciprocal_stub (gs_make_sh3_real_draw_fixture).
|
|
Verdict:
|
|
- RTL ≈ RECIP-8b (tight) AND RECIP-8b ≠ FLOAT (loose) -> the residual is RECIPROCAL QUANTIZATION. Widening
|
|
gs_reciprocal_stub IDX_BITS should fix it (Ch351 step 2).
|
|
- RTL ≠ RECIP-8b (still loose) -> S1 attribute UNDER-INTERPOLATION beyond the reciprocal.
|
|
|
|
Usage: gs_ch351_oracle.py (run gs_make_sh3_real_draw_fixture.py + the TB first to produce the .mem files)
|
|
"""
|
|
import sys, os
|
|
ROOT=os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)),".."))
|
|
DATA=os.path.join(ROOT,"sim","data","top_psmct32_raster_demo")
|
|
FBPXW, CH, TW, TH = 256, 120, 512, 512
|
|
|
|
def loadmem(name):
|
|
return [int(l,16) for l in open(os.path.join(DATA,name)) if l.strip() and not l.strip().startswith("//")]
|
|
|
|
def main():
|
|
fb = loadmem("sh3_real_fb_out.mem")
|
|
rmF = loadmem("sh3_real_refmap.mem")
|
|
rmR = loadmem("sh3_real_refmap_recip.mem")
|
|
idxw = loadmem("sh3_real_idx.mem")
|
|
pal = loadmem("sh3_real_pal.mem")
|
|
def sh3_idx(u,v):
|
|
lin=v*TW+u; return (idxw[lin>>2] >> (8*(lin&3))) & 0xFF
|
|
def exp_cell(u,v):
|
|
return pal[sh3_idx(u,v)] & 0xFFFFFF
|
|
palset = set(p & 0xFFFFFF for p in pal)
|
|
|
|
def histo(refmap, label):
|
|
hist=[0]*5; ihist=[0]*5; tot=0; itot=0; exact_color=0; clut_bad=0
|
|
sdu=sdv=0.0; nd=0
|
|
for o in range(FBPXW*CH):
|
|
rm=refmap[o]
|
|
if not (rm>>31): continue
|
|
interior=(rm>>30)&1; tu=(rm>>9)&0x1FF; tv=rm&0x1FF
|
|
col = fb[o] & 0xFFFFFF
|
|
tot+=1
|
|
if col not in palset: clut_bad+=1
|
|
if col == exp_cell(tu,tv): exact_color+=1
|
|
D=99; mdu=mdv=0
|
|
for rad in range(0,4):
|
|
for du in range(-rad,rad+1):
|
|
for dv in range(-rad,rad+1):
|
|
if max(abs(du),abs(dv))!=rad or D!=99: continue
|
|
ttu=tu+du; ttv=tv+dv
|
|
if 0<=ttu<TW and 0<=ttv<TH and col==exp_cell(ttu,ttv):
|
|
D=rad; mdu=du; mdv=dv
|
|
hist[D if D<=3 else 4]+=1
|
|
if D<=3: sdu+=mdu; sdv+=mdv; nd+=1
|
|
if interior: itot+=1; ihist[D if D<=3 else 4]+=1
|
|
print(f"== {label} == covered={tot} interior={itot}")
|
|
print(f" ALL hist D0={hist[0]} D1={hist[1]} D2={hist[2]} D3={hist[3]} none={hist[4]} "
|
|
f"<=1tex={100.0*(hist[0]+hist[1])/max(1,tot):.1f}%")
|
|
print(f" INT hist D0={ihist[0]} D1={ihist[1]} D2={ihist[2]} D3={ihist[3]} none={ihist[4]} "
|
|
f"<=1tex={100.0*(ihist[0]+ihist[1])/max(1,itot):.1f}%")
|
|
print(f" exact-color-match={100.0*exact_color/max(1,tot):.1f}% clut_bad={clut_bad} "
|
|
f"mean-delta=({sdu/max(1,nd):.3f},{sdv/max(1,nd):.3f})")
|
|
return ihist
|
|
|
|
rmA = loadmem("sh3_real_refmap_affine.mem")
|
|
print("RTL FB dump vs the references (same geometry):\n")
|
|
histo(rmF, "RTL-vs-FLOAT (ideal perspective)")
|
|
print()
|
|
ih = histo(rmR, "RTL-vs-RECIP8b (perspective + 8-bit reciprocal)")
|
|
print()
|
|
iha = histo(rmA, "RTL-vs-AFFINE (per-vertex divide + linear u,v)")
|
|
print()
|
|
# also: how different are the two references themselves (FLOAT vs RECIP8b) — the reciprocal's own error
|
|
diff=sum(1 for o in range(FBPXW*CH) if (rmF[o]>>31) and (rmF[o]&0x3FFFF)!=(rmR[o]&0x3FFFF))
|
|
covered=sum(1 for o in range(FBPXW*CH) if rmF[o]>>31)
|
|
print(f"FLOAT vs RECIP8b reference texel disagreement: {diff}/{covered} ({100.0*diff/max(1,covered):.1f}%) "
|
|
f"— the reciprocal LUT's intrinsic error on this geometry")
|
|
print()
|
|
rtl_tight = (ih[2]+ih[3]+ih[4]) < 0.05*max(1,sum(ih))
|
|
if rtl_tight:
|
|
print("VERDICT: RTL matches the RTL-faithful 8-bit-reciprocal reference -> residual is RECIPROCAL")
|
|
print(" QUANTIZATION. Ch351 step 2: widen gs_reciprocal_stub IDX_BITS (10/11/12).")
|
|
else:
|
|
print("VERDICT: RTL does NOT match even the 8-bit-reciprocal reference -> S1 attribute UNDER-")
|
|
print(" INTERPOLATION banding beyond the reciprocal. Ch351: fix S1 interpolation.")
|
|
return 0
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|