feat(renderer): implement surface creation and support checks
Created the OS surface and ensured that the selected GPU and queue family support presentation to that surface. Updated Drop to properly destroy the surface and improved documentation.
This commit is contained in:
parent
347f868af1
commit
5ee9fc1bd6
|
|
@ -5,6 +5,7 @@ edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ash = "0.38.0"
|
ash = "0.38.0"
|
||||||
|
ash-window = "0.13.0"
|
||||||
raw-window-handle = "0.6.2"
|
raw-window-handle = "0.6.2"
|
||||||
thiserror = "2.0.18"
|
thiserror = "2.0.18"
|
||||||
tracing = "0.1.44"
|
tracing = "0.1.44"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
|
||||||
use std::ffi::{CStr, c_char};
|
use std::ffi::{CStr, c_char};
|
||||||
use ash::{Entry, Instance, ext, vk, Device};
|
use ash::{Device, Entry, Instance, ext, khr, vk};
|
||||||
use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
|
use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
|
||||||
use tracing::{debug, error, info, warn};
|
use tracing::{debug, error, info, warn};
|
||||||
|
|
||||||
|
|
@ -25,6 +25,8 @@ pub struct Renderer {
|
||||||
graphics_queue: vk::Queue,
|
graphics_queue: vk::Queue,
|
||||||
/// The index of the graphics queue family.
|
/// The index of the graphics queue family.
|
||||||
graphics_queue_index: u32,
|
graphics_queue_index: u32,
|
||||||
|
surface_loader: khr::surface::Instance,
|
||||||
|
surface: vk::SurfaceKHR,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderer {
|
impl Renderer {
|
||||||
|
|
@ -88,10 +90,19 @@ impl Renderer {
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
let (debug_utils, debug_messenger) = (None, vk::DebugUtilsMessengerEXT::null());
|
let (debug_utils, debug_messenger) = (None, vk::DebugUtilsMessengerEXT::null());
|
||||||
|
|
||||||
let physical_device = Self::pick_physical_device(&instance)?;
|
// Create the OS surface
|
||||||
|
let surface = unsafe {
|
||||||
|
ash_window::create_surface(&entry, &instance, display_handle, window_handle, None)?
|
||||||
|
};
|
||||||
|
let surface_loader = khr::surface::Instance::new(&entry, &instance);
|
||||||
|
|
||||||
// Find the graphics queue and create the logical device
|
// Pick a physical device that supports our surface
|
||||||
let graphics_queue_index = Self::find_graphics_queue_family(&instance, physical_device)?;
|
let physical_device = Self::pick_physical_device(&instance, &surface_loader, surface)?;
|
||||||
|
|
||||||
|
// Find the graphics queue family (which must also support presentation)
|
||||||
|
let graphics_queue_index = Self::find_graphics_queue_family(&instance, physical_device, &surface_loader, surface)?;
|
||||||
|
|
||||||
|
// Create the logical device
|
||||||
let (device, graphics_queue) = Self::create_logical_device(&instance, physical_device, graphics_queue_index)?;
|
let (device, graphics_queue) = Self::create_logical_device(&instance, physical_device, graphics_queue_index)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
|
@ -103,6 +114,8 @@ impl Renderer {
|
||||||
device,
|
device,
|
||||||
graphics_queue,
|
graphics_queue,
|
||||||
graphics_queue_index,
|
graphics_queue_index,
|
||||||
|
surface_loader,
|
||||||
|
surface,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -110,14 +123,25 @@ impl Renderer {
|
||||||
///
|
///
|
||||||
/// This function filters for devices supporting graphics operations and scores
|
/// This function filters for devices supporting graphics operations and scores
|
||||||
/// them based on their type, preferring discrete GPUs.
|
/// them based on their type, preferring discrete GPUs.
|
||||||
fn pick_physical_device(instance: &Instance) -> Result<vk::PhysicalDevice, RendererError> {
|
fn pick_physical_device(
|
||||||
|
instance: &Instance,
|
||||||
|
surface_loader: &khr::surface::Instance,
|
||||||
|
surface: vk::SurfaceKHR,
|
||||||
|
) -> Result<vk::PhysicalDevice, RendererError> {
|
||||||
let devices = unsafe { instance.enumerate_physical_devices()? };
|
let devices = unsafe { instance.enumerate_physical_devices()? };
|
||||||
|
|
||||||
let selected = devices.into_iter()
|
let selected = devices.into_iter()
|
||||||
.filter(|&device| {
|
.filter(|&device| {
|
||||||
// Check if the device has a queue family that supports graphics
|
// Check if the device has a queue family that supports graphics AND presentation
|
||||||
let props = unsafe { instance.get_physical_device_queue_family_properties(device) };
|
let props = unsafe { instance.get_physical_device_queue_family_properties(device) };
|
||||||
props.iter().any(|p| p.queue_flags.contains(vk::QueueFlags::GRAPHICS))
|
props.iter().enumerate().any(|(i, p)| {
|
||||||
|
let supports_graphics = p.queue_flags.contains(vk::QueueFlags::GRAPHICS);
|
||||||
|
let supports_present = unsafe {
|
||||||
|
surface_loader.get_physical_device_surface_support(device, i as u32, surface)
|
||||||
|
.unwrap_or(false)
|
||||||
|
};
|
||||||
|
supports_graphics && supports_present
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.max_by_key(|&device| {
|
.max_by_key(|&device| {
|
||||||
// Score the device based on its type
|
// Score the device based on its type
|
||||||
|
|
@ -136,15 +160,24 @@ impl Renderer {
|
||||||
Ok(selected)
|
Ok(selected)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finds the index of the first queue family that supports graphics operations.
|
/// Finds the index of the first queue family that supports both graphics and presentation.
|
||||||
fn find_graphics_queue_family(
|
fn find_graphics_queue_family(
|
||||||
instance: &Instance,
|
instance: &Instance,
|
||||||
physical_device: vk::PhysicalDevice,
|
physical_device: vk::PhysicalDevice,
|
||||||
|
surface_loader: &khr::surface::Instance,
|
||||||
|
surface: vk::SurfaceKHR,
|
||||||
) -> Result<u32, RendererError> {
|
) -> Result<u32, RendererError> {
|
||||||
let props = unsafe { instance.get_physical_device_queue_family_properties(physical_device) };
|
let props = unsafe { instance.get_physical_device_queue_family_properties(physical_device) };
|
||||||
|
|
||||||
props.iter().enumerate()
|
props.iter().enumerate()
|
||||||
.find(|(_, p)| p.queue_flags.contains(vk::QueueFlags::GRAPHICS))
|
.find(|&(i, p)| {
|
||||||
|
let supports_graphics = p.queue_flags.contains(vk::QueueFlags::GRAPHICS);
|
||||||
|
let supports_present = unsafe {
|
||||||
|
surface_loader.get_physical_device_surface_support(physical_device, i as u32, surface)
|
||||||
|
.unwrap_or(false)
|
||||||
|
};
|
||||||
|
supports_graphics && supports_present
|
||||||
|
})
|
||||||
.map(|(i, _)| i as u32)
|
.map(|(i, _)| i as u32)
|
||||||
.ok_or(RendererError::NoSuitableGpu)
|
.ok_or(RendererError::NoSuitableGpu)
|
||||||
}
|
}
|
||||||
|
|
@ -167,7 +200,7 @@ impl Renderer {
|
||||||
.synchronization2(true); // Cleaner memory barriers and synchronization
|
.synchronization2(true); // Cleaner memory barriers and synchronization
|
||||||
|
|
||||||
// We need the swapchain extension to present images to the window
|
// We need the swapchain extension to present images to the window
|
||||||
let device_extensions = [ash::khr::swapchain::NAME.as_ptr()];
|
let device_extensions = [khr::swapchain::NAME.as_ptr()];
|
||||||
|
|
||||||
let create_info = vk::DeviceCreateInfo::default()
|
let create_info = vk::DeviceCreateInfo::default()
|
||||||
.queue_create_infos(std::slice::from_ref(&queue_info))
|
.queue_create_infos(std::slice::from_ref(&queue_info))
|
||||||
|
|
@ -185,12 +218,18 @@ impl Renderer {
|
||||||
impl Drop for Renderer {
|
impl Drop for Renderer {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
// 1. Destroy the logical device first
|
||||||
self.device.destroy_device(None);
|
self.device.destroy_device(None);
|
||||||
|
|
||||||
|
// 2. Destroy the surface
|
||||||
|
self.surface_loader.destroy_surface(self.surface, None);
|
||||||
|
|
||||||
|
// 3. Destroy the debug messenger if we created one
|
||||||
if let Some(utils) = &self.debug_utils {
|
if let Some(utils) = &self.debug_utils {
|
||||||
utils.destroy_debug_utils_messenger(self.debug_messenger, None);
|
utils.destroy_debug_utils_messenger(self.debug_messenger, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. Finally, destroy the instance
|
||||||
self.instance.destroy_instance(None);
|
self.instance.destroy_instance(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue