mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
renderer/opengl: setup uniform buffer objects for custom shaders
This commit is contained in:
@ -15,21 +15,35 @@ pub fn create() !Buffer {
|
||||
}
|
||||
|
||||
/// glBindBuffer
|
||||
pub fn bind(v: Buffer, target: Target) !Binding {
|
||||
glad.context.BindBuffer.?(@intFromEnum(target), v.id);
|
||||
return Binding{ .target = target };
|
||||
pub fn bind(self: Buffer, target: Target) !Binding {
|
||||
glad.context.BindBuffer.?(@intFromEnum(target), self.id);
|
||||
return Binding{ .id = self.id, .target = target };
|
||||
}
|
||||
|
||||
pub fn destroy(v: Buffer) void {
|
||||
glad.context.DeleteBuffers.?(1, &v.id);
|
||||
pub fn destroy(self: Buffer) void {
|
||||
glad.context.DeleteBuffers.?(1, &self.id);
|
||||
}
|
||||
|
||||
pub fn bindBase(self: Buffer, target: Target, idx: c.GLuint) !void {
|
||||
glad.context.BindBufferBase.?(
|
||||
@intFromEnum(target),
|
||||
idx,
|
||||
self.id,
|
||||
);
|
||||
try errors.getError();
|
||||
}
|
||||
|
||||
/// Binding is a bound buffer. By using this for functions that operate
|
||||
/// on bound buffers, you can easily defer unbinding and in safety-enabled
|
||||
/// modes verify that unbound buffers are never accessed.
|
||||
pub const Binding = struct {
|
||||
id: c.GLuint,
|
||||
target: Target,
|
||||
|
||||
pub fn unbind(b: Binding) void {
|
||||
glad.context.BindBuffer.?(@intFromEnum(b.target), 0);
|
||||
}
|
||||
|
||||
/// Sets the data of this bound buffer. The data can be any array-like
|
||||
/// type. The size of the data is automatically determined based on the type.
|
||||
pub fn setData(
|
||||
@ -103,7 +117,7 @@ pub const Binding = struct {
|
||||
return switch (@typeInfo(@TypeOf(data))) {
|
||||
.Pointer => |ptr| switch (ptr.size) {
|
||||
.One => .{
|
||||
.size = @sizeOf(ptr.child) * data.len,
|
||||
.size = @sizeOf(ptr.child),
|
||||
.ptr = data,
|
||||
},
|
||||
.Slice => .{
|
||||
@ -209,10 +223,6 @@ pub const Binding = struct {
|
||||
glad.context.VertexAttribIPointer.?(idx, size, typ, stride, offsetPtr);
|
||||
try errors.getError();
|
||||
}
|
||||
|
||||
pub fn unbind(b: Binding) void {
|
||||
glad.context.BindBuffer.?(@intFromEnum(b.target), 0);
|
||||
}
|
||||
};
|
||||
|
||||
/// Enum for possible binding targets.
|
||||
|
@ -78,6 +78,15 @@ pub fn use(p: Program) !Binding {
|
||||
return .{};
|
||||
}
|
||||
|
||||
pub fn uniformBlockBinding(
|
||||
self: Program,
|
||||
index: c.GLuint,
|
||||
binding: c.GLuint,
|
||||
) !void {
|
||||
glad.context.UniformBlockBinding.?(self.id, index, binding);
|
||||
try errors.getError();
|
||||
}
|
||||
|
||||
/// Requires the program is currently in use.
|
||||
pub fn setUniform(
|
||||
p: Program,
|
||||
|
@ -1431,6 +1431,11 @@ fn drawCustomPrograms(
|
||||
// Bind our cell program state, buffers
|
||||
const bind = try program.bind();
|
||||
defer bind.unbind();
|
||||
|
||||
// Sync the uniform data.
|
||||
// TODO: only do this when the data has changed
|
||||
try program.syncUniforms();
|
||||
|
||||
try gl.drawElementsInstanced(gl.c.GL_TRIANGLES, 6, gl.c.GL_UNSIGNED_BYTE, 1);
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,22 @@ const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const gl = @import("opengl");
|
||||
|
||||
/// The "INDEX" is the index into the global GL state and the
|
||||
/// "BINDING" is the binding location in the shader.
|
||||
const UNIFORM_INDEX: gl.c.GLuint = 0;
|
||||
const UNIFORM_BINDING: gl.c.GLuint = 0;
|
||||
|
||||
/// The uniform state. Whenever this is modified this should be
|
||||
/// synced to the buffer. The draw/bind calls don't automatically
|
||||
/// sync this so this should be done whenever the state is modified.
|
||||
uniforms: Uniforms = .{},
|
||||
|
||||
/// The actual shader program.
|
||||
program: gl.Program,
|
||||
|
||||
/// The uniform buffer that is updated with our uniform data.
|
||||
ubo: gl.Buffer,
|
||||
|
||||
/// This VAO is used for all custom shaders. It contains a single quad
|
||||
/// by using an EBO. The vertex ID (gl_VertexID) can be used to determine the
|
||||
/// position of the vertex.
|
||||
@ -14,16 +28,16 @@ vao: gl.VertexArray,
|
||||
ebo: gl.Buffer,
|
||||
|
||||
pub const Uniforms = extern struct {
|
||||
resolution: [3]f32 align(16),
|
||||
time: f32 align(4),
|
||||
time_delta: f32 align(4),
|
||||
frame_rate: f32 align(4),
|
||||
frame: i32 align(4),
|
||||
channel_time: [4][4]f32 align(16),
|
||||
channel_resolution: [4][4]f32 align(16),
|
||||
mouse: [4]f32 align(16),
|
||||
date: [4]f32 align(16),
|
||||
sample_rate: f32 align(4),
|
||||
resolution: [3]f32 align(16) = .{ 0, 0, 0 },
|
||||
time: f32 align(4) = 1,
|
||||
time_delta: f32 align(4) = 1,
|
||||
frame_rate: f32 align(4) = 1,
|
||||
frame: i32 align(4) = 1,
|
||||
channel_time: [4][4]f32 align(16) = [1][4]f32{.{ 0, 0, 0, 0 }} ** 4,
|
||||
channel_resolution: [4][4]f32 align(16) = [1][4]f32{.{ 0, 0, 0, 0 }} ** 4,
|
||||
mouse: [4]f32 align(16) = .{ 0, 0, 0, 0 },
|
||||
date: [4]f32 align(16) = .{ 0, 0, 0, 0 },
|
||||
sample_rate: f32 align(4) = 1,
|
||||
};
|
||||
|
||||
pub fn createList(alloc: Allocator, srcs: []const [:0]const u8) ![]const CustomProgram {
|
||||
@ -39,14 +53,16 @@ pub fn createList(alloc: Allocator, srcs: []const [:0]const u8) ![]const CustomP
|
||||
}
|
||||
|
||||
pub fn init(src: [:0]const u8) !CustomProgram {
|
||||
_ = src;
|
||||
const program = try gl.Program.createVF(
|
||||
@embedFile("../shaders/custom.v.glsl"),
|
||||
//src,
|
||||
@embedFile("../shaders/temp.f.glsl"),
|
||||
src,
|
||||
//@embedFile("../shaders/temp.f.glsl"),
|
||||
);
|
||||
errdefer program.destroy();
|
||||
|
||||
// Map our uniform buffer to the global GL state
|
||||
try program.uniformBlockBinding(UNIFORM_INDEX, UNIFORM_BINDING);
|
||||
|
||||
// Create our uniform buffer that is shared across all custom shaders
|
||||
const ubo = try gl.Buffer.create();
|
||||
errdefer ubo.destroy();
|
||||
@ -74,6 +90,7 @@ pub fn init(src: [:0]const u8) !CustomProgram {
|
||||
|
||||
return .{
|
||||
.program = program,
|
||||
.ubo = ubo,
|
||||
.vao = vao,
|
||||
.ebo = ebo,
|
||||
};
|
||||
@ -85,7 +102,21 @@ pub fn deinit(self: CustomProgram) void {
|
||||
self.program.destroy();
|
||||
}
|
||||
|
||||
pub fn syncUniforms(self: CustomProgram) !void {
|
||||
var ubobind = try self.ubo.bind(.uniform);
|
||||
defer ubobind.unbind();
|
||||
try ubobind.setData(self.uniforms, .static_draw);
|
||||
}
|
||||
|
||||
pub fn bind(self: CustomProgram) !Binding {
|
||||
// Move our uniform buffer into proper global index. Note that
|
||||
// in theory we can do this globally once and never worry about
|
||||
// it again. I don't think we're high-performance enough at all
|
||||
// to worry about that and this makes it so you can just move
|
||||
// around CustomProgram usage without worrying about clobbering
|
||||
// the global state.
|
||||
try self.ubo.bindBase(.uniform, UNIFORM_INDEX);
|
||||
|
||||
const program = try self.program.use();
|
||||
errdefer program.unbind();
|
||||
|
||||
|
@ -13,10 +13,12 @@ layout(binding = 0) uniform Globals {
|
||||
uniform float iSampleRate;
|
||||
};
|
||||
|
||||
layout(binding = 0) uniform sampler2D iChannel0;
|
||||
layout(binding = 1) uniform sampler2D iChannel1;
|
||||
layout(binding = 2) uniform sampler2D iChannel2;
|
||||
layout(binding = 3) uniform sampler2D iChannel3;
|
||||
layout(binding = 1) uniform sampler2D iChannel0;
|
||||
|
||||
// These are unused currently by Ghostty:
|
||||
// layout(binding = 1) uniform sampler2D iChannel1;
|
||||
// layout(binding = 2) uniform sampler2D iChannel2;
|
||||
// layout(binding = 3) uniform sampler2D iChannel3;
|
||||
|
||||
layout(location = 0) in vec4 gl_FragCoord;
|
||||
layout(location = 0) out vec4 _fragColor;
|
||||
|
@ -1,12 +1,28 @@
|
||||
#version 330 core
|
||||
#version 430 core
|
||||
|
||||
layout(location = 0) out vec4 out_FragColor;
|
||||
layout(binding = 0, std140) uniform Globals
|
||||
{
|
||||
vec3 iResolution;
|
||||
float iTime;
|
||||
float iTimeDelta;
|
||||
float iFrameRate;
|
||||
int iFrame;
|
||||
float iChannelTime[4];
|
||||
vec3 iChannelResolution[4];
|
||||
vec4 iMouse;
|
||||
vec4 iDate;
|
||||
float iSampleRate;
|
||||
} _89;
|
||||
|
||||
layout(binding = 1) uniform sampler2D iChannel0;
|
||||
|
||||
layout(location = 0) out vec4 _fragColor;
|
||||
|
||||
void main() {
|
||||
// red
|
||||
//out_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
_fragColor = vec4(_89.iSampleRate, 0.0, 0.0, 1.0);
|
||||
|
||||
// maze
|
||||
vec4 I = gl_FragCoord;
|
||||
out_FragColor = vec4(3)*modf(I*.1,I)[int(length(I)*1e4)&1];
|
||||
//vec4 I = gl_FragCoord;
|
||||
//_fragColor = vec4(3)*modf(I*.1,I)[int(length(I)*1e4)&1];
|
||||
}
|
||||
|
Reference in New Issue
Block a user