From b347ff458b36b95b5b0541a36a2a4b4d5ff139e8 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 23 Oct 2022 20:55:04 -0700 Subject: [PATCH] prepare our render state on the window --- src/Window.zig | 20 +++++++++++++- src/renderer.zig | 1 + src/renderer/OpenGL.zig | 60 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 79 insertions(+), 2 deletions(-) diff --git a/src/Window.zig b/src/Window.zig index cef35d70d..9fc43e824 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -56,6 +56,9 @@ focused: bool, /// The renderer for this window. renderer: renderer.OpenGL, +/// The render state +renderer_state: renderer.State, + /// The underlying pty for this window. 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); 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.* = .{ .alloc = alloc, .alloc_io_arena = io_arena, @@ -464,6 +471,16 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo .cursor = cursor, .focused = false, .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, .command = cmd, .mouse = .{}, @@ -591,6 +608,7 @@ pub fn destroy(self: *Window) void { self.font_lib.deinit(); self.alloc_io_arena.deinit(); + self.alloc.destroy(self.renderer_state.mutex); } 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}); // Render the grid - win.renderer.render() catch |err| { + win.renderer.draw() catch |err| { log.err("error rendering grid: {}", .{err}); return; }; diff --git a/src/renderer.zig b/src/renderer.zig index 5696a853d..2a711ee15 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -9,6 +9,7 @@ pub const OpenGL = @import("renderer/OpenGL.zig"); pub const Thread = @import("renderer/Thread.zig"); +pub const State = @import("renderer/State.zig"); test { @import("std").testing.refAllDecls(@This()); diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index 5d5a4cb1f..e0f231b56 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -9,6 +9,8 @@ const testing = std.testing; const Allocator = std.mem.Allocator; const Atlas = @import("../Atlas.zig"); const font = @import("../font/main.zig"); +const imgui = @import("imgui"); +const renderer = @import("../renderer.zig"); const terminal = @import("../terminal/main.zig"); const Terminal = terminal.Terminal; const gl = @import("../opengl.zig"); @@ -353,6 +355,62 @@ pub fn threadExit() void { 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 /// 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 @@ -830,7 +888,7 @@ fn flushAtlas(self: *OpenGL) !void { /// Render renders the current cell state. This will not modify any of /// the cells. -pub fn render(self: *OpenGL) !void { +pub fn draw(self: *OpenGL) !void { const t = trace(@src()); defer t.end();