mirror-ghostty/src/renderer/opengl/Pipeline.zig

170 lines
4.2 KiB
Zig

//! Wrapper for handling render pipelines.
const Self = @This();
const std = @import("std");
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const builtin = @import("builtin");
const gl = @import("opengl");
const OpenGL = @import("../OpenGL.zig");
const Texture = @import("Texture.zig");
const Buffer = @import("buffer.zig").Buffer;
const log = std.log.scoped(.opengl);
/// Options for initializing a render pipeline.
pub const Options = struct {
/// GLSL source of the vertex function
vertex_fn: [:0]const u8,
/// GLSL source of the fragment function
fragment_fn: [:0]const u8,
/// Vertex step function
step_fn: StepFunction = .per_vertex,
/// Whether to enable blending.
blending_enabled: bool = true,
pub const StepFunction = enum {
constant,
per_vertex,
per_instance,
};
};
program: gl.Program,
fbo: gl.Framebuffer,
vao: gl.VertexArray,
stride: usize,
blending_enabled: bool,
pub fn init(comptime VertexAttributes: ?type, opts: Options) !Self {
// Load and compile our shaders.
const program = try gl.Program.createVF(
opts.vertex_fn,
opts.fragment_fn,
);
errdefer program.destroy();
const pbind = try program.use();
defer pbind.unbind();
const fbo = try gl.Framebuffer.create();
errdefer fbo.destroy();
const fbobind = try fbo.bind(.framebuffer);
defer fbobind.unbind();
const vao = try gl.VertexArray.create();
errdefer vao.destroy();
const vaobind = try vao.bind();
defer vaobind.unbind();
if (VertexAttributes) |VA| try autoAttribute(VA, vaobind, opts.step_fn);
return .{
.program = program,
.fbo = fbo,
.vao = vao,
.stride = if (VertexAttributes) |VA| @sizeOf(VA) else 0,
.blending_enabled = opts.blending_enabled,
};
}
pub fn deinit(self: *const Self) void {
self.program.destroy();
}
fn autoAttribute(
T: type,
vaobind: gl.VertexArray.Binding,
step_fn: Options.StepFunction,
) !void {
const divisor: gl.c.GLuint = switch (step_fn) {
.per_vertex => 0,
.per_instance => 1,
.constant => std.math.maxInt(gl.c.GLuint),
};
inline for (@typeInfo(T).@"struct".fields, 0..) |field, i| {
try vaobind.enableAttribArray(i);
try vaobind.attributeBinding(i, 0);
try vaobind.bindingDivisor(i, divisor);
const offset = @offsetOf(T, field.name);
const FT = switch (@typeInfo(field.type)) {
.@"enum" => |e| e.tag_type,
else => field.type,
};
const size, const IT = switch (@typeInfo(FT)) {
.array => |a| .{ a.len, a.child },
else => .{ 1, FT },
};
try switch (IT) {
u8 => vaobind.attributeIFormat(
i,
size,
gl.c.GL_UNSIGNED_BYTE,
offset,
),
u16 => vaobind.attributeIFormat(
i,
size,
gl.c.GL_UNSIGNED_SHORT,
offset,
),
u32 => vaobind.attributeIFormat(
i,
size,
gl.c.GL_UNSIGNED_INT,
offset,
),
i8 => vaobind.attributeIFormat(
i,
size,
gl.c.GL_BYTE,
offset,
),
i16 => vaobind.attributeIFormat(
i,
size,
gl.c.GL_SHORT,
offset,
),
i32 => vaobind.attributeIFormat(
i,
size,
gl.c.GL_INT,
offset,
),
f16 => vaobind.attributeFormat(
i,
size,
gl.c.GL_HALF_FLOAT,
false,
offset,
),
f32 => vaobind.attributeFormat(
i,
size,
gl.c.GL_FLOAT,
false,
offset,
),
f64 => vaobind.attributeLFormat(
i,
size,
offset,
),
else => unreachable,
};
}
}