diff options
| author | Valentin Popov <valentin@popov.link> | 2026-06-25 06:11:36 +0300 |
|---|---|---|
| committer | Valentin Popov <valentin@popov.link> | 2026-06-25 10:45:37 +0300 |
| commit | b473b100c892a4b73ba2a5ee110364b9b923a4fc (patch) | |
| tree | a03b303325fa843cd7704cd4daecb40e3a890940 | |
| parent | 3c3221566528774651e709bc86de7b4a58ab5966 (diff) | |
| download | fparkan-b473b100c892a4b73ba2a5ee110364b9b923a4fc.tar.xz fparkan-b473b100c892a4b73ba2a5ee110364b9b923a4fc.zip | |
refactor(vulkan-ffi): extract capability probe module
| -rw-r--r-- | adapters/fparkan-render-vulkan/src/ffi.rs | 9 | ||||
| -rw-r--r-- | adapters/fparkan-render-vulkan/src/ffi/capabilities.rs | 405 | ||||
| -rw-r--r-- | adapters/fparkan-render-vulkan/src/ffi/runtime.rs | 405 | ||||
| -rw-r--r-- | adapters/fparkan-render-vulkan/src/ffi/swapchain.rs | 2 | ||||
| -rw-r--r-- | xtask/src/main.rs | 1 |
5 files changed, 418 insertions, 404 deletions
diff --git a/adapters/fparkan-render-vulkan/src/ffi.rs b/adapters/fparkan-render-vulkan/src/ffi.rs index a2daef7..54e9cf3 100644 --- a/adapters/fparkan-render-vulkan/src/ffi.rs +++ b/adapters/fparkan-render-vulkan/src/ffi.rs @@ -27,6 +27,7 @@ //! //! This crate is the declared low-level Vulkan boundary. +mod capabilities; mod instance; mod resources; mod runtime; @@ -37,6 +38,9 @@ mod swapchain; mod swapchain_resources; mod validation; +pub use self::capabilities::{ + probe_vulkan_runtime_capabilities, VulkanRuntimeCapabilityError, VulkanRuntimeCapabilityProbe, +}; pub use self::instance::{ create_vulkan_instance_probe, plan_vulkan_instance, probe_vulkan_loader, render_instance_plan_json, render_loader_probe_report_json, vulkan_entry_symbol_name, @@ -51,9 +55,8 @@ use self::resources::{ VulkanFrameSync, }; pub use self::runtime::{ - create_vulkan_logical_device_probe, probe_vulkan_runtime_capabilities, - VulkanLogicalDeviceError, VulkanLogicalDeviceProbe, VulkanLogicalDeviceReport, - VulkanRuntimeCapabilityError, VulkanRuntimeCapabilityProbe, + create_vulkan_logical_device_probe, VulkanLogicalDeviceError, VulkanLogicalDeviceProbe, + VulkanLogicalDeviceReport, }; pub use self::smoke_types::{ VulkanSmokeFrameOutcome, VulkanSmokeRenderer, VulkanSmokeRendererCreateInfo, diff --git a/adapters/fparkan-render-vulkan/src/ffi/capabilities.rs b/adapters/fparkan-render-vulkan/src/ffi/capabilities.rs new file mode 100644 index 0000000..94fb3a6 --- /dev/null +++ b/adapters/fparkan-render-vulkan/src/ffi/capabilities.rs @@ -0,0 +1,405 @@ +#![allow(unsafe_code)] + +use ash::vk; +use std::ffi::CStr; + +use super::{VulkanInstanceProbe, VulkanSurfaceProbe}; +use crate::policy::{ + compare_reports, plan_vulkan_swapchain, validate_device, VulkanCapabilityError, + VulkanCapabilityReport, VulkanDeviceType, VulkanPhysicalDeviceRecord, VulkanQueueFamily, + VulkanSurfaceFormat, VulkanSwapchainError, VulkanSwapchainPlan, VulkanSwapchainRequest, + VulkanSwapchainSurfaceCapabilities, +}; + +/// Live Vulkan device/surface capability probe. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct VulkanRuntimeCapabilityProbe { + /// Selected device/queue capability report. + pub capability: VulkanCapabilityReport, + /// Swapchain plan built from the selected device and live surface capabilities. + pub swapchain: VulkanSwapchainPlan, +} + +/// Live Vulkan device/surface capability probe error. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum VulkanRuntimeCapabilityError { + /// Physical device enumeration failed. + EnumerateDevicesFailed { + /// Vulkan result. + result: vk::Result, + }, + /// Device extension enumeration failed. + EnumerateDeviceExtensionsFailed { + /// Device name or index context. + device: String, + /// Vulkan result. + result: vk::Result, + }, + /// Queue-family present support query failed. + PresentSupportFailed { + /// Device name. + device: String, + /// Queue-family index. + queue_family: u32, + /// Vulkan result. + result: vk::Result, + }, + /// Surface format query failed. + SurfaceFormatsFailed { + /// Device name. + device: String, + /// Vulkan result. + result: vk::Result, + }, + /// Surface capability query failed. + SurfaceCapabilitiesFailed { + /// Device name. + device: String, + /// Vulkan result. + result: vk::Result, + }, + /// Present mode query failed. + PresentModesFailed { + /// Device name. + device: String, + /// Vulkan result. + result: vk::Result, + }, + /// No device satisfied Stage 0 capability policy. + Capability(VulkanCapabilityError), + /// Live surface capabilities could not produce a swapchain plan. + Swapchain(VulkanSwapchainError), +} + +impl std::fmt::Display for VulkanRuntimeCapabilityError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::EnumerateDevicesFailed { result } => { + write!(f, "Vulkan physical device enumeration failed: {result:?}") + } + Self::EnumerateDeviceExtensionsFailed { device, result } => write!( + f, + "Vulkan device {device} extension enumeration failed: {result:?}" + ), + Self::PresentSupportFailed { + device, + queue_family, + result, + } => write!( + f, + "Vulkan device {device} queue family {queue_family} present support query failed: {result:?}" + ), + Self::SurfaceFormatsFailed { device, result } => write!( + f, + "Vulkan device {device} surface format query failed: {result:?}" + ), + Self::SurfaceCapabilitiesFailed { device, result } => write!( + f, + "Vulkan device {device} surface capabilities query failed: {result:?}" + ), + Self::PresentModesFailed { device, result } => write!( + f, + "Vulkan device {device} present mode query failed: {result:?}" + ), + Self::Capability(error) => write!(f, "{error}"), + Self::Swapchain(error) => write!(f, "{error}"), + } + } +} + +impl std::error::Error for VulkanRuntimeCapabilityError {} + +pub(super) struct SelectedLiveDevice { + pub(super) physical_device: vk::PhysicalDevice, + pub(super) runtime: VulkanRuntimeCapabilityProbe, +} + +struct LiveDeviceCandidate { + physical_device: vk::PhysicalDevice, + capability: VulkanCapabilityReport, + surface_formats: Vec<VulkanSurfaceFormat>, + present_modes: Vec<i32>, + surface_capabilities: VulkanSwapchainSurfaceCapabilities, +} + +/// Probes live Vulkan device, queue, surface and swapchain capabilities. +/// +/// # Errors +/// +/// Returns [`VulkanRuntimeCapabilityError`] when device enumeration, surface +/// capability queries, Stage 0 device selection, or swapchain planning fails. +pub fn probe_vulkan_runtime_capabilities( + instance: &VulkanInstanceProbe, + surface: &VulkanSurfaceProbe, + drawable_extent: (u32, u32), +) -> Result<VulkanRuntimeCapabilityProbe, VulkanRuntimeCapabilityError> { + let selected = select_live_device_candidate(instance, surface, drawable_extent)?; + Ok(selected.runtime) +} + +pub(super) fn select_live_device_candidate( + instance: &VulkanInstanceProbe, + surface: &VulkanSurfaceProbe, + drawable_extent: (u32, u32), +) -> Result<SelectedLiveDevice, VulkanRuntimeCapabilityError> { + let devices = { + // SAFETY: The Vulkan instance is live for this query and no handles are retained. + unsafe { instance.instance.enumerate_physical_devices() }.map_err(|error| { + VulkanRuntimeCapabilityError::EnumerateDevicesFailed { result: error } + })? + }; + let mut best: Option<LiveDeviceCandidate> = None; + let mut last_error = None; + for (index, device) in devices.iter().copied().enumerate() { + let candidate = match live_device_candidate(instance, surface, device, index) { + Ok(candidate) => candidate, + Err(err) => { + last_error = Some(err); + continue; + } + }; + match &best { + Some(existing) + if compare_reports(&candidate.capability, &existing.capability) + != std::cmp::Ordering::Greater => {} + _ => best = Some(candidate), + } + } + let best = best.ok_or_else(|| { + last_error.unwrap_or(VulkanRuntimeCapabilityError::Capability( + VulkanCapabilityError::NoPhysicalDevice, + )) + })?; + let swapchain = plan_vulkan_swapchain(&VulkanSwapchainRequest { + drawable_extent, + formats: best.surface_formats, + present_modes: best.present_modes, + capabilities: best.surface_capabilities, + preferred_present_mode: vk::PresentModeKHR::MAILBOX.as_raw(), + }) + .map_err(VulkanRuntimeCapabilityError::Swapchain)?; + Ok(SelectedLiveDevice { + physical_device: best.physical_device, + runtime: VulkanRuntimeCapabilityProbe { + capability: best.capability, + swapchain, + }, + }) +} + +fn live_device_candidate( + instance: &VulkanInstanceProbe, + surface: &VulkanSurfaceProbe, + device: vk::PhysicalDevice, + index: usize, +) -> Result<LiveDeviceCandidate, VulkanRuntimeCapabilityError> { + let properties = { + // SAFETY: `device` was returned by this live instance and the result is copied by value. + unsafe { instance.instance.get_physical_device_properties(device) } + }; + let name = physical_device_name(&properties, index); + let queue_properties = { + // SAFETY: `device` was returned by this live instance and the result is owned by Rust. + unsafe { + instance + .instance + .get_physical_device_queue_family_properties(device) + } + }; + let extensions = live_device_extensions(instance, device, &name)?; + let surface_formats = live_surface_formats(surface, device, &name)?; + let present_modes = live_present_modes(surface, device, &name)?; + let surface_capabilities = live_surface_capabilities(surface, device, &name)?; + let queue_families = queue_properties + .iter() + .enumerate() + .map(|(queue_index, properties)| { + let index = u32::try_from(queue_index).unwrap_or(u32::MAX); + let present = { + // SAFETY: The physical device, surface and queue-family index are live query inputs. + unsafe { + surface.loader.get_physical_device_surface_support( + device, + index, + surface.surface, + ) + } + } + .map_err(|error| VulkanRuntimeCapabilityError::PresentSupportFailed { + device: name.clone(), + queue_family: index, + result: error, + })?; + Ok(VulkanQueueFamily { + index, + graphics: properties.queue_flags.contains(vk::QueueFlags::GRAPHICS), + present, + }) + }) + .collect::<Result<Vec<_>, VulkanRuntimeCapabilityError>>()?; + let record = VulkanPhysicalDeviceRecord { + name, + api_version: properties.api_version, + device_type: match properties.device_type { + vk::PhysicalDeviceType::DISCRETE_GPU => VulkanDeviceType::DiscreteGpu, + vk::PhysicalDeviceType::INTEGRATED_GPU => VulkanDeviceType::IntegratedGpu, + vk::PhysicalDeviceType::CPU => VulkanDeviceType::Cpu, + _ => VulkanDeviceType::Other, + }, + extensions, + queue_families, + surface_formats: surface_formats.clone(), + present_modes: present_modes.clone(), + surface_capabilities, + }; + let capability = validate_device(&record).map_err(VulkanRuntimeCapabilityError::Capability)?; + Ok(LiveDeviceCandidate { + physical_device: device, + capability, + surface_formats, + present_modes, + surface_capabilities, + }) +} + +pub(super) fn unique_queue_families(graphics: u32, present: u32) -> Vec<u32> { + if graphics == present { + vec![graphics] + } else { + vec![graphics, present] + } +} + +fn physical_device_name(properties: &vk::PhysicalDeviceProperties, index: usize) -> String { + // SAFETY: Vulkan device names are fixed-size NUL-terminated C strings per the spec. + let name = unsafe { CStr::from_ptr(properties.device_name.as_ptr()) } + .to_string_lossy() + .trim() + .to_string(); + if name.is_empty() { + format!("physical-device-{index}") + } else { + name + } +} + +fn live_device_extensions( + instance: &VulkanInstanceProbe, + device: vk::PhysicalDevice, + name: &str, +) -> Result<Vec<String>, VulkanRuntimeCapabilityError> { + let properties = { + // SAFETY: `device` was returned by this live instance and no borrowed data escapes. + unsafe { + instance + .instance + .enumerate_device_extension_properties(device) + } + } + .map_err( + |error| VulkanRuntimeCapabilityError::EnumerateDeviceExtensionsFailed { + device: name.to_string(), + result: error, + }, + )?; + let mut extensions = properties + .iter() + .map(|property| { + // SAFETY: Vulkan extension names are fixed-size NUL-terminated C strings per the spec. + unsafe { CStr::from_ptr(property.extension_name.as_ptr()) } + .to_string_lossy() + .into_owned() + }) + .collect::<Vec<_>>(); + extensions.sort(); + extensions.dedup(); + Ok(extensions) +} + +pub(super) fn live_surface_formats( + surface: &VulkanSurfaceProbe, + device: vk::PhysicalDevice, + name: &str, +) -> Result<Vec<VulkanSurfaceFormat>, VulkanRuntimeCapabilityError> { + let formats = { + // SAFETY: The physical device and surface are live query inputs and no handles are retained. + unsafe { + surface + .loader + .get_physical_device_surface_formats(device, surface.surface) + } + } + .map_err(|error| VulkanRuntimeCapabilityError::SurfaceFormatsFailed { + device: name.to_string(), + result: error, + })?; + Ok(formats + .into_iter() + .map(|format| VulkanSurfaceFormat { + format: format.format.as_raw(), + color_space: format.color_space.as_raw(), + }) + .collect()) +} + +pub(super) fn live_present_modes( + surface: &VulkanSurfaceProbe, + device: vk::PhysicalDevice, + name: &str, +) -> Result<Vec<i32>, VulkanRuntimeCapabilityError> { + let modes = { + // SAFETY: The physical device and surface are live query inputs and no handles are retained. + unsafe { + surface + .loader + .get_physical_device_surface_present_modes(device, surface.surface) + } + } + .map_err(|error| VulkanRuntimeCapabilityError::PresentModesFailed { + device: name.to_string(), + result: error, + })?; + Ok(modes.into_iter().map(vk::PresentModeKHR::as_raw).collect()) +} + +pub(super) fn live_surface_capabilities( + surface: &VulkanSurfaceProbe, + device: vk::PhysicalDevice, + name: &str, +) -> Result<VulkanSwapchainSurfaceCapabilities, VulkanRuntimeCapabilityError> { + let capabilities = { + // SAFETY: The physical device and surface are live query inputs and no handles are retained. + unsafe { + surface + .loader + .get_physical_device_surface_capabilities(device, surface.surface) + } + } + .map_err( + |error| VulkanRuntimeCapabilityError::SurfaceCapabilitiesFailed { + device: name.to_string(), + result: error, + }, + )?; + Ok(VulkanSwapchainSurfaceCapabilities { + current_extent: if capabilities.current_extent.width == u32::MAX { + None + } else { + Some(( + capabilities.current_extent.width, + capabilities.current_extent.height, + )) + }, + min_extent: ( + capabilities.min_image_extent.width, + capabilities.min_image_extent.height, + ), + max_extent: ( + capabilities.max_image_extent.width, + capabilities.max_image_extent.height, + ), + min_image_count: capabilities.min_image_count, + max_image_count: capabilities.max_image_count, + supported_usage_flags: capabilities.supported_usage_flags.as_raw(), + }) +} diff --git a/adapters/fparkan-render-vulkan/src/ffi/runtime.rs b/adapters/fparkan-render-vulkan/src/ffi/runtime.rs index b1c98df..134bf3e 100644 --- a/adapters/fparkan-render-vulkan/src/ffi/runtime.rs +++ b/adapters/fparkan-render-vulkan/src/ffi/runtime.rs @@ -1,24 +1,13 @@ #![allow(unsafe_code)] use ash::vk; -use std::ffi::{CStr, CString}; +use std::ffi::CString; -use super::{VulkanInstanceProbe, VulkanSurfaceProbe}; -use crate::policy::{ - compare_reports, plan_vulkan_swapchain, validate_device, VulkanCapabilityError, - VulkanCapabilityReport, VulkanDeviceType, VulkanPhysicalDeviceRecord, VulkanQueueFamily, - VulkanSurfaceFormat, VulkanSwapchainError, VulkanSwapchainPlan, VulkanSwapchainRequest, - VulkanSwapchainSurfaceCapabilities, +use super::capabilities::{ + select_live_device_candidate, unique_queue_families, VulkanRuntimeCapabilityError, + VulkanRuntimeCapabilityProbe, }; - -/// Live Vulkan device/surface capability probe. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct VulkanRuntimeCapabilityProbe { - /// Selected device/queue capability report. - pub capability: VulkanCapabilityReport, - /// Swapchain plan built from the selected device and live surface capabilities. - pub swapchain: VulkanSwapchainPlan, -} +use super::{VulkanInstanceProbe, VulkanSurfaceProbe}; /// Created Vulkan logical device probe. pub struct VulkanLogicalDeviceProbe { @@ -86,95 +75,6 @@ pub struct VulkanLogicalDeviceReport { pub enabled_extensions: Vec<String>, } -/// Live Vulkan device/surface capability probe error. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum VulkanRuntimeCapabilityError { - /// Physical device enumeration failed. - EnumerateDevicesFailed { - /// Vulkan result. - result: vk::Result, - }, - /// Device extension enumeration failed. - EnumerateDeviceExtensionsFailed { - /// Device name or index context. - device: String, - /// Vulkan result. - result: vk::Result, - }, - /// Queue-family present support query failed. - PresentSupportFailed { - /// Device name. - device: String, - /// Queue-family index. - queue_family: u32, - /// Vulkan result. - result: vk::Result, - }, - /// Surface format query failed. - SurfaceFormatsFailed { - /// Device name. - device: String, - /// Vulkan result. - result: vk::Result, - }, - /// Surface capability query failed. - SurfaceCapabilitiesFailed { - /// Device name. - device: String, - /// Vulkan result. - result: vk::Result, - }, - /// Present mode query failed. - PresentModesFailed { - /// Device name. - device: String, - /// Vulkan result. - result: vk::Result, - }, - /// No device satisfied Stage 0 capability policy. - Capability(VulkanCapabilityError), - /// Live surface capabilities could not produce a swapchain plan. - Swapchain(VulkanSwapchainError), -} - -impl std::fmt::Display for VulkanRuntimeCapabilityError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::EnumerateDevicesFailed { result } => { - write!(f, "Vulkan physical device enumeration failed: {result:?}") - } - Self::EnumerateDeviceExtensionsFailed { device, result } => write!( - f, - "Vulkan device {device} extension enumeration failed: {result:?}" - ), - Self::PresentSupportFailed { - device, - queue_family, - result, - } => write!( - f, - "Vulkan device {device} queue family {queue_family} present support query failed: {result:?}" - ), - Self::SurfaceFormatsFailed { device, result } => write!( - f, - "Vulkan device {device} surface format query failed: {result:?}" - ), - Self::SurfaceCapabilitiesFailed { device, result } => write!( - f, - "Vulkan device {device} surface capabilities query failed: {result:?}" - ), - Self::PresentModesFailed { device, result } => write!( - f, - "Vulkan device {device} present mode query failed: {result:?}" - ), - Self::Capability(error) => write!(f, "{error}"), - Self::Swapchain(error) => write!(f, "{error}"), - } - } -} - -impl std::error::Error for VulkanRuntimeCapabilityError {} - /// Vulkan logical device creation error. #[derive(Clone, Debug, Eq, PartialEq)] pub enum VulkanLogicalDeviceError { @@ -214,21 +114,6 @@ impl std::fmt::Display for VulkanLogicalDeviceError { impl std::error::Error for VulkanLogicalDeviceError {} -/// Probes live Vulkan device, queue, surface and swapchain capabilities. -/// -/// # Errors -/// -/// Returns [`VulkanRuntimeCapabilityError`] when device enumeration, surface -/// capability queries, Stage 0 device selection, or swapchain planning fails. -pub fn probe_vulkan_runtime_capabilities( - instance: &VulkanInstanceProbe, - surface: &VulkanSurfaceProbe, - drawable_extent: (u32, u32), -) -> Result<VulkanRuntimeCapabilityProbe, VulkanRuntimeCapabilityError> { - let selected = select_live_device_candidate(instance, surface, drawable_extent)?; - Ok(selected.runtime) -} - /// Creates a Vulkan logical device for the selected live surface-capable device. /// /// # Errors @@ -293,289 +178,9 @@ pub fn create_vulkan_logical_device_probe( }) } -struct SelectedLiveDevice { - physical_device: vk::PhysicalDevice, - runtime: VulkanRuntimeCapabilityProbe, -} - -struct LiveDeviceCandidate { - physical_device: vk::PhysicalDevice, - capability: VulkanCapabilityReport, - surface_formats: Vec<VulkanSurfaceFormat>, - present_modes: Vec<i32>, - surface_capabilities: VulkanSwapchainSurfaceCapabilities, -} - -fn select_live_device_candidate( - instance: &VulkanInstanceProbe, - surface: &VulkanSurfaceProbe, - drawable_extent: (u32, u32), -) -> Result<SelectedLiveDevice, VulkanRuntimeCapabilityError> { - let devices = { - // SAFETY: The Vulkan instance is live for this query and no handles are retained. - unsafe { instance.instance.enumerate_physical_devices() }.map_err(|error| { - VulkanRuntimeCapabilityError::EnumerateDevicesFailed { result: error } - })? - }; - let mut best: Option<LiveDeviceCandidate> = None; - let mut last_error = None; - for (index, device) in devices.iter().copied().enumerate() { - let candidate = match live_device_candidate(instance, surface, device, index) { - Ok(candidate) => candidate, - Err(err) => { - last_error = Some(err); - continue; - } - }; - match &best { - Some(existing) - if compare_reports(&candidate.capability, &existing.capability) - != std::cmp::Ordering::Greater => {} - _ => best = Some(candidate), - } - } - let best = best.ok_or_else(|| { - last_error.unwrap_or(VulkanRuntimeCapabilityError::Capability( - VulkanCapabilityError::NoPhysicalDevice, - )) - })?; - let swapchain = plan_vulkan_swapchain(&VulkanSwapchainRequest { - drawable_extent, - formats: best.surface_formats, - present_modes: best.present_modes, - capabilities: best.surface_capabilities, - preferred_present_mode: vk::PresentModeKHR::MAILBOX.as_raw(), - }) - .map_err(VulkanRuntimeCapabilityError::Swapchain)?; - Ok(SelectedLiveDevice { - physical_device: best.physical_device, - runtime: VulkanRuntimeCapabilityProbe { - capability: best.capability, - swapchain, - }, - }) -} - -fn live_device_candidate( - instance: &VulkanInstanceProbe, - surface: &VulkanSurfaceProbe, - device: vk::PhysicalDevice, - index: usize, -) -> Result<LiveDeviceCandidate, VulkanRuntimeCapabilityError> { - let properties = { - // SAFETY: `device` was returned by this live instance and the result is copied by value. - unsafe { instance.instance.get_physical_device_properties(device) } - }; - let name = physical_device_name(&properties, index); - let queue_properties = { - // SAFETY: `device` was returned by this live instance and the result is owned by Rust. - unsafe { - instance - .instance - .get_physical_device_queue_family_properties(device) - } - }; - let extensions = live_device_extensions(instance, device, &name)?; - let surface_formats = live_surface_formats(surface, device, &name)?; - let present_modes = live_present_modes(surface, device, &name)?; - let surface_capabilities = live_surface_capabilities(surface, device, &name)?; - let queue_families = queue_properties - .iter() - .enumerate() - .map(|(queue_index, properties)| { - let index = u32::try_from(queue_index).unwrap_or(u32::MAX); - let present = { - // SAFETY: The physical device, surface and queue-family index are live query inputs. - unsafe { - surface.loader.get_physical_device_surface_support( - device, - index, - surface.surface, - ) - } - } - .map_err(|error| VulkanRuntimeCapabilityError::PresentSupportFailed { - device: name.clone(), - queue_family: index, - result: error, - })?; - Ok(VulkanQueueFamily { - index, - graphics: properties.queue_flags.contains(vk::QueueFlags::GRAPHICS), - present, - }) - }) - .collect::<Result<Vec<_>, VulkanRuntimeCapabilityError>>()?; - let record = VulkanPhysicalDeviceRecord { - name, - api_version: properties.api_version, - device_type: match properties.device_type { - vk::PhysicalDeviceType::DISCRETE_GPU => VulkanDeviceType::DiscreteGpu, - vk::PhysicalDeviceType::INTEGRATED_GPU => VulkanDeviceType::IntegratedGpu, - vk::PhysicalDeviceType::CPU => VulkanDeviceType::Cpu, - _ => VulkanDeviceType::Other, - }, - extensions, - queue_families, - surface_formats: surface_formats.clone(), - present_modes: present_modes.clone(), - surface_capabilities, - }; - let capability = validate_device(&record).map_err(VulkanRuntimeCapabilityError::Capability)?; - Ok(LiveDeviceCandidate { - physical_device: device, - capability, - surface_formats, - present_modes, - surface_capabilities, - }) -} - -pub(super) fn unique_queue_families(graphics: u32, present: u32) -> Vec<u32> { - if graphics == present { - vec![graphics] - } else { - vec![graphics, present] - } -} - fn device_extension_cstrings(values: &[String]) -> Result<Vec<CString>, String> { values .iter() .map(|extension| CString::new(extension.as_str()).map_err(|_| extension.clone())) .collect() } - -fn physical_device_name(properties: &vk::PhysicalDeviceProperties, index: usize) -> String { - // SAFETY: Vulkan device names are fixed-size NUL-terminated C strings per the spec. - let name = unsafe { CStr::from_ptr(properties.device_name.as_ptr()) } - .to_string_lossy() - .trim() - .to_string(); - if name.is_empty() { - format!("physical-device-{index}") - } else { - name - } -} - -fn live_device_extensions( - instance: &VulkanInstanceProbe, - device: vk::PhysicalDevice, - name: &str, -) -> Result<Vec<String>, VulkanRuntimeCapabilityError> { - let properties = { - // SAFETY: `device` was returned by this live instance and no borrowed data escapes. - unsafe { - instance - .instance - .enumerate_device_extension_properties(device) - } - } - .map_err( - |error| VulkanRuntimeCapabilityError::EnumerateDeviceExtensionsFailed { - device: name.to_string(), - result: error, - }, - )?; - let mut extensions = properties - .iter() - .map(|property| { - // SAFETY: Vulkan extension names are fixed-size NUL-terminated C strings per the spec. - unsafe { CStr::from_ptr(property.extension_name.as_ptr()) } - .to_string_lossy() - .into_owned() - }) - .collect::<Vec<_>>(); - extensions.sort(); - extensions.dedup(); - Ok(extensions) -} - -pub(super) fn live_surface_formats( - surface: &VulkanSurfaceProbe, - device: vk::PhysicalDevice, - name: &str, -) -> Result<Vec<VulkanSurfaceFormat>, VulkanRuntimeCapabilityError> { - let formats = { - // SAFETY: The physical device and surface are live query inputs and no handles are retained. - unsafe { - surface - .loader - .get_physical_device_surface_formats(device, surface.surface) - } - } - .map_err(|error| VulkanRuntimeCapabilityError::SurfaceFormatsFailed { - device: name.to_string(), - result: error, - })?; - Ok(formats - .into_iter() - .map(|format| VulkanSurfaceFormat { - format: format.format.as_raw(), - color_space: format.color_space.as_raw(), - }) - .collect()) -} - -pub(super) fn live_present_modes( - surface: &VulkanSurfaceProbe, - device: vk::PhysicalDevice, - name: &str, -) -> Result<Vec<i32>, VulkanRuntimeCapabilityError> { - let modes = { - // SAFETY: The physical device and surface are live query inputs and no handles are retained. - unsafe { - surface - .loader - .get_physical_device_surface_present_modes(device, surface.surface) - } - } - .map_err(|error| VulkanRuntimeCapabilityError::PresentModesFailed { - device: name.to_string(), - result: error, - })?; - Ok(modes.into_iter().map(vk::PresentModeKHR::as_raw).collect()) -} - -pub(super) fn live_surface_capabilities( - surface: &VulkanSurfaceProbe, - device: vk::PhysicalDevice, - name: &str, -) -> Result<VulkanSwapchainSurfaceCapabilities, VulkanRuntimeCapabilityError> { - let capabilities = { - // SAFETY: The physical device and surface are live query inputs and no handles are retained. - unsafe { - surface - .loader - .get_physical_device_surface_capabilities(device, surface.surface) - } - } - .map_err( - |error| VulkanRuntimeCapabilityError::SurfaceCapabilitiesFailed { - device: name.to_string(), - result: error, - }, - )?; - Ok(VulkanSwapchainSurfaceCapabilities { - current_extent: if capabilities.current_extent.width == u32::MAX { - None - } else { - Some(( - capabilities.current_extent.width, - capabilities.current_extent.height, - )) - }, - min_extent: ( - capabilities.min_image_extent.width, - capabilities.min_image_extent.height, - ), - max_extent: ( - capabilities.max_image_extent.width, - capabilities.max_image_extent.height, - ), - min_image_count: capabilities.min_image_count, - max_image_count: capabilities.max_image_count, - supported_usage_flags: capabilities.supported_usage_flags.as_raw(), - }) -} diff --git a/adapters/fparkan-render-vulkan/src/ffi/swapchain.rs b/adapters/fparkan-render-vulkan/src/ffi/swapchain.rs index adfca60..ca754d4 100644 --- a/adapters/fparkan-render-vulkan/src/ffi/swapchain.rs +++ b/adapters/fparkan-render-vulkan/src/ffi/swapchain.rs @@ -3,7 +3,7 @@ use ash::{khr::swapchain, vk}; use super::{ - runtime::{ + capabilities::{ live_present_modes, live_surface_capabilities, live_surface_formats, unique_queue_families, }, VulkanInstanceProbe, VulkanLogicalDeviceProbe, VulkanRuntimeCapabilityError, diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 484e9c5..601cc3b 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1242,6 +1242,7 @@ fn has_safety_comment(line: &str) -> bool { const AUDITED_UNSAFE_SOURCE_FILES: &[&str] = &[ "adapters/fparkan-render-vulkan/src/ffi.rs", "adapters/fparkan-render-vulkan/src/ffi/instance.rs", + "adapters/fparkan-render-vulkan/src/ffi/capabilities.rs", "adapters/fparkan-render-vulkan/src/ffi/resources.rs", "adapters/fparkan-render-vulkan/src/ffi/runtime.rs", "adapters/fparkan-render-vulkan/src/ffi/smoke.rs", |
