diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2020-01-22 16:11:33 +0000 |
|---|---|---|
| committer | s-ol <s-ol@users.noreply.github.com> | 2020-01-22 16:11:33 +0000 |
| commit | 25e9c84b55593da4950b2c9ccac818c606a5321f (patch) | |
| tree | 27f949b82aeeb725025d9099cf7a14767b4a0135 | |
| download | glsl-view-25e9c84b55593da4950b2c9ccac818c606a5321f.tar.gz glsl-view-25e9c84b55593da4950b2c9ccac818c606a5321f.zip | |
initial commit
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | build.zig | 18 | ||||
| -rw-r--r-- | config.yaml | 13 | ||||
| -rw-r--r-- | src/c.zig | 5 | ||||
| -rw-r--r-- | src/config.zig | 339 | ||||
| -rw-r--r-- | src/debug_gl.zig | 16 | ||||
| -rw-r--r-- | src/main.zig | 83 | ||||
| -rw-r--r-- | src/output.zig | 76 | ||||
| -rw-r--r-- | src/shader.zig | 118 |
9 files changed, 669 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2040c29 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +zig-cache diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..33a955c --- /dev/null +++ b/build.zig @@ -0,0 +1,18 @@ +const Builder = @import("std").build.Builder; + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + const exe = b.addExecutable("t", "src/main.zig"); + exe.setBuildMode(mode); + exe.linkSystemLibrary("c"); + exe.linkSystemLibrary("yaml"); + exe.linkSystemLibrary("glfw"); + exe.linkSystemLibrary("epoxy"); + exe.install(); + + const run_cmd = exe.run(); + run_cmd.step.dependOn(b.getInstallStep()); + + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); +} diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..fcac769 --- /dev/null +++ b/config.yaml @@ -0,0 +1,13 @@ +fragment: swag.frag +width: 480 +height: 270 +outputs: + - type: window + width: 600 + height: 400 + fullscreen: false +parameters: + - name: camera + type: mat4 + - name: bg + type: vec4:rgba diff --git a/src/c.zig b/src/c.zig new file mode 100644 index 0000000..7a13fde --- /dev/null +++ b/src/c.zig @@ -0,0 +1,5 @@ +pub usingnamespace @cImport({ + @cInclude("epoxy/gl.h"); + @cInclude("GLFW/glfw3.h"); + @cInclude("yaml.h"); +}); diff --git a/src/config.zig b/src/config.zig new file mode 100644 index 0000000..8d296dc --- /dev/null +++ b/src/config.zig @@ -0,0 +1,339 @@ +const c = @import("c.zig"); +const fs = @import("std").fs; +const std = @import("std"); +const debug = @import("std").debug; + +pub const OutputType = enum { + window, +}; + +pub const OutputConfig = struct { + type: OutputType, + width: i32, + height: i32, + fullscreen: bool, // only for `Window` +}; + +pub const ParameterConfig = struct { + name: []const u8, + type: []const u8, +}; + +const defaultOutput = OutputConfig{ + .type = OutputType.window, + .width = 800, + .height = 600, + .fullscreen = false, +}; + +pub const Config = struct { + width: i32, + height: i32, + outputs: []const OutputConfig, + fragment: []const u8, + parameters: []const ParameterConfig, + arena: std.heap.ArenaAllocator, + + pub fn parse(filename: []const u8) !Config { + var parser: c.yaml_parser_t = undefined; + _ = c.yaml_parser_initialize(&parser); + defer c.yaml_parser_delete(&parser); + + const file = try fs.cwd().openFile(filename, .{}); + // c.yaml_parser_set_input_file(&parser, @as([*c]c.FILE, file.handle)); + var buffer: [1024]u8 = undefined; + const len: usize = try file.read(buffer[0..]); + c.yaml_parser_set_input_string(&parser, &buffer[0], len); + + var config: Config = .{ + .width = 1920, + .height = 1080, + .outputs = ([_]OutputConfig{defaultOutput})[0..], + .fragment = "sphere.frag", + .parameters = ([0]ParameterConfig{})[0..], + .arena = std.heap.ArenaAllocator.init(std.heap.page_allocator), + }; + + try expectEvent(&parser, c.yaml_event_type_t.YAML_STREAM_START_EVENT); + try expectEvent(&parser, c.yaml_event_type_t.YAML_DOCUMENT_START_EVENT); + try expectEvent(&parser, c.yaml_event_type_t.YAML_MAPPING_START_EVENT); + + while (true) { + var event: c.yaml_event_t = undefined; + if (c.yaml_parser_parse(&parser, &event) != 1) + return error.YAMLParserError; + defer c.yaml_event_delete(&event); + + switch (event.type) { + c.yaml_event_type_t.YAML_MAPPING_END_EVENT => break, + c.yaml_event_type_t.YAML_SCALAR_EVENT => { + const data = event.data.scalar; + const key: []const u8 = data.value[0..data.length]; + + if (std.mem.eql(u8, key, "width")) { + config.width = try parseInt(&parser, i32); + } else if (std.mem.eql(u8, key, "height")) { + config.height = try parseInt(&parser, i32); + } else if (std.mem.eql(u8, key, "fragment")) { + config.fragment = try parseString(&parser, &config.arena.allocator); + } else if (std.mem.eql(u8, key, "outputs")) { + config.outputs = try parseOutputs(&parser, &config.arena.allocator); + } else if (std.mem.eql(u8, key, "parameters")) { + config.parameters = try parseParameters(&parser, &config.arena.allocator); + } else { + debug.warn("unknown key: '{}'\n", .{key}); + try skipAny(&parser); + } + }, + else => { + debug.warn("unexpected event: {}\n", .{event.type}); + return error.InvalidConfiguration; + }, + } + } + + try expectEvent(&parser, c.yaml_event_type_t.YAML_DOCUMENT_END_EVENT); + try expectEvent(&parser, c.yaml_event_type_t.YAML_STREAM_END_EVENT); + + return config; + } +}; + +fn expectEvent(parser: *c.yaml_parser_t, expectedType: c.yaml_event_type_t) !void { + var event: c.yaml_event_t = undefined; + + if (c.yaml_parser_parse(parser, &event) != 1) + return error.YAMLParserError; + defer c.yaml_event_delete(&event); + + if (event.type != expectedType) { + debug.warn("unexpected event: {}\n", .{event.type}); + return error.InvalidConfiguration; + } +} + +fn parseString(parser: *c.yaml_parser_t, allocator: *std.mem.Allocator) ![]const u8 { + var event: c.yaml_event_t = undefined; + if (c.yaml_parser_parse(parser, &event) != 1) + return error.YAMLParserError; + defer c.yaml_event_delete(&event); + + if (event.type != c.yaml_event_type_t.YAML_SCALAR_EVENT) + return error.InvalidType; + + const data = event.data.scalar; + const value: []const u8 = data.value[0..data.length]; + return try std.mem.dupe(allocator, u8, value); +} + +fn parseInt(parser: *c.yaml_parser_t, comptime T: type) !T { + var event: c.yaml_event_t = undefined; + if (c.yaml_parser_parse(parser, &event) != 1) + return error.YAMLParserError; + defer c.yaml_event_delete(&event); + + if (event.type != c.yaml_event_type_t.YAML_SCALAR_EVENT) + return error.InvalidType; + + const data = event.data.scalar; + const value: []const u8 = data.value[0..data.length]; + return try std.fmt.parseInt(T, value, 10); +} + +fn parseEnum(parser: *c.yaml_parser_t, comptime T: type) !T { + var event: c.yaml_event_t = undefined; + if (c.yaml_parser_parse(parser, &event) != 1) + return error.YAMLParserError; + defer c.yaml_event_delete(&event); + + if (event.type != c.yaml_event_type_t.YAML_SCALAR_EVENT) + return error.InvalidType; + + const data = event.data.scalar; + const value: []const u8 = data.value[0..data.length]; + + inline for (@typeInfo(T).Enum.fields) |field| { + if (std.mem.eql(u8, value, field.name)) + return @field(T, field.name); + } + + return error.InvalidValue; +} + +fn parseBool(parser: *c.yaml_parser_t) !bool { + var event: c.yaml_event_t = undefined; + if (c.yaml_parser_parse(parser, &event) != 1) + return error.YAMLParserError; + defer c.yaml_event_delete(&event); + + if (event.type != c.yaml_event_type_t.YAML_SCALAR_EVENT) + return error.InvalidType; + + const data = event.data.scalar; + const value: []const u8 = data.value[0..data.length]; + + if (data.style != c.yaml_scalar_style_t.YAML_PLAIN_SCALAR_STYLE) + return false; + return std.mem.eql(u8, value, "true") or std.mem.eql(u8, value, "1"); +} + +fn parseOutputs(parser: *c.yaml_parser_t, allocator: *std.mem.Allocator) ![]OutputConfig { + try expectEvent(parser, c.yaml_event_type_t.YAML_SEQUENCE_START_EVENT); + + var outputs = try allocator.alloc(OutputConfig, 1024); + var output_count: usize = 0; + + while (true) { + var seqEvent: c.yaml_event_t = undefined; + if (c.yaml_parser_parse(parser, &seqEvent) != 1) + return error.YAMLParserError; + defer c.yaml_event_delete(&seqEvent); + + switch (seqEvent.type) { + c.yaml_event_type_t.YAML_SEQUENCE_END_EVENT => break, + c.yaml_event_type_t.YAML_MAPPING_START_EVENT => { + var output = &outputs[output_count]; + output.* = defaultOutput; + + while (true) { + var event: c.yaml_event_t = undefined; + if (c.yaml_parser_parse(parser, &event) != 1) + return error.YAMLParserError; + defer c.yaml_event_delete(&event); + + switch (event.type) { + c.yaml_event_type_t.YAML_MAPPING_END_EVENT => break, + c.yaml_event_type_t.YAML_SCALAR_EVENT => { + const data = event.data.scalar; + const key: []const u8 = data.value[0..data.length]; + + if (std.mem.eql(u8, key, "type")) { + output.*.type = try parseEnum(parser, OutputType); + } else if (std.mem.eql(u8, key, "width")) { + output.*.width = try parseInt(parser, i32); + } else if (std.mem.eql(u8, key, "height")) { + output.*.height = try parseInt(parser, i32); + } else if (std.mem.eql(u8, key, "fullscreen")) { + output.*.fullscreen = try parseBool(parser); + } else { + debug.warn("unknown key: '{}'\n", .{key}); + try skipAny(parser); + } + }, + else => { + debug.warn("unexpected event: {}\n", .{event.type}); + return error.InvalidConfiguration; + }, + } + } + + output_count += 1; + }, + else => { + debug.warn("unexpected event: {}\n", .{seqEvent.type}); + return error.InvalidConfiguration; + }, + } + } + + _ = allocator.realloc(outputs, output_count) catch 0; + return outputs[0..output_count]; +} + +fn parseParameters(parser: *c.yaml_parser_t, allocator: *std.mem.Allocator) ![]ParameterConfig { + try expectEvent(parser, c.yaml_event_type_t.YAML_SEQUENCE_START_EVENT); + + var parameters = try allocator.alloc(ParameterConfig, 1024); + var param_count: usize = 0; + + while (true) { + var seqEvent: c.yaml_event_t = undefined; + if (c.yaml_parser_parse(parser, &seqEvent) != 1) + return error.YAMLParserError; + defer c.yaml_event_delete(&seqEvent); + + switch (seqEvent.type) { + c.yaml_event_type_t.YAML_SEQUENCE_END_EVENT => break, + c.yaml_event_type_t.YAML_MAPPING_START_EVENT => { + var param = ¶meters[param_count]; + var have_name = false; + var have_type = false; + + while (true) { + var event: c.yaml_event_t = undefined; + if (c.yaml_parser_parse(parser, &event) != 1) + return error.YAMLParserError; + defer c.yaml_event_delete(&event); + + switch (event.type) { + c.yaml_event_type_t.YAML_MAPPING_END_EVENT => break, + c.yaml_event_type_t.YAML_SCALAR_EVENT => { + const data = event.data.scalar; + const key: []const u8 = data.value[0..data.length]; + + if (std.mem.eql(u8, key, "name")) { + param.*.name = try parseString(parser, allocator); + have_name = true; + } else if (std.mem.eql(u8, key, "type")) { + param.*.type = try parseString(parser, allocator); + have_type = true; + } else { + debug.warn("unknown key: '{}'\n", .{key}); + try skipAny(parser); + } + }, + else => { + debug.warn("unexpected event: {}\n", .{event.type}); + return error.InvalidConfiguration; + }, + } + } + + if (!(have_name and have_type)) { + debug.warn("name and type are mandatory for parameters.\n", .{}); + return error.InvalidConfiguration; + } + + param_count += 1; + }, + else => { + debug.warn("unexpected event: {}\n", .{seqEvent.type}); + return error.InvalidConfiguration; + }, + } + } + + _ = allocator.realloc(parameters, param_count) catch 0; + return parameters[0..param_count]; +} + +fn skipAny(parser: *c.yaml_parser_t) !void { + var event: c.yaml_event_t = undefined; + var depth: u32 = 0; + while (true) { + if (c.yaml_parser_parse(parser, &event) != 1) + return error.YAMLParserError; + defer c.yaml_event_delete(&event); + + switch (event.type) { + c.yaml_event_type_t.YAML_SCALAR_EVENT => { + if (depth == 0) + break; + }, + + c.yaml_event_type_t.YAML_SEQUENCE_START_EVENT, c.yaml_event_type_t.YAML_MAPPING_START_EVENT => depth += 1, + + c.yaml_event_type_t.YAML_SEQUENCE_END_EVENT, c.yaml_event_type_t.YAML_MAPPING_END_EVENT => { + depth -= 1; + + if (depth == 0) + break; + }, + + else => { + debug.warn("unexpected event: {}\n", .{event.type}); + return error.InvalidConfiguration; + }, + } + } +} diff --git a/src/debug_gl.zig b/src/debug_gl.zig new file mode 100644 index 0000000..2fdcda0 --- /dev/null +++ b/src/debug_gl.zig @@ -0,0 +1,16 @@ +const c = @import("c.zig"); +const std = @import("std"); +const os = std.os; +const panic = std.debug.panic; +const builtin = @import("builtin"); + +pub const is_on = if (builtin.mode == builtin.Mode.ReleaseFast) c.GL_FALSE else c.GL_TRUE; + +pub fn assertNoError() void { + if (builtin.mode != builtin.Mode.ReleaseFast) { + const err = c.glGetError(); + if (err != c.GL_NO_ERROR) { + panic("GL error: {}\n", .{err}); + } + } +} diff --git a/src/main.zig b/src/main.zig new file mode 100644 index 0000000..ce31e75 --- /dev/null +++ b/src/main.zig @@ -0,0 +1,83 @@ +const std = @import("std"); +const panic = std.debug.panic; +const c = @import("c.zig"); +const debug_gl = @import("debug_gl.zig"); +const cfg = @import("config.zig"); +const out = @import("output.zig"); + +var window: *c.GLFWwindow = undefined; + +fn errorCallback(err: c_int, description: [*c]const u8) callconv(.C) void { + panic("Error: {}\n", .{description}); +} + +pub fn main() !void { + var config = try cfg.Config.parse("config.yaml"); + defer config.arena.deinit(); + + _ = c.glfwSetErrorCallback(errorCallback); + + if (c.glfwInit() == c.GL_FALSE) { + panic("GLFW init failure\n", .{}); + } + defer c.glfwTerminate(); + + c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MAJOR, 3); + c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MINOR, 2); + c.glfwWindowHint(c.GLFW_OPENGL_FORWARD_COMPAT, c.GL_TRUE); + c.glfwWindowHint(c.GLFW_OPENGL_DEBUG_CONTEXT, debug_gl.is_on); + c.glfwWindowHint(c.GLFW_OPENGL_PROFILE, c.GLFW_OPENGL_CORE_PROFILE); + c.glfwWindowHint(c.GLFW_DEPTH_BITS, 0); + c.glfwWindowHint(c.GLFW_STENCIL_BITS, 8); + c.glfwWindowHint(c.GLFW_VISIBLE, c.GLFW_FALSE); + + window = c.glfwCreateWindow(config.width, config.height, "glsl-view", null, null) orelse { + panic("unable to create window\n", .{}); + }; + defer c.glfwDestroyWindow(window); + + c.glfwMakeContextCurrent(window); + c.glfwSwapInterval(1); + + c.glClearColor(0.0, 0.0, 0.0, 1.0); + + // c.glEnable(c.GL_BLEND); + // c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA); + // c.glPixelStorei(c.GL_UNPACK_ALIGNMENT, 1); + + c.glViewport(0, 0, config.width, config.height); + + debug_gl.assertNoError(); + + var outputs = try config.arena.allocator.alloc(*out.Output, config.outputs.len); + for (config.outputs) |output_config, i| { + outputs[i] = out.Output.create(&config.arena.allocator, output_config, window); + } + defer for (outputs) |output| { + output.destroy(output); + }; + + const start_time = c.glfwGetTime(); + var prev_time = start_time; + + while (c.glfwWindowShouldClose(window) == c.GL_FALSE) { + c.glClear(c.GL_COLOR_BUFFER_BIT | c.GL_DEPTH_BUFFER_BIT | c.GL_STENCIL_BUFFER_BIT); + + const now_time = c.glfwGetTime(); + const elapsed = now_time - prev_time; + prev_time = now_time; + + for (outputs) |output| { + const close = output.update(output); + if (close) { + c.glfwSetWindowShouldClose(window, c.GL_TRUE); + } + } + + c.glfwSwapBuffers(window); + + c.glfwPollEvents(); + } + + debug_gl.assertNoError(); +} diff --git a/src/output.zig b/src/output.zig new file mode 100644 index 0000000..8303107 --- /dev/null +++ b/src/output.zig @@ -0,0 +1,76 @@ +const c = @import("c.zig"); +const std = @import("std"); +const cfg = @import("config.zig"); + +pub const Output = struct { + update: fn (self: *Output) bool, + destroy: fn (self: *Output) void, + + pub fn init(update: fn (*Output) bool, destroy: fn (*Output) void) Output { + return Output{ + .update = update, + .destroy = destroy, + }; + } + + pub fn create(allocator: *std.mem.Allocator, config: cfg.OutputConfig, share_window: *c.GLFWwindow) *Output { + return switch (config.type) { + cfg.OutputType.window => WindowOutput.create(allocator, config, share_window), + else => unreachable, + }; + } +}; + +pub const WindowOutput = struct { + output: Output, + window: *c.GLFWwindow, + + pub fn create(allocator: *std.mem.Allocator, config: cfg.OutputConfig, share_window: *c.GLFWwindow) *Output { + const self = allocator.create(WindowOutput) catch unreachable; + + c.glfwDefaultWindowHints(); + + self.* = WindowOutput{ + .output = Output.init(update, destroy), + .window = c.glfwCreateWindow(config.width, config.height, "glsl-view output", null, share_window) orelse { + std.debug.panic("unable to create output window\n", .{}); + }, + }; + + c.glfwSetWindowUserPointer(self.*.window, @ptrCast(*c_void, self)); + _ = c.glfwSetKeyCallback(self.*.window, keyCallback); + + return &self.*.output; + } + + fn update(output: *Output) bool { + const self = @fieldParentPtr(WindowOutput, "output", output); + + if (c.glfwWindowShouldClose(self.*.window) == c.GL_TRUE) + return true; + + c.glClear(c.GL_COLOR_BUFFER_BIT | c.GL_DEPTH_BUFFER_BIT | c.GL_STENCIL_BUFFER_BIT); + + c.glfwSwapBuffers(self.*.window); + return false; + } + + fn destroy(output: *Output) void { + const self = @fieldParentPtr(WindowOutput, "output", output); + + c.glfwDestroyWindow(self.*.window); + } + + extern fn keyCallback(win: ?*c.GLFWwindow, key: c_int, scancode: c_int, action: c_int, mods: c_int) callconv(.C) void { + if (action != c.GLFW_PRESS) return; + const self = @ptrCast(*WindowOutput, @alignCast(@alignOf(WindowOutput), c.glfwGetWindowUserPointer(win).?)); + + std.debug.warn("key pressed: {}\n", .{ key }); + switch (key) { + // c.GLFW_KEY_F => // toggle fullscreen + // c.GLFW_KEY_LEFT => // cycle through monitors + // c.GLFW_KEY_RIGHT => // cycle through monitors + else => {}, + } + } +}; diff --git a/src/shader.zig b/src/shader.zig new file mode 100644 index 0000000..ed36035 --- /dev/null +++ b/src/shader.zig @@ -0,0 +1,118 @@ +const std = @import("std"); +const panic = std.debug.panic +const c = @import("c.zig"); + + +pub const ShaderProgram = struct { + program_id: c.GLuint, + vertex_id: c.GLuint, + fragment_id: c.GLuint, + maybe_geometry_id: ?c.GLuint, + + pub fn bind(sp: ShaderProgram) void { + c.glUseProgram(sp.program_id); + } + + pub fn attribLocation(sp: ShaderProgram, name: [*]const u8) c.GLint { + const id = c.glGetAttribLocation(sp.program_id, name); + if (id == -1) { + panic("invalid attrib: {}\n", .{name}); + } + return id; + } + + pub fn uniformLocation(sp: ShaderProgram, name: [*]const u8) c.GLint { + const id = c.glGetUniformLocation(sp.program_id, name); + if (id == -1) { + panic("invalid uniform: {}\n", .{name}); + } + return id; + } + + pub fn setUniformInt(sp: ShaderProgram, uniform_id: c.GLint, value: c_int) void { + c.glUniform1i(uniform_id, value); + } + + pub fn setUniformFloat(sp: ShaderProgram, uniform_id: c.GLint, value: f32) void { + c.glUniform1f(uniform_id, value); + } + + pub fn setUniformVec3(sp: ShaderProgram, uniform_id: c.GLint, value: math3d.Vec3) void { + c.glUniform3fv(uniform_id, 1, value.data[0..].ptr); + } + + pub fn setUniformVec4(sp: ShaderProgram, uniform_id: c.GLint, value: Vec4) void { + c.glUniform4fv(uniform_id, 1, value.data[0..].ptr); + } + + pub fn setUniformMat4x4(sp: ShaderProgram, uniform_id: c.GLint, value: Mat4x4) void { + c.glUniformMatrix4fv(uniform_id, 1, c.GL_FALSE, value.data[0][0..].ptr); + } + + pub fn create( + vertex_source: []const u8, + frag_source: []const u8, + maybe_geometry_source: ?[]u8, + ) !ShaderProgram { + var sp: ShaderProgram = undefined; + sp.vertex_id = try initGlShader(vertex_source, "vertex", c.GL_VERTEX_SHADER); + sp.fragment_id = try initGlShader(frag_source, "fragment", c.GL_FRAGMENT_SHADER); + sp.maybe_geometry_id = if (maybe_geometry_source) |geo_source| + try initGlShader(geo_source, "geometry", c.GL_GEOMETRY_SHADER) + else + null; + + sp.program_id = c.glCreateProgram(); + c.glAttachShader(sp.program_id, sp.vertex_id); + c.glAttachShader(sp.program_id, sp.fragment_id); + if (sp.maybe_geometry_id) |geo_id| { + c.glAttachShader(sp.program_id, geo_id); + } + c.glLinkProgram(sp.program_id); + + var ok: c.GLint = undefined; + c.glGetProgramiv(sp.program_id, c.GL_LINK_STATUS, &ok); + if (ok != 0) return sp; + + var error_size: c.GLint = undefined; + c.glGetProgramiv(sp.program_id, c.GL_INFO_LOG_LENGTH, &error_size); + const message = try c_allocator.alloc(u8, @intCast(usize, error_size)); + c.glGetProgramInfoLog(sp.program_id, error_size, &error_size, message.ptr); + panic("Error linking shader program: {}\n", .{message.ptr}); + } + + pub fn destroy(sp: *ShaderProgram) void { + if (sp.maybe_geometry_id) |geo_id| { + c.glDetachShader(sp.program_id, geo_id); + } + c.glDetachShader(sp.program_id, sp.fragment_id); + c.glDetachShader(sp.program_id, sp.vertex_id); + + if (sp.maybe_geometry_id) |geo_id| { + c.glDeleteShader(geo_id); + } + c.glDeleteShader(sp.fragment_id); + c.glDeleteShader(sp.vertex_id); + + c.glDeleteProgram(sp.program_id); + } +}; + +fn initGlShader(source: []const u8, name: [*]const u8, kind: c.GLenum) !c.GLuint { + const shader_id = c.glCreateShader(kind); + const source_ptr: ?[*]const u8 = source.ptr; + const source_len = @intCast(c.GLint, source.len); + c.glShaderSource(shader_id, 1, &source_ptr, &source_len); + c.glCompileShader(shader_id); + + var ok: c.GLint = undefined; + c.glGetShaderiv(shader_id, c.GL_COMPILE_STATUS, &ok); + if (ok != 0) return shader_id; + + var error_size: c.GLint = undefined; + c.glGetShaderiv(shader_id, c.GL_INFO_LOG_LENGTH, &error_size); + + const message = try c_allocator.alloc(u8, @intCast(usize, error_size)); + c.glGetShaderInfoLog(shader_id, error_size, &error_size, message.ptr); + panic("Error compiling {} shader:\n{}\n", .{ name, message.ptr }); +} |
