Port rewrite
This commit is contained in:
119
src/engine/VkAllocator.zig
Normal file
119
src/engine/VkAllocator.zig
Normal file
@@ -0,0 +1,119 @@
|
||||
const VkAllocator = @This();
|
||||
const std = @import("std");
|
||||
|
||||
const vk = @import("vulkan");
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
allocations: std.AutoHashMapUnmanaged(?*anyopaque, usize) = .empty,
|
||||
mutex: std.Thread.Mutex = .{},
|
||||
|
||||
interface: vk.AllocationCallbacks,
|
||||
|
||||
// Allocation in Zig's standard library relies heavily on the alignment being
|
||||
// comptime-known, but we only know the alignment at runtime. We could work
|
||||
// around it, but it would add much complexity. Instead, we allocate everything
|
||||
// with the same, relatively big alignment and hope for the best.
|
||||
const actual_alignment: std.mem.Alignment = .@"16";
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) !*VkAllocator {
|
||||
// NOTE We allocate the structure to pin its address.
|
||||
const self = try allocator.create(VkAllocator);
|
||||
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
.interface = .{
|
||||
.p_user_data = self,
|
||||
.pfn_allocation = &allocationFunction,
|
||||
.pfn_reallocation = &reallocationFunction,
|
||||
.pfn_free = &freeFunction,
|
||||
},
|
||||
};
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *VkAllocator) void {
|
||||
const allocator = self.allocator;
|
||||
|
||||
self.allocations.deinit(allocator);
|
||||
|
||||
self.* = undefined;
|
||||
allocator.destroy(self);
|
||||
}
|
||||
|
||||
fn allocationFunction(
|
||||
p_user_data: ?*anyopaque,
|
||||
size: usize,
|
||||
alignment: usize,
|
||||
_: vk.SystemAllocationScope,
|
||||
) callconv(vk.vulkan_call_conv) ?*anyopaque {
|
||||
const self: *VkAllocator = @ptrCast(@alignCast(p_user_data.?));
|
||||
|
||||
const desired_alignment: std.mem.Alignment = .fromByteUnits(alignment);
|
||||
std.debug.assert(std.mem.Alignment.compare(actual_alignment, .gte, desired_alignment));
|
||||
|
||||
self.mutex.lock();
|
||||
defer self.mutex.unlock();
|
||||
|
||||
self.allocations.ensureUnusedCapacity(self.allocator, 1) catch return null;
|
||||
const memory = self.allocator.alignedAlloc(u8, actual_alignment, size) catch return null;
|
||||
|
||||
self.allocations.putAssumeCapacity(memory.ptr, size);
|
||||
|
||||
return memory.ptr;
|
||||
}
|
||||
|
||||
fn reallocationFunction(
|
||||
p_user_data: ?*anyopaque,
|
||||
maybe_p_original: ?*anyopaque,
|
||||
size: usize,
|
||||
alignment: usize,
|
||||
_: vk.SystemAllocationScope,
|
||||
) callconv(vk.vulkan_call_conv) ?*anyopaque {
|
||||
const self: *VkAllocator = @ptrCast(@alignCast(p_user_data.?));
|
||||
|
||||
const desired_alignment: std.mem.Alignment = .fromByteUnits(alignment);
|
||||
std.debug.assert(std.mem.Alignment.compare(actual_alignment, .gte, desired_alignment));
|
||||
|
||||
self.mutex.lock();
|
||||
defer self.mutex.unlock();
|
||||
|
||||
// NOTE If we were pedantic, we would consider the fact that we might not
|
||||
// need unused capacity if the memory doesn't get relocated.
|
||||
self.allocations.ensureUnusedCapacity(self.allocator, 1) catch return null;
|
||||
|
||||
const old_memory = if (maybe_p_original) |p_original| blk_then: {
|
||||
const old_size = self.allocations.get(p_original).?;
|
||||
break :blk_then @as([*]align(actual_alignment.toByteUnits()) u8, @ptrCast(@alignCast(p_original)))[0..old_size];
|
||||
} else blk_else: {
|
||||
break :blk_else @as([]align(actual_alignment.toByteUnits()) u8, &.{});
|
||||
};
|
||||
|
||||
const memory = self.allocator.realloc(old_memory, size) catch return null;
|
||||
std.debug.assert(std.mem.isAligned(@intFromPtr(memory.ptr), alignment));
|
||||
|
||||
if (maybe_p_original) |p_original| {
|
||||
const removed = self.allocations.remove(p_original);
|
||||
std.debug.assert(removed);
|
||||
}
|
||||
|
||||
self.allocations.putAssumeCapacityNoClobber(memory.ptr, size);
|
||||
|
||||
return memory.ptr;
|
||||
}
|
||||
|
||||
fn freeFunction(
|
||||
p_user_data: ?*anyopaque,
|
||||
maybe_p_memory: ?*anyopaque,
|
||||
) callconv(vk.vulkan_call_conv) void {
|
||||
const self: *VkAllocator = @ptrCast(@alignCast(p_user_data.?));
|
||||
|
||||
if (maybe_p_memory) |p_memory| {
|
||||
self.mutex.lock();
|
||||
defer self.mutex.unlock();
|
||||
|
||||
const size = self.allocations.fetchRemove(p_memory).?.value;
|
||||
const memory = @as([*]align(actual_alignment.toByteUnits()) u8, @ptrCast(@alignCast(p_memory)))[0..size];
|
||||
|
||||
self.allocator.free(memory);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user