prepare our render state on the window

This commit is contained in:
Mitchell Hashimoto
2022-10-23 20:55:04 -07:00
parent 9913bba2e8
commit b347ff458b
3 changed files with 79 additions and 2 deletions

View File

@ -56,6 +56,9 @@ focused: bool,
/// The renderer for this window. /// The renderer for this window.
renderer: renderer.OpenGL, renderer: renderer.OpenGL,
/// The render state
renderer_state: renderer.State,
/// The underlying pty for this window. /// The underlying pty for this window.
pty: Pty, pty: Pty,
@ -455,6 +458,10 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo
var io_arena = std.heap.ArenaAllocator.init(alloc); var io_arena = std.heap.ArenaAllocator.init(alloc);
errdefer io_arena.deinit(); errdefer io_arena.deinit();
// The mutex used to protect our renderer state.
var mutex = try alloc.create(std.Thread.Mutex);
errdefer alloc.destroy(mutex);
self.* = .{ self.* = .{
.alloc = alloc, .alloc = alloc,
.alloc_io_arena = io_arena, .alloc_io_arena = io_arena,
@ -464,6 +471,16 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo
.cursor = cursor, .cursor = cursor,
.focused = false, .focused = false,
.renderer = renderer_impl, .renderer = renderer_impl,
.renderer_state = .{
.mutex = mutex,
.cursor = .{
.style = .blinking_block,
.visible = true,
.blink = false,
},
.terminal = &self.terminal,
.devmode = if (!DevMode.enabled) null else &DevMode.instance,
},
.pty = pty, .pty = pty,
.command = cmd, .command = cmd,
.mouse = .{}, .mouse = .{},
@ -591,6 +608,7 @@ pub fn destroy(self: *Window) void {
self.font_lib.deinit(); self.font_lib.deinit();
self.alloc_io_arena.deinit(); self.alloc_io_arena.deinit();
self.alloc.destroy(self.renderer_state.mutex);
} }
pub fn shouldClose(self: Window) bool { pub fn shouldClose(self: Window) bool {
@ -1632,7 +1650,7 @@ fn renderTimerCallback(t: *libuv.Timer) void {
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.renderer.render() catch |err| { win.renderer.draw() catch |err| {
log.err("error rendering grid: {}", .{err}); log.err("error rendering grid: {}", .{err});
return; return;
}; };

View File

@ -9,6 +9,7 @@
pub const OpenGL = @import("renderer/OpenGL.zig"); pub const OpenGL = @import("renderer/OpenGL.zig");
pub const Thread = @import("renderer/Thread.zig"); pub const Thread = @import("renderer/Thread.zig");
pub const State = @import("renderer/State.zig");
test { test {
@import("std").testing.refAllDecls(@This()); @import("std").testing.refAllDecls(@This());

View File

@ -9,6 +9,8 @@ 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 imgui = @import("imgui");
const renderer = @import("../renderer.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");
@ -353,6 +355,62 @@ pub fn threadExit() void {
glfw.makeContextCurrent(null) catch {}; glfw.makeContextCurrent(null) catch {};
} }
/// The primary render callback that is completely thread-safe.
pub fn render(
self: *OpenGL,
window: glfw.Window,
state: renderer.State,
) !void {
// Update all our data as tightly as possible within the mutex.
var gl_bg = self.background;
{
state.mutex.lock();
defer state.mutex.unlock();
// Setup our cursor state
self.cursor_visible = state.cursor.visible and !state.cursor.blink;
self.cursor_style = CursorStyle.fromTerminal(state.cursor.style) orelse .box;
// Swap bg/fg if the terminal is reversed
const bg = self.background;
const fg = self.foreground;
defer {
self.background = bg;
self.foreground = fg;
}
if (state.terminal.modes.reverse_colors) {
gl_bg = fg;
self.background = fg;
self.foreground = bg;
}
try self.rebuildCells(state.terminal);
try self.finalizeCells(state.terminal);
if (state.devmode) |dm| try dm.update();
}
// Clear the surface
gl.clearColor(
@intToFloat(f32, self.background.r) / 255,
@intToFloat(f32, self.background.g) / 255,
@intToFloat(f32, self.background.b) / 255,
1.0,
);
gl.clear(gl.c.GL_COLOR_BUFFER_BIT);
// We're out of the critical path now. Let's first render our terminal.
try self.draw();
// If we have devmode, then render that
if (state.devmode) |dm| {
const data = try dm.render();
imgui.ImplOpenGL3.renderDrawData(data);
}
// Swap our window buffers
try window.swapBuffers();
}
/// rebuildCells rebuilds all the GPU cells from our CPU state. This is a /// rebuildCells rebuilds all the GPU cells from our CPU state. This is a
/// slow operation but ensures that the GPU state exactly matches the CPU state. /// slow operation but ensures that the GPU state exactly matches the CPU state.
/// In steady-state operation, we use some GPU tricks to send down stale data /// In steady-state operation, we use some GPU tricks to send down stale data
@ -830,7 +888,7 @@ fn flushAtlas(self: *OpenGL) !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: *OpenGL) !void { pub fn draw(self: *OpenGL) !void {
const t = trace(@src()); const t = trace(@src());
defer t.end(); defer t.end();