From dc908cb73d89482193c1dfdb8a379a16fe25ba27 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 24 Oct 2022 09:52:08 -0700 Subject: [PATCH] support screen size, rip out shared state --- src/Window.zig | 42 ++++++++++++++++++----------------- src/renderer/OpenGL.zig | 49 +++++++++++++++++++++++++++++++++-------- src/renderer/State.zig | 5 ++--- src/renderer/Thread.zig | 6 ++--- 4 files changed, 67 insertions(+), 35 deletions(-) diff --git a/src/Window.zig b/src/Window.zig index 38033c1de..5bf10b6aa 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -367,8 +367,11 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo // Create our terminal grid with the initial window size const window_size = try window.getSize(); + const screen_size: renderer.ScreenSize = .{ + .width = window_size.width, + .height = window_size.height, + }; var renderer_impl = try renderer.OpenGL.init(alloc, font_group); - try renderer_impl.setScreenSize(.{ .width = window_size.width, .height = window_size.height }); renderer_impl.background = .{ .r = config.background.r, .g = config.background.g, @@ -381,10 +384,7 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo }; // Calculate our grid size based on known dimensions. - const grid_size = renderer.GridSize.init( - .{ .width = window_size.width, .height = window_size.height }, - renderer_impl.cell_size, - ); + const grid_size = renderer.GridSize.init(screen_size, renderer_impl.cell_size); // Set a minimum size that is cols=10 h=4. This matches Mac's Terminal.app // but is otherwise somewhat arbitrary. @@ -486,6 +486,7 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo .renderer_thread = render_thread, .renderer_state = .{ .mutex = mutex, + .resize_screen = screen_size, .cursor = .{ .style = .blinking_block, .visible = true, @@ -516,7 +517,7 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo // Setup our callbacks and user data window.setUserPointer(self); - //window.setSizeCallback(sizeCallback); + window.setSizeCallback(sizeCallback); window.setCharCallback(charCallback); window.setKeyCallback(keyCallback); window.setFocusCallback(focusCallback); @@ -718,18 +719,15 @@ fn sizeCallback(window: glfw.Window, width: i32, height: i32) void { .height = px_size.height, }; - // Update our grid so that the projections on render are correct. const win = window.getUserPointer(Window) orelse return; - win.renderer.setScreenSize(screen_size) catch |err| - log.err("error updating grid screen size err={}", .{err}); + + // Resize usually forces a redraw + win.render_timer.schedule() catch |err| + log.err("error scheduling render timer in sizeCallback err={}", .{err}); // Recalculate our grid size win.grid_size.update(screen_size, win.renderer.cell_size); - // Update the size of our terminal state - win.terminal.resize(win.alloc, win.grid_size.columns, win.grid_size.rows) catch |err| - log.err("error updating terminal size: {}", .{err}); - // Update the size of our pty win.pty.setSize(.{ .ws_row = @intCast(u16, win.grid_size.rows), @@ -738,14 +736,18 @@ fn sizeCallback(window: glfw.Window, width: i32, height: i32) void { .ws_ypixel = @intCast(u16, height), }) catch |err| log.err("error updating pty screen size err={}", .{err}); - // Update our viewport for this context to be the entire window. - // OpenGL works in pixels, so we have to use the pixel size. - gl.viewport(0, 0, @intCast(i32, px_size.width), @intCast(i32, px_size.height)) catch |err| - log.err("error updating OpenGL viewport err={}", .{err}); + // Enter the critical area that we want to keep small + { + win.renderer_state.mutex.lock(); + defer win.renderer_state.mutex.unlock(); - // Draw - win.render_timer.schedule() catch |err| - log.err("error scheduling render timer in sizeCallback err={}", .{err}); + // We need to setup our render state to store our new pending size + win.renderer_state.resize_screen = screen_size; + + // Update the size of our terminal state + win.terminal.resize(win.alloc, win.grid_size.columns, win.grid_size.rows) catch |err| + log.err("error updating terminal size: {}", .{err}); + } } fn charCallback(window: glfw.Window, codepoint: u21) void { diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index c1857ba3a..ddb189804 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -359,14 +359,24 @@ pub fn threadExit(self: *const OpenGL) void { pub fn render( self: *OpenGL, window: glfw.Window, - state: renderer.State, + state: *renderer.State, ) !void { + // Data we extract out of the critical area. + const Critical = struct { + devmode_data: ?*imgui.DrawData, + screen_size: ?renderer.ScreenSize, + }; + // Update all our data as tightly as possible within the mutex. var gl_bg = self.background; - { + const critical: Critical = critical: { state.mutex.lock(); defer state.mutex.unlock(); + // If we're resizing, then handle that now. + if (state.resize_screen) |size| try self.setScreenSize(size); + defer state.resize_screen = null; + // Setup our cursor state self.cursor_visible = state.cursor.visible and !state.cursor.blink; self.cursor_style = CursorStyle.fromTerminal(state.cursor.style) orelse .box; @@ -384,9 +394,33 @@ pub fn render( self.foreground = bg; } + // Build our GPU cells try self.rebuildCells(state.terminal); try self.finalizeCells(state.terminal); - if (state.devmode) |dm| if (dm.visible) try dm.update(); + + // Build our devmode draw data + const devmode_data = devmode_data: { + if (state.devmode) |dm| { + if (dm.visible) { + try dm.update(); + break :devmode_data try dm.render(); + } + } + + break :devmode_data null; + }; + + break :critical .{ + .devmode_data = devmode_data, + .screen_size = state.resize_screen, + }; + }; + + // If we are resizing we need to update the viewport + if (critical.screen_size) |size| { + // Update our viewport for this context to be the entire window. + // OpenGL works in pixels, so we have to use the pixel size. + try gl.viewport(0, 0, @intCast(i32, size.width), @intCast(i32, size.height)); } // Clear the surface @@ -402,11 +436,8 @@ pub fn render( try self.draw(); // If we have devmode, then render that - if (state.devmode) |dm| { - if (dm.visible) { - const data = try dm.render(); - imgui.ImplOpenGL3.renderDrawData(data); - } + if (critical.devmode_data) |data| { + imgui.ImplOpenGL3.renderDrawData(data); } // Swap our window buffers @@ -783,7 +814,7 @@ pub fn updateCell( /// Set the screen size for rendering. This will update the projection /// used for the shader so that the scaling of the grid is correct. -pub fn setScreenSize(self: *OpenGL, dim: renderer.ScreenSize) !void { +fn setScreenSize(self: *OpenGL, dim: renderer.ScreenSize) !void { // Update the projection uniform within our shader const bind = try self.program.use(); defer bind.unbind(); diff --git a/src/renderer/State.zig b/src/renderer/State.zig index 25963ded5..2c3e6c36c 100644 --- a/src/renderer/State.zig +++ b/src/renderer/State.zig @@ -3,6 +3,7 @@ const std = @import("std"); const DevMode = @import("../DevMode.zig"); const terminal = @import("../terminal/main.zig"); +const renderer = @import("../renderer.zig"); /// The mutex that must be held while reading any of the data in the /// members of this state. Note that the state itself is NOT protected @@ -11,7 +12,7 @@ const terminal = @import("../terminal/main.zig"); mutex: *std.Thread.Mutex, /// A new screen size if the screen was resized. -resize: ?Resize = null, +resize_screen: ?renderer.ScreenSize, /// Cursor configuration for rendering cursor: Cursor, @@ -36,5 +37,3 @@ pub const Cursor = struct { /// the cursor will not be rendered. blink: bool = false, }; - -pub const Resize = struct {}; diff --git a/src/renderer/Thread.zig b/src/renderer/Thread.zig index 4cb12f99f..ed598ec4e 100644 --- a/src/renderer/Thread.zig +++ b/src/renderer/Thread.zig @@ -31,7 +31,7 @@ window: glfw.Window, renderer: *renderer.OpenGL, /// Pointer to the shared state that is used to generate the final render. -state: *const renderer.State, +state: *renderer.State, /// Initialize the thread. This does not START the thread. This only sets /// up all the internal state necessary prior to starting the thread. It @@ -40,7 +40,7 @@ pub fn init( alloc: Allocator, window: glfw.Window, renderer_impl: *renderer.OpenGL, - state: *const renderer.State, + state: *renderer.State, ) !Thread { // We always store allocator pointer on the loop data so that // handles can use our global allocator. @@ -148,7 +148,7 @@ fn renderCallback(h: *libuv.Async) void { return; }; - t.renderer.render(t.window, t.state.*) catch |err| + t.renderer.render(t.window, t.state) catch |err| log.warn("error rendering err={}", .{err}); }