architectural rework

This commit is contained in:
2025-11-03 15:40:15 +01:00
parent a2948bd65d
commit 6b1d525e40
13 changed files with 281 additions and 121 deletions

View File

@@ -1,48 +1,85 @@
const std = @import("std");
const vk = @import("vulkan");
const root = @import("lib.zig");
const dispatchable = @import("dispatchable.zig");
const VulkanAllocator = @import("VulkanAllocator.zig");
const PhysicalDevice = @import("PhysicalDevice.zig");
extern fn __vkImplInstanceInit(*Self, *const std.mem.Allocator) ?*anyopaque;
const Self = @This();
pub const ObjectType: vk.ObjectType = .instance;
alloc_callbacks: vk.AllocationCallbacks,
alloc_callbacks: ?vk.AllocationCallbacks,
physical_devices: std.ArrayList(vk.PhysicalDevice),
dispatch_table: DispatchTable,
driver_data: ?*anyopaque,
vtable: VTable,
pub const DispatchTable = struct {
destroyInstance: ?*const fn (*const Self, std.mem.Allocator) anyerror!void = null,
enumerateInstanceVersion: ?vk.PfnEnumerateInstanceVersion = null,
//enumerateInstanceLayerProperties: vk.PfnEnumerateInstanceProperties = null,
enumerateInstanceExtensionProperties: ?vk.PfnEnumerateInstanceExtensionProperties = null,
};
pub fn init(self: *Self, p_infos: ?*const vk.InstanceCreateInfo, callbacks: ?*const vk.AllocationCallbacks) !void {
const infos = p_infos orelse return error.NullCreateInfos;
pub fn create(p_infos: ?*const vk.InstanceCreateInfo, callbacks: ?*const vk.AllocationCallbacks, p_instance: *vk.Instance) callconv(vk.vulkan_call_conv) vk.Result {
const infos = p_infos orelse return .error_initialization_failed;
if (infos.s_type != .instance_create_info) {
return error.InvalidCreateInfos;
return .error_initialization_failed;
}
self.vtable = .{};
const deref_callbacks = if (callbacks) |c| c.* else null;
if (callbacks) |c| {
self.alloc_callbacks = c.*;
const allocator = VulkanAllocator.init(deref_callbacks, .instance).allocator();
const dispatchable_instance = dispatchable.Dispatchable(Self).create(allocator) catch return .error_out_of_host_memory;
const self = dispatchable_instance.object;
self.dispatch_table = .{};
self.alloc_callbacks = deref_callbacks;
self.physical_devices = .empty;
self.driver_data = __vkImplInstanceInit(self, &allocator) orelse return .error_initialization_failed;
std.debug.assert(self.physical_devices.items.len != 0);
p_instance.* = @enumFromInt(dispatchable_instance.toHandle());
return .success;
}
pub fn destroy(p_instance: vk.Instance, callbacks: ?*const vk.AllocationCallbacks) callconv(vk.vulkan_call_conv) void {
const allocator = VulkanAllocator.init(if (callbacks) |c| c.* else null, .instance).allocator();
const dispatchable_instance = dispatchable.fromHandle(Self, @intFromEnum(p_instance)) catch return;
defer dispatchable_instance.destroy(allocator);
const self: *const Self = @ptrCast(dispatchable_instance.object);
if (self.dispatch_table.destroyInstance) |pfnDestroyInstance| {
pfnDestroyInstance(self, allocator) catch return;
} else if (std.process.hasEnvVar(allocator, root.DRIVER_LOGS_ENV_NAME) catch false) {
std.log.scoped(.vkDestroyInstance).warn("Missing dispatch implementation", .{});
}
}
pub fn getProcAddr(self: *const Self, name: []const u8) vk.PfnVoidFunction {
pub fn enumeratePhysicalDevices(p_instance: vk.Instance, count: *u32, p_devices: ?[*]vk.PhysicalDevice) callconv(vk.vulkan_call_conv) vk.Result {
const self = dispatchable.fromHandleObject(Self, @intFromEnum(p_instance)) catch return .error_unknown;
count.* = @intCast(self.physical_devices.items.len);
if (p_devices) |devices| {
@memcpy(devices[0..self.physical_devices.items.len], self.physical_devices.items);
}
return .success;
}
pub fn getProcAddr(name: []const u8) vk.PfnVoidFunction {
const allocator = std.heap.c_allocator;
const KV = struct { []const u8, vk.PfnVoidFunction };
const pfn_map = std.StaticStringMap(vk.PfnVoidFunction).init([_]KV{
.{ "vkDestroyInstance", @ptrCast(self.vtable.destroyInstance) },
.{ "vkEnumeratePhysicalDevices", @ptrCast(self.vtable.enumeratePhysicalDevices) },
.{ "vkEnumerateInstanceVersion", @ptrCast(self.vtable.enumerateInstanceVersion) },
.{ "vkEnumerateInstanceExtensionProperties", @ptrCast(self.vtable.enumerateInstanceExtensionProperties) },
.{ "vkGetPhysicalDeviceProperties", @ptrCast(self.vtable.getPhysicalDeviceProperties) },
.{ "vkDestroyInstance", @ptrCast(&destroy) },
.{ "vkEnumeratePhysicalDevices", @ptrCast(&enumeratePhysicalDevices) },
//.{ "vkGetPhysicalDeviceProperties", @ptrCast(self.dispatch_table.getPhysicalDeviceProperties) },
}, allocator) catch return null;
defer pfn_map.deinit(allocator);
return if (pfn_map.get(name)) |pfn| pfn else null;
// Falling back on PhysicalDevice's getProcAddr which will return null if not found
return if (pfn_map.get(name)) |pfn| pfn else PhysicalDevice.getProcAddr(name);
}
pub const VTable = struct {
destroyInstance: ?vk.PfnDestroyInstance = null,
enumeratePhysicalDevices: ?vk.PfnEnumeratePhysicalDevices = null,
enumerateInstanceVersion: ?vk.PfnEnumerateInstanceVersion = null,
//enumerateInstanceLayerProperties: vk.PfnEnumerateInstanceProperties = null,
enumerateInstanceExtensionProperties: ?vk.PfnEnumerateInstanceExtensionProperties = null,
getPhysicalDeviceProperties: ?vk.PfnGetPhysicalDeviceProperties = null,
};

View File

@@ -1,8 +1,57 @@
const std = @import("std");
const vk = @import("vulkan");
const root = @import("lib.zig");
const Instance = @import("Instance.zig");
const dispatchable = @import("dispatchable.zig");
const Self = @This();
pub const ObjectType: vk.ObjectType = .physical_device;
props: vk.PhysicalDeviceProperties,
queue_families: [3]vk.QueueFamilyProperties,
instance: *const Instance,
dispatch_table: DispatchTable,
driver_data: ?*anyopaque,
pub const DispatchTable = struct {};
pub fn init(instance: *const Instance, allocator: std.mem.Allocator) !*dispatchable.Dispatchable(Self) {
const dispatchable_physical_device = try dispatchable.Dispatchable(Self).create(allocator);
errdefer dispatchable_physical_device.destroy(allocator);
const self = dispatchable_physical_device.object;
self.props = .{
.api_version = undefined,
.driver_version = undefined,
.vendor_id = root.VULKAN_VENDOR_ID,
.device_id = undefined,
.device_type = undefined,
.device_name = [_]u8{0} ** vk.MAX_PHYSICAL_DEVICE_NAME_SIZE,
.pipeline_cache_uuid = undefined,
.limits = undefined,
.sparse_properties = undefined,
};
self.driver_data = null;
self.instance = instance;
self.dispatch_table = .{};
return dispatchable_physical_device;
}
pub fn getProperties(p_physical_device: vk.PhysicalDevice, properties: *vk.PhysicalDeviceProperties) callconv(vk.vulkan_call_conv) void {
const self = dispatchable.fromHandleObject(Self, @intFromEnum(p_physical_device)) catch return;
properties.* = self.props;
}
pub fn getProcAddr(name: []const u8) vk.PfnVoidFunction {
const allocator = std.heap.c_allocator;
const KV = struct { []const u8, vk.PfnVoidFunction };
const pfn_map = std.StaticStringMap(vk.PfnVoidFunction).init([_]KV{
.{ "vkGetPhysicalDeviceProperties", @ptrCast(&getProperties) },
}, allocator) catch return null;
defer pfn_map.deinit(allocator);
return if (pfn_map.get(name)) |pfn| pfn else null;
}

65
src/vulkan/VulkanAllocator.zig git.filemode.normal_file
View File

@@ -0,0 +1,65 @@
const std = @import("std");
const vk = @import("vulkan");
const Allocator = std.mem.Allocator;
const Alignment = std.mem.Alignment;
/// A Zig allocator from VkAllocationCallbacks.
/// Falls back on c_allocator if callbacks passed are null
const Self = @This();
callbacks: ?vk.AllocationCallbacks,
scope: vk.SystemAllocationScope,
pub fn init(callbacks: ?vk.AllocationCallbacks, scope: vk.SystemAllocationScope) Self {
return .{
.callbacks = callbacks,
.scope = scope,
};
}
pub fn allocator(self: *const Self) Allocator {
if (self.callbacks == null) {
return std.heap.c_allocator; // TODO: fallback on a better allocator
}
return .{
.ptr = @constCast(self),
.vtable = &.{
.alloc = alloc,
.resize = resize,
.remap = remap,
.free = free,
},
};
}
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");
}

View File

@@ -1,5 +1,6 @@
const std = @import("std");
const vk = @import("vulkan");
const root = @import("lib.zig");
const c = @cImport({
@cInclude("vulkan/vk_icd.h");
});
@@ -11,18 +12,20 @@ pub fn getInstanceProcAddr(global_pfn_map: std.StaticStringMap(vk.PfnVoidFunctio
const allocator = std.heap.c_allocator;
const get_proc_log = std.log.scoped(.vkGetInstanceProcAddr);
if (std.process.hasEnvVar(allocator, "DRIVER_LOGS") catch false) {
if (std.process.hasEnvVar(allocator, root.DRIVER_LOGS_ENV_NAME) catch false) {
get_proc_log.info("Loading {s}...", .{name});
}
if (global_pfn_map.get(name)) |pfn| {
return pfn;
}
const instance = dispatchable.fromHandle(Instance, @intFromEnum(p_instance)) catch |e| {
if (std.process.hasEnvVar(allocator, "DRIVER_LOGS") catch false) {
// Checks if instance is NULL
_ = dispatchable.fromHandle(Instance, @intFromEnum(p_instance)) catch |e| {
if (std.process.hasEnvVar(allocator, root.DRIVER_LOGS_ENV_NAME) catch false) {
get_proc_log.err("{any}", .{e});
}
return null;
};
return instance.object.getProcAddr(name);
return Instance.getProcAddr(name);
}

View File

@@ -6,8 +6,10 @@ pub const dispatchable = @import("dispatchable.zig");
pub const Instance = @import("Instance.zig");
pub const PhysicalDevice = @import("PhysicalDevice.zig");
pub const VulkanAllocator = @import("VulkanAllocator.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 = "DRIVER_LOGS";
pub const std_options: std.Options = .{
.log_level = .info,
@@ -23,6 +25,29 @@ pub fn logFn(comptime level: std.log.Level, comptime scope: @Type(.enum_literal)
nosuspend stderr.print(format ++ "\n", args) catch return;
}
pub fn retrieveDriverDataAs(handle: anytype, comptime T: type) !*T {
comptime {
switch (@typeInfo(@TypeOf(handle))) {
.pointer => |p| std.debug.assert(@hasField(p.child, "driver_data")),
else => @compileError("Invalid type passed to 'retrieveDriverDataAs': " ++ @typeName(@TypeOf(handle))),
}
}
return @ptrCast(@alignCast(@field(handle, "driver_data")));
}
const global_pfn_map = std.StaticStringMap(vk.PfnVoidFunction).initComptime(.{
.{ "vkGetInstanceProcAddr", @as(vk.PfnVoidFunction, @ptrCast(&icd.getInstanceProcAddr)) },
.{ "vkCreateInstance", @as(vk.PfnVoidFunction, @ptrCast(&Instance.create)) },
});
pub export fn vkGetInstanceProcAddr(p_instance: vk.Instance, pName: ?[*:0]const u8) callconv(vk.vulkan_call_conv) vk.PfnVoidFunction {
if (pName == null) {
return null;
}
const name = std.mem.span(pName.?);
return icd.getInstanceProcAddr(global_pfn_map, p_instance, name);
}
test {
std.testing.refAllDeclsRecursive(@This());
}