diff --git a/TODO.md b/TODO.md index e82db95..62714f0 100644 --- a/TODO.md +++ b/TODO.md @@ -10,8 +10,12 @@ code, not inferred from the README. Upstream: `mac-ai-playground/retro-flight-si - `npm test` — passes, but see T4: it only counts braces. - Terrain is deterministic across instances (`getHeightAt(317,-812)` identical in two separate `Terrain` objects) — the "persistent world" claim holds. -- Rendering **not** visually verified: the audit box has no GPU and SwiftShader crashes - the page. Needs a run on real hardware. +- Rendering **verified in software** (SwiftShader, see "How to run it headless" below): + WebGL 2 context, 60 fps, 143 distinct colours, 63,792/64,000 non-black pixels, HUD + canvas drawing 6,905 lit pixels. No GPU is involved — this LXC has no `/dev/dri` and no + `/dev/nvidia*`, so all rendering is CPU-rasterised. +- **T8 found during that verification**: the CRT post-processing shader does not compile, + so the 3D canvas renders pure black on any machine. See T8. --- @@ -87,6 +91,35 @@ not the same thing. **Fix:** persist the world seed + aircraft state; add a save/load menu entry. +## T8 — The CRT shader does not compile, so the 3D canvas is black + +Found by running the game under software WebGL, not by reading the code. `js/retro.js:29`: + +```glsl +gl_Position = vec4(position, 0.0, 1.0); +``` + +In three.js `position` is a **vec3**, so this asks for `vec4(vec3, float, float)` — five +components into a four-component vector. Invalid GLSL ES on both WebGL 1 and 2: + +``` +THREE.WebGLProgram: Shader Error 0 - VALIDATE_STATUS false +Program Info Log: Vertex shader is not compiled. +ERROR: 0:71: 'constructor' : too many arguments +``` + +The whole post-processing pass therefore fails and the game canvas reads back as **one +colour, 0/64,000 non-black pixels**. Verified in isolation: `vec4(vec3, float)` compiles, +`vec4(vec3, float, float)` does not, on both WebGL 1 and WebGL 2. + +**Fix (verified):** `gl_Position = vec4(position.xy, 0.0, 1.0);` — compiles clean, and the +canvas then reports 143 distinct colours, 63,792/64,000 non-black pixels, scanline banding +visible every 25 rows, and the scene hash changing frame to frame once the aircraft moves. +Applied locally during the audit to confirm it, then reverted — not committed. + +Note this is the kind of bug the current test suite cannot see (see T4): braces balance +perfectly. + ## T7 — Minor / housekeeping - `index.html` requests `/favicon.ico` → 404 on every load. Add one, or drop the request. @@ -98,6 +131,43 @@ not the same thing. --- +## How to run it headless (no GPU, verified working) + +This LXC has no GPU access — `lspci`/`lsmod` inside an LXC report the **Proxmox host's** +hardware (an RTX 3090 shows up), but the container has no `/dev/dri` and no `/dev/nvidia*`, +so nothing is usable. Software rendering is the only option and it does work. + +The combination that works: + +```bash +CHROME=/root/.cache/ms-playwright/chromium-1234/chrome-linux64/chrome +$CHROME --headless=new --no-sandbox --disable-gpu-sandbox \ + --use-angle=swiftshader --enable-unsafe-swiftshader \ + --remote-debugging-port=9223 --user-data-dir=/tmp/probe --no-first-run +# then attach with playwright-core: chromium.connectOverCDP('http://127.0.0.1:9223') +``` + +Gotchas, all learned the hard way: + +- **Playwright's own launcher defeats this.** `chromium.launch()` injects defaults + (`--disable-gpu` among them) that silently kill the GPU process; every attempt via + `launch()` returned `webgl: NULL` or crashed the page. Launch Chrome directly and attach + over CDP instead. +- `--use-gl=swiftshader` (legacy path) crashes the page. Use `--use-angle=swiftshader`. +- `--use-angle=vulkan` + Mesa lavapipe (`VK_DRIVER_FILES=/usr/share/vulkan/icd.d/lvp_icd.json`) + brings the GPU process up (`GPU.EstablishGpuChannel` records samples) but still yields no + WebGL context. SwiftShader is the path that works. +- Headful under `xvfb-run` crashes the page. `--headless=new` is required. +- `--dump-dom` races the page script — it captures before the probe writes its result. +- WebGL clears the drawing buffer after each frame (`preserveDrawingBuffer: false`), so a + synchronous `readPixels` returns all-black. Sample inside a `requestAnimationFrame` + callback, or the measurement is meaningless. +- `readPixels` origin is bottom-left; flip rows before reporting them as screen rows. +- Screenshots via CDP fail here (`Unable to capture screenshot`), so verification is by + pixel readback and colour histograms rather than images. + +--- + ## Suggested order T1 → T4 (tests first would be better, but T1 is the reason the project is worth keeping)