perf(rendering): split tint out of the transform buffer into a dedicated 8-bit resource - #422
perf(rendering): split tint out of the transform buffer into a dedicated 8-bit resource#422Exoridus wants to merge 4 commits into
Conversation
…ted 8-bit resource The per-instance transform row shrinks from 12 to 8 floats (matrix, translation, snap mode). Tint moves into its own natively 8-bit normalized resource: an rgba8 DataTexture on WebGL2, a packed u32 decoded via unpack4x8unorm on WebGPU. RGB stays exact (0..255 integers re-encoded losslessly); alpha is quantized to 255 steps, which is the only behavioural difference. Both backends carry the change through sprite, mesh (live and retained replay) and the retained bundles for nine-slice, repeating sprites and tilemap chunks. Those bundles share getBindGroup, so an includeTint flag keeps the layout binding consistent instead of tripping WebGPU validation. Browser tests hardcode their own mock shaders because the Vitest plugin stubs real .vert/.frag sources to empty strings; those shaders read out of bounds after the layout change and are updated accordingly.
|
To use Codex here, create an environment for this repo. |
Bundle ReportChanges will increase total bundle size by 470.64kB (1.92%) ⬆️. This is within the configured threshold ✅ Detailed changes
Affected Assets, Files, and Routes:view changes for bundle: exojs-tilemap-esmAssets Changed:
Files in
view changes for bundle: exo-esm-modules-esmAssets Changed:
Files in
Files in
Files in
Files in
Files in
Files in
Files in
Files in
Files in
Files in
Files in
Files in
Files in
Files in
Files in
Files in
view changes for bundle: site-server-esmAssets Changed:
view changes for bundle: exo-iife-min-Exo-iifeAssets Changed:
Files in
view changes for bundle: exo-esm-esmAssets Changed:
Files in
view changes for bundle: exo-full-iife-Exo-iifeAssets Changed:
Files in
|
❌ 3 Tests Failed:
View the top 3 failed test(s) by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
…nsform/tint sync allocation The perf suite under test/perf/rendering was not touched by the tint split: fakeWebGl2.ts still classified transform-texture uploads by the old width-3 (12-float/3-texel) row, and three tests hardcoded the old `* 12` row stride and `* 48` coalesced-upload byte count. Updated all to the new width-2 (8-float/2-texel) layout. Separately, the split doubled how often WebGl2Backend's partial DataTexture upload path runs per flush (transform + tint each sync independently), and that path allocated a fresh packing buffer on every call. In barrier-heavy scenes (many small flushes per frame) this is a real, reproducible allocation regression - confirmed by diffing V8 allocation-profiler call sites against the pre-split revision on the same machine, which isolated the entire increase to newly-introduced Uint8Array/Float32Array allocations in WebGl2Backend._syncTexture. Fixed by caching a reusable packing scratch per texture state (grown, never reallocated once sized) and taking a single contiguous copy when a dirty region spans the full texture width instead of looping per row.
…ploads _acquirePartialUploadScratch still allocated a fresh subarray() VIEW object whenever the reused packing buffer's length didn't exactly match the requested length — cheap per call, but with the tint split doubling partial-upload calls (transform + tint, each independently synced) on scenes with many small per-instance textures (1000 mesh bundles), it added up to a real ~15% median allocation regression, tripping the mesh gate in allocation.test.ts (748 KB vs 743 KB budget). Upload via texSubImage2D's srcOffset overload instead: it reads exactly width*height*channels elements starting at offset 0, so the oversized reused buffer can be passed directly with zero allocation, correctness unaffected by unused trailing capacity. Verified locally: mesh gate median 748 -> 694 KB/frame (budget 743 KB), all 7 allocation gates green, data-texture.test.ts and webgl2-retained-group-resources.test.ts green.
Shrinks the per-instance transform row from 12 to 8 floats (matrix, translation, snap mode) and moves tint into its own natively 8-bit normalized resource.
What changed
rgba8DataTextureon WebGL2, packedu32decoded viaunpack4x8unormon WebGPU.getBindGroup, so anincludeTintflag keeps the layout binding consistent instead of tripping WebGPU validation..vert/.fragsources to empty strings). They read out of bounds after the layout change and are updated accordingly.Verification
pnpm verify:quick— clean (0 lint errors, Prettier clean, API docs in sync)pnpm test:core— 333 files / 5395 tests green@codexo/exojs-tilemappackage + browser test: green