comptime topological sort of dependencies

This commit is contained in:
David Allemang
2024-07-15 15:38:57 -04:00
parent 0efc931006
commit 8c3e65fced
5 changed files with 96 additions and 115 deletions

View File

@@ -5,24 +5,18 @@ const Bus = @import("nu/Bus.zig");
const Render = @import("nu/Render.zig"); const Render = @import("nu/Render.zig");
const ImGui = @import("nu/ImGui.zig"); const ImGui = @import("nu/ImGui.zig");
pub fn module() nu.Module { pub const depends = .{ ImGui, Render };
return nu.Module{
.name = "App",
// .dependencies = &.{ Render.module(), ImGui.module() },
.setup = setup,
.teardown = teardown,
.update = update,
};
}
pub fn setup(_: std.mem.Allocator) !void { pub fn setup(_: std.mem.Allocator) !void {}
try ImGui.gui.register(gui);
}
pub fn teardown() void {} pub fn teardown() void {}
pub fn update() !void {} pub fn update() !void {}
pub fn gui() void { pub fn frame() !void {
ImGui.igShowMetricsWindow(null); ImGui.igShowMetricsWindow(null);
} }
// pub fn gui() void {
// ImGui.igShowMetricsWindow(null);
// }

View File

@@ -22,8 +22,8 @@ pub const nu_options: nu.Options = .{
// component of the larger structure. // component of the larger structure.
pub const nu_driver = nu.Window; pub const nu_driver = nu.Window;
pub const nu_present = nu.Render;
pub const nu_modules = .{ pub const nu_modules = .{
nu.Render,
nu.ImGui, nu.ImGui,
App, App,
}; };

View File

@@ -15,73 +15,90 @@ const Config = struct {
pub const config: Config = if (@hasDecl(root, "nu_config")) root.nu_config else .{}; 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 { pub fn main() void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const alloc = gpa.allocator(); const alloc = gpa.allocator();
var modules = std.StringArrayHashMap(Module).init(alloc); const engine = Engine(root.nu_driver, root.nu_present, root.nu_modules);
defer modules.deinit();
const driver = root.nu_driver.driver(); engine.try_invoke("setup", .{alloc}) catch @panic("Startup failed");
enable(&modules, driver.module); defer engine.rinvoke("teardown", .{});
inline for (root.nu_modules) |mod| {
enable(&modules, mod.module()); while (engine.next()) {
engine.try_invoke("fixed", .{}) catch @panic("fixed failed!");
engine.try_invoke("frame", .{}) catch @panic("frame failed!");
engine.render() catch @panic("render failed!");
}
} }
for (modules.values()) |mod| { pub fn Engine(comptime D: type, comptime R: type, comptime M: anytype) type {
std.log.debug("{s}.setup", .{mod.name}); return struct {
mod.setup(alloc) catch |err| { pub const modules = Graph.sort(.{ D, R } ++ M);
std.debug.panic("Module {s} setup error: {!}", .{ mod.name, err });
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);
}
}
}
}; };
} }
defer {
var rev = std.mem.reverseIterator(modules.values()); const Graph = struct {
while (rev.next()) |mod| { fn visit(
std.log.debug("{s}.teardown", .{mod.name}); comptime mod: type,
mod.teardown(); 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};
} }
std.log.info("Loaded modules: {s}", .{modules.keys()}); fn sort(comptime modules: anytype) []const type {
var mark: []const type = &.{};
while (driver.next()) { var result: []const type = &.{};
for (modules.values()) |mod| { for (modules) |mod| visit(mod, &mark, &result);
if (mod.update) |update| { return result;
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 });
};
}
}
}
}

View File

@@ -16,29 +16,17 @@ pub usingnamespace im;
pub const Config = struct {}; pub const Config = struct {};
const config = nu.config.imgui; const config = nu.config.imgui;
pub fn module() nu.Module { pub const depends = .{ Render, Window };
return nu.Module{
.name = "ImGui",
// .dependencies = &.{Render.module()},
.setup = setup,
.teardown = teardown,
.update = update,
};
}
pub fn loader_wrapper(procname: [*c]const u8, _: ?*anyopaque) callconv(.C) vk.PfnVoidFunction { pub fn loader_wrapper(procname: [*c]const u8, _: ?*anyopaque) callconv(.C) vk.PfnVoidFunction {
return au.glfwGetInstanceProcAddress(au.I.handle, procname); return au.glfwGetInstanceProcAddress(au.I.handle, procname);
} }
pub var gui: Hook(fn () void) = undefined;
var ctx: *im.ImGuiContext = undefined; var ctx: *im.ImGuiContext = undefined;
var descriptor_pool: vk.DescriptorPool = undefined; var descriptor_pool: vk.DescriptorPool = undefined;
pub fn setup(alloc: std.mem.Allocator) !void { pub fn setup(_: std.mem.Allocator) !void {
gui = @TypeOf(gui).init(alloc); // try Render.present.register(present);
errdefer gui.deinit();
try Render.present.register(present);
ctx = im.igCreateContext(null) orelse { ctx = im.igCreateContext(null) orelse {
return error.igCreateContextFailed; return error.igCreateContextFailed;
@@ -104,21 +92,18 @@ pub fn teardown() void {
au.D.destroyDescriptorPool(descriptor_pool, null); au.D.destroyDescriptorPool(descriptor_pool, null);
im.impl.ImGui_ImplGlfw_Shutdown(); im.impl.ImGui_ImplGlfw_Shutdown();
im.igDestroyContext(ctx); im.igDestroyContext(ctx);
gui.deinit();
} }
pub fn update() !void { pub fn frame() !void {
im.impl.ImGui_ImplGlfw_NewFrame(); im.impl.ImGui_ImplGlfw_NewFrame();
im.impl.ImGui_ImplVulkan_NewFrame(); im.impl.ImGui_ImplVulkan_NewFrame();
im.igNewFrame(); im.igNewFrame();
gui.invoke(.{});
im.igEndFrame();
im.igRender();
} }
pub fn present(cmd: au.CommandBufferProxy) void { pub fn present(cmd: au.CommandBufferProxy) void {
im.igEndFrame();
im.igRender();
im.impl.ImGui_ImplVulkan_RenderDrawData( im.impl.ImGui_ImplVulkan_RenderDrawData(
@ptrCast(im.igGetDrawData()), @ptrCast(im.igGetDrawData()),
@ptrFromInt(@intFromEnum(cmd.handle)), @ptrFromInt(@intFromEnum(cmd.handle)),

View File

@@ -32,21 +32,11 @@ pub const Config = struct {
}; };
const config = nu.config.render; const config = nu.config.render;
pub fn module() nu.Module { pub const depends = .{nu.Window};
return nu.Module{
.name = "Render",
.setup = setup,
.teardown = teardown,
.frame = frame,
.dependencies = &.{nu.Window.driver().module},
};
}
var sc: au.SwapChain = undefined; var sc: au.SwapChain = undefined;
var flights: au.Flights = undefined; var flights: au.Flights = undefined;
pub var present: nu.Hook(fn (au.CommandBufferProxy) void) = undefined;
pub fn setup(alloc: std.mem.Allocator) !void { pub fn setup(alloc: std.mem.Allocator) !void {
// todo pick apart au into helpers; not a sub-module filled with its own globals. // todo pick apart au into helpers; not a sub-module filled with its own globals.
try au.init(alloc); 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); flights = try au.Flights.init(alloc, config.frames_in_flight);
errdefer flights.deinit(); errdefer flights.deinit();
present = @TypeOf(present).init(alloc);
errdefer present.deinit();
} }
pub fn teardown() void { pub fn teardown() void {
au.D.deviceWaitIdle() catch |err| std.debug.panic("Device wait failed: {!}", .{err}); au.D.deviceWaitIdle() catch |err| std.debug.panic("Device wait failed: {!}", .{err});
errdefer present.deinit();
flights.deinit(); flights.deinit();
sc.deinit(); sc.deinit();
au.deinit(); au.deinit();
} }
pub fn frame() !void { pub fn render(engine: anytype) !void {
const flight: au.Flights.Flight = flights.next(); const flight: au.Flights.Flight = flights.next();
try flight.wait(); try flight.wait();
@@ -97,9 +83,8 @@ pub fn frame() !void {
target.begin_rendering(cmd, render_area); target.begin_rendering(cmd, render_area);
// todo manage frame in flight state for each hook; pass the current flight in as context. // 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); target.end_rendering(cmd);
try cmd.endCommandBuffer(); try cmd.endCommandBuffer();