adding command buffer pool logic

This commit is contained in:
2025-11-15 21:29:08 +01:00
parent fbdce598e3
commit 027bd2ce1c
8 changed files with 65 additions and 33 deletions

View File

@@ -2,9 +2,12 @@ const std = @import("std");
const vk = @import("vulkan"); const vk = @import("vulkan");
const base = @import("base"); const base = @import("base");
const NonDispatchable = base.NonDispatchable;
const VkError = base.VkError; const VkError = base.VkError;
const Device = base.Device; const Device = base.Device;
const SoftCommandBuffer = @import("SoftCommandBuffer.zig");
const Self = @This(); const Self = @This();
pub const Interface = base.CommandPool; pub const Interface = base.CommandPool;
@@ -28,10 +31,18 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
return self; return self;
} }
pub fn allocateCommandBuffers(interface: *Interface, info: *const vk.CommandBufferAllocateInfo) VkError![]*base.CommandBuffer { pub fn allocateCommandBuffers(interface: *Interface, info: *const vk.CommandBufferAllocateInfo) VkError!void {
_ = interface; const allocator = interface.host_allocator.allocator();
_ = info;
return VkError.FeatureNotPresent; while (interface.buffers.capacity < interface.buffers.items.len + info.command_buffer_count) {
interface.buffers.ensureUnusedCapacity(allocator, base.CommandPool.BUFFER_POOL_BASE_CAPACITY) catch return VkError.OutOfHostMemory;
}
for (0..info.command_buffer_count) |_| {
const cmd = try SoftCommandBuffer.create(interface.owner, allocator, info);
const non_dis_cmd = try NonDispatchable(base.CommandBuffer).wrap(allocator, &cmd.interface);
interface.buffers.appendAssumeCapacity(non_dis_cmd);
}
} }
pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void { pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {

View File

@@ -7,6 +7,7 @@ const cpuinfo = @import("cpuinfo");
const SoftDevice = @import("SoftDevice.zig"); const SoftDevice = @import("SoftDevice.zig");
const VkError = base.VkError; const VkError = base.VkError;
const VulkanAllocator = base.VulkanAllocator;
const Self = @This(); const Self = @This();
pub const Interface = base.PhysicalDevice; pub const Interface = base.PhysicalDevice;
@@ -14,6 +15,8 @@ pub const Interface = base.PhysicalDevice;
interface: Interface, interface: Interface,
pub fn create(allocator: std.mem.Allocator, instance: *const base.Instance) VkError!*Self { pub fn create(allocator: std.mem.Allocator, instance: *const base.Instance) VkError!*Self {
const command_allocator = VulkanAllocator.from(allocator).cloneWithScope(.command).allocator();
const self = allocator.create(Self) catch return VkError.OutOfHostMemory; const self = allocator.create(Self) catch return VkError.OutOfHostMemory;
errdefer allocator.destroy(self); errdefer allocator.destroy(self);
@@ -81,8 +84,8 @@ pub fn create(allocator: std.mem.Allocator, instance: *const base.Instance) VkEr
interface.queue_family_props.appendSlice(allocator, queue_family_props[0..]) catch return VkError.OutOfHostMemory; interface.queue_family_props.appendSlice(allocator, queue_family_props[0..]) catch return VkError.OutOfHostMemory;
// TODO: use Pytorch's cpuinfo someday // TODO: use Pytorch's cpuinfo someday
const info = cpuinfo.get(allocator) catch return VkError.InitializationFailed; const info = cpuinfo.get(command_allocator) catch return VkError.InitializationFailed;
defer info.deinit(allocator); defer info.deinit(command_allocator);
var writer = std.Io.Writer.fixed(interface.props.device_name[0 .. vk.MAX_PHYSICAL_DEVICE_NAME_SIZE - 1]); var writer = std.Io.Writer.fixed(interface.props.device_name[0 .. vk.MAX_PHYSICAL_DEVICE_NAME_SIZE - 1]);
writer.print("{s} [" ++ root.DRIVER_NAME ++ " StrollDriver]", .{info.name}) catch return VkError.InitializationFailed; writer.print("{s} [" ++ root.DRIVER_NAME ++ " StrollDriver]", .{info.name}) catch return VkError.InitializationFailed;

View File

@@ -25,5 +25,6 @@ pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.Comma
} }
pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void { pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void {
std.debug.print("{any}\n", .{self});
self.vtable.destroy(self, allocator); self.vtable.destroy(self, allocator);
} }

View File

@@ -3,25 +3,32 @@ const vk = @import("vulkan");
const VkError = @import("error_set.zig").VkError; const VkError = @import("error_set.zig").VkError;
const VulkanAllocator = @import("VulkanAllocator.zig"); const VulkanAllocator = @import("VulkanAllocator.zig");
const NonDispatchable = @import("NonDispatchable.zig").NonDispatchable;
const CommandBuffer = @import("CommandBuffer.zig"); const CommandBuffer = @import("CommandBuffer.zig");
const Device = @import("Device.zig"); const Device = @import("Device.zig");
const Self = @This(); const Self = @This();
pub const ObjectType: vk.ObjectType = .command_pool; pub const ObjectType: vk.ObjectType = .command_pool;
const BUFFER_POOL_BASE_CAPACITY = 64; pub const BUFFER_POOL_BASE_CAPACITY = 64;
owner: *Device, owner: *Device,
flags: vk.CommandPoolCreateFlags, flags: vk.CommandPoolCreateFlags,
queue_family_index: u32, queue_family_index: u32,
buffers: std.ArrayList(*CommandBuffer),
first_free_buffer_index: usize,
host_allocator: VulkanAllocator, host_allocator: VulkanAllocator,
/// Contiguous dynamic array of command buffers with free ones
/// grouped at the end and the first free index being storesd in
/// `first_free_buffer_index`
/// When freed swaps happen to keep the free buffers at the end
buffers: std.ArrayList(*NonDispatchable(CommandBuffer)),
first_free_buffer_index: usize,
vtable: *const VTable, vtable: *const VTable,
pub const VTable = struct { pub const VTable = struct {
allocateCommandBuffers: *const fn (*Self, *const vk.CommandBufferAllocateInfo) VkError![]*CommandBuffer, allocateCommandBuffers: *const fn (*Self, *const vk.CommandBufferAllocateInfo) VkError!void,
destroy: *const fn (*Self, std.mem.Allocator) void, destroy: *const fn (*Self, std.mem.Allocator) void,
reset: *const fn (*Self, vk.CommandPoolResetFlags) VkError!void, reset: *const fn (*Self, vk.CommandPoolResetFlags) VkError!void,
}; };
@@ -31,18 +38,29 @@ pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.Comma
.owner = device, .owner = device,
.flags = info.flags, .flags = info.flags,
.queue_family_index = info.queue_family_index, .queue_family_index = info.queue_family_index,
.buffers = std.ArrayList(*CommandBuffer).initCapacity(allocator, BUFFER_POOL_BASE_CAPACITY) catch return VkError.OutOfHostMemory,
.host_allocator = VulkanAllocator.from(allocator).clone(), .host_allocator = VulkanAllocator.from(allocator).clone(),
.buffers = std.ArrayList(*NonDispatchable(CommandBuffer)).initCapacity(allocator, BUFFER_POOL_BASE_CAPACITY) catch return VkError.OutOfHostMemory,
.first_free_buffer_index = 0, .first_free_buffer_index = 0,
.vtable = undefined, .vtable = undefined,
}; };
} }
pub inline fn allocateCommandBuffers(self: *Self, info: *const vk.CommandBufferAllocateInfo) VkError![]*CommandBuffer { pub fn allocateCommandBuffers(self: *Self, info: *const vk.CommandBufferAllocateInfo) VkError![]*NonDispatchable(CommandBuffer) {
return self.vtable.allocateCommandBuffers(self, info); if (self.buffers.items.len < info.command_buffer_count or self.first_free_buffer_index + info.command_buffer_count > self.buffers.items.len) {
try self.vtable.allocateCommandBuffers(self, info);
}
const bound_up = self.first_free_buffer_index + info.command_buffer_count;
const slice = self.buffers.items[self.first_free_buffer_index..bound_up];
self.first_free_buffer_index += info.command_buffer_count;
return slice;
} }
pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void { pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void {
for (self.buffers.items) |non_dis_cmd| {
non_dis_cmd.object.destroy(allocator);
non_dis_cmd.destroy(allocator);
}
self.buffers.deinit(allocator); self.buffers.deinit(allocator);
self.vtable.destroy(self, allocator); self.vtable.destroy(self, allocator);
} }

View File

@@ -2,6 +2,7 @@ const std = @import("std");
const vk = @import("vulkan"); const vk = @import("vulkan");
const Dispatchable = @import("Dispatchable.zig").Dispatchable; const Dispatchable = @import("Dispatchable.zig").Dispatchable;
const NonDispatchable = @import("NonDispatchable.zig").NonDispatchable;
const VulkanAllocator = @import("VulkanAllocator.zig"); const VulkanAllocator = @import("VulkanAllocator.zig");
const VkError = @import("error_set.zig").VkError; const VkError = @import("error_set.zig").VkError;
@@ -29,7 +30,7 @@ pub const VTable = struct {
}; };
pub const DispatchTable = struct { pub const DispatchTable = struct {
allocateCommandBuffers: *const fn (*Self, *const vk.CommandBufferAllocateInfo) VkError![]*CommandBuffer, allocateCommandBuffers: *const fn (*Self, *const vk.CommandBufferAllocateInfo) VkError![]*NonDispatchable(CommandBuffer),
allocateMemory: *const fn (*Self, std.mem.Allocator, *const vk.MemoryAllocateInfo) VkError!*DeviceMemory, allocateMemory: *const fn (*Self, std.mem.Allocator, *const vk.MemoryAllocateInfo) VkError!*DeviceMemory,
createCommandPool: *const fn (*Self, std.mem.Allocator, *const vk.CommandPoolCreateInfo) VkError!*CommandPool, createCommandPool: *const fn (*Self, std.mem.Allocator, *const vk.CommandPoolCreateInfo) VkError!*CommandPool,
createFence: *const fn (*Self, std.mem.Allocator, *const vk.FenceCreateInfo) VkError!*Fence, createFence: *const fn (*Self, std.mem.Allocator, *const vk.FenceCreateInfo) VkError!*Fence,
@@ -112,7 +113,7 @@ pub inline fn waitForFences(self: *Self, fences: []*Fence, waitForAll: bool, tim
// Command Pool functions ============================================================================================================================ // Command Pool functions ============================================================================================================================
pub inline fn allocateCommandBuffers(self: *Self, info: *const vk.CommandBufferAllocateInfo) VkError![]*CommandBuffer { pub inline fn allocateCommandBuffers(self: *Self, info: *const vk.CommandBufferAllocateInfo) VkError![]*NonDispatchable(CommandBuffer) {
return self.dispatch_table.allocateCommandBuffers(self, info); return self.dispatch_table.allocateCommandBuffers(self, info);
} }

View File

@@ -1,6 +1,7 @@
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
const vk = @import("vulkan"); const vk = @import("vulkan");
const VkError = @import("error_set.zig").VkError; const VkError = @import("error_set.zig").VkError;
const Dispatchable = @import("Dispatchable.zig").Dispatchable; const Dispatchable = @import("Dispatchable.zig").Dispatchable;
const PhysicalDevice = @import("PhysicalDevice.zig"); const PhysicalDevice = @import("PhysicalDevice.zig");

View File

@@ -36,16 +36,16 @@ pub fn NonDispatchable(comptime T: type) type {
if (handle == 0) { if (handle == 0) {
return VkError.Unknown; return VkError.Unknown;
} }
const nondispatchable: *Self = @ptrFromInt(handle); const non_dispatchable: *Self = @ptrFromInt(handle);
if (nondispatchable.object_type != T.ObjectType) { if (non_dispatchable.object_type != T.ObjectType) {
return VkError.Unknown; return VkError.Unknown;
} }
return nondispatchable; return non_dispatchable;
} }
pub inline fn fromHandleObject(handle: anytype) VkError!*T { pub inline fn fromHandleObject(handle: anytype) VkError!*T {
const nondispatchable_handle = try Self.fromHandle(handle); const non_dispatchable_handle = try Self.fromHandle(handle);
return nondispatchable_handle.object; return non_dispatchable_handle.object;
} }
}; };
} }

View File

@@ -290,12 +290,9 @@ pub export fn strollAllocateCommandBuffers(p_device: vk.Device, p_info: ?*const
return .error_validation_failed; return .error_validation_failed;
} }
const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err); const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err);
const pool = NonDispatchable(CommandPool).fromHandleObject(info.command_pool) catch |err| return toVkResult(err);
const allocator = pool.host_allocator.allocator();
const cmds = device.allocateCommandBuffers(info) catch |err| return toVkResult(err); const cmds = device.allocateCommandBuffers(info) catch |err| return toVkResult(err);
for (cmds[0..info.command_buffer_count], 0..) |cmd, i| { for (cmds[0..info.command_buffer_count], 0..) |cmd, i| {
p_cmds[i] = (NonDispatchable(CommandBuffer).wrap(allocator, cmd) catch |err| return toVkResult(err)).toVkHandle(vk.CommandBuffer); p_cmds[i] = cmd.toVkHandle(vk.CommandBuffer);
} }
return .success; return .success;
} }
@@ -336,6 +333,15 @@ pub export fn strollCreateFence(p_device: vk.Device, p_info: ?*const vk.FenceCre
return .success; return .success;
} }
pub export fn strollDestroyCommandPool(p_device: vk.Device, p_pool: vk.CommandPool, callbacks: ?*const vk.AllocationCallbacks) callconv(vk.vulkan_call_conv) void {
const allocator = VulkanAllocator.init(callbacks, .object).allocator();
const device = Dispatchable(Device).fromHandleObject(p_device) catch return;
const non_dispatchable_pool = NonDispatchable(CommandPool).fromHandle(p_pool) catch return;
device.destroyCommandPool(allocator, non_dispatchable_pool.object) catch return;
non_dispatchable_pool.destroy(allocator);
}
pub export fn strollDestroyDevice(p_device: vk.Device, callbacks: ?*const vk.AllocationCallbacks) callconv(vk.vulkan_call_conv) void { pub export fn strollDestroyDevice(p_device: vk.Device, callbacks: ?*const vk.AllocationCallbacks) callconv(vk.vulkan_call_conv) void {
const allocator = VulkanAllocator.init(callbacks, .object).allocator(); const allocator = VulkanAllocator.init(callbacks, .object).allocator();
const dispatchable = Dispatchable(Device).fromHandle(p_device) catch return; const dispatchable = Dispatchable(Device).fromHandle(p_device) catch return;
@@ -347,15 +353,6 @@ pub export fn strollDestroyDevice(p_device: vk.Device, callbacks: ?*const vk.All
dispatchable.destroy(allocator); dispatchable.destroy(allocator);
} }
pub export fn strollDestroyCommandPool(p_device: vk.Device, p_pool: vk.CommandPool, callbacks: ?*const vk.AllocationCallbacks) callconv(vk.vulkan_call_conv) void {
const allocator = VulkanAllocator.init(callbacks, .object).allocator();
const device = Dispatchable(Device).fromHandleObject(p_device) catch return;
const non_dispatchable_pool = NonDispatchable(CommandPool).fromHandle(p_pool) catch return;
device.destroyCommandPool(allocator, non_dispatchable_pool.object) catch return;
non_dispatchable_pool.destroy(allocator);
}
pub export fn strollDestroyFence(p_device: vk.Device, p_fence: vk.Fence, callbacks: ?*const vk.AllocationCallbacks) callconv(vk.vulkan_call_conv) void { pub export fn strollDestroyFence(p_device: vk.Device, p_fence: vk.Fence, callbacks: ?*const vk.AllocationCallbacks) callconv(vk.vulkan_call_conv) void {
const allocator = VulkanAllocator.init(callbacks, .object).allocator(); const allocator = VulkanAllocator.init(callbacks, .object).allocator();
const device = Dispatchable(Device).fromHandleObject(p_device) catch return; const device = Dispatchable(Device).fromHandleObject(p_device) catch return;