rename grid to a renderer, extract to subfolder

"Grid" was a really antiquated name when it had both the screen state
AND the renderering functionality tied together. This hasn't been true
for a LONG time and it is long overdue that this is renamed to its
proper name.

This also begins setting up a folder structure to anticipate future
renderers and rendering functionality. I'm not working on any alternate
renderers right now so the interface isn't expected to be good, just
laying out the files in this way.
This commit is contained in:
Mitchell Hashimoto
2022-10-23 16:47:34 -07:00
parent c3d9ee1d85
commit de9731da1f
5 changed files with 74 additions and 61 deletions

View File

@ -45,13 +45,13 @@ pub fn update(self: *DevMode) !void {
if (imgui.treeNode("Atlas: Greyscale", .{ .default_open = true })) { if (imgui.treeNode("Atlas: Greyscale", .{ .default_open = true })) {
defer imgui.treePop(); defer imgui.treePop();
const atlas = &window.font_group.atlas_greyscale; const atlas = &window.font_group.atlas_greyscale;
try self.atlasInfo(atlas, @intCast(usize, window.grid.texture.id)); try self.atlasInfo(atlas, @intCast(usize, window.renderer.texture.id));
} }
if (imgui.treeNode("Atlas: Color (Emoji)", .{ .default_open = true })) { if (imgui.treeNode("Atlas: Color (Emoji)", .{ .default_open = true })) {
defer imgui.treePop(); defer imgui.treePop();
const atlas = &window.font_group.atlas_color; const atlas = &window.font_group.atlas_color;
try self.atlasInfo(atlas, @intCast(usize, window.grid.texture_color.id)); try self.atlasInfo(atlas, @intCast(usize, window.renderer.texture_color.id));
} }
} }
} }

View File

@ -9,7 +9,7 @@ const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
const assert = std.debug.assert; const assert = std.debug.assert;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Grid = @import("Grid.zig"); const renderer = @import("renderer.zig");
const glfw = @import("glfw"); const glfw = @import("glfw");
const gl = @import("opengl.zig"); const gl = @import("opengl.zig");
const imgui = @import("imgui"); const imgui = @import("imgui");
@ -53,8 +53,8 @@ imgui_ctx: if (DevMode.enabled) *imgui.Context else void,
/// Whether the window is currently focused /// Whether the window is currently focused
focused: bool, focused: bool,
/// The terminal grid attached to this window. /// The renderer for this window.
grid: Grid, renderer: renderer.OpenGL,
/// The underlying pty for this window. /// The underlying pty for this window.
pty: Pty, pty: Pty,
@ -368,14 +368,14 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo
// 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, font_group); var renderer_impl = try renderer.OpenGL.init(alloc, font_group);
try grid.setScreenSize(.{ .width = window_size.width, .height = window_size.height }); try renderer_impl.setScreenSize(.{ .width = window_size.width, .height = window_size.height });
grid.background = .{ renderer_impl.background = .{
.r = config.background.r, .r = config.background.r,
.g = config.background.g, .g = config.background.g,
.b = config.background.b, .b = config.background.b,
}; };
grid.foreground = .{ renderer_impl.foreground = .{
.r = config.foreground.r, .r = config.foreground.r,
.g = config.foreground.g, .g = config.foreground.g,
.b = config.foreground.b, .b = config.foreground.b,
@ -384,14 +384,14 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo
// Set a minimum size that is cols=10 h=4. This matches Mac's Terminal.app // Set a minimum size that is cols=10 h=4. This matches Mac's Terminal.app
// but is otherwise somewhat arbitrary. // but is otherwise somewhat arbitrary.
try window.setSizeLimits(.{ try window.setSizeLimits(.{
.width = @floatToInt(u32, grid.cell_size.width * 10), .width = @floatToInt(u32, renderer_impl.cell_size.width * 10),
.height = @floatToInt(u32, grid.cell_size.height * 4), .height = @floatToInt(u32, renderer_impl.cell_size.height * 4),
}, .{ .width = null, .height = null }); }, .{ .width = null, .height = null });
// Create our pty // Create our pty
var pty = try Pty.open(.{ var pty = try Pty.open(.{
.ws_row = @intCast(u16, grid.size.rows), .ws_row = @intCast(u16, renderer_impl.size.rows),
.ws_col = @intCast(u16, grid.size.columns), .ws_col = @intCast(u16, renderer_impl.size.columns),
.ws_xpixel = @intCast(u16, window_size.width), .ws_xpixel = @intCast(u16, window_size.width),
.ws_ypixel = @intCast(u16, window_size.height), .ws_ypixel = @intCast(u16, window_size.height),
}); });
@ -434,7 +434,7 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo
try stream.readStart(ttyReadAlloc, ttyRead); try stream.readStart(ttyReadAlloc, ttyRead);
// Create our terminal // Create our terminal
var term = try terminal.Terminal.init(alloc, grid.size.columns, grid.size.rows); var term = try terminal.Terminal.init(alloc, renderer_impl.size.columns, renderer_impl.size.rows);
errdefer term.deinit(alloc); errdefer term.deinit(alloc);
// Setup a timer for blinking the cursor // Setup a timer for blinking the cursor
@ -463,7 +463,7 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo
.window = window, .window = window,
.cursor = cursor, .cursor = cursor,
.focused = false, .focused = false,
.grid = grid, .renderer = renderer_impl,
.pty = pty, .pty = pty,
.command = cmd, .command = cmd,
.mouse = .{}, .mouse = .{},
@ -556,7 +556,7 @@ pub fn destroy(self: *Window) void {
log.err("error waiting for command to exit: {}", .{err}); log.err("error waiting for command to exit: {}", .{err});
self.terminal.deinit(self.alloc); self.terminal.deinit(self.alloc);
self.grid.deinit(); self.renderer.deinit();
self.window.destroy(); self.window.destroy();
self.terminal_cursor.timer.close((struct { self.terminal_cursor.timer.close((struct {
@ -662,19 +662,19 @@ fn sizeCallback(window: glfw.Window, width: i32, height: i32) void {
// Update our grid so that the projections on render are correct. // Update our grid so that the projections on render are correct.
const win = window.getUserPointer(Window) orelse return; const win = window.getUserPointer(Window) orelse return;
win.grid.setScreenSize(.{ win.renderer.setScreenSize(.{
.width = px_size.width, .width = px_size.width,
.height = px_size.height, .height = px_size.height,
}) catch |err| log.err("error updating grid screen size err={}", .{err}); }) catch |err| log.err("error updating grid screen size err={}", .{err});
// Update the size of our terminal state // Update the size of our terminal state
win.terminal.resize(win.alloc, win.grid.size.columns, win.grid.size.rows) catch |err| win.terminal.resize(win.alloc, win.renderer.size.columns, win.renderer.size.rows) catch |err|
log.err("error updating terminal size: {}", .{err}); log.err("error updating terminal size: {}", .{err});
// Update the size of our pty // Update the size of our pty
win.pty.setSize(.{ win.pty.setSize(.{
.ws_row = @intCast(u16, win.grid.size.rows), .ws_row = @intCast(u16, win.renderer.size.rows),
.ws_col = @intCast(u16, win.grid.size.columns), .ws_col = @intCast(u16, win.renderer.size.columns),
.ws_xpixel = @intCast(u16, width), .ws_xpixel = @intCast(u16, width),
.ws_ypixel = @intCast(u16, height), .ws_ypixel = @intCast(u16, height),
}) catch |err| log.err("error updating pty screen size err={}", .{err}); }) catch |err| log.err("error updating pty screen size err={}", .{err});
@ -1007,7 +1007,7 @@ fn scrollCallback(window: glfw.Window, xoff: f64, yoff: f64) void {
// Positive is up // Positive is up
const sign: isize = if (yoff > 0) -1 else 1; const sign: isize = if (yoff > 0) -1 else 1;
const delta: isize = sign * @max(@divFloor(win.grid.size.rows, 15), 1); const delta: isize = sign * @max(@divFloor(win.renderer.size.rows, 15), 1);
log.info("scroll: delta={}", .{delta}); log.info("scroll: delta={}", .{delta});
win.terminal.scrollViewport(.{ .delta = delta }) catch |err| win.terminal.scrollViewport(.{ .delta = delta }) catch |err|
log.err("error scrolling viewport err={}", .{err}); log.err("error scrolling viewport err={}", .{err});
@ -1367,10 +1367,10 @@ fn cursorPosCallback(
// //
// the boundary point at which we consider selection or non-selection // the boundary point at which we consider selection or non-selection
const cell_xboundary = win.grid.cell_size.width * 0.6; const cell_xboundary = win.renderer.cell_size.width * 0.6;
// first xpos of the clicked cell // first xpos of the clicked cell
const cell_xstart = @intToFloat(f32, win.mouse.left_click_point.x) * win.grid.cell_size.width; const cell_xstart = @intToFloat(f32, win.mouse.left_click_point.x) * win.renderer.cell_size.width;
const cell_start_xpos = win.mouse.left_click_xpos - cell_xstart; const cell_start_xpos = win.mouse.left_click_xpos - cell_xstart;
// If this is the same cell, then we only start the selection if weve // If this is the same cell, then we only start the selection if weve
@ -1445,7 +1445,7 @@ fn posToViewport(self: Window, xpos: f64, ypos: f64) terminal.point.Viewport {
return .{ return .{
.x = if (xpos < 0) 0 else x: { .x = if (xpos < 0) 0 else x: {
// Our cell is the mouse divided by cell width // Our cell is the mouse divided by cell width
const cell_width = @floatCast(f64, self.grid.cell_size.width); const cell_width = @floatCast(f64, self.renderer.cell_size.width);
const x = @floatToInt(usize, xpos / cell_width); const x = @floatToInt(usize, xpos / cell_width);
// Can be off the screen if the user drags it out, so max // Can be off the screen if the user drags it out, so max
@ -1454,7 +1454,7 @@ fn posToViewport(self: Window, xpos: f64, ypos: f64) terminal.point.Viewport {
}, },
.y = if (ypos < 0) 0 else y: { .y = if (ypos < 0) 0 else y: {
const cell_height = @floatCast(f64, self.grid.cell_size.height); const cell_height = @floatCast(f64, self.renderer.cell_size.height);
const y = @floatToInt(usize, ypos / cell_height); const y = @floatToInt(usize, ypos / cell_height);
break :y @min(y, self.terminal.rows - 1); break :y @min(y, self.terminal.rows - 1);
}, },
@ -1584,23 +1584,23 @@ fn renderTimerCallback(t: *libuv.Timer) void {
// Setup our cursor settings // Setup our cursor settings
if (win.focused) { if (win.focused) {
win.grid.cursor_visible = win.terminal_cursor.visible and !win.terminal_cursor.blink; win.renderer.cursor_visible = win.terminal_cursor.visible and !win.terminal_cursor.blink;
win.grid.cursor_style = Grid.CursorStyle.fromTerminal(win.terminal_cursor.style) orelse .box; win.renderer.cursor_style = renderer.OpenGL.CursorStyle.fromTerminal(win.terminal_cursor.style) orelse .box;
} else { } else {
win.grid.cursor_visible = true; win.renderer.cursor_visible = true;
win.grid.cursor_style = .box_hollow; win.renderer.cursor_style = .box_hollow;
} }
// Calculate foreground and background colors // Calculate foreground and background colors
const bg = win.grid.background; const bg = win.renderer.background;
const fg = win.grid.foreground; const fg = win.renderer.foreground;
defer { defer {
win.grid.background = bg; win.renderer.background = bg;
win.grid.foreground = fg; win.renderer.foreground = fg;
} }
if (win.terminal.modes.reverse_colors) { if (win.terminal.modes.reverse_colors) {
win.grid.background = fg; win.renderer.background = fg;
win.grid.foreground = bg; win.renderer.foreground = bg;
} }
// Set our background // Set our background
@ -1624,15 +1624,15 @@ fn renderTimerCallback(t: *libuv.Timer) void {
gl.clear(gl.c.GL_COLOR_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.renderer.rebuildCells(&win.terminal) catch |err|
log.err("error calling rebuildCells in render timer err={}", .{err}); log.err("error calling rebuildCells in render timer err={}", .{err});
// Finalize the cells prior to render // Finalize the cells prior to render
win.grid.finalizeCells(&win.terminal) catch |err| win.renderer.finalizeCells(&win.terminal) catch |err|
log.err("error calling updateCells in render timer err={}", .{err}); log.err("error calling updateCells in render timer err={}", .{err});
// Render the grid // Render the grid
win.grid.render() catch |err| { win.renderer.render() catch |err| {
log.err("error rendering grid: {}", .{err}); log.err("error rendering grid: {}", .{err});
return; return;
}; };
@ -1812,7 +1812,7 @@ pub fn setMode(self: *Window, mode: terminal.Mode, enabled: bool) !void {
self.terminal.setDeccolmSupported(enabled); self.terminal.setDeccolmSupported(enabled);
// Force resize back to the window size // Force resize back to the window size
self.terminal.resize(self.alloc, self.grid.size.columns, self.grid.size.rows) catch |err| self.terminal.resize(self.alloc, self.renderer.size.columns, self.renderer.size.rows) catch |err|
log.err("error updating terminal size: {}", .{err}); log.err("error updating terminal size: {}", .{err});
}, },

View File

@ -105,16 +105,15 @@ fn glfwErrorCallback(code: glfw.Error, desc: [:0]const u8) void {
test { test {
_ = @import("Atlas.zig"); _ = @import("Atlas.zig");
_ = @import("Grid.zig");
_ = @import("Pty.zig"); _ = @import("Pty.zig");
_ = @import("Command.zig"); _ = @import("Command.zig");
_ = @import("TempDir.zig"); _ = @import("TempDir.zig");
_ = @import("font/main.zig"); _ = @import("font/main.zig");
_ = @import("renderer.zig");
_ = @import("terminal/Terminal.zig"); _ = @import("terminal/Terminal.zig");
_ = @import("input.zig"); _ = @import("input.zig");
// Libraries // Libraries
_ = @import("libuv");
_ = @import("segmented_pool.zig"); _ = @import("segmented_pool.zig");
_ = @import("terminal/main.zig"); _ = @import("terminal/main.zig");

14
src/renderer.zig Normal file
View File

@ -0,0 +1,14 @@
//! Renderer implementation and utilities. The renderer is responsible for
//! taking the internal screen state and turning into some output format,
//! usually for a screen.
//!
//! The renderer is closely tied to the windowing system which usually
//! has to prepare the window for the given renderer using system-specific
//! APIs. The renderers in this package assume that the renderer is already
//! setup (OpenGL has a context, Vulkan has a surface, etc.)
pub const OpenGL = @import("renderer/OpenGL.zig");
test {
@import("std").testing.refAllDecls(@This());
}

View File

@ -1,18 +1,18 @@
//! Represents a single terminal grid. //! Rendering implementation for OpenGL.
const Grid = @This(); pub const OpenGL = @This();
const std = @import("std"); const std = @import("std");
const assert = std.debug.assert; const assert = std.debug.assert;
const testing = std.testing; const testing = std.testing;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Atlas = @import("Atlas.zig"); const Atlas = @import("../Atlas.zig");
const font = @import("font/main.zig"); const font = @import("../font/main.zig");
const terminal = @import("terminal/main.zig"); const terminal = @import("../terminal/main.zig");
const Terminal = terminal.Terminal; const Terminal = terminal.Terminal;
const gl = @import("opengl.zig"); const gl = @import("../opengl.zig");
const trace = @import("tracy").trace; const trace = @import("tracy").trace;
const math = @import("math.zig"); const math = @import("../math.zig");
const lru = @import("lru.zig"); const lru = @import("../lru.zig");
const log = std.log.scoped(.grid); const log = std.log.scoped(.grid);
@ -152,7 +152,7 @@ const GPUCellMode = enum(u8) {
} }
}; };
pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Grid { pub fn init(alloc: Allocator, font_group: *font.GroupCache) !OpenGL {
// Create the initial font shaper // Create the initial font shaper
var shape_buf = try alloc.alloc(font.Shaper.Cell, 1); var shape_buf = try alloc.alloc(font.Shaper.Cell, 1);
errdefer alloc.free(shape_buf); errdefer alloc.free(shape_buf);
@ -171,8 +171,8 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Grid {
// Create our shader // Create our shader
const program = try gl.Program.createVF( const program = try gl.Program.createVF(
@embedFile("./shaders/cell.v.glsl"), @embedFile("../shaders/cell.v.glsl"),
@embedFile("./shaders/cell.f.glsl"), @embedFile("../shaders/cell.f.glsl"),
); );
// Set our cell dimensions // Set our cell dimensions
@ -284,7 +284,7 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Grid {
); );
} }
return Grid{ return OpenGL{
.alloc = alloc, .alloc = alloc,
.cells = .{}, .cells = .{},
.cells_lru = CellsLRU.init(0), .cells_lru = CellsLRU.init(0),
@ -305,7 +305,7 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Grid {
}; };
} }
pub fn deinit(self: *Grid) void { pub fn deinit(self: *OpenGL) void {
self.font_shaper.deinit(); self.font_shaper.deinit();
self.alloc.free(self.font_shaper.cell_buf); self.alloc.free(self.font_shaper.cell_buf);
@ -327,7 +327,7 @@ pub fn deinit(self: *Grid) void {
/// ///
/// Note this doesn't have to typically be manually called. Internally, /// Note this doesn't have to typically be manually called. Internally,
/// the renderer will do this when it needs more memory space. /// the renderer will do this when it needs more memory space.
pub fn rebuildCells(self: *Grid, term: *Terminal) !void { pub fn rebuildCells(self: *OpenGL, term: *Terminal) !void {
const t = trace(@src()); const t = trace(@src());
defer t.end(); defer t.end();
@ -456,7 +456,7 @@ pub fn rebuildCells(self: *Grid, term: *Terminal) !void {
/// This should be called prior to render to finalize the cells and prepare /// This should be called prior to render to finalize the cells and prepare
/// for render. This performs tasks such as preparing the cursor, refreshing /// for render. This performs tasks such as preparing the cursor, refreshing
/// the cells if necessary, etc. /// the cells if necessary, etc.
pub fn finalizeCells(self: *Grid, term: *Terminal) !void { pub fn finalizeCells(self: *OpenGL, term: *Terminal) !void {
// 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) { if (self.cells.items.len == self.cells.capacity) {
log.info("cell cache full, rebuilding from scratch", .{}); log.info("cell cache full, rebuilding from scratch", .{});
@ -468,7 +468,7 @@ pub fn finalizeCells(self: *Grid, term: *Terminal) !void {
try self.flushAtlas(); try self.flushAtlas();
} }
fn addCursor(self: *Grid, term: *Terminal) void { fn addCursor(self: *OpenGL, term: *Terminal) void {
// Add the cursor // Add the cursor
if (self.cursor_visible and term.screen.viewportIsBottom()) { if (self.cursor_visible and term.screen.viewportIsBottom()) {
const cell = term.screen.getCell( const cell = term.screen.getCell(
@ -503,7 +503,7 @@ fn addCursor(self: *Grid, term: *Terminal) void {
/// or not. If the cell wasn't updated, a full refreshCells call is /// or not. If the cell wasn't updated, a full refreshCells call is
/// needed. /// needed.
pub fn updateCell( pub fn updateCell(
self: *Grid, self: *OpenGL,
term: *Terminal, term: *Terminal,
cell: terminal.Screen.Cell, cell: terminal.Screen.Cell,
shaper_cell: font.Shaper.Cell, shaper_cell: font.Shaper.Cell,
@ -690,7 +690,7 @@ pub fn updateCell(
/// Set the screen size for rendering. This will update the projection /// Set the screen size for rendering. This will update the projection
/// used for the shader so that the scaling of the grid is correct. /// used for the shader so that the scaling of the grid is correct.
pub fn setScreenSize(self: *Grid, dim: ScreenSize) !void { pub fn setScreenSize(self: *OpenGL, dim: ScreenSize) !void {
// Update the projection uniform within our shader // Update the projection uniform within our shader
const bind = try self.program.use(); const bind = try self.program.use();
defer bind.unbind(); defer bind.unbind();
@ -725,7 +725,7 @@ pub fn setScreenSize(self: *Grid, dim: ScreenSize) !void {
} }
/// Updates the font texture atlas if it is dirty. /// Updates the font texture atlas if it is dirty.
fn flushAtlas(self: *Grid) !void { fn flushAtlas(self: *OpenGL) !void {
{ {
const atlas = &self.font_group.atlas_greyscale; const atlas = &self.font_group.atlas_greyscale;
if (atlas.modified) { if (atlas.modified) {
@ -797,7 +797,7 @@ fn flushAtlas(self: *Grid) !void {
/// Render renders the current cell state. This will not modify any of /// Render renders the current cell state. This will not modify any of
/// the cells. /// the cells.
pub fn render(self: *Grid) !void { pub fn render(self: *OpenGL) !void {
const t = trace(@src()); const t = trace(@src());
defer t.end(); defer t.end();