From fd1bd9dbf59ea427e508150076f4b372d5c92433 Mon Sep 17 00:00:00 2001 From: David Allemang Date: Wed, 10 Jul 2024 13:06:44 -0400 Subject: [PATCH] runtime hooks --- src/App.zig | 6 ++- src/main.zig | 7 +++- src/nu.zig | 98 +++++++++++++++++++++++++++++++++-------------- src/nu/ImGui.zig | 37 +++++++++++++----- src/nu/Render.zig | 39 ++++++++++++++----- src/nu/Window.zig | 2 +- 6 files changed, 138 insertions(+), 51 deletions(-) diff --git a/src/App.zig b/src/App.zig index cf4d0b7..855902f 100644 --- a/src/App.zig +++ b/src/App.zig @@ -9,9 +9,13 @@ pub fn init(alloc: std.mem.Allocator) !void { _ = alloc; } +pub fn connect() !void { + try ImGui.hooks.frame.register(nu_imgui_frame); +} + pub fn deinit() void {} -pub fn nu_frame() void {} +// pub fn nu_frame() void {} // pub fn nu_events(events: []const Bus.Event) void { // std.debug.print("{any}\n", .{events}); diff --git a/src/main.zig b/src/main.zig index b8848d5..6f9602d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -33,17 +33,20 @@ pub fn main() !void { defer _ = gpa.detectLeaks(); const alloc = gpa.allocator(); + nu.init(alloc); + defer nu.deinit(); + try nu.Window.init(alloc); defer nu.Window.deinit(); try nu.Render.init(alloc); defer nu.Render.deinit(); - try nu.ImGui.init(); + try nu.ImGui.init(alloc); defer nu.ImGui.deinit(); try App.init(alloc); defer App.deinit(); - try nu.run(); + try nu.run(alloc); } diff --git a/src/nu.zig b/src/nu.zig index 72638e4..c1f15ee 100644 --- a/src/nu.zig +++ b/src/nu.zig @@ -5,6 +5,9 @@ pub const Window = @import("nu/Window.zig"); pub const Render = @import("nu/Render.zig"); pub const ImGui = @import("nu/ImGui.zig"); +pub const Bus = @import("nu/Bus.zig"); +pub const Hook = @import("nu/hooks.zig").Hook; + pub const Options = struct { window: Window.Options = .{}, render: Render.Options = .{}, @@ -15,44 +18,81 @@ pub const options: Options = if (@hasDecl(root, "nu_options")) root.nu_options e pub const modules = root.nu_modules; pub const driver = root.nu_driver; -pub fn run() !void { - try invoke_hook("nu_enter", .{}); +pub const Hooks = struct { + pub const Enter = Hook(fn () void); + pub const Events = Hook(fn ([]Bus.Event) void); + pub const Frame = Hook(fn () anyerror!void); + pub const Close = Hook(fn () void); + + enter: Enter, + events: Events, + frame: Frame, + close: Close, +}; + +pub var hooks: Hooks = undefined; + +pub fn init(alloc: std.mem.Allocator) void { + hooks = .{ + .enter = Hooks.Enter.init(alloc), + .events = Hooks.Events.init(alloc), + .frame = Hooks.Frame.init(alloc), + .close = Hooks.Close.init(alloc), + }; +} + +pub fn deinit() void { + hooks.enter.deinit(); + hooks.events.deinit(); + hooks.frame.deinit(); + hooks.close.deinit(); +} + +pub fn run(alloc: std.mem.Allocator) !void { + // todo hooks should execute in priority order; then hook.register accepts a "priority" argument. + inline for (modules) |module| { + try module.connect(); + } + + hooks.enter.invoke(.{}); while (driver.next()) |events| { if (events.len > 0) { - try invoke_hook("nu_events", .{events}); + hooks.events.invoke(.{events}); } // todo frame timer - try invoke_hook("nu_frame", .{}); + const frame_results = try hooks.frame.invoke(alloc, .{}); + defer alloc.free(frame_results); + for (frame_results) |result| result catch |err| return err; // todo fixed timestep } - try invoke_hook("nu_close", .{}); + hooks.close.invoke(.{}); } -// todo specify hook type. -// - special handling for error unions -// - allow per-hook state somehow declared in the handler - -pub fn invoke_hook(comptime name: []const u8, args: anytype) !void { - inline for (modules) |module| { - if (@hasDecl(module, name)) { - try invoke(@field(module, name), args); - } - } -} - -fn invoke(func: anytype, args: anytype) !void { - if (@typeInfo(@TypeOf(func)).Fn.return_type) |R| { - switch (@typeInfo(R)) { - .ErrorUnion => try @call(.auto, func, args), - .Void => @call(.auto, func, args), - else => { - @compileLog(func, @typeInfo(R)); - @compileError("Invalid hook return type. Must be void or !void."); - }, - } - } -} +// // todo specify hook type. +// // - special handling for error unions +// // - allow per-hook state somehow declared in the handler +// +// pub fn invoke_hook(comptime name: []const u8, args: anytype) !void { +// inline for (modules) |module| { +// if (@hasDecl(module, name)) { +// try invoke(@field(module, name), args); +// } +// } +// } +// +// fn invoke(func: anytype, args: anytype) !void { +// if (@typeInfo(@TypeOf(func)).Fn.return_type) |R| { +// switch (@typeInfo(R)) { +// .ErrorUnion => try @call(.auto, func, args), +// .Void => @call(.auto, func, args), +// else => { +// @compileLog(func, @typeInfo(R)); +// @compileError("Invalid hook return type. Must be void or !void."); +// }, +// } +// } +// } diff --git a/src/nu/ImGui.zig b/src/nu/ImGui.zig index ed69fb1..66ad5a3 100644 --- a/src/nu/ImGui.zig +++ b/src/nu/ImGui.zig @@ -6,6 +6,7 @@ const vk = @import("vk"); const nu = @import("../nu.zig"); const au = @import("Render/au.zig"); +const Hook = @import("hooks.zig").Hook; const Render = @import("Render.zig"); const Window = @import("Window.zig"); @@ -14,6 +15,11 @@ pub usingnamespace im; pub const Options = struct {}; +pub const Hooks = struct { + pub const Frame = Hook(fn () void); + frame: Frame, +}; + pub fn loader_wrapper(procname: [*c]const u8, _: ?*anyopaque) callconv(.C) vk.PfnVoidFunction { return au.glfwGetInstanceProcAddress(au.I.handle, procname); } @@ -21,7 +27,9 @@ pub fn loader_wrapper(procname: [*c]const u8, _: ?*anyopaque) callconv(.C) vk.Pf var ctx: *im.ImGuiContext = undefined; var descriptor_pool: vk.DescriptorPool = undefined; -pub fn init() !void { +pub var hooks: Hooks = undefined; + +pub fn init(alloc: std.mem.Allocator) !void { ctx = im.igCreateContext(null) orelse { return error.igCreateContextFailed; }; @@ -77,6 +85,24 @@ pub fn init() !void { if (!im.impl.ImGui_ImplVulkan_CreateFontsTexture()) { return error.igVulkanFontTextureFailed; } + + hooks = .{ + .frame = Hooks.Frame.init(alloc), + }; + errdefer hooks.frame.deinit(); +} + +pub fn deinit() void { + hooks.frame.deinit(); + im.impl.ImGui_ImplVulkan_Shutdown(); + au.D.destroyDescriptorPool(descriptor_pool, null); + im.impl.ImGui_ImplGlfw_Shutdown(); + im.igDestroyContext(ctx); +} + +pub fn connect() !void { + try nu.hooks.frame.register(nu_frame); + try Render.hooks.present.register(nu_render_present); } pub fn nu_frame() !void { @@ -84,7 +110,7 @@ pub fn nu_frame() !void { im.impl.ImGui_ImplVulkan_NewFrame(); im.igNewFrame(); - try nu.invoke_hook("nu_imgui_frame", .{}); + hooks.frame.invoke(.{}); im.igEndFrame(); im.igRender(); @@ -97,10 +123,3 @@ pub fn nu_render_present(cmd: au.CommandBufferProxy) void { null, ); } - -pub fn deinit() void { - im.impl.ImGui_ImplVulkan_Shutdown(); - au.D.destroyDescriptorPool(descriptor_pool, null); - im.impl.ImGui_ImplGlfw_Shutdown(); - im.igDestroyContext(ctx); -} diff --git a/src/nu/Render.zig b/src/nu/Render.zig index 0d4d843..697f8c8 100644 --- a/src/nu/Render.zig +++ b/src/nu/Render.zig @@ -9,6 +9,8 @@ const vk = @import("vk"); const nu = @import("../nu.zig"); const au = @import("Render/au.zig"); +const Hook = @import("hooks.zig").Hook; + pub const Options = struct { app_name: [*:0]const u8 = "nu-au-app", app_version: struct { @@ -27,6 +29,14 @@ pub const Options = struct { frames_in_flight: u8 = 3, }; +pub const Hooks = struct { + pub const Present = Hook(fn (au.CommandBufferProxy) void); + + present: Present, +}; + +pub var hooks: Hooks = undefined; + var sc: au.SwapChain = undefined; var flights: au.Flights = undefined; @@ -40,6 +50,21 @@ pub fn init(alloc: std.mem.Allocator) !void { flights = try au.Flights.init(alloc, nu.options.render.frames_in_flight); errdefer flights.deinit(); + + hooks = .{ .present = Hooks.Present.init(alloc) }; + errdefer hooks.present.deinit(); +} + +pub fn deinit() void { + hooks.present.deinit(); + flights.deinit(); + sc.deinit(); + au.deinit(); +} + +pub fn connect() !void { + try nu.hooks.frame.register(nu_frame); + try nu.hooks.close.register(nu_close); } pub fn nu_frame() !void { @@ -70,7 +95,7 @@ pub fn nu_frame() !void { // todo manage frame in flight state for each hook; pass the current flight in as context. // will need some comptime -> anytype mapping. - try nu.invoke_hook("nu_render_present", .{cmd}); + hooks.present.invoke(.{cmd}); target.end_rendering(cmd); try cmd.endCommandBuffer(); @@ -102,12 +127,8 @@ pub fn nu_frame() !void { } } -pub fn nu_close() !void { - try au.D.deviceWaitIdle(); -} - -pub fn deinit() void { - flights.deinit(); - sc.deinit(); - au.deinit(); +pub fn nu_close() void { + au.D.deviceWaitIdle() catch |err| { + std.debug.panic("Device wait failed: {!}", .{err}); + }; } diff --git a/src/nu/Window.zig b/src/nu/Window.zig index e2363ff..303bc5b 100644 --- a/src/nu/Window.zig +++ b/src/nu/Window.zig @@ -21,7 +21,7 @@ pub const Options = struct { x11_instance_name: [*:0]const u8 = "floating_window", }; -var bus: Bus = undefined; +var bus: Bus = undefined; // todo bus should probably move to engine. pub var handle: *c.GLFWwindow = undefined; var unfocused_rate: f32 = 1.0 / 20.0;