Improved stuff n stuff
This commit is contained in:
parent
0e174f878f
commit
0aef87a252
6 changed files with 239 additions and 129 deletions
|
|
@ -38,6 +38,9 @@ pub fn FixedSlotMap(
|
|||
generations: FixedArrayList(u32, Capacity),
|
||||
free_list: FixedArrayList(u32, Capacity),
|
||||
|
||||
// --- GENERATION COUNTER ---
|
||||
next_gen: u32 = 0,
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub const empty = Self{
|
||||
|
|
@ -52,66 +55,75 @@ pub fn FixedSlotMap(
|
|||
self: *Self,
|
||||
value: T,
|
||||
) error{Full}!Handle(T) {
|
||||
// --- NEW GENERATION ---
|
||||
// --- REUSE A FREED SLOT ---
|
||||
if (self.free_list.pop()) |idx| {
|
||||
self.values.data[@intCast(idx)] = value;
|
||||
self.generations.data[@intCast(idx)] += 1;
|
||||
const gen = self.mint();
|
||||
self.values.items()[idx] = value;
|
||||
self.generations.items()[idx] = gen;
|
||||
|
||||
return .{
|
||||
.idx = idx,
|
||||
.gen = self.generations.data[idx],
|
||||
.gen = gen,
|
||||
};
|
||||
}
|
||||
|
||||
// --- METADATA ---
|
||||
const idx = self.values.len();
|
||||
const handle: Handle(T) = .{
|
||||
.idx = @intCast(idx),
|
||||
.gen = 1,
|
||||
};
|
||||
const idx: u32 = @intCast(self.values.items().len);
|
||||
const gen = self.mint();
|
||||
|
||||
// --- NEW VALUE ---
|
||||
try self.values.append(value);
|
||||
errdefer _ = self.values.swapRemove(idx);
|
||||
|
||||
// --- NEW GENERATION ---
|
||||
try self.generations.append(1);
|
||||
errdefer _ = self.values.pop();
|
||||
try self.generations.append(gen);
|
||||
|
||||
// --- RESULT ---
|
||||
return handle;
|
||||
return .{
|
||||
.idx = idx,
|
||||
.gen = gen,
|
||||
};
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// Returns an error if the free list could not grow
|
||||
/// Stale handles are silently ignored
|
||||
/// ----------------------------------------------------
|
||||
pub fn remove(
|
||||
self: *Self,
|
||||
handle: Handle(T),
|
||||
) void {
|
||||
) error{OutOfMemory}!void {
|
||||
const idx: u32 = handle.idx;
|
||||
|
||||
// --- CHECK IF STALE ---
|
||||
if (idx >= self.generations.len()) return;
|
||||
if (self.generations.data[idx] != handle.gen) return;
|
||||
if (idx >= self.generations.items().len) return;
|
||||
if (self.generations.items()[idx] != handle.gen) return;
|
||||
|
||||
// --- GENERATION ---
|
||||
self.generations.data[idx] +|= 1;
|
||||
self.free_list.append(idx) catch |err| {
|
||||
std.debug.print("[SLOTMAP_ERROR] {s}\n", .{@errorName(err)});
|
||||
};
|
||||
// --- FREE ---
|
||||
try self.free_list.append(idx);
|
||||
self.generations.items()[idx] = self.mint();
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn get(self: *Self, handle: Handle(T)) ?*T {
|
||||
if (handle.idx >= self.values.len()) return null;
|
||||
if (self.generations.data[handle.idx] != handle.gen) return null;
|
||||
return &self.values.data[handle.idx];
|
||||
if (self.generations.items()[handle.idx] != handle.gen) return null;
|
||||
return &self.values.items()[handle.idx];
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn items(self: *Self) []T {
|
||||
return self.values.items();
|
||||
pub fn clear(self: *Self) void {
|
||||
self.values.clear();
|
||||
self.generations.clear();
|
||||
self.free_list.clear();
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
fn mint(self: *Self) u32 {
|
||||
const gen = self.next_gen +% 1;
|
||||
self.next_gen = gen;
|
||||
return gen;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
79
src/gtl.zig
79
src/gtl.zig
|
|
@ -92,13 +92,17 @@ test "SlotMap" {
|
|||
// --- ADD MATERIALS ---
|
||||
_ = try materials.put(.{ .style = .{ .albedo = .{ 0.8, 0.2, 0.2, 1.0 } } });
|
||||
_ = try materials.put(.{ .unique = .{ .topology = .line_list } });
|
||||
const c = try materials.put(.{ .unique = .{ .topology = .line_list } });
|
||||
_ = try materials.put(.{ .unique = .{ .topology = .line_list } });
|
||||
|
||||
try materials.remove(c);
|
||||
|
||||
// --- # ---
|
||||
if (log.print(.debug, "MATERIALS", ansi.blue)) |a| {
|
||||
defer a.end();
|
||||
|
||||
// --- # ---
|
||||
for (materials.items()) |material| {
|
||||
for (materials.values.items) |material| {
|
||||
if (a.section(.debug, "", ansi.cyan)) |b| {
|
||||
defer b.end();
|
||||
|
||||
|
|
@ -124,67 +128,24 @@ test "SlotMap" {
|
|||
}
|
||||
}
|
||||
|
||||
test "SlotMap-DEP" { // TODO: Yes...
|
||||
{
|
||||
// --- LOG ---
|
||||
log.warn("...", .{}, @src());
|
||||
log.err("...", .{}, @src());
|
||||
log.info("...", .{}, @src());
|
||||
log.debug("...", .{}, @src());
|
||||
log.trace("...", .{}, @src());
|
||||
test "KeyedSlotMap" {
|
||||
std.debug.print("{s}━━━ KEYED SLOTMAP ━━━{s}\n", .{ ansi.blue, ansi.reset });
|
||||
|
||||
// --- PRINT ---
|
||||
if (log.print(.debug, "DEVICE INFO", ansi.bold ++ ansi.red)) |a| {
|
||||
defer a.end();
|
||||
// --- # ---
|
||||
var pipelines: KeyedSlotMap(Material.Unique, u32) = .init(std.testing.allocator);
|
||||
defer pipelines.deinit();
|
||||
|
||||
// --- VALUES ---
|
||||
a.write("device -> {s}", .{"GTX"});
|
||||
a.write("vendor -> {s}", .{"Nvidia"});
|
||||
// --- ADD PIPELINES ---
|
||||
_ = try pipelines.put(.{}, 0);
|
||||
_ = try pipelines.put(.{ .topology = .line_list }, 0);
|
||||
|
||||
// --- SECTION ---
|
||||
if (a.section(.err, "MEMORY", ansi.green)) |b| {
|
||||
defer b.end();
|
||||
|
||||
b.write("VRAM -> {}GB", .{24});
|
||||
}
|
||||
}
|
||||
|
||||
log.set(.{ .style = .rounded });
|
||||
|
||||
// --- PRINT ---
|
||||
if (log.print(.debug, "DEVICE INFO", ansi.bold ++ ansi.red)) |a| {
|
||||
defer a.end();
|
||||
|
||||
// --- VALUES ---
|
||||
a.write("device -> {s}", .{"GTX"});
|
||||
a.write("vendor -> {s}", .{"Nvidia"});
|
||||
|
||||
// --- SECTION ---
|
||||
if (a.section(.err, "MEMORY", ansi.green)) |b| {
|
||||
defer b.end();
|
||||
|
||||
b.write("VRAM -> {}GB", .{24});
|
||||
}
|
||||
}
|
||||
|
||||
log.set(.{ .style = .pipe });
|
||||
|
||||
// --- PRINT ---
|
||||
if (log.print(.debug, "DEVICE INFO", ansi.bold ++ ansi.red)) |a| {
|
||||
defer a.end();
|
||||
|
||||
// --- VALUES ---
|
||||
a.write("device -> {s}", .{"GTX"});
|
||||
a.write("vendor -> {s}", .{"Nvidia"});
|
||||
|
||||
// --- SECTION ---
|
||||
if (a.section(.err, "MEMORY", ansi.green)) |b| {
|
||||
defer b.end();
|
||||
|
||||
// --- VALUES ---
|
||||
b.write("VRAM -> {}GB", .{24});
|
||||
}
|
||||
}
|
||||
// --- ITERATOR ---
|
||||
var it = pipelines.iterator();
|
||||
while (it.next()) |entry| {
|
||||
std.debug.print("[{}] {}\n", .{
|
||||
entry.value,
|
||||
entry.key.topology,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
//! ----------------------------------------------------
|
||||
//! ----------------------------------------------------
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// Generational Handle
|
||||
/// ----------------------------------------------------
|
||||
|
|
@ -6,9 +11,15 @@ pub fn Handle(comptime T: type) type {
|
|||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
//
|
||||
// FIELDS
|
||||
//
|
||||
|
||||
idx: u32 = 0,
|
||||
gen: u32 = 0,
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn eql(
|
||||
self: Self,
|
||||
other: Self,
|
||||
|
|
@ -16,5 +27,14 @@ pub fn Handle(comptime T: type) type {
|
|||
return self.idx == other.idx and
|
||||
self.gen == other.gen;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn format(
|
||||
self: @This(),
|
||||
writer: *std.Io.Writer,
|
||||
) std.Io.Writer.Error!void {
|
||||
try writer.print("{}:{}", .{ self.idx, self.gen });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,19 +9,22 @@ const Handle = @import("handle.zig").Handle;
|
|||
/// # Example
|
||||
///
|
||||
/// ```zig
|
||||
/// // --- SHADER MAP ---
|
||||
/// var shaders: KeyedSlotMap(ShaderID, GPUShader) = .init(alloc);
|
||||
/// defer shaders.deinit();
|
||||
///
|
||||
/// // --- PIPELINE MAP ---
|
||||
/// var pipelines: KeyedSlotMap(PipelineKey, GPUPipeline) = .init(alloc);
|
||||
/// // --- # ---
|
||||
/// var pipelines: KeyedSlotMap(Material.Unique, u32) = .init(std.testing.allocator);
|
||||
/// defer pipelines.deinit();
|
||||
///
|
||||
/// // --- ADD MATERIALS ---
|
||||
/// try materials.append(alloc, .{ .shader = .pbr }); // BLACK
|
||||
/// try materials.append(alloc, .{ .shader = .pbr, .albedo = .{ 1, 0, 0, 1 } }); // RED
|
||||
/// try materials.append(alloc, .{ .shader = .unlit, .albedo = .{ 0, 1, 0, 1 } }); // GREEN
|
||||
/// try materials.append(alloc, .{ .shader = .unlit, .albedo = .{ 0, 0, 1, 1 } }); // GREEN
|
||||
/// // --- ADD PIPELINES ---
|
||||
/// _ = try pipelines.put(.{}, 0);
|
||||
/// _ = try pipelines.put(.{ .topology = .line_list }, 0);
|
||||
///
|
||||
/// // --- ITERATOR ---
|
||||
/// var it = pipelines.iterator();
|
||||
/// while (it.next()) |entry| {
|
||||
/// std.debug.print("[{}] {}\n", .{
|
||||
/// entry.value,
|
||||
/// entry.key.topology,
|
||||
/// });
|
||||
/// }
|
||||
/// ```
|
||||
/// ----------------------------------------------------
|
||||
pub fn KeyedSlotMap(
|
||||
|
|
@ -38,6 +41,71 @@ pub fn KeyedSlotMap(
|
|||
value: V,
|
||||
};
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// Cursor over live entries
|
||||
/// Removed (freed) slots are skipped
|
||||
/// ----------------------------------------------------
|
||||
pub const Iterator = struct {
|
||||
entries: []Entry,
|
||||
free_list: []const u32,
|
||||
index: usize = 0,
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn next(self: *Iterator) ?*Entry {
|
||||
return advance(self.entries, self.free_list, &self.index);
|
||||
}
|
||||
};
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// Cursor over live keys
|
||||
/// ----------------------------------------------------
|
||||
pub const KeyIterator = struct {
|
||||
entries: []Entry,
|
||||
free_list: []const u32,
|
||||
index: usize = 0,
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn next(self: *KeyIterator) ?*K {
|
||||
const entry = advance(self.entries, self.free_list, &self.index) orelse return null;
|
||||
return &entry.key;
|
||||
}
|
||||
};
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// Cursor over live values
|
||||
/// ----------------------------------------------------
|
||||
pub const ValueIterator = struct {
|
||||
entries: []Entry,
|
||||
free_list: []const u32,
|
||||
index: usize = 0,
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn next(self: *ValueIterator) ?*V {
|
||||
const entry = advance(self.entries, self.free_list, &self.index) orelse return null;
|
||||
return &entry.value;
|
||||
}
|
||||
};
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// Skips slots that live on the free list (tombstones)
|
||||
/// ----------------------------------------------------
|
||||
fn advance(
|
||||
entries: []Entry,
|
||||
free_list: []const u32,
|
||||
index: *usize,
|
||||
) ?*Entry {
|
||||
while (index.* < entries.len) : (index.* += 1) {
|
||||
if (std.mem.indexOfScalar(u32, free_list, @intCast(index.*)) != null) continue;
|
||||
const entry = &entries[index.*];
|
||||
index.* += 1;
|
||||
return entry;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//
|
||||
// FIELDS
|
||||
//
|
||||
|
|
@ -79,7 +147,7 @@ pub fn KeyedSlotMap(
|
|||
|
||||
// --- RESULT ---
|
||||
const handle = try self.slot_map.put(.{ .value = value, .key = key });
|
||||
errdefer self.slot_map.remove(handle);
|
||||
errdefer self.slot_map.remove(handle) catch {};
|
||||
|
||||
// --- NEW LOOKUP ---
|
||||
try self.lookup.put(key, handle);
|
||||
|
|
@ -89,28 +157,35 @@ pub fn KeyedSlotMap(
|
|||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// Returns an error if the free list could not grow
|
||||
/// Stale handles are silently ignored
|
||||
/// ----------------------------------------------------
|
||||
pub fn remove(
|
||||
self: *Self,
|
||||
handle: Handle(V),
|
||||
) void {
|
||||
// --- GET IDX ---
|
||||
) error{OutOfMemory}!void {
|
||||
const idx: u32 = handle.idx;
|
||||
|
||||
// --- CHECK IF STALE ---
|
||||
if (idx >= self.slot_map.generations.items.len) return;
|
||||
if (self.slot_map.generations.items[idx] != handle.gen) return;
|
||||
|
||||
// --- GRAB KEY BEFORE REMOVING ---
|
||||
const key = self.slot_map.values.items[idx].key;
|
||||
|
||||
// --- REMOVE FROM SLOTMAP ---
|
||||
self.slot_map.remove(handle);
|
||||
try self.slot_map.remove(handle);
|
||||
|
||||
// --- REMOVE KEY ---
|
||||
const key = self.values.items[idx].key;
|
||||
_ = self.lookup.remove(key);
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn removeByKey(self: *Self, key: K) void {
|
||||
pub fn removeByKey(self: *Self, key: K) error{OutOfMemory}!void {
|
||||
const handle = self.lookup.get(key) orelse return;
|
||||
_ = self.lookup.remove(key);
|
||||
self.slot_map.remove(handle);
|
||||
try self.slot_map.remove(handle);
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
|
|
@ -165,7 +240,7 @@ pub fn KeyedSlotMap(
|
|||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn count(self: *Self) u32 {
|
||||
return self.lookup.count();
|
||||
return @intCast(self.lookup.count());
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
|
|
@ -173,5 +248,35 @@ pub fn KeyedSlotMap(
|
|||
pub fn items(self: *Self) []Entry {
|
||||
return self.slot_map.values.items;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// Skips slots that live on the free list (tombstones)
|
||||
/// ----------------------------------------------------
|
||||
pub fn iterator(self: *Self) Iterator {
|
||||
return .{
|
||||
.entries = self.slot_map.values.items,
|
||||
.free_list = self.slot_map.free_list.items,
|
||||
};
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// Skips slots that live on the free list (tombstones)
|
||||
/// ----------------------------------------------------
|
||||
pub fn keyIterator(self: *Self) KeyIterator {
|
||||
return .{
|
||||
.entries = self.slot_map.values.items,
|
||||
.free_list = self.slot_map.free_list.items,
|
||||
};
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// Skips slots that live on the free list (tombstones)
|
||||
/// ----------------------------------------------------
|
||||
pub fn valueIterator(self: *Self) ValueIterator {
|
||||
return .{
|
||||
.entries = self.slot_map.values.items,
|
||||
.free_list = self.slot_map.free_list.items,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ pub fn print(
|
|||
|
||||
// --- RESULT ---
|
||||
const box = boxChars();
|
||||
std.debug.print("{s}{s}{s}{s} {s}\n", .{ color, box.top_left, box.horizontal, ansi.reset, name });
|
||||
std.debug.print("{s}{s}{s}[{s}{s}{s}]{s}\n", .{ color, box.top_left, box.horizontal, ansi.reset, name, color, ansi.reset });
|
||||
return .{ .color = color };
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ pub fn SlotMap(comptime T: type) type {
|
|||
generations: std.ArrayList(u32),
|
||||
free_list: std.ArrayList(u32),
|
||||
|
||||
// --- GENERATION COUNTER ---
|
||||
next_gen: u32 = 0,
|
||||
|
||||
// --- MEMORY ---
|
||||
alloc: std.mem.Allocator,
|
||||
|
||||
|
|
@ -40,10 +43,8 @@ pub fn SlotMap(comptime T: type) type {
|
|||
pub fn init(alloc: std.mem.Allocator) Self {
|
||||
return .{
|
||||
.values = .empty,
|
||||
|
||||
.generations = .empty,
|
||||
.free_list = .empty,
|
||||
|
||||
.alloc = alloc,
|
||||
};
|
||||
}
|
||||
|
|
@ -62,52 +63,51 @@ pub fn SlotMap(comptime T: type) type {
|
|||
self: *Self,
|
||||
value: T,
|
||||
) error{OutOfMemory}!Handle(T) {
|
||||
// --- NEW GENERATION ---
|
||||
// --- REUSE A FREED SLOT ---
|
||||
if (self.free_list.pop()) |idx| {
|
||||
self.values.items[@intCast(idx)] = value;
|
||||
self.generations.items[@intCast(idx)] += 1;
|
||||
const gen = self.mint();
|
||||
self.values.items[idx] = value;
|
||||
self.generations.items[idx] = gen;
|
||||
|
||||
return .{
|
||||
.idx = idx,
|
||||
.gen = self.generations.items[idx],
|
||||
.gen = gen,
|
||||
};
|
||||
}
|
||||
|
||||
// --- METADATA ---
|
||||
const idx = self.values.items.len;
|
||||
const handle: Handle(T) = .{
|
||||
.idx = @intCast(idx),
|
||||
.gen = 1,
|
||||
};
|
||||
const idx: u32 = @intCast(self.values.items.len);
|
||||
const gen = self.mint();
|
||||
|
||||
// --- NEW VALUE ---
|
||||
try self.values.append(self.alloc, value);
|
||||
errdefer _ = self.values.swapRemove(idx);
|
||||
|
||||
// --- NEW GENERATION ---
|
||||
try self.generations.append(self.alloc, 1);
|
||||
errdefer _ = self.values.pop();
|
||||
try self.generations.append(self.alloc, gen);
|
||||
|
||||
// --- RESULT ---
|
||||
return handle;
|
||||
return .{
|
||||
.idx = idx,
|
||||
.gen = gen,
|
||||
};
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// Returns an error if the free list could not grow
|
||||
/// Stale handles are silently ignored
|
||||
/// ----------------------------------------------------
|
||||
pub fn remove(
|
||||
self: *Self,
|
||||
handle: Handle(T),
|
||||
) void {
|
||||
) error{OutOfMemory}!void {
|
||||
const idx: u32 = handle.idx;
|
||||
|
||||
// --- CHECK IF STALE ---
|
||||
if (idx >= self.generations.items.len) return;
|
||||
if (self.generations.items[idx] != handle.gen) return;
|
||||
|
||||
// --- GENERATION ---
|
||||
self.generations.items[idx] +|= 1;
|
||||
self.free_list.append(self.alloc, idx) catch |err| {
|
||||
std.debug.print("[SLOTMAP_ERROR] {s}\n", .{@errorName(err)});
|
||||
};
|
||||
// --- FREE ---
|
||||
try self.free_list.append(self.alloc, idx);
|
||||
self.generations.items[idx] = self.mint();
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
|
|
@ -119,9 +119,21 @@ pub fn SlotMap(comptime T: type) type {
|
|||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// Reduce length to 0
|
||||
/// Invalidates all element pointers and handles
|
||||
/// ----------------------------------------------------
|
||||
pub fn items(self: *Self) []T {
|
||||
return self.values.items;
|
||||
pub fn clearRetainingCapacity(self: *Self) void {
|
||||
self.values.clearRetainingCapacity();
|
||||
self.generations.clearRetainingCapacity();
|
||||
self.free_list.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
fn mint(self: *Self) u32 {
|
||||
const gen = self.next_gen +% 1;
|
||||
self.next_gen = gen;
|
||||
return gen;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue