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.
This commit is contained in:
Leah Amelia Chen
2024-08-31 01:05:59 +02:00
parent 1d04c52bb2
commit 49415ef1d2
3 changed files with 62 additions and 42 deletions

View File

@ -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,

View File

@ -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 {

View File

@ -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<float> textureGrayscale [[texture(0)]],
texture2d<float> textureColor [[texture(1)]]
texture2d<float> 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