diff --git a/src/Window.zig b/src/Window.zig index 690d9f9..58864a1 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -3,14 +3,19 @@ const Self = @This(); const std = @import("std"); const c = @import("c.zig"); const vk = @import("vk"); +const builtin = @import("builtin"); const BaseDispatch = vk.BaseWrapper(.{ .createInstance = true, .getInstanceProcAddr = true, .enumerateInstanceExtensionProperties = true, + .enumerateInstanceLayerProperties = true, }); const InstanceDispatch = vk.InstanceWrapper(.{ .destroyInstance = true, + .createDebugUtilsMessengerEXT = true, + .destroyDebugUtilsMessengerEXT = true, + .submitDebugUtilsMessageEXT = true, }); const DeviceDispatch = vk.DeviceWrapper(.{}); @@ -23,6 +28,7 @@ vkd: DeviceDispatch, window: *c.GLFWwindow, instance: vk.Instance, +messenger: vk.DebugUtilsMessengerEXT, pub fn mainLoop(self: Self) void { while (c.glfwWindowShouldClose(self.window) == 0) : (c.glfwPollEvents()) { @@ -46,9 +52,134 @@ fn deinitWindow(self: Self) void { c.glfwDestroyWindow(self.window); } +export fn debug_callback( + message_severity: vk.DebugUtilsMessageSeverityFlagsEXT, + message_type: vk.DebugUtilsMessageTypeFlagsEXT, + p_callback_data: ?*const vk.DebugUtilsMessengerCallbackDataEXT, + _: ?*anyopaque, +) callconv(.C) vk.Bool32 { + if (p_callback_data == null) return vk.FALSE; + if (p_callback_data.?.p_message == null) return vk.FALSE; + const msg = p_callback_data.?.p_message.?; + + const scopes = .{ + "validation", + "performance", + "device_address_binding", + "general", + }; + + const scope: []const u8 = inline for (scopes) |tag| { + if (@field(message_type, tag ++ "_bit_ext")) { + break tag; + } + } else { + return vk.FALSE; + }; + + const levels = .{ + "error", + "info", + "warning", + "verbose", + }; + + const level: []const u8 = inline for (levels) |tag| { + if (@field(message_severity, tag ++ "_bit_ext")) { + break tag; + } + } else { + return vk.FALSE; + }; + + // ripped from std.log, but with my own levels and scope. + const stderr = std.io.getStdErr().writer(); + var bw = std.io.bufferedWriter(stderr); + const writer = bw.writer(); + + std.debug.getStderrMutex().lock(); + defer std.debug.getStderrMutex().unlock(); + nosuspend { + writer.print("vk-{s}({s}): {s}\n", .{ level, scope, msg }) catch return vk.FALSE; + bw.flush() catch return vk.FALSE; + } + + return vk.FALSE; +} + fn initVulkan(self: *Self) !void { self.vkb = try BaseDispatch.load(&c.glfwGetInstanceProcAddress); + var exts = std.ArrayList([*:0]const u8).init(self.allocator); + defer exts.deinit(); + + var layers = std.ArrayList([*:0]const u8).init(self.allocator); + defer layers.deinit(); + + switch (builtin.mode) { + .ReleaseSafe, .Debug => { + try layers.append("VK_LAYER_KHRONOS_validation"); + try exts.append("VK_EXT_debug_utils"); + }, + .ReleaseSmall, .ReleaseFast => {}, + } + + var glfw_ext_count: u32 = 0; + const glfw_exts: [*][*:0]const u8 = @ptrCast(c.glfwGetRequiredInstanceExtensions(&glfw_ext_count)); + try exts.appendSlice(glfw_exts[0..glfw_ext_count]); + + std.log.debug("requesting extensions: {s}", .{exts.items}); + std.log.debug("requesting layers: {s}", .{layers.items}); + + var available_ext_count: u32 = 0; + _ = try self.vkb.enumerateInstanceExtensionProperties(null, &available_ext_count, null); + const available_exts = try self.allocator.alloc(vk.ExtensionProperties, available_ext_count); + defer self.allocator.free(available_exts); + _ = try self.vkb.enumerateInstanceExtensionProperties(null, &available_ext_count, available_exts.ptr); + + var available_layer_count: u32 = 0; + _ = try self.vkb.enumerateInstanceLayerProperties(&available_layer_count, null); + const available_layers = try self.allocator.alloc(vk.LayerProperties, available_layer_count); + defer self.allocator.free(available_layers); + _ = try self.vkb.enumerateInstanceLayerProperties(&available_layer_count, available_layers.ptr); + + for (exts.items) |name| { + const required_name = std.mem.sliceTo(name, 0); + for (available_exts) |prop| { + const available_name = std.mem.sliceTo(&prop.extension_name, 0); + if (std.mem.eql(u8, required_name, available_name)) break; + } else { + return error.ExtensionNotPresent; + } + } + + for (layers.items) |name| { + const required_name = std.mem.sliceTo(name, 0); + for (available_layers) |prop| { + const available_name = std.mem.sliceTo(&prop.layer_name, 0); + if (std.mem.eql(u8, required_name, available_name)) break; + } else { + return error.LayerNotPresent; + } + } + + const debug_create_info = vk.DebugUtilsMessengerCreateInfoEXT{ + .message_severity = vk.DebugUtilsMessageSeverityFlagsEXT{ + .verbose_bit_ext = false, + .warning_bit_ext = true, + .error_bit_ext = true, + .info_bit_ext = false, + }, + .message_type = vk.DebugUtilsMessageTypeFlagsEXT{ + .general_bit_ext = true, + .validation_bit_ext = true, + .performance_bit_ext = true, + .device_address_binding_bit_ext = false, + }, + .pfn_user_callback = &debug_callback, + .p_user_data = null, + }; + const app_info = vk.ApplicationInfo{ .p_application_name = "Hello World", .application_version = vk.makeApiVersion(0, 0, 0, 0), @@ -57,33 +188,25 @@ fn initVulkan(self: *Self) !void { .api_version = vk.API_VERSION_1_3, }; - var glfw_ext_count: u32 = 0; - const glfw_exts = c.glfwGetRequiredInstanceExtensions(&glfw_ext_count); - const create_info = vk.InstanceCreateInfo{ .p_application_info = &app_info, - .enabled_extension_count = glfw_ext_count, - .pp_enabled_extension_names = @as([*]const [*:0]const u8, @ptrCast(glfw_exts)), - .enabled_layer_count = 0, + .enabled_extension_count = @intCast(exts.items.len), + .pp_enabled_extension_names = exts.items.ptr, + .enabled_layer_count = @intCast(layers.items.len), + .pp_enabled_layer_names = layers.items.ptr, + .p_next = &debug_create_info, }; self.instance = try self.vkb.createInstance(&create_info, null); self.vki = try InstanceDispatch.load(self.instance, self.vkb.dispatch.vkGetInstanceProcAddr); errdefer self.vki.destroyInstance(self.instance, null); - var ext_count: u32 = 0; - _ = try self.vkb.enumerateInstanceExtensionProperties(null, &ext_count, null); - const extensions = try self.allocator.alloc(vk.ExtensionProperties, ext_count); - defer self.allocator.free(extensions); - _ = try self.vkb.enumerateInstanceExtensionProperties(null, &ext_count, extensions.ptr); - - std.debug.print("available extensions:\n", .{}); - for (extensions) |ext| { - std.debug.print("- {s}\n", .{ext.extension_name}); - } + self.messenger = try self.vki.createDebugUtilsMessengerEXT(self.instance, &debug_create_info, null); + errdefer self.vki.destroyDebugUtilsMessengerEXT(self.instance, self.messenger, null); } fn deinitVulkan(self: Self) void { + self.vki.destroyDebugUtilsMessengerEXT(self.instance, self.messenger, null); self.vki.destroyInstance(self.instance, null); }