268 lines
11 KiB
Zig
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;
|
|
}
|