diff options
| author | Valentin Popov <valentin@popov.link> | 2026-06-25 05:16:12 +0300 |
|---|---|---|
| committer | Valentin Popov <valentin@popov.link> | 2026-06-25 10:45:36 +0300 |
| commit | dcd5417af247aa07d7c61e9cbd9074cc97122274 (patch) | |
| tree | fcf79ebf810f23ded5a9ac5e2586702101ebe910 | |
| parent | b6b47ae6f612d365e77bbaf421b7c0927df12835 (diff) | |
| download | fparkan-dcd5417af247aa07d7c61e9cbd9074cc97122274.tar.xz fparkan-dcd5417af247aa07d7c61e9cbd9074cc97122274.zip | |
refactor(vulkan-ffi): extract validation messenger module
| -rw-r--r-- | adapters/fparkan-render-vulkan/src/ffi.rs | 119 | ||||
| -rw-r--r-- | adapters/fparkan-render-vulkan/src/ffi/validation.rs | 125 | ||||
| -rw-r--r-- | xtask/src/main.rs | 1 |
3 files changed, 128 insertions, 117 deletions
diff --git a/adapters/fparkan-render-vulkan/src/ffi.rs b/adapters/fparkan-render-vulkan/src/ffi.rs index 9be61eb..618b53b 100644 --- a/adapters/fparkan-render-vulkan/src/ffi.rs +++ b/adapters/fparkan-render-vulkan/src/ffi.rs @@ -29,6 +29,7 @@ mod instance; mod surface; +mod validation; pub use self::instance::{ create_vulkan_instance_probe, plan_vulkan_instance, probe_vulkan_loader, @@ -44,16 +45,14 @@ pub use self::surface::{ create_vulkan_surface_probe, plan_vulkan_surface, render_surface_plan_json, VulkanSurfaceError, VulkanSurfacePlan, VulkanSurfaceProbe, }; +use self::validation::{create_validation_messenger, VulkanValidationMessenger}; use crate::policy::*; use crate::shader_manifest::{ triangle_shader_manifest, validate_shader_manifest, VulkanShaderManifestError, }; use ash::{khr::swapchain, vk}; use fparkan_platform::NativeWindowHandles; -use std::collections::BTreeSet; use std::ffi::{CStr, CString}; -use std::sync::atomic::{AtomicU32, Ordering}; -use std::sync::Mutex; /// Minimum Vulkan API version accepted by the Stage 0 backend. pub const MIN_VULKAN_API_VERSION: u32 = vk::API_VERSION_1_1; const KHR_PORTABILITY_ENUMERATION_EXTENSION: &str = "VK_KHR_portability_enumeration"; @@ -653,88 +652,6 @@ impl std::fmt::Display for VulkanSmokeRendererError { impl std::error::Error for VulkanSmokeRendererError {} -struct VulkanValidationShared { - warning_count: AtomicU32, - error_count: AtomicU32, - vuids: Mutex<BTreeSet<String>>, -} - -impl Default for VulkanValidationShared { - fn default() -> Self { - Self { - warning_count: AtomicU32::new(0), - error_count: AtomicU32::new(0), - vuids: Mutex::new(BTreeSet::new()), - } - } -} - -struct VulkanValidationMessenger { - loader: ash::ext::debug_utils::Instance, - messenger: vk::DebugUtilsMessengerEXT, - shared: Box<VulkanValidationShared>, -} - -impl VulkanValidationMessenger { - fn report(&self) -> VulkanValidationReport { - let vuids = self - .shared - .vuids - .lock() - .map(|values| values.iter().cloned().collect::<Vec<_>>()) - .unwrap_or_default(); - VulkanValidationReport { - warning_count: self.shared.warning_count.load(Ordering::Relaxed), - error_count: self.shared.error_count.load(Ordering::Relaxed), - vuids, - } - } -} - -impl Drop for VulkanValidationMessenger { - fn drop(&mut self) { - // SAFETY: The messenger belongs to this instance-level loader and is destroyed once. - unsafe { - self.loader - .destroy_debug_utils_messenger(self.messenger, None); - }; - } -} - -unsafe extern "system" fn vulkan_validation_callback( - message_severity: vk::DebugUtilsMessageSeverityFlagsEXT, - _message_types: vk::DebugUtilsMessageTypeFlagsEXT, - callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT<'_>, - user_data: *mut std::ffi::c_void, -) -> vk::Bool32 { - // SAFETY: The debug messenger stores a stable pointer to `VulkanValidationShared` for the messenger lifetime. - let Some(shared) = (unsafe { (user_data as *const VulkanValidationShared).as_ref() }) else { - return vk::FALSE; - }; - if message_severity.contains(vk::DebugUtilsMessageSeverityFlagsEXT::ERROR) { - shared.error_count.fetch_add(1, Ordering::Relaxed); - } else if message_severity.contains(vk::DebugUtilsMessageSeverityFlagsEXT::WARNING) { - shared.warning_count.fetch_add(1, Ordering::Relaxed); - } - // SAFETY: Vulkan invokes the callback with either a null pointer or a valid callback-data payload. - let Some(callback_data) = (unsafe { callback_data.as_ref() }) else { - return vk::FALSE; - }; - if let Some(vuid) = (!callback_data.p_message_id_name.is_null()).then(|| { - // SAFETY: `p_message_id_name` is a Vulkan-owned NUL-terminated string for the callback duration. - unsafe { CStr::from_ptr(callback_data.p_message_id_name) } - .to_string_lossy() - .into_owned() - }) { - if vuid.starts_with("VUID-") { - if let Ok(mut vuids) = shared.vuids.lock() { - vuids.insert(vuid); - } - } - } - vk::FALSE -} - struct VulkanAllocatedBuffer { buffer: vk::Buffer, memory: vk::DeviceMemory, @@ -1379,38 +1296,6 @@ impl Drop for VulkanSmokeRenderer { } } -fn create_validation_messenger( - instance: &VulkanInstanceProbe, -) -> Result<VulkanValidationMessenger, VulkanSmokeRendererError> { - let shared = Box::new(VulkanValidationShared::default()); - let loader = ash::ext::debug_utils::Instance::new(&instance.entry, &instance.instance); - let create_info = vk::DebugUtilsMessengerCreateInfoEXT::default() - .message_severity( - vk::DebugUtilsMessageSeverityFlagsEXT::WARNING - | vk::DebugUtilsMessageSeverityFlagsEXT::ERROR, - ) - .message_type( - vk::DebugUtilsMessageTypeFlagsEXT::GENERAL - | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION - | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE, - ) - .pfn_user_callback(Some(vulkan_validation_callback)) - .user_data((&raw const *shared).cast_mut().cast()); - let messenger = - // SAFETY: The create info points at a stable boxed user-data allocation for the messenger lifetime. - unsafe { loader.create_debug_utils_messenger(&create_info, None) }.map_err(|error| { - VulkanSmokeRendererError::VulkanOperation { - context: "vkCreateDebugUtilsMessengerEXT", - result: error, - } - })?; - Ok(VulkanValidationMessenger { - loader, - messenger, - shared, - }) -} - fn create_command_pool( device: &VulkanLogicalDeviceProbe, ) -> Result<vk::CommandPool, VulkanSmokeRendererError> { diff --git a/adapters/fparkan-render-vulkan/src/ffi/validation.rs b/adapters/fparkan-render-vulkan/src/ffi/validation.rs new file mode 100644 index 0000000..2045373 --- /dev/null +++ b/adapters/fparkan-render-vulkan/src/ffi/validation.rs @@ -0,0 +1,125 @@ +#![allow(unsafe_code)] + +use ash::vk; +use std::collections::BTreeSet; +use std::ffi::CStr; +use std::sync::{ + atomic::{AtomicU32, Ordering}, + Mutex, +}; + +use super::{VulkanInstanceProbe, VulkanSmokeRendererError, VulkanValidationReport}; + +struct VulkanValidationShared { + warning_count: AtomicU32, + error_count: AtomicU32, + vuids: Mutex<BTreeSet<String>>, +} + +impl Default for VulkanValidationShared { + fn default() -> Self { + Self { + warning_count: AtomicU32::new(0), + error_count: AtomicU32::new(0), + vuids: Mutex::new(BTreeSet::new()), + } + } +} + +pub(super) struct VulkanValidationMessenger { + loader: ash::ext::debug_utils::Instance, + messenger: vk::DebugUtilsMessengerEXT, + shared: Box<VulkanValidationShared>, +} + +impl VulkanValidationMessenger { + pub(super) fn report(&self) -> VulkanValidationReport { + let vuids = self + .shared + .vuids + .lock() + .map(|values| values.iter().cloned().collect::<Vec<_>>()) + .unwrap_or_default(); + VulkanValidationReport { + warning_count: self.shared.warning_count.load(Ordering::Relaxed), + error_count: self.shared.error_count.load(Ordering::Relaxed), + vuids, + } + } +} + +impl Drop for VulkanValidationMessenger { + fn drop(&mut self) { + // SAFETY: The messenger belongs to this instance-level loader and is destroyed once. + unsafe { + self.loader + .destroy_debug_utils_messenger(self.messenger, None); + }; + } +} + +unsafe extern "system" fn vulkan_validation_callback( + message_severity: vk::DebugUtilsMessageSeverityFlagsEXT, + _message_types: vk::DebugUtilsMessageTypeFlagsEXT, + callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT<'_>, + user_data: *mut std::ffi::c_void, +) -> vk::Bool32 { + // SAFETY: The debug messenger stores a stable pointer to `VulkanValidationShared` for the messenger lifetime. + let Some(shared) = (unsafe { (user_data as *const VulkanValidationShared).as_ref() }) else { + return vk::FALSE; + }; + if message_severity.contains(vk::DebugUtilsMessageSeverityFlagsEXT::ERROR) { + shared.error_count.fetch_add(1, Ordering::Relaxed); + } else if message_severity.contains(vk::DebugUtilsMessageSeverityFlagsEXT::WARNING) { + shared.warning_count.fetch_add(1, Ordering::Relaxed); + } + // SAFETY: Vulkan invokes the callback with either a null pointer or a valid callback-data payload. + let Some(callback_data) = (unsafe { callback_data.as_ref() }) else { + return vk::FALSE; + }; + if let Some(vuid) = (!callback_data.p_message_id_name.is_null()).then(|| { + // SAFETY: `p_message_id_name` is a Vulkan-owned NUL-terminated string for the callback duration. + unsafe { CStr::from_ptr(callback_data.p_message_id_name) } + .to_string_lossy() + .into_owned() + }) { + if vuid.starts_with("VUID-") { + if let Ok(mut vuids) = shared.vuids.lock() { + vuids.insert(vuid); + } + } + } + vk::FALSE +} + +pub(super) fn create_validation_messenger( + instance: &VulkanInstanceProbe, +) -> Result<VulkanValidationMessenger, VulkanSmokeRendererError> { + let shared = Box::new(VulkanValidationShared::default()); + let loader = ash::ext::debug_utils::Instance::new(&instance.entry, &instance.instance); + let create_info = vk::DebugUtilsMessengerCreateInfoEXT::default() + .message_severity( + vk::DebugUtilsMessageSeverityFlagsEXT::WARNING + | vk::DebugUtilsMessageSeverityFlagsEXT::ERROR, + ) + .message_type( + vk::DebugUtilsMessageTypeFlagsEXT::GENERAL + | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION + | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE, + ) + .pfn_user_callback(Some(vulkan_validation_callback)) + .user_data((&raw const *shared).cast_mut().cast()); + let messenger = + // SAFETY: The create info points at a stable boxed user-data allocation for the messenger lifetime. + unsafe { loader.create_debug_utils_messenger(&create_info, None) }.map_err(|error| { + VulkanSmokeRendererError::VulkanOperation { + context: "vkCreateDebugUtilsMessengerEXT", + result: error, + } + })?; + Ok(VulkanValidationMessenger { + loader, + messenger, + shared, + }) +} diff --git a/xtask/src/main.rs b/xtask/src/main.rs index bf79ec7..fd98c82 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1243,6 +1243,7 @@ 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/surface.rs", + "adapters/fparkan-render-vulkan/src/ffi/validation.rs", ]; fn is_audited_unsafe_source(path: &Path) -> bool { |
