adding queues and debug allocators

This commit is contained in:
2025-11-11 23:55:44 +01:00
parent c6db045bbc
commit c1ed06945e
12 changed files with 128 additions and 43 deletions

View File

@@ -13,10 +13,16 @@ const Self = @This();
pub const ObjectType: vk.ObjectType = .device;
physical_device: *const PhysicalDevice,
dispatch_table: *const DispatchTable,
host_allocator: VulkanAllocator,
queues: std.AutoArrayHashMapUnmanaged(u32, std.ArrayListUnmanaged(*Dispatchable(Queue))),
dispatch_table: *const DispatchTable,
vtable: *const VTable,
pub const VTable = struct {
createQueue: *const fn (std.mem.Allocator, *const Self, u32, u32, vk.DeviceQueueCreateFlags) VkError!*Queue,
destroyQueue: *const fn (*Queue, std.mem.Allocator) VkError!void,
};
pub const DispatchTable = struct {
allocateMemory: *const fn (*Self, std.mem.Allocator, *const vk.MemoryAllocateInfo) VkError!*DeviceMemory,
createFence: *const fn (*Self, std.mem.Allocator, *const vk.FenceCreateInfo) VkError!*Fence,
@@ -29,15 +35,14 @@ pub const DispatchTable = struct {
};
pub fn init(allocator: std.mem.Allocator, physical_device: *const PhysicalDevice, info: *const vk.DeviceCreateInfo) VkError!Self {
const vulkan_allocator: *VulkanAllocator = @ptrCast(@alignCast(allocator.ptr));
var self: Self = .{
_ = allocator;
_ = info;
return .{
.physical_device = physical_device,
.dispatch_table = undefined,
.host_allocator = vulkan_allocator.*,
.queues = .empty,
.dispatch_table = undefined,
.vtable = undefined,
};
try self.createQueues(allocator, info);
return self;
}
pub fn createQueues(self: *Self, allocator: std.mem.Allocator, info: *const vk.DeviceCreateInfo) VkError!void {
@@ -46,22 +51,31 @@ pub fn createQueues(self: *Self, allocator: std.mem.Allocator, info: *const vk.D
} else if (info.p_queue_create_infos == null) {
return VkError.ValidationFailed;
}
var family_sizes: std.AutoHashMap(u32, usize) = .empty;
defer family_sizes.deinit(allocator);
for (0..info.queue_create_info_count) |i| {
const queue_info = info.p_queue_create_infos.?[i];
const value = family_sizes.getOrPut(allocator, queue_info.queue_family_index) catch return VkError.OutOfHostMemory;
value.value_ptr.* += @intCast(queue_info.queue_count);
}
const res = (self.queues.getOrPut(allocator, queue_info.queue_family_index) catch return VkError.OutOfHostMemory);
const family_ptr = res.value_ptr;
if (!res.found_existing) {
family_ptr.* = .empty;
}
var it = family_sizes.iterator();
while (it.next()) |entry| {
self.queues.put(allocator, entry.key_ptr.*, std.ArrayListUnmanaged(*Dispatchable(Queue)).initCapacity(allocator, entry.value_ptr.*)) catch return VkError.OutOfHostMemory;
const queue = try self.vtable.createQueue(allocator, self, queue_info.queue_family_index, @intCast(family_ptr.items.len), queue_info.flags);
const dispatchable_queue = try Dispatchable(Queue).wrap(allocator, queue);
family_ptr.append(allocator, dispatchable_queue) catch return VkError.OutOfHostMemory;
}
}
pub fn destroy(self: *Self, allocator: std.mem.Allocator) VkError!void {
var it = self.queues.iterator();
while (it.next()) |entry| {
const family = entry.value_ptr;
for (family.items) |dispatchable_queue| {
try self.vtable.destroyQueue(dispatchable_queue.object, allocator);
dispatchable_queue.destroy(allocator);
}
family.deinit(allocator);
}
self.queues.deinit(allocator);
try self.dispatch_table.destroy(self, allocator);
}

View File

@@ -20,11 +20,15 @@ pub const ObjectType: vk.ObjectType = .instance;
physical_devices: std.ArrayListUnmanaged(*Dispatchable(PhysicalDevice)),
dispatch_table: *const DispatchTable,
vtable: *const VTable,
pub const VTable = struct {
releasePhysicalDevices: *const fn (*Self, std.mem.Allocator) VkError!void,
requestPhysicalDevices: *const fn (*Self, std.mem.Allocator) VkError!void,
};
pub const DispatchTable = struct {
destroyInstance: *const fn (*Self, std.mem.Allocator) VkError!void,
releasePhysicalDevices: *const fn (*Self, std.mem.Allocator) VkError!void,
requestPhysicalDevices: *const fn (*Self, std.mem.Allocator) VkError!void,
};
pub fn init(allocator: std.mem.Allocator, infos: *const vk.InstanceCreateInfo) VkError!Self {
@@ -33,11 +37,12 @@ pub fn init(allocator: std.mem.Allocator, infos: *const vk.InstanceCreateInfo) V
return .{
.physical_devices = .empty,
.dispatch_table = undefined,
.vtable = undefined,
};
}
pub fn deinit(self: *Self, allocator: std.mem.Allocator) VkError!void {
try self.dispatch_table.releasePhysicalDevices(self, allocator);
try self.releasePhysicalDevices(allocator);
try self.dispatch_table.destroyInstance(self, allocator);
}
@@ -61,11 +66,11 @@ pub fn enumerateVersion(version: *u32) VkError!void {
}
pub fn releasePhysicalDevices(self: *Self, allocator: std.mem.Allocator) VkError!void {
try self.dispatch_table.releasePhysicalDevices(self, allocator);
try self.vtable.releasePhysicalDevices(self, allocator);
}
pub fn requestPhysicalDevices(self: *Self, allocator: std.mem.Allocator) VkError!void {
try self.dispatch_table.requestPhysicalDevices(self, allocator);
try self.vtable.requestPhysicalDevices(self, allocator);
if (self.physical_devices.items.len == 0) {
std.log.scoped(.vkCreateInstance).info("No VkPhysicalDevice found", .{});
return;

View File

@@ -22,6 +22,7 @@ pub const DispatchTable = struct {
};
pub fn init(allocator: std.mem.Allocator, device: *const Device, index: u32, family_index: u32, flags: vk.DeviceQueueCreateFlags) VkError!Self {
std.log.scoped(.vkCreateDevice).info("Creating device queue with family index {d} and index {d}", .{ family_index, index });
_ = allocator;
return .{
.owner = device,

View File

@@ -3,12 +3,16 @@
const std = @import("std");
const vk = @import("vulkan");
const builtin = @import("builtin");
const DRIVER_DEBUG_ALLOCATOR_ENV_NAME = @import("lib.zig").DRIVER_DEBUG_ALLOCATOR_ENV_NAME;
const Allocator = std.mem.Allocator;
const Alignment = std.mem.Alignment;
const Self = @This();
pub var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
callbacks: ?vk.AllocationCallbacks,
scope: vk.SystemAllocationScope,
@@ -21,22 +25,22 @@ pub fn init(callbacks: ?*const vk.AllocationCallbacks, scope: vk.SystemAllocatio
}
pub fn allocator(self: *const Self) Allocator {
if (self.callbacks == null) {
if (self.callbacks != null) {
return .{
.ptr = @constCast(self),
.vtable = std.heap.c_allocator.vtable,
.vtable = &.{
.alloc = alloc,
.resize = resize,
.remap = remap,
.free = free,
},
};
}
return .{
.ptr = @constCast(self),
.vtable = &.{
.alloc = alloc,
.resize = resize,
.remap = remap,
.free = free,
},
};
return if (std.process.hasEnvVarConstant(DRIVER_DEBUG_ALLOCATOR_ENV_NAME) or builtin.mode == std.builtin.OptimizeMode.Debug)
debug_allocator.allocator()
else
std.heap.c_allocator;
}
fn alloc(context: *anyopaque, len: usize, alignment: Alignment, _: usize) ?[*]u8 {

View File

@@ -18,7 +18,9 @@ pub const DeviceMemory = @import("DeviceMemory.zig");
pub const Fence = @import("Fence.zig");
pub const VULKAN_VENDOR_ID = @typeInfo(vk.VendorId).@"enum".fields[@typeInfo(vk.VendorId).@"enum".fields.len - 1].value + 1;
pub const DRIVER_LOGS_ENV_NAME = "STROLL_LOGS_LEVEL";
pub const DRIVER_DEBUG_ALLOCATOR_ENV_NAME = "STROLL_DEBUG_ALLOCATOR";
pub const std_options: std.Options = .{
.log_level = .debug,

View File

@@ -75,6 +75,7 @@ const device_pfn_map = std.StaticStringMap(vk.PfnVoidFunction).initComptime(.{
functionMapEntryPoint("vkDestroyDevice"),
functionMapEntryPoint("vkCreateFence"),
functionMapEntryPoint("vkFreeMemory"),
functionMapEntryPoint("vkGetDeviceQueue"),
functionMapEntryPoint("vkGetFenceStatus"),
functionMapEntryPoint("vkMapMemory"),
functionMapEntryPoint("vkUnmapMemory"),
@@ -173,6 +174,13 @@ pub export fn strollDestroyInstance(p_instance: vk.Instance, callbacks: ?*const
const dispatchable = Dispatchable(Instance).fromHandle(p_instance) catch return;
dispatchable.object.deinit(allocator) catch {};
dispatchable.destroy(allocator);
if (std.process.hasEnvVarConstant(lib.DRIVER_DEBUG_ALLOCATOR_ENV_NAME) or builtin.mode == std.builtin.OptimizeMode.Debug) {
// All host memory allocations should've been freed by now
if (!VulkanAllocator.debug_allocator.detectLeaks()) {
std.log.scoped(.vkDestroyInstance).debug("No memory leaks detected", .{});
}
}
}
pub export fn strollEnumeratePhysicalDevices(p_instance: vk.Instance, count: *u32, p_devices: ?[*]vk.PhysicalDevice) callconv(vk.vulkan_call_conv) vk.Result {
@@ -330,6 +338,22 @@ pub export fn strollGetDeviceProcAddr(p_device: vk.Device, p_name: ?[*:0]const u
return null;
}
pub export fn strollGetDeviceQueue(p_device: vk.Device, queue_family_index: u32, queue_index: u32, p_queue: *vk.Queue) callconv(vk.vulkan_call_conv) void {
p_queue.* = .null_handle;
const device = Dispatchable(Device).fromHandleObject(p_device) catch return;
if (device.queues.get(queue_family_index)) |family| {
if (queue_index >= family.items.len) return;
const dispatchable_queue = family.items[queue_index];
const queue = dispatchable_queue.object;
// https://docs.vulkan.org/refpages/latest/refpages/source/vkGetDeviceQueue.html#VUID-vkGetDeviceQueue-flags-01841
if (queue.flags != @TypeOf(queue.flags){}) return;
p_queue.* = dispatchable_queue.toVkHandle(vk.Queue);
}
}
pub export fn strollGetFenceStatus(p_device: vk.Device, p_fence: vk.Fence) callconv(vk.vulkan_call_conv) vk.Result {
const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err);
const fence = NonDispatchable(Fence).fromHandleObject(p_fence) catch |err| return toVkResult(err);

View File

@@ -45,10 +45,9 @@ pub fn log(comptime level: std.log.Level, comptime scope: @Type(.enum_literal),
const prefix = std.fmt.comptimePrint("{s: <8}", .{"[" ++ comptime level.asText() ++ "] "});
const level_color: std.Io.tty.Color = switch (level) {
.info => .blue,
.info, .debug => .blue,
.warn => .yellow,
.err => .red,
.debug => .blue,
};
std.debug.lockStdErr();