diff --git a/.gdb_history b/.gdb_history new file mode 100644 index 0000000..f258323 --- /dev/null +++ b/.gdb_history @@ -0,0 +1,96 @@ +run +bt +q +run +bt +q +rim +run +bt +q +run +bt +q +run +bt +run +bt +q +c +start +n +n +n +n +s +n +n +s +n +p vkDestroyInstance +p vkDestroyInstance +start +n +n +n +n +s +n +s +s +start +n +n +n +s +n +s +s +q +run +q +run +bt +p name +c +p name +n +bt +q +r +bt +c +bt +w +y +q +run +bt +q +run +bt +q +q +run +bt +q +run +bt +q +q +q +q +run +bt +q +q +run +bt +q +run +bt +q +q +run +bt +q diff --git a/build.zig b/build.zig index 8e6af41..c590b79 100644 --- a/build.zig +++ b/build.zig @@ -21,6 +21,7 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/vulkan/lib.zig"), .target = target, .optimize = optimize, + .link_libc = true, }); const vulkan_headers = b.dependency("vulkan_headers", .{}); @@ -36,6 +37,7 @@ pub fn build(b: *std.Build) void { const lib_mod = b.createModule(.{ .root_source_file = b.path(impl.root_source_file), .target = target, + .link_libc = true, .optimize = optimize, .imports = &.{ .{ .name = "common", .module = common_mod }, @@ -72,6 +74,8 @@ pub fn build(b: *std.Build) void { .flags = &.{b.fmt("-DLIBVK=\"{s}\"", .{lib.name})}, }); + b.installArtifact(c_test_exe); + const run_c_test = b.addRunArtifact(c_test_exe); const test_c_step = b.step(b.fmt("test-c-{s}", .{impl.name}), b.fmt("Run lib{s} C test", .{impl.name})); test_c_step.dependOn(b.getInstallStep()); diff --git a/src/soft/Instance.zig b/src/soft/Instance.zig new file mode 100644 index 0000000..cb124c5 --- /dev/null +++ b/src/soft/Instance.zig @@ -0,0 +1,48 @@ +const std = @import("std"); +const vk = @import("vulkan"); +const common = @import("common"); +const PhysicalDevice = @import("PhysicalDevice.zig"); + +const dispatchable = common.dispatchable; + +const Self = @This(); +pub const ObjectType: vk.ObjectType = .instance; + +common_instance: common.Instance, +physical_device: dispatchable.Dispatchable(PhysicalDevice), // Software driver only has one physical device (CPU) + +pub fn create(p_infos: ?*const vk.InstanceCreateInfo, callbacks: ?*const vk.AllocationCallbacks, p_instance: *vk.Instance) callconv(vk.vulkan_call_conv) vk.Result { + const allocator = std.heap.c_allocator; + + const dispatchable_object = dispatchable.Dispatchable(Self).create(allocator, ObjectType) catch return .error_out_of_host_memory; + common.Instance.init(&dispatchable_object.object.common_instance, p_infos, callbacks) catch return .error_initialization_failed; + + dispatchable_object.object.common_instance.vtable = .{ + .destroyInstance = destroy, + .enumeratePhysicalDevices = enumeratePhysicalDevices, + .enumerateInstanceVersion = null, + //.enumerateInstanceLayerProperties = null, + .enumerateInstanceExtensionProperties = null, + }; + + dispatchable_object.object.physical_device.init() catch return .error_initialization_failed; + + p_instance.* = @enumFromInt(dispatchable.toHandle(Self, dispatchable_object)); + return .success; +} + +pub fn enumeratePhysicalDevices(p_instance: vk.Instance, count: *u32, devices: *vk.PhysicalDevice) callconv(vk.vulkan_call_conv) vk.Result { + const dispatchable_object = common.dispatchable.fromHandle(Self, @intFromEnum(p_instance)) catch return .error_initialization_failed; + _ = dispatchable_object; + _ = count; + _ = devices; + return .success; +} + +pub fn destroy(p_instance: vk.Instance, callbacks: ?*const vk.AllocationCallbacks) callconv(vk.vulkan_call_conv) void { + const allocator = std.heap.c_allocator; + _ = callbacks; + + const dispatchable_object = common.dispatchable.fromHandle(Self, @intFromEnum(p_instance)) catch return; + dispatchable_object.destroy(allocator); +} diff --git a/src/soft/PhysicalDevice.zig b/src/soft/PhysicalDevice.zig new file mode 100644 index 0000000..2fc617d --- /dev/null +++ b/src/soft/PhysicalDevice.zig @@ -0,0 +1,26 @@ +const std = @import("std"); +const vk = @import("vulkan"); +const Instance = @import("Instance.zig"); +const common = @import("common"); + +const dispatchable = common.dispatchable; + +const Self = @This(); +const ObjectType: vk.ObjectType = .physical_device; + +instance: *const Instance, +common_physical_device: common.PhysicalDevice, + +pub fn init(self: *Self) !void { + self.common_physical_device.props = .{ + .apiVersion = , + .driverVersion = VKD_DRIVER_VERSION, + .vendorID = 0x0601, + .deviceID = 0x060103, + .deviceType = VK_PHYSICAL_DEVICE_TYPE_CPU, + .deviceName = {}, + .pipelineCacheUUID = {}, + .limits = {}, + .sparseProperties = {}, + }; +} diff --git a/src/soft/lib.zig b/src/soft/lib.zig index aaf1ad4..03e0443 100644 --- a/src/soft/lib.zig +++ b/src/soft/lib.zig @@ -2,6 +2,17 @@ const std = @import("std"); const vk = @import("vulkan"); const common = @import("common"); -export fn libVulkanExport() void { - _ = common; +const Instance = @import("Instance.zig"); + +const global_pfn_map = std.StaticStringMap(vk.PfnVoidFunction).initComptime(.{ + .{ "vkGetInstanceProcAddr", @as(vk.PfnVoidFunction, @ptrCast(&common.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 common.icd.getInstanceProcAddr(global_pfn_map, p_instance, name); } diff --git a/src/vulkan/Instance.zig b/src/vulkan/Instance.zig index e7d663e..b2a66ef 100644 --- a/src/vulkan/Instance.zig +++ b/src/vulkan/Instance.zig @@ -1,24 +1,52 @@ const std = @import("std"); const vk = @import("vulkan"); -const PhysicalDevice = @import("PhysicalDevice.zig").PhysicalDevice; -const Object = @import("object.zig").Object; +const dispatchable = @import("dispatchable.zig"); -pub const Instance = extern struct { - const Self = @This(); - pub const ObjectType: vk.ObjectType = .instance; - pub const vtable: VTable = .{}; +const Self = @This(); +pub const ObjectType: vk.ObjectType = .instance; - object: Object, - //physical_devices: std.ArrayList(*PhysicalDevice), - alloc_callbacks: vk.AllocationCallbacks, +alloc_callbacks: vk.AllocationCallbacks, - pub const VTable = struct { - createInstance: ?vk.PfnCreateInstance = null, - destroyInstance: ?vk.PfnDestroyInstance = null, - enumeratePhysicalDevices: ?vk.PfnEnumeratePhysicalDevices = null, - getInstanceProcAddr: ?vk.PfnGetInstanceProcAddr = null, - enumerateInstanceVersion: ?vk.PfnEnumerateInstanceVersion = null, - //enumerateInstanceLayerProperties: vk.PfnEnumerateInstanceProperties = null, - enumerateInstanceExtensionProperties: ?vk.PfnEnumerateInstanceExtensionProperties = null, +vtable: VTable, + +pub fn init(self: *Self, p_infos: ?*const vk.InstanceCreateInfo, callbacks: ?*const vk.AllocationCallbacks) !void { + const infos = p_infos orelse return error.NullCreateInfos; + if (infos.s_type != .instance_create_info) { + return error.InvalidCreateInfos; + } + + self.vtable = .{ + .destroyInstance = null, + .enumeratePhysicalDevices = null, + .enumerateInstanceVersion = null, + //.enumerateInstanceLayerProperties = null, + .enumerateInstanceExtensionProperties = null, }; + + if (callbacks) |c| { + self.alloc_callbacks = c.*; + } +} + +pub fn getProcAddr(self: *const Self, 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) }, + }, allocator) catch return null; + defer pfn_map.deinit(allocator); + + return if (pfn_map.get(name)) |pfn| pfn else null; +} + +pub const VTable = struct { + destroyInstance: ?vk.PfnDestroyInstance, + enumeratePhysicalDevices: ?vk.PfnEnumeratePhysicalDevices, + enumerateInstanceVersion: ?vk.PfnEnumerateInstanceVersion, + //enumerateInstanceLayerProperties: vk.PfnEnumerateInstanceProperties, + enumerateInstanceExtensionProperties: ?vk.PfnEnumerateInstanceExtensionProperties, }; diff --git a/src/vulkan/PhysicalDevice.zig b/src/vulkan/PhysicalDevice.zig index 2e8e945..7259a74 100644 --- a/src/vulkan/PhysicalDevice.zig +++ b/src/vulkan/PhysicalDevice.zig @@ -1,14 +1,8 @@ const vk = @import("vulkan"); -const Instance = @import("Instance.zig").Instance; -const Object = @import("object.zig").Object; +const Instance = @import("Instance.zig"); -pub const PhysicalDevice = extern struct { - const Self = @This(); - const ObjectType: vk.ObjectType = .physical_device; +const Self = @This(); +const ObjectType: vk.ObjectType = .physical_device; - object: Object, - - instance: *Instance, - props: vk.PhysicalDeviceProperties, - queue_families: [3]vk.QueueFamilyProperties, -}; +props: vk.PhysicalDeviceProperties, +queue_families: [3]vk.QueueFamilyProperties, diff --git a/src/vulkan/dispatchable.zig b/src/vulkan/dispatchable.zig new file mode 100644 index 0000000..385af50 --- /dev/null +++ b/src/vulkan/dispatchable.zig @@ -0,0 +1,45 @@ +const std = @import("std"); +const vk = @import("vulkan"); +const c = @cImport({ + @cInclude("vulkan/vk_icd.h"); +}); + +pub fn Dispatchable(comptime T: type) type { + return extern struct { + const Self = @This(); + + loader_data: c.VK_LOADER_DATA, + object_type: vk.ObjectType, + object: *T, + + pub fn create(allocator: std.mem.Allocator, object_type: vk.ObjectType) !*Self { + const object = try allocator.create(Self); + object.* = .{ + .loader_data = .{ .loaderMagic = c.ICD_LOADER_MAGIC }, + .object_type = object_type, + .object = try allocator.create(T), + }; + return object; + } + + pub fn destroy(self: *Self, allocator: std.mem.Allocator) void { + allocator.destroy(self.object); + allocator.destroy(self); + } + }; +} + +pub inline fn fromHandle(comptime T: type, handle: usize) !*Dispatchable(T) { + if (handle == 0) { + return error.NullHandle; + } + const dispatchable: *Dispatchable(T) = @ptrFromInt(handle); + if (dispatchable.object_type != T.ObjectType) { + return error.InvalidType; + } + return dispatchable; +} + +pub inline fn toHandle(comptime T: type, handle: *Dispatchable(T)) usize { + return @intFromPtr(handle); +} diff --git a/src/vulkan/icd.zig b/src/vulkan/icd.zig index 0399a1f..b290dad 100644 --- a/src/vulkan/icd.zig +++ b/src/vulkan/icd.zig @@ -4,18 +4,25 @@ const c = @cImport({ @cInclude("vulkan/vk_icd.h"); }); -const Instance = @import("Instance.zig").Instance; -const fromHandle = @import("object.zig").fromHandle; +const Instance = @import("Instance.zig"); +const dispatchable = @import("dispatchable.zig"); -const global_pfn_map = std.StaticStringMap(vk.PfnVoidFunction).initComptime(.{ - .{ "vkGetInstanceProcAddr", @as(vk.PfnVoidFunction, @ptrCast(&getInstanceProcAddr)) }, - .{ "vkCreateInstance", @as(vk.PfnVoidFunction, @ptrCast(&Instance.vtable.createInstance)) }, -}); +pub fn getInstanceProcAddr(global_pfn_map: std.StaticStringMap(vk.PfnVoidFunction), p_instance: vk.Instance, name: []const u8) vk.PfnVoidFunction { + const allocator = std.heap.c_allocator; + const get_proc_log = std.log.scoped(.vkGetInstanceProcAddr); + + if (std.process.hasEnvVar(allocator, "DRIVER_LOGS") catch false) { + get_proc_log.info("Loading {s}...", .{name}); + } -pub fn getInstanceProcAddr(instance: vk.Instance, name: []const u8) vk.PfnVoidFunction { if (global_pfn_map.get(name)) |pfn| { return pfn; } - if (instance != .null_handle) {} - return null; + const instance = dispatchable.fromHandle(Instance, @intFromEnum(p_instance)) catch |e| { + if (std.process.hasEnvVar(allocator, "DRIVER_LOGS") catch false) { + get_proc_log.err("{any}", .{e}); + } + return null; + }; + return instance.object.getProcAddr(name); } diff --git a/src/vulkan/lib.zig b/src/vulkan/lib.zig index dd26f97..acb148c 100644 --- a/src/vulkan/lib.zig +++ b/src/vulkan/lib.zig @@ -2,15 +2,23 @@ const std = @import("std"); const vk = @import("vulkan"); pub const icd = @import("icd.zig"); +pub const dispatchable = @import("dispatchable.zig"); + pub const Instance = @import("Instance.zig"); pub const PhysicalDevice = @import("PhysicalDevice.zig"); -pub export fn vkGetInstanceProcAddr(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(instance, name); +pub const std_options: std.Options = .{ + .log_level = .info, + .logFn = logFn, +}; + +pub fn logFn(comptime level: std.log.Level, comptime scope: @Type(.enum_literal), comptime format: []const u8, args: anytype) void { + _ = level; + _ = scope; + std.debug.lockStdErr(); + defer std.debug.unlockStdErr(); + const stderr = std.fs.File.stderr().deprecatedWriter(); + nosuspend stderr.print(format ++ "\n", args) catch return; } test { diff --git a/src/vulkan/object.zig b/src/vulkan/object.zig deleted file mode 100644 index 10cdc0e..0000000 --- a/src/vulkan/object.zig +++ /dev/null @@ -1,50 +0,0 @@ -const vk = @import("vulkan"); -const c = @cImport({ - @cInclude("vulkan/vk_icd.h"); -}); - -pub const Object = extern struct { - const Self = @This(); - - loader_data: c.VK_LOADER_DATA, - kind: vk.ObjectType, - owner: ?*anyopaque, - // VK_EXT_debug_utils - name: ?[*]const u8, - - pub fn init(owner: ?*anyopaque, kind: vk.ObjectType) Self { - return .{ - .loader_data = c.ICD_LOADER_MAGIC, - .kind = kind, - .owner = owner, - .name = null, - }; - } -}; - -pub inline fn fromHandle(comptime T: type, comptime VkT: type, handle: VkT) !*T { - comptime { - if (!@hasField(T, "object") or !@hasDecl(T, "ObjectType") or @TypeOf(T.ObjectType) != vk.ObjectType) { - @compileError("Object type \"" ++ @typeName(T) ++ "\" is malformed."); - } - } - - if (handle == .null_handle) { - return error.NullHandle; - } - - const dispatchable: *T = @ptrFromInt(@intFromEnum(handle)); - if (dispatchable.object.kind != T.ObjectType) { - return error.InvalidObjectType; - } - return dispatchable; -} - -pub inline fn toHandle(comptime T: type, handle: *T) usize { - comptime { - if (!@hasDecl(T, "object") or !@hasDecl(T, "ObjectType") or @TypeOf(T.ObjectType) != vk.ObjectType) { - @compileError("Object type \"" ++ @typeName(T) ++ "\" is malformed."); - } - } - return @intFromPtr(handle); -} diff --git a/test/c/main.c b/test/c/main.c index bbb1560..f44a847 100644 --- a/test/c/main.c +++ b/test/c/main.c @@ -2,7 +2,7 @@ #include #define VK_NO_PROTOTYPES -#include +#include #include @@ -10,15 +10,38 @@ #define LIBVK "vulkan" #endif +#define CheckVk(x) \ + do { \ + if((x) != VK_SUCCESS) \ + { \ + fprintf(stderr, "Vulkan call failed\n"); \ + abort(); \ + } \ + } while(0) + int main(void) { - printf("openning ./zig-out/lib/lib" LIBVK ".so\n"); + puts("openning ./zig-out/lib/lib" LIBVK ".so"); void* lib = dlopen("./zig-out/lib/lib" LIBVK ".so", RTLD_NOW | RTLD_LOCAL); PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = dlsym(lib, "vkGetInstanceProcAddr"); - printf("test %p\n", vkGetInstanceProcAddr); - printf("test %p\n", vkGetInstanceProcAddr(NULL, "vkCreateInstance")); + #define VULKAN_GLOBAL_FUNCTION(fn) PFN_##fn fn = (PFN_##fn)vkGetInstanceProcAddr(NULL, #fn); + VULKAN_GLOBAL_FUNCTION(vkCreateInstance) + + VkInstanceCreateInfo instance_create_info = { 0 }; + instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + + VkInstance instance = VK_NULL_HANDLE; + CheckVk(vkCreateInstance(&instance_create_info, NULL, &instance)); + + printf("VkInstance %p\n", instance); + + #define VULKAN_INSTANCE_FUNCTION(fn) PFN_##fn fn = (PFN_##fn)vkGetInstanceProcAddr(instance, #fn); + VULKAN_INSTANCE_FUNCTION(vkEnumeratePhysicalDevices) + VULKAN_INSTANCE_FUNCTION(vkDestroyInstance) + + vkDestroyInstance(instance, NULL); dlclose(lib); return 0;