diff --git a/src/app.rs b/src/app.rs index 3b1a544..35c8b3b 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,5 +1,5 @@ mod functions; -use functions::{create_instance, create_logical_device, pick_physical_device}; +use functions::{create_instance, create_logical_device, create_swapchain, pick_physical_device}; use crate::VALIDATION_ENABLED; use ::anyhow::{anyhow, Result}; @@ -7,11 +7,14 @@ use ::log::*; use ::thiserror::Error; use ::vulkanalia::loader::{LibloadingLoader, LIBRARY}; use ::vulkanalia::prelude::v1_0::*; -use ::vulkanalia::vk::ExtDebugUtilsExtension; -use ::vulkanalia::vk::KhrSurfaceExtension; +use ::vulkanalia::vk::{ExtDebugUtilsExtension, KhrSurfaceExtension, KhrSwapchainExtension}; use ::vulkanalia::window as vk_window; use ::winit::window::Window; +pub(crate) const VALIDATION_LAYER: vk::ExtensionName = + vk::ExtensionName::from_bytes(b"VK_LAYER_KHRONOS_validation"); +pub(crate) const DEVICE_EXTENSIONS: &[vk::ExtensionName] = &[vk::KHR_SWAPCHAIN_EXTENSION.name]; + /// Our Vulkan app. #[derive(Clone, Debug)] pub struct App { @@ -31,6 +34,7 @@ impl App { data.surface = vk_window::create_surface(&instance, window)?; pick_physical_device(&instance, &mut data)?; let device = create_logical_device(&instance, &mut data)?; + create_swapchain(window, &instance, &device, &mut data)?; Ok(Self { entry, @@ -47,6 +51,7 @@ impl App { /// Destroys our Vulkan app. pub unsafe fn destroy(&mut self) { + self.device.destroy_swapchain_khr(self.data.swapchain, None); self.device.destroy_device(None); if VALIDATION_ENABLED { @@ -62,13 +67,18 @@ impl App { /// The Vulkan handles and associated properties used by our Vulkan app. #[derive(Clone, Debug, Default)] pub struct AppData { - surface: vk::SurfaceKHR, // Debug messenger: vk::DebugUtilsMessengerEXT, + // Surface + surface: vk::SurfaceKHR, // Physical Device / Logical Device physical_device: vk::PhysicalDevice, graphics_queue: vk::Queue, present_queue: vk::Queue, + swapchain_format: vk::Format, + swapchain_extent: vk::Extent2D, + swapchain: vk::SwapchainKHR, + swapchain_images: Vec, } #[derive(Debug, Error)] @@ -115,3 +125,27 @@ impl QueueFamilyIndicies { } } } + +#[derive(Clone, Debug)] +pub(crate) struct SwapchainSupport { + capabilities: vk::SurfaceCapabilitiesKHR, + formats: Vec, + present_modes: Vec, +} + +impl SwapchainSupport { + unsafe fn get( + instance: &Instance, + data: &AppData, + physical_device: vk::PhysicalDevice, + ) -> Result { + Ok(Self { + capabilities: instance + .get_physical_device_surface_capabilities_khr(physical_device, data.surface)?, + formats: instance + .get_physical_device_surface_formats_khr(physical_device, data.surface)?, + present_modes: instance + .get_physical_device_surface_present_modes_khr(physical_device, data.surface)?, + }) + } +} diff --git a/src/app/functions.rs b/src/app/functions.rs index f7e9bdf..ac7db03 100644 --- a/src/app/functions.rs +++ b/src/app/functions.rs @@ -1,5 +1,5 @@ use super::*; -use crate::{VALIDATION_ENABLED, VALIDATION_LAYER}; +use crate::VALIDATION_ENABLED; use ::anyhow::{anyhow, Result}; use ::log::*; @@ -139,10 +139,35 @@ pub(super) unsafe fn check_physical_device( physical_device: vk::PhysicalDevice, ) -> Result<()> { QueueFamilyIndicies::get(instance, data, physical_device)?; + check_physical_device_extensions(instance, physical_device)?; + + let support = SwapchainSupport::get(instance, data, physical_device)?; + if support.formats.is_empty() || support.present_modes.is_empty() { + return Err(anyhow!(SuitabilityError("Insufficient swapchain support."))); + } Ok(()) } +pub(super) unsafe fn check_physical_device_extensions( + instance: &Instance, + physical_device: vk::PhysicalDevice, +) -> Result<()> { + let extensions = instance + .enumerate_device_extension_properties(physical_device, None)? + .iter() + .map(|e| e.extension_name) + .collect::>(); + + if DEVICE_EXTENSIONS.iter().all(|e| extensions.contains(e)) { + Ok(()) + } else { + Err(anyhow!(SuitabilityError( + "Missing required device extensions." + ))) + } +} + pub(super) unsafe fn create_logical_device( instance: &Instance, data: &mut AppData, @@ -172,15 +197,19 @@ pub(super) unsafe fn create_logical_device( }; // Extensions - let extension_names = if instance + let mut extensions = DEVICE_EXTENSIONS + .iter() + .map(|n| n.as_ptr()) + .collect::>(); + + // mac OS Metal -> Vulkan rendering fix + if instance .enumerate_device_extension_properties(data.physical_device, None)? .iter() .any(|e| e.extension_name == vk::KHR_PORTABILITY_SUBSET_EXTENSION.name) { - vec![vk::KHR_PORTABILITY_SUBSET_EXTENSION.name.as_ptr()] - } else { - vec![] - }; + extensions.push(vk::KHR_PORTABILITY_SUBSET_EXTENSION.name.as_ptr()); + } // Features let features = vk::PhysicalDeviceFeatures::builder(); @@ -189,8 +218,8 @@ pub(super) unsafe fn create_logical_device( let info = vk::DeviceCreateInfo::builder() .queue_create_infos(&queue_infos) .enabled_layer_names(&layers) - .enabled_features(&features) - .enabled_extension_names(&extension_names); + .enabled_extension_names(&extensions) + .enabled_features(&features); let device = instance.create_device(data.physical_device, &info, None)?; @@ -200,3 +229,103 @@ pub(super) unsafe fn create_logical_device( Ok(device) } + +pub(super) fn get_swapchain_surface_format( + formats: &[vk::SurfaceFormatKHR], +) -> vk::SurfaceFormatKHR { + formats + .iter() + .cloned() + .find(|f| { + f.format == vk::Format::B8G8R8A8_SRGB + && f.color_space == vk::ColorSpaceKHR::SRGB_NONLINEAR + }) + .unwrap_or_else(|| formats[0]) +} + +pub(super) fn get_swapchain_present_mode( + present_modes: &[vk::PresentModeKHR], +) -> vk::PresentModeKHR { + present_modes + .iter() + .cloned() + .find(|m| *m == vk::PresentModeKHR::MAILBOX) + .unwrap_or(vk::PresentModeKHR::FIFO) +} + +pub(super) fn get_swapchain_extent( + window: &Window, + capabilities: vk::SurfaceCapabilitiesKHR, +) -> vk::Extent2D { + if capabilities.current_extent.width != u32::MAX { + capabilities.current_extent + } else { + let size = window.inner_size(); + let clamp = |min: u32, max: u32, v: u32| min.max(max.min(v)); + vk::Extent2D::builder() + .width(clamp( + capabilities.min_image_extent.width, + capabilities.max_image_extent.width, + size.width, + )) + .height(clamp( + capabilities.min_image_extent.height, + capabilities.max_image_extent.height, + size.height, + )) + .build() + } +} + +pub(super) unsafe fn create_swapchain( + window: &Window, + instance: &Instance, + device: &Device, + data: &mut AppData, +) -> Result<()> { + let indices = QueueFamilyIndicies::get(instance, data, data.physical_device)?; + let support = SwapchainSupport::get(instance, data, data.physical_device)?; + + let surface_format = get_swapchain_surface_format(&support.formats); + let present_mode = get_swapchain_present_mode(&support.present_modes); + let extent = get_swapchain_extent(window, support.capabilities); + + let mut image_count = support.capabilities.min_image_count + 1; + if support.capabilities.max_image_count != 0 + && image_count > support.capabilities.max_image_count + { + image_count = support.capabilities.max_image_count; + } + + let mut queue_family_indices = vec![]; + let image_sharing_mode = if indices.graphics != indices.present { + queue_family_indices.push(indices.graphics); + queue_family_indices.push(indices.present); + vk::SharingMode::CONCURRENT + } else { + vk::SharingMode::EXCLUSIVE + }; + + let info = vk::SwapchainCreateInfoKHR::builder() + .surface(data.surface) + .min_image_count(image_count) + .image_format(surface_format.format) + .image_color_space(surface_format.color_space) + .image_extent(extent) + .image_array_layers(1) + .image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT) + .image_sharing_mode(image_sharing_mode) + .queue_family_indices(&queue_family_indices) + .pre_transform(support.capabilities.current_transform) + .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE) + .present_mode(present_mode) + .clipped(true) + .old_swapchain(vk::SwapchainKHR::null()); + + data.swapchain = device.create_swapchain_khr(&info, None)?; + data.swapchain_images = device.get_swapchain_images_khr(data.swapchain)?; + data.swapchain_format = surface_format.format; + data.swapchain_extent = extent; + + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index 6b9b32c..4763a88 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,12 +2,9 @@ mod app; pub use app::App; use ::anyhow::Result; -use ::vulkanalia::prelude::v1_0::*; use ::winit::window::Window; pub const VALIDATION_ENABLED: bool = cfg!(debug_assertions); -pub const VALIDATION_LAYER: vk::ExtensionName = - vk::ExtensionName::from_bytes(b"VK_LAYER_KHRONOS_validation"); pub fn create_app(window: &Window) -> Result { Ok(unsafe { App::create(&window)? })