Repository navigation
fix(vk): size the mesh index map in whole 32-primitive blocks - #2312
Conversation
Flashing triangles over Kokiri's foliage and ground: mesh programs write the index bytes of every 32-primitive block, lanes past max_primitives too, and with 126 primitives (most of OoT's) the writes past the map were clamped onto the last primitive's last corner, a stray vertex. The map is now sized like the attributes, in whole blocks of 32 primitives. test_mesh_point_topology checks the new size. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017tiHucJGVjM9vhcGkzmPMu
hckrman101
left a comment
There was a problem hiding this comment.
Looks right to me. The map is laid out linearly (byte 4 + corners * primitive + corner, both in the epilogue and in the fallback's record copy), so a program that writes index bytes for a whole 32-lane primitive block reaches up to 4 + corners * DivCeil(max_primitives, 32) * 32 - 1. With the old size, EmitIsbeWriteMapU8's Clamped(address, mesh_map_bytes) folded every out-of-range byte onto the last one, so for 126 primitives the 6 bytes of lanes 126/127 raced with primitive 125's last corner. That fits the flashing-triangle symptom well, and sizing the map in whole blocks matches how mesh_attribute_words already sizes the primitive attributes.
Other things I checked:
- Readers only look at primitives
< count(bounded bymax_primitives), so the extra bytes are scratch and never read back. The prologue zeroing walks the new size too. mesh_mapis defined before theif (mesh_fallback) return;, so the compute fallback path gets the fix as well, not only native mesh shaders.- Cost: at most 31 * 3 extra workgroup words (~372 bytes) per mesh workgroup, which is nothing next to the attribute region.
max_primitives == 0still gives 4 bytes, same as before.- The test change tracks the new expression, nothing weakened.
No overlap with #2311 (vk_draw.cpp) or #2362 (vk_main.cpp / vk_menu.cpp); they touch different files and this fix helps whichever mesh path ends up the default on RADV.
CI: only the title check has run; the build and game-free regression workflows are waiting for approval to run on this fork, so those should go green before merging.
…ed#2312) Flashing triangles over the foliage and ground in Kokiri Forest: a stray vertex from mesh programs with 126 primitives, which are most of OoT's. **What changed:** a mesh program writes the index bytes of every 32-primitive block, including lanes past `max_primitives` (a count of 0 wraps `min(count - 1, lane)` onto every lane). The index map was sized to `max_primitives`, so those writes were clamped onto its last byte, the last primitive's last corner. `DefineMesh` now sizes the map like the attributes, in whole blocks of 32 primitives. nxnative found the same thing ("primitive index bytes for every primitive block"). `tests/test_mesh_point_topology.py` checks the new size. **Tested:** Steam Deck LCD, Mesa RADV, Kokiri Forest. I compared 150–200 screenshots per run, each against the previous one: no flashes remain, and the biggest changes are Navi moving and the lighting fading in. `tests/run_all.py`: no new failures against `main`. <img width="1269" height="712" alt="image" src="https://github.com/user-attachments/assets/9b159590-ee90-4b68-bf39-ef0373c56ad4" /> 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Flashing triangles over the foliage and ground in Kokiri Forest: a stray vertex from mesh programs with 126 primitives, which are most of OoT's.
What changed: a mesh program writes the index bytes of every 32-primitive block, including lanes past
max_primitives(a count of 0 wrapsmin(count - 1, lane)onto every lane). The index map was sized tomax_primitives, so those writes were clamped onto its last byte, the last primitive's last corner.DefineMeshnow sizes the map like the attributes, in whole blocks of 32 primitives. nxnative found the same thing ("primitive index bytes for every primitive block").tests/test_mesh_point_topology.pychecks the new size.Tested: Steam Deck LCD, Mesa RADV, Kokiri Forest. I compared 150–200 screenshots per run, each against the previous one: no flashes remain, and the biggest changes are Navi moving and the lighting fading in.
tests/run_all.py: no new failures againstmain.🤖 Generated with Claude Code