Back the GUI canvas atop a slot map
This commit is contained in:
parent
7e8691c050
commit
4e3263b352
|
@ -12,20 +12,33 @@ pub const main = ona.App.game
|
||||||
.with_module(gui)
|
.with_module(gui)
|
||||||
.with_system(.load, ona.system_fn(load), .{ .label = "Hello Ona" }).build();
|
.with_system(.load, ona.system_fn(load), .{ .label = "Hello Ona" }).build();
|
||||||
|
|
||||||
pub fn load(canvas: ona.Write(gui.Canvas)) !void {
|
fn load(canvas: ona.Write(gui.Canvas)) !void {
|
||||||
const item = try canvas.state.append("", .{
|
const item = try canvas.state.append("", .{
|
||||||
.left = 0,
|
.x = 0,
|
||||||
.top = 0,
|
.y = 0,
|
||||||
.width = 256,
|
.width = 256,
|
||||||
.height = 256,
|
.height = 256,
|
||||||
});
|
});
|
||||||
|
|
||||||
canvas.state.set_block(item, .{
|
try canvas.state.set_block(item, .{
|
||||||
.has_background = .default,
|
|
||||||
.has_color = gfx.colors.purple,
|
.has_color = gfx.colors.purple,
|
||||||
});
|
});
|
||||||
|
|
||||||
try canvas.state.set_label(item, .{
|
try canvas.state.set_label(item, .{
|
||||||
.has_text = "Hello, world",
|
.has_text = "Hello, world",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const item2 = try canvas.state.append("", .{
|
||||||
|
.x = 128,
|
||||||
|
.y = 128,
|
||||||
|
.width = 256,
|
||||||
|
.height = 256,
|
||||||
|
});
|
||||||
|
|
||||||
|
try canvas.state.set_block(item2, .{
|
||||||
|
.has_color = .{0, 1, 0, 1},
|
||||||
|
});
|
||||||
|
|
||||||
|
canvas.state.set_parent(item2, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ fn update(effects: ona.Write(Effects), loop: ona.Read(ona.Loop)) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(commands: gfx.Commands(.background), effects: ona.Write(Effects), loop: ona.Read(ona.Loop), display: ona.Write(gfx.Display)) !void {
|
fn render(commands: gfx.Commands, effects: ona.Write(Effects), loop: ona.Read(ona.Loop), display: ona.Write(gfx.Display)) !void {
|
||||||
try commands.set_target(.{
|
try commands.set_target(.{
|
||||||
.texture = effects.state.render_texture,
|
.texture = effects.state.render_texture,
|
||||||
.clear_color = gfx.colors.black,
|
.clear_color = gfx.colors.black,
|
||||||
|
|
|
@ -6,43 +6,46 @@ const ona = @import("ona");
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const Spawned = struct {
|
const Visuals = struct {
|
||||||
visual: struct {
|
spawned: SpawnMap = SpawnMap.init(ona.heap.allocator),
|
||||||
|
random: std.Random.Xoroshiro128,
|
||||||
|
mouse_position: @Vector(2, f32) = @splat(0),
|
||||||
|
|
||||||
|
const SpawnMap = ona.SlotMap(struct {
|
||||||
color: gfx.Color,
|
color: gfx.Color,
|
||||||
position: gfx.Vector2,
|
position: gfx.Vector2,
|
||||||
rotation: f32,
|
rotation: f32,
|
||||||
},
|
lifetime_seconds: f32,
|
||||||
|
});
|
||||||
lifetime_seconds: f32,
|
|
||||||
};
|
|
||||||
|
|
||||||
const Visuals = struct {
|
|
||||||
spawned: ona.stack.Parallel(Spawned) = .{},
|
|
||||||
random: std.Random.Xoroshiro128,
|
|
||||||
mouse_position: @Vector(2, f32) = @splat(0),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn cleanup(visuals: ona.Write(Visuals)) !void {
|
fn cleanup(visuals: ona.Write(Visuals)) !void {
|
||||||
visuals.state.spawned.deinit();
|
visuals.state.spawned.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load(visuals: ona.Write(Visuals)) !void {
|
|
||||||
const initial_spawn_capacity = 1024;
|
|
||||||
|
|
||||||
try visuals.state.spawned.grow(initial_spawn_capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const main = ona.App.game
|
pub const main = ona.App.game
|
||||||
.with_module(gfx)
|
.with_module(gfx)
|
||||||
.with_module(hid)
|
.with_module(hid)
|
||||||
.with_state(Visuals{.random = std.Random.Xoroshiro128.init(47342563891212)})
|
.with_state(Visuals{.random = std.Random.Xoroshiro128.init(47342563891212)})
|
||||||
.with_system(.load, ona.system_fn(load), .{.label = "load visuals"})
|
|
||||||
.with_system(.update, ona.system_fn(update), .{.label = "spawn visuals"})
|
.with_system(.update, ona.system_fn(update), .{.label = "spawn visuals"})
|
||||||
.with_system(.render, ona.system_fn(render), .{.label = "render visuals"})
|
.with_system(.render, ona.system_fn(render), .{.label = "render visuals"})
|
||||||
.with_system(.exit, ona.system_fn(cleanup), .{.label = "clean up visuals"}).build();
|
.with_system(.exit, ona.system_fn(cleanup), .{.label = "clean up visuals"}).build();
|
||||||
|
|
||||||
fn update(visuals: ona.Write(Visuals), events: ona.Receive(hid.Event), display: ona.Read(gfx.Display)) !void {
|
fn update(visuals: ona.Write(Visuals), events: ona.Receive(hid.Event), display: ona.Read(gfx.Display)) !void {
|
||||||
update_spawned(&visuals.state.spawned);
|
const float_speed = 6;
|
||||||
|
|
||||||
|
for (0 .. visuals.state.spawned.next) |i| {
|
||||||
|
const spawned = visuals.state.spawned.get(i) orelse {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
spawned.lifetime_seconds -= 1.0 / 60.0;
|
||||||
|
spawned.position -= .{0, float_speed};
|
||||||
|
|
||||||
|
if (spawned.lifetime_seconds <= 0) {
|
||||||
|
std.debug.assert(visuals.state.spawned.remove(i) != null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const random = visuals.state.random.random();
|
const random = visuals.state.random.random();
|
||||||
const width: f32 = @floatFromInt(display.state.width);
|
const width: f32 = @floatFromInt(display.state.width);
|
||||||
|
@ -51,26 +54,20 @@ fn update(visuals: ona.Write(Visuals), events: ona.Receive(hid.Event), display:
|
||||||
for (events.messages()) |event| {
|
for (events.messages()) |event| {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
.key_down => {
|
.key_down => {
|
||||||
try visuals.state.spawned.push_grow(.{
|
_ = try visuals.state.spawned.insert(.{
|
||||||
.lifetime_seconds = 2.5 + (5 * random.float(f32)),
|
.lifetime_seconds = 2.5 + (5 * random.float(f32)),
|
||||||
|
.color = .{random.float(f32), random.float(f32), random.float(f32), random.float(f32)},
|
||||||
.visual = .{
|
.position = .{width * random.float(f32), height},
|
||||||
.color = .{random.float(f32), random.float(f32), random.float(f32), random.float(f32)},
|
.rotation = std.math.pi * random.float(f32),
|
||||||
.position = .{width * random.float(f32), height},
|
|
||||||
.rotation = std.math.pi * random.float(f32),
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
.mouse_down => {
|
.mouse_down => {
|
||||||
try visuals.state.spawned.push_grow(.{
|
_ = try visuals.state.spawned.insert(.{
|
||||||
.lifetime_seconds = 2.5 + (5 * random.float(f32)),
|
.lifetime_seconds = 2.5 + (5 * random.float(f32)),
|
||||||
|
.color = .{random.float(f32), random.float(f32), random.float(f32), random.float(f32)},
|
||||||
.visual = .{
|
.position = visuals.state.mouse_position,
|
||||||
.color = .{random.float(f32), random.float(f32), random.float(f32), random.float(f32)},
|
.rotation = std.math.pi * random.float(f32),
|
||||||
.position = visuals.state.mouse_position,
|
|
||||||
.rotation = std.math.pi * random.float(f32),
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -83,35 +80,8 @@ fn update(visuals: ona.Write(Visuals), events: ona.Receive(hid.Event), display:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_spawned(spawned: *ona.stack.Parallel(Spawned)) void {
|
fn render(visuals: ona.Write(Visuals), commands: gfx.Commands) !void {
|
||||||
const float_speed = 6;
|
for (visuals.state.spawned.values()) |visual| {
|
||||||
|
|
||||||
for (spawned.values.slice(.visual)) |*visual| {
|
|
||||||
visual.position -= .{0, float_speed};
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var range = spawned.len();
|
|
||||||
var index: usize = 0;
|
|
||||||
|
|
||||||
while (index < range) : (index += 1) {
|
|
||||||
const lifetime_seconds = spawned.values.get(.lifetime_seconds, index).?;
|
|
||||||
|
|
||||||
lifetime_seconds.* -= 1.0 / 60.0;
|
|
||||||
|
|
||||||
if (lifetime_seconds.* <= 0) {
|
|
||||||
range -= 1;
|
|
||||||
|
|
||||||
std.mem.swap(f32, lifetime_seconds, spawned.values.get(.lifetime_seconds, range).?);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std.debug.assert(spawned.pop_many(spawned.len() - range));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(visuals: ona.Write(Visuals), commands: gfx.Commands(.foreground)) !void {
|
|
||||||
for (visuals.state.spawned.values.slice(.visual)) |visual| {
|
|
||||||
try commands.draw_texture(.{
|
try commands.draw_texture(.{
|
||||||
.anchor = @splat(0.5),
|
.anchor = @splat(0.5),
|
||||||
.position = visual.position,
|
.position = visual.position,
|
||||||
|
|
270
src/gfx/gfx.zig
270
src/gfx/gfx.zig
|
@ -119,6 +119,7 @@ pub const Command = union (enum) {
|
||||||
draw_font: DrawFont,
|
draw_font: DrawFont,
|
||||||
draw_texture: DrawTexture,
|
draw_texture: DrawTexture,
|
||||||
set_effect: SetEffect,
|
set_effect: SetEffect,
|
||||||
|
set_scissor: SetScissor,
|
||||||
set_target: SetTarget,
|
set_target: SetTarget,
|
||||||
|
|
||||||
pub const DrawFont = struct {
|
pub const DrawFont = struct {
|
||||||
|
@ -146,6 +147,10 @@ pub const Command = union (enum) {
|
||||||
properties: []const u8,
|
properties: []const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const SetScissor = struct {
|
||||||
|
has_rect: ?Rect,
|
||||||
|
};
|
||||||
|
|
||||||
pub const SetTarget = struct {
|
pub const SetTarget = struct {
|
||||||
texture: ?Texture = null,
|
texture: ?Texture = null,
|
||||||
clear_color: ?Color,
|
clear_color: ?Color,
|
||||||
|
@ -154,120 +159,120 @@ pub const Command = union (enum) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn Commands(comptime layer: Layer) type {
|
pub const Commands = struct {
|
||||||
return struct {
|
pending: *List,
|
||||||
pending: *List,
|
|
||||||
|
|
||||||
pub const List = struct {
|
pub const List = struct {
|
||||||
arena: std.heap.ArenaAllocator,
|
arena: std.heap.ArenaAllocator,
|
||||||
stack: ona.stack.Sequential(Command),
|
stack: ona.stack.Sequential(Command),
|
||||||
|
|
||||||
fn clear(self: *List) void {
|
fn clear(self: *List) void {
|
||||||
self.stack.clear();
|
self.stack.clear();
|
||||||
|
|
||||||
if (!self.arena.reset(.retain_capacity)) {
|
if (!self.arena.reset(.retain_capacity)) {
|
||||||
std.log.warn("failed to reset the buffer of a gfx queue with retained capacity", .{});
|
std.log.warn("failed to reset the buffer of a gfx queue with retained capacity", .{});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn deinit(self: *List) void {
|
fn deinit(self: *List) void {
|
||||||
self.arena.deinit();
|
self.arena.deinit();
|
||||||
self.stack.deinit();
|
self.stack.deinit();
|
||||||
|
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(allocator: std.mem.Allocator) List {
|
fn init(allocator: std.mem.Allocator) List {
|
||||||
return .{
|
|
||||||
.arena = std.heap.ArenaAllocator.init(allocator),
|
|
||||||
.stack = .{.allocator = allocator},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Param = struct {
|
|
||||||
swap_lists: [2]List,
|
|
||||||
swap_state: u1 = 0,
|
|
||||||
|
|
||||||
fn deinit(self: *Param) void {
|
|
||||||
for (&self.swap_lists) |*list| {
|
|
||||||
list.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.* = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pending_list(self: *Param) *List {
|
|
||||||
return &self.swap_lists[self.swap_state];
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rotate(self: *Param) void {
|
|
||||||
const swapped_state = self.swap_state ^ 1;
|
|
||||||
|
|
||||||
self.swap_lists[swapped_state].clear();
|
|
||||||
|
|
||||||
self.swap_state = swapped_state;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn submitted_commands(self: Param) []const Command {
|
|
||||||
return self.swap_lists[self.swap_state ^ 1].stack.values;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
pub fn bind(_: ona.World.BindContext) std.mem.Allocator.Error!Param {
|
|
||||||
return .{
|
return .{
|
||||||
.swap_lists = .{
|
.arena = std.heap.ArenaAllocator.init(allocator),
|
||||||
List.init(ona.heap.allocator),
|
.stack = .{.allocator = allocator},
|
||||||
List.init(ona.heap.allocator),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(param: *Param) Self {
|
|
||||||
return .{
|
|
||||||
.pending = param.pending_list(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_font(self: Self, command: Command.DrawFont) std.mem.Allocator.Error!void {
|
|
||||||
try self.pending.stack.push_grow(.{
|
|
||||||
.draw_font = .{
|
|
||||||
.text = try self.pending.arena.allocator().dupe(u8, command.text),
|
|
||||||
.position = command.position,
|
|
||||||
.size = command.size,
|
|
||||||
.font = command.font,
|
|
||||||
.tint = command.tint,
|
|
||||||
.depth = command.depth,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_texture(self: Self, command: Command.DrawTexture) std.mem.Allocator.Error!void {
|
|
||||||
try self.pending.stack.push_grow(.{.draw_texture = command});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_effect(self: Self, command: Command.SetEffect) std.mem.Allocator.Error!void {
|
|
||||||
try self.pending.stack.push_grow(.{
|
|
||||||
.set_effect = .{
|
|
||||||
.properties = try self.pending.arena.allocator().dupe(u8, command.properties),
|
|
||||||
.effect = command.effect,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const _ = layer;
|
|
||||||
|
|
||||||
pub fn unbind(param: *Param, _: ona.World.UnbindContext) void {
|
|
||||||
param.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_target(self: Self, command: Command.SetTarget) std.mem.Allocator.Error!void {
|
|
||||||
try self.pending.stack.push_grow(.{.set_target = command});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
pub const Param = struct {
|
||||||
|
swap_lists: [2]List,
|
||||||
|
swap_state: u1 = 0,
|
||||||
|
|
||||||
|
fn deinit(self: *Param) void {
|
||||||
|
for (&self.swap_lists) |*list| {
|
||||||
|
list.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.* = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pending_list(self: *Param) *List {
|
||||||
|
return &self.swap_lists[self.swap_state];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rotate(self: *Param) void {
|
||||||
|
const swapped_state = self.swap_state ^ 1;
|
||||||
|
|
||||||
|
self.swap_lists[swapped_state].clear();
|
||||||
|
|
||||||
|
self.swap_state = swapped_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn submitted_commands(self: Param) []const Command {
|
||||||
|
return self.swap_lists[self.swap_state ^ 1].stack.values;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub fn bind(_: ona.World.BindContext) std.mem.Allocator.Error!Param {
|
||||||
|
return .{
|
||||||
|
.swap_lists = .{
|
||||||
|
List.init(ona.heap.allocator),
|
||||||
|
List.init(ona.heap.allocator),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(param: *Param) Self {
|
||||||
|
return .{
|
||||||
|
.pending = param.pending_list(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_font(self: Self, command: Command.DrawFont) std.mem.Allocator.Error!void {
|
||||||
|
try self.pending.stack.push_grow(.{
|
||||||
|
.draw_font = .{
|
||||||
|
.text = try self.pending.arena.allocator().dupe(u8, command.text),
|
||||||
|
.position = command.position,
|
||||||
|
.size = command.size,
|
||||||
|
.font = command.font,
|
||||||
|
.tint = command.tint,
|
||||||
|
.depth = command.depth,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_texture(self: Self, command: Command.DrawTexture) std.mem.Allocator.Error!void {
|
||||||
|
try self.pending.stack.push_grow(.{.draw_texture = command});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_effect(self: Self, command: Command.SetEffect) std.mem.Allocator.Error!void {
|
||||||
|
try self.pending.stack.push_grow(.{
|
||||||
|
.set_effect = .{
|
||||||
|
.properties = try self.pending.arena.allocator().dupe(u8, command.properties),
|
||||||
|
.effect = command.effect,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_scissor(self: Self, command: Command.SetScissor) std.mem.Allocator.Error!void {
|
||||||
|
try self.pending.stack.push_grow(.{.set_scissor = command});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_target(self: Self, command: Command.SetTarget) std.mem.Allocator.Error!void {
|
||||||
|
try self.pending.stack.push_grow(.{.set_target = command});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unbind(param: *Param, _: ona.World.UnbindContext) void {
|
||||||
|
param.deinit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub const Descs = struct {
|
pub const Descs = struct {
|
||||||
arena: std.heap.ArenaAllocator,
|
arena: std.heap.ArenaAllocator,
|
||||||
|
@ -437,19 +442,28 @@ pub const Font = enum (u32) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Layer = enum {
|
|
||||||
background,
|
|
||||||
foreground,
|
|
||||||
overlay,
|
|
||||||
|
|
||||||
const values = std.enums.values(Layer);
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Rect = extern struct {
|
pub const Rect = extern struct {
|
||||||
left: f32,
|
left: f32,
|
||||||
top: f32,
|
top: f32,
|
||||||
right: f32,
|
right: f32,
|
||||||
bottom: f32,
|
bottom: f32,
|
||||||
|
|
||||||
|
pub fn area(self: Rect) f32 {
|
||||||
|
const width, const height = self.size();
|
||||||
|
|
||||||
|
return width * height;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(self: Rect) Vector2 {
|
||||||
|
return .{self.right - self.left, self.bottom - self.top};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const zero = Rect{
|
||||||
|
.left = 0,
|
||||||
|
.top = 0,
|
||||||
|
.right = 0,
|
||||||
|
.bottom = 0,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Texture = enum (u32) {
|
pub const Texture = enum (u32) {
|
||||||
|
@ -513,13 +527,7 @@ const Work = union (enum) {
|
||||||
width: u16,
|
width: u16,
|
||||||
height: u16,
|
height: u16,
|
||||||
finished: *std.Thread.ResetEvent,
|
finished: *std.Thread.ResetEvent,
|
||||||
commands_set: CommandsSet,
|
has_commands: ?*ona.Params(Commands).Node,
|
||||||
|
|
||||||
const CommandsSet = struct {
|
|
||||||
?*ona.Params(Commands(.background)).Node,
|
|
||||||
?*ona.Params(Commands(.foreground)).Node,
|
|
||||||
?*ona.Params(Commands(.overlay)).Node,
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const UnloadEffect = struct {
|
const UnloadEffect = struct {
|
||||||
|
@ -654,12 +662,10 @@ pub fn synchronize(exclusive: ona.Exclusive(&.{Assets, Display})) !void {
|
||||||
assets.frame_rendered.wait();
|
assets.frame_rendered.wait();
|
||||||
assets.frame_rendered.reset();
|
assets.frame_rendered.reset();
|
||||||
|
|
||||||
inline for (Layer.values) |layer| {
|
var has_command_param = exclusive.world.get_params(Commands).has_head;
|
||||||
var has_command_param = exclusive.world.get_params(Commands(layer)).has_head;
|
|
||||||
|
|
||||||
while (has_command_param) |command_param| : (has_command_param = command_param.has_next) {
|
while (has_command_param) |command_param| : (has_command_param = command_param.has_next) {
|
||||||
command_param.param.rotate();
|
command_param.param.rotate();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var display_width, var display_height = [_]c_int{0, 0};
|
var display_width, var display_height = [_]c_int{0, 0};
|
||||||
|
@ -670,27 +676,17 @@ pub fn synchronize(exclusive: ona.Exclusive(&.{Assets, Display})) !void {
|
||||||
ext.SDL_SetWindowSize(assets.window, display.width, display.height);
|
ext.SDL_SetWindowSize(assets.window, display.width, display.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
var commands_filled: usize = 0;
|
if (exclusive.world.get_params(Commands).has_head) |has_commands| {
|
||||||
var commands_set = Work.RenderFrame.CommandsSet{null, null, null};
|
|
||||||
|
|
||||||
inline for (0 .. Layer.values.len, Layer.values) |i, layer| {
|
|
||||||
if (exclusive.world.get_params(Commands(layer)).has_head) |command_param| {
|
|
||||||
commands_set[i] = command_param;
|
|
||||||
commands_filled += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (commands_filled == 0) {
|
|
||||||
assets.frame_rendered.set();
|
|
||||||
} else {
|
|
||||||
assets.pending_work.enqueue(.{
|
assets.pending_work.enqueue(.{
|
||||||
.render_frame = .{
|
.render_frame = .{
|
||||||
.commands_set = commands_set,
|
.has_commands = has_commands,
|
||||||
.width = display.width,
|
.width = display.width,
|
||||||
.height = display.height,
|
.height = display.height,
|
||||||
.clear_color = display.clear_color,
|
.clear_color = display.clear_color,
|
||||||
.finished = &assets.frame_rendered,
|
.finished = &assets.frame_rendered,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
assets.frame_rendered.set();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,6 +224,24 @@ const Frame = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_scissor(self: *Frame, resources: *Resources, command: gfx.Command.SetScissor) void {
|
||||||
|
self.flush(resources);
|
||||||
|
|
||||||
|
if (command.has_rect) |rect| {
|
||||||
|
const width, const height = rect.size();
|
||||||
|
|
||||||
|
sokol.gfx.applyScissorRect(
|
||||||
|
@intFromFloat(rect.left),
|
||||||
|
@intFromFloat(rect.top),
|
||||||
|
@intFromFloat(width),
|
||||||
|
@intFromFloat(height),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
sokol.gfx.applyScissorRect(0, 0, self.width, self.height, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_target(self: *Frame, resources: *Resources, command: gfx.Command.SetTarget) void {
|
pub fn set_target(self: *Frame, resources: *Resources, command: gfx.Command.SetTarget) void {
|
||||||
sokol.gfx.endPass();
|
sokol.gfx.endPass();
|
||||||
|
|
||||||
|
@ -402,28 +420,27 @@ pub fn process_work(pending_work: *gfx.Assets.WorkQueue, window: *ext.SDL_Window
|
||||||
.render_frame => |*render_frame| {
|
.render_frame => |*render_frame| {
|
||||||
frame.start(render_frame.width, render_frame.height);
|
frame.start(render_frame.width, render_frame.height);
|
||||||
|
|
||||||
inline for (&render_frame.commands_set) |has_commands| {
|
var has_commands = render_frame.has_commands;
|
||||||
var has_commands_currently = has_commands;
|
|
||||||
|
|
||||||
while (has_commands_currently) |commands| : (has_commands_currently = commands.has_next) {
|
while (has_commands) |commands| : (has_commands = commands.has_next) {
|
||||||
for (commands.param.submitted_commands()) |command| {
|
for (commands.param.submitted_commands()) |command| {
|
||||||
try switch (command) {
|
try switch (command) {
|
||||||
.draw_font => |draw_font| frame.draw_font(&resources, draw_font),
|
.draw_font => |draw_font| frame.draw_font(&resources, draw_font),
|
||||||
.draw_texture => |draw_texture| frame.draw_texture(&resources, draw_texture),
|
.draw_texture => |draw_texture| frame.draw_texture(&resources, draw_texture),
|
||||||
.set_effect => |set_effect| frame.set_effect(&resources, set_effect),
|
.set_effect => |set_effect| frame.set_effect(&resources, set_effect),
|
||||||
.set_target => |set_target| frame.set_target(&resources, set_target),
|
.set_scissor => |set_scissor| frame.set_scissor(&resources, set_scissor),
|
||||||
};
|
.set_target => |set_target| frame.set_target(&resources, set_target),
|
||||||
}
|
};
|
||||||
|
}
|
||||||
|
|
||||||
frame.flush(&resources);
|
frame.flush(&resources);
|
||||||
|
|
||||||
if (frame.current_target_texture != null) {
|
if (frame.current_target_texture != null) {
|
||||||
frame.set_target(&resources, .{
|
frame.set_target(&resources, .{
|
||||||
.clear_color = null,
|
.clear_color = null,
|
||||||
.clear_depth = null,
|
.clear_depth = null,
|
||||||
.clear_stencil = null,
|
.clear_stencil = null,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
184
src/gui/gui.zig
184
src/gui/gui.zig
|
@ -5,45 +5,74 @@ const ona = @import("ona");
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
pub const Box = struct {
|
pub const Box = struct {
|
||||||
left: f32,
|
x: f32,
|
||||||
top: f32,
|
y: f32,
|
||||||
width: f32,
|
width: f32,
|
||||||
height: f32,
|
height: f32,
|
||||||
|
|
||||||
|
pub fn rect(self: Box) gfx.Rect {
|
||||||
|
return .{
|
||||||
|
.left = self.x,
|
||||||
|
.top = self.y,
|
||||||
|
.right = self.x + self.width,
|
||||||
|
.bottom = self.y + self.height,
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Canvas = struct {
|
pub const Canvas = struct {
|
||||||
nodes: ona.stack.Parallel(Node) = .{},
|
nodes: NodeMap = NodeMap.init(ona.heap.allocator),
|
||||||
drawable_blocks: ona.stack.Sequential(DrawableBlock) = .{},
|
drawable_blocks: DrawableBlockMap = DrawableBlockMap.init(ona.heap.allocator),
|
||||||
drawable_labels: ona.stack.Sequential(DrawableLabel) = .{},
|
drawable_labels: DrawableLabelMap = DrawableLabelMap.init(ona.heap.allocator),
|
||||||
|
|
||||||
fn Drawable(comptime State: type) type {
|
fn Drawable(comptime State: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
state: State,
|
clip: gfx.Rect,
|
||||||
|
box: Box,
|
||||||
depth: f32,
|
depth: f32,
|
||||||
|
state: State,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const DrawableBlock = Drawable(struct {
|
const DrawableBlockMap = ona.SlotMap(Drawable(struct {
|
||||||
background: gfx.Texture = .default,
|
background: gfx.Texture = .default,
|
||||||
color: gfx.Color = gfx.colors.white,
|
color: gfx.Color = gfx.colors.white,
|
||||||
});
|
}));
|
||||||
|
|
||||||
const DrawableLabel = Drawable(struct {
|
const DrawableLabelMap = ona.SlotMap(Drawable(struct {
|
||||||
text: ona.stack.Sequential(u8) = .{},
|
text: ona.stack.Sequential(u8) = .{},
|
||||||
color: gfx.Color = gfx.colors.black,
|
color: gfx.Color = gfx.colors.black,
|
||||||
font: gfx.Font = .default,
|
font: gfx.Font = .default,
|
||||||
});
|
}));
|
||||||
|
|
||||||
const Node = struct {
|
const NodeMap = ona.SlotMap(struct {
|
||||||
name: []u8,
|
name: []u8,
|
||||||
box: Box,
|
box: Box,
|
||||||
parent_index: u32 = unused_index,
|
parent_index: u32 = unused_index,
|
||||||
block_index: u32 = unused_index,
|
block_index: u32 = unused_index,
|
||||||
label_index: u32 = unused_index,
|
label_index: u32 = unused_index,
|
||||||
style_index: u32 = unused_index,
|
|
||||||
|
|
||||||
const unused_index = std.math.maxInt(u32);
|
const Self = @This();
|
||||||
};
|
|
||||||
|
fn clip(self: Self, nodes: NodeMap) gfx.Rect {
|
||||||
|
if (self.parent_index == unused_index) {
|
||||||
|
return self.box.rect();
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodes.get(self.parent_index).?.box.rect();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn depth(self: Self, nodes: NodeMap) f32 {
|
||||||
|
var parent_index = self.parent_index;
|
||||||
|
var value: f32 = 0;
|
||||||
|
|
||||||
|
while (parent_index != unused_index) : (parent_index = nodes.get(parent_index).?.parent_index) {
|
||||||
|
value += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
pub fn append(self: *Canvas, name: []const u8, box: Box) std.mem.Allocator.Error!Item {
|
pub fn append(self: *Canvas, name: []const u8, box: Box) std.mem.Allocator.Error!Item {
|
||||||
const duped_name = try ona.heap.allocator.dupe(u8, name);
|
const duped_name = try ona.heap.allocator.dupe(u8, name);
|
||||||
|
@ -52,48 +81,17 @@ pub const Canvas = struct {
|
||||||
ona.heap.allocator.free(duped_name);
|
ona.heap.allocator.free(duped_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = self.nodes.len();
|
return @enumFromInt(try self.nodes.insert(.{
|
||||||
|
|
||||||
try self.nodes.push_grow(.{
|
|
||||||
.name = duped_name,
|
.name = duped_name,
|
||||||
.box = box,
|
.box = box,
|
||||||
});
|
}));
|
||||||
|
|
||||||
errdefer {
|
|
||||||
std.debug.assert(self.nodes.pop());
|
|
||||||
}
|
|
||||||
|
|
||||||
const node_count = self.nodes.len();
|
|
||||||
|
|
||||||
try self.drawable_blocks.grow(ona.scalars.sub(node_count, self.drawable_blocks.cap) orelse 0);
|
|
||||||
try self.drawable_labels.grow(ona.scalars.sub(node_count, self.drawable_labels.cap) orelse 0);
|
|
||||||
|
|
||||||
return @enumFromInt(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn calculate_depth(self: Canvas, item: Item) f32 {
|
|
||||||
const parent_indices = self.nodes.values.slice(.parent_index);
|
|
||||||
var depth: f32 = 0;
|
|
||||||
var index = @intFromEnum(item);
|
|
||||||
|
|
||||||
while (true) : (depth += 1) {
|
|
||||||
const parent_index = parent_indices[index];
|
|
||||||
|
|
||||||
if (parent_index == Node.unused_index) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
index = parent_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
return depth;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deinit(self: *Canvas) void {
|
fn deinit(self: *Canvas) void {
|
||||||
self.nodes.deinit();
|
self.nodes.deinit();
|
||||||
self.drawable_blocks.deinit();
|
self.drawable_blocks.deinit();
|
||||||
|
|
||||||
for (self.drawable_labels.values) |*drawable_label| {
|
for (self.drawable_labels.values()) |*drawable_label| {
|
||||||
drawable_label.state.text.deinit();
|
drawable_label.state.text.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,21 +100,19 @@ pub const Canvas = struct {
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const label_depth_offset = 0.5;
|
pub fn set_block(self: *Canvas, item: Item, set: SetBlock) std.mem.Allocator.Error!void {
|
||||||
|
const node = self.nodes.get(@intFromEnum(item)).?;
|
||||||
|
|
||||||
pub fn set_block(self: *Canvas, item: Item, set: SetBlock) void {
|
if (node.block_index == unused_index) {
|
||||||
const block_index = self.nodes.values.get(.block_index, @intFromEnum(item)).?;
|
node.block_index = @intCast(try self.drawable_blocks.insert(.{
|
||||||
|
.clip = node.clip(self.nodes),
|
||||||
if (block_index.* == Node.unused_index) {
|
.depth = node.depth(self.nodes),
|
||||||
block_index.* = @intCast(self.drawable_blocks.len());
|
.box = node.box,
|
||||||
|
|
||||||
std.debug.assert(self.drawable_blocks.push(.{
|
|
||||||
.depth = self.calculate_depth(item),
|
|
||||||
.state = .{},
|
.state = .{},
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
const block = &self.drawable_blocks.values[block_index.*];
|
const block = self.drawable_blocks.get(node.block_index).?;
|
||||||
|
|
||||||
if (set.has_background) |background| {
|
if (set.has_background) |background| {
|
||||||
block.state.background = background;
|
block.state.background = background;
|
||||||
|
@ -128,18 +124,18 @@ pub const Canvas = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_label(self: *Canvas, item: Item, set: SetLabel) std.mem.Allocator.Error!void {
|
pub fn set_label(self: *Canvas, item: Item, set: SetLabel) std.mem.Allocator.Error!void {
|
||||||
const label_index = self.nodes.values.get(.label_index, @intFromEnum(item)).?;
|
const node = self.nodes.get(@intFromEnum(item)).?;
|
||||||
|
|
||||||
if (label_index.* == Node.unused_index) {
|
if (node.label_index == unused_index) {
|
||||||
label_index.* = @intCast(self.drawable_labels.len());
|
node.label_index = @intCast(try self.drawable_labels.insert(.{
|
||||||
|
.clip = node.clip(self.nodes),
|
||||||
std.debug.assert(self.drawable_labels.push(.{
|
.depth = node.depth(self.nodes),
|
||||||
.depth = self.calculate_depth(item) + label_depth_offset,
|
.box = node.box,
|
||||||
.state = .{},
|
.state = .{},
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
const label = &self.drawable_labels.values[label_index.*];
|
const label = self.drawable_labels.get(node.label_index).?;
|
||||||
|
|
||||||
if (set.has_text) |text| {
|
if (set.has_text) |text| {
|
||||||
label.state.text.pop_all();
|
label.state.text.pop_all();
|
||||||
|
@ -159,28 +155,29 @@ pub const Canvas = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_parent(self: *Canvas, item: Item, has_parent: ?Item) void {
|
pub fn set_parent(self: *Canvas, item: Item, has_parent: ?Item) void {
|
||||||
const item_index = @intFromEnum(item);
|
const node = self.nodes.get(@intFromEnum(item)).?;
|
||||||
const parent_index = self.nodes.values.get(.parent_index, item_index).?;
|
|
||||||
|
|
||||||
parent_index.* = if (has_parent) |parent| @intFromEnum(parent) else Node.unused_index;
|
node.parent_index = if (has_parent) |parent| @intFromEnum(parent) else unused_index;
|
||||||
|
|
||||||
const label_index = self.nodes.values.get(.parent_index, item_index).?.*;
|
const depth = node.depth(self.nodes);
|
||||||
const block_index = self.nodes.values.get(.parent_index, item_index).?.*;
|
const clip = node.clip(self.nodes);
|
||||||
const label_index_exists = label_index != Node.unused_index;
|
|
||||||
const block_index_exists = block_index != Node.unused_index;
|
|
||||||
|
|
||||||
if (label_index_exists or block_index_exists) {
|
if (node.block_index != unused_index) {
|
||||||
const depth = self.calculate_depth(item);
|
const block = self.drawable_blocks.get(node.block_index).?;
|
||||||
|
|
||||||
if (label_index_exists) {
|
block.depth = depth;
|
||||||
self.drawable_labels[label_index].depth = depth + label_depth_offset;
|
block.clip = clip;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block_index_exists) {
|
if (node.label_index != unused_index) {
|
||||||
self.drawable_labels[label_index].depth = depth;
|
const label = self.drawable_labels.get(node.label_index).?;
|
||||||
}
|
|
||||||
|
label.depth = depth;
|
||||||
|
label.clip = clip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const unused_index = std.math.maxInt(u32);
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Item = enum (u32) {
|
pub const Item = enum (u32) {
|
||||||
|
@ -202,29 +199,40 @@ pub fn exit(canvas: ona.Write(Canvas)) !void {
|
||||||
canvas.state.deinit();
|
canvas.state.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(commands: gfx.Commands(.overlay), canvas: ona.Read(Canvas)) !void {
|
pub fn render(commands: gfx.Commands, canvas: ona.Read(Canvas)) !void {
|
||||||
const boxes = canvas.state.nodes.values.slice(.box);
|
// TODO: Investigate if scissor rects are necessary for clipping content that overflows.
|
||||||
|
for (canvas.state.drawable_blocks.values()) |block| {
|
||||||
|
try commands.set_scissor(.{
|
||||||
|
.has_rect = block.clip,
|
||||||
|
});
|
||||||
|
|
||||||
for (canvas.state.drawable_blocks.values, boxes) |block, box| {
|
|
||||||
try commands.draw_texture(.{
|
try commands.draw_texture(.{
|
||||||
|
.size = .{block.box.width, block.box.height},
|
||||||
|
.position = .{block.box.x, block.box.y},
|
||||||
.depth = block.depth,
|
.depth = block.depth,
|
||||||
.position = .{box.left, box.top},
|
|
||||||
.size = .{box.width, box.height},
|
|
||||||
.texture = block.state.background,
|
.texture = block.state.background,
|
||||||
.tint = block.state.color,
|
.tint = block.state.color,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (canvas.state.drawable_labels.values, boxes) |label, box| {
|
for (canvas.state.drawable_labels.values()) |label| {
|
||||||
|
try commands.set_scissor(.{
|
||||||
|
.has_rect = label.clip,
|
||||||
|
});
|
||||||
|
|
||||||
try commands.draw_font(.{
|
try commands.draw_font(.{
|
||||||
|
.size = .{label.box.width, label.box.height},
|
||||||
.depth = label.depth,
|
.depth = label.depth,
|
||||||
.text = label.state.text.values,
|
.text = label.state.text.values,
|
||||||
.position = .{box.left, box.top},
|
.position = .{label.box.x, label.box.y},
|
||||||
.size = .{box.width, box.height},
|
|
||||||
.tint = label.state.color,
|
.tint = label.state.color,
|
||||||
.font = label.state.font,
|
.font = label.state.font,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try commands.set_scissor(.{
|
||||||
|
.has_rect = null,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup(world: *ona.World) !void {
|
pub fn setup(world: *ona.World) !void {
|
||||||
|
|
|
@ -365,7 +365,7 @@ const Schedule = struct {
|
||||||
|
|
||||||
errdefer {
|
errdefer {
|
||||||
bundle.deinit();
|
bundle.deinit();
|
||||||
std.debug.assert(schedule.parallel_work_bundles.pop());
|
std.debug.assert(schedule.parallel_work_bundles.pop() != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
try populate_bundle(bundle, &schedule.graph, node);
|
try populate_bundle(bundle, &schedule.graph, node);
|
||||||
|
|
|
@ -30,13 +30,13 @@ pub fn Graph(comptime Payload: type) type {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_edges(self: *Self) void {
|
pub fn clear_edges(self: *Self) void {
|
||||||
for (self.table.values.slice(.edges)) |*edges| {
|
for (self.table.values.get(.edges)) |*edges| {
|
||||||
edges.clear();
|
edges.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Self) void {
|
pub fn deinit(self: *Self) void {
|
||||||
for (self.table.values.slice(.edges)) |*edges| {
|
for (self.table.values.get(.edges)) |*edges| {
|
||||||
edges.deinit();
|
edges.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,15 +50,17 @@ pub fn Graph(comptime Payload: type) type {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.table.values.get(.edges, @intFromEnum(node)).?.values;
|
return self.table.values.get(.edges)[@intFromEnum(node)].values;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exists(self: Self, node: Node) bool {
|
pub fn exists(self: Self, node: Node) bool {
|
||||||
if (self.table.values.get(.is_occupied, @intFromEnum(node))) |is_occupied| {
|
const node_index = @intFromEnum(node);
|
||||||
return is_occupied.*;
|
|
||||||
|
if (node_index >= self.table.values.len) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return self.table.values.get(.is_occupied)[node_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(self: Self, node: Node) ?*Payload {
|
pub fn get(self: Self, node: Node) ?*Payload {
|
||||||
|
@ -66,7 +68,7 @@ pub fn Graph(comptime Payload: type) type {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.table.values.get(.payload, @intFromEnum(node)).?;
|
return &self.table.values.get(.payload)[@intFromEnum(node)];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator) Self {
|
pub fn init(allocator: std.mem.Allocator) Self {
|
||||||
|
@ -76,13 +78,11 @@ pub fn Graph(comptime Payload: type) type {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_edge(self: *Self, dependant_node: Node, edge_node: Node) std.mem.Allocator.Error!bool {
|
pub fn insert_edge(self: *Self, dependant_node: Node, edge_node: Node) std.mem.Allocator.Error!bool {
|
||||||
if (!self.exists(edge_node)) {
|
if (!self.exists(edge_node) or !self.exists(dependant_node)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const edges = self.table.values.get(.edges, @intFromEnum(dependant_node)) orelse {
|
const edges = &self.table.values.get(.edges)[@intFromEnum(dependant_node)];
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (std.mem.indexOfScalar(Node, edges.values, edge_node) == null) {
|
if (std.mem.indexOfScalar(Node, edges.values, edge_node) == null) {
|
||||||
try edges.push_grow(edge_node);
|
try edges.push_grow(edge_node);
|
||||||
|
@ -97,7 +97,7 @@ pub fn Graph(comptime Payload: type) type {
|
||||||
|
|
||||||
pub fn nodes(self: *const Self) Nodes {
|
pub fn nodes(self: *const Self) Nodes {
|
||||||
return .{
|
return .{
|
||||||
.occupied_table = self.table.values.slice(.is_occupied),
|
.occupied_table = self.table.values.get(.is_occupied),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ pub fn Graph(comptime Payload: type) type {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std.debug.assert(self.table.values.set(.is_visited, @intFromEnum(node), true));
|
self.table.values.get(.is_visited)[@intFromEnum(node)] = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -118,14 +118,14 @@ pub fn Graph(comptime Payload: type) type {
|
||||||
|
|
||||||
const node_index = @intFromEnum(node);
|
const node_index = @intFromEnum(node);
|
||||||
|
|
||||||
self.table.values.get(.is_occupied, node_index).?.* = false;
|
self.table.values.get(.is_occupied)[node_index] = false;
|
||||||
self.node_count -= 1;
|
self.node_count -= 1;
|
||||||
|
|
||||||
return self.table.values.get(.payload, node_index).?.*;
|
return self.table.values.get(.payload)[node_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_visited(self: *Self) void {
|
pub fn reset_visited(self: *Self) void {
|
||||||
@memset(self.table.values.slice(.is_visited), false);
|
@memset(self.table.values.get(.is_visited), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn visited(self: Self, node: Node) ?bool {
|
pub fn visited(self: Self, node: Node) ?bool {
|
||||||
|
@ -133,7 +133,7 @@ pub fn Graph(comptime Payload: type) type {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.table.values.get(.is_visited, @intFromEnum(node)).?.*;
|
return self.table.values.get(.is_visited)[@intFromEnum(node)];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
121
src/ona/ona.zig
121
src/ona/ona.zig
|
@ -635,6 +635,127 @@ pub fn Shared(comptime Value: type, comptime info: ShareInfo) type {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn SlotMap(comptime Value: type) type {
|
||||||
|
const Index = @Type(.{.Int = .{
|
||||||
|
.bits = @bitSizeOf(usize) - 1,
|
||||||
|
.signedness = .unsigned,
|
||||||
|
}});
|
||||||
|
|
||||||
|
comptime std.debug.assert(@sizeOf(Index) == @sizeOf(usize));
|
||||||
|
|
||||||
|
return struct {
|
||||||
|
slots: stack.Sequential(Slot),
|
||||||
|
entries: stack.Parallel(Entry),
|
||||||
|
next: Index = 0,
|
||||||
|
|
||||||
|
const Entry = struct {
|
||||||
|
value: Value,
|
||||||
|
erased: usize,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
const Slot = packed struct {
|
||||||
|
index: Index,
|
||||||
|
is_occupied: bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn deinit(self: *Self) void {
|
||||||
|
self.slots.deinit();
|
||||||
|
self.entries.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(self: Self, index: usize) ?*Value {
|
||||||
|
if (index >= self.slots.len()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const slot = self.slots.values[index];
|
||||||
|
|
||||||
|
if (!slot.is_occupied) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &self.entries.values.get(.value)[slot.index];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(allocator: std.mem.Allocator) Self {
|
||||||
|
return .{
|
||||||
|
.slots = .{.allocator = allocator},
|
||||||
|
.entries = .{.allocator = allocator},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(self: *Self, value: Value) std.mem.Allocator.Error!usize {
|
||||||
|
const index = self.next;
|
||||||
|
const slots_len: Index = @intCast(self.slots.len());
|
||||||
|
|
||||||
|
if (self.next == slots_len) {
|
||||||
|
try self.slots.push_grow(.{
|
||||||
|
.index = slots_len + 1,
|
||||||
|
.is_occupied = false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const slot = &self.slots.values[index];
|
||||||
|
|
||||||
|
std.debug.assert(!slot.is_occupied);
|
||||||
|
|
||||||
|
self.next = slot.index;
|
||||||
|
|
||||||
|
slot.* = .{
|
||||||
|
.index = index,
|
||||||
|
.is_occupied = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
try self.entries.push_grow(.{
|
||||||
|
.value = value,
|
||||||
|
.erased = index,
|
||||||
|
});
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(self: *Self, index: usize) ?Value {
|
||||||
|
if (index >= self.slots.len()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const slot = self.slots.values[index];
|
||||||
|
|
||||||
|
if (!slot.is_occupied) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = self.entries.values.get(.value)[slot.index];
|
||||||
|
|
||||||
|
std.debug.assert(self.entries.values.write(slot.index, .{
|
||||||
|
.value = self.entries.get(.value).?.*,
|
||||||
|
.erased = self.entries.get(.erased).?.*,
|
||||||
|
}));
|
||||||
|
|
||||||
|
std.debug.assert(self.entries.pop());
|
||||||
|
|
||||||
|
if (!self.entries.is_empty()) {
|
||||||
|
self.slots.values[self.entries.values.get(.erased)[slot.index]] = slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.slots.values[index] = .{
|
||||||
|
.index = self.next,
|
||||||
|
.is_occupied = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.next = @intCast(index);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn values(self: Self) []Value {
|
||||||
|
return self.entries.values.get(.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub const SystemInfo = struct {
|
pub const SystemInfo = struct {
|
||||||
execute: *const fn ([]const *const Parameter, *const [max_parameters]*anyopaque) anyerror!void,
|
execute: *const fn ([]const *const Parameter, *const [max_parameters]*anyopaque) anyerror!void,
|
||||||
parameters: [max_parameters]*const Parameter = undefined,
|
parameters: [max_parameters]*const Parameter = undefined,
|
||||||
|
|
|
@ -35,15 +35,15 @@ pub fn Parallel(comptime Type: type) type {
|
||||||
|
|
||||||
const all_fields = std.enums.values(Field);
|
const all_fields = std.enums.values(Field);
|
||||||
|
|
||||||
|
pub fn get(self: Self, comptime field: Field) []align (alignment) Element(field) {
|
||||||
|
return self.ptr(field)[0 .. self.len];
|
||||||
|
}
|
||||||
|
|
||||||
pub fn ptr(self: Self, comptime field: Field) [*]align (alignment) Element(field) {
|
pub fn ptr(self: Self, comptime field: Field) [*]align (alignment) Element(field) {
|
||||||
return @as([*]align (alignment) Element(field), @ptrCast(self.ptrs[@intFromEnum(field)]));
|
return @as([*]align (alignment) Element(field), @ptrCast(self.ptrs[@intFromEnum(field)]));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn slice(self: Self, comptime field: Field) []align (alignment) Element(field) {
|
pub fn slice(self: Self, off: usize, len: usize) ?Self {
|
||||||
return self.ptr(field)[0 .. self.len];
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn slice_all(self: Self, off: usize, len: usize) ?Self {
|
|
||||||
if (len > self.len or off > len) {
|
if (len > self.len or off > len) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -57,31 +57,13 @@ pub fn Parallel(comptime Type: type) type {
|
||||||
return sliced;
|
return sliced;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(self: Self, comptime field: Field, index: usize) ?*Element(field) {
|
pub fn write(self: Self, index: usize, value: Type) bool {
|
||||||
if (index >= self.len) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return &self.ptr(field)[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set(self: Self, comptime field: Field, index: usize, value: Element(field)) bool {
|
|
||||||
if (index >= self.len) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.slice(field)[index] = value;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_all(self: Self, index: usize, value: Type) bool {
|
|
||||||
if (index >= self.len) {
|
if (index >= self.len) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline for (0 .. fields.len) |i| {
|
inline for (0 .. fields.len) |i| {
|
||||||
self.slice(all_fields[i])[index] = @field(value, fields[i].name);
|
self.get(all_fields[i])[index] = @field(value, fields[i].name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -123,12 +105,12 @@ pub fn parallel_alloc(comptime Element: type, allocator: std.mem.Allocator, n: u
|
||||||
|
|
||||||
pub fn parallel_copy(comptime Element: type, target: Parallel(Element), origin: Parallel(Element)) void {
|
pub fn parallel_copy(comptime Element: type, target: Parallel(Element), origin: Parallel(Element)) void {
|
||||||
inline for (comptime std.enums.values(Parallel(Element).Field)) |field| {
|
inline for (comptime std.enums.values(Parallel(Element).Field)) |field| {
|
||||||
@memcpy(target.slice(field), origin.slice(field));
|
@memcpy(target.get(field), origin.get(field));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parallel_free(comptime Element: type, allocator: std.mem.Allocator, buffers: Parallel(Element)) void {
|
pub fn parallel_free(comptime Element: type, allocator: std.mem.Allocator, buffers: Parallel(Element)) void {
|
||||||
inline for (comptime std.enums.values(Parallel(Element).Field)) |field| {
|
inline for (comptime std.enums.values(Parallel(Element).Field)) |field| {
|
||||||
allocator.free(buffers.slice(field));
|
allocator.free(buffers.get(field));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,11 +25,12 @@ pub fn Sequential(comptime Value: type) type {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn grow(self: *Self, additional: usize) std.mem.Allocator.Error!void {
|
pub fn grow(self: *Self, additional: usize) std.mem.Allocator.Error!void {
|
||||||
if (additional == 0) {
|
const grown_capacity = self.values.len + additional;
|
||||||
|
|
||||||
|
if (grown_capacity <= self.cap) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const grown_capacity = self.cap + additional;
|
|
||||||
const buffer = try self.allocator.alloc(Value, grown_capacity);
|
const buffer = try self.allocator.alloc(Value, grown_capacity);
|
||||||
|
|
||||||
errdefer self.allocator.deallocate(buffer);
|
errdefer self.allocator.deallocate(buffer);
|
||||||
|
@ -59,14 +60,16 @@ pub fn Sequential(comptime Value: type) type {
|
||||||
return self.values.len;
|
return self.values.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop(self: *Self) bool {
|
pub fn pop(self: *Self) ?Value {
|
||||||
if (self.values.len == 0) {
|
if (self.values.len == 0) {
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.values = self.values[0 .. self.values.len - 1];
|
const tail_index = self.values.len - 1;
|
||||||
|
|
||||||
return true;
|
defer self.values = self.values[0 .. tail_index];
|
||||||
|
|
||||||
|
return self.values[tail_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop_all(self: *Self) void {
|
pub fn pop_all(self: *Self) void {
|
||||||
|
@ -116,9 +119,7 @@ pub fn Sequential(comptime Value: type) type {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_grow(self: *Self, value: Value) std.mem.Allocator.Error!void {
|
pub fn push_grow(self: *Self, value: Value) std.mem.Allocator.Error!void {
|
||||||
if (self.values.len == self.cap) {
|
try self.grow(@max(1, self.values.len));
|
||||||
try self.grow(@max(1, self.cap));
|
|
||||||
}
|
|
||||||
|
|
||||||
const offset_index = self.values.len;
|
const offset_index = self.values.len;
|
||||||
|
|
||||||
|
@ -231,20 +232,29 @@ pub fn Parallel(comptime Value: type) type {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return &self.slices.field_slice(field)[self.len() - 1];
|
return @alignCast(&self.values.get(field)[self.len() - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn grow(self: *Self, additional: usize) std.mem.Allocator.Error!void {
|
pub fn grow(self: *Self, additional: usize) std.mem.Allocator.Error!void {
|
||||||
const grown_capacity = self.cap + additional;
|
const grown_capacity = self.values.len + additional;
|
||||||
|
|
||||||
|
if (grown_capacity <= self.cap) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const buffer = try ona.slices.parallel_alloc(Value, self.allocator, grown_capacity);
|
const buffer = try ona.slices.parallel_alloc(Value, self.allocator, grown_capacity);
|
||||||
|
|
||||||
if (self.cap != 0) {
|
if (self.cap != 0) {
|
||||||
ona.slices.parallel_copy(Value, buffer.slice_all(0, self.values.len).?, self.values);
|
ona.slices.parallel_copy(Value, buffer.slice(0, self.values.len).?, self.values);
|
||||||
ona.slices.parallel_free(Value, self.allocator, self.values.slice_all(0, self.cap).?);
|
ona.slices.parallel_free(Value, self.allocator, self.values.slice(0, self.cap).?);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.cap = grown_capacity;
|
self.cap = grown_capacity;
|
||||||
self.values = buffer.slice_all(0, self.values.len).?;
|
self.values = buffer.slice(0, self.values.len).?;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(self: Self) bool {
|
||||||
|
return self.values.len == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn len(self: Self) usize {
|
pub fn len(self: Self) usize {
|
||||||
|
@ -256,7 +266,7 @@ pub fn Parallel(comptime Value: type) type {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.values = self.values.slice_all(0, self.values.len - 1).?;
|
self.values = self.values.slice(0, self.values.len - 1).?;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -266,21 +276,19 @@ pub fn Parallel(comptime Value: type) type {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.values = self.values.slice_all(0, new_length).?;
|
self.values = self.values.slice(0, new_length).?;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_grow(self: *Self, value: Value) std.mem.Allocator.Error!void {
|
pub fn push_grow(self: *Self, value: Value) std.mem.Allocator.Error!void {
|
||||||
if (self.len() == self.cap) {
|
try self.grow(@max(1, self.values.len));
|
||||||
try self.grow(@max(1, self.cap));
|
|
||||||
}
|
|
||||||
|
|
||||||
const tail_index = self.values.len;
|
const tail_index = self.values.len;
|
||||||
|
|
||||||
self.values.len += 1;
|
self.values.len += 1;
|
||||||
|
|
||||||
std.debug.assert(self.values.set_all(tail_index, value));
|
std.debug.assert(self.values.write(tail_index, value));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue