117 lines
4 KiB
Rust
117 lines
4 KiB
Rust
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
use crate::error::RendererError;
|
|
use ash::{Entry, Instance, ext, vk};
|
|
use std::ffi::c_char;
|
|
// `CStr` and the tracing macros are used only by the debug-build validation callback.
|
|
#[cfg(debug_assertions)]
|
|
use std::ffi::CStr;
|
|
#[cfg(debug_assertions)]
|
|
use tracing::{debug, error, info, warn};
|
|
|
|
/// Creates a Vulkan instance and optionally a debug messenger.
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// Returns [`RendererError::VulkanError`] if instance creation fails, or if the debug messenger cannot be created in debug builds.
|
|
pub fn create_instance(
|
|
entry: &Entry,
|
|
required_extensions: &[*const c_char],
|
|
) -> Result<
|
|
(
|
|
Instance,
|
|
Option<ext::debug_utils::Instance>,
|
|
vk::DebugUtilsMessengerEXT,
|
|
),
|
|
RendererError,
|
|
> {
|
|
// The validation extension and layer are pushed only in debug builds, so in release builds these vectors are never mutated after initialization.
|
|
#[cfg_attr(
|
|
not(debug_assertions),
|
|
expect(
|
|
unused_mut,
|
|
reason = "the debug-only block below mutates these vectors"
|
|
)
|
|
)]
|
|
let mut extensions = required_extensions.to_vec();
|
|
#[cfg_attr(
|
|
not(debug_assertions),
|
|
expect(
|
|
unused_mut,
|
|
reason = "the debug-only block below mutates these vectors"
|
|
)
|
|
)]
|
|
let mut layers = Vec::new();
|
|
|
|
#[cfg(debug_assertions)]
|
|
{
|
|
extensions.push(ext::debug_utils::NAME.as_ptr());
|
|
layers.push(c"VK_LAYER_KHRONOS_validation".as_ptr());
|
|
}
|
|
|
|
let app_info = vk::ApplicationInfo::default().api_version(vk::API_VERSION_1_3);
|
|
|
|
let create_info = vk::InstanceCreateInfo::default()
|
|
.application_info(&app_info)
|
|
.enabled_extension_names(&extensions)
|
|
.enabled_layer_names(&layers);
|
|
|
|
let instance = unsafe { entry.create_instance(&create_info, None)? };
|
|
|
|
#[cfg(debug_assertions)]
|
|
let (debug_utils, debug_messenger) = {
|
|
let debug_info = vk::DebugUtilsMessengerCreateInfoEXT::default()
|
|
.message_severity(
|
|
vk::DebugUtilsMessageSeverityFlagsEXT::WARNING
|
|
| vk::DebugUtilsMessageSeverityFlagsEXT::ERROR,
|
|
)
|
|
.message_type(
|
|
vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
|
|
| vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION
|
|
| vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE,
|
|
)
|
|
.pfn_user_callback(Some(vulkan_debug_callback));
|
|
|
|
let utils = ext::debug_utils::Instance::new(entry, &instance);
|
|
let messenger = unsafe { utils.create_debug_utils_messenger(&debug_info, None)? };
|
|
|
|
(Some(utils), messenger)
|
|
};
|
|
|
|
#[cfg(not(debug_assertions))]
|
|
let (debug_utils, debug_messenger) = (None, vk::DebugUtilsMessengerEXT::null());
|
|
|
|
Ok((instance, debug_utils, debug_messenger))
|
|
}
|
|
|
|
/// The callback function invoked by Vulkan's validation layers.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// Invoked by the Vulkan loader, which must pass a valid `p_callback_data` pointer whose `p_message` is either null or a valid NUL-terminated C string. Not to be called directly.
|
|
#[cfg(debug_assertions)]
|
|
unsafe extern "system" fn vulkan_debug_callback(
|
|
message_severity: vk::DebugUtilsMessageSeverityFlagsEXT,
|
|
_message_type: vk::DebugUtilsMessageTypeFlagsEXT,
|
|
p_callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT<'_>,
|
|
_user_data: *mut std::ffi::c_void,
|
|
) -> vk::Bool32 {
|
|
let callback_data = unsafe { *p_callback_data };
|
|
|
|
let message = if callback_data.p_message.is_null() {
|
|
"".into()
|
|
} else {
|
|
unsafe { CStr::from_ptr(callback_data.p_message).to_string_lossy() }
|
|
};
|
|
|
|
match message_severity {
|
|
vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => debug!("{message}"),
|
|
vk::DebugUtilsMessageSeverityFlagsEXT::INFO => info!("{message}"),
|
|
vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => warn!("{message}"),
|
|
vk::DebugUtilsMessageSeverityFlagsEXT::ERROR => error!("{message}"),
|
|
_ => info!("{message}"),
|
|
}
|
|
|
|
vk::FALSE
|
|
}
|