ba74bbd5aa
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>
85 lines
3.2 KiB
Python
85 lines
3.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Build one scheduler bootlet that preloads the distinct CLUTs required by several fixture tags.
|
|
|
|
Usage: gs_merge_scheduler_bootlets.py <output-tag> <input-tag> [<input-tag> ...]
|
|
"""
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
ROOT = os.path.normpath(os.path.join(HERE, ".."))
|
|
DATA = os.path.join(ROOT, "sim", "data", "top_psmct32_raster_demo")
|
|
sys.path.insert(0, DATA)
|
|
import bake
|
|
|
|
RAM_QWORDS = 512
|
|
HEADER_QWORDS = 16
|
|
FBW = 10
|
|
FBPXW = 640
|
|
FBH = 480
|
|
|
|
|
|
def read_payload(tag):
|
|
path = os.path.join(DATA, f"payload_sh3_{tag}.mem")
|
|
with open(path) as f:
|
|
lines = f.readlines()
|
|
header = next((line for line in lines if line.startswith("//")), "")
|
|
match = re.search(r"QWC=(\d+)", header)
|
|
if not match:
|
|
raise ValueError(f"{path}: missing QWC metadata")
|
|
cbp_match = re.search(r"CBPs \[([^]]*)\]", header)
|
|
if not cbp_match:
|
|
raise ValueError(f"{path}: missing CBP metadata")
|
|
cbps = [int(x.strip()) for x in cbp_match.group(1).split(",") if x.strip()]
|
|
words = [int(line.strip(), 16) for line in lines if line.strip() and not line.startswith("//")]
|
|
if len(words) != RAM_QWORDS:
|
|
raise ValueError(f"{path}: expected {RAM_QWORDS} qwords, got {len(words)}")
|
|
qwc = int(match.group(1))
|
|
if qwc > RAM_QWORDS - HEADER_QWORDS:
|
|
raise ValueError(f"{path}: QWC {qwc} exceeds payload capacity")
|
|
return qwc, cbps, words[HEADER_QWORDS:HEADER_QWORDS + qwc]
|
|
|
|
|
|
def main(argv):
|
|
if len(argv) < 4:
|
|
print(__doc__.strip())
|
|
return 2
|
|
output_tag, input_tags = argv[1], argv[2:]
|
|
active = []
|
|
seen_cbps = set()
|
|
for tag in input_tags:
|
|
qwc, cbps, words = read_payload(tag)
|
|
for cbp in set(cbps):
|
|
if cbp in seen_cbps:
|
|
raise ValueError(f"duplicate relocated CBP {cbp}: fixture {tag} aliases an earlier input")
|
|
seen_cbps.add(cbp)
|
|
if len(words) != qwc:
|
|
raise ValueError(f"payload word count mismatch for {tag}")
|
|
active.extend(words)
|
|
if len(active) > RAM_QWORDS - HEADER_QWORDS:
|
|
raise ValueError(f"combined QWC {len(active)} exceeds {RAM_QWORDS - HEADER_QWORDS}")
|
|
|
|
payload_name = f"payload_sh3_{output_tag}.mem"
|
|
bios_name = f"bios_sh3_{output_tag}.mem"
|
|
with open(os.path.join(DATA, payload_name), "w") as f:
|
|
f.write(f"// combined scheduler CLUT preload tags={input_tags} CBPs={sorted(seen_cbps)} QWC={len(active)}. gitignored.\n")
|
|
for _ in range(HEADER_QWORDS):
|
|
f.write("00000000000000000000000000000000\n")
|
|
for word in active:
|
|
f.write(f"{word:032x}\n")
|
|
for _ in range(RAM_QWORDS - HEADER_QWORDS - len(active)):
|
|
f.write("00000000000000000000000000000000\n")
|
|
display1_hi = ((FBH - 1) << 12) | (FBPXW - 1)
|
|
bake.write_bios_mem(
|
|
bios_name,
|
|
bake.build_textured_demo_bootlet_disp(len(active), display1_hi, FBW),
|
|
f"combined scheduler CLUT preload tags={input_tags} QWC={len(active)} DISPLAY1={FBPXW}x{FBH}. gitignored.",
|
|
)
|
|
print(f"[bootlet] emitted {bios_name} + {payload_name}: QWC={len(active)} CBPs={sorted(seen_cbps)}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main(sys.argv))
|