const std = @import("std"); const c = @import("c.zig").c; const debug_gl = @import("debug_gl.zig"); const cfg = @import("config.zig"); const out = @import("output.zig"); const gl = @import("gl.zig"); const ctrl = @import("control.zig"); var window: *c.GLFWwindow = undefined; fn errorCallback(err: c_int, description: [*c]const u8) callconv(.c) void { std.debug.panic("Error {}: {s}\n", .{ err, description }); } pub fn main() !void { const progress = std.Progress.start(.{}); defer progress.end(); var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); const cfg_allocator = arena.allocator(); defer arena.deinit(); var config = try cfg.Config.init(cfg_allocator); _ = c.glfwSetErrorCallback(errorCallback); if (c.glfwInit() == c.GL_FALSE) { std.debug.panic("GLFW init failure\n", .{}); } defer c.glfwTerminate(); var monitor_count: c_int = 0; const monitors = c.glfwGetMonitors(&monitor_count); for (monitors[0..@as(usize, @intCast(monitor_count))], 0..) |monitor, i| { std.debug.print( "monitor {}: '{s}'\n", .{ i, @as([*:0]const u8, @ptrCast(c.glfwGetMonitorName(monitor))) }, ); } c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MAJOR, 4); 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, 0); c.glfwWindowHint(c.GLFW_VISIBLE, c.GLFW_FALSE); window = c.glfwCreateWindow(config.width, config.height, "glsl-view", null, null) orelse { std.debug.panic("unable to create window\n", .{}); }; defer c.glfwDestroyWindow(window); c.glfwMakeContextCurrent(window); c.glfwSwapInterval(1); var constants = try gl.Constants.create(window, &config); defer constants.destroy(); var outputs = try std.ArrayList(*out.Output).initCapacity(cfg_allocator, config.outputs.len); defer outputs.deinit(cfg_allocator); for (config.outputs) |*output_config| { outputs.appendAssumeCapacity(try out.Output.create(cfg_allocator, output_config, &constants)); } defer for (outputs.items) |output| { output.destroy(cfg_allocator); }; c.glfwMakeContextCurrent(window); c.glClearColor(0.0, 0.0, 0.0, 1.0); debug_gl.assertNoError(); debug_gl.init(); const start_time = c.glfwGetTime(); var prev_time = start_time; constants.normalized_quad.bind(0); var main_program = try gl.ShaderProgram.create( \\#version 330 core \\ \\layout(location = 0) in vec2 position; \\out vec2 uv; \\ \\void main() { \\ uv = position / 2.0 + 0.5; \\ gl_Position = vec4(position, 0, 1); \\} , \\#version 330 core \\ \\in vec2 uv; \\out vec4 color; \\ \\void main() { \\ vec2 check = floor(uv*10); \\ color = vec4(vec3(mod(check.x+check.y, 2.0)), 1); \\} ); defer main_program.destroy(); var fbo = try gl.FramebufferObject.create(config.width, config.height); defer fbo.destroy(); // const shader_file = try std.fs.cwd().openFile(config.fragment, .{}); // var last_stat = try shader_file.stat(); // try main_program.loadFile(config.fragment, last_stat.size); var debug_allocator = std.heap.DebugAllocator(.{}).init; defer _ = debug_allocator.deinit(); const allocator = debug_allocator.allocator(); // std.heap.c_allocator var cache = gl.UniformCache.init(allocator, &main_program); defer cache.deinit(); const control = try ctrl.ControlServer.init(cfg_allocator, progress, &cache, &config, &constants); defer control.deinit(); var loops: u8 = 0; while (c.glfwWindowShouldClose(window) == c.GL_FALSE) { c.glfwMakeContextCurrent(window); const now_time = c.glfwGetTime(); prev_time = now_time; control.update(); // const stat = try shader_file.stat(); // if (stat.mtime > last_stat.mtime or control.reload_requested) { // try main_program.loadFile(config.fragment, stat.size); // try cache.refresh(); // last_stat = stat; // // control.reload_requested = false; // } const is_fresh = if (control.dirty) blk: { c.glFinish(); var timer = try std.time.Timer.start(); { fbo.bind(); defer fbo.unbind(); c.glClear(c.GL_COLOR_BUFFER_BIT); main_program.bind(); constants.normalized_quad.draw(); } c.glFinish(); const fps = timer.read(); // ns loops = (loops + 1) % 64; if (loops == 0) { std.debug.print("\rrendered in {d:.2}ms\n", .{@as(f64, @floatFromInt(fps)) / std.time.ns_per_ms}); } control.dirty = false; break :blk true; } else false; var keep_running = false; var i: usize = outputs.items.len; while (i > 0) { i -= 1; const output = outputs.items[i]; var close_output = false; if (output.update(fbo, is_fresh)) |res| { if (res) |close| { if (close) { close_output = true; } else { keep_running = true; } } } else |err| { std.debug.print("Output {} closing due to error: {}\n", .{ output, err }); close_output = true; } if (close_output) { const removed = outputs.swapRemove(i); removed.destroy(cfg_allocator); } } if (!keep_running) break; c.glfwMakeContextCurrent(window); c.glfwSwapBuffers(window); c.glfwPollEvents(); } }