Add deferred system operations
Some checks reported errors
continuous-integration/drone/push Build was killed
Some checks reported errors
continuous-integration/drone/push Build was killed
This commit is contained in:
parent
340f843740
commit
89d975f668
@ -16,19 +16,19 @@ pub fn CallTask(comptime function: anytype) type {
|
||||
|
||||
return struct {
|
||||
args: Args,
|
||||
@"return": Future(function_fn.return_type.?),
|
||||
returned: Future(function_fn.return_type.?),
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn init(args: Args) Self {
|
||||
return .{
|
||||
.args = args,
|
||||
.@"return" = .empty,
|
||||
.returned = .empty,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn run(self: *Self) void {
|
||||
std.debug.assert(self.@"return".resolve(@call(.auto, function, self.args)));
|
||||
std.debug.assert(self.returned.resolve(@call(.auto, function, self.args)));
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -145,7 +145,7 @@ pub const TaskQueue = struct {
|
||||
while (true) {
|
||||
const runner = self.pending.dequeue();
|
||||
|
||||
if (runner.is_fn(poison_pill)) {
|
||||
if (runner.isFn(poison_pill)) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -26,16 +26,15 @@ pub const utf8 = @import("./utf8.zig");
|
||||
|
||||
pub fn Callable(comptime Output: type, comptime input_types: []const type) type {
|
||||
const InputTuple = std.meta.Tuple(input_types);
|
||||
const fn_context: *allowzero anyopaque = @ptrFromInt(0);
|
||||
|
||||
return struct {
|
||||
context: *allowzero anyopaque,
|
||||
call_with_context: *const fn (*anyopaque, InputTuple) Output,
|
||||
has_context: ?*anyopaque,
|
||||
call_with_context: *const fn (?*anyopaque, InputTuple) Output,
|
||||
|
||||
fn FnCall(comptime invoke: anytype) type {
|
||||
return struct {
|
||||
fn call(context: *anyopaque, inputs: InputTuple) Output {
|
||||
std.debug.assert(context == fn_context);
|
||||
fn call(context: ?*anyopaque, inputs: InputTuple) Output {
|
||||
std.debug.assert(context == null);
|
||||
|
||||
return @call(.auto, invoke, inputs);
|
||||
}
|
||||
@ -47,16 +46,14 @@ pub fn Callable(comptime Output: type, comptime input_types: []const type) type
|
||||
const is_zero_aligned = @typeInfo(ContextPtr).pointer.alignment == 0;
|
||||
|
||||
return struct {
|
||||
fn invoke_concrete(context: *anyopaque, inputs: InputTuple) Output {
|
||||
std.debug.assert(context != fn_context);
|
||||
|
||||
fn invoke_concrete(context: ?*anyopaque, inputs: InputTuple) Output {
|
||||
if (is_zero_aligned) {
|
||||
return @call(.auto, invoke, .{@as(ContextPtr, @ptrCast(context))} ++ inputs);
|
||||
return @call(.auto, invoke, .{@as(ContextPtr, @ptrCast(context.?))} ++ inputs);
|
||||
}
|
||||
|
||||
var args: std.meta.ArgsTuple(Invoke) = undefined;
|
||||
|
||||
args[0] = @as(ContextPtr, @ptrCast(@alignCast(context)));
|
||||
args[0] = @as(ContextPtr, @ptrCast(@alignCast(context.?)));
|
||||
|
||||
inline for (1..args.len, &inputs) |i, input| {
|
||||
args[i] = input;
|
||||
@ -73,29 +70,29 @@ pub fn Callable(comptime Output: type, comptime input_types: []const type) type
|
||||
const Self = @This();
|
||||
|
||||
pub fn call(self: Self, inputs: InputTuple) Output {
|
||||
return self.call_with_context(self.context, inputs);
|
||||
return self.call_with_context(self.has_context, inputs);
|
||||
}
|
||||
|
||||
pub fn initFn(comptime invoke: anytype) Self {
|
||||
return .{
|
||||
.context = fn_context,
|
||||
.has_context = null,
|
||||
.call_with_context = FnCall(invoke).call,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn initRef(context_ptr: anytype, comptime invoke: anytype) Self {
|
||||
return .{
|
||||
.context = @ptrCast(context_ptr),
|
||||
.has_context = @ptrCast(context_ptr),
|
||||
.call_with_context = RefCall(@TypeOf(context_ptr), invoke).invoke_concrete,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn is_fn(self: Self, invoke: anytype) bool {
|
||||
pub fn isFn(self: Self, invoke: anytype) bool {
|
||||
const Invoke = @TypeOf(invoke);
|
||||
|
||||
return switch (@typeInfo(Invoke)) {
|
||||
.pointer => self.is_fn(invoke.*),
|
||||
.@"fn" => (self.context == fn_context) and (self.call_with_context == FnCall(invoke).call),
|
||||
.pointer => self.isFn(invoke.*),
|
||||
.@"fn" => (self.has_context == null) and (self.call_with_context == FnCall(invoke).call),
|
||||
else => @compileError(std.fmt.comptimePrint("parameter `fn` must be a function type or pointer to one, not {s}", .{@typeName(Invoke)})),
|
||||
};
|
||||
}
|
||||
|
@ -8,6 +8,20 @@ pub fn Unwrapped(comptime Value: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn UnwrappedError(comptime Value: type) type {
|
||||
return switch (@typeInfo(Value)) {
|
||||
.error_union => |error_union| error_union.payload,
|
||||
else => Value,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn UnwrappedOptional(comptime Value: type) type {
|
||||
return switch (@typeInfo(Value)) {
|
||||
.optional => |optional| optional.child,
|
||||
else => Value,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isContainer(@"type": std.builtin.Type) bool {
|
||||
return switch (@"type") {
|
||||
.@"struct", .@"union", .@"enum", .@"opaque" => true,
|
||||
|
@ -4,32 +4,128 @@ const std = @import("std");
|
||||
|
||||
pub fn Binary(comptime Key: type, comptime Value: type, comptime traits: Traits(Key)) type {
|
||||
return struct {
|
||||
free_nodes: ?*Node = null,
|
||||
active_nodes: ?*Node = null,
|
||||
free_nodes: Pool = .{},
|
||||
has_root: ?*Node = null,
|
||||
|
||||
const Node = struct {
|
||||
key: Key,
|
||||
value: Value,
|
||||
has_left: ?*Node = null,
|
||||
has_right: ?*Node = null,
|
||||
has_lesser: ?*Node = null,
|
||||
has_greater: ?*Node = null,
|
||||
has_parent: ?*Node = null,
|
||||
|
||||
fn deinit(self: *Node, allocator: std.mem.Allocator) void {
|
||||
self.has_parent = undefined;
|
||||
|
||||
if (self.has_left) |left| {
|
||||
if (self.has_lesser) |left| {
|
||||
left.deinit(allocator);
|
||||
allocator.destroy(left);
|
||||
}
|
||||
|
||||
self.has_left = undefined;
|
||||
|
||||
if (self.has_right) |right| {
|
||||
if (self.has_greater) |right| {
|
||||
right.deinit(allocator);
|
||||
allocator.destroy(right);
|
||||
}
|
||||
|
||||
self.has_right = undefined;
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
fn find(self: *Node, key: Key) ?*Node {
|
||||
var node = self;
|
||||
|
||||
while (true) {
|
||||
const next_node = switch (traits.compare(key, node.key)) {
|
||||
.lesser => node.has_lesser,
|
||||
.greater => node.has_greater,
|
||||
|
||||
.equal => {
|
||||
return node;
|
||||
},
|
||||
};
|
||||
|
||||
node = next_node orelse {
|
||||
return null;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn getMax(self: *Node) *Node {
|
||||
var node = self;
|
||||
|
||||
while (self.has_lesser) |lesser| {
|
||||
node = lesser;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
fn getMin(self: *Node) *Node {
|
||||
var node = self;
|
||||
|
||||
while (self.has_lesser) |lesser| {
|
||||
node = lesser;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
fn remove(self: *Node, key: Key) ?*Node {
|
||||
const node = self.find(key) orelse {
|
||||
return null;
|
||||
};
|
||||
|
||||
if (node.has_lesser == null) {
|
||||
std.debug.assert(self.transplant(node.has_greater));
|
||||
} else if (node.has_greater == null) {
|
||||
std.debug.assert(self.transplant(node.has_lesser));
|
||||
} else {
|
||||
var successor = node.has_greater.?.getMin();
|
||||
|
||||
if (successor.has_parent != node) {
|
||||
// Move successor up: replace successor with its right child first
|
||||
std.debug.assert(successor.transplant(successor.has_greater));
|
||||
|
||||
// Attach node.right to successor
|
||||
successor.has_greater = node.has_greater;
|
||||
|
||||
if (successor.has_greater) |g| {
|
||||
g.has_parent = successor;
|
||||
}
|
||||
}
|
||||
|
||||
// Replace node with successor
|
||||
std.debug.assert(node.transplant(successor));
|
||||
|
||||
// Attach node.left to successor
|
||||
successor.has_lesser = node.has_lesser;
|
||||
|
||||
if (successor.has_lesser) |l| {
|
||||
l.has_parent = successor;
|
||||
}
|
||||
}
|
||||
|
||||
// Detach the removed node completely and return it
|
||||
node.has_parent = null;
|
||||
node.has_lesser = null;
|
||||
node.has_greater = null;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
fn transplant(self: *Node, has_node: ?*Node) bool {
|
||||
const parent = self.has_parent orelse {
|
||||
return false;
|
||||
};
|
||||
|
||||
if (parent.has_lesser == self) {
|
||||
parent.has_lesser = has_node;
|
||||
} else {
|
||||
parent.has_greater = has_node;
|
||||
}
|
||||
|
||||
if (has_node) |node| {
|
||||
node.has_parent = self.has_parent;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@ -40,8 +136,8 @@ pub fn Binary(comptime Key: type, comptime Value: type, comptime traits: Traits(
|
||||
var nodes = self.nodes;
|
||||
|
||||
while (nodes) |node| {
|
||||
const left = node.has_left orelse {
|
||||
self.nodes = node.has_right;
|
||||
const left = node.has_lesser orelse {
|
||||
self.nodes = node.has_greater;
|
||||
|
||||
return .{
|
||||
.key = node.key,
|
||||
@ -51,13 +147,13 @@ pub fn Binary(comptime Key: type, comptime Value: type, comptime traits: Traits(
|
||||
|
||||
// Find the rightmost node in left subtree or link back to current
|
||||
var pred = left;
|
||||
while (pred.has_right != null and pred.has_right != node) {
|
||||
pred = pred.has_right.?;
|
||||
while (pred.has_greater != null and pred.has_greater != node) {
|
||||
pred = pred.has_greater.?;
|
||||
}
|
||||
|
||||
if (pred.has_right != null) {
|
||||
pred.has_right = null;
|
||||
self.nodes = node.has_right;
|
||||
if (pred.has_greater != null) {
|
||||
pred.has_greater = null;
|
||||
self.nodes = node.has_greater;
|
||||
|
||||
return .{
|
||||
.key = node.key,
|
||||
@ -65,8 +161,8 @@ pub fn Binary(comptime Key: type, comptime Value: type, comptime traits: Traits(
|
||||
};
|
||||
}
|
||||
|
||||
pred.has_right = node;
|
||||
self.nodes = node.has_left;
|
||||
pred.has_greater = node;
|
||||
self.nodes = node.has_lesser;
|
||||
nodes = self.nodes;
|
||||
}
|
||||
|
||||
@ -82,16 +178,48 @@ pub fn Binary(comptime Key: type, comptime Value: type, comptime traits: Traits(
|
||||
}
|
||||
};
|
||||
|
||||
const Pool = struct {
|
||||
has_node: ?*Node = null,
|
||||
|
||||
fn create(self: *Pool, allocator: std.mem.Allocator, node: Node) std.mem.Allocator.Error!*Node {
|
||||
if (self.has_node) |free_node| {
|
||||
self.has_node = free_node.has_parent;
|
||||
|
||||
free_node.* = node;
|
||||
|
||||
return free_node;
|
||||
}
|
||||
|
||||
const created_node = try allocator.create(Node);
|
||||
|
||||
created_node.* = node;
|
||||
|
||||
return created_node;
|
||||
}
|
||||
|
||||
fn destroy(self: *Pool, node: *Node) void {
|
||||
node.* = .{
|
||||
.key = undefined,
|
||||
.value = undefined,
|
||||
.has_parent = self.has_node,
|
||||
.has_lesser = null,
|
||||
.has_greater = null,
|
||||
};
|
||||
|
||||
self.has_node = node;
|
||||
}
|
||||
};
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn clear(self: *Self) void {
|
||||
var free_nodes: ?*Node = null;
|
||||
|
||||
if (self.active_nodes) |root| {
|
||||
if (self.has_root) |root| {
|
||||
// Push root onto stack
|
||||
root.has_parent = free_nodes;
|
||||
free_nodes = root;
|
||||
self.active_nodes = null;
|
||||
self.has_root = null;
|
||||
}
|
||||
|
||||
while (free_nodes) |node| {
|
||||
@ -99,60 +227,37 @@ pub fn Binary(comptime Key: type, comptime Value: type, comptime traits: Traits(
|
||||
free_nodes = node.has_parent;
|
||||
|
||||
// Push children onto stack
|
||||
if (node.has_left) |left| {
|
||||
if (node.has_lesser) |left| {
|
||||
left.has_parent = free_nodes;
|
||||
free_nodes = left;
|
||||
}
|
||||
|
||||
if (node.has_right) |right| {
|
||||
if (node.has_greater) |right| {
|
||||
right.has_parent = free_nodes;
|
||||
free_nodes = right;
|
||||
}
|
||||
|
||||
// Add node to free list
|
||||
node.has_left = null;
|
||||
node.has_right = null;
|
||||
node.has_parent = null;
|
||||
node.key = undefined;
|
||||
node.value = undefined;
|
||||
node.has_right = self.free_nodes;
|
||||
self.free_nodes = node;
|
||||
self.free_nodes.destroy(node);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn createNode(self: *Self, allocator: std.mem.Allocator, node: Node) std.mem.Allocator.Error!*Node {
|
||||
if (self.free_nodes) |free_node| {
|
||||
self.free_nodes = free_node.has_parent;
|
||||
|
||||
free_node.* = node;
|
||||
|
||||
return free_node;
|
||||
}
|
||||
|
||||
const created_node = try allocator.create(Node);
|
||||
|
||||
created_node.* = node;
|
||||
|
||||
return created_node;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
||||
if (self.active_nodes) |node| {
|
||||
node.deinit(allocator);
|
||||
allocator.destroy(node);
|
||||
if (self.has_root) |root| {
|
||||
root.deinit(allocator);
|
||||
allocator.destroy(root);
|
||||
}
|
||||
|
||||
self.active_nodes = undefined;
|
||||
self.has_root = undefined;
|
||||
}
|
||||
|
||||
pub fn insert(self: *Self, allocator: std.mem.Allocator, key: Key, value: Value) std.mem.Allocator.Error!?*Value {
|
||||
var node = self.active_nodes orelse {
|
||||
self.active_nodes = try self.createNode(allocator, .{
|
||||
var node = self.has_root orelse {
|
||||
self.has_root = try self.free_nodes.create(allocator, .{
|
||||
.key = key,
|
||||
.value = value,
|
||||
});
|
||||
|
||||
return &self.active_nodes.?.value;
|
||||
return &self.has_root.?.value;
|
||||
};
|
||||
|
||||
while (true) {
|
||||
@ -162,26 +267,26 @@ pub fn Binary(comptime Key: type, comptime Value: type, comptime traits: Traits(
|
||||
},
|
||||
|
||||
.lesser => {
|
||||
node = node.has_left orelse {
|
||||
node.has_left = try self.createNode(allocator, .{
|
||||
node = node.has_lesser orelse {
|
||||
node.has_lesser = try self.free_nodes.create(allocator, .{
|
||||
.key = key,
|
||||
.value = value,
|
||||
.has_parent = node,
|
||||
});
|
||||
|
||||
return &node.has_left.?.value;
|
||||
return &node.has_lesser.?.value;
|
||||
};
|
||||
},
|
||||
|
||||
.greater => {
|
||||
node = node.has_right orelse {
|
||||
node.has_right = try self.createNode(allocator, .{
|
||||
node = node.has_greater orelse {
|
||||
node.has_greater = try self.free_nodes.create(allocator, .{
|
||||
.key = key,
|
||||
.value = value,
|
||||
.has_parent = node,
|
||||
});
|
||||
|
||||
return &node.has_right.?.value;
|
||||
return &node.has_greater.?.value;
|
||||
};
|
||||
},
|
||||
}
|
||||
@ -189,49 +294,54 @@ pub fn Binary(comptime Key: type, comptime Value: type, comptime traits: Traits(
|
||||
}
|
||||
|
||||
pub const empty = Self{
|
||||
.active_nodes = null,
|
||||
.has_root = null,
|
||||
};
|
||||
|
||||
pub fn get(self: Self, key: Key) ?*Value {
|
||||
var nodes = self.active_nodes;
|
||||
|
||||
while (nodes) |node| {
|
||||
nodes = switch (traits.compare(key, node.key)) {
|
||||
.lesser => node.has_left,
|
||||
.greater => node.has_right,
|
||||
|
||||
.equal => {
|
||||
return &node.value;
|
||||
},
|
||||
};
|
||||
if (self.has_root) |root| {
|
||||
if (root.find(key)) |node| {
|
||||
return &node.value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn getKey(self: Self, key: Key) ?Key {
|
||||
var nodes = self.active_nodes;
|
||||
|
||||
while (nodes) |node| {
|
||||
nodes = switch (traits.compare(key, node.key)) {
|
||||
.lesser => node.has_left,
|
||||
.greater => node.has_right,
|
||||
|
||||
.equal => {
|
||||
return node.key;
|
||||
},
|
||||
};
|
||||
if (self.has_root) |root| {
|
||||
if (root.find(key)) |node| {
|
||||
return &node.key;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn isEmpty(self: *Self) bool {
|
||||
return self.active_nodes == null;
|
||||
return self.has_root == null;
|
||||
}
|
||||
|
||||
pub fn keyValues(self: *const Self) KeyValues {
|
||||
return .{ .nodes = self.active_nodes };
|
||||
return .{ .nodes = self.has_root };
|
||||
}
|
||||
|
||||
pub fn remove(self: *Self, key: Key) ?coral.KeyValuePair(Key, Value) {
|
||||
const root = self.has_root orelse {
|
||||
return null;
|
||||
};
|
||||
|
||||
const node = root.remove(key) orelse {
|
||||
return null;
|
||||
};
|
||||
|
||||
defer {
|
||||
self.free_nodes.destroy(node);
|
||||
}
|
||||
|
||||
return .{
|
||||
.key = node.key,
|
||||
.value = node.value,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ fn scheduleName(comptime schedule: anytype) [:0]const u8 {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn hasState(self: *Self, comptime State: type) ?*State {
|
||||
pub fn hasState(self: *const Self, comptime State: type) ?*State {
|
||||
if (self.initialized_states.get(.of(State))) |boxed_state| {
|
||||
return boxed_state.has(State).?;
|
||||
}
|
||||
@ -92,6 +92,8 @@ pub fn run(self: *Self, tasks: *coral.asio.TaskQueue, comptime schedule: anytype
|
||||
const systems = self.named_systems.get(schedule_name) orelse (try self.named_systems.insert(coral.heap.allocator, schedule_name, .empty)).?;
|
||||
|
||||
try systems.run(self, tasks);
|
||||
|
||||
systems.applyDeferred(self);
|
||||
}
|
||||
|
||||
pub fn setState(self: *Self, state: anytype) error{OutOfMemory}!void {
|
||||
|
@ -7,261 +7,279 @@ const ona = @import("../ona.zig");
|
||||
const std = @import("std");
|
||||
|
||||
label: [*:0]const u8,
|
||||
onInsertion: *const fn (*const Self, std.mem.Allocator, *ona.App, *SystemGraph) std.mem.Allocator.Error![]coral.Box,
|
||||
onRun: *const fn (*ona.App, []const coral.Box) void,
|
||||
apply: *const fn (coral.Box, *ona.App) void,
|
||||
bind: *const fn (*const Self, *ona.App, *SystemGraph) std.mem.Allocator.Error!coral.Box,
|
||||
call: *const fn (coral.Box, *const ona.App) void,
|
||||
is_blocking: bool,
|
||||
|
||||
const Param = struct {
|
||||
uses_bind: bool,
|
||||
init_params: []const InitParam,
|
||||
is_blocking: bool,
|
||||
|
||||
const InitParam = struct {
|
||||
type_id: *const coral.TypeId,
|
||||
is_required: bool,
|
||||
is_read_only: bool,
|
||||
};
|
||||
|
||||
fn getInitParams(params: []const std.builtin.Type.Fn.Param) []const InitParam {
|
||||
const init_params = struct {
|
||||
const instance = get: {
|
||||
var buffer: [params.len]InitParam = undefined;
|
||||
|
||||
for (&buffer, params) |*init_param, fn_param| {
|
||||
const FnParam = fn_param.type orelse {
|
||||
@compileError("generic params on Param.init fns are disallowed");
|
||||
};
|
||||
|
||||
init_param.* = switch (@typeInfo(FnParam)) {
|
||||
.pointer => |pointer| .{
|
||||
.type_id = .of(pointer.child),
|
||||
.is_required = true,
|
||||
.is_read_only = pointer.is_const,
|
||||
},
|
||||
|
||||
.optional => |optional| switch (@typeInfo(optional.child)) {
|
||||
.pointer => |pointer| .{
|
||||
.type_id = .of(pointer.child),
|
||||
.is_required = false,
|
||||
.is_read_only = pointer.is_const,
|
||||
},
|
||||
|
||||
else => @compileError("non-pointer optional params on Param.init fns are disallowed"),
|
||||
},
|
||||
|
||||
else => @compileError("params on Param.init fns must be optionals or pointers"),
|
||||
};
|
||||
}
|
||||
|
||||
break :get buffer;
|
||||
};
|
||||
};
|
||||
|
||||
return &init_params.instance;
|
||||
}
|
||||
|
||||
pub fn init(comptime fn_param: std.builtin.Type.Fn.Param) Param {
|
||||
const FnParam = fn_param.type orelse {
|
||||
@compileError("generic behavior params are not permitted");
|
||||
};
|
||||
|
||||
const init_fn = coral.meta.hasFn(FnParam, "init") orelse {
|
||||
@compileError(std.fmt.comptimePrint("{s} must contain a .init declaration", .{@typeName(FnParam)}));
|
||||
};
|
||||
|
||||
comptime var uses_bind = false;
|
||||
|
||||
if (coral.meta.hasFn(FnParam, "bind")) |bind_fn| {
|
||||
if (bind_fn.params.len != 0) {
|
||||
@compileError(std.fmt.comptimePrint("{s}.bind cannot accept any parameters", .{@typeName(FnParam)}));
|
||||
}
|
||||
|
||||
const State = switch (@typeInfo(bind_fn.return_type.?)) {
|
||||
.error_union => |error_union| error_union.payload,
|
||||
else => bind_fn.return_type.?,
|
||||
};
|
||||
|
||||
if (init_fn.params.len == 0 or init_fn.params[0].type != *State) {
|
||||
const fn_param_name = @typeName(FnParam);
|
||||
|
||||
@compileError(std.fmt.comptimePrint("The first parameter of fn {s}.init must be of type {s} while fn {s}.bind is present", .{
|
||||
fn_param_name,
|
||||
@typeName(*State),
|
||||
fn_param_name,
|
||||
}));
|
||||
}
|
||||
|
||||
uses_bind = true;
|
||||
}
|
||||
|
||||
if (coral.meta.Unwrapped(init_fn.return_type.?) != FnParam) {
|
||||
const param_type_name = @typeName(FnParam);
|
||||
|
||||
@compileError(std.fmt.comptimePrint("Fn {s}.init must return some variation of {s}", .{
|
||||
param_type_name,
|
||||
param_type_name,
|
||||
}));
|
||||
}
|
||||
|
||||
return .{
|
||||
.init_params = getInitParams(init_fn.params[@intFromBool(uses_bind)..]),
|
||||
.is_blocking = usesTrait(FnParam, "blocking"),
|
||||
.uses_bind = uses_bind,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn after(comptime self: *const Self, comptime dependency: *const Self) *const Self {
|
||||
const afters = struct {
|
||||
fn on_insertion(behavior: *const Self, app: *ona.App, systems: *SystemGraph) std.mem.Allocator.Error!void {
|
||||
try self.onInsertion(behavior, app, systems);
|
||||
try systems.depend_on_behavior(app, behavior, dependency);
|
||||
}
|
||||
pub fn of(comptime function: anytype) *const Self {
|
||||
const Function = @TypeOf(function);
|
||||
|
||||
fn on_run(app: *ona.App) void {
|
||||
self.onRun(app);
|
||||
}
|
||||
|
||||
const instance = Self{
|
||||
.label = std.fmt.comptimePrint("({s} after {s})", .{ self.label, dependency.label }),
|
||||
.onInsertion = on_insertion,
|
||||
.onRun = on_run,
|
||||
};
|
||||
};
|
||||
|
||||
return &afters.instance;
|
||||
}
|
||||
|
||||
pub fn before(comptime self: *const Self, comptime dependant: *const Self) *const Self {
|
||||
const afters = struct {
|
||||
fn on_insertion(behavior: *const Self, app: *ona.App, systems: *SystemGraph) std.mem.Allocator.Error!void {
|
||||
try systems.depend_on_behavior(app, dependant, behavior);
|
||||
try systems.insert(app, behavior);
|
||||
}
|
||||
|
||||
const instance = Self{
|
||||
.label = std.fmt.comptimePrint("({s} before {s})", .{ self.label, dependant.label }),
|
||||
.onInsertion = on_insertion,
|
||||
};
|
||||
};
|
||||
|
||||
return &afters.instance;
|
||||
}
|
||||
|
||||
pub fn of(comptime call: anytype) *const Self {
|
||||
const Call = @TypeOf(call);
|
||||
|
||||
const call_fn = switch (@typeInfo(Call)) {
|
||||
const function_fn = switch (@typeInfo(Function)) {
|
||||
.@"fn" => |@"fn"| @"fn",
|
||||
else => @compileError("`call` parameter must be an fn type"),
|
||||
else => @compileError("`function` parameter must be an fn type"),
|
||||
};
|
||||
|
||||
const behaviors = struct {
|
||||
fn onInsertion(behavior: *const Self, allocator: std.mem.Allocator, app: *ona.App, systems: *SystemGraph) std.mem.Allocator.Error![]coral.Box {
|
||||
var states: [call_fn.params.len]coral.Box = undefined;
|
||||
comptime var states_written = 0;
|
||||
const behavior = struct {
|
||||
const ParamStates = define: {
|
||||
var param_state_types = [_]type{void} ** function_fn.params.len;
|
||||
|
||||
errdefer {
|
||||
inline for (states[states_written..]) |*state| {
|
||||
state.deinit();
|
||||
for (0..param_state_types.len, function_fn.params) |i, function_param| {
|
||||
const Param = function_param.type orelse {
|
||||
@compileError("Behavior fns may not have generic parameters");
|
||||
};
|
||||
|
||||
if (coral.meta.hasFn(Param, "bind")) |bind_fn| {
|
||||
param_state_types[i] = coral.meta.UnwrappedError(bind_fn.return_type.?);
|
||||
}
|
||||
}
|
||||
|
||||
inline for (call_fn.params) |call_param| {
|
||||
const behavior_param = comptime Param.init(call_param);
|
||||
const Tuple = std.meta.Tuple(¶m_state_types);
|
||||
|
||||
break :define struct {
|
||||
params: Tuple,
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
inline for (0..self.params.len) |i| {
|
||||
const ParamState = @TypeOf(self.params[i]);
|
||||
|
||||
if (coral.meta.hasFn(ParamState, "deinit")) |deinit_fn| {
|
||||
if (deinit_fn.params.len != 1 or deinit_fn.params[0].type != *ParamState) {
|
||||
const param_state_name = @typeName(ParamState);
|
||||
|
||||
@compileError(std.fmt.comptimePrint("Fn {s}.deinit expects only 1 argument and it must be of type {s}", .{
|
||||
param_state_name,
|
||||
param_state_name,
|
||||
}));
|
||||
}
|
||||
|
||||
if (deinit_fn.return_type != void) {
|
||||
const param_state_name = @typeName(ParamState);
|
||||
|
||||
@compileError(std.fmt.comptimePrint("Fn {s}.deinit is expected to return type void", .{
|
||||
param_state_name,
|
||||
}));
|
||||
}
|
||||
|
||||
self.params[i].deinit();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
fn apply(local_state: coral.Box, app: *ona.App) void {
|
||||
var param_states = local_state.has(ParamStates).?;
|
||||
|
||||
inline for (0..param_states.params.len) |i| {
|
||||
const ParamState = @TypeOf(param_states.params[i]);
|
||||
|
||||
if (coral.meta.hasFn(ParamState, "apply")) |apply_fn| {
|
||||
if (apply_fn.params.len != 2 or (apply_fn.params[0].type != *ParamState and apply_fn.params[1].type != *ona.App)) {
|
||||
const param_state_name = @typeName(ParamState);
|
||||
|
||||
@compileError(std.fmt.comptimePrint("Fn {s}.apply expects 2 arguments and they must be of types ({s}, {s})", .{
|
||||
param_state_name,
|
||||
@typeInfo(*ona.App),
|
||||
param_state_name,
|
||||
}));
|
||||
}
|
||||
|
||||
if (coral.meta.UnwrappedError(apply_fn.return_type.?) != void) {
|
||||
const param_state_name = @typeName(ParamState);
|
||||
|
||||
@compileError(std.fmt.comptimePrint("Fn {s}.apply is expected to return type void or error-wrapped void", .{
|
||||
param_state_name,
|
||||
}));
|
||||
}
|
||||
|
||||
coral.expect(ParamState.apply, .{ ¶m_states.params[i], app });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn bind(behavior: *const Self, app: *ona.App, systems: *SystemGraph) std.mem.Allocator.Error!coral.Box {
|
||||
inline for (function_fn.params) |function_param| {
|
||||
const Param = function_param.type orelse {
|
||||
@compileError("Behavior fns may not have generic parameters");
|
||||
};
|
||||
|
||||
const init_fn = coral.meta.hasFn(Param, "init") orelse {
|
||||
@compileError(std.fmt.comptimePrint("{s} must have a .init fn to be used as a behavior param", .{@typeName(Param)}));
|
||||
};
|
||||
|
||||
const has_bind_fn = coral.meta.hasFn(Param, "bind");
|
||||
|
||||
inline for (init_fn.params[@intFromBool(has_bind_fn != null)..]) |init_param| {
|
||||
const InitParam = coral.meta.UnwrappedOptional(init_param.type orelse {
|
||||
@compileError(std.fmt.comptimePrint("fn {s}.init may not have generic parameters", .{
|
||||
@typeName(Param),
|
||||
}));
|
||||
});
|
||||
|
||||
const init_param_pointer = switch (@typeInfo(InitParam)) {
|
||||
.pointer => |pointer| pointer,
|
||||
|
||||
else => @compileError(std.fmt.comptimePrint("Behavior param fn {s}.init expects arguments as pointer types to state only", .{
|
||||
@typeName(Param),
|
||||
})),
|
||||
};
|
||||
|
||||
inline for (behavior_param.init_params) |init_param| {
|
||||
try systems.dependOnType(app, behavior, .{
|
||||
.id = init_param.type_id,
|
||||
.is_read_only = init_param.is_read_only,
|
||||
.is_read_only = init_param_pointer.is_const,
|
||||
.id = .of(init_param_pointer.child),
|
||||
});
|
||||
}
|
||||
|
||||
states[states_written] = try .init(allocator, if (behavior_param.uses_bind) call_param.type.?.bind() else {});
|
||||
states_written += 1;
|
||||
}
|
||||
|
||||
return allocator.dupe(coral.Box, &states);
|
||||
var param_states: ParamStates = undefined;
|
||||
|
||||
inline for (0..param_states.params.len, function_fn.params) |i, param| {
|
||||
param_states.params[i] = switch (@TypeOf(param_states.params[i])) {
|
||||
void => {},
|
||||
else => coral.expect(param.type.?.bind, .{}),
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: Errdefer deinit the param states.
|
||||
|
||||
return .init(coral.heap.allocator, param_states);
|
||||
}
|
||||
|
||||
fn onRun(app: *ona.App, states: []const coral.Box) void {
|
||||
var call_args: std.meta.ArgsTuple(Call) = undefined;
|
||||
fn call(local_state: coral.Box, app: *const ona.App) void {
|
||||
var param_states = local_state.has(ParamStates).?;
|
||||
var function_args: std.meta.ArgsTuple(Function) = undefined;
|
||||
|
||||
std.debug.assert(states.len == call_args.len);
|
||||
inline for (0..function_args.len) |i| {
|
||||
const Param = function_fn.params[i].type orelse {
|
||||
@compileError("Behavior fns may not have generic parameters");
|
||||
};
|
||||
|
||||
inline for (0..call_args.len) |i| {
|
||||
const fn_param = call_fn.params[i];
|
||||
const param = comptime Param.init(fn_param);
|
||||
var init_args: std.meta.ArgsTuple(@TypeOf(fn_param.type.?.init)) = undefined;
|
||||
var init_args: std.meta.ArgsTuple(@TypeOf(Param.init)) = undefined;
|
||||
const uses_state = @TypeOf(param_states.params[i]) != void;
|
||||
|
||||
if (param.uses_bind) {
|
||||
init_args[0] = states[i].has(@TypeOf(init_args[0].*)).?;
|
||||
if (uses_state) {
|
||||
init_args[0] = ¶m_states.params[i];
|
||||
}
|
||||
|
||||
inline for (@intFromBool(param.uses_bind)..init_args.len, param.init_params) |arg_index, init_param| {
|
||||
if (init_param.is_required) {
|
||||
const InitArg = @TypeOf(init_args[arg_index].*);
|
||||
const init_fn = coral.meta.hasFn(Param, "init") orelse {
|
||||
@compileError(std.fmt.comptimePrint("{s} must have a .init fn to be used as a behavior param", .{@typeName(Param)}));
|
||||
};
|
||||
|
||||
init_args[arg_index] = app.hasState(InitArg) orelse {
|
||||
@panic(std.fmt.comptimePrint("{s} is a required state but not present in the App", .{@typeName(InitArg)}));
|
||||
};
|
||||
} else {
|
||||
const InitArg = @TypeOf(init_args[arg_index].*);
|
||||
inline for (@intFromBool(uses_state)..init_args.len, init_fn.params[@intFromBool(uses_state)..]) |arg_index, init_param| {
|
||||
const InitParam = init_param.type.?;
|
||||
|
||||
init_args[arg_index] = app.hasState(InitArg);
|
||||
switch (@typeInfo(InitParam)) {
|
||||
.optional => |optional| {
|
||||
const child_pointer = switch (@typeInfo(optional.child)) {
|
||||
.pointer => |pointer| pointer,
|
||||
else => @compileError(""),
|
||||
};
|
||||
|
||||
init_args[arg_index] = app.hasState(child_pointer.child);
|
||||
},
|
||||
|
||||
.pointer => |pointer| {
|
||||
init_args[arg_index] = app.hasState(pointer.child) orelse {
|
||||
@panic(std.fmt.comptimePrint("{s} is a required state but not present in the App", .{
|
||||
@typeName(pointer.child),
|
||||
}));
|
||||
};
|
||||
},
|
||||
|
||||
else => {
|
||||
@compileError("? or *");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
call_args[i] = coral.expect(fn_param.type.?.init, init_args);
|
||||
function_args[i] = coral.expect(Param.init, init_args);
|
||||
}
|
||||
|
||||
if (coral.meta.Unwrapped(call_fn.return_type.?) != void) {
|
||||
@compileError(std.fmt.comptimePrint("parameter `call` must return some variation of void", .{
|
||||
@typeName(Call),
|
||||
if (coral.meta.Unwrapped(function_fn.return_type.?) != void) {
|
||||
@compileError(std.fmt.comptimePrint("parameter `function` must return some variation of void", .{
|
||||
@typeName(Function),
|
||||
}));
|
||||
}
|
||||
|
||||
coral.expect(call, call_args);
|
||||
coral.expect(function, function_args);
|
||||
}
|
||||
|
||||
const instance = Self{
|
||||
.label = @typeName(Call),
|
||||
.onInsertion = onInsertion,
|
||||
.onRun = onRun,
|
||||
.label = @typeName(Function),
|
||||
.apply = apply,
|
||||
.bind = bind,
|
||||
.call = call,
|
||||
.is_blocking = is_blocking,
|
||||
};
|
||||
|
||||
.is_blocking = check: {
|
||||
for (call_fn.params) |fn_param| {
|
||||
const param = Param.init(fn_param);
|
||||
const is_blocking = infer: {
|
||||
for (function_fn.params) |function_param| {
|
||||
const Param = function_param.type orelse {
|
||||
@compileError("Behavior fns may not have generic parameters");
|
||||
};
|
||||
|
||||
if (param.is_blocking) {
|
||||
break :check true;
|
||||
for (getInitParams(Param)) |init_param| {
|
||||
const InitParam = coral.meta.UnwrappedOptional(init_param.type orelse {
|
||||
@compileError(std.fmt.comptimePrint("fn {s}.init may not have generic parameters", .{
|
||||
@typeName(Param),
|
||||
}));
|
||||
});
|
||||
|
||||
const init_param_pointer = switch (@typeInfo(InitParam)) {
|
||||
.pointer => |pointer| pointer,
|
||||
|
||||
else => @compileError(std.fmt.comptimePrint("Behavior param fn {s}.init expects arguments as pointer types to state only", .{
|
||||
@typeName(Param),
|
||||
})),
|
||||
};
|
||||
|
||||
if (usesTrait(init_param_pointer.child, "blocking")) {
|
||||
break :infer true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break :check false;
|
||||
},
|
||||
break :infer false;
|
||||
};
|
||||
};
|
||||
|
||||
return &behaviors.instance;
|
||||
return &behavior.instance;
|
||||
}
|
||||
|
||||
fn usesTrait(comptime SystemParam: type, name: []const u8) bool {
|
||||
if (@hasDecl(SystemParam, "traits")) {
|
||||
const Traits = @TypeOf(SystemParam.traits);
|
||||
fn getInitParams(comptime Param: type) []const std.builtin.Type.Fn.Param {
|
||||
const init_fn = coral.meta.hasFn(Param, "init") orelse {
|
||||
@compileError(std.fmt.comptimePrint("{s} must have a .init fn to be used as a behavior param", .{
|
||||
@typeName(Param),
|
||||
}));
|
||||
};
|
||||
|
||||
if (coral.meta.UnwrappedError(init_fn.return_type.?) != Param) {
|
||||
const param_name = @typeName(Param);
|
||||
|
||||
@compileError(std.fmt.comptimePrint("{s}.init must return {s} or an error-wrapped {s} to be used as a behavior param", .{
|
||||
param_name,
|
||||
param_name,
|
||||
param_name,
|
||||
}));
|
||||
}
|
||||
|
||||
return init_fn.params;
|
||||
}
|
||||
|
||||
fn usesTrait(comptime Param: type, name: []const u8) bool {
|
||||
if (@hasDecl(Param, "traits")) {
|
||||
const Traits = @TypeOf(Param.traits);
|
||||
|
||||
const traits_struct = switch (@typeInfo(Traits)) {
|
||||
.@"struct" => |@"struct"| @"struct",
|
||||
|
||||
else => @compileError(std.fmt.comptimePrint("{s}.traits must be a struct type", .{
|
||||
@typeName(SystemParam),
|
||||
})),
|
||||
else => @compileError(std.fmt.comptimePrint("{s}.traits must be a struct type", .{@typeName(Param)})),
|
||||
};
|
||||
|
||||
if (!traits_struct.is_tuple) {
|
||||
@compileError(std.fmt.comptimePrint("{s}.traits must be a tuple", .{@typeName(SystemParam)}));
|
||||
@compileError(std.fmt.comptimePrint("{s}.traits must be a tuple", .{@typeName(Param)}));
|
||||
}
|
||||
|
||||
for (traits_struct.fields) |field| {
|
||||
@ -269,7 +287,7 @@ fn usesTrait(comptime SystemParam: type, name: []const u8) bool {
|
||||
@compileError("All members of tuple {s}.traits must be enum literals");
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, @tagName(@field(SystemParam.traits, field.name)), name)) {
|
||||
if (std.mem.eql(u8, @tagName(@field(Param.traits, field.name)), name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -18,17 +18,13 @@ const BehaviorSet = coral.stack.Sequential(*const ona.App.Behavior);
|
||||
|
||||
const Edge = struct {
|
||||
dependencies: BehaviorSet = .empty,
|
||||
param_states: []coral.Box = &.{},
|
||||
local_state: coral.Box,
|
||||
|
||||
pub fn deinit(self: *Edge, allocator: std.mem.Allocator) void {
|
||||
for (self.param_states) |*param_state| {
|
||||
param_state.deinit();
|
||||
}
|
||||
|
||||
allocator.free(self.param_states);
|
||||
self.local_state.deinit();
|
||||
self.dependencies.deinit(allocator);
|
||||
|
||||
self.param_states = undefined;
|
||||
self.* = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
@ -42,14 +38,14 @@ const Processed = struct {
|
||||
|
||||
const Work = struct {
|
||||
behavior: *const ona.App.Behavior,
|
||||
param_states: []const coral.Box = &.{},
|
||||
local_state: coral.Box,
|
||||
};
|
||||
|
||||
const WorkSet = coral.stack.Sequential(Work);
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub const RunError = error{
|
||||
pub const RunError = std.mem.Allocator.Error || error{
|
||||
MissingDependency,
|
||||
};
|
||||
|
||||
@ -59,26 +55,28 @@ pub const TypeDependency = struct {
|
||||
};
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.processed.deinit(coral.heap.allocator);
|
||||
self.parallel_work.deinit(coral.heap.allocator);
|
||||
self.parallel_work_ranges.deinit(coral.heap.allocator);
|
||||
self.blocking_work.deinit(coral.heap.allocator);
|
||||
const allocator = coral.heap.allocator;
|
||||
|
||||
self.processed.deinit(allocator);
|
||||
self.parallel_work.deinit(allocator);
|
||||
self.parallel_work_ranges.deinit(allocator);
|
||||
self.blocking_work.deinit(allocator);
|
||||
|
||||
inline for (.{ &self.edges, &self.state_readers, &self.state_writers }) |map| {
|
||||
var key_values = map.keyValues();
|
||||
|
||||
while (key_values.nextValue()) |value| {
|
||||
value.deinit(coral.heap.allocator);
|
||||
value.deinit(allocator);
|
||||
}
|
||||
|
||||
map.deinit(coral.heap.allocator);
|
||||
map.deinit(allocator);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dependOnBehavior(self: *Self, app: *ona.App, dependant: *const ona.App.Behavior, dependency: *const ona.App.Behavior) std.mem.Allocator.Error!void {
|
||||
try self.insert(app, dependant);
|
||||
|
||||
const edges = self.edges.get(dependant) orelse (try self.edges.insert(coral.heap.allocator, dependant, .{})).?;
|
||||
const edges = self.edges.get(dependant).?;
|
||||
|
||||
if (std.mem.indexOfScalar(*const ona.App.Behavior, edges.dependencies.items.slice(), dependency) == null) {
|
||||
try edges.dependencies.pushGrow(coral.heap.allocator, dependency);
|
||||
@ -135,17 +133,28 @@ pub const empty = Self{
|
||||
.parallel_work_ranges = .empty,
|
||||
};
|
||||
|
||||
pub fn insert(self: *Self, app: *ona.App, behavior: *const ona.App.Behavior) std.mem.Allocator.Error!void {
|
||||
self.processed.clear();
|
||||
pub fn applyDeferred(self: *Self, app: *ona.App) void {
|
||||
for (self.parallel_work.items.slice()) |work| {
|
||||
work.behavior.apply(work.local_state, app);
|
||||
}
|
||||
|
||||
if (try self.edges.insert(coral.heap.allocator, behavior, .{})) |edge| {
|
||||
edge.param_states = try behavior.onInsertion(behavior, coral.heap.allocator, app, self);
|
||||
for (self.blocking_work.items.slice()) |work| {
|
||||
work.behavior.apply(work.local_state, app);
|
||||
}
|
||||
}
|
||||
|
||||
fn parallelRun(app: *ona.App, works: []const Work) void {
|
||||
for (works) |work| {
|
||||
work.behavior.onRun(app, work.param_states);
|
||||
pub fn insert(self: *Self, app: *ona.App, behavior: *const ona.App.Behavior) std.mem.Allocator.Error!void {
|
||||
self.processed.clear();
|
||||
|
||||
if (try self.edges.insert(coral.heap.allocator, behavior, undefined)) |edge| {
|
||||
errdefer {
|
||||
std.debug.assert(self.edges.remove(behavior) != null);
|
||||
}
|
||||
|
||||
edge.* = .{
|
||||
.local_state = try behavior.bind(behavior, app, self),
|
||||
.dependencies = .empty,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,12 +173,12 @@ fn process(self: *Self, behavior: *const ona.App.Behavior, edge: Edge) !Processe
|
||||
try switch (processed.is_blocking) {
|
||||
true => self.blocking_work.pushGrow(coral.heap.allocator, .{
|
||||
.behavior = behavior,
|
||||
.param_states = edge.param_states,
|
||||
.local_state = edge.local_state,
|
||||
}),
|
||||
|
||||
false => self.parallel_work.pushGrow(coral.heap.allocator, .{
|
||||
.behavior = behavior,
|
||||
.param_states = edge.param_states,
|
||||
.local_state = edge.local_state,
|
||||
}),
|
||||
};
|
||||
}
|
||||
@ -177,7 +186,7 @@ fn process(self: *Self, behavior: *const ona.App.Behavior, edge: Edge) !Processe
|
||||
return processed;
|
||||
}
|
||||
|
||||
pub fn run(self: *Self, app: *ona.App, tasks: *coral.asio.TaskQueue) (std.mem.Allocator.Error || RunError)!void {
|
||||
pub fn run(self: *Self, app: *const ona.App, tasks: *coral.asio.TaskQueue) RunError!void {
|
||||
if (self.processed.isEmpty()) {
|
||||
errdefer {
|
||||
self.processed.clear();
|
||||
@ -205,19 +214,26 @@ pub fn run(self: *Self, app: *ona.App, tasks: *coral.asio.TaskQueue) (std.mem.Al
|
||||
}
|
||||
}
|
||||
|
||||
var parallel_work_offset: usize = 0;
|
||||
{
|
||||
const parallel_work = self.parallel_work.items.slice();
|
||||
var parallel_work_offset: usize = 0;
|
||||
|
||||
for (self.parallel_work_ranges.items.slice()) |parallel_work_range| {
|
||||
const work = self.parallel_work.items.slice()[parallel_work_offset .. parallel_work_offset + parallel_work_range];
|
||||
for (self.parallel_work_ranges.items.slice()) |parallel_work_range| {
|
||||
try tasks.execute(coral.asio.CallTask(runWorkGroup).init(.{
|
||||
app,
|
||||
parallel_work[parallel_work_offset .. parallel_work_offset + parallel_work_range],
|
||||
}));
|
||||
|
||||
try tasks.execute(coral.asio.CallTask(parallelRun).init(.{ app, work }));
|
||||
|
||||
parallel_work_offset += parallel_work_range;
|
||||
parallel_work_offset += parallel_work_range;
|
||||
}
|
||||
}
|
||||
|
||||
tasks.finish();
|
||||
runWorkGroup(app, self.blocking_work.items.slice());
|
||||
}
|
||||
|
||||
for (self.blocking_work.items.slice()) |work| {
|
||||
work.behavior.onRun(app, work.param_states);
|
||||
fn runWorkGroup(app: *const ona.App, parallel_work: []const Work) void {
|
||||
for (parallel_work) |work| {
|
||||
work.behavior.call(work.local_state, app);
|
||||
}
|
||||
}
|
||||
|
@ -29,8 +29,17 @@ pub const Display = struct {
|
||||
is_hidden: bool,
|
||||
};
|
||||
|
||||
pub fn queueSynchronize(context: ona.Exclusive(Context), display: ona.Read(Display)) !void {
|
||||
context.ptr.shared.swapBuffers(display.ptr.*);
|
||||
pub fn synchronize(commands: ona.Commands) !void {
|
||||
const buffer_swap = struct {
|
||||
fn apply(app: *ona.App) void {
|
||||
const context = app.hasState(Context).?;
|
||||
const display = app.hasState(Display).?;
|
||||
|
||||
context.shared.swapBuffers(display.*);
|
||||
}
|
||||
};
|
||||
|
||||
try commands.push(buffer_swap.apply);
|
||||
}
|
||||
|
||||
pub fn setup(app: *ona.App) !void {
|
||||
@ -66,5 +75,5 @@ pub fn setup(app: *ona.App) !void {
|
||||
try app.setState(context);
|
||||
}
|
||||
|
||||
try app.on(.render, .of(queueSynchronize));
|
||||
try app.on(.render, .of(synchronize));
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ pub const Queue = struct {
|
||||
commands: coral.stack.Sequential(Command) = .empty,
|
||||
has_next: ?*Queue = null,
|
||||
|
||||
pub fn cancel(self: *Queue) void {
|
||||
pub fn reset(self: *Queue) void {
|
||||
self.commands.clear();
|
||||
}
|
||||
|
||||
@ -184,7 +184,7 @@ const Shared = struct {
|
||||
|
||||
while (self.swap_buffers[@intFromBool(!self.is_swapped)].dequeue()) |queue| {
|
||||
defer {
|
||||
queue.cancel();
|
||||
queue.reset();
|
||||
|
||||
queue.has_next = self.has_pooled_queue.load(.acquire);
|
||||
|
||||
|
@ -158,6 +158,70 @@ fn Channel(comptime Message: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
pub const Commands = struct {
|
||||
pending: *Buffer,
|
||||
|
||||
const Action = coral.Callable(void, &.{*App});
|
||||
|
||||
const Buffer = struct {
|
||||
arena: std.heap.ArenaAllocator,
|
||||
actions: coral.stack.Sequential(Action),
|
||||
|
||||
pub fn apply(self: *Buffer, app: *App) void {
|
||||
for (self.actions.items.slice()) |action| {
|
||||
action.call(.{app});
|
||||
}
|
||||
|
||||
self.reset();
|
||||
}
|
||||
|
||||
pub fn reset(self: *Buffer) void {
|
||||
if (!self.arena.reset(.retain_capacity)) {
|
||||
std.log.warn("Failed to retain command buffer arena capacity", .{});
|
||||
}
|
||||
|
||||
self.actions.clear();
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Buffer) void {
|
||||
self.actions.deinit(coral.heap.allocator);
|
||||
self.arena.deinit();
|
||||
}
|
||||
};
|
||||
|
||||
pub fn bind() Buffer {
|
||||
return .{
|
||||
.arena = .init(coral.heap.allocator),
|
||||
.actions = .empty,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn init(buffer: *Buffer) Commands {
|
||||
return .{
|
||||
.pending = buffer,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn push(self: Commands, comptime command: anytype) std.mem.Allocator.Error!void {
|
||||
const Command = @TypeOf(command);
|
||||
|
||||
switch (Command) {
|
||||
fn (*App) void => {
|
||||
try self.pending.actions.pushGrow(coral.heap.allocator, .initFn(command));
|
||||
},
|
||||
|
||||
else => {
|
||||
const arena_allocator = self.pending.arena.allocator();
|
||||
const context = try arena_allocator.create(Command);
|
||||
|
||||
context.* = .{};
|
||||
|
||||
try self.pending.actions.pushGrow(coral.heap.allocator, .initRef(command));
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn Exclusive(comptime State: type) type {
|
||||
return struct {
|
||||
ptr: *State,
|
||||
|
Loading…
x
Reference in New Issue
Block a user