diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-04-04 19:22:04 +0300 |
|---|---|---|
| committer | Can Celik <ogulcancelik@gmail.com> | 2026-04-05 01:23:06 +0300 |
| commit | 41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch) | |
| tree | 3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/pkg/opengl/Buffer.zig | |
| parent | 55c138263e190a1e79d4b997bfb9397306c0ec1f (diff) | |
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/pkg/opengl/Buffer.zig')
| -rw-r--r-- | vendor/libghostty-vt/pkg/opengl/Buffer.zig | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/pkg/opengl/Buffer.zig b/vendor/libghostty-vt/pkg/opengl/Buffer.zig new file mode 100644 index 00000000..60934295 --- /dev/null +++ b/vendor/libghostty-vt/pkg/opengl/Buffer.zig @@ -0,0 +1,245 @@ +const Buffer = @This(); + +const std = @import("std"); +const c = @import("c.zig").c; +const errors = @import("errors.zig"); +const glad = @import("glad.zig"); + +id: c.GLuint, + +/// Create a single buffer. +pub fn create() !Buffer { + var vbo: c.GLuint = undefined; + glad.context.GenBuffers.?(1, &vbo); + return Buffer{ .id = vbo }; +} + +/// glBindBuffer +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(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( + b: Binding, + data: anytype, + usage: Usage, + ) !void { + const info = dataInfo(data); + glad.context.BufferData.?( + @intFromEnum(b.target), + info.size, + info.ptr, + @intFromEnum(usage), + ); + try errors.getError(); + } + + /// 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 setSubData( + b: Binding, + offset: usize, + data: anytype, + ) !void { + const info = dataInfo(data); + glad.context.BufferSubData.?( + @intFromEnum(b.target), + @intCast(offset), + info.size, + info.ptr, + ); + try errors.getError(); + } + + /// Sets the buffer data with a null buffer that is expected to be + /// filled in the future using subData. This requires the type just so + /// we can setup the data size. + pub fn setDataNull( + b: Binding, + comptime T: type, + usage: Usage, + ) !void { + glad.context.BufferData.?( + @intFromEnum(b.target), + @sizeOf(T), + null, + @intFromEnum(usage), + ); + try errors.getError(); + } + + /// Same as setDataNull but lets you manually specify the buffer size. + pub fn setDataNullManual( + b: Binding, + size: usize, + usage: Usage, + ) !void { + glad.context.BufferData.?( + @intFromEnum(b.target), + @intCast(size), + null, + @intFromEnum(usage), + ); + try errors.getError(); + } + + fn dataInfo(data: anytype) struct { + size: isize, + ptr: *const anyopaque, + } { + return switch (@typeInfo(@TypeOf(data))) { + .pointer => |ptr| switch (ptr.size) { + .one => .{ + .size = @sizeOf(ptr.child), + .ptr = data, + }, + .slice => .{ + .size = @intCast(@sizeOf(ptr.child) * data.len), + .ptr = data.ptr, + }, + else => { + std.log.err("invalid buffer data pointer size: {}", .{ptr.size}); + unreachable; + }, + }, + else => { + std.log.err("invalid buffer data type: {s}", .{@tagName(@typeInfo(@TypeOf(data)))}); + unreachable; + }, + }; + } + + /// Shorthand for vertexAttribPointer that is specialized towards the + /// common use case of specifying an array of homogeneous types that + /// don't need normalization. This also enables the attribute at idx. + pub fn attribute( + b: Binding, + idx: c.GLuint, + size: c.GLint, + comptime T: type, + offset: usize, + ) !void { + const info: struct { + // Type of the each component in the array. + typ: c.GLenum, + + // The byte offset between each full set of attributes. + stride: c.GLsizei, + + // The size of each component used in calculating the offset. + offset: usize, + } = switch (@typeInfo(T)) { + .Array => |ary| .{ + .typ = switch (ary.child) { + f32 => c.GL_FLOAT, + else => @compileError("unsupported array child type"), + }, + .offset = @sizeOf(ary.child), + .stride = @sizeOf(T), + }, + else => @compileError("unsupported type"), + }; + + try b.attributeAdvanced( + idx, + size, + info.typ, + false, + info.stride, + offset * info.offset, + ); + try b.enableAttribArray(idx); + } + + /// VertexAttribDivisor + pub fn attributeDivisor(_: Binding, idx: c.GLuint, divisor: c.GLuint) !void { + glad.context.VertexAttribDivisor.?(idx, divisor); + try errors.getError(); + } + + pub fn attributeAdvanced( + _: Binding, + idx: c.GLuint, + size: c.GLint, + typ: c.GLenum, + normalized: bool, + stride: c.GLsizei, + offset: usize, + ) !void { + const normalized_c: c.GLboolean = if (normalized) c.GL_TRUE else c.GL_FALSE; + const offsetPtr = if (offset > 0) + @as(*const anyopaque, @ptrFromInt(offset)) + else + null; + + glad.context.VertexAttribPointer.?(idx, size, typ, normalized_c, stride, offsetPtr); + try errors.getError(); + } + + pub fn attributeIAdvanced( + _: Binding, + idx: c.GLuint, + size: c.GLint, + typ: c.GLenum, + stride: c.GLsizei, + offset: usize, + ) !void { + const offsetPtr = if (offset > 0) + @as(*const anyopaque, @ptrFromInt(offset)) + else + null; + + glad.context.VertexAttribIPointer.?(idx, size, typ, stride, offsetPtr); + try errors.getError(); + } +}; + +/// Enum for possible binding targets. +pub const Target = enum(c_uint) { + array = c.GL_ARRAY_BUFFER, + element_array = c.GL_ELEMENT_ARRAY_BUFFER, + uniform = c.GL_UNIFORM_BUFFER, + storage = c.GL_SHADER_STORAGE_BUFFER, + _, +}; + +/// Enum for possible buffer usages. +pub const Usage = enum(c_uint) { + stream_draw = c.GL_STREAM_DRAW, + stream_read = c.GL_STREAM_READ, + stream_copy = c.GL_STREAM_COPY, + static_draw = c.GL_STATIC_DRAW, + static_read = c.GL_STATIC_READ, + static_copy = c.GL_STATIC_COPY, + dynamic_draw = c.GL_DYNAMIC_DRAW, + dynamic_read = c.GL_DYNAMIC_READ, + dynamic_copy = c.GL_DYNAMIC_COPY, + _, +}; |
