Files
zig-vk-test/vk/root.zig
2026-05-31 16:25:04 -04:00

268 lines
11 KiB
Zig

const std = @import("std");
const builtin = @import("builtin");
const vk = @import("volk");
pub const InitError = error{
InitializationFailed,
Unknown,
};
pub fn init() InitError!void {
switch (vk.volkInitialize()) {
vk.VK_ERROR_INITIALIZATION_FAILED => return error.InitializationFailed,
vk.VK_SUCCESS => {},
else => return error.Unknown,
}
}
pub fn deinit() void {
vk.volkFinalize();
}
pub const Version = packed struct(u32) {
patch: u12,
minor: u10,
major: u7,
variant: u3 = 0,
pub const API_1_0: Version = .{ .variant = 0, .major = 1, .minor = 0, .patch = 0 };
pub const API_1_1: Version = .{ .variant = 0, .major = 1, .minor = 1, .patch = 0 };
pub const API_1_2: Version = .{ .variant = 0, .major = 1, .minor = 2, .patch = 0 };
pub const API_1_3: Version = .{ .variant = 0, .major = 1, .minor = 3, .patch = 0 };
pub const API_1_4: Version = .{ .variant = 0, .major = 1, .minor = 4, .patch = 0 };
};
pub const MessageTypes = struct {
general: bool,
performance: bool,
validation: bool,
};
pub const InstanceConfig = struct {
api_version: Version,
app_name: [*:0]const u8 = "",
app_version: Version = .{ .major = 0, .minor = 0, .patch = 0 },
engine_name: [*:0]const u8 = "",
engine_version: Version = .{ .major = 0, .minor = 0, .patch = 0 },
layer_names: []const [*:0]const u8 = &.{},
extension_names: []const [*:0]const u8 = &.{},
log_scope: ?@EnumLiteral() = .vulkan,
log_message_types: MessageTypes = .{
.general = false,
.performance = true,
.validation = true,
},
};
pub const PhysicalDevice = *const opaque {};
pub const Instance = struct {
handle: vk.VkInstance,
vtable: *const vk.VolkInstanceTable,
pub fn enumerate_physical_devices(self: Instance, buf: []PhysicalDevice) ![]PhysicalDevice {
var count: u32 = @intCast(buf.len);
if (self.vtable.vkEnumeratePhysicalDevices.?(self.handle, &count, @ptrCast(buf.ptr)) != vk.VK_SUCCESS) return error.Unknown;
return buf[0..count];
}
pub fn get_physical_device_properties(self: Instance, pdev: PhysicalDevice) vk.VkPhysicalDeviceProperties {
var props: vk.VkPhysicalDeviceProperties = undefined;
self.vtable.vkGetPhysicalDeviceProperties.?(@ptrCast(@constCast(pdev)), &props);
return props;
}
};
pub const InstanceContext = struct {
handle: vk.VkInstance,
vtable: vk.VolkInstanceTable,
debug: ?vk.VkDebugUtilsMessengerEXT,
pub const InstanceCreateError = error{
Unknown,
};
pub fn init(config: InstanceConfig) InstanceCreateError!InstanceContext {
const callback: vk.PFN_vkDebugUtilsMessengerCallbackEXT = if (config.log_scope) |scope| default_debug_callback(scope) else null;
var message_severity: vk.VkDebugUtilsMessageSeverityFlagsEXT = 0;
if (config.log_scope) |scope| {
if (std.log.logEnabled(.debug, scope)) message_severity |= vk.VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
if (std.log.logEnabled(.info, scope)) message_severity |= vk.VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
if (std.log.logEnabled(.warn, scope)) message_severity |= vk.VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
if (std.log.logEnabled(.err, scope)) message_severity |= vk.VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
}
var message_type: vk.VkDebugUtilsMessageTypeFlagsEXT = 0;
if (config.log_message_types.general) message_type |= vk.VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT;
if (config.log_message_types.performance) message_type |= vk.VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
if (config.log_message_types.validation) message_type |= vk.VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
var extension_names_buf: [256][*:0]const u8 = undefined;
var extension_names: std.ArrayListUnmanaged([*:0]const u8) = .initBuffer(&extension_names_buf);
extension_names.appendSliceAssumeCapacity(config.extension_names);
var layer_names_buf: [256][*:0]const u8 = undefined;
var layer_names: std.ArrayListUnmanaged([*:0]const u8) = .initBuffer(&layer_names_buf);
layer_names.appendSliceAssumeCapacity(config.layer_names);
var do_logging = message_severity != 0 and message_type != 0;
if (do_logging) {
for (extension_names.items) |name| {
if (std.mem.eql(u8, std.mem.span(name), "VK_EXT_debug_utils")) break;
} else {
var extension_props_buf: [256]vk.VkExtensionProperties = undefined;
var count: u32 = @intCast(extension_props_buf.len);
if (vk.vkEnumerateInstanceExtensionProperties.?(null, &count, &extension_props_buf) != vk.VK_SUCCESS) {
do_logging = false;
} else {
for (extension_props_buf[0..count]) |prop| {
if (std.mem.eql(u8, std.mem.sliceTo(&prop.extensionName, 0), "VK_EXT_debug_utils")) {
extension_names.appendAssumeCapacity("VK_EXT_debug_utils");
break;
}
} else {
do_logging = false;
}
}
}
}
if (do_logging and config.log_message_types.validation) {
for (layer_names.items) |name| {
if (std.mem.eql(u8, std.mem.span(name), "VK_LAYER_KHRONOS_validation")) break;
} else {
var layer_props_buf: [256]vk.VkLayerProperties = undefined;
var count: u32 = @intCast(layer_props_buf.len);
if (vk.vkEnumerateInstanceLayerProperties.?(&count, &layer_props_buf) != vk.VK_SUCCESS) {
do_logging = false;
} else {
for (layer_props_buf[0..count]) |prop| {
if (std.mem.eql(u8, std.mem.sliceTo(&prop.layerName, 0), "VK_LAYER_KHRONOS_validation")) {
layer_names.appendAssumeCapacity("VK_LAYER_KHRONOS_validation");
break;
}
}
}
}
}
const debug_info: vk.VkDebugUtilsMessengerCreateInfoEXT = .{
.sType = vk.VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
.pNext = null,
.flags = 0,
.messageSeverity = message_severity,
.messageType = message_type,
.pfnUserCallback = callback,
.pUserData = null,
};
const app_info = vk.VkApplicationInfo{
.sType = vk.VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pNext = null,
.apiVersion = @bitCast(config.api_version),
.applicationVersion = @bitCast(config.app_version),
.engineVersion = @bitCast(config.engine_version),
.pApplicationName = config.app_name,
.pEngineName = config.engine_name,
};
const info = vk.VkInstanceCreateInfo{
.sType = vk.VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pNext = if (do_logging) &debug_info else null,
.flags = 0,
.pApplicationInfo = &app_info,
.enabledLayerCount = @intCast(layer_names.items.len),
.ppEnabledLayerNames = layer_names.items.ptr,
.enabledExtensionCount = @intCast(extension_names.items.len),
.ppEnabledExtensionNames = extension_names.items.ptr,
};
// std.log.debug("{s} ", .{})
var result: InstanceContext = undefined;
switch (vk.vkCreateInstance.?(&info, null, &result.handle)) {
vk.VK_SUCCESS => {},
else => return error.Unknown,
}
vk.volkLoadInstanceTable(&result.vtable, result.handle);
result.debug = null;
if (do_logging) {
var debug_handle: vk.VkDebugUtilsMessengerEXT = undefined;
if (result.vtable.vkCreateDebugUtilsMessengerEXT.?(result.handle, &debug_info, null, &debug_handle) != vk.VK_SUCCESS) {
do_logging = false;
} else {
result.debug = debug_handle;
}
}
return result;
}
pub fn interface(self: *const InstanceContext) Instance {
return .{ .handle = self.handle, .vtable = &self.vtable };
}
pub fn deinit(self: *const InstanceContext) void {
if (self.debug) |debug_handle| {
self.vtable.vkDestroyDebugUtilsMessengerEXT.?(self.handle, debug_handle, null);
}
self.vtable.vkDestroyInstance.?(self.handle, null);
}
};
fn default_debug_callback(scope: @EnumLiteral()) vk.PFN_vkDebugUtilsMessengerCallbackEXT {
const impl = struct {
fn impl(
message_severity: vk.VkDebugUtilsMessageSeverityFlagBitsEXT,
message_types: vk.VkDebugUtilsMessageTypeFlagsEXT,
p_callback_data: [*c]const vk.VkDebugUtilsMessengerCallbackDataEXT,
_: ?*anyopaque,
) callconv(.c) vk.VkBool32 {
var types_buf: [64]u8 = undefined;
var types_str: std.ArrayListUnmanaged(u8) = .initBuffer(&types_buf);
if (message_types & vk.VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT != 0) {
if (types_str.items.len > 0) types_str.appendSliceAssumeCapacity(" ");
types_str.appendSliceAssumeCapacity("general");
}
if (message_types & vk.VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT != 0) {
if (types_str.items.len > 0) types_str.appendSliceAssumeCapacity(" ");
types_str.appendSliceAssumeCapacity("validation");
}
if (message_types & vk.VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT != 0) {
if (types_str.items.len > 0) types_str.appendSliceAssumeCapacity(" ");
types_str.appendSliceAssumeCapacity("performance");
}
if (types_str.items.len > 0) types_str.appendSliceAssumeCapacity(": ");
const fmt = "{s}{s}";
const arg = .{
types_str.items,
p_callback_data.*.pMessage,
};
const log = std.log.scoped(scope);
if (message_severity & vk.VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT != 0) {
log.err(fmt, arg);
switch (builtin.mode) {
.Debug, .ReleaseSafe => @breakpoint(),
else => {},
}
} else if (message_severity & vk.VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT != 0) {
log.warn(fmt, arg);
} else if (message_severity & vk.VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT != 0) {
log.info(fmt, arg);
} else if (message_severity & vk.VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT != 0) {
log.debug(fmt, arg);
} else {
log.debug(fmt, arg);
}
return vk.VK_FALSE;
}
};
return &impl.impl;
}