From ec3d98d2240e53410afd7976d5514e75e2af4b71 Mon Sep 17 00:00:00 2001 From: indicozy Date: Mon, 9 Nov 2026 23:06:14 +0500 Subject: [PATCH] gl: replace a Stream buffer's storage on update instead of writing into 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 --- src/graphics/gl.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/graphics/gl.rs b/src/graphics/gl.rs index 5fb34c096..1d97283c2 100644 --- a/src/graphics/gl.rs +++ b/src/graphics/gl.rs @@ -25,6 +25,7 @@ pub mod raw_gl { struct Buffer { gl_buf: GLuint, buffer_type: BufferType, + usage: BufferUsage, size: usize, // Dimension of the indices for this buffer, // used only as a type argument for glDrawElements and can be @@ -1376,6 +1377,7 @@ impl RenderingBackend for GlContext { let buffer = Buffer { gl_buf, buffer_type: type_, + usage, size, index_type, }; @@ -1404,7 +1406,16 @@ impl RenderingBackend for GlContext { self.cache.store_buffer_binding(gl_target); self.cache .bind_buffer(gl_target, buffer.gl_buf, buffer.index_type); - unsafe { glBufferSubData(gl_target, 0, size as _, data.ptr as _) }; + if buffer.usage == BufferUsage::Stream { + // The GPU may still be reading this buffer from an earlier draw. A + // glBufferSubData would then make mobile drivers (Adreno) copy the whole + // buffer before the write. glBufferData with the new data instead hands + // the driver fresh storage of just the bytes written: no copy, and no + // reallocation of the full capacity either. + unsafe { glBufferData(gl_target, size as _, data.ptr as _, gl_usage(&buffer.usage)) }; + } else { + unsafe { glBufferSubData(gl_target, 0, size as _, data.ptr as _) }; + } self.cache.restore_buffer_binding(gl_target); }