Skip to main content

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        // We intentionally suppress info messages down to debug
84        // so that users are not innundated with info messages from the runtime.
85        vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => log::Level::Trace,
86        vk::DebugUtilsMessageSeverityFlagsEXT::INFO => log::Level::Debug,
87        vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => log::Level::Warn,
88        vk::DebugUtilsMessageSeverityFlagsEXT::ERROR => log::Level::Error,
89        _ => log::Level::Warn,
90    };
91
92    let message_id_name =
93        unsafe { cd.message_id_name_as_c_str() }.map_or(Cow::Borrowed(""), CStr::to_string_lossy);
94    let message = unsafe { cd.message_as_c_str() }.map_or(Cow::Borrowed(""), CStr::to_string_lossy);
95
96    let _ = std::panic::catch_unwind(|| {
97        log::log!(
98            level,
99            "{:?} [{} (0x{:x})]\n\t{}",
100            message_type,
101            message_id_name,
102            cd.message_id_number,
103            message,
104        );
105    });
106
107    if cd.queue_label_count != 0 {
108        let labels =
109            unsafe { slice::from_raw_parts(cd.p_queue_labels, cd.queue_label_count as usize) };
110        let names = labels
111            .iter()
112            .flat_map(|dul_obj| unsafe { dul_obj.label_name_as_c_str() }.map(CStr::to_string_lossy))
113            .collect::<Vec<_>>();
114
115        let _ = std::panic::catch_unwind(|| {
116            log::log!(level, "\tqueues: {}", names.join(", "));
117        });
118    }
119
120    if cd.cmd_buf_label_count != 0 {
121        let labels =
122            unsafe { slice::from_raw_parts(cd.p_cmd_buf_labels, cd.cmd_buf_label_count as usize) };
123        let names = labels
124            .iter()
125            .flat_map(|dul_obj| unsafe { dul_obj.label_name_as_c_str() }.map(CStr::to_string_lossy))
126            .collect::<Vec<_>>();
127
128        let _ = std::panic::catch_unwind(|| {
129            log::log!(level, "\tcommand buffers: {}", names.join(", "));
130        });
131    }
132
133    if cd.object_count != 0 {
134        let labels = unsafe { slice::from_raw_parts(cd.p_objects, cd.object_count as usize) };
135        //TODO: use color fields of `vk::DebugUtilsLabelExt`?
136        let names = labels
137            .iter()
138            .map(|obj_info| {
139                let name = unsafe { obj_info.object_name_as_c_str() }
140                    .map_or(Cow::Borrowed("?"), CStr::to_string_lossy);
141
142                format!(
143                    "(type: {:?}, hndl: 0x{:x}, name: {})",
144                    obj_info.object_type, obj_info.object_handle, name
145                )
146            })
147            .collect::<Vec<_>>();
148        let _ = std::panic::catch_unwind(|| {
149            log::log!(level, "\tobjects: {}", names.join(", "));
150        });
151    }
152
153    #[cfg(feature = "validation_canary")]
154    if cfg!(debug_assertions) && level == log::Level::Error {
155        use alloc::string::ToString as _;
156
157        // Set canary and continue
158        crate::VALIDATION_CANARY.add(message.to_string());
159    }
160
161    vk::FALSE
162}
163
164impl super::DebugUtilsCreateInfo {
165    fn to_vk_create_info(&self) -> vk::DebugUtilsMessengerCreateInfoEXT<'_> {
166        let user_data_ptr: *const super::DebugUtilsMessengerUserData = &*self.callback_data;
167        vk::DebugUtilsMessengerCreateInfoEXT::default()
168            .message_severity(self.severity)
169            .message_type(self.message_type)
170            .user_data(user_data_ptr as *mut _)
171            .pfn_user_callback(Some(debug_utils_messenger_callback))
172    }
173}
174
175impl super::InstanceShared {
176    pub fn entry(&self) -> &ash::Entry {
177        &self.entry
178    }
179
180    pub fn raw_instance(&self) -> &ash::Instance {
181        &self.raw
182    }
183
184    pub fn instance_api_version(&self) -> u32 {
185        self.instance_api_version
186    }
187
188    pub fn extensions(&self) -> &[&'static CStr] {
189        &self.extensions[..]
190    }
191}
192
193impl super::Instance {
194    pub fn shared_instance(&self) -> &super::InstanceShared {
195        &self.shared
196    }
197
198    fn enumerate_instance_extension_properties(
199        entry: &ash::Entry,
200        layer_name: Option<&CStr>,
201    ) -> Result<Vec<vk::ExtensionProperties>, crate::InstanceError> {
202        let instance_extensions = {
203            profiling::scope!("vkEnumerateInstanceExtensionProperties");
204            unsafe { entry.enumerate_instance_extension_properties(layer_name) }
205        };
206        instance_extensions.map_err(|e| {
207            crate::InstanceError::with_source(
208                String::from("enumerate_instance_extension_properties() failed"),
209                e,
210            )
211        })
212    }
213
214    /// Return the instance extension names wgpu would like to enable.
215    ///
216    /// Return a vector of the names of instance extensions actually available
217    /// on `entry` that wgpu would like to enable.
218    ///
219    /// The `instance_api_version` argument should be the instance's Vulkan API
220    /// version, as obtained from `vkEnumerateInstanceVersion`. This is the same
221    /// space of values as the `VK_API_VERSION` constants.
222    ///
223    /// Note that wgpu can function without many of these extensions (for
224    /// example, `VK_KHR_wayland_surface` is certainly not going to be available
225    /// everywhere), but if one of these extensions is available at all, wgpu
226    /// assumes that it has been enabled.
227    pub fn desired_extensions(
228        entry: &ash::Entry,
229        _instance_api_version: u32,
230        flags: wgt::InstanceFlags,
231    ) -> Result<Vec<&'static CStr>, crate::InstanceError> {
232        let instance_extensions = Self::enumerate_instance_extension_properties(entry, None)?;
233
234        // Check our extensions against the available extensions
235        let mut extensions: Vec<&'static CStr> = Vec::new();
236
237        // VK_KHR_surface
238        extensions.push(khr::surface::NAME);
239
240        // Platform-specific WSI extensions
241        if cfg!(all(
242            unix,
243            not(target_os = "android"),
244            not(target_os = "macos")
245        )) {
246            // VK_KHR_xlib_surface
247            extensions.push(khr::xlib_surface::NAME);
248            // VK_KHR_xcb_surface
249            extensions.push(khr::xcb_surface::NAME);
250            // VK_KHR_wayland_surface
251            extensions.push(khr::wayland_surface::NAME);
252        }
253        if cfg!(target_os = "android") {
254            // VK_KHR_android_surface
255            extensions.push(khr::android_surface::NAME);
256        }
257        if cfg!(target_os = "windows") {
258            // VK_KHR_win32_surface
259            extensions.push(khr::win32_surface::NAME);
260        }
261        if cfg!(target_os = "macos") {
262            // VK_EXT_metal_surface
263            extensions.push(ext::metal_surface::NAME);
264            extensions.push(khr::portability_enumeration::NAME);
265        }
266        if cfg!(drm) {
267            // VK_EXT_acquire_drm_display -> VK_EXT_direct_mode_display -> VK_KHR_display
268            extensions.push(ext::acquire_drm_display::NAME);
269            extensions.push(ext::direct_mode_display::NAME);
270            extensions.push(khr::display::NAME);
271            extensions.push(khr::get_physical_device_properties2::NAME);
272            extensions.push(khr::get_display_properties2::NAME);
273        }
274
275        if flags.contains(wgt::InstanceFlags::DEBUG) {
276            // VK_EXT_debug_utils
277            extensions.push(ext::debug_utils::NAME);
278        }
279
280        // VK_EXT_swapchain_colorspace
281        // Provides wide color gamut
282        extensions.push(ext::swapchain_colorspace::NAME);
283
284        // VK_KHR_get_physical_device_properties2
285        // Even though the extension was promoted to Vulkan 1.1, we still require the extension
286        // so that we don't have to conditionally use the functions provided by the 1.1 instance
287        extensions.push(khr::get_physical_device_properties2::NAME);
288
289        // Only keep available extensions.
290        extensions.retain(|&ext| {
291            if instance_extensions
292                .iter()
293                .any(|inst_ext| inst_ext.extension_name_as_c_str() == Ok(ext))
294            {
295                true
296            } else {
297                log::debug!("Unable to find extension: {}", ext.to_string_lossy());
298                false
299            }
300        });
301        Ok(extensions)
302    }
303
304    /// # Safety
305    ///
306    /// - `raw_instance` must be created from `entry`
307    /// - `raw_instance` must be created respecting `instance_api_version`, `extensions` and `flags`
308    /// - `extensions` must be a superset of `desired_extensions()` and must be created from the
309    ///   same entry, `instance_api_version`` and flags.
310    /// - `android_sdk_version` is ignored and can be `0` for all platforms besides Android
311    /// - If `drop_callback` is [`None`], wgpu-hal will take ownership of `raw_instance`. If
312    ///   `drop_callback` is [`Some`], `raw_instance` must be valid until the callback is called.
313    ///
314    /// If `debug_utils_user_data` is `Some`, then the validation layer is
315    /// available, so create a [`vk::DebugUtilsMessengerEXT`].
316    #[allow(clippy::too_many_arguments)]
317    pub unsafe fn from_raw(
318        entry: ash::Entry,
319        raw_instance: ash::Instance,
320        instance_api_version: u32,
321        android_sdk_version: u32,
322        debug_utils_create_info: Option<super::DebugUtilsCreateInfo>,
323        extensions: Vec<&'static CStr>,
324        flags: wgt::InstanceFlags,
325        memory_budget_thresholds: wgt::MemoryBudgetThresholds,
326        has_nv_optimus: bool,
327        drop_callback: Option<crate::DropCallback>,
328    ) -> Result<Self, crate::InstanceError> {
329        log::debug!("Instance version: 0x{instance_api_version:x}");
330
331        let debug_utils = if let Some(debug_utils_create_info) = debug_utils_create_info {
332            if extensions.contains(&ext::debug_utils::NAME) {
333                log::debug!("Enabling debug utils");
334
335                let extension = ext::debug_utils::Instance::new(&entry, &raw_instance);
336                let vk_info = debug_utils_create_info.to_vk_create_info();
337                let messenger =
338                    unsafe { extension.create_debug_utils_messenger(&vk_info, None) }.unwrap();
339
340                Some(super::DebugUtils {
341                    extension,
342                    messenger,
343                    callback_data: debug_utils_create_info.callback_data,
344                })
345            } else {
346                log::debug!("Debug utils not enabled: extension not listed");
347                None
348            }
349        } else {
350            log::debug!(
351                "Debug utils not enabled: \
352                        debug_utils_user_data not passed to Instance::from_raw"
353            );
354            None
355        };
356
357        let get_physical_device_properties =
358            if extensions.contains(&khr::get_physical_device_properties2::NAME) {
359                log::debug!("Enabling device properties2");
360                Some(khr::get_physical_device_properties2::Instance::new(
361                    &entry,
362                    &raw_instance,
363                ))
364            } else {
365                None
366            };
367
368        let drop_guard = crate::DropGuard::from_option(drop_callback);
369
370        Ok(Self {
371            shared: Arc::new(super::InstanceShared {
372                raw: raw_instance,
373                extensions,
374                drop_guard,
375                flags,
376                memory_budget_thresholds,
377                debug_utils,
378                get_physical_device_properties,
379                entry,
380                has_nv_optimus,
381                instance_api_version,
382                android_sdk_version,
383            }),
384        })
385    }
386
387    fn create_surface_from_xlib(
388        &self,
389        dpy: *mut vk::Display,
390        window: vk::Window,
391    ) -> Result<super::Surface, crate::InstanceError> {
392        if !self.shared.extensions.contains(&khr::xlib_surface::NAME) {
393            return Err(crate::InstanceError::new(String::from(
394                "Vulkan driver does not support VK_KHR_xlib_surface",
395            )));
396        }
397
398        let surface = {
399            let xlib_loader =
400                khr::xlib_surface::Instance::new(&self.shared.entry, &self.shared.raw);
401            let info = vk::XlibSurfaceCreateInfoKHR::default()
402                .flags(vk::XlibSurfaceCreateFlagsKHR::empty())
403                .window(window)
404                .dpy(dpy);
405
406            unsafe { xlib_loader.create_xlib_surface(&info, None) }
407                .expect("XlibSurface::create_xlib_surface() failed")
408        };
409
410        Ok(self.create_surface_from_vk_surface_khr(surface, None))
411    }
412
413    fn create_surface_from_xcb(
414        &self,
415        connection: *mut vk::xcb_connection_t,
416        window: vk::xcb_window_t,
417    ) -> Result<super::Surface, crate::InstanceError> {
418        if !self.shared.extensions.contains(&khr::xcb_surface::NAME) {
419            return Err(crate::InstanceError::new(String::from(
420                "Vulkan driver does not support VK_KHR_xcb_surface",
421            )));
422        }
423
424        let surface = {
425            let xcb_loader = khr::xcb_surface::Instance::new(&self.shared.entry, &self.shared.raw);
426            let info = vk::XcbSurfaceCreateInfoKHR::default()
427                .flags(vk::XcbSurfaceCreateFlagsKHR::empty())
428                .window(window)
429                .connection(connection);
430
431            unsafe { xcb_loader.create_xcb_surface(&info, None) }
432                .expect("XcbSurface::create_xcb_surface() failed")
433        };
434
435        Ok(self.create_surface_from_vk_surface_khr(surface, None))
436    }
437
438    fn create_surface_from_wayland(
439        &self,
440        display: *mut vk::wl_display,
441        surface: *mut vk::wl_surface,
442    ) -> Result<super::Surface, crate::InstanceError> {
443        if !self.shared.extensions.contains(&khr::wayland_surface::NAME) {
444            return Err(crate::InstanceError::new(String::from(
445                "Vulkan driver does not support VK_KHR_wayland_surface",
446            )));
447        }
448
449        let surface = {
450            let w_loader =
451                khr::wayland_surface::Instance::new(&self.shared.entry, &self.shared.raw);
452            let info = vk::WaylandSurfaceCreateInfoKHR::default()
453                .flags(vk::WaylandSurfaceCreateFlagsKHR::empty())
454                .display(display)
455                .surface(surface);
456
457            unsafe { w_loader.create_wayland_surface(&info, None) }.expect("WaylandSurface failed")
458        };
459
460        Ok(self.create_surface_from_vk_surface_khr(surface, None))
461    }
462
463    fn create_surface_android(
464        &self,
465        window: *mut vk::ANativeWindow,
466    ) -> Result<super::Surface, crate::InstanceError> {
467        if !self.shared.extensions.contains(&khr::android_surface::NAME) {
468            return Err(crate::InstanceError::new(String::from(
469                "Vulkan driver does not support VK_KHR_android_surface",
470            )));
471        }
472
473        let surface = {
474            let a_loader =
475                khr::android_surface::Instance::new(&self.shared.entry, &self.shared.raw);
476            let info = vk::AndroidSurfaceCreateInfoKHR::default()
477                .flags(vk::AndroidSurfaceCreateFlagsKHR::empty())
478                .window(window);
479
480            unsafe { a_loader.create_android_surface(&info, None) }.expect("AndroidSurface failed")
481        };
482
483        Ok(self.create_surface_from_vk_surface_khr(surface, None))
484    }
485
486    fn create_surface_from_hwnd(
487        &self,
488        hinstance: vk::HINSTANCE,
489        hwnd: vk::HWND,
490    ) -> Result<super::Surface, crate::InstanceError> {
491        if !self.shared.extensions.contains(&khr::win32_surface::NAME) {
492            return Err(crate::InstanceError::new(String::from(
493                "Vulkan driver does not support VK_KHR_win32_surface",
494            )));
495        }
496
497        let surface = {
498            let info = vk::Win32SurfaceCreateInfoKHR::default()
499                .flags(vk::Win32SurfaceCreateFlagsKHR::empty())
500                .hinstance(hinstance)
501                .hwnd(hwnd);
502            let win32_loader =
503                khr::win32_surface::Instance::new(&self.shared.entry, &self.shared.raw);
504            unsafe {
505                win32_loader
506                    .create_win32_surface(&info, None)
507                    .expect("Unable to create Win32 surface")
508            }
509        };
510
511        // Wrap ash's `isize` `HWND` in `WindowHandle`; on Windows the
512        // `NativeSurface` builds its DXGI HDR source from it.
513        #[cfg(windows)]
514        let window_handle = Some(crate::vulkan::swapchain::WindowHandle(
515            windows::Win32::Foundation::HWND(hwnd as *mut c_void),
516        ));
517        #[cfg(not(windows))]
518        let window_handle: Option<crate::vulkan::swapchain::WindowHandle> = None;
519        Ok(self.create_surface_from_vk_surface_khr(surface, window_handle))
520    }
521
522    #[cfg(target_vendor = "apple")]
523    fn create_surface_from_layer(
524        &self,
525        layer: raw_window_metal::Layer,
526    ) -> Result<super::Surface, crate::InstanceError> {
527        if !self.shared.extensions.contains(&ext::metal_surface::NAME) {
528            return Err(crate::InstanceError::new(String::from(
529                "Vulkan driver does not support VK_EXT_metal_surface",
530            )));
531        }
532
533        // NOTE: The layer is retained by Vulkan's `vkCreateMetalSurfaceEXT`,
534        // so no need to retain it beyond the scope of this function.
535        let surface = {
536            let metal_loader =
537                ext::metal_surface::Instance::new(&self.shared.entry, &self.shared.raw);
538            let vk_info = vk::MetalSurfaceCreateInfoEXT::default()
539                .flags(vk::MetalSurfaceCreateFlagsEXT::empty())
540                .layer(layer.as_ptr().as_ptr());
541
542            unsafe { metal_loader.create_metal_surface(&vk_info, None).unwrap() }
543        };
544
545        Ok(self.create_surface_from_vk_surface_khr(surface, None))
546    }
547
548    pub(super) fn create_surface_from_vk_surface_khr(
549        &self,
550        surface: vk::SurfaceKHR,
551        hwnd: Option<crate::vulkan::swapchain::WindowHandle>,
552    ) -> super::Surface {
553        let native_surface =
554            crate::vulkan::swapchain::NativeSurface::from_vk_surface_khr(self, surface, hwnd);
555
556        super::Surface {
557            swapchain: RwLock::new(None),
558            inner: Box::new(native_surface),
559        }
560    }
561
562    /// `Instance::init` but with a callback.
563    /// If you want to add extensions, add the to the `Vec<'static CStr>` not the create info, otherwise
564    /// it will be overwritten
565    ///
566    /// # Safety:
567    /// Same as `init` but additionally
568    /// - Callback must not remove features.
569    /// - Callback must not change anything to what the instance does not support.
570    pub unsafe fn init_with_callback(
571        desc: &crate::InstanceDescriptor<'_>,
572        callback: Option<Box<super::CreateInstanceCallback>>,
573    ) -> Result<Self, crate::InstanceError> {
574        profiling::scope!("Init Vulkan Backend");
575
576        let entry = unsafe {
577            profiling::scope!("Load vk library");
578            // ohos support is already fixed on ash main, but it's unclear when
579            // a new release can happen.
580            #[cfg(target_env = "ohos")]
581            let loaded = ash::Entry::load_from("libvulkan.so");
582            #[cfg(not(target_env = "ohos"))]
583            let loaded = ash::Entry::load();
584            loaded
585        }
586        .map_err(|err| {
587            crate::InstanceError::with_source(String::from("missing Vulkan entry points"), err)
588        })?;
589        let version = {
590            profiling::scope!("vkEnumerateInstanceVersion");
591            unsafe { entry.try_enumerate_instance_version() }
592        };
593        let instance_api_version = match version {
594            // Vulkan 1.1+
595            Ok(Some(version)) => version,
596            Ok(None) => vk::API_VERSION_1_0,
597            Err(err) => {
598                return Err(crate::InstanceError::with_source(
599                    String::from("try_enumerate_instance_version() failed"),
600                    err,
601                ));
602            }
603        };
604
605        let app_name = CString::new(desc.name).unwrap();
606        let app_info = vk::ApplicationInfo::default()
607            .application_name(app_name.as_c_str())
608            .application_version(1)
609            .engine_name(c"wgpu-hal")
610            .engine_version(2)
611            .api_version(
612                // Vulkan 1.0 doesn't like anything but 1.0 passed in here...
613                if instance_api_version < vk::API_VERSION_1_1 {
614                    vk::API_VERSION_1_0
615                } else {
616                    // This is the max Vulkan API version supported by `wgpu-hal`.
617                    //
618                    // If we want to increment this, there are some things that must be done first:
619                    //  - Audit the behavioral differences between the previous and new API versions.
620                    //  - Audit all extensions used by this backend:
621                    //    - 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.
622                    //    - If any were obsoleted in the new API version, we must implement a fallback for the new API version
623                    //    - If any are non-KHR-vendored, we must ensure the new behavior is still correct (since backwards-compatibility is not guaranteed).
624                    vk::API_VERSION_1_3
625                },
626            );
627
628        let mut extensions = Self::desired_extensions(&entry, instance_api_version, desc.flags)?;
629        let mut create_info = vk::InstanceCreateInfo::default();
630
631        if let Some(callback) = callback {
632            callback(super::CreateInstanceCallbackArgs {
633                extensions: &mut extensions,
634                create_info: &mut create_info,
635                entry: &entry,
636                _phantom: PhantomData,
637            });
638        }
639
640        let instance_layers = {
641            profiling::scope!("vkEnumerateInstanceLayerProperties");
642            unsafe { entry.enumerate_instance_layer_properties() }
643        };
644        let instance_layers = instance_layers.map_err(|e| {
645            log::debug!("enumerate_instance_layer_properties: {e:?}");
646            crate::InstanceError::with_source(
647                String::from("enumerate_instance_layer_properties() failed"),
648                e,
649            )
650        })?;
651
652        fn find_layer<'layers>(
653            instance_layers: &'layers [vk::LayerProperties],
654            name: &CStr,
655        ) -> Option<&'layers vk::LayerProperties> {
656            instance_layers
657                .iter()
658                .find(|inst_layer| inst_layer.layer_name_as_c_str() == Ok(name))
659        }
660
661        let validation_layer_name = c"VK_LAYER_KHRONOS_validation";
662        let validation_layer_properties = find_layer(&instance_layers, validation_layer_name);
663
664        // Determine if VK_EXT_validation_features is available, so we can enable
665        // GPU assisted validation and synchronization validation.
666        let validation_features_are_enabled = if validation_layer_properties.is_some() {
667            // Get the all the instance extension properties.
668            let exts =
669                Self::enumerate_instance_extension_properties(&entry, Some(validation_layer_name))?;
670            // Convert all the names of the extensions into an iterator of CStrs.
671            let mut ext_names = exts
672                .iter()
673                .filter_map(|ext| ext.extension_name_as_c_str().ok());
674            // Find the validation features extension.
675            ext_names.any(|ext_name| ext_name == ext::validation_features::NAME)
676        } else {
677            false
678        };
679
680        let should_enable_gpu_based_validation = desc
681            .flags
682            .intersects(wgt::InstanceFlags::GPU_BASED_VALIDATION)
683            && validation_features_are_enabled;
684
685        let has_nv_optimus = find_layer(&instance_layers, c"VK_LAYER_NV_optimus").is_some();
686
687        let has_obs_layer = find_layer(&instance_layers, c"VK_LAYER_OBS_HOOK").is_some();
688
689        let mut layers: Vec<&'static CStr> = Vec::new();
690
691        let has_debug_extension = extensions.contains(&ext::debug_utils::NAME);
692        let mut debug_user_data = has_debug_extension.then(|| {
693            // Put the callback data on the heap, to ensure it will never be
694            // moved.
695            Box::new(super::DebugUtilsMessengerUserData {
696                validation_layer_properties: None,
697                has_obs_layer,
698            })
699        });
700
701        // Request validation layer if asked.
702        if desc.flags.intersects(wgt::InstanceFlags::VALIDATION)
703            || should_enable_gpu_based_validation
704        {
705            if let Some(layer_properties) = validation_layer_properties {
706                layers.push(validation_layer_name);
707
708                if let Some(debug_user_data) = debug_user_data.as_mut() {
709                    debug_user_data.validation_layer_properties =
710                        Some(super::ValidationLayerProperties {
711                            layer_description: layer_properties
712                                .description_as_c_str()
713                                .unwrap()
714                                .to_owned(),
715                            layer_spec_version: layer_properties.spec_version,
716                        });
717                }
718            } else {
719                log::debug!(
720                    "InstanceFlags::VALIDATION requested, but unable to find layer: {}",
721                    validation_layer_name.to_string_lossy()
722                );
723            }
724        }
725        let mut debug_utils = if let Some(callback_data) = debug_user_data {
726            // having ERROR unconditionally because Vk doesn't like empty flags
727            let mut severity = vk::DebugUtilsMessageSeverityFlagsEXT::ERROR;
728            if log::max_level() >= log::LevelFilter::Debug {
729                severity |= vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE;
730            }
731            if log::max_level() >= log::LevelFilter::Info {
732                severity |= vk::DebugUtilsMessageSeverityFlagsEXT::INFO;
733            }
734            if log::max_level() >= log::LevelFilter::Warn {
735                severity |= vk::DebugUtilsMessageSeverityFlagsEXT::WARNING;
736            }
737
738            let message_type = vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
739                | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION
740                | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE;
741
742            let create_info = super::DebugUtilsCreateInfo {
743                severity,
744                message_type,
745                callback_data,
746            };
747
748            Some(create_info)
749        } else {
750            None
751        };
752
753        #[cfg(target_os = "android")]
754        let android_sdk_version = {
755            let properties = android_system_properties::AndroidSystemProperties::new();
756            // See: https://developer.android.com/reference/android/os/Build.VERSION_CODES
757            if let Some(val) = properties.get("ro.build.version.sdk") {
758                match val.parse::<u32>() {
759                    Ok(sdk_ver) => sdk_ver,
760                    Err(err) => {
761                        log::error!(
762                            concat!(
763                                "Couldn't parse Android's ",
764                                "ro.build.version.sdk system property ({}): {}",
765                            ),
766                            val,
767                            err,
768                        );
769                        0
770                    }
771                }
772            } else {
773                log::error!("Couldn't read Android's ro.build.version.sdk system property");
774                0
775            }
776        };
777        #[cfg(not(target_os = "android"))]
778        let android_sdk_version = 0;
779
780        let mut flags = vk::InstanceCreateFlags::empty();
781
782        // Avoid VUID-VkInstanceCreateInfo-flags-06559: Only ask the instance to
783        // enumerate incomplete Vulkan implementations (which we need on Mac) if
784        // we managed to find the extension that provides the flag.
785        if extensions.contains(&khr::portability_enumeration::NAME) {
786            flags |= vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR;
787        }
788        let vk_instance = {
789            let str_pointers = layers
790                .iter()
791                .chain(extensions.iter())
792                .map(|&s: &&'static _| {
793                    // Safe because `layers` and `extensions` entries have static lifetime.
794                    s.as_ptr()
795                })
796                .collect::<Vec<_>>();
797
798            create_info = create_info
799                .flags(flags)
800                .application_info(&app_info)
801                .enabled_layer_names(&str_pointers[..layers.len()])
802                .enabled_extension_names(&str_pointers[layers.len()..]);
803
804            let mut debug_utils_create_info = debug_utils
805                .as_mut()
806                .map(|create_info| create_info.to_vk_create_info());
807            if let Some(debug_utils_create_info) = debug_utils_create_info.as_mut() {
808                create_info = create_info.push_next(debug_utils_create_info);
809            }
810
811            // Enable explicit validation features if available
812            let mut validation_features;
813            let mut validation_feature_list: ArrayVec<_, 3>;
814            if validation_features_are_enabled {
815                validation_feature_list = ArrayVec::new();
816
817                // Always enable synchronization validation
818                validation_feature_list
819                    .push(vk::ValidationFeatureEnableEXT::SYNCHRONIZATION_VALIDATION);
820
821                // Only enable GPU assisted validation if requested.
822                if should_enable_gpu_based_validation {
823                    validation_feature_list.push(vk::ValidationFeatureEnableEXT::GPU_ASSISTED);
824                    validation_feature_list
825                        .push(vk::ValidationFeatureEnableEXT::GPU_ASSISTED_RESERVE_BINDING_SLOT);
826                }
827
828                validation_features = vk::ValidationFeaturesEXT::default()
829                    .enabled_validation_features(&validation_feature_list);
830                create_info = create_info.push_next(&mut validation_features);
831            }
832
833            unsafe {
834                profiling::scope!("vkCreateInstance");
835                entry.create_instance(&create_info, None)
836            }
837            .map_err(|e| {
838                crate::InstanceError::with_source(
839                    String::from("Entry::create_instance() failed"),
840                    e,
841                )
842            })?
843        };
844
845        unsafe {
846            Self::from_raw(
847                entry,
848                vk_instance,
849                instance_api_version,
850                android_sdk_version,
851                debug_utils,
852                extensions,
853                desc.flags,
854                desc.memory_budget_thresholds,
855                has_nv_optimus,
856                None,
857            )
858        }
859    }
860}
861
862impl Drop for super::InstanceShared {
863    fn drop(&mut self) {
864        unsafe {
865            // Keep du alive since destroy_instance may also log
866            let _du = self.debug_utils.take().inspect(|du| {
867                du.extension
868                    .destroy_debug_utils_messenger(du.messenger, None);
869            });
870            if self.drop_guard.is_none() {
871                self.raw.destroy_instance(None);
872            }
873        }
874    }
875}
876
877impl crate::Instance for super::Instance {
878    type A = super::Api;
879
880    unsafe fn init(desc: &crate::InstanceDescriptor<'_>) -> Result<Self, crate::InstanceError> {
881        unsafe { Self::init_with_callback(desc, None) }
882    }
883
884    unsafe fn create_surface(
885        &self,
886        display_handle: raw_window_handle::RawDisplayHandle,
887        window_handle: raw_window_handle::RawWindowHandle,
888    ) -> Result<super::Surface, crate::InstanceError> {
889        use raw_window_handle::{RawDisplayHandle as Rdh, RawWindowHandle as Rwh};
890
891        // TODO: Replace with ash-window, which also lazy-loads the extension based on handle type
892
893        match (window_handle, display_handle) {
894            (Rwh::Wayland(handle), Rdh::Wayland(display)) => {
895                self.create_surface_from_wayland(display.display.as_ptr(), handle.surface.as_ptr())
896            }
897            (Rwh::Xlib(handle), Rdh::Xlib(display)) => {
898                let display = display.display.expect("Display pointer is not set.");
899                self.create_surface_from_xlib(display.as_ptr(), handle.window)
900            }
901            (Rwh::Xcb(handle), Rdh::Xcb(display)) => {
902                let connection = display.connection.expect("Pointer to X-Server is not set.");
903                self.create_surface_from_xcb(connection.as_ptr(), handle.window.get())
904            }
905            #[cfg(drm)]
906            (Rwh::Drm(handle), Rdh::Drm(display)) => {
907                self.create_surface_from_drm_plane(display.fd, handle.plane)
908            }
909            (Rwh::AndroidNdk(handle), _) => {
910                self.create_surface_android(handle.a_native_window.as_ptr())
911            }
912            (Rwh::Win32(handle), _) => {
913                let hinstance = handle.hinstance.ok_or_else(|| {
914                    crate::InstanceError::new(String::from(
915                        "Vulkan requires raw-window-handle's Win32::hinstance to be set",
916                    ))
917                })?;
918                self.create_surface_from_hwnd(hinstance.get(), handle.hwnd.get())
919            }
920            #[cfg(target_vendor = "apple")]
921            (Rwh::AppKit(handle), _)
922                if self.shared.extensions.contains(&ext::metal_surface::NAME) =>
923            {
924                let layer = unsafe { raw_window_metal::Layer::from_ns_view(handle.ns_view) };
925                self.create_surface_from_layer(layer)
926            }
927            #[cfg(target_vendor = "apple")]
928            (Rwh::UiKit(handle), _)
929                if self.shared.extensions.contains(&ext::metal_surface::NAME) =>
930            {
931                let layer = unsafe { raw_window_metal::Layer::from_ui_view(handle.ui_view) };
932                self.create_surface_from_layer(layer)
933            }
934            (_, _) => Err(crate::InstanceError::new(format!(
935                "window handle {window_handle:?} is not a Vulkan-compatible handle"
936            ))),
937        }
938    }
939
940    unsafe fn enumerate_adapters(
941        &self,
942        _surface_hint: Option<&super::Surface>,
943    ) -> Vec<crate::ExposedAdapter<super::Api>> {
944        use crate::auxil::db;
945
946        let raw_devices = match unsafe { self.shared.raw.enumerate_physical_devices() } {
947            Ok(devices) => devices,
948            Err(err) => {
949                log::error!("enumerate_adapters: {err}");
950                Vec::new()
951            }
952        };
953
954        let mut exposed_adapters = raw_devices
955            .into_iter()
956            .flat_map(|device| self.expose_adapter(device))
957            .collect::<Vec<_>>();
958
959        // Detect if it's an Intel + NVidia configuration with Optimus
960        let has_nvidia_dgpu = exposed_adapters.iter().any(|exposed| {
961            exposed.info.device_type == wgt::DeviceType::DiscreteGpu
962                && exposed.info.vendor == db::nvidia::VENDOR
963        });
964        if cfg!(target_os = "linux") && has_nvidia_dgpu && self.shared.has_nv_optimus {
965            for exposed in exposed_adapters.iter_mut() {
966                if exposed.info.device_type == wgt::DeviceType::IntegratedGpu
967                    && exposed.info.vendor == db::intel::VENDOR
968                {
969                    // Check if mesa driver and version less than 21.2
970                    if let Some(version) = exposed.info.driver_info.split_once("Mesa ").map(|s| {
971                        let mut components = s.1.split('.');
972                        let major = components.next().and_then(|s| u8::from_str(s).ok());
973                        let minor = components.next().and_then(|s| u8::from_str(s).ok());
974                        if let (Some(major), Some(minor)) = (major, minor) {
975                            (major, minor)
976                        } else {
977                            (0, 0)
978                        }
979                    }) {
980                        if version < (21, 2) {
981                            // See https://gitlab.freedesktop.org/mesa/mesa/-/issues/4688
982                            log::debug!(
983                                concat!(
984                                    "Disabling presentation on '{}' (id {:?}) ",
985                                    "due to NV Optimus and Intel Mesa < v21.2"
986                                ),
987                                exposed.info.name,
988                                exposed.adapter.raw
989                            );
990                            exposed.adapter.private_caps.can_present = false;
991                        }
992                    }
993                }
994            }
995        }
996
997        exposed_adapters
998    }
999}
1000
1001impl crate::Surface for super::Surface {
1002    type A = super::Api;
1003
1004    unsafe fn configure(
1005        &self,
1006        device: &super::Device,
1007        config: &crate::SurfaceConfiguration,
1008    ) -> Result<(), crate::SurfaceError> {
1009        // SAFETY: `configure`'s contract guarantees there are no resources derived from the swapchain in use.
1010        let mut swap_chain = self.swapchain.write();
1011
1012        let mut old = swap_chain.take();
1013        if let Some(ref mut old) = old {
1014            unsafe { old.release_resources(device) };
1015        }
1016
1017        let swapchain = unsafe { self.inner.create_swapchain(device, config, old)? };
1018        *swap_chain = Some(swapchain);
1019
1020        Ok(())
1021    }
1022
1023    unsafe fn unconfigure(&self, device: &super::Device) {
1024        if let Some(mut sc) = self.swapchain.write().take() {
1025            // SAFETY: `unconfigure`'s contract guarantees there are no resources derived from the swapchain in use.
1026            unsafe { sc.release_resources(device) };
1027        }
1028    }
1029
1030    unsafe fn acquire_texture(
1031        &self,
1032        timeout: Option<core::time::Duration>,
1033        fence: &super::Fence,
1034    ) -> Result<crate::AcquiredSurfaceTexture<super::Api>, crate::SurfaceError> {
1035        let mut swapchain = self.swapchain.write();
1036        let swapchain = swapchain.as_mut().unwrap();
1037
1038        unsafe { swapchain.acquire(timeout, fence) }
1039    }
1040
1041    unsafe fn discard_texture(&self, texture: super::SurfaceTexture) {
1042        unsafe {
1043            self.swapchain
1044                .write()
1045                .as_mut()
1046                .unwrap()
1047                .discard_texture(texture)
1048                .unwrap()
1049        };
1050    }
1051}