use std::sync::atomic::{AtomicU64, Ordering};
use ash::vk::{self};
use crate::{graphics_hardware_interface, vulkan::DebugCallbackData};
pub struct Instance {
pub(crate) instance: ash::Instance,
pub(crate) entry: ash::Entry,
pub(crate) debug_data: Box<DebugCallbackData>,
debug_utils: Option<ash::ext::debug_utils::Instance>,
debug_utils_messenger: Option<vk::DebugUtilsMessengerEXT>,
}
unsafe impl Send for Instance {}
impl Instance {
pub fn new(settings: crate::device::Features) -> Result<Instance, &'static str> {
let entry = ash::Entry::linked();
let available_instance_layers = unsafe { entry.enumerate_instance_layer_properties().unwrap() };
let available_instance_extensions = unsafe { entry.enumerate_instance_extension_properties(None).unwrap() };
let is_instance_layer_available = |name: &str| {
available_instance_layers
.iter()
.any(|layer| unsafe { std::ffi::CStr::from_ptr(layer.layer_name.as_ptr()).to_str().unwrap() == name })
};
let is_instance_extension_available = |name: &str| {
available_instance_extensions.iter().any(|extension| unsafe {
std::ffi::CStr::from_ptr(extension.extension_name.as_ptr()).to_str().unwrap() == name
})
};
let application_info = vk::ApplicationInfo::default().api_version(vk::make_api_version(0, 1, 4, 0));
let mut layer_names = Vec::new();
if settings.validation {
if is_instance_layer_available("VK_LAYER_KHRONOS_validation") {
layer_names.push(
std::ffi::CStr::from_bytes_with_nul(b"VK_LAYER_KHRONOS_validation\0")
.unwrap()
.as_ptr(),
);
} else {
return Err("VK_LAYER_KHRONOS_validation is not available");
}
}
if settings.api_dump {
if is_instance_layer_available("VK_LAYER_LUNARG_api_dump") {
layer_names.push(
std::ffi::CStr::from_bytes_with_nul(b"VK_LAYER_LUNARG_api_dump\0")
.unwrap()
.as_ptr(),
);
} else {
return Err("VK_LAYER_LUNARG_api_dump is not available");
}
}
let mut extension_names = Vec::new();
if is_instance_extension_available(ash::khr::surface::NAME.to_str().unwrap()) {
extension_names.push(ash::khr::surface::NAME.as_ptr());
} else {
return Err("VK_KHR_surface extension is not available");
}
#[cfg(target_os = "linux")]
{
if is_instance_extension_available(ash::khr::wayland_surface::NAME.to_str().unwrap()) {
extension_names.push(ash::khr::wayland_surface::NAME.as_ptr());
}
}
#[cfg(target_os = "windows")]
{
if is_instance_extension_available(ash::khr::win32_surface::NAME.to_str().unwrap()) {
extension_names.push(ash::khr::win32_surface::NAME.as_ptr());
}
}
#[cfg(target_os = "macos")]
{
if is_instance_extension_available(ash::ext::metal_surface::NAME.to_str().unwrap()) {
extension_names.push(ash::ext::metal_surface::NAME.as_ptr());
} else {
return Err("VK_MVK_macos_surface extension is not available");
}
if is_instance_extension_available(ash::khr::portability_enumeration::NAME.to_str().unwrap()) {
extension_names.push(ash::khr::portability_enumeration::NAME.as_ptr());
} else {
return Err("VK_KHR_portability_enumeration extension is not available");
}
}
if is_instance_extension_available(ash::khr::get_surface_capabilities2::NAME.to_str().unwrap()) {
extension_names.push(ash::khr::get_surface_capabilities2::NAME.as_ptr());
} else {
return Err("VK_KHR_get_surface_capabilities2 extension is not available");
}
if is_instance_extension_available(ash::ext::surface_maintenance1::NAME.to_str().unwrap()) {
extension_names.push(ash::ext::surface_maintenance1::NAME.as_ptr());
} else {
return Err("VK_EXT_surface_maintenance1 extension is not available");
}
if settings.validation {
if is_instance_extension_available(ash::ext::debug_utils::NAME.to_str().unwrap()) {
extension_names.push(ash::ext::debug_utils::NAME.as_ptr());
} else {
return Err("VK_EXT_debug_utils extension is not available");
}
}
let enabled_validation_features = {
let mut enabled_features = Vec::with_capacity(6);
enabled_features.push(vk::ValidationFeatureEnableEXT::SYNCHRONIZATION_VALIDATION);
enabled_features.push(vk::ValidationFeatureEnableEXT::BEST_PRACTICES);
if settings.gpu_validation {
enabled_features.push(vk::ValidationFeatureEnableEXT::GPU_ASSISTED);
}
enabled_features
};
let mut validation_features =
vk::ValidationFeaturesEXT::default().enabled_validation_features(&enabled_validation_features);
let mut instance_flags = vk::InstanceCreateFlags::empty();
if cfg!(target_os = "macos") {
instance_flags |= vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR;
}
let instance_create_info = vk::InstanceCreateInfo::default()
.flags(instance_flags)
.application_info(&application_info)
.enabled_layer_names(&layer_names)
.enabled_extension_names(&extension_names);
let instance_create_info = if settings.validation {
instance_create_info.push_next(&mut validation_features)
} else {
instance_create_info
};
let instance_result = unsafe { entry.create_instance(&instance_create_info, None) };
let instance = match instance_result {
Err(err) => match err {
vk::Result::ERROR_EXTENSION_NOT_PRESENT => Err("Extension not present"),
vk::Result::ERROR_INCOMPATIBLE_DRIVER => Err("Incompatible driver"),
vk::Result::ERROR_INITIALIZATION_FAILED => Err("Initialization failed"),
vk::Result::ERROR_LAYER_NOT_PRESENT => Err("Layer not present"),
vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => Err("Out of device memory"),
vk::Result::ERROR_OUT_OF_HOST_MEMORY => Err("Out of host memory"),
vk::Result::ERROR_UNKNOWN => Err("Unknown error"),
vk::Result::ERROR_VALIDATION_FAILED_EXT => Err("Validation failed"),
_ => Err("Unknown error"),
},
Ok(instance) => Ok(instance),
}?;
let mut debug_data = Box::new(DebugCallbackData {
error_count: AtomicU64::new(0),
error_log_function: settings.debug_log_function.unwrap_or(|message| {
println!("{}", message);
}),
});
let (debug_utils, debug_utils_messenger) = if settings.validation {
let debug_utils = ash::ext::debug_utils::Instance::new(&entry, &instance);
let debug_utils_create_info = vk::DebugUtilsMessengerCreateInfoEXT::default()
.message_severity(
vk::DebugUtilsMessageSeverityFlagsEXT::INFO
| vk::DebugUtilsMessageSeverityFlagsEXT::WARNING
| vk::DebugUtilsMessageSeverityFlagsEXT::ERROR,
)
.message_type(
vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
| vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION
| vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE,
)
.pfn_user_callback(Some(vulkan_debug_utils_callback))
.user_data(debug_data.as_mut() as *mut DebugCallbackData as *mut std::ffi::c_void);
let debug_utils_messenger = unsafe {
debug_utils
.create_debug_utils_messenger(&debug_utils_create_info, None)
.or(Err("Failed to enable debug utils messanger"))?
};
(Some(debug_utils), Some(debug_utils_messenger))
} else {
(None, None)
};
Ok(Instance {
instance,
entry,
debug_utils,
debug_utils_messenger,
debug_data,
})
}
pub fn create_device(
&mut self,
settings: crate::device::Features,
queues: &mut [(
graphics_hardware_interface::QueueSelection,
&mut Option<graphics_hardware_interface::QueueHandle>,
)],
) -> Result<crate::vulkan::Device, &'static str> {
crate::vulkan::Device::new(settings, &self, queues)
}
}
impl Drop for Instance {
fn drop(&mut self) {
unsafe {
if let Some(debug_utils) = &self.debug_utils {
if let Some(messenger) = self.debug_utils_messenger {
debug_utils.destroy_debug_utils_messenger(messenger, None);
}
}
self.instance.destroy_instance(None);
}
}
}
unsafe extern "system" fn vulkan_debug_utils_callback(
message_severity: vk::DebugUtilsMessageSeverityFlagsEXT,
_message_type: vk::DebugUtilsMessageTypeFlagsEXT,
p_callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT,
p_user_data: *mut std::ffi::c_void,
) -> vk::Bool32 {
let callback_data = if let Some(callback_data) = p_callback_data.as_ref() {
callback_data
} else {
return vk::FALSE;
};
if callback_data.p_message.is_null() {
return vk::FALSE;
}
let message = std::ffi::CStr::from_ptr(callback_data.p_message);
let message = if let Some(message) = message.to_str().ok() {
message
} else {
return vk::FALSE;
};
let user_data = if let Some(p_user_data) = (p_user_data as *mut DebugCallbackData).as_mut() {
p_user_data
} else {
return vk::FALSE;
};
match message_severity {
vk::DebugUtilsMessageSeverityFlagsEXT::INFO => {
}
vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => {
println!("{}", message);
}
vk::DebugUtilsMessageSeverityFlagsEXT::ERROR => {
(user_data.error_log_function)(message);
user_data.error_count.fetch_add(10, Ordering::SeqCst);
}
_ => {}
}
vk::FALSE
}