Ports the terminal-independent half of ZIG/src/renderer (OpenGL only): - opengl/*: buffers, frames, pipelines, render passes, samplers, targets, textures and the OpenGL GraphicsAPI backend on top of glow; the GTK GLArea glue is replaced by a get_proc_address loader closure - the GLSL shaders, copied verbatim and embedded; uniform/vertex structs as bytemuck Pod types with std140 offsets asserted in tests - shadertoy custom shaders converted with naga (GLSL in -> validate -> GLSL 430 out) instead of glslang + spirv-cross - size (screen/grid/cell/padding math), cursor style logic, backend health, options and message types The crate opts into unsafe_code = "deny"; unsafe glow calls live only in opengl/gl.rs. A headless EGL integration test builds every pipeline and the converted shadertoy shaders on Mesa and reads back rendered pixels. Note: Zig's shadertoy Uniforms order the selection background/foreground colors opposite to the GLSL block (likely an upstream bug); kept as in Zig for parity and recorded in the notes. Deferred to the next renderer pass: generic, cell, image, link, row, State, Thread, Overlay (need terminal render state). Test Plan: - cargo test -p ghostty-renderer (33 tests incl. tests/egl_headless.rs)
147 lines
5.0 KiB
GLSL
147 lines
5.0 KiB
GLSL
#include "common.glsl"
|
|
|
|
// The position of the glyph in the texture (x, y)
|
|
layout(location = 0) in uvec2 glyph_pos;
|
|
|
|
// The size of the glyph in the texture (w, h)
|
|
layout(location = 1) in uvec2 glyph_size;
|
|
|
|
// The left and top bearings for the glyph (x, y)
|
|
layout(location = 2) in ivec2 bearings;
|
|
|
|
// The grid coordinates (x, y) where x < columns and y < rows
|
|
layout(location = 3) in uvec2 grid_pos;
|
|
|
|
// The color of the rendered text glyph.
|
|
layout(location = 4) in uvec4 color;
|
|
|
|
// Which atlas this glyph is in.
|
|
layout(location = 5) in uint atlas;
|
|
|
|
// Misc glyph properties.
|
|
layout(location = 6) in uint glyph_bools;
|
|
|
|
// Values `atlas` can take.
|
|
const uint ATLAS_GRAYSCALE = 0u;
|
|
const uint ATLAS_COLOR = 1u;
|
|
|
|
// Masks for the `glyph_bools` attribute
|
|
const uint NO_MIN_CONTRAST = 1u;
|
|
const uint IS_CURSOR_GLYPH = 2u;
|
|
|
|
out CellTextVertexOut {
|
|
flat uint atlas;
|
|
flat vec4 color;
|
|
flat vec4 bg_color;
|
|
vec2 tex_coord;
|
|
} out_data;
|
|
|
|
layout(binding = 1, std430) readonly buffer bg_cells {
|
|
uint bg_colors[];
|
|
};
|
|
|
|
void main() {
|
|
uvec2 grid_size = unpack2u16(grid_size_packed_2u16);
|
|
uvec2 cursor_pos = unpack2u16(cursor_pos_packed_2u16);
|
|
bool cursor_wide = (bools & CURSOR_WIDE) != 0;
|
|
bool use_linear_blending = (bools & USE_LINEAR_BLENDING) != 0;
|
|
|
|
// Convert the grid x, y into world space x, y by accounting for cell size
|
|
vec2 cell_pos = cell_size * vec2(grid_pos);
|
|
|
|
int vid = gl_VertexID;
|
|
|
|
// We use a triangle strip with 4 vertices to render quads,
|
|
// so we determine which corner of the cell this vertex is in
|
|
// based on the vertex ID.
|
|
//
|
|
// 0 --> 1
|
|
// | .'|
|
|
// | / |
|
|
// | L |
|
|
// 2 --> 3
|
|
//
|
|
// 0 = top-left (0, 0)
|
|
// 1 = top-right (1, 0)
|
|
// 2 = bot-left (0, 1)
|
|
// 3 = bot-right (1, 1)
|
|
vec2 corner;
|
|
corner.x = float(vid == 1 || vid == 3);
|
|
corner.y = float(vid == 2 || vid == 3);
|
|
|
|
out_data.atlas = atlas;
|
|
|
|
// === Grid Cell ===
|
|
// +X
|
|
// 0,0--...->
|
|
// |
|
|
// . offset.x = bearings.x
|
|
// +Y. .|.
|
|
// . | |
|
|
// | cell_pos -> +-------+ _.
|
|
// v ._| |_. _|- offset.y = cell_size.y - bearings.y
|
|
// | | .###. | |
|
|
// | | #...# | |
|
|
// glyph_size.y -+ | ##### | |
|
|
// | | #.... | +- bearings.y
|
|
// |_| .#### | |
|
|
// | |_|
|
|
// +-------+
|
|
// |_._|
|
|
// |
|
|
// glyph_size.x
|
|
//
|
|
// In order to get the top left of the glyph, we compute an offset based on
|
|
// the bearings. The Y bearing is the distance from the bottom of the cell
|
|
// to the top of the glyph, so we subtract it from the cell height to get
|
|
// the y offset. The X bearing is the distance from the left of the cell
|
|
// to the left of the glyph, so it works as the x offset directly.
|
|
|
|
vec2 size = vec2(glyph_size);
|
|
vec2 offset = vec2(bearings);
|
|
|
|
offset.y = cell_size.y - offset.y;
|
|
|
|
// Calculate the final position of the cell which uses our glyph size
|
|
// and glyph offset to create the correct bounding box for the glyph.
|
|
cell_pos = cell_pos + size * corner + offset;
|
|
gl_Position = projection_matrix * vec4(cell_pos.x, cell_pos.y, 0.0f, 1.0f);
|
|
|
|
// Calculate the texture coordinate in pixels. This is NOT normalized
|
|
// (between 0.0 and 1.0), and does not need to be, since the texture will
|
|
// be sampled with pixel coordinate mode.
|
|
out_data.tex_coord = vec2(glyph_pos) + vec2(glyph_size) * corner;
|
|
|
|
// Get our color. We always fetch a linearized version to
|
|
// make it easier to handle minimum contrast calculations.
|
|
out_data.color = load_color(color, true);
|
|
// Get the BG color
|
|
out_data.bg_color = load_color(
|
|
unpack4u8(bg_colors[grid_pos.y * grid_size.x + grid_pos.x]),
|
|
true
|
|
);
|
|
// Blend it with the global bg color
|
|
vec4 global_bg = load_color(
|
|
unpack4u8(bg_color_packed_4u8),
|
|
true
|
|
);
|
|
out_data.bg_color += global_bg * vec4(1.0 - out_data.bg_color.a);
|
|
|
|
// If we have a minimum contrast, we need to check if we need to
|
|
// change the color of the text to ensure it has enough contrast
|
|
// with the background.
|
|
if (min_contrast > 1.0f && (glyph_bools & NO_MIN_CONTRAST) == 0) {
|
|
// Ensure our minimum contrast
|
|
out_data.color = contrasted_color(min_contrast, out_data.color, out_data.bg_color);
|
|
}
|
|
|
|
// Check if current position is under cursor (including wide cursor)
|
|
bool is_cursor_pos = ((grid_pos.x == cursor_pos.x) || (cursor_wide && (grid_pos.x == (cursor_pos.x + 1)))) && (grid_pos.y == cursor_pos.y);
|
|
|
|
// If this cell is the cursor cell, but we're not processing
|
|
// the cursor glyph itself, then we need to change the color.
|
|
if ((glyph_bools & IS_CURSOR_GLYPH) == 0 && is_cursor_pos) {
|
|
out_data.color = load_color(unpack4u8(cursor_color_packed_4u8), use_linear_blending);
|
|
}
|
|
}
|