Add the swapchain

This commit is contained in:
Timothy Warren 2023-04-05 16:34:09 -04:00
parent 2c5928dac4
commit f9c423cd47
3 changed files with 175 additions and 15 deletions

View File

@ -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<vk::Image>,
}
#[derive(Debug, Error)]
@ -115,3 +125,27 @@ impl QueueFamilyIndicies {
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct SwapchainSupport {
capabilities: vk::SurfaceCapabilitiesKHR,
formats: Vec<vk::SurfaceFormatKHR>,
present_modes: Vec<vk::PresentModeKHR>,
}
impl SwapchainSupport {
unsafe fn get(
instance: &Instance,
data: &AppData,
physical_device: vk::PhysicalDevice,
) -> Result<Self> {
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)?,
})
}
}

View File

@ -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::<HashSet<_>>();
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::<Vec<_>>();
// 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(())
}

View File

@ -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<App> {
Ok(unsafe { App::create(&window)? })