comptime topological sort of dependencies
This commit is contained in:
20
src/App.zig
20
src/App.zig
@@ -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);
|
||||||
|
// }
|
||||||
|
@@ -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,
|
||||||
};
|
};
|
||||||
|
133
src/nu.zig
133
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 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 });
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -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)),
|
||||||
|
@@ -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();
|
||||||
|
Reference in New Issue
Block a user