module: add MACH_DEBUG_TRACE option to get logs of event dispatch order
Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
parent
16a895240d
commit
8e3f9e21e1
1 changed files with 35 additions and 12 deletions
|
|
@ -67,6 +67,8 @@ const Entities = @import("entities.zig").Entities;
|
||||||
const EntityID = @import("entities.zig").EntityID;
|
const EntityID = @import("entities.zig").EntityID;
|
||||||
const is_debug = @import("Archetype.zig").is_debug;
|
const is_debug = @import("Archetype.zig").is_debug;
|
||||||
|
|
||||||
|
const log = std.log.scoped(.mach);
|
||||||
|
|
||||||
/// Verifies that M matches the basic layout of a Mach module
|
/// Verifies that M matches the basic layout of a Mach module
|
||||||
fn ModuleInterface(comptime M: type) type {
|
fn ModuleInterface(comptime M: type) type {
|
||||||
validateModule(M, true);
|
validateModule(M, true);
|
||||||
|
|
@ -121,18 +123,32 @@ pub fn Modules(comptime modules: anytype) type {
|
||||||
mod: ModsByName(modules),
|
mod: ModsByName(modules),
|
||||||
// TODO: pass mods directly instead of ComponentTypesByName?
|
// TODO: pass mods directly instead of ComponentTypesByName?
|
||||||
entities: Entities(component_types_by_name),
|
entities: Entities(component_types_by_name),
|
||||||
|
debug_trace: bool,
|
||||||
|
|
||||||
pub fn init(m: *@This(), allocator: std.mem.Allocator) !void {
|
pub fn init(m: *@This(), allocator: std.mem.Allocator) !void {
|
||||||
// TODO: switch Entities to stack allocation like Modules is
|
// TODO: switch Entities to stack allocation like Modules is
|
||||||
var entities = try Entities(component_types_by_name).init(allocator);
|
var entities = try Entities(component_types_by_name).init(allocator);
|
||||||
errdefer entities.deinit();
|
errdefer entities.deinit();
|
||||||
|
|
||||||
|
const debug_trace_str = std.process.getEnvVarOwned(
|
||||||
|
allocator,
|
||||||
|
"MACH_DEBUG_TRACE",
|
||||||
|
) catch |err| switch (err) {
|
||||||
|
error.EnvironmentVariableNotFound => null,
|
||||||
|
else => return err,
|
||||||
|
};
|
||||||
|
const debug_trace = if (debug_trace_str) |s| blk: {
|
||||||
|
defer allocator.free(s);
|
||||||
|
break :blk std.ascii.eqlIgnoreCase(s, "true");
|
||||||
|
} else false;
|
||||||
|
|
||||||
// TODO: custom event queue allocation sizes
|
// TODO: custom event queue allocation sizes
|
||||||
m.* = .{
|
m.* = .{
|
||||||
.entities = entities,
|
.entities = entities,
|
||||||
.args_queue = try std.ArrayListUnmanaged(u8).initCapacity(allocator, 8 * 1024 * 1024),
|
.args_queue = try std.ArrayListUnmanaged(u8).initCapacity(allocator, 8 * 1024 * 1024),
|
||||||
.events = EventQueue.init(allocator),
|
.events = EventQueue.init(allocator),
|
||||||
.mod = undefined,
|
.mod = undefined,
|
||||||
|
.debug_trace = debug_trace,
|
||||||
};
|
};
|
||||||
errdefer m.args_queue.deinit(allocator);
|
errdefer m.args_queue.deinit(allocator);
|
||||||
errdefer m.events.deinit();
|
errdefer m.events.deinit();
|
||||||
|
|
@ -343,21 +359,20 @@ pub fn Modules(comptime modules: anytype) type {
|
||||||
|
|
||||||
if (ev.module_name) |module_name| {
|
if (ev.module_name) |module_name| {
|
||||||
// Dispatch the local event
|
// Dispatch the local event
|
||||||
try @This().callLocal(@enumFromInt(module_name), @enumFromInt(ev.event_name), ev.args_slice, injectable);
|
try m.callLocal(@enumFromInt(module_name), @enumFromInt(ev.event_name), ev.args_slice, injectable);
|
||||||
|
|
||||||
// If we only wanted to dispatch until this event, then return.
|
// If we only wanted to dispatch until this event, then return.
|
||||||
if (options.until) |until| {
|
if (options.until) |until| {
|
||||||
if (until.module_name == module_name and until.local_event == ev.event_name) return;
|
if (until.module_name == module_name and until.local_event == ev.event_name) return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Dispatch the global event
|
try m.callGlobal(@enumFromInt(ev.event_name), ev.args_slice, injectable);
|
||||||
try @This().callGlobal(@enumFromInt(ev.event_name), ev.args_slice, injectable);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call global event handler with the specified name in all modules
|
/// Call global event handler with the specified name in all modules
|
||||||
inline fn callGlobal(event_name: GlobalEvent, args: []u8, injectable: anytype) !void {
|
inline fn callGlobal(m: *@This(), event_name: GlobalEvent, args: []u8, injectable: anytype) !void {
|
||||||
if (@typeInfo(@TypeOf(event_name)).Enum.fields.len == 0) return;
|
if (@typeInfo(@TypeOf(event_name)).Enum.fields.len == 0) return;
|
||||||
switch (event_name) {
|
switch (event_name) {
|
||||||
inline else => |ev_name| {
|
inline else => |ev_name| {
|
||||||
|
|
@ -366,12 +381,15 @@ pub fn Modules(comptime modules: anytype) type {
|
||||||
_ = ModuleInterface(M); // Validate the module
|
_ = ModuleInterface(M); // Validate the module
|
||||||
if (@hasDecl(M, "global_events")) inline for (@typeInfo(@TypeOf(M.global_events)).Struct.fields) |field| {
|
if (@hasDecl(M, "global_events")) inline for (@typeInfo(@TypeOf(M.global_events)).Struct.fields) |field| {
|
||||||
comptime if (!std.mem.eql(u8, @tagName(ev_name), field.name)) continue;
|
comptime if (!std.mem.eql(u8, @tagName(ev_name), field.name)) continue;
|
||||||
|
if (m.debug_trace) log.debug("trace(global): .{s}.{s}", .{
|
||||||
|
@tagName(M.name),
|
||||||
|
@tagName(ev_name),
|
||||||
|
});
|
||||||
const handler = @field(M.global_events, @tagName(ev_name)).handler;
|
const handler = @field(M.global_events, @tagName(ev_name)).handler;
|
||||||
if (@typeInfo(@TypeOf(handler)) == .Type) continue; // Pre-declaration of what args an event has, nothing to do.
|
if (@typeInfo(@TypeOf(handler)) == .Type) continue; // Pre-declaration of what args an event has, nothing to do.
|
||||||
if (@typeInfo(@TypeOf(handler)) != .Fn) @compileError(std.fmt.comptimePrint("mach: module .{s} declares global event .{s} = .{{ .handler = T }}, expected fn but found: {s}", .{
|
if (@typeInfo(@TypeOf(handler)) != .Fn) @compileError(std.fmt.comptimePrint("mach: module .{s} declares global event .{s} = .{{ .handler = T }}, expected fn but found: {s}", .{
|
||||||
@tagName(M.name),
|
@tagName(M.name),
|
||||||
@tagName(ev_name),
|
@tagName(ev_name),
|
||||||
|
|
||||||
@typeName(@TypeOf(handler)),
|
@typeName(@TypeOf(handler)),
|
||||||
}));
|
}));
|
||||||
try callHandler(handler, args, injectable, "." ++ @tagName(M.name) ++ "." ++ @tagName(ev_name));
|
try callHandler(handler, args, injectable, "." ++ @tagName(M.name) ++ "." ++ @tagName(ev_name));
|
||||||
|
|
@ -382,7 +400,7 @@ pub fn Modules(comptime modules: anytype) type {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call local event handler with the specified name in the specified module
|
/// Call local event handler with the specified name in the specified module
|
||||||
inline fn callLocal(module_name: ModuleName(modules), event_name: LocalEvent, args: []u8, injectable: anytype) !void {
|
inline fn callLocal(m: *@This(), module_name: ModuleName(modules), event_name: LocalEvent, args: []u8, injectable: anytype) !void {
|
||||||
if (@typeInfo(@TypeOf(event_name)).Enum.fields.len == 0) return;
|
if (@typeInfo(@TypeOf(event_name)).Enum.fields.len == 0) return;
|
||||||
switch (event_name) {
|
switch (event_name) {
|
||||||
inline else => |ev_name| {
|
inline else => |ev_name| {
|
||||||
|
|
@ -393,6 +411,11 @@ pub fn Modules(comptime modules: anytype) type {
|
||||||
_ = ModuleInterface(M); // Validate the module
|
_ = ModuleInterface(M); // Validate the module
|
||||||
if (@hasDecl(M, "local_events")) inline for (@typeInfo(@TypeOf(M.local_events)).Struct.fields) |field| {
|
if (@hasDecl(M, "local_events")) inline for (@typeInfo(@TypeOf(M.local_events)).Struct.fields) |field| {
|
||||||
comptime if (!std.mem.eql(u8, @tagName(ev_name), field.name)) continue;
|
comptime if (!std.mem.eql(u8, @tagName(ev_name), field.name)) continue;
|
||||||
|
if (m.debug_trace) log.debug("trace: .{s}.{s}", .{
|
||||||
|
@tagName(M.name),
|
||||||
|
@tagName(ev_name),
|
||||||
|
});
|
||||||
|
|
||||||
const handler = @field(M.local_events, @tagName(ev_name)).handler;
|
const handler = @field(M.local_events, @tagName(ev_name)).handler;
|
||||||
if (@typeInfo(@TypeOf(handler)) == .Type) continue; // Pre-declaration of what args an event has, nothing to do.
|
if (@typeInfo(@TypeOf(handler)) == .Type) continue; // Pre-declaration of what args an event has, nothing to do.
|
||||||
if (@typeInfo(@TypeOf(handler)) != .Fn) @compileError(std.fmt.comptimePrint("mach: module .{s} declares local event .{s} = .{{ .handler = T }}, expected fn but found: {s}", .{
|
if (@typeInfo(@TypeOf(handler)) != .Fn) @compileError(std.fmt.comptimePrint("mach: module .{s} declares local event .{s} = .{{ .handler = T }}, expected fn but found: {s}", .{
|
||||||
|
|
@ -1334,7 +1357,7 @@ test "event name calling" {
|
||||||
try modules.init(testing.allocator);
|
try modules.init(testing.allocator);
|
||||||
defer modules.deinit(testing.allocator);
|
defer modules.deinit(testing.allocator);
|
||||||
|
|
||||||
try @TypeOf(modules).callGlobal(.tick, &.{}, .{});
|
try modules.callGlobal(.tick, &.{}, .{});
|
||||||
try testing.expect(usize, 2).eql(global.ticks);
|
try testing.expect(usize, 2).eql(global.ticks);
|
||||||
|
|
||||||
// Check we can use .callGlobal() with a runtime-known event name.
|
// Check we can use .callGlobal() with a runtime-known event name.
|
||||||
|
|
@ -1345,7 +1368,7 @@ test "event name calling" {
|
||||||
alloc.* = @intFromEnum(@as(GE, .tick));
|
alloc.* = @intFromEnum(@as(GE, .tick));
|
||||||
|
|
||||||
const global_event_name = @as(GE, @enumFromInt(alloc.*));
|
const global_event_name = @as(GE, @enumFromInt(alloc.*));
|
||||||
try @TypeOf(modules).callGlobal(global_event_name, &.{}, .{});
|
try modules.callGlobal(global_event_name, &.{}, .{});
|
||||||
try testing.expect(usize, 4).eql(global.ticks);
|
try testing.expect(usize, 4).eql(global.ticks);
|
||||||
|
|
||||||
// Check we can use .callLocal() with a runtime-known event and module name.
|
// Check we can use .callLocal() with a runtime-known event and module name.
|
||||||
|
|
@ -1357,8 +1380,8 @@ test "event name calling" {
|
||||||
alloc.* = @intFromEnum(@as(LE, .update));
|
alloc.* = @intFromEnum(@as(LE, .update));
|
||||||
var module_name = @as(M, @enumFromInt(m_alloc.*));
|
var module_name = @as(M, @enumFromInt(m_alloc.*));
|
||||||
var local_event_name = @as(LE, @enumFromInt(alloc.*));
|
var local_event_name = @as(LE, @enumFromInt(alloc.*));
|
||||||
try @TypeOf(modules).callLocal(module_name, local_event_name, &.{}, .{});
|
try modules.callLocal(module_name, local_event_name, &.{}, .{});
|
||||||
try @TypeOf(modules).callLocal(module_name, local_event_name, &.{}, .{});
|
try modules.callLocal(module_name, local_event_name, &.{}, .{});
|
||||||
try testing.expect(usize, 4).eql(global.ticks);
|
try testing.expect(usize, 4).eql(global.ticks);
|
||||||
try testing.expect(usize, 0).eql(global.physics_updates);
|
try testing.expect(usize, 0).eql(global.physics_updates);
|
||||||
try testing.expect(usize, 2).eql(global.renderer_updates);
|
try testing.expect(usize, 2).eql(global.renderer_updates);
|
||||||
|
|
@ -1367,14 +1390,14 @@ test "event name calling" {
|
||||||
alloc.* = @intFromEnum(@as(LE, .update));
|
alloc.* = @intFromEnum(@as(LE, .update));
|
||||||
module_name = @as(M, @enumFromInt(m_alloc.*));
|
module_name = @as(M, @enumFromInt(m_alloc.*));
|
||||||
local_event_name = @as(LE, @enumFromInt(alloc.*));
|
local_event_name = @as(LE, @enumFromInt(alloc.*));
|
||||||
try @TypeOf(modules).callLocal(module_name, local_event_name, &.{}, .{});
|
try modules.callLocal(module_name, local_event_name, &.{}, .{});
|
||||||
try testing.expect(usize, 1).eql(global.physics_updates);
|
try testing.expect(usize, 1).eql(global.physics_updates);
|
||||||
|
|
||||||
m_alloc.* = @intFromEnum(@as(M, .engine_physics));
|
m_alloc.* = @intFromEnum(@as(M, .engine_physics));
|
||||||
alloc.* = @intFromEnum(@as(LE, .calc));
|
alloc.* = @intFromEnum(@as(LE, .calc));
|
||||||
module_name = @as(M, @enumFromInt(m_alloc.*));
|
module_name = @as(M, @enumFromInt(m_alloc.*));
|
||||||
local_event_name = @as(LE, @enumFromInt(alloc.*));
|
local_event_name = @as(LE, @enumFromInt(alloc.*));
|
||||||
try @TypeOf(modules).callLocal(module_name, local_event_name, &.{}, .{});
|
try modules.callLocal(module_name, local_event_name, &.{}, .{});
|
||||||
try testing.expect(usize, 4).eql(global.ticks);
|
try testing.expect(usize, 4).eql(global.ticks);
|
||||||
try testing.expect(usize, 1).eql(global.physics_calc);
|
try testing.expect(usize, 1).eql(global.physics_calc);
|
||||||
try testing.expect(usize, 1).eql(global.physics_updates);
|
try testing.expect(usize, 1).eql(global.physics_updates);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue