From fae09760a33521ba2f177ec5200b6e47ad283fb8 Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Thu, 13 Nov 2025 23:27:45 +0100 Subject: [PATCH] reworking vulkan allocator --- build.zig | 2 +- src/soft/SoftCommandBuffer.zig | 32 ++++++++++++ src/soft/lib.zig | 1 + src/vulkan/CommandBuffer.zig | 29 +++++++++++ src/vulkan/CommandPool.zig | 14 +++++- src/vulkan/Device.zig | 5 +- src/vulkan/Instance.zig | 2 +- src/vulkan/VulkanAllocator.zig | 89 ++++++++++++++++++---------------- src/vulkan/lib.zig | 1 + test/c/main.c | 3 +- 10 files changed, 130 insertions(+), 48 deletions(-) create mode 100644 src/soft/SoftCommandBuffer.zig create mode 100644 src/vulkan/CommandBuffer.zig diff --git a/build.zig b/build.zig index bb11b64..48ef167 100644 --- a/build.zig +++ b/build.zig @@ -39,7 +39,7 @@ pub fn build(b: *std.Build) !void { base_mod.addSystemIncludePath(vulkan_headers.path("include")); for (implementations) |impl| { - var targets = std.ArrayListUnmanaged(*std.Build.Step.Compile){}; + var targets = std.ArrayList(*std.Build.Step.Compile){}; const lib_mod = b.createModule(.{ .root_source_file = b.path(impl.root_source_file), diff --git a/src/soft/SoftCommandBuffer.zig b/src/soft/SoftCommandBuffer.zig new file mode 100644 index 0000000..7458c17 --- /dev/null +++ b/src/soft/SoftCommandBuffer.zig @@ -0,0 +1,32 @@ +const std = @import("std"); +const vk = @import("vulkan"); +const base = @import("base"); + +const VkError = base.VkError; +const Device = base.Device; + +const Self = @This(); +pub const Interface = base.CommandBuffer; + +interface: Interface, + +pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const vk.CommandBufferAllocateInfo) VkError!*Self { + const self = allocator.create(Self) catch return VkError.OutOfHostMemory; + errdefer allocator.destroy(self); + + var interface = try Interface.init(device, allocator, info); + + interface.vtable = &.{ + .destroy = destroy, + }; + + self.* = .{ + .interface = interface, + }; + return self; +} + +pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void { + const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); + allocator.destroy(self); +} diff --git a/src/soft/lib.zig b/src/soft/lib.zig index c19ba26..0f4692f 100644 --- a/src/soft/lib.zig +++ b/src/soft/lib.zig @@ -7,6 +7,7 @@ pub const SoftDevice = @import("SoftDevice.zig"); pub const SoftPhysicalDevice = @import("SoftPhysicalDevice.zig"); pub const SoftQueue = @import("SoftQueue.zig"); +pub const SoftCommandBuffer = @import("SoftCommandBuffer.zig"); pub const SoftCommandPool = @import("SoftCommandPool.zig"); pub const SoftDeviceMemory = @import("SoftDeviceMemory.zig"); pub const SoftFence = @import("SoftFence.zig"); diff --git a/src/vulkan/CommandBuffer.zig b/src/vulkan/CommandBuffer.zig new file mode 100644 index 0000000..15123c8 --- /dev/null +++ b/src/vulkan/CommandBuffer.zig @@ -0,0 +1,29 @@ +const std = @import("std"); +const vk = @import("vulkan"); + +const VkError = @import("error_set.zig").VkError; +const Device = @import("Device.zig"); + +const Self = @This(); +pub const ObjectType: vk.ObjectType = .command_buffer; + +owner: *Device, + +vtable: *const VTable, + +pub const VTable = struct { + destroy: *const fn (*Self, std.mem.Allocator) void, +}; + +pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.CommandBufferAllocateInfo) VkError!Self { + _ = allocator; + _ = info; + return .{ + .owner = device, + .vtable = undefined, + }; +} + +pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void { + self.vtable.destroy(self, allocator); +} diff --git a/src/vulkan/CommandPool.zig b/src/vulkan/CommandPool.zig index 53ba505..0b4490e 100644 --- a/src/vulkan/CommandPool.zig +++ b/src/vulkan/CommandPool.zig @@ -2,33 +2,45 @@ const std = @import("std"); const vk = @import("vulkan"); const VkError = @import("error_set.zig").VkError; +const CommandBuffer = @import("CommandBuffer.zig"); const Device = @import("Device.zig"); const Self = @This(); pub const ObjectType: vk.ObjectType = .command_pool; +const BUFFER_POOL_BASE_CAPACITY = 64; + owner: *Device, flags: vk.CommandPoolCreateFlags, queue_family_index: u32, +buffers: std.ArrayList(*CommandBuffer), +first_free_buffer_index: usize, vtable: *const VTable, pub const VTable = struct { + allocateCommandBuffers: *const fn (*Self, vk.CommandBufferAllocateInfo) VkError!*CommandBuffer, destroy: *const fn (*Self, std.mem.Allocator) void, reset: *const fn (*Self, vk.CommandPoolResetFlags) VkError!void, }; pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.CommandPoolCreateInfo) VkError!Self { - _ = allocator; return .{ .owner = device, .flags = info.flags, .queue_family_index = info.queue_family_index, + .buffers = .initCapacity(allocator, BUFFER_POOL_BASE_CAPACITY) catch return VkError.OutOfHostMemory, + .first_free_buffer_index = 0, .vtable = undefined, }; } +pub inline fn allocateCommandBuffers(self: *Self, info: vk.CommandBufferAllocateInfo) VkError!*CommandBuffer { + return self.vtable.allocateCommandBuffers(self, info); +} + pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void { + self.buffers.deinit(allocator); self.vtable.destroy(self, allocator); } diff --git a/src/vulkan/Device.zig b/src/vulkan/Device.zig index e7ebdbb..f93be73 100644 --- a/src/vulkan/Device.zig +++ b/src/vulkan/Device.zig @@ -16,7 +16,8 @@ const Self = @This(); pub const ObjectType: vk.ObjectType = .device; physical_device: *const PhysicalDevice, -queues: std.AutoArrayHashMapUnmanaged(u32, std.ArrayListUnmanaged(*Dispatchable(Queue))), +queues: std.AutoArrayHashMapUnmanaged(u32, std.ArrayList(*Dispatchable(Queue))), +host_allocator: *VulkanAllocator, dispatch_table: *const DispatchTable, vtable: *const VTable, @@ -40,11 +41,11 @@ pub const DispatchTable = struct { }; pub fn init(allocator: std.mem.Allocator, physical_device: *const PhysicalDevice, info: *const vk.DeviceCreateInfo) VkError!Self { - _ = allocator; _ = info; return .{ .physical_device = physical_device, .queues = .empty, + .host_allocator = @ptrCast(@alignCast(allocator.ptr)), .dispatch_table = undefined, .vtable = undefined, }; diff --git a/src/vulkan/Instance.zig b/src/vulkan/Instance.zig index 7ff11a5..b0a11e0 100644 --- a/src/vulkan/Instance.zig +++ b/src/vulkan/Instance.zig @@ -18,7 +18,7 @@ comptime { const Self = @This(); pub const ObjectType: vk.ObjectType = .instance; -physical_devices: std.ArrayListUnmanaged(*Dispatchable(PhysicalDevice)), +physical_devices: std.ArrayList(*Dispatchable(PhysicalDevice)), dispatch_table: *const DispatchTable, vtable: *const VTable, diff --git a/src/vulkan/VulkanAllocator.zig b/src/vulkan/VulkanAllocator.zig index c6c3ddb..a3dafa6 100644 --- a/src/vulkan/VulkanAllocator.zig +++ b/src/vulkan/VulkanAllocator.zig @@ -26,18 +26,54 @@ pub fn init(callbacks: ?*const vk.AllocationCallbacks, scope: vk.SystemAllocatio } pub fn allocator(self: *const Self) Allocator { - if (self.callbacks != null) { - return .{ - .ptr = undefined, - .vtable = &.{ - .alloc = alloc, - .resize = resize, - .remap = remap, - .free = free, - }, - }; - } + return .{ + .ptr = @ptrCast(@constCast(self)), // Ugly const cast for convenience + .vtable = &.{ + .alloc = alloc, + .resize = resize, + .remap = remap, + .free = free, + }, + }; +} +fn alloc(context: *anyopaque, len: usize, alignment: Alignment, ret_addr: usize) ?[*]u8 { + const self: *Self = @ptrCast(@alignCast(context)); + if (self.callbacks.?.pfn_allocation) |pfn_allocation| { + return @ptrCast(pfn_allocation(self.callbacks.?.p_user_data, len, alignment.toByteUnits(), self.scope)); + } else { + return getFallbackAllocator().rawAlloc(len, alignment, ret_addr); + } +} + +fn resize(context: *anyopaque, ptr: []u8, alignment: Alignment, new_len: usize, ret_addr: usize) bool { + const self: *Self = @ptrCast(@alignCast(context)); + if (self.callbacks != null) { + return new_len <= ptr.len; + } else { + return getFallbackAllocator().rawResize(ptr, alignment, new_len, ret_addr); + } +} + +fn remap(context: *anyopaque, ptr: []u8, alignment: Alignment, new_len: usize, ret_addr: usize) ?[*]u8 { + const self: *Self = @ptrCast(@alignCast(context)); + if (self.callbacks.?.pfn_reallocation) |pfn_reallocation| { + return @ptrCast(pfn_reallocation(self.callbacks.?.p_user_data, ptr.ptr, new_len, alignment.toByteUnits(), self.scope)); + } else { + return getFallbackAllocator().rawRemap(ptr, alignment, new_len, ret_addr); + } +} + +fn free(context: *anyopaque, ptr: []u8, alignment: Alignment, ret_addr: usize) void { + const self: *Self = @ptrCast(@alignCast(context)); + if (self.callbacks.?.pfn_free) |pfn_free| { + return pfn_free(self.callbacks.?.p_user_data, ptr.ptr); + } else { + return getFallbackAllocator().rawFree(ptr, alignment, ret_addr); + } +} + +inline fn getFallbackAllocator() std.mem.Allocator { if (std.process.hasEnvVarConstant(DRIVER_DEBUG_ALLOCATOR_ENV_NAME) or builtin.mode == std.builtin.OptimizeMode.Debug) { @branchHint(.unlikely); return debug_allocator.allocator(); @@ -45,34 +81,3 @@ pub fn allocator(self: *const Self) Allocator { return std.heap.c_allocator; } } - -fn alloc(context: *anyopaque, len: usize, alignment: Alignment, _: usize) ?[*]u8 { - const self: *Self = @ptrCast(@alignCast(context)); - if (self.callbacks.?.pfn_allocation) |pfn_allocation| { - return @ptrCast(pfn_allocation(self.callbacks.?.p_user_data, len, alignment.toByteUnits(), self.scope)); - } - @panic("Null PFN_vkAllocationFunction passed to VkAllocationCallbacks"); -} - -fn resize(context: *anyopaque, ptr: []u8, alignment: Alignment, new_len: usize, _: usize) bool { - _ = alignment; - _ = context; - return new_len <= ptr.len; -} - -fn remap(context: *anyopaque, ptr: []u8, alignment: Alignment, new_len: usize, _: usize) ?[*]u8 { - const self: *Self = @ptrCast(@alignCast(context)); - if (self.callbacks.?.pfn_reallocation) |pfn_reallocation| { - return @ptrCast(pfn_reallocation(self.callbacks.?.p_user_data, ptr.ptr, new_len, alignment.toByteUnits(), self.scope)); - } - @panic("Null PFN_vkReallocationFunction passed to VkAllocationCallbacks"); -} - -fn free(context: *anyopaque, ptr: []u8, alignment: Alignment, _: usize) void { - _ = alignment; - const self: *Self = @ptrCast(@alignCast(context)); - if (self.callbacks.?.pfn_free) |pfn_free| { - return pfn_free(self.callbacks.?.p_user_data, ptr.ptr); - } - @panic("Null PFN_vkFreeFunction passed to VkAllocationCallbacks"); -} diff --git a/src/vulkan/lib.zig b/src/vulkan/lib.zig index a8ed39e..1375ca8 100644 --- a/src/vulkan/lib.zig +++ b/src/vulkan/lib.zig @@ -14,6 +14,7 @@ pub const Device = @import("Device.zig"); pub const PhysicalDevice = @import("PhysicalDevice.zig"); pub const Queue = @import("Queue.zig"); +pub const CommandBuffer = @import("CommandBuffer.zig"); pub const CommandPool = @import("CommandPool.zig"); pub const DeviceMemory = @import("DeviceMemory.zig"); pub const Fence = @import("Fence.zig"); diff --git a/test/c/main.c b/test/c/main.c index 7e7889e..6b036aa 100644 --- a/test/c/main.c +++ b/test/c/main.c @@ -62,9 +62,10 @@ int main(void) volkLoadDevice(device); VkQueue queue = kvfGetDeviceQueue(device, KVF_GRAPHICS_QUEUE); + VkCommandBuffer cmd = kvfCreateCommandBuffer(device); VkFence fence = kvfCreateFence(device); - kvfSubmitCommandBuffer(device, VK_NULL_HANDLE, KVF_GRAPHICS_QUEUE, VK_NULL_HANDLE, VK_NULL_HANDLE, fence, NULL); + kvfSubmitCommandBuffer(device, cmd, KVF_GRAPHICS_QUEUE, VK_NULL_HANDLE, VK_NULL_HANDLE, fence, NULL); kvfWaitForFence(device, fence); kvfDestroyFence(device, fence);