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();
|
||||
controller.queue(.{
|
||||
.events = processEvents,
|
||||
.update = debug_scene.update,
|
||||
.begin_draw = beginDraw,
|
||||
.end_draw = endDraw,
|
||||
.ordered = true,
|
||||
processEvents,
|
||||
debug_scene.update,
|
||||
beginDraw,
|
||||
endDraw,
|
||||
Graph.Controller.Option.ordered,
|
||||
});
|
||||
try self.graph.freeController(controller);
|
||||
|
||||
@@ -159,9 +159,9 @@ fn processEvents(
|
||||
pub fn deinit(self: *Self) void {
|
||||
var controller = self.graph.getController() catch unreachable;
|
||||
controller.queue(.{
|
||||
.deinit = debug_scene.deinit,
|
||||
.clean = clean,
|
||||
.ordered = true,
|
||||
debug_scene.deinit,
|
||||
clean,
|
||||
Graph.Controller.Option.ordered,
|
||||
});
|
||||
self.graph.freeController(controller) catch unreachable;
|
||||
self.graph.runAllSystems() catch unreachable;
|
||||
|
@@ -6,8 +6,6 @@ const System = @import("graph/system.zig");
|
||||
// TODO:
|
||||
// - Use arena allocator?
|
||||
// - 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
|
||||
// - Handle system errors
|
||||
// - Removing of resources
|
||||
@@ -97,7 +95,7 @@ pub fn reset(self: *Self) void {
|
||||
for (controller.commands()) |*command| {
|
||||
switch (command.*) {
|
||||
.add_resource => |*resource| resource.deinit(controller.alloc),
|
||||
.queue_system => |*system| system.deinit(controller.alloc),
|
||||
.queue_system => {},
|
||||
}
|
||||
}
|
||||
controller.clear();
|
||||
@@ -106,10 +104,6 @@ pub fn reset(self: *Self) void {
|
||||
(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();
|
||||
// Duds cleanup
|
||||
for (self.duds.items) |*dud| {
|
||||
@@ -118,7 +112,6 @@ pub fn reset(self: *Self) void {
|
||||
}
|
||||
|
||||
fn enqueueSystem(self: *Self, system: System) !void {
|
||||
errdefer system.deinit(self.alloc);
|
||||
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().?;
|
||||
|
||||
defer next_system.deinit(self.alloc);
|
||||
self.runSystem(next_system) catch |err| {
|
||||
std.debug.print("System run error: {} while running {s}\n", .{ err, next_system.label });
|
||||
return err;
|
||||
@@ -278,17 +270,17 @@ test "simple graph smoke test" {
|
||||
cmd.queue(addOne);
|
||||
|
||||
cmd.queue(.{
|
||||
.first = .{
|
||||
.{
|
||||
addThousand,
|
||||
addThousand,
|
||||
addThousand,
|
||||
},
|
||||
.second = .{
|
||||
.{
|
||||
subThousand,
|
||||
subThousand,
|
||||
subThousand,
|
||||
},
|
||||
.ordered = true,
|
||||
Controller.Option.ordered,
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -324,7 +316,7 @@ test "complex queue graph smoke test" {
|
||||
|
||||
fn queueManySystems(cmd: *Controller) void {
|
||||
cmd.queue(.{
|
||||
.@"0" = .{
|
||||
.{
|
||||
addTen,
|
||||
addTen,
|
||||
addTen,
|
||||
@@ -333,7 +325,7 @@ test "complex queue graph smoke test" {
|
||||
},
|
||||
// `data1` = 20
|
||||
// `data2` = 5
|
||||
.@"1" = .{
|
||||
.{
|
||||
mulTen,
|
||||
mulTen,
|
||||
mulTwo,
|
||||
@@ -341,12 +333,12 @@ test "complex queue graph smoke test" {
|
||||
},
|
||||
// `data1` = 8000
|
||||
// `data2` = 9
|
||||
.@"2" = .{
|
||||
.{
|
||||
subTwenty,
|
||||
},
|
||||
.ordered = true,
|
||||
// `data1` = 7980
|
||||
// `data2` = 10
|
||||
Controller.Option.ordered,
|
||||
});
|
||||
}
|
||||
fn addTen(rsc: *Rsc) void {
|
||||
|
@@ -4,6 +4,8 @@ const System = @import("system.zig");
|
||||
const Resource = @import("resource.zig");
|
||||
const Controller = @This();
|
||||
|
||||
pub const Option = utils.SystemSetOption;
|
||||
|
||||
const DEFAULT_CONTROLLER_CAPACITY = 8;
|
||||
|
||||
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));
|
||||
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);
|
||||
}
|
||||
|
||||
fn createQueueCommands(
|
||||
self: *Controller,
|
||||
comptime system_set: anytype,
|
||||
comptime system_set: utils.SystemSet,
|
||||
command_buffer: []Command,
|
||||
requires_dud: ?System.Dud.Id,
|
||||
submit_dud: ?System.Dud.Id,
|
||||
) !usize {
|
||||
switch (@typeInfo(@TypeOf(system_set))) {
|
||||
.@"fn" => {
|
||||
var system = try System.fromFunction(system_set, self.alloc);
|
||||
system.requires_dud = requires_dud;
|
||||
system.submit_dud = submit_dud;
|
||||
command_buffer[0] = .{ .queue_system = system };
|
||||
switch (system_set) {
|
||||
.single => |single| {
|
||||
command_buffer[0] = .{ .queue_system = .{
|
||||
.function_runner = single.runner,
|
||||
.requested_types = single.requests,
|
||||
.requires_dud = requires_dud,
|
||||
.submit_dud = submit_dud,
|
||||
.label = single.label,
|
||||
} };
|
||||
return 1;
|
||||
},
|
||||
.@"struct" => {
|
||||
const ordered = utils.getOptionalTupleField(system_set, "ordered", false);
|
||||
.set => |set| {
|
||||
var queued_total: usize = 0;
|
||||
var prev_dud = requires_dud;
|
||||
var next_dud = submit_dud;
|
||||
|
||||
errdefer for (command_buffer[0..queued_total]) |command| {
|
||||
command.queue_system.deinit(self.alloc);
|
||||
};
|
||||
|
||||
if (ordered) {
|
||||
if (set.ordered) {
|
||||
next_dud = requires_dud;
|
||||
}
|
||||
|
||||
var queued_sets: usize = 0;
|
||||
var total_sets: usize = 0;
|
||||
inline for (@typeInfo(@TypeOf(system_set)).@"struct".fields) |field| {
|
||||
switch (@typeInfo(field.type)) {
|
||||
.@"fn", .@"struct" => total_sets += 1,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
const total_sets: usize = set.subsets.len;
|
||||
|
||||
inline for (@typeInfo(@TypeOf(system_set)).@"struct".fields) |field| {
|
||||
if (ordered) {
|
||||
inline for (set.subsets) |subset| {
|
||||
if (set.ordered) {
|
||||
prev_dud = next_dud;
|
||||
if (queued_sets == total_sets - 1) {
|
||||
next_dud = submit_dud;
|
||||
@@ -142,22 +136,16 @@ fn createQueueCommands(
|
||||
next_dud = self.acquireDud().?;
|
||||
}
|
||||
}
|
||||
switch (@typeInfo(field.type)) {
|
||||
.@"fn", .@"struct" => {
|
||||
queued_total += try self.createQueueCommands(
|
||||
@field(system_set, field.name),
|
||||
command_buffer[queued_total..],
|
||||
prev_dud,
|
||||
next_dud,
|
||||
);
|
||||
queued_sets += 1;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
queued_total += try self.createQueueCommands(
|
||||
subset,
|
||||
command_buffer[queued_total..],
|
||||
prev_dud,
|
||||
next_dud,
|
||||
);
|
||||
queued_sets += 1;
|
||||
}
|
||||
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| {
|
||||
switch (command.*) {
|
||||
.add_resource => |*resource| resource.deinit(self.alloc),
|
||||
.queue_system => |*system| system.deinit(self.alloc),
|
||||
.queue_system => {},
|
||||
}
|
||||
}
|
||||
self.clear();
|
||||
|
@@ -3,7 +3,7 @@ const utils = @import("utils.zig");
|
||||
const Controller = @import("controller.zig");
|
||||
|
||||
function_runner: *const fn ([]const *anyopaque) void,
|
||||
requested_types: []const Request,
|
||||
requested_types: []const utils.SystemRequest,
|
||||
requires_dud: ?Dud.Id,
|
||||
submit_dud: ?Dud.Id,
|
||||
label: []const u8,
|
||||
@@ -13,35 +13,3 @@ pub const Dud = struct {
|
||||
|
||||
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;
|
||||
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 {
|
||||
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 {
|
||||
const info = @typeInfo(@TypeOf(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)) {
|
||||
.@"fn", .@"struct" => validateSystemSet(@field(system_set, field_info.name)),
|
||||
else => {
|
||||
if (checkIsField(field_info, "ordered", bool)) continue;
|
||||
if (field_info.type == SystemSetOption) continue;
|
||||
@compileError("Invalid field \"" ++
|
||||
field_info.name ++
|
||||
"\" of type `" ++
|
||||
@@ -98,27 +170,17 @@ pub fn generateRunner(comptime system: anytype) fn ([]const *anyopaque) void {
|
||||
return RunnerImpl.runner;
|
||||
}
|
||||
|
||||
pub fn checkIsField(field: std.builtin.Type.StructField, field_name: []const u8, comptime field_type: type) bool {
|
||||
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) {
|
||||
pub fn generateRequests(comptime function: anytype) []const SystemRequest {
|
||||
return comptime blk: {
|
||||
for (@typeInfo(@TypeOf(tuple)).@"struct".fields) |field| {
|
||||
if (!std.mem.eql(u8, field.name, field_name)) continue;
|
||||
if (@TypeOf(default) != field.type)
|
||||
@compileError("Cannot get tuple field `" ++
|
||||
field_name ++
|
||||
"` with type `" ++
|
||||
@typeName(@TypeOf(default)) ++
|
||||
"` (tuple field has type `" ++
|
||||
@typeName(field.type) ++
|
||||
"`)");
|
||||
break :blk @field(tuple, field.name);
|
||||
var requests: [@typeInfo(@TypeOf(function)).@"fn".params.len]SystemRequest = undefined;
|
||||
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 = hashType(resource_type) },
|
||||
}
|
||||
}
|
||||
break :blk default;
|
||||
const requests_const = requests;
|
||||
break :blk &requests_const;
|
||||
};
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user