diff --git a/src/utilities.zig b/src/utilities.zig index 79d9519..dfdb98c 100644 --- a/src/utilities.zig +++ b/src/utilities.zig @@ -11,3 +11,9 @@ pub const QueueFamilyIndices = struct { return self.graphics_family != null and self.presentation_family != null; } }; + +pub const SwapChainDetails = struct { + surface_capabilities: vk.SurfaceCapabilitiesKHR, + formats: []vk.SurfaceFormatKHR, + presentation_modes: []vk.PresentModeKHR, +}; diff --git a/src/vulkan_renderer.zig b/src/vulkan_renderer.zig index 63cde59..349485f 100644 --- a/src/vulkan_renderer.zig +++ b/src/vulkan_renderer.zig @@ -3,6 +3,7 @@ const sdl = @import("sdl2"); const vk = @import("vulkan"); const Utilities = @import("utilities.zig"); const QueueFamilyIndices = Utilities.QueueFamilyIndices; +const SwapChainDetails = Utilities.SwapChainDetails; const enable_validation_layers = true; const validation_layers = [_][*:0]const u8{"VK_LAYER_KHRONOS_validation"}; @@ -62,7 +63,7 @@ pub const VulkanRenderer = struct { self.surface = try sdl.vulkan.createSurface(self.window, self.instance.handle); if (enable_validation_layers) { - self.debug_utils = try self.createDebugMessenger(); + self.debug_utils = try createDebugMessenger(self.instance); } self.physical_device = try self.getPhysicalDevice(); @@ -123,6 +124,58 @@ pub const VulkanRenderer = struct { return try self.vkb.createInstance(&instance_create_info, null); } + fn createLogicalDevice(self: Self) !vk.Device { + const indices = try self.getQueueFamilies(self.physical_device); + // 1 is the highest priority + const priority = [_]f32{1}; + + const qci = [_]vk.DeviceQueueCreateInfo{ + .{ + .queue_family_index = indices.graphics_family.?, + .queue_count = 1, + .p_queue_priorities = &priority, + }, + .{ + .queue_family_index = indices.presentation_family.?, + .queue_count = 1, + .p_queue_priorities = &priority, + }, + }; + + const queue_count: u32 = if (indices.graphics_family.? == indices.presentation_family.?) + 1 + else + 2; + + const device_create_info: vk.DeviceCreateInfo = .{ + .queue_create_info_count = queue_count, + .p_queue_create_infos = &qci, + .pp_enabled_extension_names = &Utilities.device_extensions, + .enabled_extension_count = @intCast(Utilities.device_extensions.len), + }; + + return try self.instance.createDevice(self.physical_device, &device_create_info, null); + } + + fn getPhysicalDevice(self: Self) !vk.PhysicalDevice { + var pdev_count: u32 = 0; + _ = try self.instance.enumeratePhysicalDevices(&pdev_count, null); + + const pdevs = try self.allocator.alloc(vk.PhysicalDevice, pdev_count); + defer self.allocator.free(pdevs); + + _ = try self.instance.enumeratePhysicalDevices(&pdev_count, pdevs.ptr); + + for (pdevs) |pdev| { + if (self.checkDeviceSuitable(pdev)) { + return pdev; + } + } + + // TODO Obviously needs to be something else + unreachable; + } + fn getRequiredExtensions(self: Self) ![][*:0]const u8 { var ext_count = sdl.vulkan.getInstanceExtensionsCount(self.window); @@ -140,6 +193,43 @@ pub const VulkanRenderer = struct { return extensions; } + fn getQueueFamilies(self: Self, pdev: vk.PhysicalDevice) !QueueFamilyIndices { + var indices: QueueFamilyIndices = .{ .graphics_family = null }; + + var queue_family_count: u32 = 0; + self.instance.getPhysicalDeviceQueueFamilyProperties(pdev, &queue_family_count, null); + + const queue_family_list = try self.allocator.alloc(vk.QueueFamilyProperties, queue_family_count); + defer self.allocator.free(queue_family_list); + + self.instance.getPhysicalDeviceQueueFamilyProperties(pdev, &queue_family_count, queue_family_list.ptr); + + for (queue_family_list, 0..) |queue_family, i| { + if (queue_family.queue_count > 0 and queue_family.queue_flags.graphics_bit) { + indices.graphics_family = @intCast(i); + } + + const presentation_support = try self.instance.getPhysicalDeviceSurfaceSupportKHR(pdev, @intCast(i), self.surface); + if (queue_family.queue_count > 0 and presentation_support == vk.TRUE) { + indices.presentation_family = @intCast(i); + } + + if (indices.isValid()) { + return indices; + } + } + + unreachable; + } + + fn getDeviceQueues(self: Self) ![2]vk.Queue { + const indices = try self.getQueueFamilies(self.physical_device); + + const graphics_queue = self.device.getDeviceQueue(indices.graphics_family.?, 0); + const presentation_queue = self.device.getDeviceQueue(indices.presentation_family.?, 0); + return .{ graphics_queue, presentation_queue }; + } + fn checkInstanceExtensions(self: Self, required_extensions: *const [][*:0]const u8) !bool { var prop_count: u32 = 0; _ = try self.vkb.enumerateInstanceExtensionProperties(null, &prop_count, null); @@ -188,25 +278,6 @@ pub const VulkanRenderer = struct { return true; } - fn getPhysicalDevice(self: Self) !vk.PhysicalDevice { - var pdev_count: u32 = 0; - _ = try self.instance.enumeratePhysicalDevices(&pdev_count, null); - - const pdevs = try self.allocator.alloc(vk.PhysicalDevice, pdev_count); - defer self.allocator.free(pdevs); - - _ = try self.instance.enumeratePhysicalDevices(&pdev_count, pdevs.ptr); - - for (pdevs) |pdev| { - if (self.checkDeviceSuitable(pdev)) { - return pdev; - } - } - - // TODO Obviously needs to be something else - unreachable; - } - fn checkDeviceSuitable(self: Self, pdev: vk.PhysicalDevice) bool { const pdev_properties = self.instance.getPhysicalDeviceProperties(pdev); @@ -214,83 +285,17 @@ pub const VulkanRenderer = struct { return false; } - const queue_family_indices = self.getQueueFamilies(pdev) catch { - return false; - }; + const queue_family_indices = self.getQueueFamilies(pdev) catch return false; - const extension_support = self.checkDeviceExtensions(pdev) catch false; + const extension_support = self.checkDeviceExtensions(pdev) catch return false; - return queue_family_indices.isValid() and extension_support; - } + const swapchain_details = self.getSwapChainDetails(pdev) catch return false; + defer self.allocator.free(swapchain_details.formats); + defer self.allocator.free(swapchain_details.presentation_modes); - fn getQueueFamilies(self: Self, pdev: vk.PhysicalDevice) !QueueFamilyIndices { - var indices: QueueFamilyIndices = .{ .graphics_family = null }; + const swapchain_valid = swapchain_details.formats.len != 0 and swapchain_details.formats.len != 0; - var queue_family_count: u32 = 0; - self.instance.getPhysicalDeviceQueueFamilyProperties(pdev, &queue_family_count, null); - - const queue_family_list = try self.allocator.alloc(vk.QueueFamilyProperties, queue_family_count); - defer self.allocator.free(queue_family_list); - - self.instance.getPhysicalDeviceQueueFamilyProperties(pdev, &queue_family_count, queue_family_list.ptr); - - for (queue_family_list, 0..) |queue_family, i| { - if (queue_family.queue_count > 0 and queue_family.queue_flags.graphics_bit) { - indices.graphics_family = @intCast(i); - } - - const presentation_support = try self.instance.getPhysicalDeviceSurfaceSupportKHR(pdev, @intCast(i), self.surface); - if (queue_family.queue_count > 0 and presentation_support == vk.TRUE) { - indices.presentation_family = @intCast(i); - } - - if (indices.isValid()) { - return indices; - } - } - - unreachable; - } - - fn createLogicalDevice(self: Self) !vk.Device { - const indices = try self.getQueueFamilies(self.physical_device); - // 1 is the highest priority - const priority = [_]f32{1}; - - const qci = [_]vk.DeviceQueueCreateInfo{ - .{ - .queue_family_index = indices.graphics_family.?, - .queue_count = 1, - .p_queue_priorities = &priority, - }, - .{ - .queue_family_index = indices.presentation_family.?, - .queue_count = 1, - .p_queue_priorities = &priority, - }, - }; - - const queue_count: u32 = if (indices.graphics_family.? == indices.presentation_family.?) - 1 - else - 2; - - const device_create_info: vk.DeviceCreateInfo = .{ - .queue_create_info_count = queue_count, - .p_queue_create_infos = &qci, - .pp_enabled_extension_names = &Utilities.device_extensions, - .enabled_extension_count = @intCast(Utilities.device_extensions.len), - }; - - return try self.instance.createDevice(self.physical_device, &device_create_info, null); - } - - fn getDeviceQueues(self: Self) ![2]vk.Queue { - const indices = try self.getQueueFamilies(self.physical_device); - - const graphics_queue = self.device.getDeviceQueue(indices.graphics_family.?, 0); - const presentation_queue = self.device.getDeviceQueue(indices.presentation_family.?, 0); - return .{ graphics_queue, presentation_queue }; + return queue_family_indices.isValid() and extension_support and swapchain_valid; } fn checkValidationLayersSupport(self: Self) bool { @@ -317,10 +322,29 @@ pub const VulkanRenderer = struct { return false; } - fn createDebugMessenger(self: Self) !vk.DebugUtilsMessengerEXT { - const debug_create_info = getDebugUtilsCreateInfo(); + fn getSwapChainDetails(self: Self, pdev: vk.PhysicalDevice) !SwapChainDetails { + // Capabilities + const surface_capabilities = try self.instance.getPhysicalDeviceSurfaceCapabilitiesKHR(pdev, self.surface); - return try self.instance.createDebugUtilsMessengerEXT(&debug_create_info, null); + // Formats + var format_count: u32 = 0; + _ = try self.instance.getPhysicalDeviceSurfaceFormatsKHR(pdev, self.surface, &format_count, null); + + const formats = try self.allocator.alloc(vk.SurfaceFormatKHR, format_count); + _ = try self.instance.getPhysicalDeviceSurfaceFormatsKHR(pdev, self.surface, &format_count, formats.ptr); + + // Presentation modes + var present_mode_count: u32 = 0; + _ = try self.instance.getPhysicalDeviceSurfacePresentModesKHR(pdev, self.surface, &present_mode_count, null); + + const presentation_modes = try self.allocator.alloc(vk.PresentModeKHR, format_count); + _ = try self.instance.getPhysicalDeviceSurfacePresentModesKHR(pdev, self.surface, &present_mode_count, presentation_modes.ptr); + + return .{ + .surface_capabilities = surface_capabilities, + .formats = formats, + .presentation_modes = presentation_modes, + }; } pub fn deinit(self: *Self) void { @@ -336,6 +360,12 @@ pub const VulkanRenderer = struct { } }; +fn createDebugMessenger(instance: Instance) !vk.DebugUtilsMessengerEXT { + const debug_create_info = getDebugUtilsCreateInfo(); + + return try instance.createDebugUtilsMessengerEXT(&debug_create_info, null); +} + fn getDebugUtilsCreateInfo() vk.DebugUtilsMessengerCreateInfoEXT { return vk.DebugUtilsMessengerCreateInfoEXT{ .message_severity = .{ .verbose_bit_ext = true, .warning_bit_ext = true, .error_bit_ext = true }, @@ -349,11 +379,42 @@ fn getDebugUtilsCreateInfo() vk.DebugUtilsMessengerCreateInfoEXT { // p_callback_data: ?*const vk.DebugUtilsMessengerCallbackDataEXT, // p_user_data: ?*anyopaque, fn debugCallback( - _: vk.DebugUtilsMessageSeverityFlagsEXT, - _: vk.DebugUtilsMessageTypeFlagsEXT, + message_severity: vk.DebugUtilsMessageSeverityFlagsEXT, + message_types: vk.DebugUtilsMessageTypeFlagsEXT, p_callback_data: ?*const vk.DebugUtilsMessengerCallbackDataEXT, _: ?*anyopaque, ) callconv(vk.vulkan_call_conv) vk.Bool32 { - std.debug.print("Validation layers: {s}\n", .{p_callback_data.?.p_message.?}); + const severity = getMessageSeverityLabel(message_severity); + const message_type = getMessageTypeLabel(message_types); + + std.debug.print("[{s}] ({s}): {s}\n", .{ severity, message_type, p_callback_data.?.p_message.? }); return vk.TRUE; } + +inline fn getMessageSeverityLabel(message_severity: vk.DebugUtilsMessageSeverityFlagsEXT) []const u8 { + if (message_severity.verbose_bit_ext) { + return "VERBOSE"; + } else if (message_severity.info_bit_ext) { + return "INFO"; + } else if (message_severity.warning_bit_ext) { + return "WARNING"; + } else if (message_severity.error_bit_ext) { + return "ERROR"; + } else { + unreachable; + } +} + +inline fn getMessageTypeLabel(message_types: vk.DebugUtilsMessageTypeFlagsEXT) []const u8 { + if (message_types.general_bit_ext) { + return "general"; + } else if (message_types.validation_bit_ext) { + return "validation"; + } else if (message_types.performance_bit_ext) { + return "performance"; + } else if (message_types.device_address_binding_bit_ext) { + return "device_address_binding"; + } else { + return "unknown"; + } +}