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