refactor(vulkan-ffi): extract validation messenger module
This commit is contained in:
@@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
mod instance;
|
mod instance;
|
||||||
mod surface;
|
mod surface;
|
||||||
|
mod validation;
|
||||||
|
|
||||||
pub use self::instance::{
|
pub use self::instance::{
|
||||||
create_vulkan_instance_probe, plan_vulkan_instance, probe_vulkan_loader,
|
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,
|
create_vulkan_surface_probe, plan_vulkan_surface, render_surface_plan_json, VulkanSurfaceError,
|
||||||
VulkanSurfacePlan, VulkanSurfaceProbe,
|
VulkanSurfacePlan, VulkanSurfaceProbe,
|
||||||
};
|
};
|
||||||
|
use self::validation::{create_validation_messenger, VulkanValidationMessenger};
|
||||||
use crate::policy::*;
|
use crate::policy::*;
|
||||||
use crate::shader_manifest::{
|
use crate::shader_manifest::{
|
||||||
triangle_shader_manifest, validate_shader_manifest, VulkanShaderManifestError,
|
triangle_shader_manifest, validate_shader_manifest, VulkanShaderManifestError,
|
||||||
};
|
};
|
||||||
use ash::{khr::swapchain, vk};
|
use ash::{khr::swapchain, vk};
|
||||||
use fparkan_platform::NativeWindowHandles;
|
use fparkan_platform::NativeWindowHandles;
|
||||||
use std::collections::BTreeSet;
|
|
||||||
use std::ffi::{CStr, CString};
|
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.
|
/// Minimum Vulkan API version accepted by the Stage 0 backend.
|
||||||
pub const MIN_VULKAN_API_VERSION: u32 = vk::API_VERSION_1_1;
|
pub const MIN_VULKAN_API_VERSION: u32 = vk::API_VERSION_1_1;
|
||||||
const KHR_PORTABILITY_ENUMERATION_EXTENSION: &str = "VK_KHR_portability_enumeration";
|
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 {}
|
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 {
|
struct VulkanAllocatedBuffer {
|
||||||
buffer: vk::Buffer,
|
buffer: vk::Buffer,
|
||||||
memory: vk::DeviceMemory,
|
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(
|
fn create_command_pool(
|
||||||
device: &VulkanLogicalDeviceProbe,
|
device: &VulkanLogicalDeviceProbe,
|
||||||
) -> Result<vk::CommandPool, VulkanSmokeRendererError> {
|
) -> Result<vk::CommandPool, VulkanSmokeRendererError> {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1243,6 +1243,7 @@ const AUDITED_UNSAFE_SOURCE_FILES: &[&str] = &[
|
|||||||
"adapters/fparkan-render-vulkan/src/ffi.rs",
|
"adapters/fparkan-render-vulkan/src/ffi.rs",
|
||||||
"adapters/fparkan-render-vulkan/src/ffi/instance.rs",
|
"adapters/fparkan-render-vulkan/src/ffi/instance.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",
|
||||||
];
|
];
|
||||||
|
|
||||||
fn is_audited_unsafe_source(path: &Path) -> bool {
|
fn is_audited_unsafe_source(path: &Path) -> bool {
|
||||||
|
|||||||
Reference in New Issue
Block a user