hand-written interface wrapper

This commit is contained in:
David Allemang
2026-05-31 16:25:04 -04:00
parent f3cef55647
commit e812180389
6 changed files with 406 additions and 120 deletions

View File

@@ -7,38 +7,39 @@ pub fn build(b: *std.Build) void {
const vk_dep = b.dependency("vk", .{});
const volk_dep = b.dependency("volk", .{});
// const volk_c = b.addTranslateC(.{
// .target = target,
// .optimize = optimize,
// .root_source_file = volk_dep.path("volk.c"),
// .link_libc = true,
// });
// volk_c.addIncludePath(vk_dep.path("include"));
const vk = b.addModule("vulkan", .{
const volk_h = b.addTranslateC(.{
.target = target,
.optimize = optimize,
.root_source_file = b.path("src/volk.zig"),
.root_source_file = volk_dep.path("volk.h"),
.link_libc = true,
});
vk.addIncludePath(vk_dep.path("include"));
vk.addIncludePath(volk_dep.path("."));
vk.addCSourceFile(.{ .file = volk_dep.path("volk.c"), .language = .c });
const mod = b.addModule("zig_vk_api_test", .{
.root_source_file = b.path("src/root.zig"),
const volk = b.addModule("volk", .{
.target = target,
.optimize = optimize,
.root_source_file = volk_h.getOutput(),
.link_libc = true,
});
volk.addIncludePath(vk_dep.path("include"));
volk.addCSourceFile(.{ .file = volk_dep.path("volk.c"), .language = .c });
const vulkan = b.addModule("vulkan", .{
.target = target,
.optimize = optimize,
.root_source_file = b.path("vk/root.zig"),
.imports = &.{
.{ .name = "volk", .module = volk },
},
});
const exe = b.addExecutable(.{
.name = "zig_vk_api_test",
.name = "main",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
.imports = &.{
.{ .name = "zig_vk_api_test", .module = mod },
.{ .name = "vk", .module = vk },
.{ .name = "vk", .module = vulkan },
},
}),
});
@@ -56,12 +57,6 @@ pub fn build(b: *std.Build) void {
run_cmd.addArgs(args);
}
const mod_tests = b.addTest(.{
.root_module = mod,
});
const run_mod_tests = b.addRunArtifact(mod_tests);
const exe_tests = b.addTest(.{
.root_module = exe.root_module,
});
@@ -69,6 +64,5 @@ pub fn build(b: *std.Build) void {
const run_exe_tests = b.addRunArtifact(exe_tests);
const test_step = b.step("test", "Run tests");
test_step.dependOn(&run_mod_tests.step);
test_step.dependOn(&run_exe_tests.step);
}

View File

@@ -1,3 +1,3 @@
[tools]
zig = "latest"
zls = "latest"
zig = "0.16.0"
zls = "0.16.0"

View File

@@ -1,67 +1,135 @@
const std = @import("std");
const Io = std.Io;
const builtin = @import("builtin");
const vk = @import("vk");
pub fn main() !void {
if (vk.c.volkInitialize() != vk.c.VK_SUCCESS) @panic("Failed to initialize vk");
defer vk.c.volkFinalize();
// const log = std.log.scoped(.main);
const app_info = vk.c.VkApplicationInfo{
.sType = vk.c.VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pNext = null,
.apiVersion = vk.c.VK_API_VERSION_1_4,
.pApplicationName = "WIP",
.applicationVersion = vk.c.VK_MAKE_VERSION(0, 0, 1),
.pEngineName = "WIP",
.engineVersion = vk.c.VK_MAKE_VERSION(0, 0, 1),
};
try vk.init();
defer vk.deinit();
const ext_names: []const [*c]const u8 = &.{"VK_EXT_debug_utils"};
const lyr_names: []const [*c]const u8 = &.{"VK_LAYER_KHRONOS_validation"};
const ctx_ins: vk.InstanceContext = try .init(.{
.api_version = .API_1_3,
.log_message_types = .{
.general = false,
.performance = true,
.validation = true,
},
});
defer ctx_ins.deinit();
const ins = ctx_ins.interface();
const ins_info = vk.c.VkInstanceCreateInfo{
.sType = vk.c.VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pNext = null,
.flags = 0,
.enabledExtensionCount = @intCast(ext_names.len),
.ppEnabledExtensionNames = ext_names.ptr,
.enabledLayerCount = @intCast(lyr_names.len),
.ppEnabledLayerNames = lyr_names.ptr,
.pApplicationInfo = &app_info,
};
const deb_info = vk.c.VkDebugUtilsMessengerCreateInfoEXT{
.sType = vk.c.VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pNext = null,
.flags = 0,
};
var pdevs_buf: [8]vk.PhysicalDevice = undefined;
for (try ins.enumerate_physical_devices(&pdevs_buf)) |pdev| {
std.log.debug("{s}", .{ins.get_physical_device_properties(pdev).deviceName});
}
var ins: vk.c.VkInstance = undefined;
if (vk.c.vkCreateInstance.?(&ins_info, null, &ins) != vk.c.VK_SUCCESS) @panic("Failed to create VkInstance");
vk.c.volkLoadInstanceOnly(ins);
defer vk.c.vkDestroyInstance.?(ins, null);
// defer ctx_ins.deinit();
// ctx_ins.deinit();
var pdevs: [3]vk.c.VkPhysicalDevice = undefined;
var pdev_count: u32 = 3;
if (vk.c.vkEnumeratePhysicalDevices.?(
ins,
&pdev_count,
&pdevs,
) != vk.c.VK_SUCCESS) @panic("Failed to enumerate physical devices.");
// const ins = ctx_ins.interface();
// pub const PFN_vkEnumeratePhysicalDevices = ?*const fn (instance: VkInstance, pPhysicalDeviceCount: [*c]u32, pPhysicalDevices: [*c]VkPhysicalDevice) callconv(.c) VkResult;
// vk.c.vkEnumerateDeviceExtensionProperties();
// const info = vk.c.VkInstanceCreateInfo {
// .sType = vk.c.VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
// const ext_names: []const [*c]const u8 = &.{"VK_EXT_debug_utils"};
// const lyr_names: []const [*c]const u8 = &.{"VK_LAYER_KHRONOS_validation"};
//
// const deb_info = vk.VkDebugUtilsMessengerCreateInfoEXT{
// .sType = vk.VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
// .pNext = null,
// .
// .flags = 0,
// .messageSeverity =
// vk.VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
// vk.VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
// vk.VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
// vk.VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
// .messageType =
// vk.VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT |
// // vk.VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
// vk.VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
// .pfnUserCallback = &debug_messenger_callback,
// .pUserData = null,
// };
// vk.c.vkCreateInstance(pCreateInfo: [*c]const VkInstanceCreateInfo, pAllocator: [*c]const VkAllocationCallbacks, pInstance: [*c]VkInstance);
// const ins_info = vk.VkInstanceCreateInfo{
// .sType = vk.VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
// .pNext = &deb_info,
// .flags = 0,
// .enabledExtensionCount = @intCast(ext_names.len),
// .ppEnabledExtensionNames = ext_names.ptr,
// .enabledLayerCount = @intCast(lyr_names.len),
// .ppEnabledLayerNames = lyr_names.ptr,
// .pApplicationInfo = &app_info,
// };
//
// var ins: vk.VkInstance = undefined;
// if (vk.vkCreateInstance.?(&ins_info, null, &ins) != vk.VK_SUCCESS) @panic("Failed to create VkInstance");
// vk.volkLoadInstanceOnly(ins);
// defer vk.vkDestroyInstance.?(ins, null);
//
// var deb: vk.VkDebugUtilsMessengerEXT = undefined;
// if (vk.vkCreateDebugUtilsMessengerEXT.?(ins, &deb_info, null, &deb) != vk.VK_SUCCESS) @panic("Failed to create VkDebugUtilsMessengerEXT");
// defer vk.vkDestroyDebugUtilsMessengerEXT.?(ins, deb, null);
//
// var pdevs: [8]vk.VkPhysicalDevice = undefined;
// var pdev_count: u32 = 8;
// if (vk.vkEnumeratePhysicalDevices.?(
// ins,
// &pdev_count,
// &pdevs,
// ) != vk.VK_SUCCESS) @panic("Failed to enumerate physical devices.");
// const pdev = pdevs[0]; // assume the drivers will put the best device first
// var props : vk.VkPhysicalDeviceProperties2 = .{ .sType = vk.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2 };
// vk.vkGetPhysicalDeviceProperties2.?(pdev, &props);
// log.info("Selected device {s} (API {d}.{d}.{d}, Driver {d})", .{
// props.properties.deviceName,
// vk.VK_API_VERSION_MAJOR(props.properties.apiVersion),
// vk.VK_API_VERSION_MINOR(props.properties.apiVersion),
// vk.VK_API_VERSION_PATCH(props.properties.apiVersion),
// vk.VK_VERSION_MAJOR(props.properties.driverVersion),
// });
}
// fn debug_messenger_callback(
// 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_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 log = std.log.scoped(.vulkan);
//
// const fmt = "{s}{s}";
// const arg = .{
// types_str.items,
// p_callback_data.*.pMessage,
// };
//
// if (message_severity & vk.VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT != 0) {
// log.err(fmt, arg);
// if (builtin.mode == .Debug) @breakpoint();
// } 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;
// }
// pub fn main(init: std.process.Init) !void {
// std.debug.print("{any}\n", .{vk});
//

View File

@@ -1,18 +0,0 @@
//! By convention, root.zig is the root source file when making a package.
const std = @import("std");
const Io = std.Io;
/// This is a documentation comment to explain the `printAnotherMessage` function below.
///
/// Accepting an `Io.Writer` instance is a handy way to write reusable code.
pub fn printAnotherMessage(writer: *Io.Writer) Io.Writer.Error!void {
try writer.print("Run `zig build test` to run the tests.\n", .{});
}
pub fn add(a: i32, b: i32) i32 {
return a + b;
}
test "basic add functionality" {
try std.testing.expect(add(3, 7) == 10);
}

View File

@@ -1,25 +0,0 @@
const std = @import("std");
pub const c = @cImport({
@cInclude("volk.h");
});
pub const InstanceTable = blk: {
const info = @typeInfo(c.VolkInstanceTable).@"struct";
var field_names: [info.fields.len][]const u8 = undefined;
var field_types: [info.fields.len]type = undefined;
var field_attrs: [info.fields.len]std.builtin.Type.StructField.Attributes = undefined;
for (info.fields, &field_names, &field_types, &field_attrs) |field, *field_name, *field_type, *field_attr| {
field_name.* = field.name;
field_type.* = std.meta.Child(field.type);
field_attr.* = .{};
}
break :blk @Struct(.auto, null, field_names, field_types, field_attrs);
};
// pub const InstanceTable: type = {
//
// };

267
vk/root.zig Normal file
View File

@@ -0,0 +1,267 @@
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;
}