Only reallocate the GPU buffer if our CPU capacity changes

Previously, every single render was re-allocating a fairly large (~1MB)
buffer on the GPU. The recommended best practice is to allocate once and
then use `glBufferSubData` to update the memory in-place on the GPU.

We now track the last size we sent to the GPU, compare it to our copy on
the CPU, and if it _grows_ then we reallocate the GPU buffer. If it
shrinks we leave the GPU as-is for now. After this, we use the sub-data
routines to update the data in place.
This commit is contained in:
Mitchell Hashimoto
2022-08-19 10:25:19 -07:00
parent 390d95a5af
commit 4ca45936f7
2 changed files with 35 additions and 2 deletions

View File

@ -27,6 +27,11 @@ cell_size: CellSize,
/// The current set of cells to render.
cells: std.ArrayListUnmanaged(GPUCell),
/// The size of the cells list that was sent to the GPU. This is used
/// to detect when the cells array was reallocated/resized and handle that
/// accordingly.
gl_cells_size: usize = 0,
/// Shader program for cell rendering.
program: gl.Program,
vao: gl.VertexArray,
@ -497,7 +502,7 @@ pub fn flushAtlas(self: *Grid) !void {
self.atlas_dirty = false;
}
pub fn render(self: Grid) !void {
pub fn render(self: *Grid) !void {
const t = trace(@src());
defer t.end();
@ -518,7 +523,25 @@ pub fn render(self: Grid) !void {
// Bind VBO and set data
var binding = try self.vbo.bind(.ArrayBuffer);
defer binding.unbind();
try binding.setData(self.cells.items, .StaticDraw);
// Our allocated buffer on the GPU is smaller than our capacity.
// We reallocate a new buffer with the full new capacity.
if (self.gl_cells_size < self.cells.capacity) {
log.info("reallocating GPU buffer old={} new={}", .{
self.gl_cells_size,
self.cells.capacity,
});
try binding.setDataNullManual(
@sizeOf(GPUCell) * self.cells.capacity,
.StaticDraw,
);
self.gl_cells_size = self.cells.capacity;
}
// We always set the data using subdata if possible to avoid reallocation
// on the GPU.
try binding.setSubData(0, self.cells.items);
// Bind our texture
try gl.Texture.active(gl.c.GL_TEXTURE0);

View File

@ -69,6 +69,16 @@ pub const Binding = struct {
try errors.getError();
}
/// Same as setDataNull but lets you manually specify the buffer size.
pub inline fn setDataNullManual(
b: Binding,
size: usize,
usage: Usage,
) !void {
c.glBufferData(@enumToInt(b.target), @intCast(c_long, size), null, @enumToInt(usage));
try errors.getError();
}
fn dataInfo(data: anytype) struct {
size: isize,
ptr: *const anyopaque,