Repository navigation
perf(head-turn): move the depth displacement into the vertex shader - #101
Conversation
Issue #100. The owner saw the public head-turn demo drop frames on a retina screen; measured at DPR 2, 1728×1000 the frame callback ran p50 7.5 / p99 11.5 ms on 2026-10-26. `app.js` rewrote every part's vertex array from its base and depth term on each changed frame (~132k vertices) and re-submitted all 224,144 triangles. Each mesh's base positions, UVs, depth displacement and indices now upload once at load; the frame callback only updates the dial and gaze uniforms of a Pixi 8.22.0 mesh vertex shader (the bundled runtime gains three lines exporting its own Shader / GlProgram — no runtime replacement). The controls, their defaults and the eye aperture are unchanged. Measured on the pool (headless Chromium, SwiftShader, DPR 2, one exclusive job, the same instrument for both variants, 600 callbacks after 120 warm): | path | p50 | p99 | | --- | ---: | ---: | | CPU (before) | 2.9 ms | 5.8 ms | | GPU (this) | 0.2 ms | 0.5 ms | Pixels: 11 dial states fixed before measuring, every pixel compared with no alignment against the CPU path — mean |ΔRGBA| ≤ 0.00008/255 on every state, at most 0.00000051 of pixels with any channel > 8 (bounds frozen beforehand: 0.05/255 and 0.001; Float64 CPU sums vs Float32 shader sums leave sparse edge-coverage deltas). Uploads: all 76 mesh buffers once at load, zero after. Not measured: Apple hardware or a physical retina display — the pool figures are a software-renderer CPU-callback time, not frame interval. Manifest and `HEAD_TURN_BUNDLE_SHA256` regenerated; the bundle README's provenance table has the new rows. `bun test src/head-turn.test.ts`, `bun run build`, `bun run verify` pass. Co-Authored-By: Naru <342599188+fj-naru[bot]@users.noreply.github.com> Co-Authored-By: Roa <342598975+fj-roa[bot]@users.noreply.github.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3c3b953e39
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // Pixi 8.22.0: retain the meshes and their buffers; a custom shader disables batching. | ||
| const frame={order:parts.map(p=>p.slot)},maskSprites=[]; | ||
| painter.render(frame); | ||
| const vertex=`#version 300 es |
There was a problem hiding this comment.
Preserve the renderer's WebGL 1 fallback
When WebGL 2 context creation is unavailable but WebGL 1 works, the bundled renderer's createContext still falls back to getContext("webgl", ...). Both new shaders explicitly require GLSL ES 3.00, and the bundled GlProgram preserves that version rather than applying its WebGL 1 compatibility defines, so the meshes can no longer render in that fallback context. The previous built-in mesh shader supported this path. Remove the explicit version declarations to use Pixi's compatibility processing, or provide a WebGL 1 shader variant.
Useful? React with 👍 / 👎.
|
Review disposition (HQ): the finding (P2 badge — the GLSL ES 3.00 shaders drop the WebGL 1 fallback the previous mesh shader supported) is a real regression and is adopted; fix follows on this PR with a forced-WebGL-1 parity check. No re-review. |
Review on #101: both new shaders declared `#version 300 es`, and the bundled Pixi 8.22.0 `GlProgram` keeps an explicit version instead of applying its WebGL 1 compatibility preprocessing, so a context that fell back to WebGL 1 failed to link them — the previous built-in mesh shader drew there. The two version lines are removed; Pixi's preprocessors now emit the ES 3.00 form on WebGL 2 and map in/out/finalColor/texture to attribute/varying/gl_FragColor/texture2D on WebGL 1 (GlProgram.ts 134–164, addProgramDefines.ts, insertVersion.ts at v8.22.0). Pool, Chromium, DPR 2: with every webgl2 request refused, the actual context is WebGL 1.0 and both shaders link; the 11 frozen dial states match a fresh CPU reference on the same context within the unchanged bounds (worst mean |ΔRGBA| 0.00008/255, worst fraction > 8 0.00000051), as on WebGL 2; the pre-repair shaders fail to link on forced WebGL 1 (negative control). Callback p99 0.5 ms on WebGL 2 and 0.4 ms on WebGL 1 (600 samples each, exclusive job); 76 mesh buffers upload once, 0 after. `bun test src/head-turn.test.ts` 7 pass, build and verify pass. Co-Authored-By: Naru <342599188+fj-naru[bot]@users.noreply.github.com> Co-Authored-By: Roa <342598975+fj-roa[bot]@users.noreply.github.com>
Closes #100. The head-turn demo rewrote ~132k vertices per changed frame on the CPU and re-sent 224k triangles; the owner saw it drop frames on retina. Now each mesh uploads once and the frame only sets shader uniforms (Pixi 8.22.0 mesh shader from the already-bundled runtime).
Pixel parity, 11 states frozen before measuring: worst mean |ΔRGBA| 0.00008/255, worst fraction of pixels > 8 0.00000051 (bounds 0.05/255 and 0.001). Not measured: Apple hardware / a physical retina screen — these are software-renderer callback times.
Merging deploys the demo.
🤖 Generated with Claude Code