diff --git a/src/App.zig b/src/App.zig index ae5db5b..a2b0349 100644 --- a/src/App.zig +++ b/src/App.zig @@ -5,24 +5,18 @@ const Bus = @import("nu/Bus.zig"); const Render = @import("nu/Render.zig"); const ImGui = @import("nu/ImGui.zig"); -pub fn module() nu.Module { - return nu.Module{ - .name = "App", - // .dependencies = &.{ Render.module(), ImGui.module() }, - .setup = setup, - .teardown = teardown, - .update = update, - }; -} +pub const depends = .{ ImGui, Render }; -pub fn setup(_: std.mem.Allocator) !void { - try ImGui.gui.register(gui); -} +pub fn setup(_: std.mem.Allocator) !void {} pub fn teardown() void {} pub fn update() !void {} -pub fn gui() void { +pub fn frame() !void { ImGui.igShowMetricsWindow(null); } + +// pub fn gui() void { +// ImGui.igShowMetricsWindow(null); +// } diff --git a/src/main.zig b/src/main.zig index 6c1751c..92139b5 100644 --- a/src/main.zig +++ b/src/main.zig @@ -22,8 +22,8 @@ pub const nu_options: nu.Options = .{ // component of the larger structure. pub const nu_driver = nu.Window; +pub const nu_present = nu.Render; pub const nu_modules = .{ - nu.Render, nu.ImGui, App, }; diff --git a/src/nu.zig b/src/nu.zig index 09a2020..d0df829 100644 --- a/src/nu.zig +++ b/src/nu.zig @@ -15,73 +15,90 @@ const Config = struct { pub const config: Config = if (@hasDecl(root, "nu_config")) root.nu_config else .{}; -pub const Module = struct { - // todo dependencies - name: []const u8, - setup: *const fn (alloc: std.mem.Allocator) anyerror!void, - teardown: *const fn () void, - update: ?*const fn () anyerror!void = null, - frame: ?*const fn () anyerror!void = null, - dependencies: []const Module = &.{}, -}; - -pub const Driver = struct { - module: Module, - next: *const fn () bool, // events? callbacks? -}; - -fn enable(modules: *std.StringArrayHashMap(Module), module: Module) void { - // this doesn't handle dependencies correctly. need to do real topological sort. - - if (modules.contains(module.name)) return; - for (module.dependencies) |dep| enable(modules, dep); - modules.putNoClobber(module.name, module) catch @panic("OOM"); -} - pub fn main() void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; const alloc = gpa.allocator(); - var modules = std.StringArrayHashMap(Module).init(alloc); - defer modules.deinit(); + const engine = Engine(root.nu_driver, root.nu_present, root.nu_modules); - const driver = root.nu_driver.driver(); - enable(&modules, driver.module); - inline for (root.nu_modules) |mod| { - enable(&modules, mod.module()); - } + engine.try_invoke("setup", .{alloc}) catch @panic("Startup failed"); + defer engine.rinvoke("teardown", .{}); - for (modules.values()) |mod| { - std.log.debug("{s}.setup", .{mod.name}); - mod.setup(alloc) catch |err| { - std.debug.panic("Module {s} setup error: {!}", .{ mod.name, err }); - }; - } - defer { - var rev = std.mem.reverseIterator(modules.values()); - while (rev.next()) |mod| { - std.log.debug("{s}.teardown", .{mod.name}); - mod.teardown(); - } - } + while (engine.next()) { + engine.try_invoke("fixed", .{}) catch @panic("fixed failed!"); + engine.try_invoke("frame", .{}) catch @panic("frame failed!"); - std.log.info("Loaded modules: {s}", .{modules.keys()}); - - while (driver.next()) { - for (modules.values()) |mod| { - if (mod.update) |update| { - update() catch |err| { - std.debug.panic("Module {s} update event error: {!}", .{ mod.name, err }); - }; - } - } - - for (modules.values()) |mod| { - if (mod.frame) |frame| { - frame() catch |err| { - std.debug.panic("Module {s} frame event error: {!}", .{ mod.name, err }); - }; - } - } + engine.render() catch @panic("render failed!"); } } + +pub fn Engine(comptime D: type, comptime R: type, comptime M: anytype) type { + return struct { + pub const modules = Graph.sort(.{ D, R } ++ M); + + pub fn next() bool { + return D.next(); + } + + pub fn render() !void { + return R.render(@This()); + } + + pub fn try_invoke(comptime name: []const u8, args: anytype) !void { + inline for (modules) |mod| { + if (@hasDecl(mod, name)) { + try @call(.auto, @field(mod, name), args); + } + } + } + + pub fn invoke(comptime name: []const u8, args: anytype) void { + inline for (modules) |mod| { + if (@hasDecl(mod, name)) { + @call(.auto, @field(mod, name), args); + } + } + } + + pub fn try_rinvoke(comptime name: []const u8, args: anytype) !void { + comptime var it = std.mem.reverseIterator(modules); + inline while (it.next()) |mod| { + if (@hasDecl(mod, name)) { + try @call(.auto, @field(mod, name), args); + } + } + } + + pub fn rinvoke(comptime name: []const u8, args: anytype) void { + comptime var it = std.mem.reverseIterator(modules); + inline while (it.next()) |mod| { + if (@hasDecl(mod, name)) { + @call(.auto, @field(mod, name), args); + } + } + } + }; +} + +const Graph = struct { + fn visit( + comptime mod: type, + comptime mark: *[]const type, + comptime result: *[]const type, + ) void { + if (std.mem.indexOfScalar(type, result.*, mod)) |_| return; + if (std.mem.indexOfScalar(type, mark.*, mod)) |_| @compileError("Cycle"); + mark.* = mark.* ++ .{mod}; + if (@hasDecl(mod, "depends")) + for (mod.depends) |dep| + visit(dep, mark, result); + result.* = result.* ++ .{mod}; + } + + fn sort(comptime modules: anytype) []const type { + var mark: []const type = &.{}; + var result: []const type = &.{}; + for (modules) |mod| visit(mod, &mark, &result); + return result; + } +}; diff --git a/src/nu/ImGui.zig b/src/nu/ImGui.zig index d96a0de..a65b513 100644 --- a/src/nu/ImGui.zig +++ b/src/nu/ImGui.zig @@ -16,29 +16,17 @@ pub usingnamespace im; pub const Config = struct {}; const config = nu.config.imgui; -pub fn module() nu.Module { - return nu.Module{ - .name = "ImGui", - // .dependencies = &.{Render.module()}, - .setup = setup, - .teardown = teardown, - .update = update, - }; -} +pub const depends = .{ Render, Window }; pub fn loader_wrapper(procname: [*c]const u8, _: ?*anyopaque) callconv(.C) vk.PfnVoidFunction { return au.glfwGetInstanceProcAddress(au.I.handle, procname); } -pub var gui: Hook(fn () void) = undefined; var ctx: *im.ImGuiContext = undefined; var descriptor_pool: vk.DescriptorPool = undefined; -pub fn setup(alloc: std.mem.Allocator) !void { - gui = @TypeOf(gui).init(alloc); - errdefer gui.deinit(); - - try Render.present.register(present); +pub fn setup(_: std.mem.Allocator) !void { + // try Render.present.register(present); ctx = im.igCreateContext(null) orelse { return error.igCreateContextFailed; @@ -104,21 +92,18 @@ pub fn teardown() void { au.D.destroyDescriptorPool(descriptor_pool, null); im.impl.ImGui_ImplGlfw_Shutdown(); im.igDestroyContext(ctx); - gui.deinit(); } -pub fn update() !void { +pub fn frame() !void { im.impl.ImGui_ImplGlfw_NewFrame(); im.impl.ImGui_ImplVulkan_NewFrame(); im.igNewFrame(); - - gui.invoke(.{}); - - im.igEndFrame(); - im.igRender(); } pub fn present(cmd: au.CommandBufferProxy) void { + im.igEndFrame(); + im.igRender(); + im.impl.ImGui_ImplVulkan_RenderDrawData( @ptrCast(im.igGetDrawData()), @ptrFromInt(@intFromEnum(cmd.handle)), diff --git a/src/nu/Render.zig b/src/nu/Render.zig index 3e4ddac..3a6694f 100644 --- a/src/nu/Render.zig +++ b/src/nu/Render.zig @@ -32,21 +32,11 @@ pub const Config = struct { }; const config = nu.config.render; -pub fn module() nu.Module { - return nu.Module{ - .name = "Render", - .setup = setup, - .teardown = teardown, - .frame = frame, - .dependencies = &.{nu.Window.driver().module}, - }; -} +pub const depends = .{nu.Window}; var sc: au.SwapChain = undefined; var flights: au.Flights = undefined; -pub var present: nu.Hook(fn (au.CommandBufferProxy) void) = undefined; - pub fn setup(alloc: std.mem.Allocator) !void { // todo pick apart au into helpers; not a sub-module filled with its own globals. try au.init(alloc); @@ -57,20 +47,16 @@ pub fn setup(alloc: std.mem.Allocator) !void { flights = try au.Flights.init(alloc, config.frames_in_flight); errdefer flights.deinit(); - - present = @TypeOf(present).init(alloc); - errdefer present.deinit(); } pub fn teardown() void { au.D.deviceWaitIdle() catch |err| std.debug.panic("Device wait failed: {!}", .{err}); - errdefer present.deinit(); flights.deinit(); sc.deinit(); au.deinit(); } -pub fn frame() !void { +pub fn render(engine: anytype) !void { const flight: au.Flights.Flight = flights.next(); try flight.wait(); @@ -97,9 +83,8 @@ pub fn frame() !void { target.begin_rendering(cmd, render_area); // todo manage frame in flight state for each hook; pass the current flight in as context. - // will need some comptime -> anytype mapping. - present.invoke(.{cmd}); + engine.invoke("present", .{cmd}); target.end_rendering(cmd); try cmd.endCommandBuffer();