feat(renderer): port the OpenGL backend, shaders and size math
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)
This commit is contained in:
1 parent
ac0e05def1
commit
c548e63bd1
35 files changed
+5477
-5
No files matched your search
@@ -0,0 +1,145 @@
|
||||
#include "common.glsl"
|
||||
|
||||
layout(binding = 0) uniform sampler2D image;
|
||||
|
||||
layout(location = 0) in float in_opacity;
|
||||
layout(location = 1) in uint info;
|
||||
|
||||
// 4 bits of info.
|
||||
const uint BG_IMAGE_POSITION = 15u;
|
||||
const uint BG_IMAGE_TL = 0u;
|
||||
const uint BG_IMAGE_TC = 1u;
|
||||
const uint BG_IMAGE_TR = 2u;
|
||||
const uint BG_IMAGE_ML = 3u;
|
||||
const uint BG_IMAGE_MC = 4u;
|
||||
const uint BG_IMAGE_MR = 5u;
|
||||
const uint BG_IMAGE_BL = 6u;
|
||||
const uint BG_IMAGE_BC = 7u;
|
||||
const uint BG_IMAGE_BR = 8u;
|
||||
|
||||
// 2 bits of info shifted 4.
|
||||
const uint BG_IMAGE_FIT = 3u << 4;
|
||||
const uint BG_IMAGE_CONTAIN = 0u << 4;
|
||||
const uint BG_IMAGE_COVER = 1u << 4;
|
||||
const uint BG_IMAGE_STRETCH = 2u << 4;
|
||||
const uint BG_IMAGE_NO_FIT = 3u << 4;
|
||||
|
||||
// 1 bit of info shifted 6.
|
||||
const uint BG_IMAGE_REPEAT = 1u << 6;
|
||||
|
||||
flat out vec4 bg_color;
|
||||
flat out vec2 offset;
|
||||
flat out vec2 scale;
|
||||
flat out float opacity;
|
||||
// We use a uint to pass the repeat value because
|
||||
// bools aren't allowed for vertex outputs in OpenGL.
|
||||
flat out uint repeat;
|
||||
|
||||
void main() {
|
||||
bool use_linear_blending = (bools & USE_LINEAR_BLENDING) != 0;
|
||||
|
||||
vec4 position;
|
||||
position.x = (gl_VertexID == 2) ? 3.0 : -1.0;
|
||||
position.y = (gl_VertexID == 0) ? -3.0 : 1.0;
|
||||
position.z = 1.0;
|
||||
position.w = 1.0;
|
||||
|
||||
// Single triangle is clipped to viewport.
|
||||
//
|
||||
// X <- vid == 0: (-1, -3)
|
||||
// |\
|
||||
// | \
|
||||
// | \
|
||||
// |###\
|
||||
// |#+# \ `+` is (0, 0). `#`s are viewport area.
|
||||
// |### \
|
||||
// X------X <- vid == 2: (3, 1)
|
||||
// ^
|
||||
// vid == 1: (-1, 1)
|
||||
|
||||
gl_Position = position;
|
||||
|
||||
opacity = in_opacity;
|
||||
|
||||
repeat = info & BG_IMAGE_REPEAT;
|
||||
|
||||
vec2 screen_size = screen_size;
|
||||
vec2 tex_size = textureSize(image, 0);
|
||||
|
||||
vec2 dest_size = tex_size;
|
||||
switch (info & BG_IMAGE_FIT) {
|
||||
// For `contain` we scale by a factor that makes the image
|
||||
// width match the screen width or makes the image height
|
||||
// match the screen height, whichever is smaller.
|
||||
case BG_IMAGE_CONTAIN: {
|
||||
float scale = min(screen_size.x / tex_size.x, screen_size.y / tex_size.y);
|
||||
dest_size = tex_size * scale;
|
||||
} break;
|
||||
|
||||
// For `cover` we scale by a factor that makes the image
|
||||
// width match the screen width or makes the image height
|
||||
// match the screen height, whichever is larger.
|
||||
case BG_IMAGE_COVER: {
|
||||
float scale = max(screen_size.x / tex_size.x, screen_size.y / tex_size.y);
|
||||
dest_size = tex_size * scale;
|
||||
} break;
|
||||
|
||||
// For `stretch` we stretch the image to the size of
|
||||
// the screen without worrying about aspect ratio.
|
||||
case BG_IMAGE_STRETCH: {
|
||||
dest_size = screen_size;
|
||||
} break;
|
||||
|
||||
// For `none` we just use the original texture size.
|
||||
case BG_IMAGE_NO_FIT: {
|
||||
dest_size = tex_size;
|
||||
} break;
|
||||
}
|
||||
|
||||
vec2 start = vec2(0.0);
|
||||
vec2 mid = (screen_size - dest_size) / vec2(2.0);
|
||||
vec2 end = screen_size - dest_size;
|
||||
|
||||
vec2 dest_offset = mid;
|
||||
switch (info & BG_IMAGE_POSITION) {
|
||||
case BG_IMAGE_TL: {
|
||||
dest_offset = vec2(start.x, start.y);
|
||||
} break;
|
||||
case BG_IMAGE_TC: {
|
||||
dest_offset = vec2(mid.x, start.y);
|
||||
} break;
|
||||
case BG_IMAGE_TR: {
|
||||
dest_offset = vec2(end.x, start.y);
|
||||
} break;
|
||||
case BG_IMAGE_ML: {
|
||||
dest_offset = vec2(start.x, mid.y);
|
||||
} break;
|
||||
case BG_IMAGE_MC: {
|
||||
dest_offset = vec2(mid.x, mid.y);
|
||||
} break;
|
||||
case BG_IMAGE_MR: {
|
||||
dest_offset = vec2(end.x, mid.y);
|
||||
} break;
|
||||
case BG_IMAGE_BL: {
|
||||
dest_offset = vec2(start.x, end.y);
|
||||
} break;
|
||||
case BG_IMAGE_BC: {
|
||||
dest_offset = vec2(mid.x, end.y);
|
||||
} break;
|
||||
case BG_IMAGE_BR: {
|
||||
dest_offset = vec2(end.x, end.y);
|
||||
} break;
|
||||
}
|
||||
|
||||
offset = dest_offset;
|
||||
scale = tex_size / dest_size;
|
||||
|
||||
// We load a fully opaque version of the bg color and combine it with
|
||||
// the alpha separately, because we need these as separate values in
|
||||
// the framgment shader.
|
||||
uvec4 u_bg_color = unpack4u8(bg_color_packed_4u8);
|
||||
bg_color = vec4(load_color(
|
||||
uvec4(u_bg_color.rgb, 255),
|
||||
use_linear_blending
|
||||
).rgb, float(u_bg_color.a) / 255.0);
|
||||
}
|
||||
Reference in new issue
Block a user