diff --git a/src/c.zig b/src/c.zig index eb0370879..80f582be2 100644 --- a/src/c.zig +++ b/src/c.zig @@ -1,3 +1,3 @@ pub usingnamespace @cImport({ - @cInclude("raylib.h"); + @cInclude("epoxy/gl.h"); }); diff --git a/src/main.zig b/src/main.zig index 6083561f1..eac21cdc0 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,8 +1,7 @@ const std = @import("std"); const glfw = @import("glfw"); -const c = @cImport({ - @cInclude("epoxy/gl.h"); -}); +const c = @import("c.zig"); +const gl = @import("opengl.zig"); pub fn main() !void { try glfw.init(.{}); @@ -28,44 +27,20 @@ pub fn main() !void { }).callback); // Create our vertex shader - const vs = c.glCreateShader(c.GL_VERTEX_SHADER); - c.glShaderSource( - vs, - 1, - &@ptrCast([*c]const u8, vs_source), - null, - ); - c.glCompileShader(vs); - var success: c_int = undefined; - c.glGetShaderiv(vs, c.GL_COMPILE_STATUS, &success); - if (success != c.GL_TRUE) { - var msg: [512]u8 = undefined; - c.glGetShaderInfoLog(vs, 512, null, &msg); - std.log.err("Fail: {s}\n", .{std.mem.sliceTo(&msg, 0)}); - return; - } + const vs = try gl.Shader.create(gl.c.GL_VERTEX_SHADER); + try vs.setSourceAndCompile(vs_source); + defer vs.destroy(); - const fs = c.glCreateShader(c.GL_FRAGMENT_SHADER); - c.glShaderSource( - fs, - 1, - &@ptrCast([*c]const u8, fs_source), - null, - ); - c.glCompileShader(fs); - c.glGetShaderiv(fs, c.GL_COMPILE_STATUS, &success); - if (success != c.GL_TRUE) { - var msg: [512]u8 = undefined; - c.glGetShaderInfoLog(fs, 512, null, &msg); - std.log.err("FS fail: {s}\n", .{std.mem.sliceTo(&msg, 0)}); - return; - } + const fs = try gl.Shader.create(gl.c.GL_FRAGMENT_SHADER); + try fs.setSourceAndCompile(fs_source); + defer fs.destroy(); // Shader program const program = c.glCreateProgram(); - c.glAttachShader(program, vs); - c.glAttachShader(program, fs); + c.glAttachShader(program, vs.handle); + c.glAttachShader(program, fs.handle); c.glLinkProgram(program); + var success: c_int = undefined; c.glGetProgramiv(program, c.GL_LINK_STATUS, &success); if (success != c.GL_TRUE) { var msg: [512]u8 = undefined; @@ -73,8 +48,8 @@ pub fn main() !void { std.log.err("program fail: {s}\n", .{std.mem.sliceTo(&msg, 0)}); return; } - c.glDeleteShader(vs); - c.glDeleteShader(fs); + c.glDeleteShader(vs.handle); + c.glDeleteShader(fs.handle); // Create our bufer or vertices const vertices = [_]f32{ diff --git a/src/opengl.zig b/src/opengl.zig new file mode 100644 index 000000000..15ac6d78f --- /dev/null +++ b/src/opengl.zig @@ -0,0 +1,85 @@ +const std = @import("std"); +const assert = std.debug.assert; +const log = std.log.scoped(.opengl); + +/// This can be used to access the OpenGL headers. +pub const c = @import("c.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 (c.glGetError()) { + 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. +fn mustError() Error!void { + try getError(); + return Error.Unknown; +} + +pub const Shader = struct { + handle: c.GLuint, + + pub fn create(typ: c.GLenum) Error!Shader { + const handle = c.glCreateShader(typ); + if (handle == 0) { + try mustError(); + unreachable; + } + + log.debug("shader created id={}", .{handle}); + return Shader{ .handle = handle }; + } + + /// Set the source and compile a shader. + pub fn setSourceAndCompile(s: Shader, source: [:0]const u8) !void { + c.glShaderSource(s.handle, 1, &@ptrCast([*c]const u8, source), null); + c.glCompileShader(s.handle); + + // Check if compilation succeeded + var success: c_int = undefined; + c.glGetShaderiv(s.handle, c.GL_COMPILE_STATUS, &success); + if (success == c.GL_TRUE) return; + log.err("shader compilation failure handle={} message={s}", .{ + s.handle, + 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 fn getInfoLog(s: Shader) [512]u8 { + var msg: [512]u8 = undefined; + c.glGetShaderInfoLog(s.handle, msg.len, null, &msg); + return msg; + } + + pub fn destroy(s: Shader) void { + assert(s.handle != 0); + c.glDeleteShader(s.handle); + log.debug("shader destroyed id={}", .{s.handle}); + } +};