mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-04-25 19:08:39 +03:00
249 lines
7.0 KiB
Zig
249 lines
7.0 KiB
Zig
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;
|
|
},
|
|
};
|
|
}
|
|
|
|
pub fn enableAttribArray(_: Binding, idx: c.GLuint) !void {
|
|
glad.context.EnableVertexAttribArray.?(idx);
|
|
}
|
|
|
|
/// 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,
|
|
_,
|
|
};
|
|
|
|
/// 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,
|
|
_,
|
|
};
|