vulkan: move vulkan instance creation to separate module
This commit is contained in:
parent
8989dd7987
commit
de0e1ad58e
5 changed files with 271 additions and 231 deletions
|
|
@ -45,18 +45,17 @@ use {
|
||||||
drm::{Drm, DrmError, sync_obj::SyncObjCtx},
|
drm::{Drm, DrmError, sync_obj::SyncObjCtx},
|
||||||
gbm::GbmError,
|
gbm::GbmError,
|
||||||
},
|
},
|
||||||
|
vulkan_core::VulkanCoreError,
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
ash::{LoadingError, vk},
|
ash::vk,
|
||||||
gpu_alloc::{AllocationError, MapError},
|
gpu_alloc::{AllocationError, MapError},
|
||||||
jay_config::video::GfxApi,
|
jay_config::video::GfxApi,
|
||||||
log::Level,
|
log::Level,
|
||||||
once_cell::sync::Lazy,
|
|
||||||
std::{
|
std::{
|
||||||
cell::Cell,
|
cell::Cell,
|
||||||
ffi::{CStr, CString},
|
ffi::{CStr, CString},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::Arc,
|
|
||||||
},
|
},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
uapi::{OwnedFd, c::dev_t},
|
uapi::{OwnedFd, c::dev_t},
|
||||||
|
|
@ -64,14 +63,10 @@ use {
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum VulkanError {
|
pub enum VulkanError {
|
||||||
|
#[error(transparent)]
|
||||||
|
Core(#[from] VulkanCoreError),
|
||||||
#[error("Could not create a GBM device")]
|
#[error("Could not create a GBM device")]
|
||||||
Gbm(#[source] GbmError),
|
Gbm(#[source] GbmError),
|
||||||
#[error("Could not load libvulkan.so")]
|
|
||||||
Load(#[source] Arc<LoadingError>),
|
|
||||||
#[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")]
|
#[error("Could not list device extensions")]
|
||||||
DeviceExtensions(#[source] vk::Result),
|
DeviceExtensions(#[source] vk::Result),
|
||||||
#[error("Could not create the device")]
|
#[error("Could not create the device")]
|
||||||
|
|
@ -84,8 +79,6 @@ pub enum VulkanError {
|
||||||
CreateBuffer(#[source] vk::Result),
|
CreateBuffer(#[source] vk::Result),
|
||||||
#[error("Could not create a shader module")]
|
#[error("Could not create a shader module")]
|
||||||
CreateShaderModule(#[source] vk::Result),
|
CreateShaderModule(#[source] vk::Result),
|
||||||
#[error("Missing required instance extension {0:?}")]
|
|
||||||
MissingInstanceExtension(&'static CStr),
|
|
||||||
#[error("Could not allocate a command pool")]
|
#[error("Could not allocate a command pool")]
|
||||||
AllocateCommandPool(#[source] vk::Result),
|
AllocateCommandPool(#[source] vk::Result),
|
||||||
#[error("Could not allocate a command buffer")]
|
#[error("Could not allocate a command buffer")]
|
||||||
|
|
@ -94,10 +87,6 @@ pub enum VulkanError {
|
||||||
NoGraphicsQueue,
|
NoGraphicsQueue,
|
||||||
#[error("Missing required device extension {0:?}")]
|
#[error("Missing required device extension {0:?}")]
|
||||||
MissingDeviceExtension(&'static CStr),
|
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")]
|
#[error("Could not enumerate the physical devices")]
|
||||||
EnumeratePhysicalDevices(#[source] vk::Result),
|
EnumeratePhysicalDevices(#[source] vk::Result),
|
||||||
#[error("Could not find a vulkan device that matches dev_t {0}")]
|
#[error("Could not find a vulkan device that matches dev_t {0}")]
|
||||||
|
|
@ -232,9 +221,6 @@ impl From<VulkanError> for GfxError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static VULKAN_VALIDATION: Lazy<bool> =
|
|
||||||
Lazy::new(|| std::env::var("JAY_VULKAN_VALIDATION").ok().as_deref() == Some("1"));
|
|
||||||
|
|
||||||
pub fn create_graphics_context(
|
pub fn create_graphics_context(
|
||||||
eng: &Rc<AsyncEngine>,
|
eng: &Rc<AsyncEngine>,
|
||||||
ring: &Rc<IoUring>,
|
ring: &Rc<IoUring>,
|
||||||
|
|
@ -242,7 +228,7 @@ pub fn create_graphics_context(
|
||||||
caps_thread: Option<&PrCapsThread>,
|
caps_thread: Option<&PrCapsThread>,
|
||||||
software: bool,
|
software: bool,
|
||||||
) -> Result<Rc<dyn GfxContext>, GfxError> {
|
) -> Result<Rc<dyn GfxContext>, GfxError> {
|
||||||
let instance = VulkanInstance::new(Level::Info, *VULKAN_VALIDATION)?;
|
let instance = VulkanInstance::new(Level::Info)?;
|
||||||
let device = 'device: {
|
let device = 'device: {
|
||||||
if let Some(t) = caps_thread {
|
if let Some(t) = caps_thread {
|
||||||
match unsafe { t.run(|| instance.create_device(drm, true, software)) } {
|
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<Rc<dyn Allocator>, AllocatorError> {
|
pub fn create_vulkan_allocator(drm: &Drm) -> Result<Rc<dyn Allocator>, AllocatorError> {
|
||||||
let instance = VulkanInstance::new(Level::Debug, *VULKAN_VALIDATION)?;
|
let instance = VulkanInstance::new(Level::Debug)?;
|
||||||
let device = instance.create_device(drm, false, false)?;
|
let device = instance.create_device(drm, false, false)?;
|
||||||
let allocator = device.create_bo_allocator(drm)?;
|
let allocator = device.create_bo_allocator(drm)?;
|
||||||
Ok(Rc::new(allocator))
|
Ok(Rc::new(allocator))
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,7 @@ use {
|
||||||
gfx_apis::vulkan::{
|
gfx_apis::vulkan::{
|
||||||
VulkanError,
|
VulkanError,
|
||||||
format::{VulkanBlendBufferLimits, VulkanFormat},
|
format::{VulkanBlendBufferLimits, VulkanFormat},
|
||||||
instance::{
|
instance::VulkanInstance,
|
||||||
API_VERSION, ApiVersionDisplay, Extensions, VulkanInstance,
|
|
||||||
map_extension_properties,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
utils::bitflags::BitflagsExt,
|
utils::bitflags::BitflagsExt,
|
||||||
video::{
|
video::{
|
||||||
|
|
@ -16,6 +13,7 @@ use {
|
||||||
drm::{Drm, sync_obj::SyncObjCtx},
|
drm::{Drm, sync_obj::SyncObjCtx},
|
||||||
gbm::{GBM_BO_USE_RENDERING, GbmDevice},
|
gbm::{GBM_BO_USE_RENDERING, GbmDevice},
|
||||||
},
|
},
|
||||||
|
vulkan_core::{API_VERSION, ApiVersionDisplay, Extensions, map_extension_properties},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
arrayvec::ArrayVec,
|
arrayvec::ArrayVec,
|
||||||
|
|
|
||||||
|
|
@ -1,222 +1,34 @@
|
||||||
use {
|
use {
|
||||||
crate::gfx_apis::vulkan::{VULKAN_VALIDATION, VulkanError},
|
crate::{gfx_apis::vulkan::VulkanError, vulkan_core::VulkanCoreInstance},
|
||||||
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,
|
|
||||||
log::Level,
|
log::Level,
|
||||||
once_cell::sync::Lazy,
|
|
||||||
run_on_drop::on_drop,
|
|
||||||
std::{
|
std::{
|
||||||
ffi::{CStr, CString, c_void},
|
ops::{Deref, DerefMut},
|
||||||
fmt::{Display, Formatter},
|
|
||||||
iter::IntoIterator,
|
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
slice,
|
|
||||||
sync::Arc,
|
|
||||||
},
|
},
|
||||||
uapi::{Ustr, ustr},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct VulkanInstance {
|
pub struct VulkanInstance {
|
||||||
pub(super) _entry: &'static Entry,
|
instance: VulkanCoreInstance,
|
||||||
pub(super) instance: Instance,
|
}
|
||||||
pub(super) debug_utils: debug_utils::Instance,
|
|
||||||
pub(super) messenger: DebugUtilsMessengerEXT,
|
impl Deref for VulkanInstance {
|
||||||
pub(super) log_level: Level,
|
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 {
|
impl VulkanInstance {
|
||||||
pub fn new(log_level: Level, validation: bool) -> Result<Rc<Self>, VulkanError> {
|
pub fn new(log_level: Level) -> Result<Rc<Self>, VulkanError> {
|
||||||
static ENTRY: Lazy<Result<Entry, Arc<LoadingError>>> =
|
|
||||||
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();
|
|
||||||
Ok(Rc::new(Self {
|
Ok(Rc::new(Self {
|
||||||
_entry: entry,
|
instance: VulkanCoreInstance::new(log_level)?,
|
||||||
instance,
|
|
||||||
debug_utils,
|
|
||||||
messenger,
|
|
||||||
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<CString, u32>;
|
|
||||||
|
|
||||||
fn get_instance_extensions(entry: &Entry, layer: Option<&CStr>) -> Result<Extensions, VulkanError> {
|
|
||||||
unsafe {
|
|
||||||
entry
|
|
||||||
.enumerate_instance_extension_properties(layer)
|
|
||||||
.map_err(VulkanError::InstanceExtensions)
|
|
||||||
.map(map_extension_properties)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_available_layers(entry: &Entry) -> Result<AHashSet<CString>, VulkanError> {
|
|
||||||
unsafe {
|
|
||||||
entry
|
|
||||||
.enumerate_instance_layer_properties()
|
|
||||||
.map_err(VulkanError::InstanceLayers)
|
|
||||||
.map(map_layer_properties)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_layer_properties(props: Vec<LayerProperties>) -> AHashSet<CString> {
|
|
||||||
props
|
|
||||||
.into_iter()
|
|
||||||
.map(|e| unsafe { CStr::from_ptr(e.layer_name.as_ptr()).to_owned() })
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn map_extension_properties(props: Vec<ExtensionProperties>) -> 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!("<null>")
|
|
||||||
} 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;
|
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,7 @@ mod user_session;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod version;
|
mod version;
|
||||||
mod video;
|
mod video;
|
||||||
|
mod vulkan_core;
|
||||||
mod wheel;
|
mod wheel;
|
||||||
mod wire;
|
mod wire;
|
||||||
mod wire_dbus;
|
mod wire_dbus;
|
||||||
|
|
|
||||||
243
src/vulkan_core.rs
Normal file
243
src/vulkan_core.rs
Normal file
|
|
@ -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<Result<Entry, Arc<LoadingError>>> =
|
||||||
|
Lazy::new(|| unsafe { Entry::load() }.map_err(Arc::new));
|
||||||
|
|
||||||
|
static VULKAN_VALIDATION: Lazy<bool> =
|
||||||
|
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<LoadingError>),
|
||||||
|
#[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<Self, VulkanCoreError> {
|
||||||
|
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<CString, u32>;
|
||||||
|
|
||||||
|
fn get_instance_extensions(
|
||||||
|
entry: &Entry,
|
||||||
|
layer: Option<&CStr>,
|
||||||
|
) -> Result<Extensions, VulkanCoreError> {
|
||||||
|
unsafe {
|
||||||
|
entry
|
||||||
|
.enumerate_instance_extension_properties(layer)
|
||||||
|
.map_err(VulkanCoreError::InstanceExtensions)
|
||||||
|
.map(map_extension_properties)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_available_layers(entry: &Entry) -> Result<AHashSet<CString>, VulkanCoreError> {
|
||||||
|
unsafe {
|
||||||
|
entry
|
||||||
|
.enumerate_instance_layer_properties()
|
||||||
|
.map_err(VulkanCoreError::InstanceLayers)
|
||||||
|
.map(map_layer_properties)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map_layer_properties(props: Vec<LayerProperties>) -> AHashSet<CString> {
|
||||||
|
props
|
||||||
|
.into_iter()
|
||||||
|
.map(|e| unsafe { CStr::from_ptr(e.layer_name.as_ptr()).to_owned() })
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_extension_properties(props: Vec<ExtensionProperties>) -> 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!("<null>")
|
||||||
|
} 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;
|
||||||
Loading…
Add table
Add a link
Reference in a new issue