Port rewrite

This commit is contained in:
2025-11-19 14:43:57 +01:00
parent 444d20243c
commit bbafc55f6f
6 changed files with 928 additions and 256 deletions

119
src/engine/VkAllocator.zig Normal file
View 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);
}
}