From 5309f4d08058ecb721d30d9ef2822b66026ddb62 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 3 Aug 2024 15:39:02 -0700 Subject: [PATCH 1/7] renderer/metal: extend background color of grid border into padding --- src/renderer/Metal.zig | 26 +++++++++++++++++++++++++- src/renderer/metal/shaders.zig | 21 ++++++++++++++++----- src/renderer/shaders/cell.metal | 12 ++++++++++++ src/renderer/size.zig | 19 +++++++++++++++++++ 4 files changed, 72 insertions(+), 6 deletions(-) diff --git a/src/renderer/Metal.zig b/src/renderer/Metal.zig index e2caa002b..93bf09fa1 100644 --- a/src/renderer/Metal.zig +++ b/src/renderer/Metal.zig @@ -621,6 +621,8 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal { .uniforms = .{ .projection_matrix = undefined, .cell_size = undefined, + .grid_size = undefined, + .grid_padding = undefined, .min_contrast = options.config.min_contrast, .cursor_pos = .{ std.math.maxInt(u16), std.math.maxInt(u16) }, .cursor_color = undefined, @@ -832,7 +834,8 @@ pub fn setFontGrid(self: *Metal, grid: *font.SharedGrid) void { self.grid_metrics = metrics; // Reset our cell contents. - self.cells.resize(self.alloc, self.gridSize().?) catch |err| { + const grid_size = self.gridSize().?; + self.cells.resize(self.alloc, grid_size) catch |err| { // The setFontGrid function can't fail but resizing our cell // buffer definitely can fail. If it does, our renderer is probably // screwed but let's just log it and continue until we can figure @@ -857,6 +860,11 @@ pub fn setFontGrid(self: *Metal, grid: *font.SharedGrid) void { @floatFromInt(metrics.cell_width), @floatFromInt(metrics.cell_height), }, + .grid_size = .{ + grid_size.columns, + grid_size.rows, + }, + .grid_padding = self.uniforms.grid_padding, .min_contrast = self.uniforms.min_contrast, .cursor_pos = self.uniforms.cursor_pos, .cursor_color = self.uniforms.cursor_color, @@ -1951,6 +1959,12 @@ pub fn setScreenSize( self.padding.explicit; const padded_dim = dim.subPadding(padding); + // Blank space around the grid. + const blank = dim.blankPadding(padding, grid_size, .{ + .width = self.grid_metrics.cell_width, + .height = self.grid_metrics.cell_height, + }).add(padding); + // Set the size of the drawable surface to the bounds self.layer.setProperty("drawableSize", macos.graphics.Size{ .width = @floatFromInt(dim.width), @@ -1970,6 +1984,16 @@ pub fn setScreenSize( @floatFromInt(self.grid_metrics.cell_width), @floatFromInt(self.grid_metrics.cell_height), }, + .grid_size = .{ + grid_size.columns, + grid_size.rows, + }, + .grid_padding = .{ + @floatFromInt(blank.top), + @floatFromInt(blank.right), + @floatFromInt(blank.bottom), + @floatFromInt(blank.left), + }, .min_contrast = old.min_contrast, .cursor_pos = old.cursor_pos, .cursor_color = old.cursor_color, diff --git a/src/renderer/metal/shaders.zig b/src/renderer/metal/shaders.zig index a6303c78d..5a9be3b73 100644 --- a/src/renderer/metal/shaders.zig +++ b/src/renderer/metal/shaders.zig @@ -106,20 +106,31 @@ pub const Image = extern struct { /// The uniforms that are passed to the terminal cell shader. pub const Uniforms = extern struct { + // Note: all of the explicit aligmnments are copied from the + // MSL developer reference just so that we can be sure that we got + // it all exactly right. + /// The projection matrix for turning world coordinates to normalized. /// This is calculated based on the size of the screen. - projection_matrix: math.Mat, + projection_matrix: math.Mat align(16), /// Size of a single cell in pixels, unscaled. - cell_size: [2]f32, + cell_size: [2]f32 align(8), + + /// Size of the grid in columns and rows. + grid_size: [2]u16 align(4), + + /// The padding around the terminal grid in pixels. In order: + /// top, right, bottom, left. + grid_padding: [4]f32 align(16), /// The minimum contrast ratio for text. The contrast ratio is calculated /// according to the WCAG 2.0 spec. - min_contrast: f32, + min_contrast: f32 align(4), /// The cursor position and color. - cursor_pos: [2]u16, - cursor_color: [4]u8, + cursor_pos: [2]u16 align(4), + cursor_color: [4]u8 align(4), }; /// The uniforms used for custom postprocess shaders. diff --git a/src/renderer/shaders/cell.metal b/src/renderer/shaders/cell.metal index f486d4ecf..01b577c32 100644 --- a/src/renderer/shaders/cell.metal +++ b/src/renderer/shaders/cell.metal @@ -3,6 +3,8 @@ using namespace metal; struct Uniforms { float4x4 projection_matrix; float2 cell_size; + ushort2 grid_size; + float4 grid_padding; float min_contrast; ushort2 cursor_pos; uchar4 cursor_color; @@ -98,6 +100,16 @@ vertex CellBgVertexOut cell_bg_vertex(unsigned int vid [[vertex_id]], float2 cell_size_scaled = uniforms.cell_size; cell_size_scaled.x = cell_size_scaled.x * input.cell_width; + // If we're at the edge of the grid, we add our padding to the background + // to extend it. Note: grid_padding is top/right/bottom/left. + // TODO: top/left + if (input.grid_pos.y == uniforms.grid_size.y - 1) { + cell_size_scaled.y += uniforms.grid_padding.b; + } + if (input.grid_pos.x == uniforms.grid_size.x - 1) { + cell_size_scaled.x += uniforms.grid_padding.g; + } + // Turn the cell position into a vertex point depending on the // vertex ID. Since we use instanced drawing, we have 4 vertices // for each corner of the cell. We can use vertex ID to determine diff --git a/src/renderer/size.zig b/src/renderer/size.zig index 7b458b57e..1c2cfe97a 100644 --- a/src/renderer/size.zig +++ b/src/renderer/size.zig @@ -34,6 +34,25 @@ pub const ScreenSize = struct { }; } + /// Calculates the amount of blank space around the grid. This is possible + /// when padding isn't balanced. + /// + /// The "self" screen size here should be the unpadded screen. + pub fn blankPadding(self: ScreenSize, padding: Padding, grid: GridSize, cell: CellSize) Padding { + const grid_width = grid.columns * cell.width; + const grid_height = grid.rows * cell.height; + const padded_width = grid_width + (padding.left + padding.right); + const padded_height = grid_height + (padding.top + padding.bottom); + const leftover_width = self.width - padded_width; + const leftover_height = self.height - padded_height; + return .{ + .top = 0, + .bottom = leftover_height, + .right = leftover_width, + .left = 0, + }; + } + /// Returns true if two sizes are equal. pub fn equals(self: ScreenSize, other: ScreenSize) bool { return self.width == other.width and self.height == other.height; From 9214db3c5f49e9a4d973d377455e0772cc4fa3db Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 3 Aug 2024 16:03:42 -0700 Subject: [PATCH 2/7] renderer/metal: handle top/left --- src/renderer/shaders/cell.metal | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/renderer/shaders/cell.metal b/src/renderer/shaders/cell.metal index 01b577c32..bfe55aade 100644 --- a/src/renderer/shaders/cell.metal +++ b/src/renderer/shaders/cell.metal @@ -102,11 +102,16 @@ vertex CellBgVertexOut cell_bg_vertex(unsigned int vid [[vertex_id]], // If we're at the edge of the grid, we add our padding to the background // to extend it. Note: grid_padding is top/right/bottom/left. - // TODO: top/left - if (input.grid_pos.y == uniforms.grid_size.y - 1) { + if (input.grid_pos.y == 0) { + cell_pos.y -= uniforms.grid_padding.r; + cell_size_scaled.y += uniforms.grid_padding.r; + } else if (input.grid_pos.y == uniforms.grid_size.y - 1) { cell_size_scaled.y += uniforms.grid_padding.b; } - if (input.grid_pos.x == uniforms.grid_size.x - 1) { + if (input.grid_pos.x == 0) { + cell_pos.x -= uniforms.grid_padding.a; + cell_size_scaled.x += uniforms.grid_padding.a; + } else if (input.grid_pos.x == uniforms.grid_size.x - 1) { cell_size_scaled.x += uniforms.grid_padding.g; } From 55e8c421b51a6c998ea97e3d7edf275cdc23024d Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 3 Aug 2024 16:14:14 -0700 Subject: [PATCH 3/7] config: add window-padding-color --- src/config.zig | 1 + src/config/Config.zig | 15 +++++++++++++++ src/renderer/Metal.zig | 16 ++++++++++++---- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/config.zig b/src/config.zig index ba87fb6db..34b209c09 100644 --- a/src/config.zig +++ b/src/config.zig @@ -20,6 +20,7 @@ pub const RepeatableCodepointMap = Config.RepeatableCodepointMap; pub const RepeatableFontVariation = Config.RepeatableFontVariation; pub const RepeatableString = Config.RepeatableString; pub const ShellIntegrationFeatures = Config.ShellIntegrationFeatures; +pub const WindowPaddingColor = Config.WindowPaddingColor; // Alternate APIs pub const CAPI = @import("config/CAPI.zig"); diff --git a/src/config/Config.zig b/src/config/Config.zig index b31570a14..b3da3c279 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -669,6 +669,16 @@ keybind: Keybinds = .{}, /// given a certain viewport size and grid cell size. @"window-padding-balance": bool = false, +/// The color of the padding area of the window. Valid values are: +/// +/// * `background` - The background color specified in `background`. +/// * `extend` - Extend the background color of the nearest grid cell. +/// +/// The default value is "extend". This allows for smooth resizing of a +/// terminal grid without having visible empty areas around the edge. The edge +/// cells may appear slightly larger due to the extension. +@"window-padding-color": WindowPaddingColor = .extend, + /// Synchronize rendering with the screen refresh rate. If true, this will /// minimize tearing and align redraws with the screen but may cause input /// latency. If false, this will maximize redraw frequency but may cause tearing, @@ -2678,6 +2688,11 @@ pub const OptionAsAlt = enum { right, }; +pub const WindowPaddingColor = enum { + background, + extend, +}; + /// Color represents a color using RGB. /// /// This is a packed struct so that the C API to read color values just diff --git a/src/renderer/Metal.zig b/src/renderer/Metal.zig index 93bf09fa1..b910cde74 100644 --- a/src/renderer/Metal.zig +++ b/src/renderer/Metal.zig @@ -345,6 +345,7 @@ pub const DerivedConfig = struct { invert_selection_fg_bg: bool, bold_is_bright: bool, min_contrast: f32, + padding_color: configpkg.WindowPaddingColor, custom_shaders: std.ArrayListUnmanaged([:0]const u8), links: link.Set, vsync: bool, @@ -402,6 +403,7 @@ pub const DerivedConfig = struct { .invert_selection_fg_bg = config.@"selection-invert-fg-bg", .bold_is_bright = config.@"bold-is-bright", .min_contrast = @floatCast(config.@"minimum-contrast"), + .padding_color = config.@"window-padding-color", .selection_background = if (config.@"selection-background") |bg| bg.toTerminalRGB() @@ -1960,10 +1962,16 @@ pub fn setScreenSize( const padded_dim = dim.subPadding(padding); // Blank space around the grid. - const blank = dim.blankPadding(padding, grid_size, .{ - .width = self.grid_metrics.cell_width, - .height = self.grid_metrics.cell_height, - }).add(padding); + const blank: renderer.Padding = switch (self.config.padding_color) { + // We can use zero padding because the backgroudn color is our + // clear color. + .background => .{}, + + .extend => dim.blankPadding(padding, grid_size, .{ + .width = self.grid_metrics.cell_width, + .height = self.grid_metrics.cell_height, + }).add(padding), + }; // Set the size of the drawable surface to the bounds self.layer.setProperty("drawableSize", macos.graphics.Size{ From ed1c163c7bb16f25b4ff16e8340434f4b1759335 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 3 Aug 2024 16:29:17 -0700 Subject: [PATCH 4/7] renderer/opengl: support window-padding-color=extend --- src/renderer/OpenGL.zig | 40 +++++++++++++++++++++++++++++++- src/renderer/shaders/cell.v.glsl | 17 ++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index f1d1c3e53..e37dff83c 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -138,10 +138,11 @@ const SetScreenSize = struct { return error.OpenGLUninitialized; // Apply our padding + const grid_size = r.gridSize(self.size); const padding = if (r.padding.balance) renderer.Padding.balanced( self.size, - r.gridSize(self.size), + grid_size, .{ .width = r.grid_metrics.cell_width, .height = r.grid_metrics.cell_height, @@ -151,6 +152,18 @@ const SetScreenSize = struct { r.padding.explicit; const padded_size = self.size.subPadding(padding); + // Blank space around the grid. + const blank: renderer.Padding = switch (r.config.padding_color) { + // We can use zero padding because the backgroudn color is our + // clear color. + .background => .{}, + + .extend => self.size.blankPadding(padding, grid_size, .{ + .width = r.grid_metrics.cell_width, + .height = r.grid_metrics.cell_height, + }).add(padding), + }; + log.debug("GL api: screen size padded={} screen={} grid={} cell={} padding={}", .{ padded_size, self.size, @@ -189,6 +202,29 @@ const SetScreenSize = struct { ); } + // Setup our grid padding + { + const program = gl_state.cell_program; + const bind = try program.program.use(); + defer bind.unbind(); + try program.program.setUniform( + "grid_padding", + @Vector(4, f32){ + @floatFromInt(blank.top), + @floatFromInt(blank.right), + @floatFromInt(blank.bottom), + @floatFromInt(blank.left), + }, + ); + try program.program.setUniform( + "grid_size", + @Vector(2, f32){ + @floatFromInt(grid_size.columns), + @floatFromInt(grid_size.rows), + }, + ); + } + // Update our custom shader resolution if (gl_state.custom) |*custom_state| { try custom_state.setScreenSize(self.size); @@ -252,6 +288,7 @@ pub const DerivedConfig = struct { invert_selection_fg_bg: bool, bold_is_bright: bool, min_contrast: f32, + padding_color: configpkg.WindowPaddingColor, custom_shaders: std.ArrayListUnmanaged([:0]const u8), links: link.Set, @@ -308,6 +345,7 @@ pub const DerivedConfig = struct { .invert_selection_fg_bg = config.@"selection-invert-fg-bg", .bold_is_bright = config.@"bold-is-bright", .min_contrast = @floatCast(config.@"minimum-contrast"), + .padding_color = config.@"window-padding-color", .selection_background = if (config.@"selection-background") |bg| bg.toTerminalRGB() diff --git a/src/renderer/shaders/cell.v.glsl b/src/renderer/shaders/cell.v.glsl index 16e61773c..9b2b90026 100644 --- a/src/renderer/shaders/cell.v.glsl +++ b/src/renderer/shaders/cell.v.glsl @@ -55,6 +55,8 @@ flat out uint mode; uniform sampler2D text; uniform sampler2D text_color; uniform vec2 cell_size; +uniform vec2 grid_size; +uniform vec4 grid_padding; uniform mat4 projection; uniform float min_contrast; @@ -167,6 +169,21 @@ void main() { switch (mode) { case MODE_BG: + // If we're at the edge of the grid, we add our padding to the background + // to extend it. Note: grid_padding is top/right/bottom/left. + if (grid_coord.y == 0) { + cell_pos.y -= grid_padding.r; + cell_size_scaled.y += grid_padding.r; + } else if (grid_coord.y == grid_size.y - 1) { + cell_size_scaled.y += grid_padding.b; + } + if (grid_coord.x == 0) { + cell_pos.x -= grid_padding.a; + cell_size_scaled.x += grid_padding.a; + } else if (grid_coord.x == grid_size.x - 1) { + cell_size_scaled.x += grid_padding.g; + } + // Calculate the final position of our cell in world space. // We have to add our cell size since our vertices are offset // one cell up and to the left. (Do the math to verify yourself) From 56b3aa0b5ebfcdfadd4ebf4d812d9d0fce867044 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 3 Aug 2024 18:33:53 -0700 Subject: [PATCH 5/7] renderer/metal: font grid change should run all screen size logic --- src/renderer/Metal.zig | 44 ++++++++++++++---------------------------- 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/src/renderer/Metal.zig b/src/renderer/Metal.zig index b910cde74..400eba995 100644 --- a/src/renderer/Metal.zig +++ b/src/renderer/Metal.zig @@ -835,16 +835,6 @@ pub fn setFontGrid(self: *Metal, grid: *font.SharedGrid) void { const metrics = grid.metrics; self.grid_metrics = metrics; - // Reset our cell contents. - const grid_size = self.gridSize().?; - self.cells.resize(self.alloc, grid_size) catch |err| { - // The setFontGrid function can't fail but resizing our cell - // buffer definitely can fail. If it does, our renderer is probably - // screwed but let's just log it and continue until we can figure - // out a better way to handle this. - log.err("error resizing cells buffer err={}", .{err}); - }; - // Reset our shaper cache. If our font changed (not just the size) then // the data in the shaper cache may be invalid and cannot be used, so we // always clear the cache just in case. @@ -852,25 +842,21 @@ pub fn setFontGrid(self: *Metal, grid: *font.SharedGrid) void { self.font_shaper_cache.deinit(self.alloc); self.font_shaper_cache = font_shaper_cache; - // Reset our viewport to force a rebuild - self.cells_viewport = null; - - // Update our uniforms - self.uniforms = .{ - .projection_matrix = self.uniforms.projection_matrix, - .cell_size = .{ - @floatFromInt(metrics.cell_width), - @floatFromInt(metrics.cell_height), - }, - .grid_size = .{ - grid_size.columns, - grid_size.rows, - }, - .grid_padding = self.uniforms.grid_padding, - .min_contrast = self.uniforms.min_contrast, - .cursor_pos = self.uniforms.cursor_pos, - .cursor_color = self.uniforms.cursor_color, - }; + // Run a screen size update since this handles a lot of our uniforms + // that are grid size dependent and changing the font grid can change + // the grid size. + // + // If the screen size isn't set, it will be eventually so that'll call + // the setScreenSize automatically. + if (self.screen_size) |size| { + self.setScreenSize(size, self.padding.explicit) catch |err| { + // The setFontGrid function can't fail but resizing our cell + // buffer definitely can fail. If it does, our renderer is probably + // screwed but let's just log it and continue until we can figure + // out a better way to handle this. + log.err("error resizing cells buffer err={}", .{err}); + }; + } } /// Update the frame data. From 172179bbff7d3f0df73ba1c29c6906b69ead2619 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 3 Aug 2024 18:37:15 -0700 Subject: [PATCH 6/7] renderer/metal: can access grid metrics directly on font grid Its never recalculated so we don't need a lock. --- src/renderer/Metal.zig | 59 ++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 40 deletions(-) diff --git a/src/renderer/Metal.zig b/src/renderer/Metal.zig index 400eba995..64412ff45 100644 --- a/src/renderer/Metal.zig +++ b/src/renderer/Metal.zig @@ -66,9 +66,6 @@ config: DerivedConfig, /// The mailbox for communicating with the window. surface_mailbox: apprt.surface.Mailbox, -/// Current font metrics defining our grid. -grid_metrics: font.face.Metrics, - /// Current screen size dimensions for this grid. This is set on the first /// resize event, and is not immediately available. screen_size: ?renderer.ScreenSize, @@ -583,18 +580,6 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal { var shaders = try Shaders.init(alloc, gpu_state.device, custom_shaders); errdefer shaders.deinit(alloc); - // Initialize all the data that requires a critical font section. - const font_critical: struct { - metrics: font.Metrics, - } = font_critical: { - const grid = options.font_grid; - grid.lock.lockShared(); - defer grid.lock.unlockShared(); - break :font_critical .{ - .metrics = grid.metrics, - }; - }; - const display_link: ?DisplayLink = switch (builtin.os.tag) { .macos => if (options.config.vsync) try macos.video.DisplayLink.createWithActiveCGDisplays() @@ -608,7 +593,6 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal { .alloc = alloc, .config = options.config, .surface_mailbox = options.surface_mailbox, - .grid_metrics = font_critical.metrics, .screen_size = null, .padding = options.padding, .focused = true, @@ -773,8 +757,8 @@ fn gridSize(self: *Metal) ?renderer.GridSize { return renderer.GridSize.init( screen_size.subPadding(self.padding.explicit), .{ - .width = self.grid_metrics.cell_width, - .height = self.grid_metrics.cell_height, + .width = self.font_grid.metrics.cell_width, + .height = self.font_grid.metrics.cell_height, }, ); } @@ -830,11 +814,6 @@ pub fn setFontGrid(self: *Metal, grid: *font.SharedGrid) void { frame.color_modified = 0; } - // Get our metrics from the grid. This doesn't require a lock because - // the metrics are never recalculated. - const metrics = grid.metrics; - self.grid_metrics = metrics; - // Reset our shaper cache. If our font changed (not just the size) then // the data in the shaper cache may be invalid and cannot be used, so we // always clear the cache just in case. @@ -1724,8 +1703,8 @@ fn prepKittyVirtualPlacement( const rp = p.renderPlacement( storage, &image, - self.grid_metrics.cell_width, - self.grid_metrics.cell_height, + self.font_grid.metrics.cell_width, + self.font_grid.metrics.cell_height, ) catch |err| { log.warn("error rendering virtual placement err={}", .{err}); return; @@ -1785,7 +1764,7 @@ fn prepKittyPlacement( const vp_y = t.screen.pages.pointFromPin(.screen, top.*).?.screen.y; const img_y = t.screen.pages.pointFromPin(.screen, rect.top_left).?.screen.y; const offset_cells = vp_y - img_y; - const offset_pixels = offset_cells * self.grid_metrics.cell_height; + const offset_pixels = offset_cells * self.font_grid.metrics.cell_height; break :offset_y @intCast(offset_pixels); } else 0; @@ -1813,8 +1792,8 @@ fn prepKittyPlacement( image.height -| source_y; // Calculate the width/height of our image. - const dest_width = if (p.columns > 0) p.columns * self.grid_metrics.cell_width else source_width; - const dest_height = if (p.rows > 0) p.rows * self.grid_metrics.cell_height else source_height; + const dest_width = if (p.columns > 0) p.columns * self.font_grid.metrics.cell_width else source_width; + const dest_height = if (p.rows > 0) p.rows * self.font_grid.metrics.cell_height else source_height; // Accumulate the placement if (image.width > 0 and image.height > 0) { @@ -1939,8 +1918,8 @@ pub fn setScreenSize( dim, grid_size, .{ - .width = self.grid_metrics.cell_width, - .height = self.grid_metrics.cell_height, + .width = self.font_grid.metrics.cell_width, + .height = self.font_grid.metrics.cell_height, }, ) else @@ -1954,8 +1933,8 @@ pub fn setScreenSize( .background => .{}, .extend => dim.blankPadding(padding, grid_size, .{ - .width = self.grid_metrics.cell_width, - .height = self.grid_metrics.cell_height, + .width = self.font_grid.metrics.cell_width, + .height = self.font_grid.metrics.cell_height, }).add(padding), }; @@ -1975,8 +1954,8 @@ pub fn setScreenSize( -1 * @as(f32, @floatFromInt(padding.top)), ), .cell_size = .{ - @floatFromInt(self.grid_metrics.cell_width), - @floatFromInt(self.grid_metrics.cell_height), + @floatFromInt(self.font_grid.metrics.cell_width), + @floatFromInt(self.font_grid.metrics.cell_height), }, .grid_size = .{ grid_size.columns, @@ -2075,7 +2054,7 @@ pub fn setScreenSize( }; } - log.debug("screen size screen={} grid={}, cell_width={} cell_height={}", .{ dim, grid_size, self.grid_metrics.cell_width, self.grid_metrics.cell_height }); + log.debug("screen size screen={} grid={}, cell_width={} cell_height={}", .{ dim, grid_size, self.font_grid.metrics.cell_width, self.font_grid.metrics.cell_height }); } /// Convert the terminal state to GPU cells stored in CPU memory. These @@ -2439,7 +2418,7 @@ fn updateCell( shaper_run.font_index, glyph_index, .{ - .grid_metrics = self.grid_metrics, + .grid_metrics = self.font_grid.metrics, .thicken = self.config.font_thicken, }, ); @@ -2490,7 +2469,7 @@ fn updateCell( @intFromEnum(sprite), .{ .cell_width = if (cell.wide == .wide) 2 else 1, - .grid_metrics = self.grid_metrics, + .grid_metrics = self.font_grid.metrics, }, ); @@ -2515,7 +2494,7 @@ fn updateCell( @intFromEnum(font.Sprite.strikethrough), .{ .cell_width = if (cell.wide == .wide) 2 else 1, - .grid_metrics = self.grid_metrics, + .grid_metrics = self.font_grid.metrics, }, ); @@ -2572,7 +2551,7 @@ fn addCursor( @intFromEnum(sprite), .{ .cell_width = if (wide) 2 else 1, - .grid_metrics = self.grid_metrics, + .grid_metrics = self.font_grid.metrics, }, ) catch |err| { log.warn("error rendering cursor glyph err={}", .{err}); @@ -2606,7 +2585,7 @@ fn addPreeditCell( @intCast(cp.codepoint), .regular, .text, - .{ .grid_metrics = self.grid_metrics }, + .{ .grid_metrics = self.font_grid.metrics }, ) catch |err| { log.warn("error rendering preedit glyph err={}", .{err}); return; From 1cc5a69c43e8ba0c50ea39c440750bf04e38ba8b Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 3 Aug 2024 18:37:42 -0700 Subject: [PATCH 7/7] Revert "renderer/metal: can access grid metrics directly on font grid" This reverts commit 172179bbff7d3f0df73ba1c29c6906b69ead2619. --- src/renderer/Metal.zig | 59 ++++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/src/renderer/Metal.zig b/src/renderer/Metal.zig index 64412ff45..400eba995 100644 --- a/src/renderer/Metal.zig +++ b/src/renderer/Metal.zig @@ -66,6 +66,9 @@ config: DerivedConfig, /// The mailbox for communicating with the window. surface_mailbox: apprt.surface.Mailbox, +/// Current font metrics defining our grid. +grid_metrics: font.face.Metrics, + /// Current screen size dimensions for this grid. This is set on the first /// resize event, and is not immediately available. screen_size: ?renderer.ScreenSize, @@ -580,6 +583,18 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal { var shaders = try Shaders.init(alloc, gpu_state.device, custom_shaders); errdefer shaders.deinit(alloc); + // Initialize all the data that requires a critical font section. + const font_critical: struct { + metrics: font.Metrics, + } = font_critical: { + const grid = options.font_grid; + grid.lock.lockShared(); + defer grid.lock.unlockShared(); + break :font_critical .{ + .metrics = grid.metrics, + }; + }; + const display_link: ?DisplayLink = switch (builtin.os.tag) { .macos => if (options.config.vsync) try macos.video.DisplayLink.createWithActiveCGDisplays() @@ -593,6 +608,7 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal { .alloc = alloc, .config = options.config, .surface_mailbox = options.surface_mailbox, + .grid_metrics = font_critical.metrics, .screen_size = null, .padding = options.padding, .focused = true, @@ -757,8 +773,8 @@ fn gridSize(self: *Metal) ?renderer.GridSize { return renderer.GridSize.init( screen_size.subPadding(self.padding.explicit), .{ - .width = self.font_grid.metrics.cell_width, - .height = self.font_grid.metrics.cell_height, + .width = self.grid_metrics.cell_width, + .height = self.grid_metrics.cell_height, }, ); } @@ -814,6 +830,11 @@ pub fn setFontGrid(self: *Metal, grid: *font.SharedGrid) void { frame.color_modified = 0; } + // Get our metrics from the grid. This doesn't require a lock because + // the metrics are never recalculated. + const metrics = grid.metrics; + self.grid_metrics = metrics; + // Reset our shaper cache. If our font changed (not just the size) then // the data in the shaper cache may be invalid and cannot be used, so we // always clear the cache just in case. @@ -1703,8 +1724,8 @@ fn prepKittyVirtualPlacement( const rp = p.renderPlacement( storage, &image, - self.font_grid.metrics.cell_width, - self.font_grid.metrics.cell_height, + self.grid_metrics.cell_width, + self.grid_metrics.cell_height, ) catch |err| { log.warn("error rendering virtual placement err={}", .{err}); return; @@ -1764,7 +1785,7 @@ fn prepKittyPlacement( const vp_y = t.screen.pages.pointFromPin(.screen, top.*).?.screen.y; const img_y = t.screen.pages.pointFromPin(.screen, rect.top_left).?.screen.y; const offset_cells = vp_y - img_y; - const offset_pixels = offset_cells * self.font_grid.metrics.cell_height; + const offset_pixels = offset_cells * self.grid_metrics.cell_height; break :offset_y @intCast(offset_pixels); } else 0; @@ -1792,8 +1813,8 @@ fn prepKittyPlacement( image.height -| source_y; // Calculate the width/height of our image. - const dest_width = if (p.columns > 0) p.columns * self.font_grid.metrics.cell_width else source_width; - const dest_height = if (p.rows > 0) p.rows * self.font_grid.metrics.cell_height else source_height; + const dest_width = if (p.columns > 0) p.columns * self.grid_metrics.cell_width else source_width; + const dest_height = if (p.rows > 0) p.rows * self.grid_metrics.cell_height else source_height; // Accumulate the placement if (image.width > 0 and image.height > 0) { @@ -1918,8 +1939,8 @@ pub fn setScreenSize( dim, grid_size, .{ - .width = self.font_grid.metrics.cell_width, - .height = self.font_grid.metrics.cell_height, + .width = self.grid_metrics.cell_width, + .height = self.grid_metrics.cell_height, }, ) else @@ -1933,8 +1954,8 @@ pub fn setScreenSize( .background => .{}, .extend => dim.blankPadding(padding, grid_size, .{ - .width = self.font_grid.metrics.cell_width, - .height = self.font_grid.metrics.cell_height, + .width = self.grid_metrics.cell_width, + .height = self.grid_metrics.cell_height, }).add(padding), }; @@ -1954,8 +1975,8 @@ pub fn setScreenSize( -1 * @as(f32, @floatFromInt(padding.top)), ), .cell_size = .{ - @floatFromInt(self.font_grid.metrics.cell_width), - @floatFromInt(self.font_grid.metrics.cell_height), + @floatFromInt(self.grid_metrics.cell_width), + @floatFromInt(self.grid_metrics.cell_height), }, .grid_size = .{ grid_size.columns, @@ -2054,7 +2075,7 @@ pub fn setScreenSize( }; } - log.debug("screen size screen={} grid={}, cell_width={} cell_height={}", .{ dim, grid_size, self.font_grid.metrics.cell_width, self.font_grid.metrics.cell_height }); + log.debug("screen size screen={} grid={}, cell_width={} cell_height={}", .{ dim, grid_size, self.grid_metrics.cell_width, self.grid_metrics.cell_height }); } /// Convert the terminal state to GPU cells stored in CPU memory. These @@ -2418,7 +2439,7 @@ fn updateCell( shaper_run.font_index, glyph_index, .{ - .grid_metrics = self.font_grid.metrics, + .grid_metrics = self.grid_metrics, .thicken = self.config.font_thicken, }, ); @@ -2469,7 +2490,7 @@ fn updateCell( @intFromEnum(sprite), .{ .cell_width = if (cell.wide == .wide) 2 else 1, - .grid_metrics = self.font_grid.metrics, + .grid_metrics = self.grid_metrics, }, ); @@ -2494,7 +2515,7 @@ fn updateCell( @intFromEnum(font.Sprite.strikethrough), .{ .cell_width = if (cell.wide == .wide) 2 else 1, - .grid_metrics = self.font_grid.metrics, + .grid_metrics = self.grid_metrics, }, ); @@ -2551,7 +2572,7 @@ fn addCursor( @intFromEnum(sprite), .{ .cell_width = if (wide) 2 else 1, - .grid_metrics = self.font_grid.metrics, + .grid_metrics = self.grid_metrics, }, ) catch |err| { log.warn("error rendering cursor glyph err={}", .{err}); @@ -2585,7 +2606,7 @@ fn addPreeditCell( @intCast(cp.codepoint), .regular, .text, - .{ .grid_metrics = self.font_grid.metrics }, + .{ .grid_metrics = self.grid_metrics }, ) catch |err| { log.warn("error rendering preedit glyph err={}", .{err}); return;