mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 00:06:09 +03:00
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:
27
src/Grid.zig
27
src/Grid.zig
@ -27,6 +27,11 @@ cell_size: CellSize,
|
|||||||
/// The current set of cells to render.
|
/// The current set of cells to render.
|
||||||
cells: std.ArrayListUnmanaged(GPUCell),
|
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.
|
/// Shader program for cell rendering.
|
||||||
program: gl.Program,
|
program: gl.Program,
|
||||||
vao: gl.VertexArray,
|
vao: gl.VertexArray,
|
||||||
@ -497,7 +502,7 @@ pub fn flushAtlas(self: *Grid) !void {
|
|||||||
self.atlas_dirty = false;
|
self.atlas_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(self: Grid) !void {
|
pub fn render(self: *Grid) !void {
|
||||||
const t = trace(@src());
|
const t = trace(@src());
|
||||||
defer t.end();
|
defer t.end();
|
||||||
|
|
||||||
@ -518,7 +523,25 @@ pub fn render(self: Grid) !void {
|
|||||||
// Bind VBO and set data
|
// Bind VBO and set data
|
||||||
var binding = try self.vbo.bind(.ArrayBuffer);
|
var binding = try self.vbo.bind(.ArrayBuffer);
|
||||||
defer binding.unbind();
|
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
|
// Bind our texture
|
||||||
try gl.Texture.active(gl.c.GL_TEXTURE0);
|
try gl.Texture.active(gl.c.GL_TEXTURE0);
|
||||||
|
@ -69,6 +69,16 @@ pub const Binding = struct {
|
|||||||
try errors.getError();
|
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 {
|
fn dataInfo(data: anytype) struct {
|
||||||
size: isize,
|
size: isize,
|
||||||
ptr: *const anyopaque,
|
ptr: *const anyopaque,
|
||||||
|
Reference in New Issue
Block a user