resources pretty done

master
Mike 5 years ago
parent a547add727
commit 07905d0b09

@ -49,8 +49,9 @@ pub fn typeId64(comptime T: type) u64 {
return hashStringFnv(u64, @typeName(T)); return hashStringFnv(u64, @typeName(T));
} }
/// u32 Fowler-Noll-Vo string hash
pub fn hashString(comptime str: []const u8) u32 { pub fn hashString(comptime str: []const u8) u32 {
return @truncate(u32, std.hash.Wyhash.hash(0, str)); return hashStringFnv(u32, str);
} }
/// FowlerNollVo string hash. ReturnType should be u32/u64 /// FowlerNollVo string hash. ReturnType should be u32/u64

@ -23,22 +23,26 @@ pub const Assets = struct {
self.caches.deinit(); self.caches.deinit();
} }
pub fn registerCache(self: *Assets, comptime AssetT: type, comptime LoaderT: type) *Cache(AssetT) { pub fn get(self: *Assets, comptime AssetT: type) *Cache(AssetT) {
var cache = Cache(AssetT).initPtr(self.allocator, LoaderT); if (self.caches.getValue(utils.typeId(AssetT))) |tid| {
return @intToPtr(*Cache(AssetT), tid);
}
var cache = Cache(AssetT).initPtr(self.allocator);
_ = self.caches.put(utils.typeId(AssetT), @ptrToInt(cache)) catch unreachable; _ = self.caches.put(utils.typeId(AssetT), @ptrToInt(cache)) catch unreachable;
return cache; return cache;
} }
pub fn get(self: Assets, comptime AssetT: type) *Cache(AssetT) { pub fn load(self: *Assets, id: u16, comptime loader: var) ReturnType(loader, false) {
if (self.caches.getValue(utils.typeId(AssetT))) |tid| { return self.get(ReturnType(loader, true)).load(id, loader);
return @intToPtr(*Cache(AssetT), tid);
}
unreachable;
} }
pub fn load(self: Assets, comptime AssetT: type, comptime LoaderT: type, args: LoaderT.LoadArgs) *AssetT { fn ReturnType(comptime loader: var, strip_ptr: bool) type {
var cache = self.get(AssetT); var ret = @typeInfo(@TypeOf(@field(loader, "load"))).BoundFn.return_type.?;
return cache.load(666, LoaderT, args); if (strip_ptr) {
return ret.Child;
}
return ret;
} }
}; };
@ -50,13 +54,6 @@ test "assets" {
} }
}; };
const ThingLoader = struct {
pub const LoadArgs = struct {};
pub fn load(self: @This(), args: var) *Thing {
return std.testing.allocator.create(Thing) catch unreachable;
}
};
const OtherThing = struct { const OtherThing = struct {
fart: i32, fart: i32,
pub fn deinit(self: *@This()) void { pub fn deinit(self: *@This()) void {
@ -64,20 +61,33 @@ test "assets" {
} }
}; };
const OtherThingLoader = struct { const OtherThingLoadArgs = struct {
pub const LoadArgs = struct {}; pub fn load(self: @This()) *OtherThing {
pub fn load(self: @This(), args: var) *OtherThing {
return std.testing.allocator.create(OtherThing) catch unreachable; return std.testing.allocator.create(OtherThing) catch unreachable;
} }
}; };
const ThingLoadArgs = struct {
pub fn load(self: @This()) *Thing {
return std.testing.allocator.create(Thing) catch unreachable;
}
};
var assets = Assets.init(std.testing.allocator); var assets = Assets.init(std.testing.allocator);
defer assets.deinit(); defer assets.deinit();
var cache = assets.registerCache(Thing, ThingLoader); var thing = assets.get(Thing).load(6, ThingLoadArgs{});
var thing = assets.get(Thing).load(6, ThingLoader, ThingLoader.LoadArgs{}); std.testing.expectEqual(assets.get(Thing).size(), 1);
std.testing.expectEqual(cache.size(), 1);
var thing2 = assets.load(4, ThingLoadArgs{});
std.testing.expectEqual(assets.get(Thing).size(), 2);
var other_thing = assets.get(OtherThing).load(6, OtherThingLoadArgs{});
std.testing.expectEqual(assets.get(OtherThing).size(), 1);
var other_thing2 = assets.load(8, OtherThingLoadArgs{});
std.testing.expectEqual(assets.get(OtherThing).size(), 2);
var thing2 = assets.load(Thing, ThingLoader, ThingLoader.LoadArgs{}); assets.get(OtherThing).clear();
std.testing.expectEqual(cache.size(), 2); std.testing.expectEqual(assets.get(OtherThing).size(), 0);
} }

@ -1,24 +1,18 @@
const std = @import("std"); const std = @import("std");
const ErasedPtr = @import("../ecs/utils.zig").ErasedPtr; const ErasedPtr = @import("../ecs/utils.zig").ErasedPtr;
/// Simple cache for resources of a given type. TLoader should be a struct that implements a single /// Simple cache for resources of a given type. If any resource has a deinit method it will be called when clear
/// method: load(args: var) *T. If any resource has a deinit method it will be called when clear /// or remove is called. Implementing a "loader" which is passed to "load" is a struct with one method:
/// or remove is called. /// - load(self: @This()) *T.
pub fn Cache(comptime T: type) type { pub fn Cache(comptime T: type) type {
return struct { return struct {
const Self = @This(); const Self = @This();
safe_deinit: fn (*@This()) void, safe_deinit: fn (*@This()) void,
resources: std.AutoHashMap(u16, *T), resources: std.AutoHashMap(u32, *T),
loader: ErasedPtr,
allocator: ?*std.mem.Allocator = null, allocator: ?*std.mem.Allocator = null,
pub fn initPtr(allocator: *std.mem.Allocator, comptime LoaderT: type) *@This() { pub fn initPtr(allocator: *std.mem.Allocator) *@This() {
const ptr = if (@sizeOf(LoaderT) > 0)
ErasedPtr.init(allocator.create(LoaderT) catch unreachable)
else
ErasedPtr.init(LoaderT);
var cache = allocator.create(@This()) catch unreachable; var cache = allocator.create(@This()) catch unreachable;
cache.safe_deinit = struct { cache.safe_deinit = struct {
fn deinit(self: *Self) void { fn deinit(self: *Self) void {
@ -27,18 +21,12 @@ pub fn Cache(comptime T: type) type {
self.allocator.?.destroy(self); self.allocator.?.destroy(self);
} }
}.deinit; }.deinit;
cache.loader = ptr; cache.resources = std.AutoHashMap(u32, *T).init(allocator);
cache.resources = std.AutoHashMap(u16, *T).init(allocator);
cache.allocator = allocator; cache.allocator = allocator;
return cache; return cache;
} }
pub fn init(allocator: *std.mem.Allocator, comptime LoaderT: type) @This() { pub fn init(allocator: *std.mem.Allocator) @This() {
const ptr = if (@sizeOf(LoaderT) > 0)
ErasedPtr.init(std.testing.allocator.create(LoaderT) catch unreachable)
else
ErasedPtr.init(LoaderT);
return .{ return .{
.safe_deinit = struct { .safe_deinit = struct {
fn deinit(self: *Self) void { fn deinit(self: *Self) void {
@ -46,8 +34,7 @@ pub fn Cache(comptime T: type) type {
self.resources.deinit(); self.resources.deinit();
} }
}.deinit, }.deinit,
.loader = ptr, .resources = std.AutoHashMap(u32, *T).init(allocator),
.resources = std.AutoHashMap(u16, *T).init(allocator),
}; };
} }
@ -55,21 +42,21 @@ pub fn Cache(comptime T: type) type {
self.safe_deinit(self); self.safe_deinit(self);
} }
fn load(self: *@This(), id: u16, comptime LoaderT: type, args: LoaderT.LoadArgs) *T { pub fn load(self: *@This(), id: u32, comptime loader: var) @typeInfo(@TypeOf(@field(loader, "load"))).BoundFn.return_type.? {
if (self.resources.getValue(id)) |resource| { if (self.resources.getValue(id)) |resource| {
return resource; return resource;
} }
var resource = self.loader.as(LoaderT).load(args); var resource = loader.load();
_ = self.resources.put(id, resource) catch unreachable; _ = self.resources.put(id, resource) catch unreachable;
return resource; return resource;
} }
pub fn contains(self: *@This(), id: u16) bool { pub fn contains(self: *@This(), id: u32) bool {
return self.resources.contains(id); return self.resources.contains(id);
} }
pub fn remove(self: *@This(), id: u16) void { pub fn remove(self: *@This(), id: u32) void {
if (self.resources.remove(id)) |kv| { if (self.resources.remove(id)) |kv| {
if (@hasDecl(T, "deinit")) { if (@hasDecl(T, "deinit")) {
@call(.{ .modifier = .always_inline }, @field(kv.value, "deinit"), .{}); @call(.{ .modifier = .always_inline }, @field(kv.value, "deinit"), .{});
@ -95,6 +82,8 @@ pub fn Cache(comptime T: type) type {
} }
test "cache" { test "cache" {
const utils = @import("../ecs/utils.zig");
const Thing = struct { const Thing = struct {
fart: i32, fart: i32,
pub fn deinit(self: *@This()) void { pub fn deinit(self: *@This()) void {
@ -102,21 +91,20 @@ test "cache" {
} }
}; };
const ThingLoader = struct { const ThingLoadArgs = struct {
pub const LoadArgs = struct {}; pub fn load(self: @This()) *Thing {
pub fn load(self: @This(), args: var) *Thing {
return std.testing.allocator.create(Thing) catch unreachable; return std.testing.allocator.create(Thing) catch unreachable;
} }
}; };
var cache = Cache(Thing).init(std.testing.allocator, ThingLoader); var cache = Cache(Thing).init(std.testing.allocator);
defer cache.deinit(); defer cache.deinit();
var thing = cache.load(6, ThingLoader, ThingLoader.LoadArgs{}); var thing = cache.load(utils.hashString("my/id"), ThingLoadArgs{});
var thing2 = cache.load(2, ThingLoader, ThingLoader.LoadArgs{}); var thing2 = cache.load(utils.hashString("another/id"), ThingLoadArgs{});
std.testing.expectEqual(cache.size(), 2); std.testing.expectEqual(cache.size(), 2);
cache.remove(2); cache.remove(utils.hashString("my/id"));
std.testing.expectEqual(cache.size(), 1); std.testing.expectEqual(cache.size(), 1);
cache.clear(); cache.clear();

Loading…
Cancel
Save