From 657c8540c845b7843b88195b0c303d0bf4d20b4d Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 15 Nov 2022 19:48:32 -0800 Subject: [PATCH] renderer: font size changed event, OpenGL impl --- src/renderer/OpenGL.zig | 94 +++++++++++++++++++++++++++++++--------- src/renderer/Thread.zig | 4 ++ src/renderer/message.zig | 6 +++ src/window/message.zig | 1 + 4 files changed, 84 insertions(+), 21 deletions(-) diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index 35f0228c2..b56f9825a 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -156,22 +156,15 @@ pub fn init(alloc: Allocator, options: renderer.Options) !OpenGL { var shaper = try font.Shaper.init(shape_buf); errdefer shaper.deinit(); - // Get our cell metrics based on a regular font ascii 'M'. Why 'M'? - // Doesn't matter, any normal ASCII will do we're just trying to make - // sure we use the regular font. - const metrics = metrics: { - const index = (try options.font_group.indexForCodepoint(alloc, 'M', .regular, .text)).?; - const face = try options.font_group.group.faceFromIndex(index); - break :metrics face.metrics; - }; - log.debug("cell dimensions={}", .{metrics}); - // Create our shader const program = try gl.Program.createVF( @embedFile("shaders/cell.v.glsl"), @embedFile("shaders/cell.f.glsl"), ); + // Setup our font metrics uniform + const metrics = try resetFontMetrics(alloc, program, options.font_group); + // Set our cell dimensions const pbind = try program.use(); defer pbind.unbind(); @@ -314,21 +307,26 @@ pub fn deinit(self: *OpenGL) void { self.vao.destroy(); self.program.destroy(); - { - // Our LRU values are array lists so we need to deallocate those first - var it = self.cells_lru.queue.first; - while (it) |node| { - it = node.next; - node.data.value.deinit(self.alloc); - } - - self.cells_lru.deinit(self.alloc); - } + self.resetCellsLRU(); + self.cells_lru.deinit(self.alloc); self.cells.deinit(self.alloc); self.* = undefined; } +fn resetCellsLRU(self: *OpenGL) void { + // Our LRU values are array lists so we need to deallocate those first + var it = self.cells_lru.queue.first; + while (it) |node| { + it = node.next; + node.data.value.deinit(self.alloc); + } + self.cells_lru.deinit(self.alloc); + + // Initialize our new LRU + self.cells_lru = CellsLRU.init(0); +} + /// Returns the hints that we want for this pub fn windowHints() glfw.Window.Hints { return .{ @@ -448,15 +446,69 @@ pub fn threadExit(self: *const OpenGL) void { } /// Callback when the focus changes for the terminal this is rendering. +/// +/// Must be called on the render thread. pub fn setFocus(self: *OpenGL, focus: bool) !void { self.focused = focus; } /// Called to toggle the blink state of the cursor +/// +/// Must be called on the render thread. pub fn blinkCursor(self: *OpenGL, reset: bool) void { self.cursor_visible = reset or !self.cursor_visible; } +/// Set the new font size. +/// +/// Must be called on the render thread. +pub fn setFontSize(self: *OpenGL, size: font.face.DesiredSize) !void { + // Set our new size, this will also reset our font atlas. + try self.font_group.setSize(size); + + // Invalidate our cell cache. + self.resetCellsLRU(); + + // Reset our GPU uniforms + const metrics = try resetFontMetrics(self.alloc, self.program, self.font_group); + + // Recalculate our cell size. If it is the same as before, then we do + // nothing since the grid size couldn't have possibly changed. + const new_cell_size = .{ .width = metrics.cell_width, .height = metrics.cell_height }; + if (std.meta.eql(self.cell_size, new_cell_size)) return; + + // Notify the window that the cell size changed. +} + +/// Reload the font metrics, recalculate cell size, and send that all +/// down to the GPU. +fn resetFontMetrics( + alloc: Allocator, + program: gl.Program, + font_group: *font.GroupCache, +) !font.face.Metrics { + // Get our cell metrics based on a regular font ascii 'M'. Why 'M'? + // Doesn't matter, any normal ASCII will do we're just trying to make + // sure we use the regular font. + const metrics = metrics: { + const index = (try font_group.indexForCodepoint(alloc, 'M', .regular, .text)).?; + const face = try font_group.group.faceFromIndex(index); + break :metrics face.metrics; + }; + log.debug("cell dimensions={}", .{metrics}); + + // Set our uniforms that rely on metrics + const pbind = try program.use(); + defer pbind.unbind(); + try program.setUniform("cell_size", @Vector(2, f32){ metrics.cell_width, metrics.cell_height }); + try program.setUniform("underline_position", metrics.underline_position); + try program.setUniform("underline_thickness", metrics.underline_thickness); + try program.setUniform("strikethrough_position", metrics.strikethrough_position); + try program.setUniform("strikethrough_thickness", metrics.strikethrough_thickness); + + return metrics; +} + /// The primary render callback that is completely thread-safe. pub fn render( self: *OpenGL, @@ -946,7 +998,7 @@ pub fn updateCell( /// Returns the grid size for a given screen size. This is safe to call /// on any thread. -pub fn gridSize(self: *OpenGL, screen_size: renderer.ScreenSize) renderer.GridSize { +fn gridSize(self: *OpenGL, screen_size: renderer.ScreenSize) renderer.GridSize { return renderer.GridSize.init( screen_size.subPadding(self.padding.explicit), self.cell_size, diff --git a/src/renderer/Thread.zig b/src/renderer/Thread.zig index a8ad1f667..87a949a43 100644 --- a/src/renderer/Thread.zig +++ b/src/renderer/Thread.zig @@ -275,6 +275,10 @@ fn drainMailbox(self: *Thread) !void { _ = try self.cursor_h.again(); } }, + + .font_size => |size| { + try self.renderer.setFontSize(size); + }, } } } diff --git a/src/renderer/message.zig b/src/renderer/message.zig index 87cbc9f4e..02c9ddefc 100644 --- a/src/renderer/message.zig +++ b/src/renderer/message.zig @@ -1,6 +1,7 @@ const std = @import("std"); const assert = std.debug.assert; const Allocator = std.mem.Allocator; +const font = @import("../font/main.zig"); /// The messages that can be sent to a renderer thread. pub const Message = union(enum) { @@ -12,4 +13,9 @@ pub const Message = union(enum) { /// Reset the cursor blink by immediately showing the cursor then /// restarting the timer. reset_cursor_blink: void, + + /// Change the font size. This should recalculate the grid size and + /// send a grid size change message back to the window thread if + /// the size changes. + font_size: font.face.DesiredSize, }; diff --git a/src/window/message.zig b/src/window/message.zig index 8dd814092..9f74f4e60 100644 --- a/src/window/message.zig +++ b/src/window/message.zig @@ -1,5 +1,6 @@ const App = @import("../App.zig"); const Window = @import("../Window.zig"); +const renderer = @import("../renderer.zig"); /// The message types that can be sent to a single window. pub const Message = union(enum) {