wgpu_hal/vulkan/
instance.rs

1use alloc::{borrow::ToOwned as _, boxed::Box, ffi::CString, string::String, sync::Arc, vec::Vec};
2use core::{
3    ffi::{c_void, CStr},
4    marker::PhantomData,
5    slice,
6    str::FromStr,
7};
8use std::thread;
9
10use arrayvec::ArrayVec;
11use ash::{ext, khr, vk};
12use parking_lot::RwLock;
13
14unsafe extern "system" fn debug_utils_messenger_callback(
15    message_severity: vk::DebugUtilsMessageSeverityFlagsEXT,
16    message_type: vk::DebugUtilsMessageTypeFlagsEXT,
17    callback_data_ptr: *const vk::DebugUtilsMessengerCallbackDataEXT,
18    user_data: *mut c_void,
19) -> vk::Bool32 {
20    use alloc::borrow::Cow;
21
22    if thread::panicking() {
23        return vk::FALSE;
24    }
25
26    let cd = unsafe { &*callback_data_ptr };
27    let user_data = unsafe { &*user_data.cast::<super::DebugUtilsMessengerUserData>() };
28
29    const VUID_VKCMDENDDEBUGUTILSLABELEXT_COMMANDBUFFER_01912: i32 = 0x56146426;
30    if cd.message_id_number == VUID_VKCMDENDDEBUGUTILSLABELEXT_COMMANDBUFFER_01912 {
31        // https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/5671
32        // Versions 1.3.240 through 1.3.250 return a spurious error here if
33        // the debug range start and end appear in different command buffers.
34        if let Some(layer_properties) = user_data.validation_layer_properties.as_ref() {
35            if layer_properties.layer_description.as_ref() == c"Khronos Validation Layer"
36                && layer_properties.layer_spec_version >= vk::make_api_version(0, 1, 3, 240)
37                && layer_properties.layer_spec_version <= vk::make_api_version(0, 1, 3, 250)
38            {
39                return vk::FALSE;
40            }
41        }
42    }
43
44    // Silence Vulkan Validation error "VUID-VkSwapchainCreateInfoKHR-pNext-07781"
45    // This happens when a surface is configured with a size outside the allowed extent.
46    // It's a false positive due to the inherent racy-ness of surface resizing.
47    const VUID_VKSWAPCHAINCREATEINFOKHR_PNEXT_07781: i32 = 0x4c8929c1;
48    if cd.message_id_number == VUID_VKSWAPCHAINCREATEINFOKHR_PNEXT_07781 {
49        return vk::FALSE;
50    }
51
52    // Silence Vulkan Validation error "VUID-VkRenderPassBeginInfo-framebuffer-04627"
53    // if the OBS layer is enabled. This is a bug in the OBS layer. As the OBS layer
54    // does not have a version number they increment, there is no way to qualify the
55    // suppression of the error to a specific version of the OBS layer.
56    //
57    // See https://github.com/obsproject/obs-studio/issues/9353
58    const VUID_VKRENDERPASSBEGININFO_FRAMEBUFFER_04627: i32 = 0x45125641;
59    if cd.message_id_number == VUID_VKRENDERPASSBEGININFO_FRAMEBUFFER_04627
60        && user_data.has_obs_layer
61    {
62        return vk::FALSE;
63    }
64
65    // Silence Vulkan Validation error "VUID-vkCmdCopyImageToBuffer-pRegions-00184".
66    // While we aren't sure yet, we suspect this is probably a VVL issue.
67    // https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/9276
68    const VUID_VKCMDCOPYIMAGETOBUFFER_PREGIONS_00184: i32 = 0x45ef177c;
69    if cd.message_id_number == VUID_VKCMDCOPYIMAGETOBUFFER_PREGIONS_00184 {
70        return vk::FALSE;
71    }
72
73    // Silence Vulkan Validation error "VUID-StandaloneSpirv-None-10684".
74    //
75    // This is a bug. To prevent massive noise in the tests, lets suppress it for now.
76    // https://github.com/gfx-rs/wgpu/issues/7696
77    const VUID_STANDALONESPIRV_NONE_10684: i32 = 0xb210f7c2_u32 as i32;
78    if cd.message_id_number == VUID_STANDALONESPIRV_NONE_10684 {
79        return vk::FALSE;
80    }
81
82    let level = match message_severity {
83        vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => log::Level::Debug,
84        vk::DebugUtilsMessageSeverityFlagsEXT::INFO => log::Level::Info,
85        vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => log::Level::Warn,
86        vk::DebugUtilsMessageSeverityFlagsEXT::ERROR => log::Level::Error,
87        _ => log::Level::Warn,
88    };
89
90    let message_id_name =
91        unsafe { cd.message_id_name_as_c_str() }.map_or(Cow::Borrowed(""), CStr::to_string_lossy);
92    let message = unsafe { cd.message_as_c_str() }.map_or(Cow::Borrowed(""), CStr::to_string_lossy);
93
94    let _ = std::panic::catch_unwind(|| {
95        log::log!(
96            level,
97            "{:?} [{} (0x{:x})]\n\t{}",
98            message_type,
99            message_id_name,
100            cd.message_id_number,
101            message,
102        );
103    });
104
105    if cd.queue_label_count != 0 {
106        let labels =
107            unsafe { slice::from_raw_parts(cd.p_queue_labels, cd.queue_label_count as usize) };
108        let names = labels
109            .iter()
110            .flat_map(|dul_obj| unsafe { dul_obj.label_name_as_c_str() }.map(CStr::to_string_lossy))
111            .collect::<Vec<_>>();
112
113        let _ = std::panic::catch_unwind(|| {
114            log::log!(level, "\tqueues: {}", names.join(", "));
115        });
116    }
117
118    if cd.cmd_buf_label_count != 0 {
119        let labels =
120            unsafe { slice::from_raw_parts(cd.p_cmd_buf_labels, cd.cmd_buf_label_count as usize) };
121        let names = labels
122            .iter()
123            .flat_map(|dul_obj| unsafe { dul_obj.label_name_as_c_str() }.map(CStr::to_string_lossy))
124            .collect::<Vec<_>>();
125
126        let _ = std::panic::catch_unwind(|| {
127            log::log!(level, "\tcommand buffers: {}", names.join(", "));
128        });
129    }
130
131    if cd.object_count != 0 {
132        let labels = unsafe { slice::from_raw_parts(cd.p_objects, cd.object_count as usize) };
133        //TODO: use color fields of `vk::DebugUtilsLabelExt`?
134        let names = labels
135            .iter()
136            .map(|obj_info| {
137                let name = unsafe { obj_info.object_name_as_c_str() }
138                    .map_or(Cow::Borrowed("?"), CStr::to_string_lossy);
139
140                format!(
141                    "(type: {:?}, hndl: 0x{:x}, name: {})",
142                    obj_info.object_type, obj_info.object_handle, name
143                )
144            })
145            .collect::<Vec<_>>();
146        let _ = std::panic::catch_unwind(|| {
147            log::log!(level, "\tobjects: {}", names.join(", "));
148        });
149    }
150
151    #[cfg(feature = "validation_canary")]
152    if cfg!(debug_assertions) && level == log::Level::Error {
153        use alloc::string::ToString as _;
154
155        // Set canary and continue
156        crate::VALIDATION_CANARY.add(message.to_string());
157    }
158
159    vk::FALSE
160}
161
162impl super::DebugUtilsCreateInfo {
163    fn to_vk_create_info(&self) -> vk::DebugUtilsMessengerCreateInfoEXT<'_> {
164        let user_data_ptr: *const super::DebugUtilsMessengerUserData = &*self.callback_data;
165        vk::DebugUtilsMessengerCreateInfoEXT::default()
166            .message_severity(self.severity)
167            .message_type(self.message_type)
168            .user_data(user_data_ptr as *mut _)
169            .pfn_user_callback(Some(debug_utils_messenger_callback))
170    }
171}
172
173impl super::Swapchain {
174    /// # Safety
175    ///
176    /// - The device must have been made idle before calling this function.
177    unsafe fn release_resources(mut self, device: &ash::Device) -> Self {
178        profiling::scope!("Swapchain::release_resources");
179        {
180            profiling::scope!("vkDeviceWaitIdle");
181            // We need to also wait until all presentation work is done. Because there is no way to portably wait until
182            // the presentation work is done, we are forced to wait until the device is idle.
183            let _ = unsafe {
184                device
185                    .device_wait_idle()
186                    .map_err(super::map_host_device_oom_and_lost_err)
187            };
188        };
189
190        unsafe { device.destroy_fence(self.fence, None) }
191
192        // We cannot take this by value, as the function returns `self`.
193        for semaphore in self.acquire_semaphores.drain(..) {
194            let arc_removed = Arc::into_inner(semaphore).expect(
195                "Trying to destroy a SurfaceAcquireSemaphores that is still in use by a SurfaceTexture",
196            );
197            let mutex_removed = arc_removed.into_inner();
198
199            unsafe { mutex_removed.destroy(device) };
200        }
201
202        for semaphore in self.present_semaphores.drain(..) {
203            let arc_removed = Arc::into_inner(semaphore).expect(
204                "Trying to destroy a SurfacePresentSemaphores that is still in use by a SurfaceTexture",
205            );
206            let mutex_removed = arc_removed.into_inner();
207
208            unsafe { mutex_removed.destroy(device) };
209        }
210
211        self
212    }
213}
214
215impl super::InstanceShared {
216    pub fn entry(&self) -> &ash::Entry {
217        &self.entry
218    }
219
220    pub fn raw_instance(&self) -> &ash::Instance {
221        &self.raw
222    }
223
224    pub fn instance_api_version(&self) -> u32 {
225        self.instance_api_version
226    }
227
228    pub fn extensions(&self) -> &[&'static CStr] {
229        &self.extensions[..]
230    }
231}
232
233impl super::Instance {
234    pub fn shared_instance(&self) -> &super::InstanceShared {
235        &self.shared
236    }
237
238    fn enumerate_instance_extension_properties(
239        entry: &ash::Entry,
240        layer_name: Option<&CStr>,
241    ) -> Result<Vec<vk::ExtensionProperties>, crate::InstanceError> {
242        let instance_extensions = {
243            profiling::scope!("vkEnumerateInstanceExtensionProperties");
244            unsafe { entry.enumerate_instance_extension_properties(layer_name) }
245        };
246        instance_extensions.map_err(|e| {
247            crate::InstanceError::with_source(
248                String::from("enumerate_instance_extension_properties() failed"),
249                e,
250            )
251        })
252    }
253
254    /// Return the instance extension names wgpu would like to enable.
255    ///
256    /// Return a vector of the names of instance extensions actually available
257    /// on `entry` that wgpu would like to enable.
258    ///
259    /// The `instance_api_version` argument should be the instance's Vulkan API
260    /// version, as obtained from `vkEnumerateInstanceVersion`. This is the same
261    /// space of values as the `VK_API_VERSION` constants.
262    ///
263    /// Note that wgpu can function without many of these extensions (for
264    /// example, `VK_KHR_wayland_surface` is certainly not going to be available
265    /// everywhere), but if one of these extensions is available at all, wgpu
266    /// assumes that it has been enabled.
267    pub fn desired_extensions(
268        entry: &ash::Entry,
269        _instance_api_version: u32,
270        flags: wgt::InstanceFlags,
271    ) -> Result<Vec<&'static CStr>, crate::InstanceError> {
272        let instance_extensions = Self::enumerate_instance_extension_properties(entry, None)?;
273
274        // Check our extensions against the available extensions
275        let mut extensions: Vec<&'static CStr> = Vec::new();
276
277        // VK_KHR_surface
278        extensions.push(khr::surface::NAME);
279
280        // Platform-specific WSI extensions
281        if cfg!(all(
282            unix,
283            not(target_os = "android"),
284            not(target_os = "macos")
285        )) {
286            // VK_KHR_xlib_surface
287            extensions.push(khr::xlib_surface::NAME);
288            // VK_KHR_xcb_surface
289            extensions.push(khr::xcb_surface::NAME);
290            // VK_KHR_wayland_surface
291            extensions.push(khr::wayland_surface::NAME);
292        }
293        if cfg!(target_os = "android") {
294            // VK_KHR_android_surface
295            extensions.push(khr::android_surface::NAME);
296        }
297        if cfg!(target_os = "windows") {
298            // VK_KHR_win32_surface
299            extensions.push(khr::win32_surface::NAME);
300        }
301        if cfg!(target_os = "macos") {
302            // VK_EXT_metal_surface
303            extensions.push(ext::metal_surface::NAME);
304            extensions.push(khr::portability_enumeration::NAME);
305        }
306        if cfg!(all(
307            unix,
308            not(target_vendor = "apple"),
309            not(target_family = "wasm")
310        )) {
311            // VK_EXT_acquire_drm_display -> VK_EXT_direct_mode_display -> VK_KHR_display
312            extensions.push(ext::acquire_drm_display::NAME);
313            extensions.push(ext::direct_mode_display::NAME);
314            extensions.push(khr::display::NAME);
315            //  VK_EXT_physical_device_drm -> VK_KHR_get_physical_device_properties2
316            extensions.push(ext::physical_device_drm::NAME);
317            extensions.push(khr::get_display_properties2::NAME);
318        }
319
320        if flags.contains(wgt::InstanceFlags::DEBUG) {
321            // VK_EXT_debug_utils
322            extensions.push(ext::debug_utils::NAME);
323        }
324
325        // VK_EXT_swapchain_colorspace
326        // Provides wide color gamut
327        extensions.push(ext::swapchain_colorspace::NAME);
328
329        // VK_KHR_get_physical_device_properties2
330        // Even though the extension was promoted to Vulkan 1.1, we still require the extension
331        // so that we don't have to conditionally use the functions provided by the 1.1 instance
332        extensions.push(khr::get_physical_device_properties2::NAME);
333
334        // Only keep available extensions.
335        extensions.retain(|&ext| {
336            if instance_extensions
337                .iter()
338                .any(|inst_ext| inst_ext.extension_name_as_c_str() == Ok(ext))
339            {
340                true
341            } else {
342                log::warn!("Unable to find extension: {}", ext.to_string_lossy());
343                false
344            }
345        });
346        Ok(extensions)
347    }
348
349    /// # Safety
350    ///
351    /// - `raw_instance` must be created from `entry`
352    /// - `raw_instance` must be created respecting `instance_api_version`, `extensions` and `flags`
353    /// - `extensions` must be a superset of `desired_extensions()` and must be created from the
354    ///   same entry, `instance_api_version`` and flags.
355    /// - `android_sdk_version` is ignored and can be `0` for all platforms besides Android
356    /// - If `drop_callback` is [`None`], wgpu-hal will take ownership of `raw_instance`. If
357    ///   `drop_callback` is [`Some`], `raw_instance` must be valid until the callback is called.
358    ///
359    /// If `debug_utils_user_data` is `Some`, then the validation layer is
360    /// available, so create a [`vk::DebugUtilsMessengerEXT`].
361    #[allow(clippy::too_many_arguments)]
362    pub unsafe fn from_raw(
363        entry: ash::Entry,
364        raw_instance: ash::Instance,
365        instance_api_version: u32,
366        android_sdk_version: u32,
367        debug_utils_create_info: Option<super::DebugUtilsCreateInfo>,
368        extensions: Vec<&'static CStr>,
369        flags: wgt::InstanceFlags,
370        memory_budget_thresholds: wgt::MemoryBudgetThresholds,
371        has_nv_optimus: bool,
372        drop_callback: Option<crate::DropCallback>,
373    ) -> Result<Self, crate::InstanceError> {
374        log::debug!("Instance version: 0x{:x}", instance_api_version);
375
376        let debug_utils = if let Some(debug_utils_create_info) = debug_utils_create_info {
377            if extensions.contains(&ext::debug_utils::NAME) {
378                log::info!("Enabling debug utils");
379
380                let extension = ext::debug_utils::Instance::new(&entry, &raw_instance);
381                let vk_info = debug_utils_create_info.to_vk_create_info();
382                let messenger =
383                    unsafe { extension.create_debug_utils_messenger(&vk_info, None) }.unwrap();
384
385                Some(super::DebugUtils {
386                    extension,
387                    messenger,
388                    callback_data: debug_utils_create_info.callback_data,
389                })
390            } else {
391                log::debug!("Debug utils not enabled: extension not listed");
392                None
393            }
394        } else {
395            log::debug!(
396                "Debug utils not enabled: \
397                        debug_utils_user_data not passed to Instance::from_raw"
398            );
399            None
400        };
401
402        let get_physical_device_properties =
403            if extensions.contains(&khr::get_physical_device_properties2::NAME) {
404                log::debug!("Enabling device properties2");
405                Some(khr::get_physical_device_properties2::Instance::new(
406                    &entry,
407                    &raw_instance,
408                ))
409            } else {
410                None
411            };
412
413        let drop_guard = crate::DropGuard::from_option(drop_callback);
414
415        Ok(Self {
416            shared: Arc::new(super::InstanceShared {
417                raw: raw_instance,
418                extensions,
419                drop_guard,
420                flags,
421                memory_budget_thresholds,
422                debug_utils,
423                get_physical_device_properties,
424                entry,
425                has_nv_optimus,
426                instance_api_version,
427                android_sdk_version,
428            }),
429        })
430    }
431
432    fn create_surface_from_xlib(
433        &self,
434        dpy: *mut vk::Display,
435        window: vk::Window,
436    ) -> Result<super::Surface, crate::InstanceError> {
437        if !self.shared.extensions.contains(&khr::xlib_surface::NAME) {
438            return Err(crate::InstanceError::new(String::from(
439                "Vulkan driver does not support VK_KHR_xlib_surface",
440            )));
441        }
442
443        let surface = {
444            let xlib_loader =
445                khr::xlib_surface::Instance::new(&self.shared.entry, &self.shared.raw);
446            let info = vk::XlibSurfaceCreateInfoKHR::default()
447                .flags(vk::XlibSurfaceCreateFlagsKHR::empty())
448                .window(window)
449                .dpy(dpy);
450
451            unsafe { xlib_loader.create_xlib_surface(&info, None) }
452                .expect("XlibSurface::create_xlib_surface() failed")
453        };
454
455        Ok(self.create_surface_from_vk_surface_khr(surface))
456    }
457
458    fn create_surface_from_xcb(
459        &self,
460        connection: *mut vk::xcb_connection_t,
461        window: vk::xcb_window_t,
462    ) -> Result<super::Surface, crate::InstanceError> {
463        if !self.shared.extensions.contains(&khr::xcb_surface::NAME) {
464            return Err(crate::InstanceError::new(String::from(
465                "Vulkan driver does not support VK_KHR_xcb_surface",
466            )));
467        }
468
469        let surface = {
470            let xcb_loader = khr::xcb_surface::Instance::new(&self.shared.entry, &self.shared.raw);
471            let info = vk::XcbSurfaceCreateInfoKHR::default()
472                .flags(vk::XcbSurfaceCreateFlagsKHR::empty())
473                .window(window)
474                .connection(connection);
475
476            unsafe { xcb_loader.create_xcb_surface(&info, None) }
477                .expect("XcbSurface::create_xcb_surface() failed")
478        };
479
480        Ok(self.create_surface_from_vk_surface_khr(surface))
481    }
482
483    fn create_surface_from_wayland(
484        &self,
485        display: *mut vk::wl_display,
486        surface: *mut vk::wl_surface,
487    ) -> Result<super::Surface, crate::InstanceError> {
488        if !self.shared.extensions.contains(&khr::wayland_surface::NAME) {
489            return Err(crate::InstanceError::new(String::from(
490                "Vulkan driver does not support VK_KHR_wayland_surface",
491            )));
492        }
493
494        let surface = {
495            let w_loader =
496                khr::wayland_surface::Instance::new(&self.shared.entry, &self.shared.raw);
497            let info = vk::WaylandSurfaceCreateInfoKHR::default()
498                .flags(vk::WaylandSurfaceCreateFlagsKHR::empty())
499                .display(display)
500                .surface(surface);
501
502            unsafe { w_loader.create_wayland_surface(&info, None) }.expect("WaylandSurface failed")
503        };
504
505        Ok(self.create_surface_from_vk_surface_khr(surface))
506    }
507
508    fn create_surface_android(
509        &self,
510        window: *mut vk::ANativeWindow,
511    ) -> Result<super::Surface, crate::InstanceError> {
512        if !self.shared.extensions.contains(&khr::android_surface::NAME) {
513            return Err(crate::InstanceError::new(String::from(
514                "Vulkan driver does not support VK_KHR_android_surface",
515            )));
516        }
517
518        let surface = {
519            let a_loader =
520                khr::android_surface::Instance::new(&self.shared.entry, &self.shared.raw);
521            let info = vk::AndroidSurfaceCreateInfoKHR::default()
522                .flags(vk::AndroidSurfaceCreateFlagsKHR::empty())
523                .window(window);
524
525            unsafe { a_loader.create_android_surface(&info, None) }.expect("AndroidSurface failed")
526        };
527
528        Ok(self.create_surface_from_vk_surface_khr(surface))
529    }
530
531    fn create_surface_from_hwnd(
532        &self,
533        hinstance: vk::HINSTANCE,
534        hwnd: vk::HWND,
535    ) -> Result<super::Surface, crate::InstanceError> {
536        if !self.shared.extensions.contains(&khr::win32_surface::NAME) {
537            return Err(crate::InstanceError::new(String::from(
538                "Vulkan driver does not support VK_KHR_win32_surface",
539            )));
540        }
541
542        let surface = {
543            let info = vk::Win32SurfaceCreateInfoKHR::default()
544                .flags(vk::Win32SurfaceCreateFlagsKHR::empty())
545                .hinstance(hinstance)
546                .hwnd(hwnd);
547            let win32_loader =
548                khr::win32_surface::Instance::new(&self.shared.entry, &self.shared.raw);
549            unsafe {
550                win32_loader
551                    .create_win32_surface(&info, None)
552                    .expect("Unable to create Win32 surface")
553            }
554        };
555
556        Ok(self.create_surface_from_vk_surface_khr(surface))
557    }
558
559    #[cfg(metal)]
560    fn create_surface_from_view(
561        &self,
562        view: core::ptr::NonNull<c_void>,
563    ) -> Result<super::Surface, crate::InstanceError> {
564        if !self.shared.extensions.contains(&ext::metal_surface::NAME) {
565            return Err(crate::InstanceError::new(String::from(
566                "Vulkan driver does not support VK_EXT_metal_surface",
567            )));
568        }
569
570        let layer = unsafe { crate::metal::Surface::get_metal_layer(view.cast()) };
571        // NOTE: The layer is retained by Vulkan's `vkCreateMetalSurfaceEXT`,
572        // so no need to retain it beyond the scope of this function.
573        let layer_ptr = (*layer).cast();
574
575        let surface = {
576            let metal_loader =
577                ext::metal_surface::Instance::new(&self.shared.entry, &self.shared.raw);
578            let vk_info = vk::MetalSurfaceCreateInfoEXT::default()
579                .flags(vk::MetalSurfaceCreateFlagsEXT::empty())
580                .layer(layer_ptr);
581
582            unsafe { metal_loader.create_metal_surface(&vk_info, None).unwrap() }
583        };
584
585        Ok(self.create_surface_from_vk_surface_khr(surface))
586    }
587
588    pub(super) fn create_surface_from_vk_surface_khr(
589        &self,
590        surface: vk::SurfaceKHR,
591    ) -> super::Surface {
592        let functor = khr::surface::Instance::new(&self.shared.entry, &self.shared.raw);
593        super::Surface {
594            raw: surface,
595            functor,
596            instance: Arc::clone(&self.shared),
597            swapchain: RwLock::new(None),
598        }
599    }
600
601    /// `Instance::init` but with a callback.
602    /// If you want to add extensions, add the to the `Vec<'static CStr>` not the create info, otherwise
603    /// it will be overwritten
604    ///
605    /// # Safety:
606    /// Same as `init` but additionally
607    /// - Callback must not remove features.
608    /// - Callback must not change anything to what the instance does not support.
609    pub unsafe fn init_with_callback(
610        desc: &crate::InstanceDescriptor,
611        callback: Option<Box<super::CreateInstanceCallback>>,
612    ) -> Result<Self, crate::InstanceError> {
613        profiling::scope!("Init Vulkan Backend");
614
615        let entry = unsafe {
616            profiling::scope!("Load vk library");
617            ash::Entry::load()
618        }
619        .map_err(|err| {
620            crate::InstanceError::with_source(String::from("missing Vulkan entry points"), err)
621        })?;
622        let version = {
623            profiling::scope!("vkEnumerateInstanceVersion");
624            unsafe { entry.try_enumerate_instance_version() }
625        };
626        let instance_api_version = match version {
627            // Vulkan 1.1+
628            Ok(Some(version)) => version,
629            Ok(None) => vk::API_VERSION_1_0,
630            Err(err) => {
631                return Err(crate::InstanceError::with_source(
632                    String::from("try_enumerate_instance_version() failed"),
633                    err,
634                ));
635            }
636        };
637
638        let app_name = CString::new(desc.name).unwrap();
639        let app_info = vk::ApplicationInfo::default()
640            .application_name(app_name.as_c_str())
641            .application_version(1)
642            .engine_name(c"wgpu-hal")
643            .engine_version(2)
644            .api_version(
645                // Vulkan 1.0 doesn't like anything but 1.0 passed in here...
646                if instance_api_version < vk::API_VERSION_1_1 {
647                    vk::API_VERSION_1_0
648                } else {
649                    // This is the max Vulkan API version supported by `wgpu-hal`.
650                    //
651                    // If we want to increment this, there are some things that must be done first:
652                    //  - Audit the behavioral differences between the previous and new API versions.
653                    //  - Audit all extensions used by this backend:
654                    //    - 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.
655                    //    - If any were obsoleted in the new API version, we must implement a fallback for the new API version
656                    //    - If any are non-KHR-vendored, we must ensure the new behavior is still correct (since backwards-compatibility is not guaranteed).
657                    vk::API_VERSION_1_3
658                },
659            );
660
661        let mut extensions = Self::desired_extensions(&entry, instance_api_version, desc.flags)?;
662        let mut create_info = vk::InstanceCreateInfo::default();
663
664        if let Some(callback) = callback {
665            callback(super::CreateInstanceCallbackArgs {
666                extensions: &mut extensions,
667                create_info: &mut create_info,
668                entry: &entry,
669                _phantom: PhantomData,
670            });
671        }
672
673        let instance_layers = {
674            profiling::scope!("vkEnumerateInstanceLayerProperties");
675            unsafe { entry.enumerate_instance_layer_properties() }
676        };
677        let instance_layers = instance_layers.map_err(|e| {
678            log::debug!("enumerate_instance_layer_properties: {:?}", e);
679            crate::InstanceError::with_source(
680                String::from("enumerate_instance_layer_properties() failed"),
681                e,
682            )
683        })?;
684
685        fn find_layer<'layers>(
686            instance_layers: &'layers [vk::LayerProperties],
687            name: &CStr,
688        ) -> Option<&'layers vk::LayerProperties> {
689            instance_layers
690                .iter()
691                .find(|inst_layer| inst_layer.layer_name_as_c_str() == Ok(name))
692        }
693
694        let validation_layer_name = c"VK_LAYER_KHRONOS_validation";
695        let validation_layer_properties = find_layer(&instance_layers, validation_layer_name);
696
697        // Determine if VK_EXT_validation_features is available, so we can enable
698        // GPU assisted validation and synchronization validation.
699        let validation_features_are_enabled = if validation_layer_properties.is_some() {
700            // Get the all the instance extension properties.
701            let exts =
702                Self::enumerate_instance_extension_properties(&entry, Some(validation_layer_name))?;
703            // Convert all the names of the extensions into an iterator of CStrs.
704            let mut ext_names = exts
705                .iter()
706                .filter_map(|ext| ext.extension_name_as_c_str().ok());
707            // Find the validation features extension.
708            ext_names.any(|ext_name| ext_name == ext::validation_features::NAME)
709        } else {
710            false
711        };
712
713        let should_enable_gpu_based_validation = desc
714            .flags
715            .intersects(wgt::InstanceFlags::GPU_BASED_VALIDATION)
716            && validation_features_are_enabled;
717
718        let has_nv_optimus = find_layer(&instance_layers, c"VK_LAYER_NV_optimus").is_some();
719
720        let has_obs_layer = find_layer(&instance_layers, c"VK_LAYER_OBS_HOOK").is_some();
721
722        let mut layers: Vec<&'static CStr> = Vec::new();
723
724        let has_debug_extension = extensions.contains(&ext::debug_utils::NAME);
725        let mut debug_user_data = has_debug_extension.then(|| {
726            // Put the callback data on the heap, to ensure it will never be
727            // moved.
728            Box::new(super::DebugUtilsMessengerUserData {
729                validation_layer_properties: None,
730                has_obs_layer,
731            })
732        });
733
734        // Request validation layer if asked.
735        if desc.flags.intersects(wgt::InstanceFlags::VALIDATION)
736            || should_enable_gpu_based_validation
737        {
738            if let Some(layer_properties) = validation_layer_properties {
739                layers.push(validation_layer_name);
740
741                if let Some(debug_user_data) = debug_user_data.as_mut() {
742                    debug_user_data.validation_layer_properties =
743                        Some(super::ValidationLayerProperties {
744                            layer_description: layer_properties
745                                .description_as_c_str()
746                                .unwrap()
747                                .to_owned(),
748                            layer_spec_version: layer_properties.spec_version,
749                        });
750                }
751            } else {
752                log::warn!(
753                    "InstanceFlags::VALIDATION requested, but unable to find layer: {}",
754                    validation_layer_name.to_string_lossy()
755                );
756            }
757        }
758        let mut debug_utils = if let Some(callback_data) = debug_user_data {
759            // having ERROR unconditionally because Vk doesn't like empty flags
760            let mut severity = vk::DebugUtilsMessageSeverityFlagsEXT::ERROR;
761            if log::max_level() >= log::LevelFilter::Debug {
762                severity |= vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE;
763            }
764            if log::max_level() >= log::LevelFilter::Info {
765                severity |= vk::DebugUtilsMessageSeverityFlagsEXT::INFO;
766            }
767            if log::max_level() >= log::LevelFilter::Warn {
768                severity |= vk::DebugUtilsMessageSeverityFlagsEXT::WARNING;
769            }
770
771            let message_type = vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
772                | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION
773                | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE;
774
775            let create_info = super::DebugUtilsCreateInfo {
776                severity,
777                message_type,
778                callback_data,
779            };
780
781            Some(create_info)
782        } else {
783            None
784        };
785
786        #[cfg(target_os = "android")]
787        let android_sdk_version = {
788            let properties = android_system_properties::AndroidSystemProperties::new();
789            // See: https://developer.android.com/reference/android/os/Build.VERSION_CODES
790            if let Some(val) = properties.get("ro.build.version.sdk") {
791                match val.parse::<u32>() {
792                    Ok(sdk_ver) => sdk_ver,
793                    Err(err) => {
794                        log::error!(
795                            concat!(
796                                "Couldn't parse Android's ",
797                                "ro.build.version.sdk system property ({}): {}",
798                            ),
799                            val,
800                            err,
801                        );
802                        0
803                    }
804                }
805            } else {
806                log::error!("Couldn't read Android's ro.build.version.sdk system property");
807                0
808            }
809        };
810        #[cfg(not(target_os = "android"))]
811        let android_sdk_version = 0;
812
813        let mut flags = vk::InstanceCreateFlags::empty();
814
815        // Avoid VUID-VkInstanceCreateInfo-flags-06559: Only ask the instance to
816        // enumerate incomplete Vulkan implementations (which we need on Mac) if
817        // we managed to find the extension that provides the flag.
818        if extensions.contains(&khr::portability_enumeration::NAME) {
819            flags |= vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR;
820        }
821        let vk_instance = {
822            let str_pointers = layers
823                .iter()
824                .chain(extensions.iter())
825                .map(|&s: &&'static _| {
826                    // Safe because `layers` and `extensions` entries have static lifetime.
827                    s.as_ptr()
828                })
829                .collect::<Vec<_>>();
830
831            create_info = create_info
832                .flags(flags)
833                .application_info(&app_info)
834                .enabled_layer_names(&str_pointers[..layers.len()])
835                .enabled_extension_names(&str_pointers[layers.len()..]);
836
837            let mut debug_utils_create_info = debug_utils
838                .as_mut()
839                .map(|create_info| create_info.to_vk_create_info());
840            if let Some(debug_utils_create_info) = debug_utils_create_info.as_mut() {
841                create_info = create_info.push_next(debug_utils_create_info);
842            }
843
844            // Enable explicit validation features if available
845            let mut validation_features;
846            let mut validation_feature_list: ArrayVec<_, 3>;
847            if validation_features_are_enabled {
848                validation_feature_list = ArrayVec::new();
849
850                // Always enable synchronization validation
851                validation_feature_list
852                    .push(vk::ValidationFeatureEnableEXT::SYNCHRONIZATION_VALIDATION);
853
854                // Only enable GPU assisted validation if requested.
855                if should_enable_gpu_based_validation {
856                    validation_feature_list.push(vk::ValidationFeatureEnableEXT::GPU_ASSISTED);
857                    validation_feature_list
858                        .push(vk::ValidationFeatureEnableEXT::GPU_ASSISTED_RESERVE_BINDING_SLOT);
859                }
860
861                validation_features = vk::ValidationFeaturesEXT::default()
862                    .enabled_validation_features(&validation_feature_list);
863                create_info = create_info.push_next(&mut validation_features);
864            }
865
866            unsafe {
867                profiling::scope!("vkCreateInstance");
868                entry.create_instance(&create_info, None)
869            }
870            .map_err(|e| {
871                crate::InstanceError::with_source(
872                    String::from("Entry::create_instance() failed"),
873                    e,
874                )
875            })?
876        };
877
878        unsafe {
879            Self::from_raw(
880                entry,
881                vk_instance,
882                instance_api_version,
883                android_sdk_version,
884                debug_utils,
885                extensions,
886                desc.flags,
887                desc.memory_budget_thresholds,
888                has_nv_optimus,
889                None,
890            )
891        }
892    }
893}
894
895impl Drop for super::InstanceShared {
896    fn drop(&mut self) {
897        unsafe {
898            // Keep du alive since destroy_instance may also log
899            let _du = self.debug_utils.take().inspect(|du| {
900                du.extension
901                    .destroy_debug_utils_messenger(du.messenger, None);
902            });
903            if self.drop_guard.is_none() {
904                self.raw.destroy_instance(None);
905            }
906        }
907    }
908}
909
910impl crate::Instance for super::Instance {
911    type A = super::Api;
912
913    unsafe fn init(desc: &crate::InstanceDescriptor) -> Result<Self, crate::InstanceError> {
914        unsafe { Self::init_with_callback(desc, None) }
915    }
916
917    unsafe fn create_surface(
918        &self,
919        display_handle: raw_window_handle::RawDisplayHandle,
920        window_handle: raw_window_handle::RawWindowHandle,
921    ) -> Result<super::Surface, crate::InstanceError> {
922        use raw_window_handle::{RawDisplayHandle as Rdh, RawWindowHandle as Rwh};
923
924        // TODO: Replace with ash-window, which also lazy-loads the extension based on handle type
925
926        match (window_handle, display_handle) {
927            (Rwh::Wayland(handle), Rdh::Wayland(display)) => {
928                self.create_surface_from_wayland(display.display.as_ptr(), handle.surface.as_ptr())
929            }
930            (Rwh::Xlib(handle), Rdh::Xlib(display)) => {
931                let display = display.display.expect("Display pointer is not set.");
932                self.create_surface_from_xlib(display.as_ptr(), handle.window)
933            }
934            (Rwh::Xcb(handle), Rdh::Xcb(display)) => {
935                let connection = display.connection.expect("Pointer to X-Server is not set.");
936                self.create_surface_from_xcb(connection.as_ptr(), handle.window.get())
937            }
938            (Rwh::AndroidNdk(handle), _) => {
939                self.create_surface_android(handle.a_native_window.as_ptr())
940            }
941            (Rwh::Win32(handle), _) => {
942                let hinstance = handle.hinstance.ok_or_else(|| {
943                    crate::InstanceError::new(String::from(
944                        "Vulkan requires raw-window-handle's Win32::hinstance to be set",
945                    ))
946                })?;
947                self.create_surface_from_hwnd(hinstance.get(), handle.hwnd.get())
948            }
949            #[cfg(all(target_os = "macos", feature = "metal"))]
950            (Rwh::AppKit(handle), _)
951                if self.shared.extensions.contains(&ext::metal_surface::NAME) =>
952            {
953                self.create_surface_from_view(handle.ns_view)
954            }
955            #[cfg(all(any(target_os = "ios", target_os = "visionos"), feature = "metal"))]
956            (Rwh::UiKit(handle), _)
957                if self.shared.extensions.contains(&ext::metal_surface::NAME) =>
958            {
959                self.create_surface_from_view(handle.ui_view)
960            }
961            (_, _) => Err(crate::InstanceError::new(format!(
962                "window handle {window_handle:?} is not a Vulkan-compatible handle"
963            ))),
964        }
965    }
966
967    unsafe fn enumerate_adapters(
968        &self,
969        _surface_hint: Option<&super::Surface>,
970    ) -> Vec<crate::ExposedAdapter<super::Api>> {
971        use crate::auxil::db;
972
973        let raw_devices = match unsafe { self.shared.raw.enumerate_physical_devices() } {
974            Ok(devices) => devices,
975            Err(err) => {
976                log::error!("enumerate_adapters: {}", err);
977                Vec::new()
978            }
979        };
980
981        let mut exposed_adapters = raw_devices
982            .into_iter()
983            .flat_map(|device| self.expose_adapter(device))
984            .collect::<Vec<_>>();
985
986        // Detect if it's an Intel + NVidia configuration with Optimus
987        let has_nvidia_dgpu = exposed_adapters.iter().any(|exposed| {
988            exposed.info.device_type == wgt::DeviceType::DiscreteGpu
989                && exposed.info.vendor == db::nvidia::VENDOR
990        });
991        if cfg!(target_os = "linux") && has_nvidia_dgpu && self.shared.has_nv_optimus {
992            for exposed in exposed_adapters.iter_mut() {
993                if exposed.info.device_type == wgt::DeviceType::IntegratedGpu
994                    && exposed.info.vendor == db::intel::VENDOR
995                {
996                    // Check if mesa driver and version less than 21.2
997                    if let Some(version) = exposed.info.driver_info.split_once("Mesa ").map(|s| {
998                        let mut components = s.1.split('.');
999                        let major = components.next().and_then(|s| u8::from_str(s).ok());
1000                        let minor = components.next().and_then(|s| u8::from_str(s).ok());
1001                        if let (Some(major), Some(minor)) = (major, minor) {
1002                            (major, minor)
1003                        } else {
1004                            (0, 0)
1005                        }
1006                    }) {
1007                        if version < (21, 2) {
1008                            // See https://gitlab.freedesktop.org/mesa/mesa/-/issues/4688
1009                            log::warn!(
1010                                concat!(
1011                                    "Disabling presentation on '{}' (id {:?}) ",
1012                                    "due to NV Optimus and Intel Mesa < v21.2"
1013                                ),
1014                                exposed.info.name,
1015                                exposed.adapter.raw
1016                            );
1017                            exposed.adapter.private_caps.can_present = false;
1018                        }
1019                    }
1020                }
1021            }
1022        }
1023
1024        exposed_adapters
1025    }
1026}
1027
1028impl Drop for super::Surface {
1029    fn drop(&mut self) {
1030        unsafe { self.functor.destroy_surface(self.raw, None) };
1031    }
1032}
1033
1034impl crate::Surface for super::Surface {
1035    type A = super::Api;
1036
1037    unsafe fn configure(
1038        &self,
1039        device: &super::Device,
1040        config: &crate::SurfaceConfiguration,
1041    ) -> Result<(), crate::SurfaceError> {
1042        // SAFETY: `configure`'s contract guarantees there are no resources derived from the swapchain in use.
1043        let mut swap_chain = self.swapchain.write();
1044        let old = swap_chain
1045            .take()
1046            .map(|sc| unsafe { sc.release_resources(&device.shared.raw) });
1047
1048        let swapchain = unsafe { device.create_swapchain(self, config, old)? };
1049        *swap_chain = Some(swapchain);
1050
1051        Ok(())
1052    }
1053
1054    unsafe fn unconfigure(&self, device: &super::Device) {
1055        if let Some(sc) = self.swapchain.write().take() {
1056            // SAFETY: `unconfigure`'s contract guarantees there are no resources derived from the swapchain in use.
1057            let swapchain = unsafe { sc.release_resources(&device.shared.raw) };
1058            unsafe { swapchain.functor.destroy_swapchain(swapchain.raw, None) };
1059        }
1060    }
1061
1062    unsafe fn acquire_texture(
1063        &self,
1064        timeout: Option<core::time::Duration>,
1065        fence: &super::Fence,
1066    ) -> Result<Option<crate::AcquiredSurfaceTexture<super::Api>>, crate::SurfaceError> {
1067        let mut swapchain = self.swapchain.write();
1068        let swapchain = swapchain.as_mut().unwrap();
1069
1070        let mut timeout_ns = match timeout {
1071            Some(duration) => duration.as_nanos() as u64,
1072            None => u64::MAX,
1073        };
1074
1075        // AcquireNextImageKHR on Android (prior to Android 11) doesn't support timeouts
1076        // and will also log verbose warnings if tying to use a timeout.
1077        //
1078        // Android 10 implementation for reference:
1079        // https://android.googlesource.com/platform/frameworks/native/+/refs/tags/android-mainline-10.0.0_r13/vulkan/libvulkan/swapchain.cpp#1426
1080        // Android 11 implementation for reference:
1081        // https://android.googlesource.com/platform/frameworks/native/+/refs/tags/android-mainline-11.0.0_r45/vulkan/libvulkan/swapchain.cpp#1438
1082        //
1083        // Android 11 corresponds to an SDK_INT/ro.build.version.sdk of 30
1084        if cfg!(target_os = "android") && self.instance.android_sdk_version < 30 {
1085            timeout_ns = u64::MAX;
1086        }
1087
1088        let acquire_semaphore_arc = swapchain.get_acquire_semaphore();
1089        // Nothing should be using this, so we don't block, but panic if we fail to lock.
1090        let acquire_semaphore_guard = acquire_semaphore_arc
1091            .try_lock()
1092            .expect("Failed to lock a SwapchainSemaphores.");
1093
1094        // Wait for all commands writing to the previously acquired image to
1095        // complete.
1096        //
1097        // Almost all the steps in the usual acquire-draw-present flow are
1098        // asynchronous: they get something started on the presentation engine
1099        // or the GPU, but on the CPU, control returns immediately. Without some
1100        // sort of intervention, the CPU could crank out frames much faster than
1101        // the presentation engine can display them.
1102        //
1103        // This is the intervention: if any submissions drew on this image, and
1104        // thus waited for `locked_swapchain_semaphores.acquire`, wait for all
1105        // of them to finish, thus ensuring that it's okay to pass `acquire` to
1106        // `vkAcquireNextImageKHR` again.
1107        swapchain.device.wait_for_fence(
1108            fence,
1109            acquire_semaphore_guard.previously_used_submission_index,
1110            timeout_ns,
1111        )?;
1112
1113        // will block if no image is available
1114        let (index, suboptimal) = match unsafe {
1115            profiling::scope!("vkAcquireNextImageKHR");
1116            swapchain.functor.acquire_next_image(
1117                swapchain.raw,
1118                timeout_ns,
1119                acquire_semaphore_guard.acquire,
1120                swapchain.fence,
1121            )
1122        } {
1123            // We treat `VK_SUBOPTIMAL_KHR` as `VK_SUCCESS` on Android.
1124            // See the comment in `Queue::present`.
1125            #[cfg(target_os = "android")]
1126            Ok((index, _)) => (index, false),
1127            #[cfg(not(target_os = "android"))]
1128            Ok(pair) => pair,
1129            Err(error) => {
1130                return match error {
1131                    vk::Result::TIMEOUT => Ok(None),
1132                    vk::Result::NOT_READY | vk::Result::ERROR_OUT_OF_DATE_KHR => {
1133                        Err(crate::SurfaceError::Outdated)
1134                    }
1135                    vk::Result::ERROR_SURFACE_LOST_KHR => Err(crate::SurfaceError::Lost),
1136                    // We don't use VK_EXT_full_screen_exclusive
1137                    // VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT
1138                    other => Err(super::map_host_device_oom_and_lost_err(other).into()),
1139                };
1140            }
1141        };
1142
1143        // Wait for the image was acquired to be fully ready to be rendered too.
1144        //
1145        // This wait is very important on Windows to avoid bad frame pacing on
1146        // Windows where the Vulkan driver is using a DXGI swapchain. See
1147        // https://github.com/gfx-rs/wgpu/issues/8310 and
1148        // https://github.com/gfx-rs/wgpu/issues/8354 for more details.
1149        //
1150        // On other platforms, this wait may serve to slightly decrease frame
1151        // latency, depending on how the platform implements waiting within
1152        // acquire.
1153        unsafe {
1154            swapchain
1155                .device
1156                .raw
1157                .wait_for_fences(&[swapchain.fence], false, timeout_ns)
1158                .map_err(super::map_host_device_oom_and_lost_err)?;
1159
1160            swapchain
1161                .device
1162                .raw
1163                .reset_fences(&[swapchain.fence])
1164                .map_err(super::map_host_device_oom_and_lost_err)?;
1165        }
1166
1167        drop(acquire_semaphore_guard);
1168        // We only advance the surface semaphores if we successfully acquired an image, otherwise
1169        // we should try to re-acquire using the same semaphores.
1170        swapchain.advance_acquire_semaphore();
1171
1172        let present_semaphore_arc = swapchain.get_present_semaphores(index);
1173
1174        // special case for Intel Vulkan returning bizarre values (ugh)
1175        if swapchain.device.vendor_id == crate::auxil::db::intel::VENDOR && index > 0x100 {
1176            return Err(crate::SurfaceError::Outdated);
1177        }
1178
1179        let identity = swapchain.device.texture_identity_factory.next();
1180
1181        let texture = super::SurfaceTexture {
1182            index,
1183            texture: super::Texture {
1184                raw: swapchain.images[index as usize],
1185                drop_guard: None,
1186                block: None,
1187                external_memory: None,
1188                format: swapchain.config.format,
1189                copy_size: crate::CopyExtent {
1190                    width: swapchain.config.extent.width,
1191                    height: swapchain.config.extent.height,
1192                    depth: 1,
1193                },
1194                identity,
1195            },
1196            acquire_semaphores: acquire_semaphore_arc,
1197            present_semaphores: present_semaphore_arc,
1198        };
1199        Ok(Some(crate::AcquiredSurfaceTexture {
1200            texture,
1201            suboptimal,
1202        }))
1203    }
1204
1205    unsafe fn discard_texture(&self, _texture: super::SurfaceTexture) {}
1206}