summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMitchell Hashimoto <mitchell.hashimoto@gmail.com>2022-10-23 20:55:04 -0700
committerMitchell Hashimoto <mitchell.hashimoto@gmail.com>2022-10-23 20:55:04 -0700
commitb347ff458b36b95b5b0541a36a2a4b4d5ff139e8 (patch)
treebc7bd3d30e4e331f3cb747ccda8d19a9414e7e20 /src
parent9913bba2e805cd6647bf050feeabea95b90e4f25 (diff)
prepare our render state on the window
Diffstat (limited to 'src')
-rw-r--r--src/Window.zig20
-rw-r--r--src/renderer.zig1
-rw-r--r--src/renderer/OpenGL.zig60
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();