get rid of Z buffer stuff

This optimization will take more work, and we already got a lot of the
way there by optimizing how we send data down to the GPU.
This commit is contained in:
Mitchell Hashimoto
2022-08-19 13:47:57 -07:00
parent 2351f88631
commit eeaad799e5
3 changed files with 5 additions and 61 deletions

View File

@ -35,17 +35,6 @@ layout (location = 5) in vec4 bg_color_in;
// the entire terminal grid in a single GPU pass. // the entire terminal grid in a single GPU pass.
layout (location = 6) in uint mode_in; layout (location = 6) in uint mode_in;
// The Z-depth. This is between 0.0 and 1.0. This is used to paint newer
// values on top of older ones to limit the amount of data that needs to
// be sent to the GPU.
//
// 0 is further back/away, 1 is front/near
//
// TODO: It would be better to just send the Z value as part of grid_coord
// and normalize it to NDC and then linearly transform it but this was easy
// to get going more quickly.
layout (location = 7) in float grid_z;
// The background or foreground color for the fragment, depending on // The background or foreground color for the fragment, depending on
// whether this is a background or foreground pass. // whether this is a background or foreground pass.
flat out vec4 color; flat out vec4 color;
@ -96,7 +85,7 @@ void main() {
// Our Z value. For now we just use grid_z directly but we pull it // Our Z value. For now we just use grid_z directly but we pull it
// out here so the variable name is more uniform to our cell_pos and // out here so the variable name is more uniform to our cell_pos and
// in case we want to do any other math later. // in case we want to do any other math later.
float cell_z = grid_z; float cell_z = 0.0;
// Turn the cell position into a vertex point depending on the // Turn the cell position into a vertex point depending on the
// gl_VertexID. Since we use instanced drawing, we have 4 vertices // gl_VertexID. Since we use instanced drawing, we have 4 vertices

View File

@ -58,9 +58,6 @@ foreground: terminal.color.RGB,
/// Default background color /// Default background color
background: terminal.color.RGB, background: terminal.color.RGB,
/// The current value of the Z buffer.
cell_z: u16 = 0,
/// Available cursor styles for drawing. The values represents the mode value /// Available cursor styles for drawing. The values represents the mode value
/// in the shader. /// in the shader.
pub const CursorStyle = enum(u8) { pub const CursorStyle = enum(u8) {
@ -112,10 +109,6 @@ const GPUCell = struct {
/// uint mode /// uint mode
mode: u8, mode: u8,
/// float grid_z. This is normalized to a float by dividing by the
/// max int value.
grid_z: u16,
}; };
pub fn init(alloc: Allocator, config: *const Config) !Grid { pub fn init(alloc: Allocator, config: *const Config) !Grid {
@ -214,8 +207,6 @@ pub fn init(alloc: Allocator, config: *const Config) !Grid {
try vbobind.attributeAdvanced(5, 4, gl.c.GL_UNSIGNED_BYTE, false, @sizeOf(GPUCell), offset); try vbobind.attributeAdvanced(5, 4, gl.c.GL_UNSIGNED_BYTE, false, @sizeOf(GPUCell), offset);
offset += 4 * @sizeOf(u8); offset += 4 * @sizeOf(u8);
try vbobind.attributeIAdvanced(6, 1, gl.c.GL_UNSIGNED_BYTE, @sizeOf(GPUCell), offset); try vbobind.attributeIAdvanced(6, 1, gl.c.GL_UNSIGNED_BYTE, @sizeOf(GPUCell), offset);
offset += 1 * @sizeOf(u8);
try vbobind.attributeAdvanced(7, 1, gl.c.GL_UNSIGNED_SHORT, true, @sizeOf(GPUCell), offset);
try vbobind.enableAttribArray(0); try vbobind.enableAttribArray(0);
try vbobind.enableAttribArray(1); try vbobind.enableAttribArray(1);
try vbobind.enableAttribArray(2); try vbobind.enableAttribArray(2);
@ -223,7 +214,6 @@ pub fn init(alloc: Allocator, config: *const Config) !Grid {
try vbobind.enableAttribArray(4); try vbobind.enableAttribArray(4);
try vbobind.enableAttribArray(5); try vbobind.enableAttribArray(5);
try vbobind.enableAttribArray(6); try vbobind.enableAttribArray(6);
try vbobind.enableAttribArray(7);
try vbobind.attributeDivisor(0, 1); try vbobind.attributeDivisor(0, 1);
try vbobind.attributeDivisor(1, 1); try vbobind.attributeDivisor(1, 1);
try vbobind.attributeDivisor(2, 1); try vbobind.attributeDivisor(2, 1);
@ -231,7 +221,6 @@ pub fn init(alloc: Allocator, config: *const Config) !Grid {
try vbobind.attributeDivisor(4, 1); try vbobind.attributeDivisor(4, 1);
try vbobind.attributeDivisor(5, 1); try vbobind.attributeDivisor(5, 1);
try vbobind.attributeDivisor(6, 1); try vbobind.attributeDivisor(6, 1);
try vbobind.attributeDivisor(7, 1);
// Build our texture // Build our texture
const tex = try gl.Texture.create(); const tex = try gl.Texture.create();
@ -303,23 +292,18 @@ pub fn rebuildCells(self: *Grid, term: Terminal) !void {
// * 3 for background modes and cursor and underlines // * 3 for background modes and cursor and underlines
// + 1 for cursor // + 1 for cursor
// * N for cache space for changes (term.screen.rows * term.screen.cols * 3) + 1,
((term.screen.rows * term.screen.cols * 3) + 1) * 10,
); );
// We've written no data to the GPU, refresh it all // We've written no data to the GPU, refresh it all
self.gl_cells_written = 0; self.gl_cells_written = 0;
// Reset the Z buffer at the end no matter what since we're fresh.
defer self.cell_z = 0;
// Build each cell // Build each cell
var rowIter = term.screen.rowIterator(.viewport); var rowIter = term.screen.rowIterator(.viewport);
var y: usize = 0; var y: usize = 0;
while (rowIter.next()) |line| { while (rowIter.next()) |line| {
defer y += 1; defer y += 1;
for (line) |cell, x| { for (line) |cell, x| {
self.cell_z = 0;
assert(try self.updateCell(term, cell, x, y)); assert(try self.updateCell(term, cell, x, y));
} }
} }
@ -338,9 +322,7 @@ pub fn finalizeCells(self: *Grid, term: Terminal) !void {
self.addCursor(term); self.addCursor(term);
// If we're out of space or we have no more Z-space, rebuild. // If we're out of space or we have no more Z-space, rebuild.
if (self.cells.items.len == self.cells.capacity or if (self.cells.items.len == self.cells.capacity) {
self.cell_z == std.math.maxInt(@TypeOf(self.cell_z)))
{
log.info("cell cache full, rebuilding from scratch", .{}); log.info("cell cache full, rebuilding from scratch", .{});
try self.rebuildCells(term); try self.rebuildCells(term);
} }
@ -349,6 +331,7 @@ pub fn finalizeCells(self: *Grid, term: Terminal) !void {
if (self.atlas_dirty) { if (self.atlas_dirty) {
log.info("atlas dirty, flushing changes", .{}); log.info("atlas dirty, flushing changes", .{});
try self.flushAtlas(); try self.flushAtlas();
self.atlas_dirty = false;
} }
} }
@ -367,9 +350,6 @@ fn addCursor(self: *Grid, term: Terminal) void {
.bg_g = 0xFF, .bg_g = 0xFF,
.bg_b = 0xFF, .bg_b = 0xFF,
.bg_a = 255, .bg_a = 255,
// The cursor is always at the very front
.grid_z = 255,
}); });
} }
} }
@ -445,25 +425,12 @@ pub fn updateCell(
}; };
if (self.cells.items.len + needed > self.cells.capacity) return false; if (self.cells.items.len + needed > self.cells.capacity) return false;
// Bump our Z value. The "zbump" is the max amount we could increase
// the Z value this call. If it could theoretically go over the max
// int size of the Z type, then we return false -- we need a rebuild.
const zbump = 2;
const zmax = std.math.maxInt(@TypeOf(self.cell_z));
const zmax_padded = zmax - (zbump * 2);
if (self.cell_z > zmax_padded) {
self.cell_z = zmax;
return false;
}
self.cell_z += zbump;
// If the cell has a background, we always draw it. // If the cell has a background, we always draw it.
if (colors.bg) |rgb| { if (colors.bg) |rgb| {
self.cells.appendAssumeCapacity(.{ self.cells.appendAssumeCapacity(.{
.mode = 1, .mode = 1,
.grid_col = @intCast(u16, x), .grid_col = @intCast(u16, x),
.grid_row = @intCast(u16, y), .grid_row = @intCast(u16, y),
.grid_z = self.cell_z,
.glyph_x = 0, .glyph_x = 0,
.glyph_y = 0, .glyph_y = 0,
.glyph_width = 0, .glyph_width = 0,
@ -502,7 +469,6 @@ pub fn updateCell(
.mode = 2, .mode = 2,
.grid_col = @intCast(u16, x), .grid_col = @intCast(u16, x),
.grid_row = @intCast(u16, y), .grid_row = @intCast(u16, y),
.grid_z = self.cell_z + 1,
.glyph_x = glyph.atlas_x, .glyph_x = glyph.atlas_x,
.glyph_y = glyph.atlas_y, .glyph_y = glyph.atlas_y,
.glyph_width = glyph.width, .glyph_width = glyph.width,
@ -525,7 +491,6 @@ pub fn updateCell(
.mode = 6, // underline .mode = 6, // underline
.grid_col = @intCast(u16, x), .grid_col = @intCast(u16, x),
.grid_row = @intCast(u16, y), .grid_row = @intCast(u16, y),
.grid_z = self.cell_z + 1,
.glyph_x = 0, .glyph_x = 0,
.glyph_y = 0, .glyph_y = 0,
.glyph_width = 0, .glyph_width = 0,
@ -584,8 +549,6 @@ fn flushAtlas(self: *Grid) !void {
.UnsignedByte, .UnsignedByte,
self.font_atlas.atlas.data.ptr, self.font_atlas.atlas.data.ptr,
); );
self.atlas_dirty = false;
} }
/// Render renders the current cell state. This will not modify any of /// Render renders the current cell state. This will not modify any of

View File

@ -200,14 +200,6 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo
gl.c.glEnable(gl.c.GL_BLEND); gl.c.glEnable(gl.c.GL_BLEND);
gl.c.glBlendFunc(gl.c.GL_SRC_ALPHA, gl.c.GL_ONE_MINUS_SRC_ALPHA); gl.c.glBlendFunc(gl.c.GL_SRC_ALPHA, gl.c.GL_ONE_MINUS_SRC_ALPHA);
// Depth test since we use the Z-buffer as an optimization to send
// only changed cells to the GPU. As cells are updated, we bump their
// Z value to be closer, so that over time new values are rendered "over"
// values (in reality old values are culled by the depth test, so they
// aren't ever "rendered").
gl.c.glEnable(gl.c.GL_DEPTH_TEST);
gl.c.glDepthFunc(gl.c.GL_LESS);
// Create our terminal grid with the initial window size // Create our terminal grid with the initial window size
const window_size = try window.getSize(); const window_size = try window.getSize();
var grid = try Grid.init(alloc, config); var grid = try Grid.init(alloc, config);
@ -968,7 +960,7 @@ fn renderTimerCallback(t: *libuv.Timer) void {
.a = win.bg_a, .a = win.bg_a,
}; };
gl.clearColor(gl_bg.r, gl_bg.g, gl_bg.b, gl_bg.a); gl.clearColor(gl_bg.r, gl_bg.g, gl_bg.b, gl_bg.a);
gl.clear(gl.c.GL_COLOR_BUFFER_BIT | gl.c.GL_DEPTH_BUFFER_BIT); gl.clear(gl.c.GL_COLOR_BUFFER_BIT);
// For now, rebuild all cells // For now, rebuild all cells
win.grid.rebuildCells(win.terminal) catch |err| win.grid.rebuildCells(win.terminal) catch |err|