Files
TeslaRel410/emulator/PHASE3-PROGRESS.md
T
CydandClaude Opus 4.8 57524d4a68 Phase 3c: fix production vr_sync abort; full game runs through renderer
Root cause: vpx_max_postboot_acks=200, a Phase-2 bring-up guard, capped how
many post-boot replies the device would ever feed. A real BattleTech session
issues an unbounded stream of sync/frame/render replies and aborted with
"velocirender_receive timed out - sends_wo_rcv" at exactly the 200th ack
(143 sync + 55 frame-ack + 2 render). Cap is now effectively unlimited
(0x7fffffff; override VPX_MAX_ACKS for diagnostics).

After the fix the full game (game.conf, RIO disabled, cycles=max) runs
indefinitely: 2500+ syncs, 1264+ frame-acks, no abort, into L4VIDEO content
load (Thor mech + terrain from ALPHA_1/REL410/BT/VIDEO/GEO -- 841 real .bgf
models; test.egg is only mission params). Window still shows background:
the game uses the full DPL hierarchy (instance/object/lod/geogroup + DCS
transforms) which the flat flyk-tuned scene walk doesn't traverse yet -- that
is Phase 3d, documented in PHASE3-PROGRESS.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 14:45:20 -05:00

169 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase 3 — Render Backend: Progress
**Status (2026-07-03): Phase 3a + 3b + 3c complete. 3a — the render command
stream is fully captured and decoded, and a captured DIVRGB frame
reconstructed to pixels offline. 3b — a live OpenGL window built into the
emulated board draws each frame in real time inside DOSBox-X. 3c — the
production `vr_sync` abort is fixed and the full BattleTech v4.10 game now runs
indefinitely through the emulated board, drawing 1000s of frames (was hard-
capped at 200). First images ever produced from the Rel 4.10 VPX protocol
without a real board. Next (3d): traverse the game's DPL hierarchy + DCS
transforms so its mechs actually appear.**
![decoded DIVRGB calibration screen](divrgb-decoded.png)
That is `flyk divrgb.scn` — Division's SMPTE color-bar calibration scene —
rendered *entirely from the FIFO command stream captured by the emulated VPX
board*: geometry, connectivity, materials, camera, viewport and background all
come from the wire, none from the scene files. `divrgb-frame0.png` is the same
capture drawn with the actual frame-0 spline camera the app sent.
## 3a. Full FIFO capture
`vpxlog.cpp` now records every FIFO burst when `VPX_FIFODUMP=<path>` is set
(records: `'VPXM'` magic, u32 length, raw bytes; one record per burst between
`outputData` tag writes). The capture used here: `divrgb.fifodump`
(1297 messages) from `divrgb.conf`.
Tools:
- `decode_fifodump.py <dump> [--hex N] [--action A]` — action census + payload
hexdumps.
- `render_capture.py <dump> [-o out.png] [--frame N] [--eye x,y,z]`
reconstructs the scene graph and software-renders the frame each
`draw_scene` commits.
## The Rel 4.10 wire protocol (established from this capture)
One burst per message: `[action:4][payload]`, packetized at 508 bytes.
The i860 image download itself rides the FIFO in this build (actions 1820,
`VREND.MNG`). The DPL3 `vr_action` enum holds for 023 but Rel 4.10 extends
and re-purposes the tail:
| action | meaning | payload |
|---|---|---|
| 0 | init | args string (`/device~0x150~/video~svga~…`) |
| 1 | create | `[type][name]`**the host assigns node names** (1,2,3…); the board's create reply node value is ignored |
| 3 | flush | `[name][type][node struct]` (see node types below) |
| 7 | dcs_link | `[parent][child]` |
| 9 | draw_scene | `[view?][0][0][1][1.0f]` — commits the frame |
| 11 | list_add | `[parent][child]` |
| 1821 | 860code/data/bss/args | i860 download (FIFO in this build) |
| 23 | **set_geom_verts** (Rel4.10; DPL3 had 22) | hdr `[name][0][n_verts][3][n_blocks][1][5][n_verts][1.0f]`, then float32 x,y,z per vertex |
| 25 | **set_geom_conns** (new) | hdr `[name][n_polys][loop_len][0]`, then indices; each poly a closed loop (last=first) |
| 31 | **camera** (new) | `[?][view][3×3 rotation, row-major][eye x,y,z]` per frame |
| 45 (0x2D) | sync | token ping (see PHASE2) |
Node types (from create/flush pairs in this capture):
| type | node | flush payload highlights |
|---|---|---|
| 2 | texture/ramp? | one word, `0xFFFFFFFF` / `0x0FFFFFFF` |
| 3 | view | window l,b,r,t (±1 × ±0.6154), window-plane distance 1.3, viewport 832×512, near 2, far 12000, background RGB |
| 4 | light | |
| 5 | dcs | 4×4 matrix |
| 6 | material (old-style) | RGB floats |
| 7 | object | |
| 8 | lod | 2×4 bounds + lists |
| 9 | geogroup | bounds; **payload int 14 = material node name** |
| 10 | geometry | `[geo_type][n_polys][n_verts]…[radius]` |
| 11 | material | **floats 1012 (payload) = diffuse RGB** |
Graph: `list_add` links object→lod→geogroup→geometry (and zone-level nodes to
node 0); geogroups bind materials by name. The 13 DIVRGB bars decode to the
textbook SMPTE pattern — 7 color bars, the reverse strip, and the I/white/+Q
PLUGE row — which validates vertices, connectivity, materials, camera and
window mapping in one image. (Division screen x runs opposite to a GL-style
eye space; without negating x the pattern comes out mirrored.)
## 3b. Live OpenGL backend (DONE)
![live GL window rendering DIVRGB](divrgb-live-gl.png)
`VPX_RENDER=1` turns on a live render backend built into `vpxlog.cpp`
(Windows/WGL). The same burst assembler that feeds `VPX_FIFODUMP` now also
feeds an in-process scene store (`scene_burst()``struct VScene`), and each
`vr_draw_scene` publishes a frame snapshot to a dedicated OpenGL window thread
(`rt_main`) that draws it with fixed-function GL. No build-system change was
needed — `opengl32` was already linked, and `vpxlog.cpp` is already in
`hardware/`.
Validated: `flyk divrgb.scn` (`divrgb.conf` with `VPX_RENDER=1`) opens the
"VPX VelociRender (emulated)" window and draws the SMPTE bars live, framed by
the real `camera.spl` spline camera the app sends — matching the offline
`render_capture.py` decode. The window runs on its own thread, so it keeps
displaying the last frame after the DOS app exits.
Design notes:
- Multi-burst payloads (`set_geom_verts`/`conns` continuations) are tracked
with `geom_active` / `conn_active` state, same as the offline decoder.
- Projection: `glFrustum` from the view-node window rect scaled by
`near/window_distance`; a `glScalef(-1,1,1)` handles Division's mirrored
screen-x. Camera is the row-major 3×3 from action 31 loaded as the
modelview rotation, then `glTranslatef(-eye)`.
- Frame handoff is a critical-section-guarded `VFrame` + auto-reset event;
the emulator thread never blocks on GL.
Game path (`alpha1.conf`, full BattleTech v4.10): the window opens and draws
the view background, but the run hits the **pre-existing production-path sync
timeout** (`velocirender_receive timed out — sends_wo_rcv=3`, then
`failed in vr_sync`) — the `btdpl.ini`/netnub launch bursts several sends
before its first receive, which the POLL_THRESHOLD gating stalls. This is not
a 3b issue (the `flyk` clean-launch path renders fine); it is the same
production-sync item still open from Phase 2.
## 3c. The full game runs through the live renderer (sync abort fixed)
**The production `vr_sync` abort is fixed, and BattleTech v4.10 now runs
indefinitely through the emulated board + live renderer.** Root cause was a
Phase-2 bring-up guard left in the device: `vpx_max_postboot_acks = 200`
capped how many post-boot replies the device would ever feed. A real game
session issues an unbounded stream of sync/frame/render replies; the game
aborted with `velocirender_receive timed out — sends_wo_rcv` the instant the
device fell silent — at *exactly* the 200th ack (measured: 143 sync + 55
frame-ack + 2 render = 200). The cap is now effectively unlimited
(`0x7fffffff`, override with `VPX_MAX_ACKS`). Same run after the fix: 2500+
syncs, 1264+ frame-acks, no abort. The game progresses past sync into
`L4VIDEO.cpp` content/entity setup (loading the Thor mech and terrain) and
draws continuously.
Contrary to the earlier note, the content **is** on the production image: 841
`.bgf` models under `ALPHA_1/REL410/BT/VIDEO/GEO` (incl. `BUTTEE.BGF`,
`MSLR.BGF`, the `THR*` Thor mech). `test.egg` is only mission parameters (as
the pod owner said); the geometry loads from `VIDEO/GEO` regardless.
Game render config: `game.conf` (RIO disabled so the missing cockpit board
doesn't stall the control manager; `cycles=max`).
### What the game's wire stream looks like (vs. flyk)
The game geometry path is heavier and different from flyk's flat DIVRGB:
- **action 26 (0x1A) dominates** — texture texel upload. 32-byte headers
`[node][size][w][h]` (`0x1000`=64×64, `0x4000`=128×128 tiles) then 256-byte
texel rows. 2880+ of these = the mission texture set streaming in.
- action 28 (0x1C): material/light parameter blocks.
- actions 23/25 (verts/conns) still carry mesh geometry but are a small
fraction of the stream.
- The scene graph is the **full DPL hierarchy** — zone → dcs → instance →
object → lod → geogroup → geometry — with **DCS transform matrices**
positioning each mech/prop. flyk's DIVRGB put geometry directly in
geogroups at world coordinates.
## Remaining (Phase 3d: game geometry on screen)
The window currently shows only the view background for the game because the
live backend's `scene_publish_frame()` walks the *flat* flyk layout
(geogroup→geometry at world coords). To draw the game's mechs it must:
1. **Traverse the full hierarchy** (instance/object/lod/geogroup) and **apply
DCS transform matrices** (accumulate the 4×4s down the graph) so each mech
lands at its world position; pick a LOD.
2. **Arena content search path**: the game logs `couldn't load object
thr_tshd.bgf` — that object lives in `VIDEO/GEO/ARENA/` and `…/POLAR/`
(arena-specific), not the top `GEO` dir; the geometry search path needs the
selected arena subdir (a `setenv`/config detail, not protocol).
3. **Texturing** (action 26 texel maps + SVT), lighting, depth/material.
Current backend is flat-shaded untextured polys.
4. **DOS/4GW sync variant** for the CYCLE `flyk yip.scn` fixture (Red Planet
geometry) — the older action-check sync.