UIRenderer.flush wrote to mtl_vbuf at byte offset 0 on every flush. Metal records draw commands but reads the buffer at GPU execution time, so a frame with multiple flushes ended up rendering whatever the LAST writer left in the buffer for every draw. Chess UI hit this hard: each of the 32 pieces in the initial position triggers two bind_texture flushes (atlas -> pieces -> atlas), so ~64 mid-frame flushes silently rendered the final info-panel batch over the board and the sprites. New GPU protocol method update_buffer_at(buf, data, size, byte_offset); Metal impl writes at offset via [*]u8 arithmetic on [buf contents]. UIRenderer tracks mtl_buf_offset (reset in begin, advanced per flush, aligned to 16B, wraps on overflow) and draws each batch with vertex_off = byte_off / UI_VERTEX_BYTES. Metal buffer over-allocated 4x the per-flush max (~3 MB) for headroom. GL path untouched — glBufferData already orphans the storage. 71/71 regression tests pass. Metal-clear example, macOS GL chess, and WASM chess all still build.
2.4 KiB
2.4 KiB