Add system-parameterised local states
Some checks failed
continuous-integration/drone Build is failing
Some checks failed
continuous-integration/drone Build is failing
This commit is contained in:
parent
e1d41ded4a
commit
d2a246fce0
27
build.zig
27
build.zig
@ -109,26 +109,18 @@ pub fn build(b: *std.Build) void {
|
||||
|
||||
const shaderc_dependency = b.dependency("shaderc_zig", .{});
|
||||
|
||||
const sdl_dependency = b.dependency("sdl", .{
|
||||
.target = config.module_target,
|
||||
.optimize = config.optimize,
|
||||
.preferred_linkage = .static,
|
||||
});
|
||||
|
||||
const coral_module = b.addModule("coral", .{
|
||||
.root_source_file = b.path("src/coral/coral.zig"),
|
||||
.target = config.module_target,
|
||||
.optimize = config.optimize,
|
||||
});
|
||||
|
||||
const ext_module = b.createModule(.{
|
||||
.root_source_file = b.path("src/ext/ext.zig"),
|
||||
.target = config.module_target,
|
||||
.optimize = config.optimize,
|
||||
.link_libc = true,
|
||||
});
|
||||
|
||||
ext_module.linkSystemLibrary("SDL3", .{
|
||||
.needed = true,
|
||||
.preferred_link_mode = .dynamic,
|
||||
});
|
||||
|
||||
ext_module.linkLibrary(shaderc_dependency.artifact("shaderc"));
|
||||
|
||||
const ona_module = b.addModule("ona", .{
|
||||
.root_source_file = b.path("src/ona/ona.zig"),
|
||||
.target = config.module_target,
|
||||
@ -136,10 +128,6 @@ pub fn build(b: *std.Build) void {
|
||||
.link_libc = true,
|
||||
|
||||
.imports = &.{
|
||||
.{
|
||||
.name = "ext",
|
||||
.module = ext_module,
|
||||
},
|
||||
.{
|
||||
.name = "coral",
|
||||
.module = coral_module,
|
||||
@ -147,6 +135,9 @@ pub fn build(b: *std.Build) void {
|
||||
},
|
||||
});
|
||||
|
||||
ona_module.linkLibrary(shaderc_dependency.artifact("shaderc"));
|
||||
ona_module.linkLibrary(sdl_dependency.artifact("SDL3"));
|
||||
|
||||
// config.addShaders(ona_module, &.{
|
||||
// "./src/ona/gfx/effect_shader.zig",
|
||||
// "./src/ona/gfx/effect_fragment.zig",
|
||||
|
@ -7,6 +7,10 @@
|
||||
.url = "git+https://github.com/tiawl/shaderc.zig#06565d2af3beec9780b11524984211ebd104fd21",
|
||||
.hash = "shaderc_zig-1.0.0-mOl840tjAwBiAnMSfRskq0Iq3JJ9jPRHy2JoEgnUvSpV",
|
||||
},
|
||||
.sdl = .{
|
||||
.url = "git+https://github.com/castholm/SDL.git#0f81c0affb2584b242b2fb5744e7dfebcfd904a5",
|
||||
.hash = "sdl-0.2.6+3.2.20-7uIn9JkjfwGIQ6j3-etow2rCe-Zt16Yj-2gdp9jW7WZ9",
|
||||
},
|
||||
},
|
||||
|
||||
.fingerprint = 0x7d0142e88b22421d,
|
||||
|
@ -2,40 +2,61 @@ const coral = @import("./coral.zig");
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
delete: *const fn (*anyopaque) void,
|
||||
destroy: *const fn (*anyopaque) void,
|
||||
erased: *anyopaque,
|
||||
|
||||
fn Layout(comptime Type: type) type {
|
||||
return struct {
|
||||
return switch (Type) {
|
||||
void => struct {
|
||||
fn destroy(_: *anyopaque) void {}
|
||||
},
|
||||
|
||||
else => struct {
|
||||
allocator: std.mem.Allocator,
|
||||
value: Type,
|
||||
|
||||
const TypeLayout = @This();
|
||||
|
||||
fn delete(erased: *anyopaque) void {
|
||||
fn destroy(erased: *anyopaque) void {
|
||||
const layout: *TypeLayout = @ptrCast(@alignCast(erased));
|
||||
|
||||
if (@hasDecl(Type, "deinit")) {
|
||||
const deinit_fn = switch (@typeInfo(@TypeOf(Type.deinit))) {
|
||||
.@"fn" => |@"fn"| @"fn",
|
||||
|
||||
else => @compileError(std.fmt.comptimePrint("Declaration `{s}.deinit` must be an fn type to be boxable", .{
|
||||
@typeName(Type),
|
||||
})),
|
||||
};
|
||||
|
||||
if (deinit_fn.params.len != 1 or deinit_fn.params[0].type != *Type) {
|
||||
@compileError(std.fmt.comptimePrint("Fn `{s}.deinit` is only permitted 1 parameter to be boxable and it must be of type {s}", .{
|
||||
@typeName(Type),
|
||||
@typeName(*Type),
|
||||
}));
|
||||
}
|
||||
|
||||
layout.value.deinit();
|
||||
}
|
||||
|
||||
layout.allocator.destroy(layout);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.delete(self.erased);
|
||||
self.destroy(self.erased);
|
||||
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn has(self: Self, comptime Type: type) ?*Type {
|
||||
const ValueLayout = Layout(Type);
|
||||
pub fn has(self: Self, comptime Value: type) ?*Value {
|
||||
const ValueLayout = Layout(Value);
|
||||
|
||||
if (self.delete == ValueLayout.delete) {
|
||||
if (self.destroy == ValueLayout.destroy) {
|
||||
const layout: *ValueLayout = @ptrCast(@alignCast(self.erased));
|
||||
|
||||
return &layout.value;
|
||||
@ -44,25 +65,25 @@ pub fn has(self: Self, comptime Type: type) ?*Type {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn init(value: anytype) std.mem.Allocator.Error!Self {
|
||||
return initWithAllocator(coral.heap.allocator, value);
|
||||
}
|
||||
|
||||
pub fn initWithAllocator(allocator: std.mem.Allocator, value: anytype) std.mem.Allocator.Error!Self {
|
||||
const ValueLayout = Layout(@TypeOf(value));
|
||||
pub fn init(allocator: std.mem.Allocator, value: anytype) std.mem.Allocator.Error!Self {
|
||||
const Value = @TypeOf(value);
|
||||
const ValueLayout = Layout(Value);
|
||||
const value_layout = try allocator.create(ValueLayout);
|
||||
|
||||
errdefer {
|
||||
allocator.destroy(value_layout);
|
||||
}
|
||||
|
||||
value_layout.* = .{
|
||||
.allocator = allocator,
|
||||
.value = value,
|
||||
};
|
||||
if (@hasField(ValueLayout, "allocator")) {
|
||||
value_layout.allocator = allocator;
|
||||
}
|
||||
|
||||
if (@hasField(ValueLayout, "value")) {
|
||||
value_layout.value = value;
|
||||
}
|
||||
|
||||
return .{
|
||||
.erased = @ptrCast(value_layout),
|
||||
.delete = ValueLayout.delete,
|
||||
.destroy = ValueLayout.destroy,
|
||||
};
|
||||
}
|
||||
|
@ -251,10 +251,8 @@ pub const TaskQueue = struct {
|
||||
|
||||
std.debug.assert(threads_spawned == thread_count);
|
||||
|
||||
const name = comptime try coral.ShortString.init("ona worker");
|
||||
|
||||
for (threads) |thread| {
|
||||
thread.setName(name.slice()) catch |set_name_error| {
|
||||
thread.setName("ona worker") catch |set_name_error| {
|
||||
switch (set_name_error) {
|
||||
error.Unsupported, error.NameTooLong => break,
|
||||
else => continue,
|
||||
|
@ -10,12 +10,12 @@ pub const hashes = @import("./hashes.zig");
|
||||
|
||||
pub const heap = @import("./heap.zig");
|
||||
|
||||
pub const list = @import("./list.zig");
|
||||
|
||||
pub const map = @import("./map.zig");
|
||||
|
||||
pub const scalars = @import("./scalars.zig");
|
||||
|
||||
pub const stack = @import("./stack.zig");
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
pub const tree = @import("./tree.zig");
|
||||
@ -113,243 +113,6 @@ pub fn KeyValuePair(comptime Key: type, comptime Value: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
pub const ShortString = extern struct {
|
||||
buffer: [max]u8,
|
||||
remaining: u8,
|
||||
|
||||
pub const Error = error{
|
||||
StringTooLong,
|
||||
};
|
||||
|
||||
pub fn append(self: *ShortString, c: u8) Error!void {
|
||||
const post_remaining = scalars.sub(self.remaining, 1) orelse {
|
||||
return error.StringTooLong;
|
||||
};
|
||||
|
||||
self.buffer[self.len()] = c;
|
||||
self.remaining = post_remaining;
|
||||
}
|
||||
|
||||
pub fn assign(self: *ShortString, data: []const u8) Error!void {
|
||||
self.clear();
|
||||
|
||||
try self.join(data);
|
||||
}
|
||||
|
||||
pub fn clear(self: *ShortString) void {
|
||||
self.* = empty;
|
||||
}
|
||||
|
||||
pub fn init(data: []const u8) Error!ShortString {
|
||||
var string = empty;
|
||||
|
||||
try string.join(data);
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
pub fn isEmpty(self: ShortString) bool {
|
||||
return self.remaining == max;
|
||||
}
|
||||
|
||||
pub fn join(self: *ShortString, data: []const u8) Error!void {
|
||||
const remaining = scalars.sub(self.remaining, data.len) orelse {
|
||||
return error.StringTooLong;
|
||||
};
|
||||
|
||||
@memcpy(self.buffer[self.len()..(max - remaining)], data);
|
||||
|
||||
self.remaining = @intCast(remaining);
|
||||
}
|
||||
|
||||
pub const empty = ShortString{
|
||||
.buffer = [_]u8{0} ** max,
|
||||
.remaining = max,
|
||||
};
|
||||
|
||||
pub fn len(self: ShortString) usize {
|
||||
return max - self.remaining;
|
||||
}
|
||||
|
||||
pub const max = 255;
|
||||
|
||||
pub fn ptr(self: *const ShortString) [*:0]const u8 {
|
||||
return @ptrCast(&self.buffer);
|
||||
}
|
||||
|
||||
pub fn slice(self: *const ShortString) [:0]const u8 {
|
||||
return @ptrCast(self.ptr()[0..self.len()]);
|
||||
}
|
||||
|
||||
pub fn writeFormat(self: ShortString, output: bytes.Writable) bytes.ReadWriteError!void {
|
||||
return bytes.writeAll(output, self.slice());
|
||||
}
|
||||
|
||||
pub fn writer(self: *ShortString) bytes.Writable {
|
||||
const writing = struct {
|
||||
fn write(string: *ShortString, buffer: []const u8) usize {
|
||||
string.append(buffer) catch {
|
||||
return 0;
|
||||
};
|
||||
|
||||
return buffer.len;
|
||||
}
|
||||
};
|
||||
|
||||
return .init(ShortString, self, writing.write);
|
||||
}
|
||||
};
|
||||
|
||||
pub fn Stack(comptime Value: type) type {
|
||||
return struct {
|
||||
values: []Value,
|
||||
cap: usize,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn clear(self: *Self) void {
|
||||
self.values = self.values[0..0];
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
if (self.cap == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
heap.allocator.free(self.values.ptr[0..self.cap]);
|
||||
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub const empty = Self{
|
||||
.values = &.{},
|
||||
.cap = 0,
|
||||
};
|
||||
|
||||
pub fn grow(self: *Self, additional: usize) std.mem.Allocator.Error!void {
|
||||
const grown_capacity = self.values.len + additional;
|
||||
|
||||
if (grown_capacity <= self.cap) {
|
||||
return;
|
||||
}
|
||||
|
||||
const buffer = try heap.allocator.alloc(Value, grown_capacity);
|
||||
|
||||
errdefer {
|
||||
heap.allocator.deallocate(buffer);
|
||||
}
|
||||
|
||||
if (self.cap != 0) {
|
||||
@memcpy(buffer[0..self.values.len], self.values);
|
||||
heap.allocator.free(self.values.ptr[0..self.cap]);
|
||||
}
|
||||
|
||||
self.values = buffer[0..self.values.len];
|
||||
self.cap = grown_capacity;
|
||||
}
|
||||
|
||||
pub fn isEmpty(self: Self) bool {
|
||||
return self.values.len == 0;
|
||||
}
|
||||
|
||||
pub fn isFull(self: Self) bool {
|
||||
return self.values.len == self.cap;
|
||||
}
|
||||
|
||||
pub fn get(self: Self) ?*Value {
|
||||
return if (self.isEmpty()) null else &self.values[self.values.len - 1];
|
||||
}
|
||||
|
||||
pub fn pop(self: *Self) ?Value {
|
||||
if (self.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const tail_index = self.values.len - 1;
|
||||
|
||||
defer self.values = self.values[0..tail_index];
|
||||
|
||||
return self.values[tail_index];
|
||||
}
|
||||
|
||||
pub fn push(self: *Self, value: Value) bool {
|
||||
if (self.isFull()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const offset_index = self.values.len;
|
||||
|
||||
self.values = self.values.ptr[0 .. self.values.len + 1];
|
||||
self.values[offset_index] = value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn pushAll(self: *Self, values: []const Value) bool {
|
||||
const new_length = self.values.len + values.len;
|
||||
|
||||
if (new_length > self.cap) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const offset_index = self.values.len;
|
||||
|
||||
self.values = self.values.ptr[0..new_length];
|
||||
|
||||
for (0..values.len) |index| {
|
||||
self.values[offset_index + index] = values[index];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn pushGrow(self: *Self, value: Value) std.mem.Allocator.Error!void {
|
||||
try self.grow(@max(1, self.values.len));
|
||||
|
||||
std.debug.assert(self.push(value));
|
||||
}
|
||||
|
||||
pub fn pushMany(self: *Self, n: usize, value: Value) bool {
|
||||
const new_length = self.values.len + n;
|
||||
|
||||
if (new_length > self.cap) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const offset_index = self.values.len;
|
||||
|
||||
self.values = self.values.ptr[0..new_length];
|
||||
|
||||
for (0..n) |index| {
|
||||
self.values[offset_index + index] = value;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn resize(self: *Self, size: usize, default_value: Value) std.mem.Allocator.Error!void {
|
||||
if (self.cap == size) {
|
||||
return;
|
||||
}
|
||||
|
||||
const values = try heap.allocator.alloc(Value, size);
|
||||
|
||||
for (0..@min(values.len, self.values.len)) |i| {
|
||||
values[i] = self.values[i];
|
||||
}
|
||||
|
||||
if (values.len > self.values.len) {
|
||||
for (self.values.len..values.len) |i| {
|
||||
values[i] = default_value;
|
||||
}
|
||||
}
|
||||
|
||||
self.values = values[0..values.len];
|
||||
self.cap = values.len;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn require(function: anytype, args: std.meta.ArgsTuple(@TypeOf(function))) switch (@typeInfo(@typeInfo(@TypeOf(function)).@"fn".return_type.?)) {
|
||||
.error_union => |error_union| error_union.payload,
|
||||
else => @compileError("fn parameter `function` must return an error union"),
|
||||
|
@ -1,125 +0,0 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn Linked(comptime Value: type, comptime block_size: usize) type {
|
||||
return struct {
|
||||
has_head_block: ?*Block,
|
||||
has_tail_block: ?*Block,
|
||||
|
||||
const Block = struct {
|
||||
values: std.BoundedArray(Value, block_size) = .{},
|
||||
has_next: ?*Block = null,
|
||||
};
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub const Values = struct {
|
||||
has_block: ?*Block,
|
||||
block_index: std.math.IntFittingRange(0, block_size),
|
||||
|
||||
pub fn next(self: *Values) ?*Value {
|
||||
var block = self.has_block orelse {
|
||||
return null;
|
||||
};
|
||||
|
||||
if (self.block_index >= block.values.len) {
|
||||
self.has_block = block.has_next;
|
||||
self.block_index = 0;
|
||||
|
||||
block = self.has_block orelse {
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
defer {
|
||||
self.block_index += 1;
|
||||
}
|
||||
|
||||
return &block.values.slice()[self.block_index];
|
||||
}
|
||||
};
|
||||
|
||||
pub fn append(self: *Self, allocator: std.mem.Allocator, value: Value) std.mem.Allocator.Error!*Value {
|
||||
const tail_block = self.has_tail_block orelse create: {
|
||||
const block = try allocator.create(Block);
|
||||
|
||||
block.* = .{};
|
||||
self.has_head_block = block;
|
||||
self.has_tail_block = block;
|
||||
|
||||
break :create block;
|
||||
};
|
||||
|
||||
tail_block.values.append(value) catch {
|
||||
const block = try allocator.create(Block);
|
||||
|
||||
block.* = .{};
|
||||
tail_block.has_next = block;
|
||||
self.has_tail_block = block;
|
||||
|
||||
block.values.append(value) catch {
|
||||
unreachable;
|
||||
};
|
||||
};
|
||||
|
||||
return &tail_block.values.slice()[tail_block.values.len - 1];
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
||||
var blocks = self.has_head_block;
|
||||
|
||||
while (blocks) |block| {
|
||||
const has_next = block.has_next;
|
||||
|
||||
allocator.destroy(block);
|
||||
|
||||
blocks = has_next;
|
||||
}
|
||||
|
||||
self.has_head_block = undefined;
|
||||
self.has_tail_block = undefined;
|
||||
}
|
||||
|
||||
pub const empty = Self{
|
||||
.has_head_block = null,
|
||||
.has_tail_block = null,
|
||||
};
|
||||
|
||||
pub fn get(self: Self, index: usize) ?*Value {
|
||||
if (self.has_tail_block) |tail_block| {
|
||||
if (tail_block.values.len == 0) {
|
||||
std.debug.assert(self.has_head_block == self.has_tail_block);
|
||||
|
||||
return if (tail_block.values.len == 0) null else tail_block.values.slice()[index];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn isEmpty(self: Self) bool {
|
||||
return self.has_head_block == null;
|
||||
}
|
||||
|
||||
pub fn len(self: Self) usize {
|
||||
if (self.has_tail_block) |tail_block| {
|
||||
var accounted = tail_block.values.len;
|
||||
var blocks = self.has_head_block;
|
||||
|
||||
while (blocks != self.has_tail_block) : (blocks = blocks.?.has_next) {
|
||||
accounted += block_size;
|
||||
}
|
||||
|
||||
return accounted;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
pub fn values(self: *const Self) Values {
|
||||
return Values{
|
||||
.has_block = self.has_head_block,
|
||||
.block_index = 0,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
238
src/coral/stack.zig
Normal file
238
src/coral/stack.zig
Normal file
@ -0,0 +1,238 @@
|
||||
const std = @import("std");
|
||||
|
||||
fn Generic(comptime Item: type, comptime Buffer: type) type {
|
||||
return struct {
|
||||
items: Buffer,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn clear(self: *Self) void {
|
||||
self.items.len = 0;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
||||
self.items.deinit(allocator);
|
||||
}
|
||||
|
||||
pub const empty = Self{
|
||||
.items = .{},
|
||||
};
|
||||
|
||||
pub fn isEmpty(self: Self) bool {
|
||||
return self.items.len == 0;
|
||||
}
|
||||
|
||||
pub fn isFull(self: Self) bool {
|
||||
return self.items.len == self.items.cap;
|
||||
}
|
||||
|
||||
pub fn get(self: Self) ?Item {
|
||||
return if (self.isEmpty()) null else self.items.get(self.items.len - 1).?;
|
||||
}
|
||||
|
||||
pub fn pop(self: *Self) ?Item {
|
||||
if (self.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const tail_index = self.items.len - 1;
|
||||
|
||||
defer {
|
||||
self.items.len = tail_index;
|
||||
}
|
||||
|
||||
return self.items.get(tail_index).?;
|
||||
}
|
||||
|
||||
pub fn push(self: *Self, item: Item) bool {
|
||||
if (self.isFull()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const index = self.items.len;
|
||||
|
||||
self.items.len += 1;
|
||||
|
||||
std.debug.assert(self.items.set(index, item));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn pushAll(self: *Self, items: []const Item) bool {
|
||||
const new_len = self.items.len + items.len;
|
||||
|
||||
if (new_len < self.cap) {
|
||||
const index = self.items.len;
|
||||
|
||||
self.items.len = new_len;
|
||||
|
||||
std.debug.assert(self.items.sliced(index, self.items.len).copy(items));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn pushGrow(self: *Self, allocator: std.mem.Allocator, item: Item) std.mem.Allocator.Error!void {
|
||||
if (self.isFull()) {
|
||||
try self.items.grow(allocator, @max(1, self.items.cap));
|
||||
}
|
||||
|
||||
std.debug.assert(self.push(item));
|
||||
}
|
||||
|
||||
pub fn pushMany(self: *Self, n: usize, item: Item) bool {
|
||||
const new_len = self.items.len + n;
|
||||
|
||||
if (new_len > self.cap) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const offset = self.items.len;
|
||||
|
||||
self.items.len = new_len;
|
||||
|
||||
for (offset..(offset + n)) |i| {
|
||||
std.debug.assert(self.items.set(i, item));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn Parallel(comptime Item: type) type {
|
||||
const item_fields = switch (@typeInfo(Item)) {
|
||||
.@"struct" => |@"struct"| @"struct".fields,
|
||||
else => @compileError("`Item` must be a struct type"),
|
||||
};
|
||||
|
||||
const item_align = @alignOf(Item);
|
||||
const item_size = @sizeOf(usize);
|
||||
const byte_size = @sizeOf(u8);
|
||||
|
||||
return Generic(Item, struct {
|
||||
ptr: [*]align(item_align) u8 = undefined,
|
||||
len: u32 = 0,
|
||||
cap: u32 = 0,
|
||||
|
||||
pub const Field = std.meta.FieldEnum(Item);
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
||||
if (self.cap != 0) {
|
||||
allocator.free(self.ptr[0 .. item_size * self.cap]);
|
||||
}
|
||||
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn get(self: Self, index: usize) ?Item {
|
||||
if (index >= self.cap) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var ptr: [*]u8 = self.ptr;
|
||||
var item: Item = undefined;
|
||||
|
||||
inline for (item_fields) |item_field| {
|
||||
@field(item, item_field.name) = @as([*]item_field.type, @ptrCast(@alignCast(ptr)))[index];
|
||||
ptr += @sizeOf(item_field.type) * self.len;
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
pub fn grow(self: *Self, allocator: std.mem.Allocator, amount: usize) std.mem.Allocator.Error!void {
|
||||
const new_cap = std.math.cast(u32, @as(usize, self.cap + amount)) orelse {
|
||||
return error.OutOfMemory;
|
||||
};
|
||||
|
||||
const reallocation = try allocator.alignedAlloc(u8, item_align, byte_size * new_cap);
|
||||
const len_in_bytes = byte_size * self.len;
|
||||
|
||||
@memcpy(reallocation[0..len_in_bytes], self.ptr[0..len_in_bytes]);
|
||||
allocator.free(self.ptr[0 .. byte_size * self.cap]);
|
||||
|
||||
self.ptr = reallocation.ptr;
|
||||
self.cap = @intCast(new_cap);
|
||||
}
|
||||
|
||||
pub fn set(self: Self, index: usize, item: Item) bool {
|
||||
if (index >= self.cap) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var ptr: [*]u8 = self.ptr;
|
||||
|
||||
inline for (item_fields) |item_field| {
|
||||
@as([*]item_field.type, @ptrCast(@alignCast(ptr)))[index] = @field(item, item_field.name);
|
||||
ptr += @sizeOf(item_field.type) * self.len;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn slice(self: Self, comptime field: Field) []item_fields[@intFromEnum(field)].type {
|
||||
const field_index = @intFromEnum(field);
|
||||
var ptr: [*]u8 = self.ptr;
|
||||
|
||||
inline for (item_fields[0..field_index]) |item_field| {
|
||||
ptr += @sizeOf(item_field.type) * self.len;
|
||||
}
|
||||
|
||||
return @as([*]item_fields[field_index].type, @ptrCast(@alignCast(ptr)))[0..self.len];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn Sequential(comptime Item: type) type {
|
||||
return Generic(Item, struct {
|
||||
ptr: [*]Item = undefined,
|
||||
len: u32 = 0,
|
||||
cap: u32 = 0,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
||||
if (self.cap != 0) {
|
||||
allocator.free(self.ptr[0..self.cap]);
|
||||
}
|
||||
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn get(self: *Self, index: usize) ?Item {
|
||||
if (index >= self.cap) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return self.ptr[index];
|
||||
}
|
||||
|
||||
pub fn grow(self: *Self, allocator: std.mem.Allocator, amount: usize) std.mem.Allocator.Error!void {
|
||||
const new_cap = std.math.cast(u32, @as(usize, self.cap + amount)) orelse {
|
||||
return error.OutOfMemory;
|
||||
};
|
||||
|
||||
self.ptr = (try allocator.realloc(self.ptr[0..self.cap], new_cap)).ptr;
|
||||
self.cap = @intCast(new_cap);
|
||||
}
|
||||
|
||||
pub fn set(self: *Self, index: usize, value: Item) bool {
|
||||
if (index >= self.cap) {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.ptr[index] = value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn slice(self: Self) []Item {
|
||||
return self.ptr[0..self.len];
|
||||
}
|
||||
});
|
||||
}
|
@ -4,6 +4,7 @@ const std = @import("std");
|
||||
|
||||
pub fn main() void {
|
||||
ona.realtime_app
|
||||
.with(.initModule(ona.hid))
|
||||
.with(.initModule(ona.gfx))
|
||||
.run();
|
||||
}
|
||||
|
@ -1,4 +0,0 @@
|
||||
pub usingnamespace @cImport({
|
||||
@cInclude("SDL3/SDL.h");
|
||||
@cInclude("shaderc/shaderc.h");
|
||||
});
|
@ -75,6 +75,8 @@ pub fn init() std.mem.Allocator.Error!Self {
|
||||
.elapsed = 0,
|
||||
});
|
||||
|
||||
try ona.registerChannel(&self, Exit);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@ -99,7 +101,7 @@ pub fn run(self: *Self, tasks: *coral.asio.TaskQueue, comptime schedule: anytype
|
||||
}
|
||||
|
||||
pub fn setState(self: *Self, state: anytype) std.mem.Allocator.Error!void {
|
||||
var boxed_state = try coral.Box.init(state);
|
||||
var boxed_state = try coral.Box.init(coral.heap.allocator, state);
|
||||
|
||||
errdefer {
|
||||
boxed_state.deinit();
|
||||
|
@ -7,73 +7,41 @@ const ona = @import("../ona.zig");
|
||||
const std = @import("std");
|
||||
|
||||
label: [*:0]const u8,
|
||||
on_insertion: *const fn (*const Self, *ona.App, *SystemGraph) std.mem.Allocator.Error!void,
|
||||
on_run: *const fn (*ona.App) void,
|
||||
traits: ona.Traits,
|
||||
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,
|
||||
is_blocking: bool,
|
||||
|
||||
const Param = struct {
|
||||
uses_bind: bool,
|
||||
init_params: []const InitParam,
|
||||
has_bind: ?fn (*ona.App) void = null,
|
||||
traits: ona.Traits = .{},
|
||||
is_blocking: bool,
|
||||
|
||||
const InitParam = struct {
|
||||
type: type,
|
||||
type_id: *const coral.TypeId,
|
||||
is_required: bool,
|
||||
is_read_only: bool,
|
||||
traits: ona.Traits,
|
||||
};
|
||||
|
||||
fn has_fn(comptime SystemParam: type, comptime name: []const u8) ?std.builtin.Type.Fn {
|
||||
switch (@typeInfo(SystemParam)) {
|
||||
.@"struct", .@"opaque" => {},
|
||||
else => @compileError("system fn params must be either struct or opaque types"),
|
||||
}
|
||||
|
||||
if (@hasDecl(SystemParam, name)) {
|
||||
return switch (@typeInfo(@TypeOf(@field(SystemParam, name)))) {
|
||||
.@"fn" => |@"fn"| @"fn",
|
||||
else => @compileError(std.fmt.comptimePrint("system params with a .{s} declaration must be made an fn type", .{name})),
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn init(comptime fn_param: std.builtin.Type.Fn.Param) Param {
|
||||
if (fn_param.is_generic) {
|
||||
@compileError("generic params on behavior fns are disallowed");
|
||||
}
|
||||
|
||||
const init_fn = has_fn(fn_param.type.?, "init") orelse {
|
||||
@compileError("behavior params must declare a .init fn");
|
||||
};
|
||||
|
||||
if (init_fn.return_type != fn_param.type.?) {
|
||||
const param_type_name = @typeName(fn_param.type.?);
|
||||
|
||||
@compileError(std.fmt.comptimePrint("{s}.init must return {s}", .{ param_type_name, param_type_name }));
|
||||
}
|
||||
|
||||
fn getInitParams(params: []const std.builtin.Type.Fn.Param) []const InitParam {
|
||||
const init_params = struct {
|
||||
const instance = get: {
|
||||
var buffer: [init_fn.params.len]InitParam = undefined;
|
||||
var buffer: [params.len]InitParam = undefined;
|
||||
|
||||
for (&buffer, init_fn.params) |*init_param, init_fn_param| {
|
||||
if (init_fn_param.is_generic) {
|
||||
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(init_fn_param.type.?)) {
|
||||
init_param.* = switch (@typeInfo(FnParam)) {
|
||||
.pointer => |pointer| .{
|
||||
.type = pointer.child,
|
||||
.type_id = .of(pointer.child),
|
||||
.is_required = true,
|
||||
.is_read_only = pointer.is_const,
|
||||
.traits = if (@hasDecl(pointer.child, "traits") and @TypeOf(pointer.child.traits) == ona.Traits) pointer.child.traits else .{},
|
||||
},
|
||||
|
||||
.optional => |optional| switch (@typeInfo(optional.child)) {
|
||||
.pointer => |pointer| .{
|
||||
.type = pointer.child,
|
||||
.type_id = .of(pointer.child),
|
||||
.is_required = false,
|
||||
.is_read_only = pointer.is_const,
|
||||
},
|
||||
@ -89,47 +57,50 @@ const Param = struct {
|
||||
};
|
||||
};
|
||||
|
||||
var param = Param{
|
||||
.init_params = &init_params.instance,
|
||||
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");
|
||||
};
|
||||
|
||||
if (has_fn(fn_param.type.?, "bind")) |bind_fn| {
|
||||
const type_error_message = std.fmt.comptimePrint("{s}.bind must match the signature fn ({s}) !{s}", .{
|
||||
@typeName(@TypeOf(fn_param.type.?.bind)),
|
||||
@typeName(*ona.App),
|
||||
@typeName(void),
|
||||
});
|
||||
|
||||
if (bind_fn.params.len != 1) {
|
||||
@compileError(type_error_message);
|
||||
}
|
||||
|
||||
if (bind_fn.params[0].type.? != *ona.App) {
|
||||
@compileError(type_error_message);
|
||||
}
|
||||
|
||||
const binding = struct {
|
||||
fn bind(app: *ona.App) void {
|
||||
return switch (@typeInfo(bind_fn.return_type.?)) {
|
||||
.error_union => coral.require(fn_param.type.?.bind, .{app}),
|
||||
.void => fn_param.type.?.bind(app),
|
||||
else => @compileError(type_error_message),
|
||||
};
|
||||
}
|
||||
const init_fn = hasFn(FnParam, "init", null) orelse {
|
||||
@compileError(std.fmt.comptimePrint("{s} must contain a .init declaration", .{@typeName(FnParam)}));
|
||||
};
|
||||
|
||||
param.has_bind = binding.bind;
|
||||
comptime var uses_bind = false;
|
||||
|
||||
if (hasFn(FnParam, "bind", &.{})) |bind_fn| {
|
||||
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,
|
||||
}));
|
||||
}
|
||||
|
||||
if (@hasDecl(fn_param.type.?, "traits") and @TypeOf(fn_param.type.?.traits) == ona.Traits) {
|
||||
param.traits = fn_param.type.?.traits;
|
||||
uses_bind = true;
|
||||
}
|
||||
|
||||
inline for (param.init_params) |init_param| {
|
||||
param.traits = param.traits.derived(init_param.traits);
|
||||
if (init_fn.return_type != FnParam) {
|
||||
const param_type_name = @typeName(FnParam);
|
||||
|
||||
@compileError(std.fmt.comptimePrint("Fn {s}.init must return {s}", .{ param_type_name, param_type_name }));
|
||||
}
|
||||
|
||||
return param;
|
||||
return .{
|
||||
.init_params = getInitParams(init_fn.params[@intFromBool(uses_bind)..]),
|
||||
.is_blocking = usesTrait(FnParam, "blocking"),
|
||||
.uses_bind = uses_bind,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@ -138,18 +109,18 @@ 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.on_insertion(behavior, app, systems);
|
||||
try self.onInsertion(behavior, app, systems);
|
||||
try systems.depend_on_behavior(app, behavior, dependency);
|
||||
}
|
||||
|
||||
fn on_run(app: *ona.App) void {
|
||||
self.on_run(app);
|
||||
self.onRun(app);
|
||||
}
|
||||
|
||||
const instance = Self{
|
||||
.label = std.fmt.comptimePrint("({s} after {s})", .{ self.label, dependency.label }),
|
||||
.on_insertion = on_insertion,
|
||||
.on_run = on_run,
|
||||
.onInsertion = on_insertion,
|
||||
.onRun = on_run,
|
||||
};
|
||||
};
|
||||
|
||||
@ -165,13 +136,61 @@ pub fn before(comptime self: *const Self, comptime dependant: *const Self) *cons
|
||||
|
||||
const instance = Self{
|
||||
.label = std.fmt.comptimePrint("({s} before {s})", .{ self.label, dependant.label }),
|
||||
.on_insertion = on_insertion,
|
||||
.onInsertion = on_insertion,
|
||||
};
|
||||
};
|
||||
|
||||
return &afters.instance;
|
||||
}
|
||||
|
||||
fn hasFn(comptime SystemParam: type, comptime name: []const u8, comptime has_expected_param_types: ?[]const type) ?std.builtin.Type.Fn {
|
||||
switch (@typeInfo(SystemParam)) {
|
||||
.@"struct", .@"opaque" => {},
|
||||
|
||||
else => @compileError(std.fmt.comptimePrint("{s} must be a struct or opaque types", .{
|
||||
@typeName(SystemParam),
|
||||
})),
|
||||
}
|
||||
|
||||
if (@hasDecl(SystemParam, name)) {
|
||||
const named_fn = switch (@typeInfo(@TypeOf(@field(SystemParam, name)))) {
|
||||
.@"fn" => |@"fn"| @"fn",
|
||||
|
||||
else => @compileError(std.fmt.comptimePrint("{s}.{s} declaration must be made an fn type", .{
|
||||
@typeName(SystemParam),
|
||||
name,
|
||||
})),
|
||||
};
|
||||
|
||||
if (has_expected_param_types) |expected_param_types| {
|
||||
if (named_fn.params.len != expected_param_types.len) {
|
||||
@compileError(std.fmt.comptimePrint("{s}.{s} fn must accept {d} parameters, not {d}", .{
|
||||
@typeName(SystemParam),
|
||||
name,
|
||||
expected_param_types.len,
|
||||
named_fn.params.len,
|
||||
}));
|
||||
}
|
||||
|
||||
inline for (named_fn.params, expected_param_types, 1..expected_param_types.len + 1) |fn_param, ExpectedParam, i| {
|
||||
if (fn_param.type != ExpectedParam) {
|
||||
@compileError(std.fmt.comptimePrint("Parameter {d} of {s}.{s} fn must be {s}, not {s}", .{
|
||||
i,
|
||||
@typeName(SystemParam),
|
||||
name,
|
||||
@typeName(ExpectedParam),
|
||||
@typeName(fn_param.type.?),
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return named_fn;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn of(comptime call: anytype) *const Self {
|
||||
const Call = @TypeOf(call);
|
||||
|
||||
@ -181,41 +200,62 @@ pub fn of(comptime call: anytype) *const Self {
|
||||
};
|
||||
|
||||
const behaviors = struct {
|
||||
fn on_insertion(behavior: *const Self, app: *ona.App, systems: *SystemGraph) std.mem.Allocator.Error!void {
|
||||
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;
|
||||
|
||||
errdefer {
|
||||
inline for (states[states_written..]) |*state| {
|
||||
state.deinit();
|
||||
}
|
||||
}
|
||||
|
||||
inline for (call_fn.params) |call_param| {
|
||||
const behavior_param = Param.init(call_param);
|
||||
const behavior_param = comptime Param.init(call_param);
|
||||
|
||||
inline for (behavior_param.init_params) |init_param| {
|
||||
try systems.dependOnType(app, behavior, .{
|
||||
.id = .of(init_param.type),
|
||||
.id = init_param.type_id,
|
||||
.is_read_only = init_param.is_read_only,
|
||||
});
|
||||
}
|
||||
|
||||
if (behavior_param.has_bind) |bind| {
|
||||
bind(app);
|
||||
}
|
||||
}
|
||||
states[states_written] = try .init(allocator, if (behavior_param.uses_bind) call_param.type.?.bind() else {});
|
||||
states_written += 1;
|
||||
}
|
||||
|
||||
fn on_run(app: *ona.App) void {
|
||||
return allocator.dupe(coral.Box, &states);
|
||||
}
|
||||
|
||||
fn onRun(app: *ona.App, states: []const coral.Box) void {
|
||||
var call_args: std.meta.ArgsTuple(Call) = undefined;
|
||||
|
||||
inline for (&call_args, call_fn.params) |*call_arg, call_fn_param| {
|
||||
const call_param = Param.init(call_fn_param);
|
||||
var init_args: std.meta.ArgsTuple(@TypeOf(call_fn_param.type.?.init)) = undefined;
|
||||
std.debug.assert(states.len == call_args.len);
|
||||
|
||||
inline for (&init_args, call_param.init_params) |*init_arg, init_param| {
|
||||
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;
|
||||
|
||||
if (param.uses_bind) {
|
||||
init_args[0] = states[i].has(@TypeOf(init_args[0].*)).?;
|
||||
}
|
||||
|
||||
inline for (@intFromBool(param.uses_bind)..init_args.len, param.init_params) |arg_index, init_param| {
|
||||
if (init_param.is_required) {
|
||||
init_arg.* = app.hasState(init_param.type) orelse {
|
||||
@panic(std.fmt.comptimePrint("{s} is a required state but not present in the App", .{@typeName(init_param.type)}));
|
||||
const InitArg = @TypeOf(init_args[arg_index].*);
|
||||
|
||||
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 {
|
||||
init_arg.* = app.hasState(init_param.type);
|
||||
const InitArg = @TypeOf(init_args[arg_index].*);
|
||||
|
||||
init_args[arg_index] = app.hasState(InitArg);
|
||||
}
|
||||
}
|
||||
|
||||
call_arg.* = @call(.auto, call_fn_param.type.?.init, init_args);
|
||||
call_args[i] = @call(.auto, fn_param.type.?.init, init_args);
|
||||
}
|
||||
|
||||
switch (@typeInfo(call_fn.return_type.?)) {
|
||||
@ -230,20 +270,52 @@ pub fn of(comptime call: anytype) *const Self {
|
||||
|
||||
const instance = Self{
|
||||
.label = @typeName(Call),
|
||||
.on_insertion = on_insertion,
|
||||
.on_run = on_run,
|
||||
.onInsertion = onInsertion,
|
||||
.onRun = onRun,
|
||||
|
||||
.traits = derive: {
|
||||
var derived_traits = ona.Traits{};
|
||||
.is_blocking = check: {
|
||||
for (call_fn.params) |fn_param| {
|
||||
const param = Param.init(fn_param);
|
||||
|
||||
for (call_fn.params) |call_param| {
|
||||
derived_traits = derived_traits.derived(Param.init(call_param).traits);
|
||||
if (param.is_blocking) {
|
||||
break :check true;
|
||||
}
|
||||
}
|
||||
|
||||
break :derive derived_traits;
|
||||
break :check false;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
return &behaviors.instance;
|
||||
}
|
||||
|
||||
fn usesTrait(comptime SystemParam: type, name: []const u8) bool {
|
||||
if (@hasDecl(SystemParam, "traits")) {
|
||||
const Traits = @TypeOf(SystemParam.traits);
|
||||
|
||||
const traits_struct = switch (@typeInfo(Traits)) {
|
||||
.@"struct" => |@"struct"| @"struct",
|
||||
|
||||
else => @compileError(std.fmt.comptimePrint("{s}.traits must be a struct type", .{
|
||||
@typeName(SystemParam),
|
||||
})),
|
||||
};
|
||||
|
||||
if (!traits_struct.is_tuple) {
|
||||
@compileError(std.fmt.comptimePrint("{s}.traits must be a tuple", .{@typeName(SystemParam)}));
|
||||
}
|
||||
|
||||
for (traits_struct.fields) |field| {
|
||||
if (@typeInfo(field.type) != .enum_literal) {
|
||||
@compileError("All members of tuple {s}.traits must be enum literals");
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, @tagName(@field(SystemParam.traits, field.name)), name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -4,22 +4,49 @@ const ona = @import("../ona.zig");
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
dependants_edges: Map(BehaviorSet),
|
||||
edges: Map(Edge),
|
||||
processed: Map(void),
|
||||
state_readers: AccessMap,
|
||||
state_writers: AccessMap,
|
||||
blocking_work: BehaviorSet,
|
||||
parallel_work: BehaviorSet,
|
||||
parallel_work_ranges: coral.Stack(usize),
|
||||
blocking_work: WorkSet,
|
||||
parallel_work: WorkSet,
|
||||
parallel_work_ranges: coral.stack.Sequential(usize),
|
||||
|
||||
const AccessMap = coral.tree.Binary(*const coral.TypeId, BehaviorSet, coral.tree.scalarTraits(*const coral.TypeId));
|
||||
|
||||
const BehaviorSet = coral.Stack(*const ona.App.Behavior);
|
||||
const BehaviorSet = coral.stack.Sequential(*const ona.App.Behavior);
|
||||
|
||||
const Edge = struct {
|
||||
dependencies: BehaviorSet = .empty,
|
||||
param_states: []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.dependencies.deinit(allocator);
|
||||
|
||||
self.param_states = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
fn Map(comptime Payload: type) type {
|
||||
return coral.tree.Binary(*const ona.App.Behavior, Payload, coral.tree.scalarTraits(*const ona.App.Behavior));
|
||||
}
|
||||
|
||||
const Processed = struct {
|
||||
is_blocking: bool,
|
||||
};
|
||||
|
||||
const Work = struct {
|
||||
behavior: *const ona.App.Behavior,
|
||||
param_states: []const coral.Box = &.{},
|
||||
};
|
||||
|
||||
const WorkSet = coral.stack.Sequential(Work);
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub const RunError = error{
|
||||
@ -33,15 +60,15 @@ pub const TypeDependency = struct {
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.processed.deinit(coral.heap.allocator);
|
||||
self.parallel_work.deinit();
|
||||
self.parallel_work_ranges.deinit();
|
||||
self.blocking_work.deinit();
|
||||
self.parallel_work.deinit(coral.heap.allocator);
|
||||
self.parallel_work_ranges.deinit(coral.heap.allocator);
|
||||
self.blocking_work.deinit(coral.heap.allocator);
|
||||
|
||||
inline for (.{ &self.dependants_edges, &self.state_readers, &self.state_writers }) |map| {
|
||||
inline for (.{ &self.edges, &self.state_readers, &self.state_writers }) |map| {
|
||||
var key_values = map.keyValues();
|
||||
|
||||
while (key_values.nextValue()) |value| {
|
||||
value.deinit();
|
||||
value.deinit(coral.heap.allocator);
|
||||
}
|
||||
|
||||
map.deinit(coral.heap.allocator);
|
||||
@ -51,56 +78,56 @@ pub fn deinit(self: *Self) void {
|
||||
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.dependants_edges.get(dependant) orelse (try self.dependants_edges.insert(coral.heap.allocator, dependant, .empty)).?;
|
||||
const edges = self.edges.get(dependant) orelse (try self.edges.insert(coral.heap.allocator, dependant, .{})).?;
|
||||
|
||||
if (std.mem.indexOfScalar(*const ona.App.Behavior, edges.values, dependency) == null) {
|
||||
try edges.pushGrow(dependency);
|
||||
if (std.mem.indexOfScalar(*const ona.App.Behavior, edges.dependencies.items.slice(), dependency) == null) {
|
||||
try edges.dependencies.pushGrow(coral.heap.allocator, dependency);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dependOnType(self: *Self, app: *ona.App, dependant: *const ona.App.Behavior, dependency: TypeDependency) std.mem.Allocator.Error!void {
|
||||
const readers = self.state_readers.get(dependency.id) orelse (try self.state_readers.insert(coral.heap.allocator, dependency.id, .empty)).?;
|
||||
|
||||
if (std.mem.indexOfScalar(*const ona.App.Behavior, readers.values, dependant)) |index| {
|
||||
for (readers.values[0..index]) |reader| {
|
||||
if (std.mem.indexOfScalar(*const ona.App.Behavior, readers.items.slice(), dependant)) |index| {
|
||||
for (readers.items.slice()[0..index]) |reader| {
|
||||
try self.dependOnBehavior(app, dependant, reader);
|
||||
}
|
||||
|
||||
for (readers.values[index + 1 ..]) |reader| {
|
||||
for (readers.items.slice()[index + 1 ..]) |reader| {
|
||||
try self.dependOnBehavior(app, dependant, reader);
|
||||
}
|
||||
} else {
|
||||
for (readers.values) |reader| {
|
||||
for (readers.items.slice()) |reader| {
|
||||
try self.dependOnBehavior(app, dependant, reader);
|
||||
}
|
||||
|
||||
try readers.pushGrow(dependant);
|
||||
try readers.pushGrow(coral.heap.allocator, dependant);
|
||||
}
|
||||
|
||||
if (!dependency.is_read_only) {
|
||||
const writers = self.state_writers.get(dependency.id) orelse (try self.state_writers.insert(coral.heap.allocator, dependency.id, .empty)).?;
|
||||
|
||||
if (std.mem.indexOfScalar(*const ona.App.Behavior, writers.values, dependant)) |index| {
|
||||
for (writers.values[0..index]) |reader| {
|
||||
if (std.mem.indexOfScalar(*const ona.App.Behavior, writers.items.slice(), dependant)) |index| {
|
||||
for (writers.items.slice()[0..index]) |reader| {
|
||||
try self.dependOnBehavior(app, dependant, reader);
|
||||
}
|
||||
|
||||
for (writers.values[index..]) |reader| {
|
||||
for (writers.items.slice()[index..]) |reader| {
|
||||
try self.dependOnBehavior(app, dependant, reader);
|
||||
}
|
||||
} else {
|
||||
for (writers.values) |reader| {
|
||||
for (writers.items.slice()) |reader| {
|
||||
try self.dependOnBehavior(app, dependant, reader);
|
||||
}
|
||||
|
||||
try writers.pushGrow(dependant);
|
||||
try writers.pushGrow(coral.heap.allocator, dependant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const empty = Self{
|
||||
.edges = .empty,
|
||||
.processed = .empty,
|
||||
.dependants_edges = .empty,
|
||||
.state_readers = .empty,
|
||||
.state_writers = .empty,
|
||||
.blocking_work = .empty,
|
||||
@ -111,36 +138,43 @@ pub const empty = Self{
|
||||
pub fn insert(self: *Self, app: *ona.App, behavior: *const ona.App.Behavior) std.mem.Allocator.Error!void {
|
||||
self.processed.clear();
|
||||
|
||||
if (try self.dependants_edges.insert(coral.heap.allocator, behavior, .empty) != null) {
|
||||
try behavior.on_insertion(behavior, app, self);
|
||||
if (try self.edges.insert(coral.heap.allocator, behavior, .{})) |edge| {
|
||||
edge.param_states = try behavior.onInsertion(behavior, coral.heap.allocator, app, self);
|
||||
}
|
||||
}
|
||||
|
||||
fn parallelRun(app: *ona.App, behaviors: []const *const ona.App.Behavior) void {
|
||||
for (behaviors) |behavior| {
|
||||
behavior.on_run(app);
|
||||
fn parallelRun(app: *ona.App, works: []const Work) void {
|
||||
for (works) |work| {
|
||||
work.behavior.onRun(app, work.param_states);
|
||||
}
|
||||
}
|
||||
|
||||
fn process(self: *Self, behavior: *const ona.App.Behavior, dependencies: BehaviorSet) !ona.Traits {
|
||||
var inherited_traits = behavior.traits;
|
||||
fn process(self: *Self, behavior: *const ona.App.Behavior, edge: Edge) !Processed {
|
||||
var processed = Processed{ .is_blocking = behavior.is_blocking };
|
||||
|
||||
for (dependencies.values) |dependency| {
|
||||
const traits = try self.process(dependency, (self.dependants_edges.get(dependency) orelse {
|
||||
for (edge.dependencies.items.slice()) |dependency| {
|
||||
const processed_dependency = try self.process(dependency, (self.edges.get(dependency) orelse {
|
||||
return error.MissingDependency;
|
||||
}).*);
|
||||
|
||||
inherited_traits = inherited_traits.derived(traits);
|
||||
processed.is_blocking = processed.is_blocking or processed_dependency.is_blocking;
|
||||
}
|
||||
|
||||
if (try self.processed.insert(coral.heap.allocator, behavior, {}) != null) {
|
||||
try switch (inherited_traits.is_thread_unsafe) {
|
||||
true => self.blocking_work.pushGrow(behavior),
|
||||
false => self.parallel_work.pushGrow(behavior),
|
||||
try switch (processed.is_blocking) {
|
||||
true => self.blocking_work.pushGrow(coral.heap.allocator, .{
|
||||
.behavior = behavior,
|
||||
.param_states = edge.param_states,
|
||||
}),
|
||||
|
||||
false => self.parallel_work.pushGrow(coral.heap.allocator, .{
|
||||
.behavior = behavior,
|
||||
.param_states = edge.param_states,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
return inherited_traits;
|
||||
return processed;
|
||||
}
|
||||
|
||||
pub fn run(self: *Self, app: *ona.App, tasks: *coral.asio.TaskQueue) (std.mem.Allocator.Error || RunError)!void {
|
||||
@ -153,19 +187,19 @@ pub fn run(self: *Self, app: *ona.App, tasks: *coral.asio.TaskQueue) (std.mem.Al
|
||||
self.parallel_work.clear();
|
||||
self.parallel_work_ranges.clear();
|
||||
|
||||
var dependants_edges = self.dependants_edges.keyValues();
|
||||
var dependants_edges = self.edges.keyValues();
|
||||
|
||||
while (dependants_edges.next()) |dependants_edge| {
|
||||
const parallel_work_offset = self.parallel_work.values.len;
|
||||
const flags = try self.process(dependants_edge.key, dependants_edge.value.*);
|
||||
const parallel_work_offset = self.parallel_work.items.len;
|
||||
const processed = try self.process(dependants_edge.key, dependants_edge.value.*);
|
||||
|
||||
if (flags.is_thread_unsafe) {
|
||||
std.debug.assert(parallel_work_offset == self.parallel_work.values.len);
|
||||
if (processed.is_blocking) {
|
||||
std.debug.assert(parallel_work_offset == self.parallel_work.items.len);
|
||||
} else {
|
||||
const parallel_work_added = self.parallel_work.values.len - parallel_work_offset;
|
||||
const parallel_work_added = self.parallel_work.items.len - parallel_work_offset;
|
||||
|
||||
if (parallel_work_added != 0) {
|
||||
try self.parallel_work_ranges.pushGrow(parallel_work_added);
|
||||
try self.parallel_work_ranges.pushGrow(coral.heap.allocator, parallel_work_added);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -173,8 +207,8 @@ pub fn run(self: *Self, app: *ona.App, tasks: *coral.asio.TaskQueue) (std.mem.Al
|
||||
|
||||
var parallel_work_offset: usize = 0;
|
||||
|
||||
for (self.parallel_work_ranges.values) |parallel_work_range| {
|
||||
const work = self.parallel_work.values[parallel_work_offset .. parallel_work_offset + parallel_work_range];
|
||||
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];
|
||||
|
||||
try tasks.execute(coral.asio.CallTask(parallelRun).init(.{ app, work }));
|
||||
|
||||
@ -183,7 +217,7 @@ pub fn run(self: *Self, app: *ona.App, tasks: *coral.asio.TaskQueue) (std.mem.Al
|
||||
|
||||
tasks.finish();
|
||||
|
||||
for (self.blocking_work.values) |behavior| {
|
||||
behavior.on_run(app);
|
||||
for (self.blocking_work.items.slice()) |work| {
|
||||
work.behavior.onRun(app, work.param_states);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,10 @@ const builtin = @import("builtin");
|
||||
|
||||
const coral = @import("coral");
|
||||
|
||||
const ext = @import("ext");
|
||||
const ext = @cImport({
|
||||
@cInclude("SDL3/SDL.h");
|
||||
@cInclude("shaderc/shaderc.h");
|
||||
});
|
||||
|
||||
const glsl = @import("./gfx/glsl.zig");
|
||||
|
||||
@ -29,7 +32,7 @@ const Context = struct {
|
||||
const injected_source = try glsl.inject(frame_allocator, source, 430, injections);
|
||||
|
||||
const result = ext.shaderc_compile_into_spv(self.shader_compiler, injected_source.ptr, injected_source.len, switch (kind) {
|
||||
.fragment => ext.shaderc_glsl_vertex_shader,
|
||||
.fragment => ext.shaderc_glsl_fragment_shader,
|
||||
.vertex => ext.shaderc_glsl_vertex_shader,
|
||||
}, name, "main", self.spirv_options) orelse {
|
||||
return error.OutOfMemory;
|
||||
@ -88,7 +91,7 @@ pub const Effect = struct {
|
||||
pipeline: *ext.SDL_GPUGraphicsPipeline,
|
||||
};
|
||||
|
||||
fn compile_shaders(context: ona.Write(Context)) !void {
|
||||
fn compile_shaders(context: ona.Exclusive(Context), _: ona.Assets(Effect)) !void {
|
||||
const Camera = extern struct {
|
||||
projection: [4]@Vector(4, f32),
|
||||
};
|
||||
@ -122,6 +125,7 @@ fn compile_shaders(context: ona.Write(Context)) !void {
|
||||
.code = vertex_spirv.ptr,
|
||||
.code_size = vertex_spirv.len,
|
||||
.format = ext.SDL_GPU_SHADERFORMAT_SPIRV,
|
||||
.stage = ext.SDL_GPU_SHADERSTAGE_VERTEX,
|
||||
.entrypoint = "main",
|
||||
});
|
||||
|
||||
@ -133,69 +137,28 @@ fn compile_shaders(context: ona.Write(Context)) !void {
|
||||
.code = fragment_spirv.ptr,
|
||||
.code_size = fragment_spirv.len,
|
||||
.format = ext.SDL_GPU_SHADERFORMAT_SPIRV,
|
||||
.stage = ext.SDL_GPU_SHADERSTAGE_FRAGMENT,
|
||||
.entrypoint = "main",
|
||||
});
|
||||
|
||||
defer {
|
||||
ext.SDL_ReleaseGPUShader(context.ptr.gpu_device, effect_fragment);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll(exit: ona.Send(ona.App.Exit), hid_events: ona.Send(ona.hid.Event)) !void {
|
||||
var event: ext.SDL_Event = undefined;
|
||||
|
||||
while (ext.SDL_PollEvent(&event)) {
|
||||
try hid_events.push(switch (event.type) {
|
||||
ext.SDL_EVENT_QUIT => {
|
||||
return exit.push(.success);
|
||||
},
|
||||
|
||||
ext.SDL_EVENT_KEY_UP => .{ .key_up = @enumFromInt(event.key.scancode) },
|
||||
ext.SDL_EVENT_KEY_DOWN => .{ .key_down = @enumFromInt(event.key.scancode) },
|
||||
|
||||
ext.SDL_EVENT_MOUSE_BUTTON_UP => .{
|
||||
.mouse_up = switch (event.button.button) {
|
||||
ext.SDL_BUTTON_LEFT => .left,
|
||||
ext.SDL_BUTTON_RIGHT => .right,
|
||||
ext.SDL_BUTTON_MIDDLE => .middle,
|
||||
|
||||
else => {
|
||||
continue;
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
ext.SDL_EVENT_MOUSE_BUTTON_DOWN => .{
|
||||
.mouse_down = switch (event.button.button) {
|
||||
ext.SDL_BUTTON_LEFT => .left,
|
||||
ext.SDL_BUTTON_RIGHT => .right,
|
||||
ext.SDL_BUTTON_MIDDLE => .middle,
|
||||
|
||||
else => {
|
||||
continue;
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
ext.SDL_EVENT_MOUSE_MOTION => .{
|
||||
.mouse_motion = .{
|
||||
.relative_position = .{ event.motion.xrel, event.motion.yrel },
|
||||
.absolute_position = .{ event.motion.x, event.motion.y },
|
||||
},
|
||||
},
|
||||
|
||||
else => {
|
||||
continue;
|
||||
},
|
||||
});
|
||||
}
|
||||
// _ = try effects.insert(.{
|
||||
// .pipeline = ext.SDL_CreateGPUGraphicsPipeline(context.ptr.gpu_device, &.{
|
||||
// .vertex_shader = effect_vertex,
|
||||
// .fragment_shader = effect_fragment,
|
||||
// .primitive_type = ext.SDL_GPU_PRIMITIVETYPE_TRIANGLELIST,
|
||||
// }).?,
|
||||
// });
|
||||
}
|
||||
|
||||
pub fn prepare(display: ona.Write(Display)) void {
|
||||
display.ptr.is_hidden = false;
|
||||
}
|
||||
|
||||
pub fn render(context: ona.Read(Context)) !void {
|
||||
pub fn render(context: ona.Exclusive(Context)) !void {
|
||||
const commands = ext.SDL_AcquireGPUCommandBuffer(context.ptr.gpu_device) orelse {
|
||||
return error.SdlFailure;
|
||||
};
|
||||
@ -204,16 +167,23 @@ pub fn render(context: ona.Read(Context)) !void {
|
||||
_ = !ext.SDL_CancelGPUCommandBuffer(commands);
|
||||
}
|
||||
|
||||
var swapchain_texture: *ext.SDL_Texture = undefined;
|
||||
var has_swapchain_texture: ?*ext.SDL_GPUTexture = null;
|
||||
var sawpchain_width, var swapchain_height = [2]u32{ 0, 0 };
|
||||
|
||||
if (!ext.SDL_WaitAndAcquireGPUSwapchainTexture(commands, context.ptr.window, @ptrCast(&swapchain_texture), null, null)) {
|
||||
if (!ext.SDL_WaitAndAcquireGPUSwapchainTexture(
|
||||
commands,
|
||||
context.ptr.window,
|
||||
&has_swapchain_texture,
|
||||
&sawpchain_width,
|
||||
&swapchain_height,
|
||||
)) {
|
||||
return error.SdlFailure;
|
||||
}
|
||||
|
||||
{
|
||||
if (has_swapchain_texture) |swapchain_texture| {
|
||||
const color_targets = [_]ext.SDL_GPUColorTargetInfo{
|
||||
.{
|
||||
.texture = @ptrCast(swapchain_texture),
|
||||
.texture = swapchain_texture,
|
||||
.load_op = ext.SDL_GPU_LOADOP_CLEAR,
|
||||
.clear_color = .{ .r = 0, .g = 0.2, .b = 0.4, .a = 1.0 },
|
||||
},
|
||||
@ -288,6 +258,8 @@ pub fn setup(app: *ona.App) !void {
|
||||
.ReleaseFast => ext.shaderc_optimization_level_performance,
|
||||
});
|
||||
|
||||
try ona.registerAsset(app, Effect);
|
||||
|
||||
try app.setState(Context{
|
||||
.frame_arena = .init(coral.heap.allocator),
|
||||
.shader_compiler = shader_compiler,
|
||||
@ -298,12 +270,11 @@ pub fn setup(app: *ona.App) !void {
|
||||
|
||||
try app.on(.load, .of(prepare));
|
||||
try app.on(.load, .of(compile_shaders));
|
||||
try app.on(.post_update, .of(poll));
|
||||
try app.on(.render, .of(render));
|
||||
try app.on(.finish, .of(synchronize));
|
||||
}
|
||||
|
||||
pub fn synchronize(context: ona.Write(Context), display: ona.Read(Display)) !void {
|
||||
pub fn synchronize(context: ona.Exclusive(Context), display: ona.Read(Display)) !void {
|
||||
const window_flags = ext.SDL_GetWindowFlags(context.ptr.window);
|
||||
const is_window_hidden = (window_flags & ext.SDL_WINDOW_HIDDEN) != 0;
|
||||
|
||||
|
@ -1,3 +1,7 @@
|
||||
const ext = @cImport({
|
||||
@cInclude("SDL3/SDL.h");
|
||||
});
|
||||
|
||||
const ona = @import("ona.zig");
|
||||
|
||||
pub const Event = union(enum) {
|
||||
@ -195,3 +199,58 @@ pub const KeyScancode = enum(u32) {
|
||||
right_gui = 0xE7,
|
||||
_,
|
||||
};
|
||||
|
||||
pub fn poll(exit: ona.Send(ona.App.Exit), hid_events: ona.Send(ona.hid.Event)) !void {
|
||||
var event: ext.SDL_Event = undefined;
|
||||
|
||||
while (ext.SDL_PollEvent(&event)) {
|
||||
try hid_events.push(switch (event.type) {
|
||||
ext.SDL_EVENT_QUIT => {
|
||||
return exit.push(.success);
|
||||
},
|
||||
|
||||
ext.SDL_EVENT_KEY_UP => .{ .key_up = @enumFromInt(event.key.scancode) },
|
||||
ext.SDL_EVENT_KEY_DOWN => .{ .key_down = @enumFromInt(event.key.scancode) },
|
||||
|
||||
ext.SDL_EVENT_MOUSE_BUTTON_UP => .{
|
||||
.mouse_up = switch (event.button.button) {
|
||||
ext.SDL_BUTTON_LEFT => .left,
|
||||
ext.SDL_BUTTON_RIGHT => .right,
|
||||
ext.SDL_BUTTON_MIDDLE => .middle,
|
||||
|
||||
else => {
|
||||
continue;
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
ext.SDL_EVENT_MOUSE_BUTTON_DOWN => .{
|
||||
.mouse_down = switch (event.button.button) {
|
||||
ext.SDL_BUTTON_LEFT => .left,
|
||||
ext.SDL_BUTTON_RIGHT => .right,
|
||||
ext.SDL_BUTTON_MIDDLE => .middle,
|
||||
|
||||
else => {
|
||||
continue;
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
ext.SDL_EVENT_MOUSE_MOTION => .{
|
||||
.mouse_motion = .{
|
||||
.relative_position = .{ event.motion.xrel, event.motion.yrel },
|
||||
.absolute_position = .{ event.motion.x, event.motion.y },
|
||||
},
|
||||
},
|
||||
|
||||
else => {
|
||||
continue;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup(app: *ona.App) !void {
|
||||
try app.on(.post_update, .of(poll));
|
||||
try ona.registerChannel(app, Event);
|
||||
}
|
||||
|
192
src/ona/ona.zig
192
src/ona/ona.zig
@ -1,7 +1,5 @@
|
||||
pub const App = @import("./App.zig");
|
||||
|
||||
pub const Assets = @import("./Assets.zig");
|
||||
|
||||
const coral = @import("coral");
|
||||
|
||||
pub const gfx = @import("./gfx.zig");
|
||||
@ -10,24 +8,133 @@ pub const hid = @import("./hid.zig");
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
pub const AssetPath = struct { u32 };
|
||||
|
||||
pub fn Assets(comptime Asset: type) type {
|
||||
return struct {
|
||||
queue: *Queue,
|
||||
store: *Store,
|
||||
|
||||
pub const Handle = packed struct {
|
||||
index: u32,
|
||||
salt: u32,
|
||||
};
|
||||
|
||||
const Queue = coral.stack.Sequential(struct {
|
||||
asset_path: AssetPath,
|
||||
reserved_handle: Handle,
|
||||
});
|
||||
|
||||
const Self = @This();
|
||||
|
||||
const State = struct {
|
||||
queue: Queue,
|
||||
};
|
||||
|
||||
pub const Store = struct {
|
||||
released_handles: coral.stack.Sequential(Handle) = .empty,
|
||||
|
||||
assets: coral.stack.Parallel(struct {
|
||||
asset: Asset,
|
||||
state: AssetState,
|
||||
}) = .empty,
|
||||
|
||||
const AssetState = struct {
|
||||
usage: enum(u32) { vacant, reserved, occupied },
|
||||
salt: u32,
|
||||
};
|
||||
|
||||
pub fn reserve(self: *Store) std.mem.Allocator.Error!Handle {
|
||||
if (self.released_handles.pop()) |handle| {
|
||||
const state = &self.assets.items.slice(.state)[handle.index];
|
||||
|
||||
std.debug.assert(state.usage == .vacant);
|
||||
std.debug.assert(state.salt == handle.salt);
|
||||
|
||||
state.usage = .reserved;
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
const handle = Handle{
|
||||
.index = self.assets.items.len,
|
||||
.salt = 1,
|
||||
};
|
||||
|
||||
try self.assets.pushGrow(coral.heap.allocator, .{
|
||||
.asset = undefined,
|
||||
|
||||
.state = .{
|
||||
.usage = .reserved,
|
||||
.salt = handle.salt,
|
||||
},
|
||||
});
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
pub fn resolve(self: *Store, reserved_handle: Handle, asset: Asset) bool {
|
||||
const state = self.assets.items.slice(.state)[reserved_handle.index];
|
||||
|
||||
if (state.usage != .reserved) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (reserved_handle.salt != state.salt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.assets.items.slice(.asset)[reserved_handle.index] = asset;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn bind() State {
|
||||
return .{
|
||||
.queue = .empty,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn init(state: *State, store: *Store) Self {
|
||||
return .{
|
||||
.store = store,
|
||||
.queue = &state.queue,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn insert(self: Self, asset: Asset) std.mem.Allocator.Error!Handle {
|
||||
const reserved_handle = try self.store.reserve();
|
||||
|
||||
std.debug.assert(self.store.resolve(reserved_handle, asset));
|
||||
|
||||
return reserved_handle;
|
||||
}
|
||||
|
||||
pub fn load(self: Self, path: AssetPath) std.mem.Allocator.Error!Handle {
|
||||
const reserved_handle = try self.store.reserve();
|
||||
|
||||
try self.queue.pushGrow(coral.heap.allocator, .{
|
||||
.asset_path = path,
|
||||
.reserved_handle = reserved_handle,
|
||||
});
|
||||
|
||||
return reserved_handle;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn Channel(comptime Message: type) type {
|
||||
return struct {
|
||||
buffers: [2]coral.Stack(Message) = .{ .empty, .empty },
|
||||
buffers: [2]coral.stack.Sequential(Message) = .{ .empty, .empty },
|
||||
swap_index: u1 = 0,
|
||||
ticks: u1 = 0,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn bind(app: *App) std.mem.Allocator.Error!void {
|
||||
if (app.hasState(Self) == null) {
|
||||
try app.setState(Self{});
|
||||
try app.on(.post_update, .of(swap));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
for (&self.buffers) |*buffer| {
|
||||
buffer.deinit();
|
||||
buffer.deinit(coral.heap.allocator);
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,15 +149,31 @@ fn Channel(comptime Message: type) type {
|
||||
}
|
||||
|
||||
fn messages(self: Self) []const Message {
|
||||
return self.buffers[self.swap_index ^ 1].values;
|
||||
return self.buffers[self.swap_index ^ 1].items.slice();
|
||||
}
|
||||
|
||||
fn push(self: *Self, message: Message) std.mem.Allocator.Error!void {
|
||||
try self.buffers[self.swap_index].pushGrow(message);
|
||||
try self.buffers[self.swap_index].pushGrow(coral.heap.allocator, message);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn Exclusive(comptime State: type) type {
|
||||
return struct {
|
||||
ptr: *State,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn init(state: *State) Self {
|
||||
return .{
|
||||
.ptr = state,
|
||||
};
|
||||
}
|
||||
|
||||
pub const traits = .{.blocking};
|
||||
};
|
||||
}
|
||||
|
||||
pub fn Read(comptime State: type) type {
|
||||
return struct {
|
||||
ptr: *const State,
|
||||
@ -66,38 +189,36 @@ pub fn Read(comptime State: type) type {
|
||||
}
|
||||
|
||||
pub fn Receive(comptime Message: type) type {
|
||||
const TypedChannel = Channel(Message);
|
||||
const MessageChannel = Channel(Message);
|
||||
|
||||
return struct {
|
||||
channel: *const TypedChannel,
|
||||
has_channel: ?*const MessageChannel,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub const bind = TypedChannel.bind;
|
||||
|
||||
pub fn init(channel: *const TypedChannel) Self {
|
||||
pub fn init(channel: ?*const MessageChannel) Self {
|
||||
return .{
|
||||
.channel = channel,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn messages(self: Self) []const Message {
|
||||
return self.channel.messages();
|
||||
if (self.has_channel) |channel| {
|
||||
return channel.messages();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn Send(comptime Message: type) type {
|
||||
const TypedChannel = Channel(Message);
|
||||
const MessageChannel = Channel(Message);
|
||||
|
||||
return struct {
|
||||
channel: *TypedChannel,
|
||||
channel: *MessageChannel,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub const bind = TypedChannel.bind;
|
||||
|
||||
pub fn init(channel: *TypedChannel) Self {
|
||||
pub fn init(channel: *MessageChannel) Self {
|
||||
return .{
|
||||
.channel = channel,
|
||||
};
|
||||
@ -109,16 +230,6 @@ pub fn Send(comptime Message: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
pub const Traits = packed struct {
|
||||
is_thread_unsafe: bool = false,
|
||||
|
||||
pub fn derived(self: Traits, other: Traits) Traits {
|
||||
return .{
|
||||
.is_thread_unsafe = self.is_thread_unsafe or other.is_thread_unsafe,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub fn Write(comptime State: type) type {
|
||||
return struct {
|
||||
ptr: *State,
|
||||
@ -135,6 +246,19 @@ pub fn Write(comptime State: type) type {
|
||||
|
||||
pub const realtime_app = App.Setup.init(run_realtime_loop);
|
||||
|
||||
pub fn registerAsset(app: *App, comptime Asset: type) std.mem.Allocator.Error!void {
|
||||
const AssetStore = Assets(Asset).Store;
|
||||
|
||||
try app.setState(AssetStore{});
|
||||
}
|
||||
|
||||
pub fn registerChannel(app: *App, comptime Message: type) std.mem.Allocator.Error!void {
|
||||
const MessageChannel = Channel(Message);
|
||||
|
||||
try app.on(.post_update, .of(MessageChannel.swap));
|
||||
try app.setState(MessageChannel{});
|
||||
}
|
||||
|
||||
fn run_realtime_loop(app: *App) !void {
|
||||
// ext.SDL_SetLogPriorities(ext.SDL_LOG_PRIORITY_VERBOSE);
|
||||
// ext.SDL_SetLogOutputFunction(sdl_log, null);
|
||||
|
Loading…
x
Reference in New Issue
Block a user