use alloc::{borrow::ToOwned as _, boxed::Box, ffi::CString, string::String, sync::Arc, vec::Vec}; use core::{
ffi::{c_void, CStr},
marker::PhantomData,
slice,
str::FromStr,
}; use std::thread;
use arrayvec::ArrayVec; use ash::{ext, khr, vk}; use parking_lot::RwLock;
let cd = unsafe { &*callback_data_ptr }; let user_data = unsafe { &*user_data.cast::<super::DebugUtilsMessengerUserData>() };
const VUID_VKCMDENDDEBUGUTILSLABELEXT_COMMANDBUFFER_01912: i32 = 0x56146426; if cd.message_id_number == VUID_VKCMDENDDEBUGUTILSLABELEXT_COMMANDBUFFER_01912 { // https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/5671 // Versions 1.3.240 through 1.3.250 return a spurious error here if // the debug range start and end appear in different command buffers. iflet Some(layer_properties) = user_data.validation_layer_properties.as_ref() { if layer_properties.layer_description.as_ref() == c"Khronos Validation Layer"
&& layer_properties.layer_spec_version >= vk::make_api_version(0, 1, 3, 240)
&& layer_properties.layer_spec_version <= vk::make_api_version(0, 1, 3, 250)
{ return vk::FALSE;
}
}
}
// Silence Vulkan Validation error "VUID-VkSwapchainCreateInfoKHR-pNext-07781" // This happens when a surface is configured with a size outside the allowed extent. // It's a false positive due to the inherent racy-ness of surface resizing. const VUID_VKSWAPCHAINCREATEINFOKHR_PNEXT_07781: i32 = 0x4c8929c1; if cd.message_id_number == VUID_VKSWAPCHAINCREATEINFOKHR_PNEXT_07781 { return vk::FALSE;
}
// Silence Vulkan Validation error "VUID-VkRenderPassBeginInfo-framebuffer-04627" // if the OBS layer is enabled. This is a bug in the OBS layer. As the OBS layer // does not have a version number they increment, there is no way to qualify the // suppression of the error to a specific version of the OBS layer. // // See https://github.com/obsproject/obs-studio/issues/9353 const VUID_VKRENDERPASSBEGININFO_FRAMEBUFFER_04627: i32 = 0x45125641; if cd.message_id_number == VUID_VKRENDERPASSBEGININFO_FRAMEBUFFER_04627
&& user_data.has_obs_layer
{ return vk::FALSE;
}
// Silence Vulkan Validation error "VUID-vkCmdCopyImageToBuffer-pRegions-00184". // While we aren't sure yet, we suspect this is probably a VVL issue. // https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/9276 const VUID_VKCMDCOPYIMAGETOBUFFER_PREGIONS_00184: i32 = 0x45ef177c; if cd.message_id_number == VUID_VKCMDCOPYIMAGETOBUFFER_PREGIONS_00184 { return vk::FALSE;
}
// Silence Vulkan Validation error "VUID-StandaloneSpirv-None-10684". // // This is a bug. To prevent massive noise in the tests, lets suppress it for now. // https://github.com/gfx-rs/wgpu/issues/7696 const VUID_STANDALONESPIRV_NONE_10684: i32 = 0xb210f7c2_u32 as i32; if cd.message_id_number == VUID_STANDALONESPIRV_NONE_10684 { return vk::FALSE;
}
let level = match message_severity { // We intentionally suppress info messages down to debug // so that users are not innundated with info messages from the runtime.
vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => log::Level::Trace,
vk::DebugUtilsMessageSeverityFlagsEXT::INFO => log::Level::Debug,
vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => log::Level::Warn,
vk::DebugUtilsMessageSeverityFlagsEXT::ERROR => log::Level::Error,
_ => log::Level::Warn,
};
let message_id_name = unsafe { cd.message_id_name_as_c_str() }.map_or(Cow::Borrowed(""), CStr::to_string_lossy); let message = unsafe { cd.message_as_c_str() }.map_or(Cow::Borrowed(""), CStr::to_string_lossy);
if cd.object_count != 0 { let labels = unsafe { slice::from_raw_parts(cd.p_objects, cd.object_count as usize) }; //TODO: use color fields of `vk::DebugUtilsLabelExt`? let names = labels
.iter()
.map(|obj_info| { let name = unsafe { obj_info.object_name_as_c_str() }
.map_or(Cow::Borrowed("?"), CStr::to_string_lossy);
/// Return the instance extension names wgpu would like to enable. /// /// Return a vector of the names of instance extensions actually available /// on `entry` that wgpu would like to enable. /// /// The `instance_api_version` argument should be the instance's Vulkan API /// version, as obtained from `vkEnumerateInstanceVersion`. This is the same /// space of values as the `VK_API_VERSION` constants. /// /// Note that wgpu can function without many of these extensions (for /// example, `VK_KHR_wayland_surface` is certainly not going to be available /// everywhere), but if one of these extensions is available at all, wgpu /// assumes that it has been enabled. pubfn desired_extensions(
entry: &ash::Entry,
_instance_api_version: u32,
flags: wgt::InstanceFlags,
) -> Result<Vec<&'static CStr>, crate::InstanceError> { let instance_extensions = Self::enumerate_instance_extension_properties(entry, None)?;
// Check our extensions against the available extensions letmut extensions: Vec<&'static CStr> = Vec::new();
if flags.contains(wgt::InstanceFlags::DEBUG) { // VK_EXT_debug_utils
extensions.push(ext::debug_utils::NAME);
}
// VK_EXT_swapchain_colorspace // Provides wide color gamut
extensions.push(ext::swapchain_colorspace::NAME);
// VK_KHR_get_physical_device_properties2 // Even though the extension was promoted to Vulkan 1.1, we still require the extension // so that we don't have to conditionally use the functions provided by the 1.1 instance
extensions.push(khr::get_physical_device_properties2::NAME);
// Only keep available extensions.
extensions.retain(|&ext| { if instance_extensions
.iter()
.any(|inst_ext| inst_ext.extension_name_as_c_str() == Ok(ext))
{ true
} else {
log::debug!("Unable to find extension: {}", ext.to_string_lossy()); false
}
});
Ok(extensions)
}
/// # Safety /// /// - `raw_instance` must be created from `entry` /// - `raw_instance` must be created respecting `instance_api_version`, `extensions` and `flags` /// - `extensions` must be a superset of `desired_extensions()` and must be created from the /// same entry, `instance_api_version`` and flags. /// - `android_sdk_version` is ignored and can be `0` for all platforms besides Android /// - If `drop_callback` is [`None`], wgpu-hal will take ownership of `raw_instance`. If /// `drop_callback` is [`Some`], `raw_instance` must be valid until the callback is called. /// /// If `debug_utils_user_data` is `Some`, then the validation layer is /// available, so create a [`vk::DebugUtilsMessengerEXT`]. #[allow(clippy::too_many_arguments)] pubunsafefn from_raw(
entry: ash::Entry,
raw_instance: ash::Instance,
instance_api_version: u32,
android_sdk_version: u32,
debug_utils_create_info: Option<super::DebugUtilsCreateInfo>,
extensions: Vec<&'static CStr>,
flags: wgt::InstanceFlags,
memory_budget_thresholds: wgt::MemoryBudgetThresholds,
has_nv_optimus: bool,
drop_callback: Option<crate::DropCallback>,
) -> Result<Self, crate::InstanceError> {
log::debug!("Instance version: 0x{instance_api_version:x}");
let debug_utils = iflet Some(debug_utils_create_info) = debug_utils_create_info { if extensions.contains(&ext::debug_utils::NAME) {
log::debug!("Enabling debug utils");
let extension = ext::debug_utils::Instance::new(&entry, &raw_instance); let vk_info = debug_utils_create_info.to_vk_create_info(); let messenger = unsafe { extension.create_debug_utils_messenger(&vk_info, None) }.unwrap();
Some(super::DebugUtils {
extension,
messenger,
callback_data: debug_utils_create_info.callback_data,
})
} else {
log::debug!("Debug utils not enabled: extension not listed");
None
}
} else {
log::debug!( "Debug utils not enabled: \
debug_utils_user_data not passed to Instance::from_raw"
);
None
};
let get_physical_device_properties = if extensions.contains(&khr::get_physical_device_properties2::NAME) {
log::debug!("Enabling device properties2");
Some(khr::get_physical_device_properties2::Instance::new(
&entry,
&raw_instance,
))
} else {
None
};
let drop_guard = crate::DropGuard::from_option(drop_callback);
fn create_surface_from_xlib(
&self,
dpy: *mut vk::Display,
window: vk::Window,
) -> Result<super::Surface, crate::InstanceError> { if !self.shared.extensions.contains(&khr::xlib_surface::NAME) { return Err(crate::InstanceError::new(String::from( "Vulkan driver does not support VK_KHR_xlib_surface",
)));
}
let surface = { let xlib_loader =
khr::xlib_surface::Instance::new(&self.shared.entry, &tyle='color:red'>self.shared.raw); let info = vk::XlibSurfaceCreateInfoKHR::default()
.flags(vk::XlibSurfaceCreateFlagsKHR::empty())
.window(window)
.dpy(dpy);
fn create_surface_from_xcb(
&self,
connection: *mut vk::xcb_connection_t,
window: vk::xcb_window_t,
) -> Result<super::Surface, crate::InstanceError> { if !self.shared.extensions.contains(&khr::xcb_surface::NAME) { return Err(crate::InstanceError::new(String::from( "Vulkan driver does not support VK_KHR_xcb_surface",
)));
}
let surface = { let xcb_loader = khr::xcb_surface::Instance::new(&self.shared.entry, &self.shared.raw); let info = vk::XcbSurfaceCreateInfoKHR::default()
.flags(vk::XcbSurfaceCreateFlagsKHR::empty())
.window(window)
.connection(connection);
fn create_surface_from_wayland(
&self,
display: *mut vk::wl_display,
surface: *mut vk::wl_surface,
) -> Result<super::Surface, crate::InstanceError> { if !self.shared.extensions.contains(&khr::wayland_surface::NAME) { return Err(crate::InstanceError::new(String::from( "Vulkan driver does not support VK_KHR_wayland_surface",
)));
}
let surface = { let w_loader =
khr::wayland_surface::Instance::new(&self.shared.entry, &n style='color:red'>self.shared.raw); let info = vk::WaylandSurfaceCreateInfoKHR::default()
.flags(vk::WaylandSurfaceCreateFlagsKHR::empty())
.display(display)
.surface(surface);
fn create_surface_android(
&self,
window: *mut vk::ANativeWindow,
) -> Result<super::Surface, crate::InstanceError> { if !self.shared.extensions.contains(&khr::android_surface::NAME) { return Err(crate::InstanceError::new(String::from( "Vulkan driver does not support VK_KHR_android_surface",
)));
}
let surface = { let a_loader =
khr::android_surface::Instance::new(&self.shared.entry, &n style='color:red'>self.shared.raw); let info = vk::AndroidSurfaceCreateInfoKHR::default()
.flags(vk::AndroidSurfaceCreateFlagsKHR::empty())
.window(window);
#[cfg(target_vendor = "apple")] fn create_surface_from_layer(
&self,
layer: raw_window_metal::Layer,
) -> Result<super::Surface, crate::InstanceError> { if !self.shared.extensions.contains(&ext::metal_surface::NAME) { return Err(crate::InstanceError::new(String::from( "Vulkan driver does not support VK_EXT_metal_surface",
)));
}
// NOTE: The layer is retained by Vulkan's `vkCreateMetalSurfaceEXT`, // so no need to retain it beyond the scope of this function. let surface = { let metal_loader =
ext::metal_surface::Instance::new(&self.shared.entry, &style='color:red'>self.shared.raw); let vk_info = vk::MetalSurfaceCreateInfoEXT::default()
.flags(vk::MetalSurfaceCreateFlagsEXT::empty())
.layer(layer.as_ptr().as_ptr());
/// `Instance::init` but with a callback. /// If you want to add extensions, add the to the `Vec<'static CStr>` not the create info, otherwise /// it will be overwritten /// /// # Safety: /// Same as `init` but additionally /// - Callback must not remove features. /// - Callback must not change anything to what the instance does not support. pubunsafefn init_with_callback(
desc: &crate::InstanceDescriptor<'_>,
callback: Option<Box<super::CreateInstanceCallback>>,
) -> Result<Self, crate::InstanceError> {
profiling::scope!("Init Vulkan Backend");
let entry = unsafe {
profiling::scope!("Load vk library");
ash::Entry::load()
}
.map_err(|err| { crate::InstanceError::with_source(String::from("missing Vulkan entry points"), err)
})?; let version = {
profiling::scope!("vkEnumerateInstanceVersion"); unsafe { entry.try_enumerate_instance_version() }
}; let instance_api_version = match version { // Vulkan 1.1+
Ok(Some(version)) => version,
Ok(None) => vk::API_VERSION_1_0,
Err(err) => { return Err(crate::InstanceError::with_source(
String::from("try_enumerate_instance_version() failed"),
err,
));
}
};
let app_name = CString::new(desc.name).unwrap(); let app_info = vk::ApplicationInfo::default()
.application_name(app_name.as_c_str())
.application_version(1)
.engine_name(c"wgpu-hal")
.engine_version(2)
.api_version( // Vulkan 1.0 doesn't like anything but 1.0 passed in here... if instance_api_version < vk::API_VERSION_1_1 {
vk::API_VERSION_1_0
} else { // This is the max Vulkan API version supported by `wgpu-hal`. // // If we want to increment this, there are some things that must be done first: // - Audit the behavioral differences between the previous and new API versions. // - Audit all extensions used by this backend: // - If any were promoted in the new API version and the behavior has changed, we must handle the new behavior in addition to the old behavior. // - If any were obsoleted in the new API version, we must implement a fallback for the new API version // - If any are non-KHR-vendored, we must ensure the new behavior is still correct (since backwards-compatibility is not guaranteed).
vk::API_VERSION_1_3
},
);
let validation_layer_name = c"VK_LAYER_KHRONOS_validation"; let validation_layer_properties = find_layer(&instance_layers, validation_layer_name);
// Determine if VK_EXT_validation_features is available, so we can enable // GPU assisted validation and synchronization validation. let validation_features_are_enabled = if validation_layer_properties.is_some() { // Get the all the instance extension properties. let exts = Self::enumerate_instance_extension_properties(&entry, Some(validation_layer_name))?; // Convert all the names of the extensions into an iterator of CStrs. letmut ext_names = exts
.iter()
.filter_map(|ext| ext.extension_name_as_c_str().ok()); // Find the validation features extension.
ext_names.any(|ext_name| ext_name == ext::validation_features::NAME)
} else { false
};
let should_enable_gpu_based_validation = desc
.flags
.intersects(wgt::InstanceFlags::GPU_BASED_VALIDATION)
&& validation_features_are_enabled;
let has_nv_optimus = find_layer(&instance_layers, c"VK_LAYER_NV_optimus").is_some();
let has_obs_layer = find_layer(&instance_layers, c"VK_LAYER_OBS_HOOK").is_some();
letmut layers: Vec<&'static CStr> = Vec::new();
let has_debug_extension = extensions.contains(&ext::debug_utils::NAME); letmut debug_user_data = has_debug_extension.then(|| { // Put the callback data on the heap, to ensure it will never be // moved. Box::new(super::DebugUtilsMessengerUserData {
validation_layer_properties: None,
has_obs_layer,
})
});
// Request validation layer if asked. if desc.flags.intersects(wgt::InstanceFlags::VALIDATION)
|| should_enable_gpu_based_validation
{ iflet Some(layer_properties) = validation_layer_properties {
layers.push(validation_layer_name);
// Avoid VUID-VkInstanceCreateInfo-flags-06559: Only ask the instance to // enumerate incomplete Vulkan implementations (which we need on Mac) if // we managed to find the extension that provides the flag. if extensions.contains(&khr::portability_enumeration::NAME) {
flags |= vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR;
} let vk_instance = { let str_pointers = layers
.iter()
.chain(extensions.iter())
.map(|&s: &&'static _| { // Safe because `layers` and `extensions` entries have static lifetime.
s.as_ptr()
})
.collect::<Vec<_>>();
// Enable explicit validation features if available letmut validation_features; letmut validation_feature_list: ArrayVec<_, 3>; if validation_features_are_enabled {
validation_feature_list = ArrayVec::new();
// Only enable GPU assisted validation if requested. if should_enable_gpu_based_validation {
validation_feature_list.push(vk::ValidationFeatureEnableEXT::GPU_ASSISTED);
validation_feature_list
.push(vk::ValidationFeatureEnableEXT::GPU_ASSISTED_RESERVE_BINDING_SLOT);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.