mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 07:46:12 +03:00
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:
@ -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
|
||||||
|
43
src/Grid.zig
43
src/Grid.zig
@ -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
|
||||||
|
@ -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|
|
||||||
|
Reference in New Issue
Block a user