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;