Introduction
Vulkan is designed to be a minimalistic and highly performant API. As a result, there is almost no errro checking in the APIs by default. But, we can add validations if we want. We do it by adding Valudation Layers
.
Suggestion
For performance reason, it is advisable to have error/debug validation layers only in development environment.
Add validation Layers
First of all, import ext::debug_utils,
from ash crate
and os::raw::{c_char, c_void}
from std
.
use ash::{vk, Entry, Instance, ext::debug_utils};
Add a new crate called log
with, cargo add log
.
Then, add the following at a convenient place.
pub const ENABLE_VALIDATION_LAYERS: bool = true;
const REQUIRED_LAYERS: [&str; 1] = ["VK_LAYER_KHRONOS_validation"];
Now, in create_instance
function, add the following code at appropriate place.
if ENABLE_VALIDATION_LAYERS {
extension_names.push(debug_utils::NAME.as_ptr());
}
Create a function called get_layer_names_and_pointers
, which returns a tuple containing layer names, and layer names pointers in comptible format.
pub fn get_layer_names_and_pointers() -> (Vec<CString>, Vec<*const c_char>) {
let layer_names = REQUIRED_LAYERS
.iter()
.map(|name| CString::new(*name).unwrap())
.collect::<Vec<_>>();
let layer_names_ptrs = layer_names
.iter()
.map(|name| name.as_ptr())
.collect::<Vec<_>>();
(layer_names, layer_names_ptrs)
}
Also, add another function that will check whether the debug layer, which we want to enable, is available or not.
pub fn check_validation_layer_support(entry: &Entry) {
let supported_layers = unsafe { entry.enumerate_instance_layer_properties().unwrap() };
for required in REQUIRED_LAYERS.iter() {
let found = supported_layers.iter().any(|layer| {
let name = unsafe { CStr::from_ptr(layer.layer_name.as_ptr()) };
let name = name.to_str().expect("Failed to get layer name pointer");
required == &name
});
if !found {
panic!("Validation layer not supported: {}", required);
}
}
}
Now, create a callback function, that will be called from the validation layers
unsafe extern "system" fn vulkan_debug_callback(
flag: vk::DebugUtilsMessageSeverityFlagsEXT,
typ: vk::DebugUtilsMessageTypeFlagsEXT,
p_callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT,
_: *mut c_void,
) -> vk::Bool32 {
use vk::DebugUtilsMessageSeverityFlagsEXT as Flag;
let message = CStr::from_ptr((*p_callback_data).p_message);
match flag {
Flag::VERBOSE => log::debug!("{:?} - {:?}", typ, message),
Flag::INFO => log::info!("{:?} - {:?}", typ, message),
Flag::WARNING => log::warn!("{:?} - {:?}", typ, message),
_ => log::error!("{:?} - {:?}", typ, message),
}
vk::FALSE
}
Now, create a function that will actually setup debug messanger.
/// Setup the debug message if validation layers are enabled.
pub fn setup_debug_messenger(
entry: &Entry,
instance: &Instance,
) -> Option<(debug_utils::Instance, vk::DebugUtilsMessengerEXT)> {
if !ENABLE_VALIDATION_LAYERS {
return None;
}
let create_info = vk::DebugUtilsMessengerCreateInfoEXT::default()
.flags(vk::DebugUtilsMessengerCreateFlagsEXT::empty())
.message_severity(
vk::DebugUtilsMessageSeverityFlagsEXT::ERROR
| vk::DebugUtilsMessageSeverityFlagsEXT::WARNING
| vk::DebugUtilsMessageSeverityFlagsEXT::INFO,
)
.message_type(
vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
| vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION
| vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE,
)
.pfn_user_callback(Some(vulkan_debug_callback));
let debug_utils = debug_utils::Instance::new(entry, instance);
let debug_utils_messenger = unsafe {
debug_utils
.create_debug_utils_messenger(&create_info, None)
.unwrap()
};
Some((debug_utils, debug_utils_messenger))
}
Finally, its time to enable the validation layer. Update instance createation code with the following snippet.
let (_layer_names, layer_names_ptrs) = get_layer_names_and_pointers();
let mut instance_create_info = vk::InstanceCreateInfo::default()
.application_info(&app_info)
.enabled_extension_names(&extension_names)
.flags(create_flags);
if ENABLE_VALIDATION_LAYERS {
check_validation_layer_support(entry);
instance_create_info = instance_create_info.enabled_layer_names(&layer_names_ptrs);
}
unsafe { entry.create_instance(&instance_create_info, None).unwrap() }
If everything went well, then you should now be able to create vulkan instance with dvalidation layers enabled.
Please find the 🔗 final code on github