refactor for background process + spawning gui
This commit is contained in:
@@ -1,4 +1,9 @@
|
||||
// Glyph rendering shader for terminal emulator
|
||||
// Supports both legacy quad-based rendering and new instanced cell rendering
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// LEGACY QUAD-BASED RENDERING (for backwards compatibility)
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
struct VertexInput {
|
||||
@location(0) position: vec2<f32>,
|
||||
@@ -46,3 +51,311 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
// The background was already rendered, so we just blend the glyph on top
|
||||
return vec4<f32>(in.color.rgb, in.color.a * glyph_alpha);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// KITTY-STYLE INSTANCED CELL RENDERING
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
// Color table uniform containing 256 indexed colors + default fg/bg
|
||||
struct ColorTable {
|
||||
// 256 indexed colors + default_fg (256) + default_bg (257)
|
||||
colors: array<vec4<f32>, 258>,
|
||||
}
|
||||
|
||||
// Grid parameters uniform
|
||||
struct GridParams {
|
||||
// Grid dimensions in cells
|
||||
cols: u32,
|
||||
rows: u32,
|
||||
// Cell dimensions in pixels
|
||||
cell_width: f32,
|
||||
cell_height: f32,
|
||||
// Screen dimensions in pixels
|
||||
screen_width: f32,
|
||||
screen_height: f32,
|
||||
// Y offset for tab bar
|
||||
y_offset: f32,
|
||||
// Cursor position (-1 if hidden)
|
||||
cursor_col: i32,
|
||||
cursor_row: i32,
|
||||
// Cursor style: 0=block, 1=underline, 2=bar
|
||||
cursor_style: u32,
|
||||
// Padding
|
||||
_padding: vec2<u32>,
|
||||
}
|
||||
|
||||
// GPUCell instance data (matches Rust GPUCell struct)
|
||||
struct GPUCell {
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
decoration_fg: u32,
|
||||
sprite_idx: u32,
|
||||
attrs: u32,
|
||||
}
|
||||
|
||||
// Sprite info for glyph positioning
|
||||
struct SpriteInfo {
|
||||
// UV coordinates in atlas (x, y, width, height) - normalized 0-1
|
||||
uv: vec4<f32>,
|
||||
// Offset from cell origin (x, y) in pixels
|
||||
offset: vec2<f32>,
|
||||
// Size in pixels
|
||||
size: vec2<f32>,
|
||||
}
|
||||
|
||||
// Uniforms and storage buffers for instanced rendering
|
||||
@group(1) @binding(0)
|
||||
var<uniform> color_table: ColorTable;
|
||||
|
||||
@group(1) @binding(1)
|
||||
var<uniform> grid_params: GridParams;
|
||||
|
||||
@group(1) @binding(2)
|
||||
var<storage, read> cells: array<GPUCell>;
|
||||
|
||||
@group(1) @binding(3)
|
||||
var<storage, read> sprites: array<SpriteInfo>;
|
||||
|
||||
// Constants for packed color decoding
|
||||
const COLOR_TYPE_DEFAULT: u32 = 0u;
|
||||
const COLOR_TYPE_INDEXED: u32 = 1u;
|
||||
const COLOR_TYPE_RGB: u32 = 2u;
|
||||
|
||||
// Constants for cell attributes
|
||||
const ATTR_DECORATION_MASK: u32 = 0x7u;
|
||||
const ATTR_BOLD_BIT: u32 = 0x8u;
|
||||
const ATTR_ITALIC_BIT: u32 = 0x10u;
|
||||
const ATTR_REVERSE_BIT: u32 = 0x20u;
|
||||
const ATTR_STRIKE_BIT: u32 = 0x40u;
|
||||
const ATTR_DIM_BIT: u32 = 0x80u;
|
||||
|
||||
// Colored glyph flag
|
||||
const COLORED_GLYPH_FLAG: u32 = 0x80000000u;
|
||||
|
||||
// Vertex output for instanced cell rendering
|
||||
struct CellVertexOutput {
|
||||
@builtin(position) clip_position: vec4<f32>,
|
||||
@location(0) uv: vec2<f32>,
|
||||
@location(1) fg_color: vec4<f32>,
|
||||
@location(2) bg_color: vec4<f32>,
|
||||
@location(3) @interpolate(flat) is_background: u32,
|
||||
@location(4) @interpolate(flat) is_colored_glyph: u32,
|
||||
}
|
||||
|
||||
// Resolve a packed color to RGBA
|
||||
fn resolve_color(packed: u32, is_foreground: bool) -> vec4<f32> {
|
||||
let color_type = packed & 0xFFu;
|
||||
|
||||
if color_type == COLOR_TYPE_DEFAULT {
|
||||
// Default color - use color table entry 256 (fg) or 257 (bg)
|
||||
if is_foreground {
|
||||
return color_table.colors[256];
|
||||
} else {
|
||||
return color_table.colors[257];
|
||||
}
|
||||
} else if color_type == COLOR_TYPE_INDEXED {
|
||||
// Indexed color - look up in color table
|
||||
let index = (packed >> 8u) & 0xFFu;
|
||||
return color_table.colors[index];
|
||||
} else {
|
||||
// RGB color - extract components
|
||||
let r = f32((packed >> 8u) & 0xFFu) / 255.0;
|
||||
let g = f32((packed >> 16u) & 0xFFu) / 255.0;
|
||||
let b = f32((packed >> 24u) & 0xFFu) / 255.0;
|
||||
return vec4<f32>(r, g, b, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert sRGB to linear (for GPU rendering to sRGB surface)
|
||||
fn srgb_to_linear(c: f32) -> f32 {
|
||||
if c <= 0.04045 {
|
||||
return c / 12.92;
|
||||
} else {
|
||||
return pow((c + 0.055) / 1.055, 2.4);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert pixel coordinate to NDC
|
||||
fn pixel_to_ndc(pixel: vec2<f32>, screen: vec2<f32>) -> vec2<f32> {
|
||||
return vec2<f32>(
|
||||
(pixel.x / screen.x) * 2.0 - 1.0,
|
||||
1.0 - (pixel.y / screen.y) * 2.0
|
||||
);
|
||||
}
|
||||
|
||||
// Background vertex shader (renders cell backgrounds)
|
||||
// vertex_index: 0-3 for quad corners
|
||||
// instance_index: cell index in row-major order
|
||||
@vertex
|
||||
fn vs_cell_bg(
|
||||
@builtin(vertex_index) vertex_index: u32,
|
||||
@builtin(instance_index) instance_index: u32
|
||||
) -> CellVertexOutput {
|
||||
let col = instance_index % grid_params.cols;
|
||||
let row = instance_index / grid_params.cols;
|
||||
|
||||
// Skip if out of bounds
|
||||
if row >= grid_params.rows {
|
||||
var out: CellVertexOutput;
|
||||
out.clip_position = vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Get cell data
|
||||
let cell = cells[instance_index];
|
||||
|
||||
// Calculate cell pixel position
|
||||
let cell_x = f32(col) * grid_params.cell_width;
|
||||
let cell_y = grid_params.y_offset + f32(row) * grid_params.cell_height;
|
||||
|
||||
// Quad vertex positions (0=top-left, 1=top-right, 2=bottom-right, 3=bottom-left)
|
||||
var positions: array<vec2<f32>, 4>;
|
||||
positions[0] = vec2<f32>(cell_x, cell_y);
|
||||
positions[1] = vec2<f32>(cell_x + grid_params.cell_width, cell_y);
|
||||
positions[2] = vec2<f32>(cell_x + grid_params.cell_width, cell_y + grid_params.cell_height);
|
||||
positions[3] = vec2<f32>(cell_x, cell_y + grid_params.cell_height);
|
||||
|
||||
let screen_size = vec2<f32>(grid_params.screen_width, grid_params.screen_height);
|
||||
let ndc_pos = pixel_to_ndc(positions[vertex_index], screen_size);
|
||||
|
||||
// Resolve colors
|
||||
let attrs = cell.attrs;
|
||||
let is_reverse = (attrs & ATTR_REVERSE_BIT) != 0u;
|
||||
|
||||
var fg = resolve_color(cell.fg, true);
|
||||
var bg = resolve_color(cell.bg, false);
|
||||
|
||||
// Handle reverse video
|
||||
if is_reverse {
|
||||
let tmp = fg;
|
||||
fg = bg;
|
||||
bg = tmp;
|
||||
}
|
||||
|
||||
// Convert to linear for sRGB surface
|
||||
fg = vec4<f32>(srgb_to_linear(fg.r), srgb_to_linear(fg.g), srgb_to_linear(fg.b), fg.a);
|
||||
bg = vec4<f32>(srgb_to_linear(bg.r), srgb_to_linear(bg.g), srgb_to_linear(bg.b), bg.a);
|
||||
|
||||
var out: CellVertexOutput;
|
||||
out.clip_position = vec4<f32>(ndc_pos, 0.0, 1.0);
|
||||
out.uv = vec2<f32>(0.0, 0.0); // Not used for background
|
||||
out.fg_color = fg;
|
||||
out.bg_color = bg;
|
||||
out.is_background = 1u;
|
||||
out.is_colored_glyph = 0u;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// Glyph vertex shader (renders cell glyphs)
|
||||
@vertex
|
||||
fn vs_cell_glyph(
|
||||
@builtin(vertex_index) vertex_index: u32,
|
||||
@builtin(instance_index) instance_index: u32
|
||||
) -> CellVertexOutput {
|
||||
let col = instance_index % grid_params.cols;
|
||||
let row = instance_index / grid_params.cols;
|
||||
|
||||
// Skip if out of bounds
|
||||
if row >= grid_params.rows {
|
||||
var out: CellVertexOutput;
|
||||
out.clip_position = vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Get cell data
|
||||
let cell = cells[instance_index];
|
||||
let sprite_idx = cell.sprite_idx & ~COLORED_GLYPH_FLAG;
|
||||
let is_colored = (cell.sprite_idx & COLORED_GLYPH_FLAG) != 0u;
|
||||
|
||||
// Skip if no glyph
|
||||
if sprite_idx == 0u {
|
||||
var out: CellVertexOutput;
|
||||
out.clip_position = vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Get sprite info
|
||||
let sprite = sprites[sprite_idx];
|
||||
|
||||
// Skip if sprite has no size
|
||||
if sprite.size.x <= 0.0 || sprite.size.y <= 0.0 {
|
||||
var out: CellVertexOutput;
|
||||
out.clip_position = vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Calculate cell pixel position
|
||||
let cell_x = f32(col) * grid_params.cell_width;
|
||||
let cell_y = grid_params.y_offset + f32(row) * grid_params.cell_height;
|
||||
|
||||
// Calculate glyph position (baseline-relative)
|
||||
let baseline_y = cell_y + grid_params.cell_height * 0.8;
|
||||
let glyph_x = cell_x + sprite.offset.x;
|
||||
let glyph_y = baseline_y - sprite.offset.y - sprite.size.y;
|
||||
|
||||
// Quad vertex positions
|
||||
var positions: array<vec2<f32>, 4>;
|
||||
positions[0] = vec2<f32>(glyph_x, glyph_y);
|
||||
positions[1] = vec2<f32>(glyph_x + sprite.size.x, glyph_y);
|
||||
positions[2] = vec2<f32>(glyph_x + sprite.size.x, glyph_y + sprite.size.y);
|
||||
positions[3] = vec2<f32>(glyph_x, glyph_y + sprite.size.y);
|
||||
|
||||
// UV coordinates
|
||||
var uvs: array<vec2<f32>, 4>;
|
||||
uvs[0] = vec2<f32>(sprite.uv.x, sprite.uv.y);
|
||||
uvs[1] = vec2<f32>(sprite.uv.x + sprite.uv.z, sprite.uv.y);
|
||||
uvs[2] = vec2<f32>(sprite.uv.x + sprite.uv.z, sprite.uv.y + sprite.uv.w);
|
||||
uvs[3] = vec2<f32>(sprite.uv.x, sprite.uv.y + sprite.uv.w);
|
||||
|
||||
let screen_size = vec2<f32>(grid_params.screen_width, grid_params.screen_height);
|
||||
let ndc_pos = pixel_to_ndc(positions[vertex_index], screen_size);
|
||||
|
||||
// Resolve colors
|
||||
let attrs = cell.attrs;
|
||||
let is_reverse = (attrs & ATTR_REVERSE_BIT) != 0u;
|
||||
|
||||
var fg = resolve_color(cell.fg, true);
|
||||
var bg = resolve_color(cell.bg, false);
|
||||
|
||||
if is_reverse {
|
||||
let tmp = fg;
|
||||
fg = bg;
|
||||
bg = tmp;
|
||||
}
|
||||
|
||||
// Convert to linear
|
||||
fg = vec4<f32>(srgb_to_linear(fg.r), srgb_to_linear(fg.g), srgb_to_linear(fg.b), fg.a);
|
||||
|
||||
var out: CellVertexOutput;
|
||||
out.clip_position = vec4<f32>(ndc_pos, 0.0, 1.0);
|
||||
out.uv = uvs[vertex_index];
|
||||
out.fg_color = fg;
|
||||
out.bg_color = vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
||||
out.is_background = 0u;
|
||||
out.is_colored_glyph = select(0u, 1u, is_colored);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// Fragment shader for cell rendering (both background and glyph)
|
||||
@fragment
|
||||
fn fs_cell(in: CellVertexOutput) -> @location(0) vec4<f32> {
|
||||
if in.is_background == 1u {
|
||||
// Background - just output the bg color
|
||||
return in.bg_color;
|
||||
}
|
||||
|
||||
// Glyph - sample from atlas
|
||||
let glyph_alpha = textureSample(atlas_texture, atlas_sampler, in.uv).r;
|
||||
|
||||
if in.is_colored_glyph == 1u {
|
||||
// Colored glyph (emoji) - use atlas color directly
|
||||
// Note: For now we just use alpha since our atlas is single-channel
|
||||
// Full emoji support would need an RGBA atlas
|
||||
return vec4<f32>(in.fg_color.rgb, glyph_alpha);
|
||||
}
|
||||
|
||||
// Normal glyph - tint with foreground color
|
||||
return vec4<f32>(in.fg_color.rgb, in.fg_color.a * glyph_alpha);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user