From fb0929a11b562b81f031967776ed6efe6b1bbfb4 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 17 Nov 2023 08:52:34 -0800 Subject: [PATCH] renderer/opengl: extract cell program state to dedicated struct --- pkg/opengl/Buffer.zig | 23 ++- pkg/opengl/Program.zig | 35 +++-- pkg/opengl/VertexArray.zig | 19 ++- src/apprt/gtk/ImguiWidget.zig | 2 +- src/renderer/OpenGL.zig | 119 +++------------ src/renderer/opengl/Buffer.zig | 218 ---------------------------- src/renderer/opengl/CellProgram.zig | 183 +++++++++++++++++++++++ src/renderer/opengl/Program.zig | 128 ---------------- src/renderer/opengl/Shader.zig | 56 ------- src/renderer/opengl/Texture.zig | 163 --------------------- src/renderer/opengl/VertexArray.zig | 29 ---- src/renderer/opengl/c.zig | 3 - src/renderer/opengl/draw.zig | 59 -------- src/renderer/opengl/errors.zig | 33 ----- src/renderer/opengl/extensions.zig | 32 ---- src/renderer/opengl/glad.zig | 45 ------ src/renderer/opengl/main.zig | 23 --- src/renderer/shadertoy.zig | 10 +- 18 files changed, 250 insertions(+), 930 deletions(-) delete mode 100644 src/renderer/opengl/Buffer.zig create mode 100644 src/renderer/opengl/CellProgram.zig delete mode 100644 src/renderer/opengl/Program.zig delete mode 100644 src/renderer/opengl/Shader.zig delete mode 100644 src/renderer/opengl/Texture.zig delete mode 100644 src/renderer/opengl/VertexArray.zig delete mode 100644 src/renderer/opengl/c.zig delete mode 100644 src/renderer/opengl/draw.zig delete mode 100644 src/renderer/opengl/errors.zig delete mode 100644 src/renderer/opengl/extensions.zig delete mode 100644 src/renderer/opengl/glad.zig delete mode 100644 src/renderer/opengl/main.zig diff --git a/pkg/opengl/Buffer.zig b/pkg/opengl/Buffer.zig index b794ca4f0..c004dc3f4 100644 --- a/pkg/opengl/Buffer.zig +++ b/pkg/opengl/Buffer.zig @@ -36,7 +36,7 @@ pub const Binding = struct { /// 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( + pub fn setData( b: Binding, data: anytype, usage: Usage, @@ -48,7 +48,7 @@ pub const Binding = struct { /// 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( + pub fn setSubData( b: Binding, offset: usize, data: anytype, @@ -61,7 +61,7 @@ pub const Binding = struct { /// 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( + pub fn setDataNull( b: Binding, comptime T: type, usage: Usage, @@ -71,7 +71,7 @@ pub const Binding = struct { } /// Same as setDataNull but lets you manually specify the buffer size. - pub inline fn setDataNullManual( + pub fn setDataNullManual( b: Binding, size: usize, 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); } @@ -158,7 +158,7 @@ pub const Binding = struct { try errors.getError(); } - pub inline fn attributeAdvanced( + pub fn attributeAdvanced( _: Binding, idx: c.GLuint, size: c.GLint, @@ -177,7 +177,7 @@ pub const Binding = struct { try errors.getError(); } - pub inline fn attributeIAdvanced( + pub fn attributeIAdvanced( _: Binding, idx: c.GLuint, size: c.GLint, @@ -194,25 +194,24 @@ pub const Binding = struct { try errors.getError(); } - pub inline fn unbind(b: *Binding) void { + pub fn unbind(b: Binding) void { glad.context.BindBuffer.?(@intFromEnum(b.target), 0); - b.* = undefined; } }; /// Create a single buffer. -pub inline fn create() !Buffer { +pub 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 { +pub 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 { +pub fn destroy(v: Buffer) void { glad.context.DeleteBuffers.?(1, &v.id); } diff --git a/pkg/opengl/Program.zig b/pkg/opengl/Program.zig index d266bd226..e8a691f17 100644 --- a/pkg/opengl/Program.zig +++ b/pkg/opengl/Program.zig @@ -11,23 +11,22 @@ const glad = @import("glad.zig"); id: c.GLuint, -const Binding = struct { - pub inline fn unbind(_: Binding) void { +pub const Binding = struct { + pub fn unbind(_: Binding) void { glad.context.UseProgram.?(0); } }; -pub inline fn create() !Program { +pub fn create() !Program { const id = glad.context.CreateProgram.?(); if (id == 0) try errors.mustError(); - 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 /// 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); try vs.setSourceAndCompile(vsrc); defer vs.destroy(); @@ -44,12 +43,18 @@ pub inline fn createVF(vsrc: [:0]const u8, fsrc: [:0]const u8) !Program { 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); try errors.getError(); } -pub inline fn link(p: Program) !void { +pub fn link(p: Program) !void { glad.context.LinkProgram.?(p.id); // Check if linking succeeded @@ -67,14 +72,14 @@ pub inline fn link(p: Program) !void { return error.CompileFailed; } -pub inline fn use(p: Program) !Binding { +pub fn use(p: Program) !Binding { glad.context.UseProgram.?(p.id); try errors.getError(); - return Binding{}; + return .{}; } /// Requires the program is currently in use. -pub inline fn setUniform( +pub fn setUniform( p: Program, n: [:0]const u8, value: anytype, @@ -115,14 +120,8 @@ pub inline fn setUniform( // // 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 { +pub 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}); -} diff --git a/pkg/opengl/VertexArray.zig b/pkg/opengl/VertexArray.zig index b86794042..4071c3a2a 100644 --- a/pkg/opengl/VertexArray.zig +++ b/pkg/opengl/VertexArray.zig @@ -7,23 +7,26 @@ const errors = @import("errors.zig"); id: c.GLuint, /// Create a single vertex array object. -pub inline fn create() !VertexArray { +pub 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 { +pub fn bind(v: VertexArray) !Binding { glad.context.BindVertexArray.?(v.id); try errors.getError(); + return .{}; } -pub inline fn destroy(v: VertexArray) void { +pub fn destroy(v: VertexArray) void { glad.context.DeleteVertexArrays.?(1, &v.id); } + +pub const Binding = struct { + pub fn unbind(self: Binding) void { + _ = self; + glad.context.BindVertexArray.?(0); + } +}; diff --git a/src/apprt/gtk/ImguiWidget.zig b/src/apprt/gtk/ImguiWidget.zig index eb9b97f06..d0ce195f8 100644 --- a/src/apprt/gtk/ImguiWidget.zig +++ b/src/apprt/gtk/ImguiWidget.zig @@ -6,7 +6,7 @@ const assert = std.debug.assert; const cimgui = @import("cimgui"); const c = @import("c.zig"); const key = @import("key.zig"); -const gl = @import("../../renderer/opengl/main.zig"); +const gl = @import("opengl"); const input = @import("../../input.zig"); const log = std.log.scoped(.gtk_imgui_widget); diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index 7d91cc9a9..1d9bf6b81 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -21,6 +21,8 @@ const trace = @import("tracy").trace; const math = @import("../math.zig"); const Surface = @import("../Surface.zig"); +const CellProgram = @import("opengl/CellProgram.zig"); + const log = std.log.scoped(.grid); /// 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 - try gl_state.program.setUniform( + try gl_state.cell_program.program.setUniform( "projection", // 2D orthographic projection with the full w/h @@ -152,18 +154,18 @@ const SetFontSize = struct { fn apply(self: SetFontSize, r: *const OpenGL) !void { const gl_state = r.gl_state orelse return error.OpenGLUninitialized; - try gl_state.program.setUniform( + try gl_state.cell_program.program.setUniform( "cell_size", @Vector(2, f32){ @floatFromInt(self.metrics.cell_width), @floatFromInt(self.metrics.cell_height), }, ); - try gl_state.program.setUniform( + try gl_state.cell_program.program.setUniform( "strikethrough_position", @as(f32, @floatFromInt(self.metrics.strikethrough_position)), ); - try gl_state.program.setUniform( + try gl_state.cell_program.program.setUniform( "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); - // Setup our VAO - try gl_state.vao.bind(); - defer gl.VertexArray.unbind() catch null; - - // 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 cell program state, buffers + const bind = try gl_state.cell_program.bind(); + defer bind.unbind(); // Bind our textures 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"); 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 (self.deferred_screen_size) |v| { try v.apply(self); @@ -1506,8 +1496,8 @@ pub fn drawFrame(self: *OpenGL, surface: *apprt.Surface) !void { self.deferred_font_size = null; } - try self.drawCells(binding, self.cells_bg); - try self.drawCells(binding, self.cells); + try self.drawCells(bind.vbo, self.cells_bg); + try self.drawCells(bind.vbo, self.cells); // Swap our window buffers switch (apprt.runtime) { @@ -1573,10 +1563,7 @@ fn drawCells( /// easy to create/destroy these as a set in situations i.e. where the /// OpenGL context is replaced. const GLState = struct { - program: gl.Program, - vao: gl.VertexArray, - ebo: gl.Buffer, - vbo: gl.Buffer, + cell_program: CellProgram, texture: gl.Texture, texture_color: gl.Texture, @@ -1614,74 +1601,6 @@ const GLState = struct { try gl.enable(gl.c.GL_BLEND); 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 const tex = try gl.Texture.create(); 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 .{ - .program = program, - .vao = vao, - .ebo = ebo, - .vbo = vbo, + .cell_program = cell_program, .texture = tex, .texture_color = tex_color, }; @@ -1737,9 +1657,6 @@ const GLState = struct { pub fn deinit(self: *GLState) void { self.texture.destroy(); self.texture_color.destroy(); - self.vbo.destroy(); - self.ebo.destroy(); - self.vao.destroy(); - self.program.destroy(); + self.cell_program.deinit(); } }; diff --git a/src/renderer/opengl/Buffer.zig b/src/renderer/opengl/Buffer.zig deleted file mode 100644 index b794ca4f0..000000000 --- a/src/renderer/opengl/Buffer.zig +++ /dev/null @@ -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); -} diff --git a/src/renderer/opengl/CellProgram.zig b/src/renderer/opengl/CellProgram.zig new file mode 100644 index 000000000..2f3ce3742 --- /dev/null +++ b/src/renderer/opengl/CellProgram.zig @@ -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(); + } +}; diff --git a/src/renderer/opengl/Program.zig b/src/renderer/opengl/Program.zig deleted file mode 100644 index d266bd226..000000000 --- a/src/renderer/opengl/Program.zig +++ /dev/null @@ -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}); -} diff --git a/src/renderer/opengl/Shader.zig b/src/renderer/opengl/Shader.zig deleted file mode 100644 index beaae9e94..000000000 --- a/src/renderer/opengl/Shader.zig +++ /dev/null @@ -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}); -} diff --git a/src/renderer/opengl/Texture.zig b/src/renderer/opengl/Texture.zig deleted file mode 100644 index 91a65b565..000000000 --- a/src/renderer/opengl/Texture.zig +++ /dev/null @@ -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); -} diff --git a/src/renderer/opengl/VertexArray.zig b/src/renderer/opengl/VertexArray.zig deleted file mode 100644 index b86794042..000000000 --- a/src/renderer/opengl/VertexArray.zig +++ /dev/null @@ -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); -} diff --git a/src/renderer/opengl/c.zig b/src/renderer/opengl/c.zig deleted file mode 100644 index 8f4a0f22f..000000000 --- a/src/renderer/opengl/c.zig +++ /dev/null @@ -1,3 +0,0 @@ -pub usingnamespace @cImport({ - @cInclude("glad/gl.h"); -}); diff --git a/src/renderer/opengl/draw.zig b/src/renderer/opengl/draw.zig deleted file mode 100644 index ea6b63103..000000000 --- a/src/renderer/opengl/draw.zig +++ /dev/null @@ -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(); -} diff --git a/src/renderer/opengl/errors.zig b/src/renderer/opengl/errors.zig deleted file mode 100644 index 86639a53a..000000000 --- a/src/renderer/opengl/errors.zig +++ /dev/null @@ -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; -} diff --git a/src/renderer/opengl/extensions.zig b/src/renderer/opengl/extensions.zig deleted file mode 100644 index ca8a4973d..000000000 --- a/src/renderer/opengl/extensions.zig +++ /dev/null @@ -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); - } -}; diff --git a/src/renderer/opengl/glad.zig b/src/renderer/opengl/glad.zig deleted file mode 100644 index 4ee85c549..000000000 --- a/src/renderer/opengl/glad.zig +++ /dev/null @@ -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); -} diff --git a/src/renderer/opengl/main.zig b/src/renderer/opengl/main.zig deleted file mode 100644 index 79d32acea..000000000 --- a/src/renderer/opengl/main.zig +++ /dev/null @@ -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"); diff --git a/src/renderer/shadertoy.zig b/src/renderer/shadertoy.zig index 90541fda9..5f7dc402e 100644 --- a/src/renderer/shadertoy.zig +++ b/src/renderer/shadertoy.zig @@ -198,13 +198,21 @@ pub fn mslFromSpv(alloc: Allocator, spv: []const u8) ![:0]const u8 { /// Convert SPIR-V binary to GLSL.. 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; return try spvCross(alloc, c.SPVC_BACKEND_GLSL, spv, (struct { fn setOptions(options: c.spvc_compiler_options) error{SpvcFailed}!void { if (c.spvc_compiler_options_set_uint( options, c.SPVC_COMPILER_OPTION_GLSL_VERSION, - 430, + GLSL_VERSION, ) != c.SPVC_SUCCESS) { return error.SpvcFailed; }