refactor(vulkan-ffi): extract swapchain probe module

This commit is contained in:
2026-06-25 07:07:31 +04:00
parent f91378b884
commit 3c32215665
4 changed files with 237 additions and 218 deletions
+7 -4
View File
@@ -33,6 +33,7 @@ mod runtime;
mod smoke; mod smoke;
mod smoke_types; mod smoke_types;
mod surface; mod surface;
mod swapchain;
mod swapchain_resources; mod swapchain_resources;
mod validation; mod validation;
@@ -50,11 +51,9 @@ use self::resources::{
VulkanFrameSync, VulkanFrameSync,
}; };
pub use self::runtime::{ pub use self::runtime::{
create_vulkan_logical_device_probe, create_vulkan_swapchain_probe, create_vulkan_logical_device_probe, probe_vulkan_runtime_capabilities,
create_vulkan_swapchain_probe_for_extent, probe_vulkan_runtime_capabilities,
VulkanLogicalDeviceError, VulkanLogicalDeviceProbe, VulkanLogicalDeviceReport, VulkanLogicalDeviceError, VulkanLogicalDeviceProbe, VulkanLogicalDeviceReport,
VulkanRuntimeCapabilityError, VulkanRuntimeCapabilityProbe, VulkanSwapchainProbe, VulkanRuntimeCapabilityError, VulkanRuntimeCapabilityProbe,
VulkanSwapchainProbeError, VulkanSwapchainReport,
}; };
pub use self::smoke_types::{ pub use self::smoke_types::{
VulkanSmokeFrameOutcome, VulkanSmokeRenderer, VulkanSmokeRendererCreateInfo, VulkanSmokeFrameOutcome, VulkanSmokeRenderer, VulkanSmokeRendererCreateInfo,
@@ -66,6 +65,10 @@ pub use self::surface::{
create_vulkan_surface_probe, plan_vulkan_surface, render_surface_plan_json, VulkanSurfaceError, create_vulkan_surface_probe, plan_vulkan_surface, render_surface_plan_json, VulkanSurfaceError,
VulkanSurfacePlan, VulkanSurfaceProbe, VulkanSurfacePlan, VulkanSurfaceProbe,
}; };
pub use self::swapchain::{
create_vulkan_swapchain_probe, create_vulkan_swapchain_probe_for_extent, VulkanSwapchainProbe,
VulkanSwapchainProbeError, VulkanSwapchainReport,
};
use self::swapchain_resources::{ use self::swapchain_resources::{
create_swapchain_resources, destroy_swapchain_resources, VulkanSwapchainResources, create_swapchain_resources, destroy_swapchain_resources, VulkanSwapchainResources,
}; };
@@ -1,14 +1,14 @@
#![allow(unsafe_code)] #![allow(unsafe_code)]
use ash::{khr::swapchain, vk}; use ash::vk;
use std::ffi::{CStr, CString}; use std::ffi::{CStr, CString};
use super::{VulkanInstanceProbe, VulkanSurfaceProbe}; use super::{VulkanInstanceProbe, VulkanSurfaceProbe};
use crate::policy::{ use crate::policy::{
compare_reports, plan_vulkan_swapchain, select_composite_alpha, validate_device, compare_reports, plan_vulkan_swapchain, validate_device, VulkanCapabilityError,
VulkanCapabilityError, VulkanCapabilityReport, VulkanDeviceType, VulkanPhysicalDeviceRecord, VulkanCapabilityReport, VulkanDeviceType, VulkanPhysicalDeviceRecord, VulkanQueueFamily,
VulkanQueueFamily, VulkanSurfaceFormat, VulkanSwapchainError, VulkanSwapchainPlan, VulkanSurfaceFormat, VulkanSwapchainError, VulkanSwapchainPlan, VulkanSwapchainRequest,
VulkanSwapchainRequest, VulkanSwapchainSurfaceCapabilities, VulkanSwapchainSurfaceCapabilities,
}; };
/// Live Vulkan device/surface capability probe. /// Live Vulkan device/surface capability probe.
@@ -86,46 +86,6 @@ pub struct VulkanLogicalDeviceReport {
pub enabled_extensions: Vec<String>, pub enabled_extensions: Vec<String>,
} }
/// Created Vulkan swapchain probe.
pub struct VulkanSwapchainProbe {
loader: swapchain::Device,
swapchain: vk::SwapchainKHR,
/// Deterministic swapchain creation report.
pub report: VulkanSwapchainReport,
}
impl Drop for VulkanSwapchainProbe {
fn drop(&mut self) {
// SAFETY: The swapchain was created by this probe and is destroyed once during drop.
unsafe { self.loader.destroy_swapchain(self.swapchain, None) };
}
}
impl VulkanSwapchainProbe {
/// Returns the live swapchain handle.
#[must_use]
pub fn swapchain(&self) -> vk::SwapchainKHR {
self.swapchain
}
/// Returns the swapchain extension loader for this live swapchain.
#[must_use]
pub fn loader(&self) -> &swapchain::Device {
&self.loader
}
}
/// Runtime swapchain creation report.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct VulkanSwapchainReport {
/// Report schema version.
pub schema: u32,
/// Deterministic swapchain policy used for creation.
pub plan: VulkanSwapchainPlan,
/// Number of images returned by `vkGetSwapchainImagesKHR`.
pub image_count: u32,
}
/// Live Vulkan device/surface capability probe error. /// Live Vulkan device/surface capability probe error.
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub enum VulkanRuntimeCapabilityError { pub enum VulkanRuntimeCapabilityError {
@@ -254,50 +214,6 @@ impl std::fmt::Display for VulkanLogicalDeviceError {
impl std::error::Error for VulkanLogicalDeviceError {} impl std::error::Error for VulkanLogicalDeviceError {}
/// Vulkan swapchain creation error.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum VulkanSwapchainProbeError {
/// Live runtime capability probing failed before swapchain creation.
Runtime(VulkanRuntimeCapabilityError),
/// Deterministic swapchain planning failed before create.
Plan(VulkanSwapchainError),
/// Surface capability query failed.
SurfaceCapabilitiesFailed {
/// Vulkan result.
result: vk::Result,
},
/// Swapchain creation failed.
CreateFailed {
/// Vulkan result.
result: vk::Result,
},
/// Swapchain image query failed.
ImagesFailed {
/// Vulkan result.
result: vk::Result,
},
}
impl std::fmt::Display for VulkanSwapchainProbeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Runtime(error) => write!(f, "{error}"),
Self::Plan(error) => write!(f, "{error}"),
Self::SurfaceCapabilitiesFailed { result } => {
write!(f, "Vulkan surface capabilities query failed: {result:?}")
}
Self::CreateFailed { result } => {
write!(f, "Vulkan swapchain creation failed: {result:?}")
}
Self::ImagesFailed { result } => {
write!(f, "Vulkan swapchain image query failed: {result:?}")
}
}
}
}
impl std::error::Error for VulkanSwapchainProbeError {}
/// Probes live Vulkan device, queue, surface and swapchain capabilities. /// Probes live Vulkan device, queue, surface and swapchain capabilities.
/// ///
/// # Errors /// # Errors
@@ -377,127 +293,6 @@ pub fn create_vulkan_logical_device_probe(
}) })
} }
/// Creates a Vulkan swapchain for the live logical device and surface.
///
/// # Errors
///
/// Returns [`VulkanSwapchainProbeError`] when live surface capability queries,
/// swapchain creation, or swapchain image enumeration fails.
pub fn create_vulkan_swapchain_probe(
instance: &VulkanInstanceProbe,
surface: &VulkanSurfaceProbe,
device: &VulkanLogicalDeviceProbe,
) -> Result<VulkanSwapchainProbe, VulkanSwapchainProbeError> {
create_vulkan_swapchain_probe_for_extent(
instance,
surface,
device,
device.runtime.swapchain.extent,
vk::SwapchainKHR::null(),
)
}
/// Creates a Vulkan swapchain for the live logical device and surface at a specific extent.
///
/// # Errors
///
/// Returns [`VulkanSwapchainProbeError`] when live surface capability queries,
/// swapchain creation, or swapchain image enumeration fails.
pub fn create_vulkan_swapchain_probe_for_extent(
instance: &VulkanInstanceProbe,
surface: &VulkanSurfaceProbe,
device: &VulkanLogicalDeviceProbe,
drawable_extent: (u32, u32),
old_swapchain: vk::SwapchainKHR,
) -> Result<VulkanSwapchainProbe, VulkanSwapchainProbeError> {
let raw_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.physical_device(), surface.surface)
}
}
.map_err(|error| VulkanSwapchainProbeError::SurfaceCapabilitiesFailed { result: error })?;
let surface_formats = live_surface_formats(
surface,
device.physical_device(),
&device.report.device_name,
)
.map_err(VulkanSwapchainProbeError::Runtime)?;
let present_modes = live_present_modes(
surface,
device.physical_device(),
&device.report.device_name,
)
.map_err(VulkanSwapchainProbeError::Runtime)?;
let capabilities = live_surface_capabilities(
surface,
device.physical_device(),
&device.report.device_name,
)
.map_err(VulkanSwapchainProbeError::Runtime)?;
let plan = plan_vulkan_swapchain(&VulkanSwapchainRequest {
drawable_extent,
formats: surface_formats,
present_modes,
capabilities,
preferred_present_mode: vk::PresentModeKHR::MAILBOX.as_raw(),
})
.map_err(VulkanSwapchainProbeError::Plan)?;
let queue_family_indices = unique_queue_families(
device.runtime.capability.graphics_queue_family,
device.runtime.capability.present_queue_family,
);
let sharing_mode = if queue_family_indices.len() > 1 {
vk::SharingMode::CONCURRENT
} else {
vk::SharingMode::EXCLUSIVE
};
let create_info = vk::SwapchainCreateInfoKHR::default()
.surface(surface.surface)
.min_image_count(plan.image_count)
.image_format(vk::Format::from_raw(plan.format.format))
.image_color_space(vk::ColorSpaceKHR::from_raw(plan.format.color_space))
.image_extent(vk::Extent2D {
width: plan.extent.0,
height: plan.extent.1,
})
.image_array_layers(1)
.image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT)
.image_sharing_mode(sharing_mode)
.queue_family_indices(&queue_family_indices)
.pre_transform(raw_capabilities.current_transform)
.composite_alpha(select_composite_alpha(
raw_capabilities.supported_composite_alpha,
))
.present_mode(vk::PresentModeKHR::from_raw(plan.present_mode))
.old_swapchain(old_swapchain)
.clipped(true);
let loader = swapchain::Device::new(&instance.instance, device.device());
// SAFETY: The create info references live instance/device/surface handles for this call.
let swapchain = unsafe { loader.create_swapchain(&create_info, None) }
.map_err(|error| VulkanSwapchainProbeError::CreateFailed { result: error })?;
// SAFETY: The swapchain was created above and the returned image handles are owned by it.
let images = match unsafe { loader.get_swapchain_images(swapchain) } {
Ok(images) => images,
Err(error) => {
// SAFETY: The swapchain was created above on this loader/device pair and is destroyed on setup failure.
unsafe { loader.destroy_swapchain(swapchain, None) };
return Err(VulkanSwapchainProbeError::ImagesFailed { result: error });
}
};
Ok(VulkanSwapchainProbe {
loader,
swapchain,
report: VulkanSwapchainReport {
schema: 1,
plan,
image_count: images.len().try_into().unwrap_or(u32::MAX),
},
})
}
struct SelectedLiveDevice { struct SelectedLiveDevice {
physical_device: vk::PhysicalDevice, physical_device: vk::PhysicalDevice,
runtime: VulkanRuntimeCapabilityProbe, runtime: VulkanRuntimeCapabilityProbe,
@@ -636,7 +431,7 @@ fn live_device_candidate(
}) })
} }
fn unique_queue_families(graphics: u32, present: u32) -> Vec<u32> { pub(super) fn unique_queue_families(graphics: u32, present: u32) -> Vec<u32> {
if graphics == present { if graphics == present {
vec![graphics] vec![graphics]
} else { } else {
@@ -697,7 +492,7 @@ fn live_device_extensions(
Ok(extensions) Ok(extensions)
} }
fn live_surface_formats( pub(super) fn live_surface_formats(
surface: &VulkanSurfaceProbe, surface: &VulkanSurfaceProbe,
device: vk::PhysicalDevice, device: vk::PhysicalDevice,
name: &str, name: &str,
@@ -723,7 +518,7 @@ fn live_surface_formats(
.collect()) .collect())
} }
fn live_present_modes( pub(super) fn live_present_modes(
surface: &VulkanSurfaceProbe, surface: &VulkanSurfaceProbe,
device: vk::PhysicalDevice, device: vk::PhysicalDevice,
name: &str, name: &str,
@@ -743,7 +538,7 @@ fn live_present_modes(
Ok(modes.into_iter().map(vk::PresentModeKHR::as_raw).collect()) Ok(modes.into_iter().map(vk::PresentModeKHR::as_raw).collect())
} }
fn live_surface_capabilities( pub(super) fn live_surface_capabilities(
surface: &VulkanSurfaceProbe, surface: &VulkanSurfaceProbe,
device: vk::PhysicalDevice, device: vk::PhysicalDevice,
name: &str, name: &str,
@@ -0,0 +1,220 @@
#![allow(unsafe_code)]
use ash::{khr::swapchain, vk};
use super::{
runtime::{
live_present_modes, live_surface_capabilities, live_surface_formats, unique_queue_families,
},
VulkanInstanceProbe, VulkanLogicalDeviceProbe, VulkanRuntimeCapabilityError,
VulkanSurfaceProbe,
};
use crate::policy::{
plan_vulkan_swapchain, select_composite_alpha, VulkanSwapchainError, VulkanSwapchainPlan,
VulkanSwapchainRequest,
};
/// Created Vulkan swapchain probe.
pub struct VulkanSwapchainProbe {
loader: swapchain::Device,
swapchain: vk::SwapchainKHR,
/// Deterministic swapchain creation report.
pub report: VulkanSwapchainReport,
}
impl Drop for VulkanSwapchainProbe {
fn drop(&mut self) {
// SAFETY: The swapchain was created by this probe and is destroyed once during drop.
unsafe { self.loader.destroy_swapchain(self.swapchain, None) };
}
}
impl VulkanSwapchainProbe {
/// Returns the live swapchain handle.
#[must_use]
pub fn swapchain(&self) -> vk::SwapchainKHR {
self.swapchain
}
/// Returns the swapchain extension loader for this live swapchain.
#[must_use]
pub fn loader(&self) -> &swapchain::Device {
&self.loader
}
}
/// Runtime swapchain creation report.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct VulkanSwapchainReport {
/// Report schema version.
pub schema: u32,
/// Deterministic swapchain policy used for creation.
pub plan: VulkanSwapchainPlan,
/// Number of images returned by `vkGetSwapchainImagesKHR`.
pub image_count: u32,
}
/// Vulkan swapchain creation error.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum VulkanSwapchainProbeError {
/// Live runtime capability probing failed before swapchain creation.
Runtime(VulkanRuntimeCapabilityError),
/// Deterministic swapchain planning failed before create.
Plan(VulkanSwapchainError),
/// Surface capability query failed.
SurfaceCapabilitiesFailed {
/// Vulkan result.
result: vk::Result,
},
/// Swapchain creation failed.
CreateFailed {
/// Vulkan result.
result: vk::Result,
},
/// Swapchain image query failed.
ImagesFailed {
/// Vulkan result.
result: vk::Result,
},
}
impl std::fmt::Display for VulkanSwapchainProbeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Runtime(error) => write!(f, "{error}"),
Self::Plan(error) => write!(f, "{error}"),
Self::SurfaceCapabilitiesFailed { result } => {
write!(f, "Vulkan surface capabilities query failed: {result:?}")
}
Self::CreateFailed { result } => {
write!(f, "Vulkan swapchain creation failed: {result:?}")
}
Self::ImagesFailed { result } => {
write!(f, "Vulkan swapchain image query failed: {result:?}")
}
}
}
}
impl std::error::Error for VulkanSwapchainProbeError {}
/// Creates a Vulkan swapchain for the live logical device and surface.
///
/// # Errors
///
/// Returns [`VulkanSwapchainProbeError`] when live surface capability queries,
/// swapchain creation, or swapchain image enumeration fails.
pub fn create_vulkan_swapchain_probe(
instance: &VulkanInstanceProbe,
surface: &VulkanSurfaceProbe,
device: &VulkanLogicalDeviceProbe,
) -> Result<VulkanSwapchainProbe, VulkanSwapchainProbeError> {
create_vulkan_swapchain_probe_for_extent(
instance,
surface,
device,
device.runtime.swapchain.extent,
vk::SwapchainKHR::null(),
)
}
/// Creates a Vulkan swapchain for the live logical device and surface at a specific extent.
///
/// # Errors
///
/// Returns [`VulkanSwapchainProbeError`] when live surface capability queries,
/// swapchain creation, or swapchain image enumeration fails.
pub fn create_vulkan_swapchain_probe_for_extent(
instance: &VulkanInstanceProbe,
surface: &VulkanSurfaceProbe,
device: &VulkanLogicalDeviceProbe,
drawable_extent: (u32, u32),
old_swapchain: vk::SwapchainKHR,
) -> Result<VulkanSwapchainProbe, VulkanSwapchainProbeError> {
let raw_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.physical_device(), surface.surface)
}
}
.map_err(|error| VulkanSwapchainProbeError::SurfaceCapabilitiesFailed { result: error })?;
let surface_formats = live_surface_formats(
surface,
device.physical_device(),
&device.report.device_name,
)
.map_err(VulkanSwapchainProbeError::Runtime)?;
let present_modes = live_present_modes(
surface,
device.physical_device(),
&device.report.device_name,
)
.map_err(VulkanSwapchainProbeError::Runtime)?;
let capabilities = live_surface_capabilities(
surface,
device.physical_device(),
&device.report.device_name,
)
.map_err(VulkanSwapchainProbeError::Runtime)?;
let plan = plan_vulkan_swapchain(&VulkanSwapchainRequest {
drawable_extent,
formats: surface_formats,
present_modes,
capabilities,
preferred_present_mode: vk::PresentModeKHR::MAILBOX.as_raw(),
})
.map_err(VulkanSwapchainProbeError::Plan)?;
let queue_family_indices = unique_queue_families(
device.runtime.capability.graphics_queue_family,
device.runtime.capability.present_queue_family,
);
let sharing_mode = if queue_family_indices.len() > 1 {
vk::SharingMode::CONCURRENT
} else {
vk::SharingMode::EXCLUSIVE
};
let create_info = vk::SwapchainCreateInfoKHR::default()
.surface(surface.surface)
.min_image_count(plan.image_count)
.image_format(vk::Format::from_raw(plan.format.format))
.image_color_space(vk::ColorSpaceKHR::from_raw(plan.format.color_space))
.image_extent(vk::Extent2D {
width: plan.extent.0,
height: plan.extent.1,
})
.image_array_layers(1)
.image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT)
.image_sharing_mode(sharing_mode)
.queue_family_indices(&queue_family_indices)
.pre_transform(raw_capabilities.current_transform)
.composite_alpha(select_composite_alpha(
raw_capabilities.supported_composite_alpha,
))
.present_mode(vk::PresentModeKHR::from_raw(plan.present_mode))
.old_swapchain(old_swapchain)
.clipped(true);
let loader = swapchain::Device::new(&instance.instance, device.device());
// SAFETY: The create info references live instance/device/surface handles for this call.
let swapchain = unsafe { loader.create_swapchain(&create_info, None) }
.map_err(|error| VulkanSwapchainProbeError::CreateFailed { result: error })?;
// SAFETY: The swapchain was created above and the returned image handles are owned by it.
let images = match unsafe { loader.get_swapchain_images(swapchain) } {
Ok(images) => images,
Err(error) => {
// SAFETY: The swapchain was created above on this loader/device pair and is destroyed on setup failure.
unsafe { loader.destroy_swapchain(swapchain, None) };
return Err(VulkanSwapchainProbeError::ImagesFailed { result: error });
}
};
Ok(VulkanSwapchainProbe {
loader,
swapchain,
report: VulkanSwapchainReport {
schema: 1,
plan,
image_count: images.len().try_into().unwrap_or(u32::MAX),
},
})
}
+1
View File
@@ -1245,6 +1245,7 @@ const AUDITED_UNSAFE_SOURCE_FILES: &[&str] = &[
"adapters/fparkan-render-vulkan/src/ffi/resources.rs", "adapters/fparkan-render-vulkan/src/ffi/resources.rs",
"adapters/fparkan-render-vulkan/src/ffi/runtime.rs", "adapters/fparkan-render-vulkan/src/ffi/runtime.rs",
"adapters/fparkan-render-vulkan/src/ffi/smoke.rs", "adapters/fparkan-render-vulkan/src/ffi/smoke.rs",
"adapters/fparkan-render-vulkan/src/ffi/swapchain.rs",
"adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs", "adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs",
"adapters/fparkan-render-vulkan/src/ffi/surface.rs", "adapters/fparkan-render-vulkan/src/ffi/surface.rs",
"adapters/fparkan-render-vulkan/src/ffi/validation.rs", "adapters/fparkan-render-vulkan/src/ffi/validation.rs",