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