renderer: add custom shader cursor uniforms

Based on / supersedes PR #6912, implements discussion #6901

Co-authored-by: Krone Corylus <ahustinkrone@gmail.com>
This commit is contained in:
Qwerasd
2025-06-22 10:28:39 -06:00
parent c7a7474be0
commit bb576d1340
6 changed files with 75 additions and 5 deletions

View File

@@ -50,7 +50,11 @@ pub fn renderGlyph(
const region = try canvas.writeAtlas(alloc, atlas);
return font.Glyph{
.width = width,
// HACK: Set the width for the bar cursor to just the thickness,
// this is just for the benefit of the custom shader cursor
// uniform code. -- In the future code will be introduced to
// auto-crop the canvas so that this isn't needed.
.width = if (sprite == .cursor_bar) thickness else width,
.height = height,
.offset_x = 0,
.offset_y = @intCast(height),

View File

@@ -33,6 +33,8 @@ pub const cellpkg = @import("metal/cell.zig");
pub const imagepkg = @import("metal/image.zig");
pub const custom_shader_target: shadertoy.Target = .msl;
// The fragCoord for Metal shaders is +Y = down.
pub const custom_shader_y_is_down = true;
/// Triple buffering.
pub const swap_chain_count = 3;

View File

@@ -28,6 +28,8 @@ pub const cellpkg = @import("opengl/cell.zig");
pub const imagepkg = @import("opengl/image.zig");
pub const custom_shader_target: shadertoy.Target = .glsl;
// The fragCoord for OpenGL shaders is +Y = up.
pub const custom_shader_y_is_down = false;
/// Because OpenGL's frame completion is always
/// sync, we have no need for multi-buffering.

View File

@@ -675,6 +675,9 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
.mouse = @splat(0), // not currently updated
.date = @splat(0), // not currently updated
.sample_rate = 0, // N/A, we don't have any audio
.current_cursor = @splat(0),
.previous_cursor = @splat(0),
.cursor_change_time = 0,
},
// Fonts
@@ -1966,17 +1969,70 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
self.custom_shader_uniforms.frame += 1;
const screen = self.size.screen;
const padding = self.size.padding;
const cell = self.size.cell;
self.custom_shader_uniforms.resolution = .{
@floatFromInt(self.size.screen.width),
@floatFromInt(self.size.screen.height),
@floatFromInt(screen.width),
@floatFromInt(screen.height),
1,
};
self.custom_shader_uniforms.channel_resolution[0] = .{
@floatFromInt(self.size.screen.width),
@floatFromInt(self.size.screen.height),
@floatFromInt(screen.width),
@floatFromInt(screen.height),
1,
0,
};
// Update custom cursor uniforms, if we have a cursor.
if (self.cells.fg_rows.lists[0].items.len > 0) {
const cursor: shaderpkg.CellText =
self.cells.fg_rows.lists[0].items[0];
const cursor_width: f32 = @floatFromInt(cursor.glyph_size[0]);
const cursor_height: f32 = @floatFromInt(cursor.glyph_size[1]);
var pixel_x: f32 = @floatFromInt(
cursor.grid_pos[0] * cell.width + padding.left,
);
var pixel_y: f32 = @floatFromInt(
cursor.grid_pos[1] * cell.height + padding.top,
);
pixel_x += @floatFromInt(cursor.bearings[0]);
pixel_y += @floatFromInt(cursor.bearings[1]);
// If +Y is up in our shaders, we need to flip the coordinate.
if (!GraphicsAPI.custom_shader_y_is_down) {
pixel_y = @as(f32, @floatFromInt(screen.height)) - pixel_y;
// We need to add the cursor height because we need the +Y
// edge for the Y coordinate, and flipping means that it's
// the -Y edge now.
pixel_y += cursor_height;
}
const cursor_changed: bool =
pixel_x != self.custom_shader_uniforms.current_cursor[0] or
pixel_y != self.custom_shader_uniforms.current_cursor[1] or
cursor_width != self.custom_shader_uniforms.current_cursor[2] or
cursor_height != self.custom_shader_uniforms.current_cursor[3];
if (cursor_changed) {
self.custom_shader_uniforms.previous_cursor =
self.custom_shader_uniforms.current_cursor;
self.custom_shader_uniforms.current_cursor = .{
pixel_x,
pixel_y,
cursor_width,
cursor_height,
};
self.custom_shader_uniforms.cursor_change_time =
self.custom_shader_uniforms.time;
}
}
}
/// Convert the terminal state to GPU cells stored in CPU memory. These

View File

@@ -11,6 +11,9 @@ layout(binding = 1, std140) uniform Globals {
uniform vec4 iMouse;
uniform vec4 iDate;
uniform float iSampleRate;
uniform vec4 iCurrentCursor;
uniform vec4 iPreviousCursor;
uniform float iTimeCursorChange;
};
layout(binding = 0) uniform sampler2D iChannel0;

View File

@@ -21,6 +21,9 @@ pub const Uniforms = extern struct {
mouse: [4]f32 align(16),
date: [4]f32 align(16),
sample_rate: f32 align(4),
current_cursor: [4]f32 align(16),
previous_cursor: [4]f32 align(16),
cursor_change_time: f32 align(4),
};
/// The target to load shaders for.