mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
renderer/opengl: extract cell program state to dedicated struct
This commit is contained in:
@ -36,7 +36,7 @@ pub const Binding = struct {
|
|||||||
|
|
||||||
/// Sets the data of this bound buffer. The data can be any array-like
|
/// 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.
|
/// type. The size of the data is automatically determined based on the type.
|
||||||
pub inline fn setData(
|
pub fn setData(
|
||||||
b: Binding,
|
b: Binding,
|
||||||
data: anytype,
|
data: anytype,
|
||||||
usage: Usage,
|
usage: Usage,
|
||||||
@ -48,7 +48,7 @@ pub const Binding = struct {
|
|||||||
|
|
||||||
/// Sets the data of this bound buffer. The data can be any array-like
|
/// 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.
|
/// type. The size of the data is automatically determined based on the type.
|
||||||
pub inline fn setSubData(
|
pub fn setSubData(
|
||||||
b: Binding,
|
b: Binding,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
data: anytype,
|
data: anytype,
|
||||||
@ -61,7 +61,7 @@ pub const Binding = struct {
|
|||||||
/// Sets the buffer data with a null buffer that is expected to be
|
/// 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
|
/// filled in the future using subData. This requires the type just so
|
||||||
/// we can setup the data size.
|
/// we can setup the data size.
|
||||||
pub inline fn setDataNull(
|
pub fn setDataNull(
|
||||||
b: Binding,
|
b: Binding,
|
||||||
comptime T: type,
|
comptime T: type,
|
||||||
usage: Usage,
|
usage: Usage,
|
||||||
@ -71,7 +71,7 @@ pub const Binding = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Same as setDataNull but lets you manually specify the buffer size.
|
/// Same as setDataNull but lets you manually specify the buffer size.
|
||||||
pub inline fn setDataNullManual(
|
pub fn setDataNullManual(
|
||||||
b: Binding,
|
b: Binding,
|
||||||
size: usize,
|
size: usize,
|
||||||
usage: Usage,
|
usage: Usage,
|
||||||
@ -106,7 +106,7 @@ pub const Binding = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn enableAttribArray(_: Binding, idx: c.GLuint) !void {
|
pub fn enableAttribArray(_: Binding, idx: c.GLuint) !void {
|
||||||
glad.context.EnableVertexAttribArray.?(idx);
|
glad.context.EnableVertexAttribArray.?(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +158,7 @@ pub const Binding = struct {
|
|||||||
try errors.getError();
|
try errors.getError();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn attributeAdvanced(
|
pub fn attributeAdvanced(
|
||||||
_: Binding,
|
_: Binding,
|
||||||
idx: c.GLuint,
|
idx: c.GLuint,
|
||||||
size: c.GLint,
|
size: c.GLint,
|
||||||
@ -177,7 +177,7 @@ pub const Binding = struct {
|
|||||||
try errors.getError();
|
try errors.getError();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn attributeIAdvanced(
|
pub fn attributeIAdvanced(
|
||||||
_: Binding,
|
_: Binding,
|
||||||
idx: c.GLuint,
|
idx: c.GLuint,
|
||||||
size: c.GLint,
|
size: c.GLint,
|
||||||
@ -194,25 +194,24 @@ pub const Binding = struct {
|
|||||||
try errors.getError();
|
try errors.getError();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn unbind(b: *Binding) void {
|
pub fn unbind(b: Binding) void {
|
||||||
glad.context.BindBuffer.?(@intFromEnum(b.target), 0);
|
glad.context.BindBuffer.?(@intFromEnum(b.target), 0);
|
||||||
b.* = undefined;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Create a single buffer.
|
/// Create a single buffer.
|
||||||
pub inline fn create() !Buffer {
|
pub fn create() !Buffer {
|
||||||
var vbo: c.GLuint = undefined;
|
var vbo: c.GLuint = undefined;
|
||||||
glad.context.GenBuffers.?(1, &vbo);
|
glad.context.GenBuffers.?(1, &vbo);
|
||||||
return Buffer{ .id = vbo };
|
return Buffer{ .id = vbo };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// glBindBuffer
|
/// glBindBuffer
|
||||||
pub inline fn bind(v: Buffer, target: Target) !Binding {
|
pub fn bind(v: Buffer, target: Target) !Binding {
|
||||||
glad.context.BindBuffer.?(@intFromEnum(target), v.id);
|
glad.context.BindBuffer.?(@intFromEnum(target), v.id);
|
||||||
return Binding{ .target = target };
|
return Binding{ .target = target };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn destroy(v: Buffer) void {
|
pub fn destroy(v: Buffer) void {
|
||||||
glad.context.DeleteBuffers.?(1, &v.id);
|
glad.context.DeleteBuffers.?(1, &v.id);
|
||||||
}
|
}
|
||||||
|
@ -11,23 +11,22 @@ const glad = @import("glad.zig");
|
|||||||
|
|
||||||
id: c.GLuint,
|
id: c.GLuint,
|
||||||
|
|
||||||
const Binding = struct {
|
pub const Binding = struct {
|
||||||
pub inline fn unbind(_: Binding) void {
|
pub fn unbind(_: Binding) void {
|
||||||
glad.context.UseProgram.?(0);
|
glad.context.UseProgram.?(0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub inline fn create() !Program {
|
pub fn create() !Program {
|
||||||
const id = glad.context.CreateProgram.?();
|
const id = glad.context.CreateProgram.?();
|
||||||
if (id == 0) try errors.mustError();
|
if (id == 0) try errors.mustError();
|
||||||
|
|
||||||
log.debug("program created id={}", .{id});
|
log.debug("program created id={}", .{id});
|
||||||
return Program{ .id = id };
|
return .{ .id = id };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a program from a vertex and fragment shader source. This will
|
/// Create a program from a vertex and fragment shader source. This will
|
||||||
/// compile and link the vertex and fragment shader.
|
/// compile and link the vertex and fragment shader.
|
||||||
pub inline fn createVF(vsrc: [:0]const u8, fsrc: [:0]const u8) !Program {
|
pub fn createVF(vsrc: [:0]const u8, fsrc: [:0]const u8) !Program {
|
||||||
const vs = try Shader.create(c.GL_VERTEX_SHADER);
|
const vs = try Shader.create(c.GL_VERTEX_SHADER);
|
||||||
try vs.setSourceAndCompile(vsrc);
|
try vs.setSourceAndCompile(vsrc);
|
||||||
defer vs.destroy();
|
defer vs.destroy();
|
||||||
@ -44,12 +43,18 @@ pub inline fn createVF(vsrc: [:0]const u8, fsrc: [:0]const u8) !Program {
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn attachShader(p: Program, s: Shader) !void {
|
pub fn destroy(p: Program) void {
|
||||||
|
assert(p.id != 0);
|
||||||
|
glad.context.DeleteProgram.?(p.id);
|
||||||
|
log.debug("program destroyed id={}", .{p.id});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn attachShader(p: Program, s: Shader) !void {
|
||||||
glad.context.AttachShader.?(p.id, s.id);
|
glad.context.AttachShader.?(p.id, s.id);
|
||||||
try errors.getError();
|
try errors.getError();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn link(p: Program) !void {
|
pub fn link(p: Program) !void {
|
||||||
glad.context.LinkProgram.?(p.id);
|
glad.context.LinkProgram.?(p.id);
|
||||||
|
|
||||||
// Check if linking succeeded
|
// Check if linking succeeded
|
||||||
@ -67,14 +72,14 @@ pub inline fn link(p: Program) !void {
|
|||||||
return error.CompileFailed;
|
return error.CompileFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn use(p: Program) !Binding {
|
pub fn use(p: Program) !Binding {
|
||||||
glad.context.UseProgram.?(p.id);
|
glad.context.UseProgram.?(p.id);
|
||||||
try errors.getError();
|
try errors.getError();
|
||||||
return Binding{};
|
return .{};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Requires the program is currently in use.
|
/// Requires the program is currently in use.
|
||||||
pub inline fn setUniform(
|
pub fn setUniform(
|
||||||
p: Program,
|
p: Program,
|
||||||
n: [:0]const u8,
|
n: [:0]const u8,
|
||||||
value: anytype,
|
value: anytype,
|
||||||
@ -115,14 +120,8 @@ pub inline fn setUniform(
|
|||||||
//
|
//
|
||||||
// NOTE(mitchellh): we can add a dynamic version that uses an allocator
|
// NOTE(mitchellh): we can add a dynamic version that uses an allocator
|
||||||
// if we ever need it.
|
// if we ever need it.
|
||||||
pub inline fn getInfoLog(s: Program) [512]u8 {
|
pub fn getInfoLog(s: Program) [512]u8 {
|
||||||
var msg: [512]u8 = undefined;
|
var msg: [512]u8 = undefined;
|
||||||
glad.context.GetProgramInfoLog.?(s.id, msg.len, null, &msg);
|
glad.context.GetProgramInfoLog.?(s.id, msg.len, null, &msg);
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn destroy(p: Program) void {
|
|
||||||
assert(p.id != 0);
|
|
||||||
glad.context.DeleteProgram.?(p.id);
|
|
||||||
log.debug("program destroyed id={}", .{p.id});
|
|
||||||
}
|
|
||||||
|
@ -7,23 +7,26 @@ const errors = @import("errors.zig");
|
|||||||
id: c.GLuint,
|
id: c.GLuint,
|
||||||
|
|
||||||
/// Create a single vertex array object.
|
/// Create a single vertex array object.
|
||||||
pub inline fn create() !VertexArray {
|
pub fn create() !VertexArray {
|
||||||
var vao: c.GLuint = undefined;
|
var vao: c.GLuint = undefined;
|
||||||
glad.context.GenVertexArrays.?(1, &vao);
|
glad.context.GenVertexArrays.?(1, &vao);
|
||||||
return VertexArray{ .id = vao };
|
return VertexArray{ .id = vao };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unbind any active vertex array.
|
|
||||||
pub inline fn unbind() !void {
|
|
||||||
glad.context.BindVertexArray.?(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// glBindVertexArray
|
/// glBindVertexArray
|
||||||
pub inline fn bind(v: VertexArray) !void {
|
pub fn bind(v: VertexArray) !Binding {
|
||||||
glad.context.BindVertexArray.?(v.id);
|
glad.context.BindVertexArray.?(v.id);
|
||||||
try errors.getError();
|
try errors.getError();
|
||||||
|
return .{};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn destroy(v: VertexArray) void {
|
pub fn destroy(v: VertexArray) void {
|
||||||
glad.context.DeleteVertexArrays.?(1, &v.id);
|
glad.context.DeleteVertexArrays.?(1, &v.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const Binding = struct {
|
||||||
|
pub fn unbind(self: Binding) void {
|
||||||
|
_ = self;
|
||||||
|
glad.context.BindVertexArray.?(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -6,7 +6,7 @@ const assert = std.debug.assert;
|
|||||||
const cimgui = @import("cimgui");
|
const cimgui = @import("cimgui");
|
||||||
const c = @import("c.zig");
|
const c = @import("c.zig");
|
||||||
const key = @import("key.zig");
|
const key = @import("key.zig");
|
||||||
const gl = @import("../../renderer/opengl/main.zig");
|
const gl = @import("opengl");
|
||||||
const input = @import("../../input.zig");
|
const input = @import("../../input.zig");
|
||||||
|
|
||||||
const log = std.log.scoped(.gtk_imgui_widget);
|
const log = std.log.scoped(.gtk_imgui_widget);
|
||||||
|
@ -21,6 +21,8 @@ const trace = @import("tracy").trace;
|
|||||||
const math = @import("../math.zig");
|
const math = @import("../math.zig");
|
||||||
const Surface = @import("../Surface.zig");
|
const Surface = @import("../Surface.zig");
|
||||||
|
|
||||||
|
const CellProgram = @import("opengl/CellProgram.zig");
|
||||||
|
|
||||||
const log = std.log.scoped(.grid);
|
const log = std.log.scoped(.grid);
|
||||||
|
|
||||||
/// The runtime can request a single-threaded draw by setting this boolean
|
/// The runtime can request a single-threaded draw by setting this boolean
|
||||||
@ -132,7 +134,7 @@ const SetScreenSize = struct {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Update the projection uniform within our shader
|
// Update the projection uniform within our shader
|
||||||
try gl_state.program.setUniform(
|
try gl_state.cell_program.program.setUniform(
|
||||||
"projection",
|
"projection",
|
||||||
|
|
||||||
// 2D orthographic projection with the full w/h
|
// 2D orthographic projection with the full w/h
|
||||||
@ -152,18 +154,18 @@ const SetFontSize = struct {
|
|||||||
fn apply(self: SetFontSize, r: *const OpenGL) !void {
|
fn apply(self: SetFontSize, r: *const OpenGL) !void {
|
||||||
const gl_state = r.gl_state orelse return error.OpenGLUninitialized;
|
const gl_state = r.gl_state orelse return error.OpenGLUninitialized;
|
||||||
|
|
||||||
try gl_state.program.setUniform(
|
try gl_state.cell_program.program.setUniform(
|
||||||
"cell_size",
|
"cell_size",
|
||||||
@Vector(2, f32){
|
@Vector(2, f32){
|
||||||
@floatFromInt(self.metrics.cell_width),
|
@floatFromInt(self.metrics.cell_width),
|
||||||
@floatFromInt(self.metrics.cell_height),
|
@floatFromInt(self.metrics.cell_height),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
try gl_state.program.setUniform(
|
try gl_state.cell_program.program.setUniform(
|
||||||
"strikethrough_position",
|
"strikethrough_position",
|
||||||
@as(f32, @floatFromInt(self.metrics.strikethrough_position)),
|
@as(f32, @floatFromInt(self.metrics.strikethrough_position)),
|
||||||
);
|
);
|
||||||
try gl_state.program.setUniform(
|
try gl_state.cell_program.program.setUniform(
|
||||||
"strikethrough_thickness",
|
"strikethrough_thickness",
|
||||||
@as(f32, @floatFromInt(self.metrics.strikethrough_thickness)),
|
@as(f32, @floatFromInt(self.metrics.strikethrough_thickness)),
|
||||||
);
|
);
|
||||||
@ -1471,17 +1473,9 @@ pub fn drawFrame(self: *OpenGL, surface: *apprt.Surface) !void {
|
|||||||
);
|
);
|
||||||
gl.clear(gl.c.GL_COLOR_BUFFER_BIT);
|
gl.clear(gl.c.GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
// Setup our VAO
|
// Bind our cell program state, buffers
|
||||||
try gl_state.vao.bind();
|
const bind = try gl_state.cell_program.bind();
|
||||||
defer gl.VertexArray.unbind() catch null;
|
defer bind.unbind();
|
||||||
|
|
||||||
// Bind EBO
|
|
||||||
var ebobind = try gl_state.ebo.bind(.ElementArrayBuffer);
|
|
||||||
defer ebobind.unbind();
|
|
||||||
|
|
||||||
// Bind VBO and set data
|
|
||||||
var binding = try gl_state.vbo.bind(.ArrayBuffer);
|
|
||||||
defer binding.unbind();
|
|
||||||
|
|
||||||
// Bind our textures
|
// Bind our textures
|
||||||
try gl.Texture.active(gl.c.GL_TEXTURE0);
|
try gl.Texture.active(gl.c.GL_TEXTURE0);
|
||||||
@ -1492,10 +1486,6 @@ pub fn drawFrame(self: *OpenGL, surface: *apprt.Surface) !void {
|
|||||||
var texbind1 = try gl_state.texture_color.bind(.@"2D");
|
var texbind1 = try gl_state.texture_color.bind(.@"2D");
|
||||||
defer texbind1.unbind();
|
defer texbind1.unbind();
|
||||||
|
|
||||||
// Pick our shader to use
|
|
||||||
const pbind = try gl_state.program.use();
|
|
||||||
defer pbind.unbind();
|
|
||||||
|
|
||||||
// If we have deferred operations, run them.
|
// If we have deferred operations, run them.
|
||||||
if (self.deferred_screen_size) |v| {
|
if (self.deferred_screen_size) |v| {
|
||||||
try v.apply(self);
|
try v.apply(self);
|
||||||
@ -1506,8 +1496,8 @@ pub fn drawFrame(self: *OpenGL, surface: *apprt.Surface) !void {
|
|||||||
self.deferred_font_size = null;
|
self.deferred_font_size = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try self.drawCells(binding, self.cells_bg);
|
try self.drawCells(bind.vbo, self.cells_bg);
|
||||||
try self.drawCells(binding, self.cells);
|
try self.drawCells(bind.vbo, self.cells);
|
||||||
|
|
||||||
// Swap our window buffers
|
// Swap our window buffers
|
||||||
switch (apprt.runtime) {
|
switch (apprt.runtime) {
|
||||||
@ -1573,10 +1563,7 @@ fn drawCells(
|
|||||||
/// easy to create/destroy these as a set in situations i.e. where the
|
/// easy to create/destroy these as a set in situations i.e. where the
|
||||||
/// OpenGL context is replaced.
|
/// OpenGL context is replaced.
|
||||||
const GLState = struct {
|
const GLState = struct {
|
||||||
program: gl.Program,
|
cell_program: CellProgram,
|
||||||
vao: gl.VertexArray,
|
|
||||||
ebo: gl.Buffer,
|
|
||||||
vbo: gl.Buffer,
|
|
||||||
texture: gl.Texture,
|
texture: gl.Texture,
|
||||||
texture_color: gl.Texture,
|
texture_color: gl.Texture,
|
||||||
|
|
||||||
@ -1614,74 +1601,6 @@ const GLState = struct {
|
|||||||
try gl.enable(gl.c.GL_BLEND);
|
try gl.enable(gl.c.GL_BLEND);
|
||||||
try gl.blendFunc(gl.c.GL_ONE, gl.c.GL_ONE_MINUS_SRC_ALPHA);
|
try gl.blendFunc(gl.c.GL_ONE, gl.c.GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
// Shader
|
|
||||||
const program = try gl.Program.createVF(
|
|
||||||
@embedFile("shaders/cell.v.glsl"),
|
|
||||||
@embedFile("shaders/cell.f.glsl"),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Set our cell dimensions
|
|
||||||
const pbind = try program.use();
|
|
||||||
defer pbind.unbind();
|
|
||||||
|
|
||||||
// Set all of our texture indexes
|
|
||||||
try program.setUniform("text", 0);
|
|
||||||
try program.setUniform("text_color", 1);
|
|
||||||
|
|
||||||
// Setup our VAO
|
|
||||||
const vao = try gl.VertexArray.create();
|
|
||||||
errdefer vao.destroy();
|
|
||||||
try vao.bind();
|
|
||||||
defer gl.VertexArray.unbind() catch null;
|
|
||||||
|
|
||||||
// Element buffer (EBO)
|
|
||||||
const ebo = try gl.Buffer.create();
|
|
||||||
errdefer ebo.destroy();
|
|
||||||
var ebobind = try ebo.bind(.ElementArrayBuffer);
|
|
||||||
defer ebobind.unbind();
|
|
||||||
try ebobind.setData([6]u8{
|
|
||||||
0, 1, 3, // Top-left triangle
|
|
||||||
1, 2, 3, // Bottom-right triangle
|
|
||||||
}, .StaticDraw);
|
|
||||||
|
|
||||||
// Vertex buffer (VBO)
|
|
||||||
const vbo = try gl.Buffer.create();
|
|
||||||
errdefer vbo.destroy();
|
|
||||||
var vbobind = try vbo.bind(.ArrayBuffer);
|
|
||||||
defer vbobind.unbind();
|
|
||||||
var offset: usize = 0;
|
|
||||||
try vbobind.attributeAdvanced(0, 2, gl.c.GL_UNSIGNED_SHORT, false, @sizeOf(GPUCell), offset);
|
|
||||||
offset += 2 * @sizeOf(u16);
|
|
||||||
try vbobind.attributeAdvanced(1, 2, gl.c.GL_UNSIGNED_INT, false, @sizeOf(GPUCell), offset);
|
|
||||||
offset += 2 * @sizeOf(u32);
|
|
||||||
try vbobind.attributeAdvanced(2, 2, gl.c.GL_UNSIGNED_INT, false, @sizeOf(GPUCell), offset);
|
|
||||||
offset += 2 * @sizeOf(u32);
|
|
||||||
try vbobind.attributeAdvanced(3, 2, gl.c.GL_INT, false, @sizeOf(GPUCell), offset);
|
|
||||||
offset += 2 * @sizeOf(i32);
|
|
||||||
try vbobind.attributeAdvanced(4, 4, gl.c.GL_UNSIGNED_BYTE, false, @sizeOf(GPUCell), offset);
|
|
||||||
offset += 4 * @sizeOf(u8);
|
|
||||||
try vbobind.attributeAdvanced(5, 4, gl.c.GL_UNSIGNED_BYTE, false, @sizeOf(GPUCell), offset);
|
|
||||||
offset += 4 * @sizeOf(u8);
|
|
||||||
try vbobind.attributeIAdvanced(6, 1, gl.c.GL_UNSIGNED_BYTE, @sizeOf(GPUCell), offset);
|
|
||||||
offset += 1 * @sizeOf(u8);
|
|
||||||
try vbobind.attributeIAdvanced(7, 1, gl.c.GL_UNSIGNED_BYTE, @sizeOf(GPUCell), offset);
|
|
||||||
try vbobind.enableAttribArray(0);
|
|
||||||
try vbobind.enableAttribArray(1);
|
|
||||||
try vbobind.enableAttribArray(2);
|
|
||||||
try vbobind.enableAttribArray(3);
|
|
||||||
try vbobind.enableAttribArray(4);
|
|
||||||
try vbobind.enableAttribArray(5);
|
|
||||||
try vbobind.enableAttribArray(6);
|
|
||||||
try vbobind.enableAttribArray(7);
|
|
||||||
try vbobind.attributeDivisor(0, 1);
|
|
||||||
try vbobind.attributeDivisor(1, 1);
|
|
||||||
try vbobind.attributeDivisor(2, 1);
|
|
||||||
try vbobind.attributeDivisor(3, 1);
|
|
||||||
try vbobind.attributeDivisor(4, 1);
|
|
||||||
try vbobind.attributeDivisor(5, 1);
|
|
||||||
try vbobind.attributeDivisor(6, 1);
|
|
||||||
try vbobind.attributeDivisor(7, 1);
|
|
||||||
|
|
||||||
// Build our texture
|
// Build our texture
|
||||||
const tex = try gl.Texture.create();
|
const tex = try gl.Texture.create();
|
||||||
errdefer tex.destroy();
|
errdefer tex.destroy();
|
||||||
@ -1724,11 +1643,12 @@ const GLState = struct {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build our cell renderer
|
||||||
|
const cell_program = try CellProgram.init();
|
||||||
|
errdefer cell_program.deinit();
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.program = program,
|
.cell_program = cell_program,
|
||||||
.vao = vao,
|
|
||||||
.ebo = ebo,
|
|
||||||
.vbo = vbo,
|
|
||||||
.texture = tex,
|
.texture = tex,
|
||||||
.texture_color = tex_color,
|
.texture_color = tex_color,
|
||||||
};
|
};
|
||||||
@ -1737,9 +1657,6 @@ const GLState = struct {
|
|||||||
pub fn deinit(self: *GLState) void {
|
pub fn deinit(self: *GLState) void {
|
||||||
self.texture.destroy();
|
self.texture.destroy();
|
||||||
self.texture_color.destroy();
|
self.texture_color.destroy();
|
||||||
self.vbo.destroy();
|
self.cell_program.deinit();
|
||||||
self.ebo.destroy();
|
|
||||||
self.vao.destroy();
|
|
||||||
self.program.destroy();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,218 +0,0 @@
|
|||||||
const Buffer = @This();
|
|
||||||
|
|
||||||
const std = @import("std");
|
|
||||||
const c = @import("c.zig");
|
|
||||||
const errors = @import("errors.zig");
|
|
||||||
const glad = @import("glad.zig");
|
|
||||||
|
|
||||||
id: c.GLuint,
|
|
||||||
|
|
||||||
/// Enum for possible binding targets.
|
|
||||||
pub const Target = enum(c_uint) {
|
|
||||||
ArrayBuffer = c.GL_ARRAY_BUFFER,
|
|
||||||
ElementArrayBuffer = c.GL_ELEMENT_ARRAY_BUFFER,
|
|
||||||
_,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Enum for possible buffer usages.
|
|
||||||
pub const Usage = enum(c_uint) {
|
|
||||||
StreamDraw = c.GL_STREAM_DRAW,
|
|
||||||
StreamRead = c.GL_STREAM_READ,
|
|
||||||
StreamCopy = c.GL_STREAM_COPY,
|
|
||||||
StaticDraw = c.GL_STATIC_DRAW,
|
|
||||||
StaticRead = c.GL_STATIC_READ,
|
|
||||||
StaticCopy = c.GL_STATIC_COPY,
|
|
||||||
DynamicDraw = c.GL_DYNAMIC_DRAW,
|
|
||||||
DynamicRead = c.GL_DYNAMIC_READ,
|
|
||||||
DynamicCopy = c.GL_DYNAMIC_COPY,
|
|
||||||
_,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// 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 {
|
|
||||||
target: Target,
|
|
||||||
|
|
||||||
/// 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 inline 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 inline 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 inline 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 inline 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) * data.len,
|
|
||||||
.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 inline 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 inline 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 inline 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn unbind(b: *Binding) void {
|
|
||||||
glad.context.BindBuffer.?(@intFromEnum(b.target), 0);
|
|
||||||
b.* = undefined;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Create a single buffer.
|
|
||||||
pub inline fn create() !Buffer {
|
|
||||||
var vbo: c.GLuint = undefined;
|
|
||||||
glad.context.GenBuffers.?(1, &vbo);
|
|
||||||
return Buffer{ .id = vbo };
|
|
||||||
}
|
|
||||||
|
|
||||||
/// glBindBuffer
|
|
||||||
pub inline fn bind(v: Buffer, target: Target) !Binding {
|
|
||||||
glad.context.BindBuffer.?(@intFromEnum(target), v.id);
|
|
||||||
return Binding{ .target = target };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn destroy(v: Buffer) void {
|
|
||||||
glad.context.DeleteBuffers.?(1, &v.id);
|
|
||||||
}
|
|
183
src/renderer/opengl/CellProgram.zig
Normal file
183
src/renderer/opengl/CellProgram.zig
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
/// The OpenGL program for rendering terminal cells.
|
||||||
|
const CellProgram = @This();
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const gl = @import("opengl");
|
||||||
|
|
||||||
|
program: gl.Program,
|
||||||
|
vao: gl.VertexArray,
|
||||||
|
ebo: gl.Buffer,
|
||||||
|
vbo: gl.Buffer,
|
||||||
|
|
||||||
|
/// The raw structure that maps directly to the buffer sent to the vertex shader.
|
||||||
|
/// This must be "extern" so that the field order is not reordered by the
|
||||||
|
/// Zig compiler.
|
||||||
|
const Cell = extern struct {
|
||||||
|
/// vec2 grid_coord
|
||||||
|
grid_col: u16,
|
||||||
|
grid_row: u16,
|
||||||
|
|
||||||
|
/// vec2 glyph_pos
|
||||||
|
glyph_x: u32 = 0,
|
||||||
|
glyph_y: u32 = 0,
|
||||||
|
|
||||||
|
/// vec2 glyph_size
|
||||||
|
glyph_width: u32 = 0,
|
||||||
|
glyph_height: u32 = 0,
|
||||||
|
|
||||||
|
/// vec2 glyph_size
|
||||||
|
glyph_offset_x: i32 = 0,
|
||||||
|
glyph_offset_y: i32 = 0,
|
||||||
|
|
||||||
|
/// vec4 fg_color_in
|
||||||
|
fg_r: u8,
|
||||||
|
fg_g: u8,
|
||||||
|
fg_b: u8,
|
||||||
|
fg_a: u8,
|
||||||
|
|
||||||
|
/// vec4 bg_color_in
|
||||||
|
bg_r: u8,
|
||||||
|
bg_g: u8,
|
||||||
|
bg_b: u8,
|
||||||
|
bg_a: u8,
|
||||||
|
|
||||||
|
/// uint mode
|
||||||
|
mode: CellMode,
|
||||||
|
|
||||||
|
/// The width in grid cells that a rendering takes.
|
||||||
|
grid_width: u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
const CellMode = enum(u8) {
|
||||||
|
bg = 1,
|
||||||
|
fg = 2,
|
||||||
|
fg_color = 7,
|
||||||
|
strikethrough = 8,
|
||||||
|
|
||||||
|
// Non-exhaustive because masks change it
|
||||||
|
_,
|
||||||
|
|
||||||
|
/// Apply a mask to the mode.
|
||||||
|
pub fn mask(self: CellMode, m: CellMode) CellMode {
|
||||||
|
return @enumFromInt(@intFromEnum(self) | @intFromEnum(m));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn init() !CellProgram {
|
||||||
|
// Load and compile our shaders.
|
||||||
|
const program = try gl.Program.createVF(
|
||||||
|
@embedFile("../shaders/cell.v.glsl"),
|
||||||
|
@embedFile("../shaders/cell.f.glsl"),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set our cell dimensions
|
||||||
|
const pbind = try program.use();
|
||||||
|
defer pbind.unbind();
|
||||||
|
|
||||||
|
// Set all of our texture indexes
|
||||||
|
try program.setUniform("text", 0);
|
||||||
|
try program.setUniform("text_color", 1);
|
||||||
|
|
||||||
|
// Setup our VAO
|
||||||
|
const vao = try gl.VertexArray.create();
|
||||||
|
errdefer vao.destroy();
|
||||||
|
const vaobind = try vao.bind();
|
||||||
|
defer vaobind.unbind();
|
||||||
|
|
||||||
|
// Element buffer (EBO)
|
||||||
|
const ebo = try gl.Buffer.create();
|
||||||
|
errdefer ebo.destroy();
|
||||||
|
var ebobind = try ebo.bind(.ElementArrayBuffer);
|
||||||
|
defer ebobind.unbind();
|
||||||
|
try ebobind.setData([6]u8{
|
||||||
|
0, 1, 3, // Top-left triangle
|
||||||
|
1, 2, 3, // Bottom-right triangle
|
||||||
|
}, .StaticDraw);
|
||||||
|
|
||||||
|
// Vertex buffer (VBO)
|
||||||
|
const vbo = try gl.Buffer.create();
|
||||||
|
errdefer vbo.destroy();
|
||||||
|
var vbobind = try vbo.bind(.ArrayBuffer);
|
||||||
|
defer vbobind.unbind();
|
||||||
|
var offset: usize = 0;
|
||||||
|
try vbobind.attributeAdvanced(0, 2, gl.c.GL_UNSIGNED_SHORT, false, @sizeOf(Cell), offset);
|
||||||
|
offset += 2 * @sizeOf(u16);
|
||||||
|
try vbobind.attributeAdvanced(1, 2, gl.c.GL_UNSIGNED_INT, false, @sizeOf(Cell), offset);
|
||||||
|
offset += 2 * @sizeOf(u32);
|
||||||
|
try vbobind.attributeAdvanced(2, 2, gl.c.GL_UNSIGNED_INT, false, @sizeOf(Cell), offset);
|
||||||
|
offset += 2 * @sizeOf(u32);
|
||||||
|
try vbobind.attributeAdvanced(3, 2, gl.c.GL_INT, false, @sizeOf(Cell), offset);
|
||||||
|
offset += 2 * @sizeOf(i32);
|
||||||
|
try vbobind.attributeAdvanced(4, 4, gl.c.GL_UNSIGNED_BYTE, false, @sizeOf(Cell), offset);
|
||||||
|
offset += 4 * @sizeOf(u8);
|
||||||
|
try vbobind.attributeAdvanced(5, 4, gl.c.GL_UNSIGNED_BYTE, false, @sizeOf(Cell), offset);
|
||||||
|
offset += 4 * @sizeOf(u8);
|
||||||
|
try vbobind.attributeIAdvanced(6, 1, gl.c.GL_UNSIGNED_BYTE, @sizeOf(Cell), offset);
|
||||||
|
offset += 1 * @sizeOf(u8);
|
||||||
|
try vbobind.attributeIAdvanced(7, 1, gl.c.GL_UNSIGNED_BYTE, @sizeOf(Cell), offset);
|
||||||
|
try vbobind.enableAttribArray(0);
|
||||||
|
try vbobind.enableAttribArray(1);
|
||||||
|
try vbobind.enableAttribArray(2);
|
||||||
|
try vbobind.enableAttribArray(3);
|
||||||
|
try vbobind.enableAttribArray(4);
|
||||||
|
try vbobind.enableAttribArray(5);
|
||||||
|
try vbobind.enableAttribArray(6);
|
||||||
|
try vbobind.enableAttribArray(7);
|
||||||
|
try vbobind.attributeDivisor(0, 1);
|
||||||
|
try vbobind.attributeDivisor(1, 1);
|
||||||
|
try vbobind.attributeDivisor(2, 1);
|
||||||
|
try vbobind.attributeDivisor(3, 1);
|
||||||
|
try vbobind.attributeDivisor(4, 1);
|
||||||
|
try vbobind.attributeDivisor(5, 1);
|
||||||
|
try vbobind.attributeDivisor(6, 1);
|
||||||
|
try vbobind.attributeDivisor(7, 1);
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.program = program,
|
||||||
|
.vao = vao,
|
||||||
|
.ebo = ebo,
|
||||||
|
.vbo = vbo,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bind(self: CellProgram) !Binding {
|
||||||
|
const program = try self.program.use();
|
||||||
|
errdefer program.unbind();
|
||||||
|
|
||||||
|
const vao = try self.vao.bind();
|
||||||
|
errdefer vao.unbind();
|
||||||
|
|
||||||
|
const ebo = try self.ebo.bind(.ElementArrayBuffer);
|
||||||
|
errdefer ebo.unbind();
|
||||||
|
|
||||||
|
const vbo = try self.vbo.bind(.ArrayBuffer);
|
||||||
|
errdefer vbo.unbind();
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.program = program,
|
||||||
|
.vao = vao,
|
||||||
|
.ebo = ebo,
|
||||||
|
.vbo = vbo,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: CellProgram) void {
|
||||||
|
self.vbo.destroy();
|
||||||
|
self.ebo.destroy();
|
||||||
|
self.vao.destroy();
|
||||||
|
self.program.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Binding = struct {
|
||||||
|
program: gl.Program.Binding,
|
||||||
|
vao: gl.VertexArray.Binding,
|
||||||
|
ebo: gl.Buffer.Binding,
|
||||||
|
vbo: gl.Buffer.Binding,
|
||||||
|
|
||||||
|
pub fn unbind(self: Binding) void {
|
||||||
|
self.vbo.unbind();
|
||||||
|
self.ebo.unbind();
|
||||||
|
self.vao.unbind();
|
||||||
|
self.program.unbind();
|
||||||
|
}
|
||||||
|
};
|
@ -1,128 +0,0 @@
|
|||||||
const Program = @This();
|
|
||||||
|
|
||||||
const std = @import("std");
|
|
||||||
const assert = std.debug.assert;
|
|
||||||
const log = std.log.scoped(.opengl);
|
|
||||||
|
|
||||||
const c = @import("c.zig");
|
|
||||||
const Shader = @import("Shader.zig");
|
|
||||||
const errors = @import("errors.zig");
|
|
||||||
const glad = @import("glad.zig");
|
|
||||||
|
|
||||||
id: c.GLuint,
|
|
||||||
|
|
||||||
const Binding = struct {
|
|
||||||
pub inline fn unbind(_: Binding) void {
|
|
||||||
glad.context.UseProgram.?(0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub inline fn create() !Program {
|
|
||||||
const id = glad.context.CreateProgram.?();
|
|
||||||
if (id == 0) try errors.mustError();
|
|
||||||
|
|
||||||
log.debug("program created id={}", .{id});
|
|
||||||
return Program{ .id = id };
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a program from a vertex and fragment shader source. This will
|
|
||||||
/// compile and link the vertex and fragment shader.
|
|
||||||
pub inline fn createVF(vsrc: [:0]const u8, fsrc: [:0]const u8) !Program {
|
|
||||||
const vs = try Shader.create(c.GL_VERTEX_SHADER);
|
|
||||||
try vs.setSourceAndCompile(vsrc);
|
|
||||||
defer vs.destroy();
|
|
||||||
|
|
||||||
const fs = try Shader.create(c.GL_FRAGMENT_SHADER);
|
|
||||||
try fs.setSourceAndCompile(fsrc);
|
|
||||||
defer fs.destroy();
|
|
||||||
|
|
||||||
const p = try create();
|
|
||||||
try p.attachShader(vs);
|
|
||||||
try p.attachShader(fs);
|
|
||||||
try p.link();
|
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn attachShader(p: Program, s: Shader) !void {
|
|
||||||
glad.context.AttachShader.?(p.id, s.id);
|
|
||||||
try errors.getError();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn link(p: Program) !void {
|
|
||||||
glad.context.LinkProgram.?(p.id);
|
|
||||||
|
|
||||||
// Check if linking succeeded
|
|
||||||
var success: c_int = undefined;
|
|
||||||
glad.context.GetProgramiv.?(p.id, c.GL_LINK_STATUS, &success);
|
|
||||||
if (success == c.GL_TRUE) {
|
|
||||||
log.debug("program linked id={}", .{p.id});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
log.err("program link failure id={} message={s}", .{
|
|
||||||
p.id,
|
|
||||||
std.mem.sliceTo(&p.getInfoLog(), 0),
|
|
||||||
});
|
|
||||||
return error.CompileFailed;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn use(p: Program) !Binding {
|
|
||||||
glad.context.UseProgram.?(p.id);
|
|
||||||
try errors.getError();
|
|
||||||
return Binding{};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Requires the program is currently in use.
|
|
||||||
pub inline fn setUniform(
|
|
||||||
p: Program,
|
|
||||||
n: [:0]const u8,
|
|
||||||
value: anytype,
|
|
||||||
) !void {
|
|
||||||
const loc = glad.context.GetUniformLocation.?(
|
|
||||||
p.id,
|
|
||||||
@ptrCast(n.ptr),
|
|
||||||
);
|
|
||||||
if (loc < 0) {
|
|
||||||
return error.UniformNameInvalid;
|
|
||||||
}
|
|
||||||
try errors.getError();
|
|
||||||
|
|
||||||
// Perform the correct call depending on the type of the value.
|
|
||||||
switch (@TypeOf(value)) {
|
|
||||||
comptime_int => glad.context.Uniform1i.?(loc, value),
|
|
||||||
f32 => glad.context.Uniform1f.?(loc, value),
|
|
||||||
@Vector(2, f32) => glad.context.Uniform2f.?(loc, value[0], value[1]),
|
|
||||||
@Vector(3, f32) => glad.context.Uniform3f.?(loc, value[0], value[1], value[2]),
|
|
||||||
@Vector(4, f32) => glad.context.Uniform4f.?(loc, value[0], value[1], value[2], value[3]),
|
|
||||||
[4]@Vector(4, f32) => glad.context.UniformMatrix4fv.?(
|
|
||||||
loc,
|
|
||||||
1,
|
|
||||||
c.GL_FALSE,
|
|
||||||
@ptrCast(&value),
|
|
||||||
),
|
|
||||||
else => {
|
|
||||||
log.warn("unsupported uniform type {}", .{@TypeOf(value)});
|
|
||||||
unreachable;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
try errors.getError();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// getInfoLog returns the info log for this program. This attempts to
|
|
||||||
/// keep the log fully stack allocated and is therefore limited to a max
|
|
||||||
/// amount of elements.
|
|
||||||
//
|
|
||||||
// NOTE(mitchellh): we can add a dynamic version that uses an allocator
|
|
||||||
// if we ever need it.
|
|
||||||
pub inline fn getInfoLog(s: Program) [512]u8 {
|
|
||||||
var msg: [512]u8 = undefined;
|
|
||||||
glad.context.GetProgramInfoLog.?(s.id, msg.len, null, &msg);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn destroy(p: Program) void {
|
|
||||||
assert(p.id != 0);
|
|
||||||
glad.context.DeleteProgram.?(p.id);
|
|
||||||
log.debug("program destroyed id={}", .{p.id});
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
const Shader = @This();
|
|
||||||
|
|
||||||
const std = @import("std");
|
|
||||||
const assert = std.debug.assert;
|
|
||||||
const log = std.log.scoped(.opengl);
|
|
||||||
|
|
||||||
const c = @import("c.zig");
|
|
||||||
const errors = @import("errors.zig");
|
|
||||||
const glad = @import("glad.zig");
|
|
||||||
|
|
||||||
id: c.GLuint,
|
|
||||||
|
|
||||||
pub inline fn create(typ: c.GLenum) errors.Error!Shader {
|
|
||||||
const id = glad.context.CreateShader.?(typ);
|
|
||||||
if (id == 0) {
|
|
||||||
try errors.mustError();
|
|
||||||
unreachable;
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug("shader created id={}", .{id});
|
|
||||||
return Shader{ .id = id };
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the source and compile a shader.
|
|
||||||
pub inline fn setSourceAndCompile(s: Shader, source: [:0]const u8) !void {
|
|
||||||
glad.context.ShaderSource.?(s.id, 1, &@as([*c]const u8, @ptrCast(source)), null);
|
|
||||||
glad.context.CompileShader.?(s.id);
|
|
||||||
|
|
||||||
// Check if compilation succeeded
|
|
||||||
var success: c_int = undefined;
|
|
||||||
glad.context.GetShaderiv.?(s.id, c.GL_COMPILE_STATUS, &success);
|
|
||||||
if (success == c.GL_TRUE) return;
|
|
||||||
log.err("shader compilation failure id={} message={s}", .{
|
|
||||||
s.id,
|
|
||||||
std.mem.sliceTo(&s.getInfoLog(), 0),
|
|
||||||
});
|
|
||||||
return error.CompileFailed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// getInfoLog returns the info log for this shader. This attempts to
|
|
||||||
/// keep the log fully stack allocated and is therefore limited to a max
|
|
||||||
/// amount of elements.
|
|
||||||
//
|
|
||||||
// NOTE(mitchellh): we can add a dynamic version that uses an allocator
|
|
||||||
// if we ever need it.
|
|
||||||
pub inline fn getInfoLog(s: Shader) [512]u8 {
|
|
||||||
var msg: [512]u8 = undefined;
|
|
||||||
glad.context.GetShaderInfoLog.?(s.id, msg.len, null, &msg);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn destroy(s: Shader) void {
|
|
||||||
assert(s.id != 0);
|
|
||||||
glad.context.DeleteShader.?(s.id);
|
|
||||||
log.debug("shader destroyed id={}", .{s.id});
|
|
||||||
}
|
|
@ -1,163 +0,0 @@
|
|||||||
const Texture = @This();
|
|
||||||
|
|
||||||
const std = @import("std");
|
|
||||||
const c = @import("c.zig");
|
|
||||||
const errors = @import("errors.zig");
|
|
||||||
const glad = @import("glad.zig");
|
|
||||||
|
|
||||||
id: c.GLuint,
|
|
||||||
|
|
||||||
pub inline fn active(target: c.GLenum) !void {
|
|
||||||
glad.context.ActiveTexture.?(target);
|
|
||||||
try errors.getError();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enun for possible texture binding targets.
|
|
||||||
pub const Target = enum(c_uint) {
|
|
||||||
@"1D" = c.GL_TEXTURE_1D,
|
|
||||||
@"2D" = c.GL_TEXTURE_2D,
|
|
||||||
@"3D" = c.GL_TEXTURE_3D,
|
|
||||||
@"1DArray" = c.GL_TEXTURE_1D_ARRAY,
|
|
||||||
@"2DArray" = c.GL_TEXTURE_2D_ARRAY,
|
|
||||||
Rectangle = c.GL_TEXTURE_RECTANGLE,
|
|
||||||
CubeMap = c.GL_TEXTURE_CUBE_MAP,
|
|
||||||
Buffer = c.GL_TEXTURE_BUFFER,
|
|
||||||
@"2DMultisample" = c.GL_TEXTURE_2D_MULTISAMPLE,
|
|
||||||
@"2DMultisampleArray" = c.GL_TEXTURE_2D_MULTISAMPLE_ARRAY,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Enum for possible texture parameters.
|
|
||||||
pub const Parameter = enum(c_uint) {
|
|
||||||
BaseLevel = c.GL_TEXTURE_BASE_LEVEL,
|
|
||||||
CompareFunc = c.GL_TEXTURE_COMPARE_FUNC,
|
|
||||||
CompareMode = c.GL_TEXTURE_COMPARE_MODE,
|
|
||||||
LodBias = c.GL_TEXTURE_LOD_BIAS,
|
|
||||||
MinFilter = c.GL_TEXTURE_MIN_FILTER,
|
|
||||||
MagFilter = c.GL_TEXTURE_MAG_FILTER,
|
|
||||||
MinLod = c.GL_TEXTURE_MIN_LOD,
|
|
||||||
MaxLod = c.GL_TEXTURE_MAX_LOD,
|
|
||||||
MaxLevel = c.GL_TEXTURE_MAX_LEVEL,
|
|
||||||
SwizzleR = c.GL_TEXTURE_SWIZZLE_R,
|
|
||||||
SwizzleG = c.GL_TEXTURE_SWIZZLE_G,
|
|
||||||
SwizzleB = c.GL_TEXTURE_SWIZZLE_B,
|
|
||||||
SwizzleA = c.GL_TEXTURE_SWIZZLE_A,
|
|
||||||
WrapS = c.GL_TEXTURE_WRAP_S,
|
|
||||||
WrapT = c.GL_TEXTURE_WRAP_T,
|
|
||||||
WrapR = c.GL_TEXTURE_WRAP_R,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Internal format enum for texture images.
|
|
||||||
pub const InternalFormat = enum(c_int) {
|
|
||||||
Red = c.GL_RED,
|
|
||||||
RGBA = c.GL_RGBA,
|
|
||||||
|
|
||||||
// There are so many more that I haven't filled in.
|
|
||||||
_,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Format for texture images
|
|
||||||
pub const Format = enum(c_uint) {
|
|
||||||
Red = c.GL_RED,
|
|
||||||
BGRA = c.GL_BGRA,
|
|
||||||
|
|
||||||
// There are so many more that I haven't filled in.
|
|
||||||
_,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Data type for texture images.
|
|
||||||
pub const DataType = enum(c_uint) {
|
|
||||||
UnsignedByte = c.GL_UNSIGNED_BYTE,
|
|
||||||
|
|
||||||
// There are so many more that I haven't filled in.
|
|
||||||
_,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Binding = struct {
|
|
||||||
target: Target,
|
|
||||||
|
|
||||||
pub inline fn unbind(b: *Binding) void {
|
|
||||||
glad.context.BindTexture.?(@intFromEnum(b.target), 0);
|
|
||||||
b.* = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generateMipmap(b: Binding) void {
|
|
||||||
glad.context.GenerateMipmap.?(@intFromEnum(b.target));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parameter(b: Binding, name: Parameter, value: anytype) !void {
|
|
||||||
switch (@TypeOf(value)) {
|
|
||||||
c.GLint => glad.context.TexParameteri.?(
|
|
||||||
@intFromEnum(b.target),
|
|
||||||
@intFromEnum(name),
|
|
||||||
value,
|
|
||||||
),
|
|
||||||
else => unreachable,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn image2D(
|
|
||||||
b: Binding,
|
|
||||||
level: c.GLint,
|
|
||||||
internal_format: InternalFormat,
|
|
||||||
width: c.GLsizei,
|
|
||||||
height: c.GLsizei,
|
|
||||||
border: c.GLint,
|
|
||||||
format: Format,
|
|
||||||
typ: DataType,
|
|
||||||
data: ?*const anyopaque,
|
|
||||||
) !void {
|
|
||||||
glad.context.TexImage2D.?(
|
|
||||||
@intFromEnum(b.target),
|
|
||||||
level,
|
|
||||||
@intFromEnum(internal_format),
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
border,
|
|
||||||
@intFromEnum(format),
|
|
||||||
@intFromEnum(typ),
|
|
||||||
data,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn subImage2D(
|
|
||||||
b: Binding,
|
|
||||||
level: c.GLint,
|
|
||||||
xoffset: c.GLint,
|
|
||||||
yoffset: c.GLint,
|
|
||||||
width: c.GLsizei,
|
|
||||||
height: c.GLsizei,
|
|
||||||
format: Format,
|
|
||||||
typ: DataType,
|
|
||||||
data: ?*const anyopaque,
|
|
||||||
) !void {
|
|
||||||
glad.context.TexSubImage2D.?(
|
|
||||||
@intFromEnum(b.target),
|
|
||||||
level,
|
|
||||||
xoffset,
|
|
||||||
yoffset,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
@intFromEnum(format),
|
|
||||||
@intFromEnum(typ),
|
|
||||||
data,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Create a single texture.
|
|
||||||
pub inline fn create() !Texture {
|
|
||||||
var id: c.GLuint = undefined;
|
|
||||||
glad.context.GenTextures.?(1, &id);
|
|
||||||
return Texture{ .id = id };
|
|
||||||
}
|
|
||||||
|
|
||||||
/// glBindTexture
|
|
||||||
pub inline fn bind(v: Texture, target: Target) !Binding {
|
|
||||||
glad.context.BindTexture.?(@intFromEnum(target), v.id);
|
|
||||||
try errors.getError();
|
|
||||||
return Binding{ .target = target };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn destroy(v: Texture) void {
|
|
||||||
glad.context.DeleteTextures.?(1, &v.id);
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
const VertexArray = @This();
|
|
||||||
|
|
||||||
const c = @import("c.zig");
|
|
||||||
const glad = @import("glad.zig");
|
|
||||||
const errors = @import("errors.zig");
|
|
||||||
|
|
||||||
id: c.GLuint,
|
|
||||||
|
|
||||||
/// Create a single vertex array object.
|
|
||||||
pub inline fn create() !VertexArray {
|
|
||||||
var vao: c.GLuint = undefined;
|
|
||||||
glad.context.GenVertexArrays.?(1, &vao);
|
|
||||||
return VertexArray{ .id = vao };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unbind any active vertex array.
|
|
||||||
pub inline fn unbind() !void {
|
|
||||||
glad.context.BindVertexArray.?(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// glBindVertexArray
|
|
||||||
pub inline fn bind(v: VertexArray) !void {
|
|
||||||
glad.context.BindVertexArray.?(v.id);
|
|
||||||
try errors.getError();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn destroy(v: VertexArray) void {
|
|
||||||
glad.context.DeleteVertexArrays.?(1, &v.id);
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
pub usingnamespace @cImport({
|
|
||||||
@cInclude("glad/gl.h");
|
|
||||||
});
|
|
@ -1,59 +0,0 @@
|
|||||||
const c = @import("c.zig");
|
|
||||||
const errors = @import("errors.zig");
|
|
||||||
const glad = @import("glad.zig");
|
|
||||||
|
|
||||||
pub fn clearColor(r: f32, g: f32, b: f32, a: f32) void {
|
|
||||||
glad.context.ClearColor.?(r, g, b, a);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear(mask: c.GLbitfield) void {
|
|
||||||
glad.context.Clear.?(mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn drawArrays(mode: c.GLenum, first: c.GLint, count: c.GLsizei) !void {
|
|
||||||
glad.context.DrawArrays.?(mode, first, count);
|
|
||||||
try errors.getError();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn drawElements(mode: c.GLenum, count: c.GLsizei, typ: c.GLenum, offset: usize) !void {
|
|
||||||
const offsetPtr = if (offset == 0) null else @as(*const anyopaque, @ptrFromInt(offset));
|
|
||||||
glad.context.DrawElements.?(mode, count, typ, offsetPtr);
|
|
||||||
try errors.getError();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn drawElementsInstanced(
|
|
||||||
mode: c.GLenum,
|
|
||||||
count: c.GLsizei,
|
|
||||||
typ: c.GLenum,
|
|
||||||
primcount: usize,
|
|
||||||
) !void {
|
|
||||||
glad.context.DrawElementsInstanced.?(mode, count, typ, null, @intCast(primcount));
|
|
||||||
try errors.getError();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enable(cap: c.GLenum) !void {
|
|
||||||
glad.context.Enable.?(cap);
|
|
||||||
try errors.getError();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn frontFace(mode: c.GLenum) !void {
|
|
||||||
glad.context.FrontFace.?(mode);
|
|
||||||
try errors.getError();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn blendFunc(sfactor: c.GLenum, dfactor: c.GLenum) !void {
|
|
||||||
glad.context.BlendFunc.?(sfactor, dfactor);
|
|
||||||
try errors.getError();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn viewport(x: c.GLint, y: c.GLint, width: c.GLsizei, height: c.GLsizei) !void {
|
|
||||||
glad.context.Viewport.?(x, y, width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pixelStore(mode: c.GLenum, value: anytype) !void {
|
|
||||||
switch (@typeInfo(@TypeOf(value))) {
|
|
||||||
.ComptimeInt, .Int => glad.context.PixelStorei.?(mode, value),
|
|
||||||
else => unreachable,
|
|
||||||
}
|
|
||||||
try errors.getError();
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const c = @import("c.zig");
|
|
||||||
const glad = @import("glad.zig");
|
|
||||||
|
|
||||||
pub const Error = error{
|
|
||||||
InvalidEnum,
|
|
||||||
InvalidValue,
|
|
||||||
InvalidOperation,
|
|
||||||
InvalidFramebufferOperation,
|
|
||||||
OutOfMemory,
|
|
||||||
|
|
||||||
Unknown,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// getError returns the error (if any) from the last OpenGL operation.
|
|
||||||
pub fn getError() Error!void {
|
|
||||||
return switch (glad.context.GetError.?()) {
|
|
||||||
c.GL_NO_ERROR => {},
|
|
||||||
c.GL_INVALID_ENUM => Error.InvalidEnum,
|
|
||||||
c.GL_INVALID_VALUE => Error.InvalidValue,
|
|
||||||
c.GL_INVALID_OPERATION => Error.InvalidOperation,
|
|
||||||
c.GL_INVALID_FRAMEBUFFER_OPERATION => Error.InvalidFramebufferOperation,
|
|
||||||
c.GL_OUT_OF_MEMORY => Error.OutOfMemory,
|
|
||||||
else => Error.Unknown,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// mustError just calls getError but always results in an error being returned.
|
|
||||||
/// If getError has no error, then Unknown is returned.
|
|
||||||
pub fn mustError() Error!void {
|
|
||||||
try getError();
|
|
||||||
return Error.Unknown;
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const c = @import("c.zig");
|
|
||||||
const errors = @import("errors.zig");
|
|
||||||
const glad = @import("glad.zig");
|
|
||||||
|
|
||||||
/// Returns the number of extensions.
|
|
||||||
pub fn len() !u32 {
|
|
||||||
var n: c.GLint = undefined;
|
|
||||||
glad.context.GetIntegerv.?(c.GL_NUM_EXTENSIONS, &n);
|
|
||||||
try errors.getError();
|
|
||||||
return @intCast(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an iterator for the extensions.
|
|
||||||
pub fn iterator() !Iterator {
|
|
||||||
return Iterator{ .len = try len() };
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterator for the available extensions.
|
|
||||||
pub const Iterator = struct {
|
|
||||||
/// The total number of extensions.
|
|
||||||
len: c.GLuint = 0,
|
|
||||||
i: c.GLuint = 0,
|
|
||||||
|
|
||||||
pub fn next(self: *Iterator) !?[]const u8 {
|
|
||||||
if (self.i >= self.len) return null;
|
|
||||||
const res = glad.context.GetStringi.?(c.GL_EXTENSIONS, self.i);
|
|
||||||
try errors.getError();
|
|
||||||
self.i += 1;
|
|
||||||
return std.mem.sliceTo(res, 0);
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,45 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const c = @import("c.zig");
|
|
||||||
|
|
||||||
pub const Context = c.GladGLContext;
|
|
||||||
|
|
||||||
/// This is the current context. Set this var manually prior to calling
|
|
||||||
/// any of this package's functions. I know its nasty to have a global but
|
|
||||||
/// this makes it match OpenGL API styles where it also operates on a
|
|
||||||
/// threadlocal global.
|
|
||||||
pub threadlocal var context: Context = undefined;
|
|
||||||
|
|
||||||
/// Initialize Glad. This is guaranteed to succeed if no errors are returned.
|
|
||||||
/// The getProcAddress param is an anytype so that we can accept multiple
|
|
||||||
/// forms of the function depending on what we're interfacing with.
|
|
||||||
pub fn load(getProcAddress: anytype) !c_int {
|
|
||||||
const GlProc = *const fn () callconv(.C) void;
|
|
||||||
const GlfwFn = *const fn ([*:0]const u8) callconv(.C) ?GlProc;
|
|
||||||
|
|
||||||
const res = switch (@TypeOf(getProcAddress)) {
|
|
||||||
// glfw
|
|
||||||
GlfwFn => c.gladLoadGLContext(&context, @ptrCast(getProcAddress)),
|
|
||||||
|
|
||||||
// null proc address means that we are just loading the globally
|
|
||||||
// pointed gl functions
|
|
||||||
@TypeOf(null) => c.gladLoaderLoadGLContext(&context),
|
|
||||||
|
|
||||||
// try as-is. If this introduces a compiler error, then add a new case.
|
|
||||||
else => c.gladLoadGLContext(&context, getProcAddress),
|
|
||||||
};
|
|
||||||
if (res == 0) return error.GLInitFailed;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unload() void {
|
|
||||||
c.gladLoaderUnloadGLContext(&context);
|
|
||||||
context = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn versionMajor(res: c_uint) c_uint {
|
|
||||||
return c.GLAD_VERSION_MAJOR(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn versionMinor(res: c_uint) c_uint {
|
|
||||||
return c.GLAD_VERSION_MINOR(res);
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
//! OpenGL bindings.
|
|
||||||
//!
|
|
||||||
//! These are purpose-built for usage within this program. While they closely
|
|
||||||
//! align with the OpenGL C APIs, they aren't meant to be general purpose,
|
|
||||||
//! they aren't meant to have 100% API coverage, and they aren't meant to
|
|
||||||
//! be hyper-performant.
|
|
||||||
//!
|
|
||||||
//! For performance-intensive or unsupported aspects of OpenGL, the C
|
|
||||||
//! API is exposed via the `c` constant.
|
|
||||||
//!
|
|
||||||
//! WARNING: Lots of performance improvements that we can make with Zig
|
|
||||||
//! comptime help. I'm deferring this until later but have some fun ideas.
|
|
||||||
|
|
||||||
pub const c = @import("c.zig");
|
|
||||||
pub const glad = @import("glad.zig");
|
|
||||||
pub usingnamespace @import("draw.zig");
|
|
||||||
|
|
||||||
pub const ext = @import("extensions.zig");
|
|
||||||
pub const Buffer = @import("Buffer.zig");
|
|
||||||
pub const Program = @import("Program.zig");
|
|
||||||
pub const Shader = @import("Shader.zig");
|
|
||||||
pub const Texture = @import("Texture.zig");
|
|
||||||
pub const VertexArray = @import("VertexArray.zig");
|
|
@ -198,13 +198,21 @@ pub fn mslFromSpv(alloc: Allocator, spv: []const u8) ![:0]const u8 {
|
|||||||
|
|
||||||
/// Convert SPIR-V binary to GLSL..
|
/// Convert SPIR-V binary to GLSL..
|
||||||
pub fn glslFromSpv(alloc: Allocator, spv: []const u8) ![:0]const u8 {
|
pub fn glslFromSpv(alloc: Allocator, spv: []const u8) ![:0]const u8 {
|
||||||
|
// Our minimum version for shadertoy shaders is OpenGL 4.2 because
|
||||||
|
// Spirv-Cross generates binding locations for uniforms which is
|
||||||
|
// only supported in OpenGL 4.2 and above.
|
||||||
|
//
|
||||||
|
// If we can figure out a way to NOT do this then we can lower this
|
||||||
|
// version.
|
||||||
|
const GLSL_VERSION = 420;
|
||||||
|
|
||||||
const c = spvcross.c;
|
const c = spvcross.c;
|
||||||
return try spvCross(alloc, c.SPVC_BACKEND_GLSL, spv, (struct {
|
return try spvCross(alloc, c.SPVC_BACKEND_GLSL, spv, (struct {
|
||||||
fn setOptions(options: c.spvc_compiler_options) error{SpvcFailed}!void {
|
fn setOptions(options: c.spvc_compiler_options) error{SpvcFailed}!void {
|
||||||
if (c.spvc_compiler_options_set_uint(
|
if (c.spvc_compiler_options_set_uint(
|
||||||
options,
|
options,
|
||||||
c.SPVC_COMPILER_OPTION_GLSL_VERSION,
|
c.SPVC_COMPILER_OPTION_GLSL_VERSION,
|
||||||
430,
|
GLSL_VERSION,
|
||||||
) != c.SPVC_SUCCESS) {
|
) != c.SPVC_SUCCESS) {
|
||||||
return error.SpvcFailed;
|
return error.SpvcFailed;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user