impriving architecture

This commit is contained in:
2025-11-02 00:34:45 +01:00
parent f95098cead
commit 7639c40074
12 changed files with 339 additions and 99 deletions

96
.gdb_history git.filemode.normal_file
View File

@@ -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

View File

@@ -21,6 +21,7 @@ pub fn build(b: *std.Build) void {
.root_source_file = b.path("src/vulkan/lib.zig"), .root_source_file = b.path("src/vulkan/lib.zig"),
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
.link_libc = true,
}); });
const vulkan_headers = b.dependency("vulkan_headers", .{}); const vulkan_headers = b.dependency("vulkan_headers", .{});
@@ -36,6 +37,7 @@ pub fn build(b: *std.Build) void {
const lib_mod = b.createModule(.{ const lib_mod = b.createModule(.{
.root_source_file = b.path(impl.root_source_file), .root_source_file = b.path(impl.root_source_file),
.target = target, .target = target,
.link_libc = true,
.optimize = optimize, .optimize = optimize,
.imports = &.{ .imports = &.{
.{ .name = "common", .module = common_mod }, .{ .name = "common", .module = common_mod },
@@ -72,6 +74,8 @@ pub fn build(b: *std.Build) void {
.flags = &.{b.fmt("-DLIBVK=\"{s}\"", .{lib.name})}, .flags = &.{b.fmt("-DLIBVK=\"{s}\"", .{lib.name})},
}); });
b.installArtifact(c_test_exe);
const run_c_test = b.addRunArtifact(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})); 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()); test_c_step.dependOn(b.getInstallStep());

48
src/soft/Instance.zig git.filemode.normal_file
View File

@@ -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);
}

26
src/soft/PhysicalDevice.zig git.filemode.normal_file
View File

@@ -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 = {},
};
}

View File

@@ -2,6 +2,17 @@ const std = @import("std");
const vk = @import("vulkan"); const vk = @import("vulkan");
const common = @import("common"); const common = @import("common");
export fn libVulkanExport() void { const Instance = @import("Instance.zig");
_ = common;
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);
} }

View File

@@ -1,24 +1,52 @@
const std = @import("std"); const std = @import("std");
const vk = @import("vulkan"); const vk = @import("vulkan");
const PhysicalDevice = @import("PhysicalDevice.zig").PhysicalDevice; const dispatchable = @import("dispatchable.zig");
const Object = @import("object.zig").Object;
pub const Instance = extern struct {
const Self = @This(); const Self = @This();
pub const ObjectType: vk.ObjectType = .instance; pub const ObjectType: vk.ObjectType = .instance;
pub const vtable: VTable = .{};
object: Object,
//physical_devices: std.ArrayList(*PhysicalDevice),
alloc_callbacks: vk.AllocationCallbacks, alloc_callbacks: vk.AllocationCallbacks,
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 { pub const VTable = struct {
createInstance: ?vk.PfnCreateInstance = null, destroyInstance: ?vk.PfnDestroyInstance,
destroyInstance: ?vk.PfnDestroyInstance = null, enumeratePhysicalDevices: ?vk.PfnEnumeratePhysicalDevices,
enumeratePhysicalDevices: ?vk.PfnEnumeratePhysicalDevices = null, enumerateInstanceVersion: ?vk.PfnEnumerateInstanceVersion,
getInstanceProcAddr: ?vk.PfnGetInstanceProcAddr = null, //enumerateInstanceLayerProperties: vk.PfnEnumerateInstanceProperties,
enumerateInstanceVersion: ?vk.PfnEnumerateInstanceVersion = null, enumerateInstanceExtensionProperties: ?vk.PfnEnumerateInstanceExtensionProperties,
//enumerateInstanceLayerProperties: vk.PfnEnumerateInstanceProperties = null,
enumerateInstanceExtensionProperties: ?vk.PfnEnumerateInstanceExtensionProperties = null,
};
}; };

View File

@@ -1,14 +1,8 @@
const vk = @import("vulkan"); const vk = @import("vulkan");
const Instance = @import("Instance.zig").Instance; const Instance = @import("Instance.zig");
const Object = @import("object.zig").Object;
pub const PhysicalDevice = extern struct {
const Self = @This(); const Self = @This();
const ObjectType: vk.ObjectType = .physical_device; const ObjectType: vk.ObjectType = .physical_device;
object: Object,
instance: *Instance,
props: vk.PhysicalDeviceProperties, props: vk.PhysicalDeviceProperties,
queue_families: [3]vk.QueueFamilyProperties, queue_families: [3]vk.QueueFamilyProperties,
};

45
src/vulkan/dispatchable.zig git.filemode.normal_file
View File

@@ -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);
}

View File

@@ -4,18 +4,25 @@ const c = @cImport({
@cInclude("vulkan/vk_icd.h"); @cInclude("vulkan/vk_icd.h");
}); });
const Instance = @import("Instance.zig").Instance; const Instance = @import("Instance.zig");
const fromHandle = @import("object.zig").fromHandle; const dispatchable = @import("dispatchable.zig");
const global_pfn_map = std.StaticStringMap(vk.PfnVoidFunction).initComptime(.{ pub fn getInstanceProcAddr(global_pfn_map: std.StaticStringMap(vk.PfnVoidFunction), p_instance: vk.Instance, name: []const u8) vk.PfnVoidFunction {
.{ "vkGetInstanceProcAddr", @as(vk.PfnVoidFunction, @ptrCast(&getInstanceProcAddr)) }, const allocator = std.heap.c_allocator;
.{ "vkCreateInstance", @as(vk.PfnVoidFunction, @ptrCast(&Instance.vtable.createInstance)) }, 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| { if (global_pfn_map.get(name)) |pfn| {
return pfn; return pfn;
} }
if (instance != .null_handle) {} const instance = dispatchable.fromHandle(Instance, @intFromEnum(p_instance)) catch |e| {
return null; if (std.process.hasEnvVar(allocator, "DRIVER_LOGS") catch false) {
get_proc_log.err("{any}", .{e});
}
return null;
};
return instance.object.getProcAddr(name);
} }

View File

@@ -2,15 +2,23 @@ const std = @import("std");
const vk = @import("vulkan"); const vk = @import("vulkan");
pub const icd = @import("icd.zig"); pub const icd = @import("icd.zig");
pub const dispatchable = @import("dispatchable.zig");
pub const Instance = @import("Instance.zig"); pub const Instance = @import("Instance.zig");
pub const PhysicalDevice = @import("PhysicalDevice.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 { pub const std_options: std.Options = .{
if (pName == null) { .log_level = .info,
return null; .logFn = logFn,
} };
const name = std.mem.span(pName.?);
return icd.getInstanceProcAddr(instance, name); 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 { test {

View File

@@ -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);
}

View File

@@ -2,7 +2,7 @@
#include <stdlib.h> #include <stdlib.h>
#define VK_NO_PROTOTYPES #define VK_NO_PROTOTYPES
#include <vulkan/vulkan.h> #include <vulkan/vulkan_core.h>
#include <dlfcn.h> #include <dlfcn.h>
@@ -10,15 +10,38 @@
#define LIBVK "vulkan" #define LIBVK "vulkan"
#endif #endif
#define CheckVk(x) \
do { \
if((x) != VK_SUCCESS) \
{ \
fprintf(stderr, "Vulkan call failed\n"); \
abort(); \
} \
} while(0)
int main(void) 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); void* lib = dlopen("./zig-out/lib/lib" LIBVK ".so", RTLD_NOW | RTLD_LOCAL);
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = dlsym(lib, "vkGetInstanceProcAddr"); PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = dlsym(lib, "vkGetInstanceProcAddr");
printf("test %p\n", vkGetInstanceProcAddr); #define VULKAN_GLOBAL_FUNCTION(fn) PFN_##fn fn = (PFN_##fn)vkGetInstanceProcAddr(NULL, #fn);
printf("test %p\n", vkGetInstanceProcAddr(NULL, "vkCreateInstance")); 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); dlclose(lib);
return 0; return 0;