Parse system set into SystemSet
This commit is contained in:
16
src/game.zig
16
src/game.zig
@@ -71,11 +71,11 @@ pub fn run(self: *Self) GameError!void {
|
|||||||
|
|
||||||
var controller = try self.graph.getController();
|
var controller = try self.graph.getController();
|
||||||
controller.queue(.{
|
controller.queue(.{
|
||||||
.events = processEvents,
|
processEvents,
|
||||||
.update = debug_scene.update,
|
debug_scene.update,
|
||||||
.begin_draw = beginDraw,
|
beginDraw,
|
||||||
.end_draw = endDraw,
|
endDraw,
|
||||||
.ordered = true,
|
Graph.Controller.Option.ordered,
|
||||||
});
|
});
|
||||||
try self.graph.freeController(controller);
|
try self.graph.freeController(controller);
|
||||||
|
|
||||||
@@ -159,9 +159,9 @@ fn processEvents(
|
|||||||
pub fn deinit(self: *Self) void {
|
pub fn deinit(self: *Self) void {
|
||||||
var controller = self.graph.getController() catch unreachable;
|
var controller = self.graph.getController() catch unreachable;
|
||||||
controller.queue(.{
|
controller.queue(.{
|
||||||
.deinit = debug_scene.deinit,
|
debug_scene.deinit,
|
||||||
.clean = clean,
|
clean,
|
||||||
.ordered = true,
|
Graph.Controller.Option.ordered,
|
||||||
});
|
});
|
||||||
self.graph.freeController(controller) catch unreachable;
|
self.graph.freeController(controller) catch unreachable;
|
||||||
self.graph.runAllSystems() catch unreachable;
|
self.graph.runAllSystems() catch unreachable;
|
||||||
|
@@ -6,8 +6,6 @@ const System = @import("graph/system.zig");
|
|||||||
// TODO:
|
// TODO:
|
||||||
// - Use arena allocator?
|
// - Use arena allocator?
|
||||||
// - Resolve missing resource problem
|
// - Resolve missing resource problem
|
||||||
// - Parse system sets into a properly defined data structure instead of relying on `@typeInfo`
|
|
||||||
// - Find a better way to represent system sets
|
|
||||||
// - Organize a better way to execute single commands on graph
|
// - Organize a better way to execute single commands on graph
|
||||||
// - Handle system errors
|
// - Handle system errors
|
||||||
// - Removing of resources
|
// - Removing of resources
|
||||||
@@ -97,7 +95,7 @@ pub fn reset(self: *Self) void {
|
|||||||
for (controller.commands()) |*command| {
|
for (controller.commands()) |*command| {
|
||||||
switch (command.*) {
|
switch (command.*) {
|
||||||
.add_resource => |*resource| resource.deinit(controller.alloc),
|
.add_resource => |*resource| resource.deinit(controller.alloc),
|
||||||
.queue_system => |*system| system.deinit(controller.alloc),
|
.queue_system => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
controller.clear();
|
controller.clear();
|
||||||
@@ -106,10 +104,6 @@ pub fn reset(self: *Self) void {
|
|||||||
(i + 1) * self.duds.items.len / self.controllers.items.len,
|
(i + 1) * self.duds.items.len / self.controllers.items.len,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// System cleanup
|
|
||||||
for (self.system_queue.items) |*system| {
|
|
||||||
system.deinit(self.alloc);
|
|
||||||
}
|
|
||||||
self.system_queue.clearRetainingCapacity();
|
self.system_queue.clearRetainingCapacity();
|
||||||
// Duds cleanup
|
// Duds cleanup
|
||||||
for (self.duds.items) |*dud| {
|
for (self.duds.items) |*dud| {
|
||||||
@@ -118,7 +112,6 @@ pub fn reset(self: *Self) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn enqueueSystem(self: *Self, system: System) !void {
|
fn enqueueSystem(self: *Self, system: System) !void {
|
||||||
errdefer system.deinit(self.alloc);
|
|
||||||
try self.system_queue.append(self.alloc, system);
|
try self.system_queue.append(self.alloc, system);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +141,6 @@ pub fn runAllSystems(self: *Self) GraphError!void {
|
|||||||
|
|
||||||
const next_system = self.system_queue.pop().?;
|
const next_system = self.system_queue.pop().?;
|
||||||
|
|
||||||
defer next_system.deinit(self.alloc);
|
|
||||||
self.runSystem(next_system) catch |err| {
|
self.runSystem(next_system) catch |err| {
|
||||||
std.debug.print("System run error: {} while running {s}\n", .{ err, next_system.label });
|
std.debug.print("System run error: {} while running {s}\n", .{ err, next_system.label });
|
||||||
return err;
|
return err;
|
||||||
@@ -278,17 +270,17 @@ test "simple graph smoke test" {
|
|||||||
cmd.queue(addOne);
|
cmd.queue(addOne);
|
||||||
|
|
||||||
cmd.queue(.{
|
cmd.queue(.{
|
||||||
.first = .{
|
.{
|
||||||
addThousand,
|
addThousand,
|
||||||
addThousand,
|
addThousand,
|
||||||
addThousand,
|
addThousand,
|
||||||
},
|
},
|
||||||
.second = .{
|
.{
|
||||||
subThousand,
|
subThousand,
|
||||||
subThousand,
|
subThousand,
|
||||||
subThousand,
|
subThousand,
|
||||||
},
|
},
|
||||||
.ordered = true,
|
Controller.Option.ordered,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -324,7 +316,7 @@ test "complex queue graph smoke test" {
|
|||||||
|
|
||||||
fn queueManySystems(cmd: *Controller) void {
|
fn queueManySystems(cmd: *Controller) void {
|
||||||
cmd.queue(.{
|
cmd.queue(.{
|
||||||
.@"0" = .{
|
.{
|
||||||
addTen,
|
addTen,
|
||||||
addTen,
|
addTen,
|
||||||
addTen,
|
addTen,
|
||||||
@@ -333,7 +325,7 @@ test "complex queue graph smoke test" {
|
|||||||
},
|
},
|
||||||
// `data1` = 20
|
// `data1` = 20
|
||||||
// `data2` = 5
|
// `data2` = 5
|
||||||
.@"1" = .{
|
.{
|
||||||
mulTen,
|
mulTen,
|
||||||
mulTen,
|
mulTen,
|
||||||
mulTwo,
|
mulTwo,
|
||||||
@@ -341,12 +333,12 @@ test "complex queue graph smoke test" {
|
|||||||
},
|
},
|
||||||
// `data1` = 8000
|
// `data1` = 8000
|
||||||
// `data2` = 9
|
// `data2` = 9
|
||||||
.@"2" = .{
|
.{
|
||||||
subTwenty,
|
subTwenty,
|
||||||
},
|
},
|
||||||
.ordered = true,
|
|
||||||
// `data1` = 7980
|
// `data1` = 7980
|
||||||
// `data2` = 10
|
// `data2` = 10
|
||||||
|
Controller.Option.ordered,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
fn addTen(rsc: *Rsc) void {
|
fn addTen(rsc: *Rsc) void {
|
||||||
|
@@ -4,6 +4,8 @@ const System = @import("system.zig");
|
|||||||
const Resource = @import("resource.zig");
|
const Resource = @import("resource.zig");
|
||||||
const Controller = @This();
|
const Controller = @This();
|
||||||
|
|
||||||
|
pub const Option = utils.SystemSetOption;
|
||||||
|
|
||||||
const DEFAULT_CONTROLLER_CAPACITY = 8;
|
const DEFAULT_CONTROLLER_CAPACITY = 8;
|
||||||
|
|
||||||
alloc: std.mem.Allocator,
|
alloc: std.mem.Allocator,
|
||||||
@@ -90,50 +92,42 @@ fn queueInternal(self: *Controller, comptime system_set: anytype) !void {
|
|||||||
const command_buffer = try self.command_buffer.addManyAsSlice(self.alloc, utils.countSystems(system_set));
|
const command_buffer = try self.command_buffer.addManyAsSlice(self.alloc, utils.countSystems(system_set));
|
||||||
errdefer self.command_buffer.shrinkRetainingCapacity(prev_count);
|
errdefer self.command_buffer.shrinkRetainingCapacity(prev_count);
|
||||||
|
|
||||||
const commands_created = try self.createQueueCommands(system_set, command_buffer, null, self.submit_dud);
|
const commands_created = try self.createQueueCommands(utils.SystemSet.fromAny(system_set), command_buffer, null, self.submit_dud);
|
||||||
std.debug.assert(commands_created == command_buffer.len);
|
std.debug.assert(commands_created == command_buffer.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn createQueueCommands(
|
fn createQueueCommands(
|
||||||
self: *Controller,
|
self: *Controller,
|
||||||
comptime system_set: anytype,
|
comptime system_set: utils.SystemSet,
|
||||||
command_buffer: []Command,
|
command_buffer: []Command,
|
||||||
requires_dud: ?System.Dud.Id,
|
requires_dud: ?System.Dud.Id,
|
||||||
submit_dud: ?System.Dud.Id,
|
submit_dud: ?System.Dud.Id,
|
||||||
) !usize {
|
) !usize {
|
||||||
switch (@typeInfo(@TypeOf(system_set))) {
|
switch (system_set) {
|
||||||
.@"fn" => {
|
.single => |single| {
|
||||||
var system = try System.fromFunction(system_set, self.alloc);
|
command_buffer[0] = .{ .queue_system = .{
|
||||||
system.requires_dud = requires_dud;
|
.function_runner = single.runner,
|
||||||
system.submit_dud = submit_dud;
|
.requested_types = single.requests,
|
||||||
command_buffer[0] = .{ .queue_system = system };
|
.requires_dud = requires_dud,
|
||||||
|
.submit_dud = submit_dud,
|
||||||
|
.label = single.label,
|
||||||
|
} };
|
||||||
return 1;
|
return 1;
|
||||||
},
|
},
|
||||||
.@"struct" => {
|
.set => |set| {
|
||||||
const ordered = utils.getOptionalTupleField(system_set, "ordered", false);
|
|
||||||
var queued_total: usize = 0;
|
var queued_total: usize = 0;
|
||||||
var prev_dud = requires_dud;
|
var prev_dud = requires_dud;
|
||||||
var next_dud = submit_dud;
|
var next_dud = submit_dud;
|
||||||
|
|
||||||
errdefer for (command_buffer[0..queued_total]) |command| {
|
if (set.ordered) {
|
||||||
command.queue_system.deinit(self.alloc);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (ordered) {
|
|
||||||
next_dud = requires_dud;
|
next_dud = requires_dud;
|
||||||
}
|
}
|
||||||
|
|
||||||
var queued_sets: usize = 0;
|
var queued_sets: usize = 0;
|
||||||
var total_sets: usize = 0;
|
const total_sets: usize = set.subsets.len;
|
||||||
inline for (@typeInfo(@TypeOf(system_set)).@"struct".fields) |field| {
|
|
||||||
switch (@typeInfo(field.type)) {
|
|
||||||
.@"fn", .@"struct" => total_sets += 1,
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline for (@typeInfo(@TypeOf(system_set)).@"struct".fields) |field| {
|
inline for (set.subsets) |subset| {
|
||||||
if (ordered) {
|
if (set.ordered) {
|
||||||
prev_dud = next_dud;
|
prev_dud = next_dud;
|
||||||
if (queued_sets == total_sets - 1) {
|
if (queued_sets == total_sets - 1) {
|
||||||
next_dud = submit_dud;
|
next_dud = submit_dud;
|
||||||
@@ -142,22 +136,16 @@ fn createQueueCommands(
|
|||||||
next_dud = self.acquireDud().?;
|
next_dud = self.acquireDud().?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch (@typeInfo(field.type)) {
|
queued_total += try self.createQueueCommands(
|
||||||
.@"fn", .@"struct" => {
|
subset,
|
||||||
queued_total += try self.createQueueCommands(
|
command_buffer[queued_total..],
|
||||||
@field(system_set, field.name),
|
prev_dud,
|
||||||
command_buffer[queued_total..],
|
next_dud,
|
||||||
prev_dud,
|
);
|
||||||
next_dud,
|
queued_sets += 1;
|
||||||
);
|
|
||||||
queued_sets += 1;
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return queued_total;
|
return queued_total;
|
||||||
},
|
},
|
||||||
else => @compileError("System set must be either a single function or a tuple of other system sets"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +192,7 @@ pub fn deinit(self: *Controller) void {
|
|||||||
for (self.command_buffer.items) |*command| {
|
for (self.command_buffer.items) |*command| {
|
||||||
switch (command.*) {
|
switch (command.*) {
|
||||||
.add_resource => |*resource| resource.deinit(self.alloc),
|
.add_resource => |*resource| resource.deinit(self.alloc),
|
||||||
.queue_system => |*system| system.deinit(self.alloc),
|
.queue_system => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.clear();
|
self.clear();
|
||||||
|
@@ -3,7 +3,7 @@ const utils = @import("utils.zig");
|
|||||||
const Controller = @import("controller.zig");
|
const Controller = @import("controller.zig");
|
||||||
|
|
||||||
function_runner: *const fn ([]const *anyopaque) void,
|
function_runner: *const fn ([]const *anyopaque) void,
|
||||||
requested_types: []const Request,
|
requested_types: []const utils.SystemRequest,
|
||||||
requires_dud: ?Dud.Id,
|
requires_dud: ?Dud.Id,
|
||||||
submit_dud: ?Dud.Id,
|
submit_dud: ?Dud.Id,
|
||||||
label: []const u8,
|
label: []const u8,
|
||||||
@@ -13,35 +13,3 @@ pub const Dud = struct {
|
|||||||
|
|
||||||
required_count: u16 = 0,
|
required_count: u16 = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Request = union(enum) {
|
|
||||||
resource: utils.Hash,
|
|
||||||
controller: void,
|
|
||||||
// TODO:
|
|
||||||
// - Params
|
|
||||||
};
|
|
||||||
|
|
||||||
const Self = @This();
|
|
||||||
pub fn fromFunction(comptime function: anytype, alloc: std.mem.Allocator) !Self {
|
|
||||||
utils.validateSystem(function);
|
|
||||||
|
|
||||||
var requests: [@typeInfo(@TypeOf(function)).@"fn".params.len]Request = undefined;
|
|
||||||
inline for (0.., @typeInfo(@TypeOf(function)).@"fn".params) |i, param| {
|
|
||||||
switch (@typeInfo(param.type.?).pointer.child) {
|
|
||||||
Controller => requests[i] = .controller,
|
|
||||||
else => |resource_type| requests[i] = .{ .resource = utils.hashType(resource_type) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Self{
|
|
||||||
.requested_types = try alloc.dupe(Request, &requests),
|
|
||||||
.function_runner = utils.generateRunner(function),
|
|
||||||
.requires_dud = null,
|
|
||||||
.submit_dud = null,
|
|
||||||
.label = @typeName(@TypeOf(function)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: *const Self, alloc: std.mem.Allocator) void {
|
|
||||||
alloc.free(self.requested_types);
|
|
||||||
}
|
|
||||||
|
@@ -6,6 +6,77 @@ const System = @import("system.zig");
|
|||||||
pub const Hash = u32;
|
pub const Hash = u32;
|
||||||
const HashAlgorithm = std.crypto.hash.blake2.Blake2s(@bitSizeOf(Hash));
|
const HashAlgorithm = std.crypto.hash.blake2.Blake2s(@bitSizeOf(Hash));
|
||||||
|
|
||||||
|
pub const SystemSetOption = enum {
|
||||||
|
ordered,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const SystemRequest = union(enum) {
|
||||||
|
resource: Hash,
|
||||||
|
controller: void,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const SystemSet = union(enum) {
|
||||||
|
single: struct {
|
||||||
|
runner: *const fn ([]const *anyopaque) void,
|
||||||
|
requests: []const SystemRequest,
|
||||||
|
label: []const u8,
|
||||||
|
},
|
||||||
|
set: struct {
|
||||||
|
subsets: []const SystemSet,
|
||||||
|
ordered: bool,
|
||||||
|
},
|
||||||
|
|
||||||
|
pub fn fromAny(comptime any: anytype) SystemSet {
|
||||||
|
return comptime switch (@typeInfo(@TypeOf(any))) {
|
||||||
|
.@"struct" => fromStruct(any),
|
||||||
|
.@"fn" => fromFunction(any),
|
||||||
|
else => @compileError("System set must be either a tuple or a function, got " ++ @typeName(@TypeOf(any))),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub fn fromStruct(comptime set: anytype) SystemSet {
|
||||||
|
return comptime blk: {
|
||||||
|
var subset_count = 0;
|
||||||
|
for (@typeInfo(@TypeOf(set)).@"struct".fields) |field| {
|
||||||
|
const info = @typeInfo(field.type);
|
||||||
|
if (info == .@"fn" or info == .@"struct") {
|
||||||
|
subset_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var subsets: [subset_count]SystemSet = undefined;
|
||||||
|
var ordered = false;
|
||||||
|
var i = 0;
|
||||||
|
for (@typeInfo(@TypeOf(set)).@"struct".fields) |field| {
|
||||||
|
const info = @typeInfo(field.type);
|
||||||
|
if (info == .@"fn" or info == .@"struct") {
|
||||||
|
subsets[i] = SystemSet.fromAny(@field(set, field.name));
|
||||||
|
i += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (field.type == SystemSetOption) {
|
||||||
|
switch (@field(set, field.name)) {
|
||||||
|
SystemSetOption.ordered => ordered = true,
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
@compileError("System set contains extraneous elements");
|
||||||
|
}
|
||||||
|
const subsets_const = subsets;
|
||||||
|
break :blk SystemSet{ .set = .{
|
||||||
|
.subsets = &subsets_const,
|
||||||
|
.ordered = ordered,
|
||||||
|
} };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub fn fromFunction(comptime function: anytype) SystemSet {
|
||||||
|
return comptime SystemSet{ .single = .{
|
||||||
|
.runner = generateRunner(function),
|
||||||
|
.requests = generateRequests(function),
|
||||||
|
.label = @typeName(@TypeOf(function)),
|
||||||
|
} };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub inline fn hashType(comptime h_type: type) Hash {
|
pub inline fn hashType(comptime h_type: type) Hash {
|
||||||
return hashString(@typeName(h_type));
|
return hashString(@typeName(h_type));
|
||||||
}
|
}
|
||||||
@@ -29,6 +100,7 @@ pub fn validateResource(comptime resource_type: type) void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Make validators print helpful errors so I don't have to check reference trace all the time
|
||||||
pub fn validateSystem(comptime system: anytype) void {
|
pub fn validateSystem(comptime system: anytype) void {
|
||||||
const info = @typeInfo(@TypeOf(system));
|
const info = @typeInfo(@TypeOf(system));
|
||||||
if (info != .@"fn") @compileError("System can only be a function, got " ++ @typeName(system));
|
if (info != .@"fn") @compileError("System can only be a function, got " ++ @typeName(system));
|
||||||
@@ -62,7 +134,7 @@ pub fn validateSystemSet(comptime system_set: anytype) void {
|
|||||||
switch (@typeInfo(field_info.type)) {
|
switch (@typeInfo(field_info.type)) {
|
||||||
.@"fn", .@"struct" => validateSystemSet(@field(system_set, field_info.name)),
|
.@"fn", .@"struct" => validateSystemSet(@field(system_set, field_info.name)),
|
||||||
else => {
|
else => {
|
||||||
if (checkIsField(field_info, "ordered", bool)) continue;
|
if (field_info.type == SystemSetOption) continue;
|
||||||
@compileError("Invalid field \"" ++
|
@compileError("Invalid field \"" ++
|
||||||
field_info.name ++
|
field_info.name ++
|
||||||
"\" of type `" ++
|
"\" of type `" ++
|
||||||
@@ -98,27 +170,17 @@ pub fn generateRunner(comptime system: anytype) fn ([]const *anyopaque) void {
|
|||||||
return RunnerImpl.runner;
|
return RunnerImpl.runner;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn checkIsField(field: std.builtin.Type.StructField, field_name: []const u8, comptime field_type: type) bool {
|
pub fn generateRequests(comptime function: anytype) []const SystemRequest {
|
||||||
if (!std.mem.eql(u8, field.name, field_name)) return false;
|
|
||||||
if (field.type != field_type) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getOptionalTupleField(tuple: anytype, comptime field_name: []const u8, comptime default: anytype) @TypeOf(default) {
|
|
||||||
return comptime blk: {
|
return comptime blk: {
|
||||||
for (@typeInfo(@TypeOf(tuple)).@"struct".fields) |field| {
|
var requests: [@typeInfo(@TypeOf(function)).@"fn".params.len]SystemRequest = undefined;
|
||||||
if (!std.mem.eql(u8, field.name, field_name)) continue;
|
for (0.., @typeInfo(@TypeOf(function)).@"fn".params) |i, param| {
|
||||||
if (@TypeOf(default) != field.type)
|
switch (@typeInfo(param.type.?).pointer.child) {
|
||||||
@compileError("Cannot get tuple field `" ++
|
Controller => requests[i] = .controller,
|
||||||
field_name ++
|
else => |resource_type| requests[i] = .{ .resource = hashType(resource_type) },
|
||||||
"` with type `" ++
|
}
|
||||||
@typeName(@TypeOf(default)) ++
|
|
||||||
"` (tuple field has type `" ++
|
|
||||||
@typeName(field.type) ++
|
|
||||||
"`)");
|
|
||||||
break :blk @field(tuple, field.name);
|
|
||||||
}
|
}
|
||||||
break :blk default;
|
const requests_const = requests;
|
||||||
|
break :blk &requests_const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user