From de0e1ad58e2bc35c596aa2d9379b217a9fed82f2 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sat, 14 Feb 2026 23:02:06 +0100 Subject: [PATCH] vulkan: move vulkan instance creation to separate module --- src/gfx_apis/vulkan.rs | 26 +--- src/gfx_apis/vulkan/device.rs | 6 +- src/gfx_apis/vulkan/instance.rs | 226 +++-------------------------- src/main.rs | 1 + src/vulkan_core.rs | 243 ++++++++++++++++++++++++++++++++ 5 files changed, 271 insertions(+), 231 deletions(-) create mode 100644 src/vulkan_core.rs diff --git a/src/gfx_apis/vulkan.rs b/src/gfx_apis/vulkan.rs index bd136b9b..8ab6168f 100644 --- a/src/gfx_apis/vulkan.rs +++ b/src/gfx_apis/vulkan.rs @@ -45,18 +45,17 @@ use { drm::{Drm, DrmError, sync_obj::SyncObjCtx}, gbm::GbmError, }, + vulkan_core::VulkanCoreError, }, ahash::AHashMap, - ash::{LoadingError, vk}, + ash::vk, gpu_alloc::{AllocationError, MapError}, jay_config::video::GfxApi, log::Level, - once_cell::sync::Lazy, std::{ cell::Cell, ffi::{CStr, CString}, rc::Rc, - sync::Arc, }, thiserror::Error, uapi::{OwnedFd, c::dev_t}, @@ -64,14 +63,10 @@ use { #[derive(Debug, Error)] pub enum VulkanError { + #[error(transparent)] + Core(#[from] VulkanCoreError), #[error("Could not create a GBM device")] Gbm(#[source] GbmError), - #[error("Could not load libvulkan.so")] - Load(#[source] Arc), - #[error("Could not list instance extensions")] - InstanceExtensions(#[source] vk::Result), - #[error("Could not list instance layers")] - InstanceLayers(#[source] vk::Result), #[error("Could not list device extensions")] DeviceExtensions(#[source] vk::Result), #[error("Could not create the device")] @@ -84,8 +79,6 @@ pub enum VulkanError { CreateBuffer(#[source] vk::Result), #[error("Could not create a shader module")] CreateShaderModule(#[source] vk::Result), - #[error("Missing required instance extension {0:?}")] - MissingInstanceExtension(&'static CStr), #[error("Could not allocate a command pool")] AllocateCommandPool(#[source] vk::Result), #[error("Could not allocate a command buffer")] @@ -94,10 +87,6 @@ pub enum VulkanError { NoGraphicsQueue, #[error("Missing required device extension {0:?}")] MissingDeviceExtension(&'static CStr), - #[error("Could not create an instance")] - CreateInstance(#[source] vk::Result), - #[error("Could not create a debug-utils messenger")] - Messenger(#[source] vk::Result), #[error("Could not enumerate the physical devices")] EnumeratePhysicalDevices(#[source] vk::Result), #[error("Could not find a vulkan device that matches dev_t {0}")] @@ -232,9 +221,6 @@ impl From for GfxError { } } -pub static VULKAN_VALIDATION: Lazy = - Lazy::new(|| std::env::var("JAY_VULKAN_VALIDATION").ok().as_deref() == Some("1")); - pub fn create_graphics_context( eng: &Rc, ring: &Rc, @@ -242,7 +228,7 @@ pub fn create_graphics_context( caps_thread: Option<&PrCapsThread>, software: bool, ) -> Result, GfxError> { - let instance = VulkanInstance::new(Level::Info, *VULKAN_VALIDATION)?; + let instance = VulkanInstance::new(Level::Info)?; let device = 'device: { if let Some(t) = caps_thread { match unsafe { t.run(|| instance.create_device(drm, true, software)) } { @@ -259,7 +245,7 @@ pub fn create_graphics_context( } pub fn create_vulkan_allocator(drm: &Drm) -> Result, AllocatorError> { - let instance = VulkanInstance::new(Level::Debug, *VULKAN_VALIDATION)?; + let instance = VulkanInstance::new(Level::Debug)?; let device = instance.create_device(drm, false, false)?; let allocator = device.create_bo_allocator(drm)?; Ok(Rc::new(allocator)) diff --git a/src/gfx_apis/vulkan/device.rs b/src/gfx_apis/vulkan/device.rs index 33debdfc..76aa4968 100644 --- a/src/gfx_apis/vulkan/device.rs +++ b/src/gfx_apis/vulkan/device.rs @@ -5,10 +5,7 @@ use { gfx_apis::vulkan::{ VulkanError, format::{VulkanBlendBufferLimits, VulkanFormat}, - instance::{ - API_VERSION, ApiVersionDisplay, Extensions, VulkanInstance, - map_extension_properties, - }, + instance::VulkanInstance, }, utils::bitflags::BitflagsExt, video::{ @@ -16,6 +13,7 @@ use { drm::{Drm, sync_obj::SyncObjCtx}, gbm::{GBM_BO_USE_RENDERING, GbmDevice}, }, + vulkan_core::{API_VERSION, ApiVersionDisplay, Extensions, map_extension_properties}, }, ahash::AHashMap, arrayvec::ArrayVec, diff --git a/src/gfx_apis/vulkan/instance.rs b/src/gfx_apis/vulkan/instance.rs index 98852381..8946a72b 100644 --- a/src/gfx_apis/vulkan/instance.rs +++ b/src/gfx_apis/vulkan/instance.rs @@ -1,222 +1,34 @@ use { - crate::gfx_apis::vulkan::{VULKAN_VALIDATION, VulkanError}, - ahash::{AHashMap, AHashSet}, - ash::{ - Entry, Instance, LoadingError, - ext::{debug_utils, validation_features}, - vk::{ - API_VERSION_1_3, ApplicationInfo, Bool32, DebugUtilsMessageSeverityFlagsEXT, - DebugUtilsMessageTypeFlagsEXT, DebugUtilsMessengerCallbackDataEXT, - DebugUtilsMessengerCreateInfoEXT, DebugUtilsMessengerEXT, ExtensionProperties, FALSE, - InstanceCreateInfo, LayerProperties, ValidationFeaturesEXT, api_version_major, - api_version_minor, api_version_patch, api_version_variant, - }, - }, - isnt::std_1::collections::IsntHashMapExt, + crate::{gfx_apis::vulkan::VulkanError, vulkan_core::VulkanCoreInstance}, log::Level, - once_cell::sync::Lazy, - run_on_drop::on_drop, std::{ - ffi::{CStr, CString, c_void}, - fmt::{Display, Formatter}, - iter::IntoIterator, + ops::{Deref, DerefMut}, rc::Rc, - slice, - sync::Arc, }, - uapi::{Ustr, ustr}, }; pub struct VulkanInstance { - pub(super) _entry: &'static Entry, - pub(super) instance: Instance, - pub(super) debug_utils: debug_utils::Instance, - pub(super) messenger: DebugUtilsMessengerEXT, - pub(super) log_level: Level, + instance: VulkanCoreInstance, +} + +impl Deref for VulkanInstance { + type Target = VulkanCoreInstance; + + fn deref(&self) -> &Self::Target { + &self.instance + } +} + +impl DerefMut for VulkanInstance { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.instance + } } impl VulkanInstance { - pub fn new(log_level: Level, validation: bool) -> Result, VulkanError> { - static ENTRY: Lazy>> = - Lazy::new(|| unsafe { Entry::load() }.map_err(Arc::new)); - let entry = match &*ENTRY { - Ok(e) => e, - Err(e) => return Err(VulkanError::Load(e.clone())), - }; - let extensions = get_instance_extensions(entry, None)?; - for &ext in REQUIRED_INSTANCE_EXTENSIONS { - if extensions.not_contains_key(ext) { - return Err(VulkanError::MissingInstanceExtension(ext)); - } - } - let mut enabled_extensions: Vec<_> = REQUIRED_INSTANCE_EXTENSIONS - .iter() - .map(|c| c.as_ptr()) - .collect(); - let app_info = ApplicationInfo::default() - .api_version(API_VERSION) - .application_name(c"jay") - .application_version(1); - let mut severity = DebugUtilsMessageSeverityFlagsEXT::empty() - | DebugUtilsMessageSeverityFlagsEXT::ERROR - | DebugUtilsMessageSeverityFlagsEXT::WARNING; - if *VULKAN_VALIDATION { - severity |= DebugUtilsMessageSeverityFlagsEXT::INFO - | DebugUtilsMessageSeverityFlagsEXT::VERBOSE; - } - let types = DebugUtilsMessageTypeFlagsEXT::empty() - | DebugUtilsMessageTypeFlagsEXT::VALIDATION - | DebugUtilsMessageTypeFlagsEXT::PERFORMANCE - | DebugUtilsMessageTypeFlagsEXT::GENERAL; - let mut debug_info = DebugUtilsMessengerCreateInfoEXT::default() - .message_severity(severity) - .message_type(types) - .pfn_user_callback(Some(debug_callback)); - let validation_features = [ - // ash::vk::ValidationFeatureEnableEXT::DEBUG_PRINTF, - // ash::vk::ValidationFeatureEnableEXT::BEST_PRACTICES, - // ash::vk::ValidationFeatureEnableEXT::SYNCHRONIZATION_VALIDATION, - // ash::vk::ValidationFeatureEnableEXT::GPU_ASSISTED, - ]; - let mut validation_info = - ValidationFeaturesEXT::default().enabled_validation_features(&validation_features); - let mut create_info = InstanceCreateInfo::default() - .application_info(&app_info) - .push_next(&mut debug_info); - let validation_layer_name = VALIDATION_LAYER.as_ptr(); - if validation { - if get_available_layers(entry)?.contains(VALIDATION_LAYER) { - create_info = - create_info.enabled_layer_names(slice::from_ref(&validation_layer_name)); - let extensions = get_instance_extensions(entry, Some(VALIDATION_LAYER))?; - if extensions.contains_key(validation_features::NAME) { - enabled_extensions.push(validation_features::NAME.as_ptr()); - create_info = create_info.push_next(&mut validation_info); - } else { - log::warn!("{:?} is not available", validation_features::NAME); - } - } else { - log::warn!( - "Vulkan validation was requested but validation layers are not available" - ); - } - } - create_info = create_info.enabled_extension_names(&enabled_extensions); - let instance = match unsafe { entry.create_instance(&create_info, None) } { - Ok(i) => i, - Err(e) => return Err(VulkanError::CreateInstance(e)), - }; - let destroy_instance = on_drop(|| unsafe { instance.destroy_instance(None) }); - let debug_utils = debug_utils::Instance::new(entry, &instance); - let messenger = unsafe { debug_utils.create_debug_utils_messenger(&debug_info, None) }; - let messenger = match messenger { - Ok(m) => m, - Err(e) => return Err(VulkanError::Messenger(e)), - }; - destroy_instance.forget(); + pub fn new(log_level: Level) -> Result, VulkanError> { Ok(Rc::new(Self { - _entry: entry, - instance, - debug_utils, - messenger, - log_level, + instance: VulkanCoreInstance::new(log_level)?, })) } } - -impl Drop for VulkanInstance { - fn drop(&mut self) { - unsafe { - self.debug_utils - .destroy_debug_utils_messenger(self.messenger, None); - self.instance.destroy_instance(None); - } - } -} - -const REQUIRED_INSTANCE_EXTENSIONS: &[&CStr] = &[debug_utils::NAME]; - -const VALIDATION_LAYER: &CStr = c"VK_LAYER_KHRONOS_validation"; - -pub type Extensions = AHashMap; - -fn get_instance_extensions(entry: &Entry, layer: Option<&CStr>) -> Result { - unsafe { - entry - .enumerate_instance_extension_properties(layer) - .map_err(VulkanError::InstanceExtensions) - .map(map_extension_properties) - } -} - -fn get_available_layers(entry: &Entry) -> Result, VulkanError> { - unsafe { - entry - .enumerate_instance_layer_properties() - .map_err(VulkanError::InstanceLayers) - .map(map_layer_properties) - } -} - -fn map_layer_properties(props: Vec) -> AHashSet { - props - .into_iter() - .map(|e| unsafe { CStr::from_ptr(e.layer_name.as_ptr()).to_owned() }) - .collect() -} - -pub fn map_extension_properties(props: Vec) -> Extensions { - props - .into_iter() - .map(|e| { - let s = unsafe { CStr::from_ptr(e.extension_name.as_ptr()) }; - (s.to_owned(), e.spec_version) - }) - .collect() -} - -unsafe extern "system" fn debug_callback( - message_severity: DebugUtilsMessageSeverityFlagsEXT, - _message_types: DebugUtilsMessageTypeFlagsEXT, - p_callback_data: *const DebugUtilsMessengerCallbackDataEXT, - _p_user_data: *mut c_void, -) -> Bool32 { - let _level = match message_severity { - DebugUtilsMessageSeverityFlagsEXT::ERROR => Level::Error, - DebugUtilsMessageSeverityFlagsEXT::WARNING => Level::Warn, - DebugUtilsMessageSeverityFlagsEXT::INFO => Level::Info, - DebugUtilsMessageSeverityFlagsEXT::VERBOSE => Level::Trace, - _ => Level::Warn, - }; - let data = unsafe { &*p_callback_data }; - let message = unsafe { Ustr::from_ptr(data.p_message) }; - let message_id_name = if data.p_message_id_name.is_null() { - ustr!("") - } else { - unsafe { Ustr::from_ptr(data.p_message_id_name) } - }; - log::log!( - Level::Info, - "VULKAN: {} ({})", - message.display(), - message_id_name.display() - ); - FALSE -} - -pub struct ApiVersionDisplay(pub u32); - -impl Display for ApiVersionDisplay { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}.{}.{}.{}", - api_version_variant(self.0), - api_version_major(self.0), - api_version_minor(self.0), - api_version_patch(self.0), - ) - } -} - -pub const API_VERSION: u32 = API_VERSION_1_3; diff --git a/src/main.rs b/src/main.rs index c7354044..4d3c060c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -108,6 +108,7 @@ mod user_session; mod utils; mod version; mod video; +mod vulkan_core; mod wheel; mod wire; mod wire_dbus; diff --git a/src/vulkan_core.rs b/src/vulkan_core.rs new file mode 100644 index 00000000..f269c3d0 --- /dev/null +++ b/src/vulkan_core.rs @@ -0,0 +1,243 @@ +use { + ahash::{AHashMap, AHashSet}, + ash::{ + Entry, Instance, LoadingError, + ext::{debug_utils, validation_features}, + vk::{ + self, API_VERSION_1_3, ApplicationInfo, Bool32, DebugUtilsMessageSeverityFlagsEXT, + DebugUtilsMessageTypeFlagsEXT, DebugUtilsMessengerCallbackDataEXT, + DebugUtilsMessengerCreateInfoEXT, DebugUtilsMessengerEXT, ExtensionProperties, FALSE, + InstanceCreateInfo, LayerProperties, ValidationFeaturesEXT, api_version_major, + api_version_minor, api_version_patch, api_version_variant, + }, + }, + isnt::std_1::collections::IsntHashMapExt, + log::Level, + once_cell::sync::Lazy, + run_on_drop::on_drop, + std::{ + ffi::{CStr, CString, c_void}, + fmt::{Display, Formatter}, + slice, + sync::Arc, + }, + thiserror::Error, + uapi::{Ustr, ustr}, +}; + +static VULKAN_ENTRY: Lazy>> = + Lazy::new(|| unsafe { Entry::load() }.map_err(Arc::new)); + +static VULKAN_VALIDATION: Lazy = + Lazy::new(|| std::env::var("JAY_VULKAN_VALIDATION").ok().as_deref() == Some("1")); + +#[derive(Debug, Error)] +pub enum VulkanCoreError { + #[error("Could not load libvulkan.so")] + Load(#[source] Arc), + #[error("Could not list instance extensions")] + InstanceExtensions(#[source] vk::Result), + #[error("Could not list instance layers")] + InstanceLayers(#[source] vk::Result), + #[error("Missing required instance extension {0:?}")] + MissingInstanceExtension(&'static CStr), + #[error("Could not create an instance")] + CreateInstance(#[source] vk::Result), + #[error("Could not create a debug-utils messenger")] + Messenger(#[source] vk::Result), +} + +pub struct VulkanCoreInstance { + _entry: &'static Entry, + pub instance: Instance, + debug_utils: debug_utils::Instance, + messenger: DebugUtilsMessengerEXT, + pub log_level: Level, +} + +impl VulkanCoreInstance { + pub fn new(log_level: Level) -> Result { + let entry = match &*VULKAN_ENTRY { + Ok(e) => e, + Err(e) => return Err(VulkanCoreError::Load(e.clone())), + }; + let extensions = get_instance_extensions(entry, None)?; + for &ext in REQUIRED_INSTANCE_EXTENSIONS { + if extensions.not_contains_key(ext) { + return Err(VulkanCoreError::MissingInstanceExtension(ext)); + } + } + let mut enabled_extensions: Vec<_> = REQUIRED_INSTANCE_EXTENSIONS + .iter() + .map(|c| c.as_ptr()) + .collect(); + let app_info = ApplicationInfo::default() + .api_version(API_VERSION) + .application_name(c"jay") + .application_version(1); + let mut severity = DebugUtilsMessageSeverityFlagsEXT::empty() + | DebugUtilsMessageSeverityFlagsEXT::ERROR + | DebugUtilsMessageSeverityFlagsEXT::WARNING; + if *VULKAN_VALIDATION { + severity |= DebugUtilsMessageSeverityFlagsEXT::INFO + | DebugUtilsMessageSeverityFlagsEXT::VERBOSE; + } + let types = DebugUtilsMessageTypeFlagsEXT::empty() + | DebugUtilsMessageTypeFlagsEXT::VALIDATION + | DebugUtilsMessageTypeFlagsEXT::PERFORMANCE + | DebugUtilsMessageTypeFlagsEXT::GENERAL; + let mut debug_info = DebugUtilsMessengerCreateInfoEXT::default() + .message_severity(severity) + .message_type(types) + .pfn_user_callback(Some(debug_callback)); + let validation_features = [ + // ash::vk::ValidationFeatureEnableEXT::DEBUG_PRINTF, + // ash::vk::ValidationFeatureEnableEXT::BEST_PRACTICES, + // ash::vk::ValidationFeatureEnableEXT::SYNCHRONIZATION_VALIDATION, + // ash::vk::ValidationFeatureEnableEXT::GPU_ASSISTED, + ]; + let mut validation_info = + ValidationFeaturesEXT::default().enabled_validation_features(&validation_features); + let mut create_info = InstanceCreateInfo::default() + .application_info(&app_info) + .push_next(&mut debug_info); + let validation_layer_name = VALIDATION_LAYER.as_ptr(); + if *VULKAN_VALIDATION { + if get_available_layers(entry)?.contains(VALIDATION_LAYER) { + create_info = + create_info.enabled_layer_names(slice::from_ref(&validation_layer_name)); + let extensions = get_instance_extensions(entry, Some(VALIDATION_LAYER))?; + if extensions.contains_key(validation_features::NAME) { + enabled_extensions.push(validation_features::NAME.as_ptr()); + create_info = create_info.push_next(&mut validation_info); + } else { + log::warn!("{:?} is not available", validation_features::NAME); + } + } else { + log::warn!( + "Vulkan validation was requested but validation layers are not available" + ); + } + } + create_info = create_info.enabled_extension_names(&enabled_extensions); + let instance = match unsafe { entry.create_instance(&create_info, None) } { + Ok(i) => i, + Err(e) => return Err(VulkanCoreError::CreateInstance(e)), + }; + let destroy_instance = on_drop(|| unsafe { instance.destroy_instance(None) }); + let debug_utils = debug_utils::Instance::new(entry, &instance); + let messenger = unsafe { debug_utils.create_debug_utils_messenger(&debug_info, None) }; + let messenger = match messenger { + Ok(m) => m, + Err(e) => return Err(VulkanCoreError::Messenger(e)), + }; + destroy_instance.forget(); + Ok(Self { + _entry: entry, + instance, + debug_utils, + messenger, + log_level, + }) + } +} + +impl Drop for VulkanCoreInstance { + fn drop(&mut self) { + unsafe { + self.debug_utils + .destroy_debug_utils_messenger(self.messenger, None); + self.instance.destroy_instance(None); + } + } +} + +const REQUIRED_INSTANCE_EXTENSIONS: &[&CStr] = &[debug_utils::NAME]; + +const VALIDATION_LAYER: &CStr = c"VK_LAYER_KHRONOS_validation"; + +pub type Extensions = AHashMap; + +fn get_instance_extensions( + entry: &Entry, + layer: Option<&CStr>, +) -> Result { + unsafe { + entry + .enumerate_instance_extension_properties(layer) + .map_err(VulkanCoreError::InstanceExtensions) + .map(map_extension_properties) + } +} + +fn get_available_layers(entry: &Entry) -> Result, VulkanCoreError> { + unsafe { + entry + .enumerate_instance_layer_properties() + .map_err(VulkanCoreError::InstanceLayers) + .map(map_layer_properties) + } +} + +fn map_layer_properties(props: Vec) -> AHashSet { + props + .into_iter() + .map(|e| unsafe { CStr::from_ptr(e.layer_name.as_ptr()).to_owned() }) + .collect() +} + +pub fn map_extension_properties(props: Vec) -> Extensions { + props + .into_iter() + .map(|e| { + let s = unsafe { CStr::from_ptr(e.extension_name.as_ptr()) }; + (s.to_owned(), e.spec_version) + }) + .collect() +} + +unsafe extern "system" fn debug_callback( + message_severity: DebugUtilsMessageSeverityFlagsEXT, + _message_types: DebugUtilsMessageTypeFlagsEXT, + p_callback_data: *const DebugUtilsMessengerCallbackDataEXT, + _p_user_data: *mut c_void, +) -> Bool32 { + let _level = match message_severity { + DebugUtilsMessageSeverityFlagsEXT::ERROR => Level::Error, + DebugUtilsMessageSeverityFlagsEXT::WARNING => Level::Warn, + DebugUtilsMessageSeverityFlagsEXT::INFO => Level::Info, + DebugUtilsMessageSeverityFlagsEXT::VERBOSE => Level::Trace, + _ => Level::Warn, + }; + let data = unsafe { &*p_callback_data }; + let message = unsafe { Ustr::from_ptr(data.p_message) }; + let message_id_name = if data.p_message_id_name.is_null() { + ustr!("") + } else { + unsafe { Ustr::from_ptr(data.p_message_id_name) } + }; + log::log!( + Level::Info, + "VULKAN: {} ({})", + message.display(), + message_id_name.display() + ); + FALSE +} + +pub struct ApiVersionDisplay(pub u32); + +impl Display for ApiVersionDisplay { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}.{}.{}.{}", + api_version_variant(self.0), + api_version_major(self.0), + api_version_minor(self.0), + api_version_patch(self.0), + ) + } +} + +pub const API_VERSION: u32 = API_VERSION_1_3;