use std::ffi::{CStr, CString};
use ash::prelude::VkResult;
pub use ash::version::{DeviceV1_0, EntryV1_0, InstanceV1_0};
use ash::vk;
use crate::vulkan::VkCreateInstanceError::VkError;
use crate::vulkan::{VkDebugReporter, VkEntry};
use ash::extensions::ext::DebugUtils;
use raw_window_handle::HasRawWindowHandle;
use std::sync::Arc;
pub struct VkInstance {
pub entry: Arc<VkEntry>,
pub instance: ash::Instance,
pub debug_reporter: Option<VkDebugReporter>,
}
#[derive(Debug)]
pub enum VkCreateInstanceError {
InstanceError(ash::InstanceError),
VkError(vk::Result),
}
impl std::error::Error for VkCreateInstanceError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match *self {
VkCreateInstanceError::InstanceError(ref e) => Some(e),
VkCreateInstanceError::VkError(ref e) => Some(e),
}
}
}
impl core::fmt::Display for VkCreateInstanceError {
fn fmt(
&self,
fmt: &mut core::fmt::Formatter,
) -> core::fmt::Result {
match *self {
VkCreateInstanceError::InstanceError(ref e) => e.fmt(fmt),
VkCreateInstanceError::VkError(ref e) => e.fmt(fmt),
}
}
}
impl From<ash::InstanceError> for VkCreateInstanceError {
fn from(result: ash::InstanceError) -> Self {
VkCreateInstanceError::InstanceError(result)
}
}
impl From<vk::Result> for VkCreateInstanceError {
fn from(result: vk::Result) -> Self {
VkCreateInstanceError::VkError(result)
}
}
impl VkInstance {
pub fn new(
entry: VkEntry,
window: &dyn HasRawWindowHandle,
app_name: &CString,
require_validation_layers_present: bool,
validation_layer_debug_report_flags: vk::DebugUtilsMessengerCreateFlagsEXT,
) -> Result<VkInstance, VkCreateInstanceError> {
let vulkan_version = match entry.try_enumerate_instance_version()? {
Some(version) => version,
None => vk::make_version(1, 0, 0),
};
let vulkan_version_tuple = (
vk::version_major(vulkan_version),
vk::version_minor(vulkan_version),
vk::version_patch(vulkan_version),
);
log::info!("Found Vulkan version: {:?}", vulkan_version_tuple);
let minimum_version = vk::make_version(1, 1, 0);
if vulkan_version < minimum_version {
return Err(VkError(vk::Result::ERROR_INCOMPATIBLE_DRIVER));
}
let layers = entry.enumerate_instance_layer_properties()?;
log::debug!("Available Layers: {:#?}", layers);
let extensions = entry.enumerate_instance_extension_properties()?;
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 = ash_window::enumerate_required_extensions(window)?;
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(vk::Result::ERROR_LAYER_NOT_PRESENT.into());
} else {
log::warn!("Could not find an appropriate validation layer. Check that the vulkan SDK has been installed or disable validation.");
}
}
let debug_extension = DebugUtils::name();
let has_debug_extension = extensions.iter().any(|extension| unsafe {
debug_extension == CStr::from_ptr(extension.extension_name.as_ptr())
});
if !has_debug_extension {
if require_validation_layers_present {
log::error!("Could not find the debug extension. Check that the vulkan SDK has been installed or disable validation.");
return Err(vk::Result::ERROR_EXTENSION_NOT_PRESENT.into());
} else {
log::warn!("Could not find the debug extension. Check that the vulkan SDK has been installed or disable validation.");
}
}
if let Some(best_validation_layer) = best_validation_layer {
if has_debug_extension {
layer_names.push(best_validation_layer);
extension_names.push(DebugUtils::name());
}
}
}
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 extension_names: Vec<_> = extension_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);
log::info!("Creating vulkan instance");
let instance: ash::Instance = unsafe { entry.create_instance(&create_info, None)? };
let debug_reporter = if !validation_layer_debug_report_flags.is_empty() {
Some(Self::setup_vulkan_debug_callback(
&entry,
&instance,
validation_layer_debug_report_flags,
)?)
} else {
None
};
Ok(VkInstance {
entry: Arc::new(entry),
instance,
debug_reporter,
})
}
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_callback<E: EntryV1_0, I: InstanceV1_0>(
entry: &E,
instance: &I,
debug_report_flags: vk::DebugUtilsMessengerCreateFlagsEXT,
) -> VkResult<VkDebugReporter> {
log::info!("Seting up vulkan debug callback");
let debug_info = vk::DebugUtilsMessengerCreateInfoEXT::builder()
.flags(debug_report_flags)
.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");
}
}