Files
retroDE_ps2/docs/decisions/0011-gs-dump-ingestion.md
T
thejayman77 ec82764bef Initial commit: retroDE_ps2 — first-of-its-kind PS2 GS FPGA core (DE25-Nano / Agilex 5)
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>
2026-06-29 20:10:50 -04:00

102 lines
6.8 KiB
Markdown

# 0011 — GS dump ingestion (Ch340): parse, census, translate a supported subset
Status: **ACCEPTED — Ch340 CLOSED (2026-06-21)** as a parser + census / fail-closed victory. Brick 5
(authentic on-silicon render) is **explicitly waived** because the authentic dump contains zero
supported segments — that is the census doing its job, not a failure.
## Closeout (honest framing, per Codex)
- Authentic `cubes_demo` GS dump (MIT homebrew, content-clean) parsed **deterministically, 0 malformed**.
- Container format pinned from PCSX2 source and validated byte-exact; byte-exact synthetic parser test
passes (`tools/test_gs.sh`).
- Primitive reconstruction (GS vertex-kick model) works: **648 triangles + 540 sprites**.
- Support census classified **every** primitive; histograms + reasons emitted to
`captures/gs/reports/cubes.census.txt` (aggregate only).
- Translator **failed closed with no scene**: every authentic triangle is textured (`TME=1`, no real-
texture path) and sprites are unsupported. Nothing was approximated.
- Core trust-boundary goal achieved: **authentic GS traffic enters the pipeline and unsupported
content is reported, not faked.** The translator→`ps2_feeder`→staging path is proven on the
supported synthetic fixture; authentic silicon render is deferred to the census-derived blocker.
- Mechanical top blocker → **Ch341: textured-triangle ingestion** (real texture state/upload/bind).
Do NOT hunt another dump for a convenient flat segment; do NOT substitute synthetic silicon for
authentic Brick 5.
Original design follows (the boundary it set still holds).
## Goal & trust boundary
Authentic GS traffic enters the proven host pipeline, is decoded **deterministically**, and a
**strictly-supported subset** reaches pixels with **no hidden approximation**. Ch340's honest victory
is that property — NOT a full game frame rendering. Real captures will expose unsupported textures,
transfers, blend/state, and primitive modes; those are **reported**, never approximated.
## Pipeline
```
.gs[.xz] ──(container parser)──► raw packets ──(GIF/GS decoder)──► normalized event stream
│ │
│ (local, gitignored) ▼
└────────────────────────────────────────────────► support census + histograms (reports/)
supported subset ──(translator)─┴─► ps2_feeder scene file
(Ch339 encoder streams it)
```
The translator emits the Ch339 text scene grammar (`tri`/`trig`/`tritile`/`rect`/`go`); it does NOT
re-implement the staging format. `ps2_feeder -f scene.txt` renders it on the existing bitstream.
## Normalized event stream (schema v1, versioned)
A flat, ordered list; every event carries `byte_off`, `frame_idx`, `event_idx`. Event kinds:
- `FRAME_BOUNDARY {field}` — VSync packet (frame delimiter).
- `GIFTAG {path, nloop, eop, pre, prim, flg, nreg, regs}` — decoded GIF tag header.
- `GSREG {addr, name, value}` — a GS register write (from A+D, REGLIST, or PACKED expansion).
- `IMAGE {qwc, dst_fmt}` — an IMAGE-mode (HWREG/texture/FB upload) transfer; payload bytes summarized,
NOT inlined into committable output.
- `READFIFO {qwc}` — local→host transfer.
- `MALFORMED {reason}` — structural decode failure at this offset.
`GSREG.name` covers the register set we already encode in `bake.py`/`ps2_feeder`: PRIM, RGBAQ, ST, UV,
XYZ2/XYZ3, XYZF2/XYZF3, TEX0_1/2, CLAMP, FOG, TEX1/2, FRAME_1/2, ZBUF_1/2, TEST_1/2, ALPHA_1/2,
SCISSOR, PRMODE/PRMODECONT, BITBLTBUF, TRXPOS, TRXREG, TRXDIR, etc. Unknown addrs → `GSREG` with
`name="UNKNOWN_0xNN"` (reported, not dropped).
## Support census (every event classified)
- **translated** — emitted into the ps2_feeder scene (the supported subset, below).
- **ignored (justified)** — safely skippable with a stated reason (e.g. FOG with FGE off; a redundant
state write; a NOP). The justification is explicit per category.
- **unsupported** — a real effect we cannot faithfully reproduce yet (textured prim, sprite with a
real texture, blend mode ≠ the proven source-over, PSM we don't render, Z format, dest-alpha test,
scissor we don't honor, TRIANGLE_FAN/STRIP we haven't reduced, lines/points, IMAGE texture upload).
Recorded with frame/event/byte offset + the exact reason. **Never approximated.**
- **malformed** — structural failure (bad GIF tag, truncated payload, length mismatch).
Reports (committable, no game content): per-dump JSON/text with frame count, a GS-register-write
histogram, a primitive-mode histogram, and the full unsupported/malformed list with offsets+reasons.
## Supported subset that reaches pixels (Ch340 v1)
Matches what the feeder + `ps2_feeder` already render faithfully on silicon:
- `PRIM` = TRIANGLE (prim type 3), with `IIP` flat or gouraud (per-vertex RGBAQ).
- Vertices via `XYZ2`/`XYZ3` (Z honored — Ch338 cross-batch Z is correct).
- Color via `RGBAQ` (MODULATE through the unity texel — matches the proven path).
- `FRAME_1`/`ZBUF_1`/`TEST_1`(GEQUAL)/`ALPHA_1`(the proven source-over) within the supported envelope.
A draw segment qualifies only if EVERY primitive in it is in this subset and the state matches the
proven envelope. Sprites→rect and triangle-strip→triangle reduction are candidate Ch341 work, decided
from the census, not pre-built.
## Acceptance (Codex)
1. Byte-exact parser tests on a small **synthetic** `.gs` fixture (`captures/gs/synthetic/`, authored
once the real container format is confirmed).
2. One authentic dump parses **deterministically** (same events every run).
3. Frame / register / primitive histograms emitted.
4. Unsupported events carry frame/event/byte offset + reason.
5. ≥1 carefully chosen supported frame/segment translates to a ps2_feeder scene and **renders on
silicon**.
6. Translation failures **stop before board access**.
## Bricks (gated on the dump)
1. Inspect the real dump bytes; confirm the container framing (header, compression, packet types).
Build the container parser + a matching synthetic fixture; byte-exact parser test.
2. GIF/GS decoder → normalized events (GIF tag + A+D/REGLIST/PACKED register expansion). Unit-tested
against hand-built GIF packets (the encode side already exists in `bake.py`).
3. Census + histograms over the normalized stream; emit reports.
4. Translator: supported-subset events → ps2_feeder scene file; everything else → census. Fail closed.
5. Pick a supported segment from the census; render via `ps2_feeder -f`; confirm on silicon.
Ch341 is then chosen from the census's highest-impact blocker, not guessed now.