Repository navigation
Conversation
…to it buffer_update used glBufferSubData for every buffer. When the GPU may still read the buffer from an earlier draw (the usual case for Stream buffers that are rewritten every frame), tiled mobile GPUs such as Adreno copy the whole buffer before the write. For Stream buffers, upload with glBufferData of the written size instead: the driver hands out fresh storage and drops the old one once the GPU is done with it. Immutable and Dynamic buffers are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
|
Closing as a duplicate of #682, which makes the same change (and was first). Our Adreno measurements are posted there. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
buffer_updatewrites into a buffer withglBufferSubData. When the GPU may still read that buffer from an earlier draw, a tiled mobile GPU has to either wait for it or copy it first. Adreno copies the whole buffer. macroquad hits this case every frame: each draw call slot owns a Stream vertex buffer ofdraw_call_vertex_capacity(10000 vertices, about 400 KB) plus an index buffer, and rewrites it every frame. macroquad#807 removed the CPU-side cost of that, but the GPU-side upload is still made once per draw call.This PR changes only Stream buffers. For them,
buffer_updatenow uploads withglBufferDataof the written size. The driver gets fresh storage and frees the old storage once the GPU is done with it, so nothing is copied, and only the bytes written are allocated. Immutable and Dynamic buffers keepglBufferSubData. The Metal backend already avoids the same hazard by rotating buffers (see #644); this gives the GL path the same property without extra buffers.Numbers
A macroquad 0.4 game (Razdor, 2.5D map view, a few dozen draw calls per frame), idle map, release build. The only difference between the two builds is this commit (on miniquad 0.4.11):
Earlier, before macroquad batching was reduced on our side, simpleperf on the phone put about 60% of the frame's CPU time in
memmoveand 14% inioctl, both underglBufferSubDatain the Adreno driver.Alternatives tried
glBufferData(capacity, NULL)and thenglBufferSubData) also removes the copy. But it reallocates the full capacity on every update, which made llvmpipe 55–100% slower in the same test.QuadGldoesn't help:QuadGl::drawruns several times per frame (every camera change flushes), so the same slots are rewritten within a frame.This was found and written with Claude Code (AI-assisted). I measured it on the device and reviewed the change myself.