From 49415ef1d283aec9df5019148ba63a596473a751 Mon Sep 17 00:00:00 2001 From: Leah Amelia Chen Date: Sat, 31 Aug 2024 01:05:59 +0200 Subject: [PATCH] renderer(metal): implement blinking (untested!) See a9561a46 (this commit's OpenGL equivalent) for more information. Untested for now since I don't have a macOS setup. --- src/renderer/Metal.zig | 33 +++++++++++++++----- src/renderer/metal/shaders.zig | 18 +++++++---- src/renderer/shaders/cell.metal | 53 ++++++++++++++++----------------- 3 files changed, 62 insertions(+), 42 deletions(-) diff --git a/src/renderer/Metal.zig b/src/renderer/Metal.zig index 1629678f6..1b05316d9 100644 --- a/src/renderer/Metal.zig +++ b/src/renderer/Metal.zig @@ -97,6 +97,9 @@ cursor_color: ?terminal.color.RGB, /// foreground color as the cursor color. cursor_invert: bool, +/// Whether blinking cells are currently visible. Synchronized with cursor blinking. +blink_visible: bool = true, + /// The current frame background color. This is only updated during /// the updateFrame method. current_background_color: terminal.color.RGB, @@ -867,7 +870,7 @@ pub fn updateFrame( self: *Metal, surface: *apprt.Surface, state: *renderer.State, - cursor_blink_visible: bool, + blink_visible: bool, ) !void { _ = surface; @@ -950,7 +953,7 @@ pub fn updateFrame( const cursor_style = renderer.cursorStyle( state, self.focused, - cursor_blink_visible, + blink_visible, ); // Get our preedit state @@ -1027,6 +1030,9 @@ pub fn updateFrame( .full_rebuild = full_rebuild, }; }; + + self.blink_visible = blink_visible; + defer { critical.screen.deinit(); if (critical.preedit) |p| p.deinit(self.alloc); @@ -1101,6 +1107,8 @@ pub fn drawFrame(self: *Metal, surface: *apprt.Surface) !void { errdefer self.gpu_state.releaseFrame(); // log.debug("drawing frame index={}", .{self.gpu_state.frame_index}); + self.uniforms.blink_visible = self.blink_visible; + // Setup our frame data try frame.uniforms.sync(self.gpu_state.device, &.{self.uniforms}); try frame.cells_bg.sync(self.gpu_state.device, self.cells.bg_cells); @@ -1603,6 +1611,11 @@ fn drawCellFgs( @as(c_ulong, 1), }, ); + encoder.msgSend( + void, + objc.sel("setFragmentBuffer:offset:atIndex:"), + .{ frame.uniforms.buffer.value, @as(c_ulong, 0), @as(c_ulong, 1) }, + ); encoder.msgSend( void, @@ -2539,15 +2552,19 @@ fn updateCell( break :glyph; } - const mode: mtl_shaders.CellText.Mode = switch (try fgMode( + var mode: mtl_shaders.CellText.Mode = .{ .fg = true }; + + switch (try fgMode( render.presentation, cell_pin, )) { - .normal => .fg, - .color => .fg_color, - .constrained => .fg_constrained, - .powerline => .fg_powerline, - }; + .normal => {}, + .color => mode.fg_color = true, + .constrained => mode.fg_constrained = true, + .powerline => mode.fg_powerline = true, + } + + mode.fg_blink = style.flags.blink; try self.cells.add(self.alloc, .text, .{ .mode = mode, diff --git a/src/renderer/metal/shaders.zig b/src/renderer/metal/shaders.zig index 2a202de30..057e9c6fd 100644 --- a/src/renderer/metal/shaders.zig +++ b/src/renderer/metal/shaders.zig @@ -137,6 +137,9 @@ pub const Uniforms = extern struct { cursor_pos: [2]u16 align(4), cursor_color: [4]u8 align(4), + /// Whether blinking cells and cursors are visible on this frame. + blink_visible: bool align(1), + const PaddingExtend = packed struct(u8) { left: bool = false, right: bool = false, @@ -324,12 +327,15 @@ pub const CellText = extern struct { mode: Mode align(1), constraint_width: u8 align(1) = 0, - pub const Mode = enum(u8) { - fg = 1, - fg_constrained = 2, - fg_color = 3, - cursor = 4, - fg_powerline = 5, + pub const Mode = packed struct(u8) { + fg: bool, + fg_constrained: bool, + fg_color: bool, + cursor: bool, + fg_powerline: bool, + fg_blink: bool, + + _padding: u3 = 0, }; test { diff --git a/src/renderer/shaders/cell.metal b/src/renderer/shaders/cell.metal index 734608c76..296c478b8 100644 --- a/src/renderer/shaders/cell.metal +++ b/src/renderer/shaders/cell.metal @@ -18,6 +18,7 @@ struct Uniforms { float min_contrast; ushort2 cursor_pos; uchar4 cursor_color; + bool blink_visible; }; //------------------------------------------------------------------- @@ -161,9 +162,9 @@ fragment float4 cell_bg_fragment( enum CellTextMode : uint8_t { MODE_TEXT = 1u, MODE_TEXT_CONSTRAINED = 2u, - MODE_TEXT_COLOR = 3u, - MODE_TEXT_CURSOR = 4u, - MODE_TEXT_POWERLINE = 5u, + MODE_TEXT_COLOR = 4u, + MODE_TEXT_CURSOR = 8u, + MODE_TEXT_POWERLINE = 16u, }; struct CellTextVertexIn { @@ -257,7 +258,7 @@ vertex CellTextVertexOut cell_text_vertex( // If we're constrained then we need to scale the glyph. // We also always constrain colored glyphs since we should have // their scaled cell size exactly correct. - if (in.mode == MODE_TEXT_CONSTRAINED || in.mode == MODE_TEXT_COLOR) { + if (in.mode & (MODE_TEXT_CONSTRAINED | MODE_TEXT_COLOR)) { float max_width = uniforms.cell_size.x * in.constraint_width; if (size.x > max_width) { float new_y = size.y * (max_width / size.x); @@ -285,14 +286,14 @@ vertex CellTextVertexOut cell_text_vertex( // since we want color glyphs to appear in their original color // and Powerline glyphs to be unaffected (else parts of the line would // have different colors as some parts are displayed via background colors). - if (uniforms.min_contrast > 1.0f && in.mode == MODE_TEXT) { + if (uniforms.min_contrast > 1.0f && !(mode & ~(MODE_TEXT | MODE_TEXT_BLINK))) { float4 bg_color = float4(bg_colors[in.grid_pos.y * uniforms.grid_size.x + in.grid_pos.x]) / 255.0f; out.color = contrasted_color(uniforms.min_contrast, out.color, bg_color); } // If this cell is the cursor cell, then we need to change the color. if ( - in.mode != MODE_TEXT_CURSOR && + !(in.mode & MODE_TEXT_CURSOR) && in.grid_pos.x == uniforms.cursor_pos.x && in.grid_pos.y == uniforms.cursor_pos.y ) { @@ -305,7 +306,8 @@ vertex CellTextVertexOut cell_text_vertex( fragment float4 cell_text_fragment( CellTextVertexOut in [[stage_in]], texture2d textureGrayscale [[texture(0)]], - texture2d textureColor [[texture(1)]] + texture2d textureColor [[texture(1)]], + constant Uniforms& uniforms [[buffer(1)]] ) { constexpr sampler textureSampler( coord::pixel, @@ -313,28 +315,23 @@ fragment float4 cell_text_fragment( filter::nearest ); - switch (in.mode) { - default: - case MODE_TEXT_CURSOR: - case MODE_TEXT_CONSTRAINED: - case MODE_TEXT_POWERLINE: - case MODE_TEXT: { - // We premult the alpha to our whole color since our blend function - // uses One/OneMinusSourceAlpha to avoid blurry edges. - // We first premult our given color. - float4 premult = float4(in.color.rgb * in.color.a, in.color.a); - - // Then premult the texture color - float a = textureGrayscale.sample(textureSampler, in.tex_coord).r; - premult = premult * a; - - return premult; - } - - case MODE_TEXT_COLOR: { - return textureColor.sample(textureSampler, in.tex_coord); - } + if (in.mode & MODE_TEXT_COLOR) { + return textureColor.sample(textureSampler, in.tex_coord); } + if (in.mode & MODE_TEXT_BLINK && !uniforms.blink_visible) { + discard_fragment(); + } + + // We premult the alpha to our whole color since our blend function + // uses One/OneMinusSourceAlpha to avoid blurry edges. + // We first premult our given color. + float4 premult = float4(in.color.rgb * in.color.a, in.color.a); + + // Then premult the texture color + float a = textureGrayscale.sample(textureSampler, in.tex_coord).r; + premult = premult * a; + + return premult; } //------------------------------------------------------------------- // Image Shader