use std::ffi::{CStr, CString};
use ash::prelude::VkResult;
use ash::vk;
use crate::vulkan::{VkDebugReporter, VkEntry};
use crate::{RafxError, RafxResult};
use ash::extensions::ext::DebugUtils;
use ash::vk::DebugUtilsMessageTypeFlagsEXT;
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
use std::sync::Arc;
pub struct VkInstance {
pub entry: Arc<VkEntry>,
pub instance: ash::Instance,
pub debug_reporter: Option<Arc<VkDebugReporter>>,
}
impl VkInstance {
pub fn new(
entry: VkEntry,
display: &dyn HasRawDisplayHandle,
_window: &dyn HasRawWindowHandle,
app_name: &CString,
require_validation_layers_present: bool,
validation_layer_debug_report_flags: vk::DebugUtilsMessageSeverityFlagsEXT,
enable_debug_names: bool,
) -> RafxResult<VkInstance> {
let vulkan_version = match entry.entry().try_enumerate_instance_version()? {
Some(version) => version,
None => vk::make_api_version(0, 1, 0, 0),
};
let vulkan_version_tuple = (
vk::api_version_major(vulkan_version),
vk::api_version_minor(vulkan_version),
vk::api_version_patch(vulkan_version),
);
log::info!("Found Vulkan version: {:?}", vulkan_version_tuple);
let minimum_version = vk::make_api_version(0, 1, 1, 0);
if vulkan_version < minimum_version {
return Err(RafxError::VkError(vk::Result::ERROR_INCOMPATIBLE_DRIVER))?;
}
let layers = entry.entry().enumerate_instance_layer_properties()?;
log::debug!("Available Layers: {:#?}", layers);
let extensions = entry
.entry()
.enumerate_instance_extension_properties(None)?;
log::debug!("Available Extensions: {:#?}", extensions);
let appinfo = vk::ApplicationInfo::builder()
.application_name(app_name)
.application_version(0)
.engine_name(app_name)
.engine_version(0)
.api_version(vulkan_version);
let mut layer_names = vec![];
let mut extension_names: Vec<_> =
ash_window::enumerate_required_extensions(display.raw_display_handle())?
.iter()
.copied()
.collect();
let debug_utils_extension_available = extensions.iter().any(|extension|
unsafe { CStr::from_ptr(extension.extension_name.as_ptr()) } == DebugUtils::name()
);
let mut use_debug_utils_extension = debug_utils_extension_available && enable_debug_names;
if !validation_layer_debug_report_flags.is_empty() {
let best_validation_layer = VkInstance::find_best_validation_layer(&layers);
if best_validation_layer.is_none() {
if require_validation_layers_present {
log::error!("Could not find an appropriate validation layer. Check that the vulkan SDK has been installed or disable validation.");
return Err(RafxError::ValidationRequiredButUnavailable);
} else {
log::warn!("Could not find an appropriate validation layer. Check that the vulkan SDK has been installed or disable validation.");
}
}
if !debug_utils_extension_available {
if require_validation_layers_present {
log::error!("Could not find the DebugUtils extension. Check that the vulkan SDK has been installed or disable validation.");
return Err(RafxError::ValidationRequiredButUnavailable);
} else {
log::warn!("Could not find the DebugUtils extension. Check that the vulkan SDK has been installed or disable validation.");
}
}
if let Some(best_validation_layer) = best_validation_layer {
if debug_utils_extension_available {
layer_names.push(best_validation_layer);
use_debug_utils_extension |= true;
}
}
}
if use_debug_utils_extension {
extension_names.push(DebugUtils::name().as_ptr());
}
if extensions.iter().any(|extension|
unsafe { CStr::from_ptr(extension.extension_name.as_ptr()) } == vk::ExtSwapchainColorspaceFn::name()
) {
extension_names.push(vk::ExtSwapchainColorspaceFn::name().as_ptr());
}
#[allow(unused_mut)]
let mut create_instance_flags = vk::InstanceCreateFlags::empty();
#[cfg(target_os = "macos")]
{
const VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR: vk::InstanceCreateFlags =
vk::InstanceCreateFlags::from_raw(0x00000001);
fn khr_portability_subset_extension_name() -> &'static CStr {
CStr::from_bytes_with_nul(b"VK_KHR_portability_enumeration\0")
.expect("Wrong extension string")
}
let swapchain_extension_name = khr_portability_subset_extension_name();
if extensions.iter().any(|extension| unsafe {
CStr::from_ptr(extension.extension_name.as_ptr()) == swapchain_extension_name
}) {
extension_names.push(swapchain_extension_name.as_ptr() as *const i8);
create_instance_flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
}
}
if log::log_enabled!(log::Level::Debug) {
log::debug!("Using layers: {:?}", layer_names);
log::debug!("Using extensions: {:?}", extension_names);
}
let layer_names: Vec<_> = layer_names.iter().map(|x| x.as_ptr()).collect();
let create_info = vk::InstanceCreateInfo::builder()
.application_info(&appinfo)
.enabled_layer_names(&layer_names)
.enabled_extension_names(&extension_names)
.flags(create_instance_flags);
log::info!("Creating vulkan instance");
let instance: ash::Instance = unsafe { entry.entry().create_instance(&create_info, None)? };
let debug_reporter = if use_debug_utils_extension {
Some(Self::setup_vulkan_debug_reporter(
&entry.entry(),
&instance,
validation_layer_debug_report_flags,
)?)
} else {
None
};
Ok(VkInstance {
entry: Arc::new(entry),
instance,
debug_reporter: debug_reporter.map(Arc::new),
})
}
fn find_best_validation_layer(layers: &[ash::vk::LayerProperties]) -> Option<&'static CStr> {
fn khronos_validation_layer_name() -> &'static CStr {
CStr::from_bytes_with_nul(b"VK_LAYER_KHRONOS_validation\0")
.expect("Wrong extension string")
}
fn lunarg_validation_layer_name() -> &'static CStr {
CStr::from_bytes_with_nul(b"VK_LAYER_LUNARG_standard_validation\0")
.expect("Wrong extension string")
}
let khronos_validation_layer_name = khronos_validation_layer_name();
let lunarg_validation_layer_name = lunarg_validation_layer_name();
let mut best_available_layer = None;
for layer in layers {
let layer_name = unsafe { CStr::from_ptr(layer.layer_name.as_ptr()) };
if layer_name == khronos_validation_layer_name {
best_available_layer = Some(khronos_validation_layer_name);
break;
}
if layer_name == lunarg_validation_layer_name {
best_available_layer = Some(lunarg_validation_layer_name);
}
}
best_available_layer
}
fn setup_vulkan_debug_reporter(
entry: &ash::Entry,
instance: &ash::Instance,
debug_report_flags: vk::DebugUtilsMessageSeverityFlagsEXT,
) -> VkResult<VkDebugReporter> {
log::info!("Seting up vulkan debug callback");
let debug_info = vk::DebugUtilsMessengerCreateInfoEXT::builder()
.message_severity(debug_report_flags)
.message_type(
DebugUtilsMessageTypeFlagsEXT::GENERAL
| DebugUtilsMessageTypeFlagsEXT::VALIDATION
| DebugUtilsMessageTypeFlagsEXT::PERFORMANCE,
)
.pfn_user_callback(Some(super::debug_reporter::vulkan_debug_callback));
let debug_report_loader = ash::extensions::ext::DebugUtils::new(entry, instance);
let debug_callback =
unsafe { debug_report_loader.create_debug_utils_messenger(&debug_info, None)? };
Ok(VkDebugReporter {
debug_report_loader,
debug_callback,
})
}
}
impl Drop for VkInstance {
fn drop(&mut self) {
log::trace!("destroying VkInstance");
std::mem::drop(self.debug_reporter.take());
unsafe {
self.instance.destroy_instance(None);
}
log::trace!("destroyed VkInstance");
}
}