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,160 @@
|
||||
//! Wrapper for handling render pipelines (`opengl/Pipeline.zig`).
|
||||
use super::gl::{
|
||||
self, AttribType, FramebufferTarget, Gl, RawFramebuffer, RawProgram, RawVertexArray,
|
||||
};
|
||||
|
||||
/// Vertex step function.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub enum StepFunction {
|
||||
Constant,
|
||||
#[default]
|
||||
PerVertex,
|
||||
PerInstance,
|
||||
}
|
||||
|
||||
/// Options for initializing a render pipeline.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Options<'a> {
|
||||
/// GLSL source of the vertex function
|
||||
pub vertex_fn: &'a str,
|
||||
/// GLSL source of the fragment function
|
||||
pub fragment_fn: &'a str,
|
||||
|
||||
/// Vertex step function
|
||||
pub step_fn: StepFunction,
|
||||
|
||||
/// Whether to enable blending.
|
||||
pub blending_enabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> Options<'a> {
|
||||
pub fn new(vertex_fn: &'a str, fragment_fn: &'a str) -> Self {
|
||||
Options {
|
||||
vertex_fn,
|
||||
fragment_fn,
|
||||
step_fn: StepFunction::PerVertex,
|
||||
blending_enabled: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// One vertex attribute of a vertex struct (what Zig derives by comptime
|
||||
/// reflection over the struct fields in `autoAttribute`).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Attribute {
|
||||
/// Number of components (1 for scalars, N for `[T; N]`).
|
||||
pub size: i32,
|
||||
/// Component type.
|
||||
pub ty: AttribType,
|
||||
/// Byte offset within the vertex struct.
|
||||
pub offset: u32,
|
||||
}
|
||||
|
||||
/// A vertex struct whose fields map 1:1 to vertex attribute locations
|
||||
/// 0, 1, 2, ... in declaration order.
|
||||
pub trait Vertex: bytemuck::Pod {
|
||||
const ATTRIBUTES: &'static [Attribute];
|
||||
}
|
||||
|
||||
pub struct Pipeline {
|
||||
gl: Gl,
|
||||
|
||||
pub program: RawProgram,
|
||||
|
||||
pub fbo: RawFramebuffer,
|
||||
|
||||
pub vao: RawVertexArray,
|
||||
|
||||
pub stride: usize,
|
||||
|
||||
pub blending_enabled: bool,
|
||||
}
|
||||
|
||||
impl Pipeline {
|
||||
/// Build a pipeline with vertex attributes derived from `V`
|
||||
/// (`Pipeline.init(VertexAttributes, opts)`).
|
||||
pub fn init<V: Vertex>(gl: &Gl, opts: &Options<'_>) -> gl::Result<Pipeline> {
|
||||
Self::init_impl(gl, Some((V::ATTRIBUTES, size_of::<V>())), opts)
|
||||
}
|
||||
|
||||
/// Build a pipeline without any vertex attributes
|
||||
/// (`Pipeline.init(null, opts)`).
|
||||
pub fn init_no_attributes(gl: &Gl, opts: &Options<'_>) -> gl::Result<Pipeline> {
|
||||
Self::init_impl(gl, None, opts)
|
||||
}
|
||||
|
||||
fn init_impl(
|
||||
gl: &Gl,
|
||||
attrs: Option<(&[Attribute], usize)>,
|
||||
opts: &Options<'_>,
|
||||
) -> gl::Result<Pipeline> {
|
||||
// Load and compile our shaders.
|
||||
let program = gl.create_program_vf(opts.vertex_fn, opts.fragment_fn)?;
|
||||
|
||||
let fbo = match gl.create_framebuffer() {
|
||||
Ok(f) => f,
|
||||
Err(e) => {
|
||||
gl.delete_program(program);
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
let vao = match gl.create_vertex_array() {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
gl.delete_framebuffer(fbo);
|
||||
gl.delete_program(program);
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
// From here on, dropping `this` releases everything on error.
|
||||
let this = Pipeline {
|
||||
gl: gl.clone(),
|
||||
program,
|
||||
fbo,
|
||||
vao,
|
||||
stride: attrs.map_or(0, |(_, stride)| stride),
|
||||
blending_enabled: opts.blending_enabled,
|
||||
};
|
||||
|
||||
let r = (|| -> gl::Result<()> {
|
||||
gl.use_program(Some(program))?;
|
||||
let fbobind = gl.bind_framebuffer(fbo, FramebufferTarget::Framebuffer);
|
||||
let r = (|| {
|
||||
gl.bind_vertex_array(Some(vao))?;
|
||||
let r = match attrs {
|
||||
Some((attrs, _)) => auto_attribute(gl, attrs, opts.step_fn),
|
||||
None => Ok(()),
|
||||
};
|
||||
let _ = gl.bind_vertex_array(None);
|
||||
r
|
||||
})();
|
||||
gl.unbind_framebuffer(fbobind);
|
||||
let _ = gl.use_program(None);
|
||||
r
|
||||
})();
|
||||
r?;
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Pipeline {
|
||||
fn drop(&mut self) {
|
||||
self.gl.delete_program(self.program);
|
||||
self.gl.delete_framebuffer(self.fbo);
|
||||
self.gl.delete_vertex_array(self.vao);
|
||||
}
|
||||
}
|
||||
|
||||
fn auto_attribute(gl: &Gl, attrs: &[Attribute], step_fn: StepFunction) -> gl::Result<()> {
|
||||
let divisor: u32 = match step_fn {
|
||||
StepFunction::PerVertex => 0,
|
||||
StepFunction::PerInstance => 1,
|
||||
StepFunction::Constant => u32::MAX,
|
||||
};
|
||||
|
||||
for (i, a) in attrs.iter().enumerate() {
|
||||
gl.setup_vertex_attrib(i as u32, 0, divisor, a.size, a.ty, a.offset)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Reference in new issue
Block a user