Snapshot: fog implementation + fidelity tooling baseline (pre bilinear-clamp fix)
Per-vertex GS fog end-to-end (gs_stub emit incl. persp_emit5, gs_prim_list_feeder XYZ2->XYZF2 on PRIM.FGE, gs_make_sh3_scheduler_fixture.py F/FGE packing), new fog TBs, fidelity attribution tooling. Functional baseline before removing the dead bilinear lerp8 clamps (Codex: 161-node comb loop -> -0.042ns setup fail). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,9 @@ Address convention matches the codebase: VRAM byte base = PTR*256 (TBP0/DBP/CBP
|
||||
and PTRs are page-aligned (multiple of 32) so page_index*8192 composes correctly off that base.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
# block grid is shared by PSMCT32 and PSMT8 (4 rows x 8 cols), value = block index within page
|
||||
BLOCK = [
|
||||
[ 0, 1, 4, 5,16,17,20,21],
|
||||
@@ -52,6 +55,40 @@ def psmt8_addr(tbp, fbw, x, y):
|
||||
block_idx = BLOCK[(y >> 4) & 3][(x >> 4) & 7]
|
||||
return tbp*256 + page_index*8192 + block_idx*256 + COL8[y & 15][x & 15]
|
||||
|
||||
_COL4 = None
|
||||
_BLOCK4 = (
|
||||
(0,2,8,10), (1,3,9,11), (4,6,12,14), (5,7,13,15),
|
||||
(16,18,24,26), (17,19,25,27), (20,22,28,30), (21,23,29,31),
|
||||
)
|
||||
|
||||
def _col4_table():
|
||||
"""Load the canonical PSMT4 permutation from the RTL contract."""
|
||||
global _COL4
|
||||
if _COL4 is not None:
|
||||
return _COL4
|
||||
rtl = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||
"rtl", "gif_gs", "gs_swizzle_psmt4_stub.sv")
|
||||
text = open(rtl, encoding="utf-8").read()
|
||||
lo = text.index("function automatic logic [8:0] col_idx_psmt4")
|
||||
hi = text.index("endfunction", lo)
|
||||
tab = [None] * 512
|
||||
for key, value in re.findall(r"9'd(\d+)\s*:\s*return\s+9'd(\d+)", text[lo:hi]):
|
||||
tab[int(key)] = int(value)
|
||||
tab[511] = 511 # explicit default arm in the RTL function
|
||||
if any(v is None for v in tab) or sorted(tab) != list(range(512)):
|
||||
raise RuntimeError("PSMT4 RTL column table is incomplete or non-bijective")
|
||||
_COL4 = tuple(tab)
|
||||
return _COL4
|
||||
|
||||
def psmt4_addr(tbp, fbw, x, y):
|
||||
"""Return (byte address, high-nibble select) for a PSMT4 texel."""
|
||||
if fbw & 1:
|
||||
raise ValueError(f"PSMT4 TBW must be even, got {fbw}")
|
||||
page_index = (y >> 7) * (fbw >> 1) + (x >> 7)
|
||||
block_idx = _BLOCK4[(y >> 4) & 7][(x >> 5) & 3]
|
||||
nib = _col4_table()[((y & 15) << 5) | (x & 31)]
|
||||
return tbp*256 + page_index*8192 + block_idx*256 + (nib >> 1), bool(nib & 1)
|
||||
|
||||
class LocalMem:
|
||||
"""4 MiB GS VRAM. Seed from the dump's initial snapshot, then replay host->local uploads in order."""
|
||||
SIZE = 0x400000
|
||||
@@ -82,6 +119,17 @@ class LocalMem:
|
||||
out[r+x] = self.m[a] if 0 <= a < self.SIZE else 0
|
||||
return out
|
||||
|
||||
def read_psmt4(self, tbp, fbw, tw, th):
|
||||
"""Return one unpacked 0..15 index byte per raster-order texel."""
|
||||
out = bytearray(tw*th)
|
||||
for y in range(th):
|
||||
r = y*tw
|
||||
for x in range(tw):
|
||||
a, hi = psmt4_addr(tbp, fbw, x, y)
|
||||
b = self.m[a] if 0 <= a < self.SIZE else 0
|
||||
out[r+x] = ((b >> 4) & 0xF) if hi else (b & 0xF)
|
||||
return out
|
||||
|
||||
def read_ct32_word(self, dbp, dbw, x, y):
|
||||
a = ct32_addr(dbp, dbw, x, y)
|
||||
return int.from_bytes(self.m[a:a+4], "little") if a+4 <= self.SIZE else 0
|
||||
|
||||
Reference in New Issue
Block a user