113 lines
3.9 KiB
Rust
113 lines
3.9 KiB
Rust
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
//! Logic for selecting physical devices and creating logical devices.
|
|
|
|
use crate::error::RendererError;
|
|
use ash::{Device, Instance, khr, vk};
|
|
|
|
/// Picks a physical device (GPU) that supports the required features and extensions.
|
|
pub 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()? };
|
|
|
|
for device in devices {
|
|
if is_device_suitable(instance, device, surface_loader, surface) {
|
|
let props = unsafe { instance.get_physical_device_properties(device) };
|
|
let name =
|
|
unsafe { std::ffi::CStr::from_ptr(props.device_name.as_ptr()).to_string_lossy() };
|
|
tracing::info!("Selected GPU: \"{name}\"");
|
|
return Ok(device);
|
|
}
|
|
}
|
|
|
|
Err(RendererError::NoSuitableGpu)
|
|
}
|
|
|
|
/// Creates a logical device and retrieves the graphics queue.
|
|
pub fn create_logical_device(
|
|
instance: &Instance,
|
|
physical_device: vk::PhysicalDevice,
|
|
queue_family_index: u32,
|
|
) -> Result<(Device, vk::Queue), RendererError> {
|
|
let priorities = [1.0];
|
|
let queue_info = vk::DeviceQueueCreateInfo::default()
|
|
.queue_family_index(queue_family_index)
|
|
.queue_priorities(&priorities);
|
|
|
|
let device_extensions = [khr::swapchain::NAME.as_ptr()];
|
|
|
|
// Enable Vulkan 1.3 features
|
|
let mut synchronization2_features =
|
|
vk::PhysicalDeviceSynchronization2Features::default().synchronization2(true);
|
|
let mut dynamic_rendering_features =
|
|
vk::PhysicalDeviceDynamicRenderingFeatures::default().dynamic_rendering(true);
|
|
|
|
let create_info = vk::DeviceCreateInfo::default()
|
|
.queue_create_infos(std::slice::from_ref(&queue_info))
|
|
.enabled_extension_names(&device_extensions)
|
|
.push_next(&mut synchronization2_features)
|
|
.push_next(&mut dynamic_rendering_features);
|
|
|
|
let device = unsafe { instance.create_device(physical_device, &create_info, None)? };
|
|
let graphics_queue = unsafe { device.get_device_queue(queue_family_index, 0) };
|
|
|
|
Ok((device, graphics_queue))
|
|
}
|
|
|
|
/// Finds a queue family that supports both graphics commands and presentation.
|
|
pub fn find_graphics_queue_family(
|
|
instance: &Instance,
|
|
physical_device: vk::PhysicalDevice,
|
|
surface_loader: &khr::surface::Instance,
|
|
surface: vk::SurfaceKHR,
|
|
) -> Result<u32, RendererError> {
|
|
let props = unsafe { instance.get_physical_device_queue_family_properties(physical_device) };
|
|
|
|
for (index, prop) in props.iter().enumerate() {
|
|
#[allow(clippy::expect_used)]
|
|
let index = u32::try_from(index).expect("Queue family index exceeds u32 range");
|
|
let graphics = prop.queue_flags.contains(vk::QueueFlags::GRAPHICS);
|
|
let present = unsafe {
|
|
surface_loader.get_physical_device_surface_support(physical_device, index, surface)?
|
|
};
|
|
|
|
if graphics && present {
|
|
return Ok(index);
|
|
}
|
|
}
|
|
|
|
Err(RendererError::NoSuitableGpu)
|
|
}
|
|
|
|
fn is_device_suitable(
|
|
instance: &Instance,
|
|
device: vk::PhysicalDevice,
|
|
surface_loader: &khr::surface::Instance,
|
|
surface: vk::SurfaceKHR,
|
|
) -> bool {
|
|
let extensions = unsafe {
|
|
instance
|
|
.enumerate_device_extension_properties(device)
|
|
.unwrap_or_default()
|
|
};
|
|
let has_swapchain = extensions.iter().any(|ext| unsafe {
|
|
std::ffi::CStr::from_ptr(ext.extension_name.as_ptr()) == khr::swapchain::NAME
|
|
});
|
|
|
|
let formats = unsafe {
|
|
surface_loader
|
|
.get_physical_device_surface_formats(device, surface)
|
|
.unwrap_or_default()
|
|
};
|
|
let present_modes = unsafe {
|
|
surface_loader
|
|
.get_physical_device_surface_present_modes(device, surface)
|
|
.unwrap_or_default()
|
|
};
|
|
|
|
has_swapchain && !formats.is_empty() && !present_modes.is_empty()
|
|
}
|