diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/rust/wgpu-hal/src/vulkan/instance.rs | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/wgpu-hal/src/vulkan/instance.rs')
-rw-r--r-- | third_party/rust/wgpu-hal/src/vulkan/instance.rs | 1011 |
1 files changed, 1011 insertions, 0 deletions
diff --git a/third_party/rust/wgpu-hal/src/vulkan/instance.rs b/third_party/rust/wgpu-hal/src/vulkan/instance.rs new file mode 100644 index 0000000000..c4ef573461 --- /dev/null +++ b/third_party/rust/wgpu-hal/src/vulkan/instance.rs @@ -0,0 +1,1011 @@ +use std::{ + ffi::{c_void, CStr, CString}, + slice, + str::FromStr, + sync::Arc, + thread, +}; + +use ash::{ + extensions::{ext, khr}, + vk, +}; +use parking_lot::RwLock; + +unsafe extern "system" fn debug_utils_messenger_callback( + message_severity: vk::DebugUtilsMessageSeverityFlagsEXT, + message_type: vk::DebugUtilsMessageTypeFlagsEXT, + callback_data_ptr: *const vk::DebugUtilsMessengerCallbackDataEXT, + user_data: *mut c_void, +) -> vk::Bool32 { + use std::borrow::Cow; + + if thread::panicking() { + return vk::FALSE; + } + + let cd = unsafe { &*callback_data_ptr }; + let user_data = unsafe { &*(user_data as *mut 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. + let khronos_validation_layer = + std::ffi::CStr::from_bytes_with_nul(b"Khronos Validation Layer\0").unwrap(); + if user_data.validation_layer_description.as_ref() == khronos_validation_layer + && user_data.validation_layer_spec_version >= vk::make_api_version(0, 1, 3, 240) + && user_data.validation_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 s 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; + } + + let level = match message_severity { + vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => log::Level::Debug, + vk::DebugUtilsMessageSeverityFlagsEXT::INFO => log::Level::Info, + vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => log::Level::Warn, + vk::DebugUtilsMessageSeverityFlagsEXT::ERROR => log::Level::Error, + _ => log::Level::Warn, + }; + + let message_id_name = if cd.p_message_id_name.is_null() { + Cow::from("") + } else { + unsafe { CStr::from_ptr(cd.p_message_id_name) }.to_string_lossy() + }; + let message = if cd.p_message.is_null() { + Cow::from("") + } else { + unsafe { CStr::from_ptr(cd.p_message) }.to_string_lossy() + }; + + let _ = std::panic::catch_unwind(|| { + log::log!( + level, + "{:?} [{} (0x{:x})]\n\t{}", + message_type, + message_id_name, + cd.message_id_number, + message, + ); + }); + + if cd.queue_label_count != 0 { + let labels = + unsafe { slice::from_raw_parts(cd.p_queue_labels, cd.queue_label_count as usize) }; + let names = labels + .iter() + .flat_map(|dul_obj| { + unsafe { dul_obj.p_label_name.as_ref() } + .map(|lbl| unsafe { CStr::from_ptr(lbl) }.to_string_lossy()) + }) + .collect::<Vec<_>>(); + + let _ = std::panic::catch_unwind(|| { + log::log!(level, "\tqueues: {}", names.join(", ")); + }); + } + + if cd.cmd_buf_label_count != 0 { + let labels = + unsafe { slice::from_raw_parts(cd.p_cmd_buf_labels, cd.cmd_buf_label_count as usize) }; + let names = labels + .iter() + .flat_map(|dul_obj| { + unsafe { dul_obj.p_label_name.as_ref() } + .map(|lbl| unsafe { CStr::from_ptr(lbl) }.to_string_lossy()) + }) + .collect::<Vec<_>>(); + + let _ = std::panic::catch_unwind(|| { + log::log!(level, "\tcommand buffers: {}", names.join(", ")); + }); + } + + 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.p_object_name.as_ref() } + .map(|name| unsafe { CStr::from_ptr(name) }.to_string_lossy()) + .unwrap_or(Cow::Borrowed("?")); + + format!( + "(type: {:?}, hndl: 0x{:x}, name: {})", + obj_info.object_type, obj_info.object_handle, name + ) + }) + .collect::<Vec<_>>(); + let _ = std::panic::catch_unwind(|| { + log::log!(level, "\tobjects: {}", names.join(", ")); + }); + } + + if cfg!(debug_assertions) && level == log::Level::Error { + // Set canary and continue + crate::VALIDATION_CANARY.add(message.to_string()); + } + + vk::FALSE +} + +impl super::DebugUtilsCreateInfo { + fn to_vk_create_info(&self) -> vk::DebugUtilsMessengerCreateInfoEXTBuilder<'_> { + let user_data_ptr: *const super::DebugUtilsMessengerUserData = &*self.callback_data; + vk::DebugUtilsMessengerCreateInfoEXT::builder() + .message_severity(self.severity) + .message_type(self.message_type) + .user_data(user_data_ptr as *mut _) + .pfn_user_callback(Some(debug_utils_messenger_callback)) + } +} + +impl super::Swapchain { + /// # Safety + /// + /// - The device must have been made idle before calling this function. + unsafe fn release_resources(mut self, device: &ash::Device) -> Self { + profiling::scope!("Swapchain::release_resources"); + { + profiling::scope!("vkDeviceWaitIdle"); + // We need to also wait until all presentation work is done. Because there is no way to portably wait until + // the presentation work is done, we are forced to wait until the device is idle. + let _ = unsafe { device.device_wait_idle() }; + }; + + for semaphore in self.surface_semaphores.drain(..) { + unsafe { + device.destroy_semaphore(semaphore, None); + } + } + + self + } +} + +impl super::InstanceShared { + pub fn entry(&self) -> &ash::Entry { + &self.entry + } + + pub fn raw_instance(&self) -> &ash::Instance { + &self.raw + } + + pub fn instance_api_version(&self) -> u32 { + self.instance_api_version + } + + pub fn extensions(&self) -> &[&'static CStr] { + &self.extensions[..] + } +} + +impl super::Instance { + pub fn shared_instance(&self) -> &super::InstanceShared { + &self.shared + } + + /// 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. + pub fn desired_extensions( + entry: &ash::Entry, + _instance_api_version: u32, + flags: wgt::InstanceFlags, + ) -> Result<Vec<&'static CStr>, crate::InstanceError> { + let instance_extensions = { + profiling::scope!("vkEnumerateInstanceExtensionProperties"); + entry.enumerate_instance_extension_properties(None) + }; + let instance_extensions = instance_extensions.map_err(|e| { + crate::InstanceError::with_source( + String::from("enumerate_instance_extension_properties() failed"), + e, + ) + })?; + + // Check our extensions against the available extensions + let mut extensions: Vec<&'static CStr> = Vec::new(); + + // VK_KHR_surface + extensions.push(khr::Surface::name()); + + // Platform-specific WSI extensions + if cfg!(all( + unix, + not(target_os = "android"), + not(target_os = "macos") + )) { + // VK_KHR_xlib_surface + extensions.push(khr::XlibSurface::name()); + // VK_KHR_xcb_surface + extensions.push(khr::XcbSurface::name()); + // VK_KHR_wayland_surface + extensions.push(khr::WaylandSurface::name()); + } + if cfg!(target_os = "android") { + // VK_KHR_android_surface + extensions.push(khr::AndroidSurface::name()); + } + if cfg!(target_os = "windows") { + // VK_KHR_win32_surface + extensions.push(khr::Win32Surface::name()); + } + if cfg!(target_os = "macos") { + // VK_EXT_metal_surface + extensions.push(ext::MetalSurface::name()); + extensions.push(ash::vk::KhrPortabilityEnumerationFn::name()); + } + + if flags.contains(wgt::InstanceFlags::DEBUG) { + // VK_EXT_debug_utils + extensions.push(ext::DebugUtils::name()); + } + + // VK_EXT_swapchain_colorspace + // Provides wide color gamut + extensions.push(vk::ExtSwapchainColorspaceFn::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(vk::KhrGetPhysicalDeviceProperties2Fn::name()); + + // Only keep available extensions. + extensions.retain(|&ext| { + if instance_extensions.iter().any(|inst_ext| { + crate::auxil::cstr_from_bytes_until_nul(&inst_ext.extension_name) == Some(ext) + }) { + true + } else { + log::warn!("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 `debug_utils_user_data` is `Some`, then the validation layer is + /// available, so create a [`vk::DebugUtilsMessengerEXT`]. + #[allow(clippy::too_many_arguments)] + pub unsafe fn 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, + has_nv_optimus: bool, + drop_guard: Option<crate::DropGuard>, + ) -> Result<Self, crate::InstanceError> { + log::debug!("Instance version: 0x{:x}", instance_api_version); + + let debug_utils = if let Some(debug_utils_create_info) = debug_utils_create_info { + if extensions.contains(&ext::DebugUtils::name()) { + log::info!("Enabling debug utils"); + + let extension = ext::DebugUtils::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::info!("Debug utils not enabled: extension not listed"); + None + } + } else { + log::info!( + "Debug utils not enabled: \ + debug_utils_user_data not passed to Instance::from_raw" + ); + None + }; + + let get_physical_device_properties = + if extensions.contains(&khr::GetPhysicalDeviceProperties2::name()) { + log::debug!("Enabling device properties2"); + Some(khr::GetPhysicalDeviceProperties2::new( + &entry, + &raw_instance, + )) + } else { + None + }; + + Ok(Self { + shared: Arc::new(super::InstanceShared { + raw: raw_instance, + extensions, + drop_guard, + flags, + debug_utils, + get_physical_device_properties, + entry, + has_nv_optimus, + instance_api_version, + android_sdk_version, + }), + }) + } + + #[allow(dead_code)] + fn create_surface_from_xlib( + &self, + dpy: *mut vk::Display, + window: vk::Window, + ) -> Result<super::Surface, crate::InstanceError> { + if !self.shared.extensions.contains(&khr::XlibSurface::name()) { + return Err(crate::InstanceError::new(String::from( + "Vulkan driver does not support VK_KHR_xlib_surface", + ))); + } + + let surface = { + let xlib_loader = khr::XlibSurface::new(&self.shared.entry, &self.shared.raw); + let info = vk::XlibSurfaceCreateInfoKHR::builder() + .flags(vk::XlibSurfaceCreateFlagsKHR::empty()) + .window(window) + .dpy(dpy); + + unsafe { xlib_loader.create_xlib_surface(&info, None) } + .expect("XlibSurface::create_xlib_surface() failed") + }; + + Ok(self.create_surface_from_vk_surface_khr(surface)) + } + + #[allow(dead_code)] + 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::XcbSurface::name()) { + return Err(crate::InstanceError::new(String::from( + "Vulkan driver does not support VK_KHR_xcb_surface", + ))); + } + + let surface = { + let xcb_loader = khr::XcbSurface::new(&self.shared.entry, &self.shared.raw); + let info = vk::XcbSurfaceCreateInfoKHR::builder() + .flags(vk::XcbSurfaceCreateFlagsKHR::empty()) + .window(window) + .connection(connection); + + unsafe { xcb_loader.create_xcb_surface(&info, None) } + .expect("XcbSurface::create_xcb_surface() failed") + }; + + Ok(self.create_surface_from_vk_surface_khr(surface)) + } + + #[allow(dead_code)] + fn create_surface_from_wayland( + &self, + display: *mut c_void, + surface: *mut c_void, + ) -> Result<super::Surface, crate::InstanceError> { + if !self + .shared + .extensions + .contains(&khr::WaylandSurface::name()) + { + return Err(crate::InstanceError::new(String::from( + "Vulkan driver does not support VK_KHR_wayland_surface", + ))); + } + + let surface = { + let w_loader = khr::WaylandSurface::new(&self.shared.entry, &self.shared.raw); + let info = vk::WaylandSurfaceCreateInfoKHR::builder() + .flags(vk::WaylandSurfaceCreateFlagsKHR::empty()) + .display(display) + .surface(surface); + + unsafe { w_loader.create_wayland_surface(&info, None) }.expect("WaylandSurface failed") + }; + + Ok(self.create_surface_from_vk_surface_khr(surface)) + } + + #[allow(dead_code)] + fn create_surface_android( + &self, + window: *const c_void, + ) -> Result<super::Surface, crate::InstanceError> { + if !self + .shared + .extensions + .contains(&khr::AndroidSurface::name()) + { + return Err(crate::InstanceError::new(String::from( + "Vulkan driver does not support VK_KHR_android_surface", + ))); + } + + let surface = { + let a_loader = khr::AndroidSurface::new(&self.shared.entry, &self.shared.raw); + let info = vk::AndroidSurfaceCreateInfoKHR::builder() + .flags(vk::AndroidSurfaceCreateFlagsKHR::empty()) + .window(window as *mut _); + + unsafe { a_loader.create_android_surface(&info, None) }.expect("AndroidSurface failed") + }; + + Ok(self.create_surface_from_vk_surface_khr(surface)) + } + + #[allow(dead_code)] + fn create_surface_from_hwnd( + &self, + hinstance: *mut c_void, + hwnd: *mut c_void, + ) -> Result<super::Surface, crate::InstanceError> { + if !self.shared.extensions.contains(&khr::Win32Surface::name()) { + return Err(crate::InstanceError::new(String::from( + "Vulkan driver does not support VK_KHR_win32_surface", + ))); + } + + let surface = { + let info = vk::Win32SurfaceCreateInfoKHR::builder() + .flags(vk::Win32SurfaceCreateFlagsKHR::empty()) + .hinstance(hinstance) + .hwnd(hwnd); + let win32_loader = khr::Win32Surface::new(&self.shared.entry, &self.shared.raw); + unsafe { + win32_loader + .create_win32_surface(&info, None) + .expect("Unable to create Win32 surface") + } + }; + + Ok(self.create_surface_from_vk_surface_khr(surface)) + } + + #[cfg(metal)] + fn create_surface_from_view( + &self, + view: *mut c_void, + ) -> Result<super::Surface, crate::InstanceError> { + if !self.shared.extensions.contains(&ext::MetalSurface::name()) { + return Err(crate::InstanceError::new(String::from( + "Vulkan driver does not support VK_EXT_metal_surface", + ))); + } + + let layer = unsafe { + crate::metal::Surface::get_metal_layer(view as *mut objc::runtime::Object, None) + }; + + let surface = { + let metal_loader = ext::MetalSurface::new(&self.shared.entry, &self.shared.raw); + let vk_info = vk::MetalSurfaceCreateInfoEXT::builder() + .flags(vk::MetalSurfaceCreateFlagsEXT::empty()) + .layer(layer as *mut _) + .build(); + + unsafe { metal_loader.create_metal_surface(&vk_info, None).unwrap() } + }; + + Ok(self.create_surface_from_vk_surface_khr(surface)) + } + + fn create_surface_from_vk_surface_khr(&self, surface: vk::SurfaceKHR) -> super::Surface { + let functor = khr::Surface::new(&self.shared.entry, &self.shared.raw); + super::Surface { + raw: surface, + functor, + instance: Arc::clone(&self.shared), + swapchain: RwLock::new(None), + } + } +} + +impl Drop for super::InstanceShared { + fn drop(&mut self) { + unsafe { + // Keep du alive since destroy_instance may also log + let _du = self.debug_utils.take().map(|du| { + du.extension + .destroy_debug_utils_messenger(du.messenger, None); + du + }); + if let Some(_drop_guard) = self.drop_guard.take() { + self.raw.destroy_instance(None); + } + } + } +} + +impl crate::Instance<super::Api> for super::Instance { + unsafe fn init(desc: &crate::InstanceDescriptor) -> Result<Self, crate::InstanceError> { + profiling::scope!("Init Vulkan Backend"); + use crate::auxil::cstr_from_bytes_until_nul; + + 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"); + 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::builder() + .application_name(app_name.as_c_str()) + .application_version(1) + .engine_name(CStr::from_bytes_with_nul(b"wgpu-hal\0").unwrap()) + .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 extensions = Self::desired_extensions(&entry, instance_api_version, desc.flags)?; + + let instance_layers = { + profiling::scope!("vkEnumerateInstanceLayerProperties"); + entry.enumerate_instance_layer_properties() + }; + let instance_layers = instance_layers.map_err(|e| { + log::debug!("enumerate_instance_layer_properties: {:?}", e); + crate::InstanceError::with_source( + String::from("enumerate_instance_layer_properties() failed"), + e, + ) + })?; + + fn find_layer<'layers>( + instance_layers: &'layers [vk::LayerProperties], + name: &CStr, + ) -> Option<&'layers vk::LayerProperties> { + instance_layers + .iter() + .find(|inst_layer| cstr_from_bytes_until_nul(&inst_layer.layer_name) == Some(name)) + } + + let nv_optimus_layer = CStr::from_bytes_with_nul(b"VK_LAYER_NV_optimus\0").unwrap(); + let has_nv_optimus = find_layer(&instance_layers, nv_optimus_layer).is_some(); + + let obs_layer = CStr::from_bytes_with_nul(b"VK_LAYER_OBS_HOOK\0").unwrap(); + let has_obs_layer = find_layer(&instance_layers, obs_layer).is_some(); + + let mut layers: Vec<&'static CStr> = Vec::new(); + + // Request validation layer if asked. + let mut debug_utils = None; + if desc.flags.intersects(wgt::InstanceFlags::VALIDATION) { + let validation_layer_name = + CStr::from_bytes_with_nul(b"VK_LAYER_KHRONOS_validation\0").unwrap(); + if let Some(layer_properties) = find_layer(&instance_layers, validation_layer_name) { + layers.push(validation_layer_name); + + if extensions.contains(&ext::DebugUtils::name()) { + // Put the callback data on the heap, to ensure it will never be + // moved. + let callback_data = Box::new(super::DebugUtilsMessengerUserData { + validation_layer_description: cstr_from_bytes_until_nul( + &layer_properties.description, + ) + .unwrap() + .to_owned(), + validation_layer_spec_version: layer_properties.spec_version, + has_obs_layer, + }); + + // having ERROR unconditionally because Vk doesn't like empty flags + let mut severity = vk::DebugUtilsMessageSeverityFlagsEXT::ERROR; + if log::max_level() >= log::LevelFilter::Debug { + severity |= vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE; + } + if log::max_level() >= log::LevelFilter::Info { + severity |= vk::DebugUtilsMessageSeverityFlagsEXT::INFO; + } + if log::max_level() >= log::LevelFilter::Warn { + severity |= vk::DebugUtilsMessageSeverityFlagsEXT::WARNING; + } + + let message_type = vk::DebugUtilsMessageTypeFlagsEXT::GENERAL + | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION + | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE; + + let create_info = super::DebugUtilsCreateInfo { + severity, + message_type, + callback_data, + }; + + let vk_create_info = create_info.to_vk_create_info().build(); + + debug_utils = Some((create_info, vk_create_info)); + } + } else { + log::warn!( + "InstanceFlags::VALIDATION requested, but unable to find layer: {}", + validation_layer_name.to_string_lossy() + ); + } + } + + #[cfg(target_os = "android")] + let android_sdk_version = { + let properties = android_system_properties::AndroidSystemProperties::new(); + // See: https://developer.android.com/reference/android/os/Build.VERSION_CODES + if let Some(val) = properties.get("ro.build.version.sdk") { + match val.parse::<u32>() { + Ok(sdk_ver) => sdk_ver, + Err(err) => { + log::error!( + "Couldn't parse Android's ro.build.version.sdk system property ({val}): {err}" + ); + 0 + } + } + } else { + log::error!("Couldn't read Android's ro.build.version.sdk system property"); + 0 + } + }; + #[cfg(not(target_os = "android"))] + let android_sdk_version = 0; + + let mut flags = vk::InstanceCreateFlags::empty(); + + // 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(&ash::vk::KhrPortabilityEnumerationFn::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<_>>(); + + let mut create_info = vk::InstanceCreateInfo::builder() + .flags(flags) + .application_info(&app_info) + .enabled_layer_names(&str_pointers[..layers.len()]) + .enabled_extension_names(&str_pointers[layers.len()..]); + + if let Some(&mut (_, ref mut vk_create_info)) = debug_utils.as_mut() { + create_info = create_info.push_next(vk_create_info); + } + + unsafe { + profiling::scope!("vkCreateInstance"); + entry.create_instance(&create_info, None) + } + .map_err(|e| { + crate::InstanceError::with_source( + String::from("Entry::create_instance() failed"), + e, + ) + })? + }; + + unsafe { + Self::from_raw( + entry, + vk_instance, + instance_api_version, + android_sdk_version, + debug_utils.map(|(i, _)| i), + extensions, + desc.flags, + has_nv_optimus, + Some(Box::new(())), // `Some` signals that wgpu-hal is in charge of destroying vk_instance + ) + } + } + + unsafe fn create_surface( + &self, + display_handle: raw_window_handle::RawDisplayHandle, + window_handle: raw_window_handle::RawWindowHandle, + ) -> Result<super::Surface, crate::InstanceError> { + use raw_window_handle::{RawDisplayHandle as Rdh, RawWindowHandle as Rwh}; + + match (window_handle, display_handle) { + (Rwh::Wayland(handle), Rdh::Wayland(display)) => { + self.create_surface_from_wayland(display.display.as_ptr(), handle.surface.as_ptr()) + } + (Rwh::Xlib(handle), Rdh::Xlib(display)) => { + let display = display.display.expect("Display pointer is not set."); + self.create_surface_from_xlib(display.as_ptr() as *mut *const c_void, handle.window) + } + (Rwh::Xcb(handle), Rdh::Xcb(display)) => { + let connection = display.connection.expect("Pointer to X-Server is not set."); + self.create_surface_from_xcb(connection.as_ptr(), handle.window.get()) + } + (Rwh::AndroidNdk(handle), _) => { + self.create_surface_android(handle.a_native_window.as_ptr()) + } + #[cfg(windows)] + (Rwh::Win32(handle), _) => { + use winapi::um::libloaderapi::GetModuleHandleW; + + let hinstance = unsafe { GetModuleHandleW(std::ptr::null()) }; + self.create_surface_from_hwnd(hinstance as *mut _, handle.hwnd.get() as *mut _) + } + #[cfg(all(target_os = "macos", feature = "metal"))] + (Rwh::AppKit(handle), _) + if self.shared.extensions.contains(&ext::MetalSurface::name()) => + { + self.create_surface_from_view(handle.ns_view.as_ptr()) + } + #[cfg(all(target_os = "ios", feature = "metal"))] + (Rwh::UiKit(handle), _) + if self.shared.extensions.contains(&ext::MetalSurface::name()) => + { + self.create_surface_from_view(handle.ui_view.as_ptr()) + } + (_, _) => Err(crate::InstanceError::new(format!( + "window handle {window_handle:?} is not a Vulkan-compatible handle" + ))), + } + } + + unsafe fn destroy_surface(&self, surface: super::Surface) { + unsafe { surface.functor.destroy_surface(surface.raw, None) }; + } + + unsafe fn enumerate_adapters(&self) -> Vec<crate::ExposedAdapter<super::Api>> { + use crate::auxil::db; + + let raw_devices = match unsafe { self.shared.raw.enumerate_physical_devices() } { + Ok(devices) => devices, + Err(err) => { + log::error!("enumerate_adapters: {}", err); + Vec::new() + } + }; + + let mut exposed_adapters = raw_devices + .into_iter() + .flat_map(|device| self.expose_adapter(device)) + .collect::<Vec<_>>(); + + // Detect if it's an Intel + NVidia configuration with Optimus + let has_nvidia_dgpu = exposed_adapters.iter().any(|exposed| { + exposed.info.device_type == wgt::DeviceType::DiscreteGpu + && exposed.info.vendor == db::nvidia::VENDOR + }); + if cfg!(target_os = "linux") && has_nvidia_dgpu && self.shared.has_nv_optimus { + for exposed in exposed_adapters.iter_mut() { + if exposed.info.device_type == wgt::DeviceType::IntegratedGpu + && exposed.info.vendor == db::intel::VENDOR + { + // Check if mesa driver and version less than 21.2 + if let Some(version) = exposed.info.driver_info.split_once("Mesa ").map(|s| { + let mut components = s.1.split('.'); + let major = components.next().and_then(|s| u8::from_str(s).ok()); + let minor = components.next().and_then(|s| u8::from_str(s).ok()); + if let (Some(major), Some(minor)) = (major, minor) { + (major, minor) + } else { + (0, 0) + } + }) { + if version < (21, 2) { + // See https://gitlab.freedesktop.org/mesa/mesa/-/issues/4688 + log::warn!( + "Disabling presentation on '{}' (id {:?}) due to NV Optimus and Intel Mesa < v21.2", + exposed.info.name, + exposed.adapter.raw + ); + exposed.adapter.private_caps.can_present = false; + } + } + } + } + } + + exposed_adapters + } +} + +impl crate::Surface<super::Api> for super::Surface { + unsafe fn configure( + &self, + device: &super::Device, + config: &crate::SurfaceConfiguration, + ) -> Result<(), crate::SurfaceError> { + // Safety: `configure`'s contract guarantees there are no resources derived from the swapchain in use. + let mut swap_chain = self.swapchain.write(); + let old = swap_chain + .take() + .map(|sc| unsafe { sc.release_resources(&device.shared.raw) }); + + let swapchain = unsafe { device.create_swapchain(self, config, old)? }; + *swap_chain = Some(swapchain); + + Ok(()) + } + + unsafe fn unconfigure(&self, device: &super::Device) { + if let Some(sc) = self.swapchain.write().take() { + // Safety: `unconfigure`'s contract guarantees there are no resources derived from the swapchain in use. + let swapchain = unsafe { sc.release_resources(&device.shared.raw) }; + unsafe { swapchain.functor.destroy_swapchain(swapchain.raw, None) }; + } + } + + unsafe fn acquire_texture( + &self, + timeout: Option<std::time::Duration>, + ) -> Result<Option<crate::AcquiredSurfaceTexture<super::Api>>, crate::SurfaceError> { + let mut swapchain = self.swapchain.write(); + let sc = swapchain.as_mut().unwrap(); + + let mut timeout_ns = match timeout { + Some(duration) => duration.as_nanos() as u64, + None => u64::MAX, + }; + + // AcquireNextImageKHR on Android (prior to Android 11) doesn't support timeouts + // and will also log verbose warnings if tying to use a timeout. + // + // Android 10 implementation for reference: + // https://android.googlesource.com/platform/frameworks/native/+/refs/tags/android-mainline-10.0.0_r13/vulkan/libvulkan/swapchain.cpp#1426 + // Android 11 implementation for reference: + // https://android.googlesource.com/platform/frameworks/native/+/refs/tags/android-mainline-11.0.0_r45/vulkan/libvulkan/swapchain.cpp#1438 + // + // Android 11 corresponds to an SDK_INT/ro.build.version.sdk of 30 + if cfg!(target_os = "android") && self.instance.android_sdk_version < 30 { + timeout_ns = u64::MAX; + } + + let wait_semaphore = sc.surface_semaphores[sc.next_surface_index]; + + // will block if no image is available + let (index, suboptimal) = match unsafe { + sc.functor + .acquire_next_image(sc.raw, timeout_ns, wait_semaphore, vk::Fence::null()) + } { + // We treat `VK_SUBOPTIMAL_KHR` as `VK_SUCCESS` on Android. + // See the comment in `Queue::present`. + #[cfg(target_os = "android")] + Ok((index, _)) => (index, false), + #[cfg(not(target_os = "android"))] + Ok(pair) => pair, + Err(error) => { + return match error { + vk::Result::TIMEOUT => Ok(None), + vk::Result::NOT_READY | vk::Result::ERROR_OUT_OF_DATE_KHR => { + Err(crate::SurfaceError::Outdated) + } + vk::Result::ERROR_SURFACE_LOST_KHR => Err(crate::SurfaceError::Lost), + other => Err(crate::DeviceError::from(other).into()), + } + } + }; + + sc.next_surface_index += 1; + sc.next_surface_index %= sc.surface_semaphores.len(); + + // special case for Intel Vulkan returning bizarre values (ugh) + if sc.device.vendor_id == crate::auxil::db::intel::VENDOR && index > 0x100 { + return Err(crate::SurfaceError::Outdated); + } + + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkRenderPassBeginInfo.html#VUID-VkRenderPassBeginInfo-framebuffer-03209 + let raw_flags = if sc + .raw_flags + .contains(vk::SwapchainCreateFlagsKHR::MUTABLE_FORMAT) + { + vk::ImageCreateFlags::MUTABLE_FORMAT | vk::ImageCreateFlags::EXTENDED_USAGE + } else { + vk::ImageCreateFlags::empty() + }; + + let texture = super::SurfaceTexture { + index, + texture: super::Texture { + raw: sc.images[index as usize], + drop_guard: None, + block: None, + usage: sc.config.usage, + format: sc.config.format, + raw_flags, + copy_size: crate::CopyExtent { + width: sc.config.extent.width, + height: sc.config.extent.height, + depth: 1, + }, + view_formats: sc.view_formats.clone(), + }, + wait_semaphore, + }; + Ok(Some(crate::AcquiredSurfaceTexture { + texture, + suboptimal, + })) + } + + unsafe fn discard_texture(&self, _texture: super::SurfaceTexture) {} +} |