wgpu_hal/vulkan/
instance.rs

1use std::{
2    ffi::{c_void, CStr, CString},
3    slice,
4    str::FromStr,
5    sync::Arc,
6    thread,
7};
8
9use arrayvec::ArrayVec;
10use ash::{ext, khr, vk};
11use parking_lot::RwLock;
12
13unsafe extern "system" fn debug_utils_messenger_callback(
14    message_severity: vk::DebugUtilsMessageSeverityFlagsEXT,
15    message_type: vk::DebugUtilsMessageTypeFlagsEXT,
16    callback_data_ptr: *const vk::DebugUtilsMessengerCallbackDataEXT,
17    user_data: *mut c_void,
18) -> vk::Bool32 {
19    use std::borrow::Cow;
20
21    if thread::panicking() {
22        return vk::FALSE;
23    }
24
25    let cd = unsafe { &*callback_data_ptr };
26    let user_data = unsafe { &*user_data.cast::<super::DebugUtilsMessengerUserData>() };
27
28    const VUID_VKCMDENDDEBUGUTILSLABELEXT_COMMANDBUFFER_01912: i32 = 0x56146426;
29    if cd.message_id_number == VUID_VKCMDENDDEBUGUTILSLABELEXT_COMMANDBUFFER_01912 {
30        // https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/5671
31        // Versions 1.3.240 through 1.3.250 return a spurious error here if
32        // the debug range start and end appear in different command buffers.
33        const KHRONOS_VALIDATION_LAYER: &CStr =
34            unsafe { CStr::from_bytes_with_nul_unchecked(b"Khronos Validation Layer\0") };
35        if let Some(layer_properties) = user_data.validation_layer_properties.as_ref() {
36            if layer_properties.layer_description.as_ref() == 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    let level = match message_severity {
67        vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => log::Level::Debug,
68        vk::DebugUtilsMessageSeverityFlagsEXT::INFO => log::Level::Info,
69        vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => log::Level::Warn,
70        vk::DebugUtilsMessageSeverityFlagsEXT::ERROR => log::Level::Error,
71        _ => log::Level::Warn,
72    };
73
74    let message_id_name =
75        unsafe { cd.message_id_name_as_c_str() }.map_or(Cow::Borrowed(""), CStr::to_string_lossy);
76    let message = unsafe { cd.message_as_c_str() }.map_or(Cow::Borrowed(""), CStr::to_string_lossy);
77
78    let _ = std::panic::catch_unwind(|| {
79        log::log!(
80            level,
81            "{:?} [{} (0x{:x})]\n\t{}",
82            message_type,
83            message_id_name,
84            cd.message_id_number,
85            message,
86        );
87    });
88
89    if cd.queue_label_count != 0 {
90        let labels =
91            unsafe { slice::from_raw_parts(cd.p_queue_labels, cd.queue_label_count as usize) };
92        let names = labels
93            .iter()
94            .flat_map(|dul_obj| unsafe { dul_obj.label_name_as_c_str() }.map(CStr::to_string_lossy))
95            .collect::<Vec<_>>();
96
97        let _ = std::panic::catch_unwind(|| {
98            log::log!(level, "\tqueues: {}", names.join(", "));
99        });
100    }
101
102    if cd.cmd_buf_label_count != 0 {
103        let labels =
104            unsafe { slice::from_raw_parts(cd.p_cmd_buf_labels, cd.cmd_buf_label_count as usize) };
105        let names = labels
106            .iter()
107            .flat_map(|dul_obj| unsafe { dul_obj.label_name_as_c_str() }.map(CStr::to_string_lossy))
108            .collect::<Vec<_>>();
109
110        let _ = std::panic::catch_unwind(|| {
111            log::log!(level, "\tcommand buffers: {}", names.join(", "));
112        });
113    }
114
115    if cd.object_count != 0 {
116        let labels = unsafe { slice::from_raw_parts(cd.p_objects, cd.object_count as usize) };
117        //TODO: use color fields of `vk::DebugUtilsLabelExt`?
118        let names = labels
119            .iter()
120            .map(|obj_info| {
121                let name = unsafe { obj_info.object_name_as_c_str() }
122                    .map_or(Cow::Borrowed("?"), CStr::to_string_lossy);
123
124                format!(
125                    "(type: {:?}, hndl: 0x{:x}, name: {})",
126                    obj_info.object_type, obj_info.object_handle, name
127                )
128            })
129            .collect::<Vec<_>>();
130        let _ = std::panic::catch_unwind(|| {
131            log::log!(level, "\tobjects: {}", names.join(", "));
132        });
133    }
134
135    if cfg!(debug_assertions) && level == log::Level::Error {
136        // Set canary and continue
137        crate::VALIDATION_CANARY.add(message.to_string());
138    }
139
140    vk::FALSE
141}
142
143impl super::DebugUtilsCreateInfo {
144    fn to_vk_create_info(&self) -> vk::DebugUtilsMessengerCreateInfoEXT<'_> {
145        let user_data_ptr: *const super::DebugUtilsMessengerUserData = &*self.callback_data;
146        vk::DebugUtilsMessengerCreateInfoEXT::default()
147            .message_severity(self.severity)
148            .message_type(self.message_type)
149            .user_data(user_data_ptr as *mut _)
150            .pfn_user_callback(Some(debug_utils_messenger_callback))
151    }
152}
153
154impl super::Swapchain {
155    /// # Safety
156    ///
157    /// - The device must have been made idle before calling this function.
158    unsafe fn release_resources(mut self, device: &ash::Device) -> Self {
159        profiling::scope!("Swapchain::release_resources");
160        {
161            profiling::scope!("vkDeviceWaitIdle");
162            // We need to also wait until all presentation work is done. Because there is no way to portably wait until
163            // the presentation work is done, we are forced to wait until the device is idle.
164            let _ = unsafe {
165                device
166                    .device_wait_idle()
167                    .map_err(super::map_host_device_oom_and_lost_err)
168            };
169        };
170
171        // We cannot take this by value, as the function returns `self`.
172        for semaphore in self.surface_semaphores.drain(..) {
173            let arc_removed = Arc::into_inner(semaphore).expect(
174                "Trying to destroy a SurfaceSemaphores that is still in use by a SurfaceTexture",
175            );
176            let mutex_removed = arc_removed.into_inner();
177
178            unsafe { mutex_removed.destroy(device) };
179        }
180
181        self
182    }
183}
184
185impl super::InstanceShared {
186    pub fn entry(&self) -> &ash::Entry {
187        &self.entry
188    }
189
190    pub fn raw_instance(&self) -> &ash::Instance {
191        &self.raw
192    }
193
194    pub fn instance_api_version(&self) -> u32 {
195        self.instance_api_version
196    }
197
198    pub fn extensions(&self) -> &[&'static CStr] {
199        &self.extensions[..]
200    }
201}
202
203impl super::Instance {
204    pub fn shared_instance(&self) -> &super::InstanceShared {
205        &self.shared
206    }
207
208    fn enumerate_instance_extension_properties(
209        entry: &ash::Entry,
210        layer_name: Option<&CStr>,
211    ) -> Result<Vec<vk::ExtensionProperties>, crate::InstanceError> {
212        let instance_extensions = {
213            profiling::scope!("vkEnumerateInstanceExtensionProperties");
214            unsafe { entry.enumerate_instance_extension_properties(layer_name) }
215        };
216        instance_extensions.map_err(|e| {
217            crate::InstanceError::with_source(
218                String::from("enumerate_instance_extension_properties() failed"),
219                e,
220            )
221        })
222    }
223
224    /// Return the instance extension names wgpu would like to enable.
225    ///
226    /// Return a vector of the names of instance extensions actually available
227    /// on `entry` that wgpu would like to enable.
228    ///
229    /// The `instance_api_version` argument should be the instance's Vulkan API
230    /// version, as obtained from `vkEnumerateInstanceVersion`. This is the same
231    /// space of values as the `VK_API_VERSION` constants.
232    ///
233    /// Note that wgpu can function without many of these extensions (for
234    /// example, `VK_KHR_wayland_surface` is certainly not going to be available
235    /// everywhere), but if one of these extensions is available at all, wgpu
236    /// assumes that it has been enabled.
237    pub fn desired_extensions(
238        entry: &ash::Entry,
239        _instance_api_version: u32,
240        flags: wgt::InstanceFlags,
241    ) -> Result<Vec<&'static CStr>, crate::InstanceError> {
242        let instance_extensions = Self::enumerate_instance_extension_properties(entry, None)?;
243
244        // Check our extensions against the available extensions
245        let mut extensions: Vec<&'static CStr> = Vec::new();
246
247        // VK_KHR_surface
248        extensions.push(khr::surface::NAME);
249
250        // Platform-specific WSI extensions
251        if cfg!(all(
252            unix,
253            not(target_os = "android"),
254            not(target_os = "macos")
255        )) {
256            // VK_KHR_xlib_surface
257            extensions.push(khr::xlib_surface::NAME);
258            // VK_KHR_xcb_surface
259            extensions.push(khr::xcb_surface::NAME);
260            // VK_KHR_wayland_surface
261            extensions.push(khr::wayland_surface::NAME);
262        }
263        if cfg!(target_os = "android") {
264            // VK_KHR_android_surface
265            extensions.push(khr::android_surface::NAME);
266        }
267        if cfg!(target_os = "windows") {
268            // VK_KHR_win32_surface
269            extensions.push(khr::win32_surface::NAME);
270        }
271        if cfg!(target_os = "macos") {
272            // VK_EXT_metal_surface
273            extensions.push(ext::metal_surface::NAME);
274            extensions.push(khr::portability_enumeration::NAME);
275        }
276
277        if flags.contains(wgt::InstanceFlags::DEBUG) {
278            // VK_EXT_debug_utils
279            extensions.push(ext::debug_utils::NAME);
280        }
281
282        // VK_EXT_swapchain_colorspace
283        // Provides wide color gamut
284        extensions.push(ext::swapchain_colorspace::NAME);
285
286        // VK_KHR_get_physical_device_properties2
287        // Even though the extension was promoted to Vulkan 1.1, we still require the extension
288        // so that we don't have to conditionally use the functions provided by the 1.1 instance
289        extensions.push(khr::get_physical_device_properties2::NAME);
290
291        // Only keep available extensions.
292        extensions.retain(|&ext| {
293            if instance_extensions
294                .iter()
295                .any(|inst_ext| inst_ext.extension_name_as_c_str() == Ok(ext))
296            {
297                true
298            } else {
299                log::warn!("Unable to find extension: {}", ext.to_string_lossy());
300                false
301            }
302        });
303        Ok(extensions)
304    }
305
306    /// # Safety
307    ///
308    /// - `raw_instance` must be created from `entry`
309    /// - `raw_instance` must be created respecting `instance_api_version`, `extensions` and `flags`
310    /// - `extensions` must be a superset of `desired_extensions()` and must be created from the
311    ///   same entry, `instance_api_version`` and flags.
312    /// - `android_sdk_version` is ignored and can be `0` for all platforms besides Android
313    /// - If `drop_callback` is [`None`], wgpu-hal will take ownership of `raw_instance`. If
314    ///   `drop_callback` is [`Some`], `raw_instance` must be valid until the callback is called.
315    ///
316    /// If `debug_utils_user_data` is `Some`, then the validation layer is
317    /// available, so create a [`vk::DebugUtilsMessengerEXT`].
318    #[allow(clippy::too_many_arguments)]
319    pub unsafe fn from_raw(
320        entry: ash::Entry,
321        raw_instance: ash::Instance,
322        instance_api_version: u32,
323        android_sdk_version: u32,
324        debug_utils_create_info: Option<super::DebugUtilsCreateInfo>,
325        extensions: Vec<&'static CStr>,
326        flags: wgt::InstanceFlags,
327        has_nv_optimus: bool,
328        drop_callback: Option<crate::DropCallback>,
329    ) -> Result<Self, crate::InstanceError> {
330        log::debug!("Instance version: 0x{:x}", instance_api_version);
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::info!("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                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))
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))
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))
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))
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        Ok(self.create_surface_from_vk_surface_khr(surface))
512    }
513
514    #[cfg(metal)]
515    fn create_surface_from_view(
516        &self,
517        view: std::ptr::NonNull<c_void>,
518    ) -> Result<super::Surface, crate::InstanceError> {
519        if !self.shared.extensions.contains(&ext::metal_surface::NAME) {
520            return Err(crate::InstanceError::new(String::from(
521                "Vulkan driver does not support VK_EXT_metal_surface",
522            )));
523        }
524
525        let layer = unsafe { crate::metal::Surface::get_metal_layer(view.cast()) };
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 layer_ptr = (*layer).cast();
529
530        let surface = {
531            let metal_loader =
532                ext::metal_surface::Instance::new(&self.shared.entry, &self.shared.raw);
533            let vk_info = vk::MetalSurfaceCreateInfoEXT::default()
534                .flags(vk::MetalSurfaceCreateFlagsEXT::empty())
535                .layer(layer_ptr);
536
537            unsafe { metal_loader.create_metal_surface(&vk_info, None).unwrap() }
538        };
539
540        Ok(self.create_surface_from_vk_surface_khr(surface))
541    }
542
543    fn create_surface_from_vk_surface_khr(&self, surface: vk::SurfaceKHR) -> super::Surface {
544        let functor = khr::surface::Instance::new(&self.shared.entry, &self.shared.raw);
545        super::Surface {
546            raw: surface,
547            functor,
548            instance: Arc::clone(&self.shared),
549            swapchain: RwLock::new(None),
550        }
551    }
552}
553
554impl Drop for super::InstanceShared {
555    fn drop(&mut self) {
556        unsafe {
557            // Keep du alive since destroy_instance may also log
558            let _du = self.debug_utils.take().inspect(|du| {
559                du.extension
560                    .destroy_debug_utils_messenger(du.messenger, None);
561            });
562            if self.drop_guard.is_none() {
563                self.raw.destroy_instance(None);
564            }
565        }
566    }
567}
568
569impl crate::Instance for super::Instance {
570    type A = super::Api;
571
572    unsafe fn init(desc: &crate::InstanceDescriptor) -> Result<Self, crate::InstanceError> {
573        profiling::scope!("Init Vulkan Backend");
574
575        let entry = unsafe {
576            profiling::scope!("Load vk library");
577            ash::Entry::load()
578        }
579        .map_err(|err| {
580            crate::InstanceError::with_source(String::from("missing Vulkan entry points"), err)
581        })?;
582        let version = {
583            profiling::scope!("vkEnumerateInstanceVersion");
584            unsafe { entry.try_enumerate_instance_version() }
585        };
586        let instance_api_version = match version {
587            // Vulkan 1.1+
588            Ok(Some(version)) => version,
589            Ok(None) => vk::API_VERSION_1_0,
590            Err(err) => {
591                return Err(crate::InstanceError::with_source(
592                    String::from("try_enumerate_instance_version() failed"),
593                    err,
594                ));
595            }
596        };
597
598        let app_name = CString::new(desc.name).unwrap();
599        let app_info = vk::ApplicationInfo::default()
600            .application_name(app_name.as_c_str())
601            .application_version(1)
602            .engine_name(CStr::from_bytes_with_nul(b"wgpu-hal\0").unwrap())
603            .engine_version(2)
604            .api_version(
605                // Vulkan 1.0 doesn't like anything but 1.0 passed in here...
606                if instance_api_version < vk::API_VERSION_1_1 {
607                    vk::API_VERSION_1_0
608                } else {
609                    // This is the max Vulkan API version supported by `wgpu-hal`.
610                    //
611                    // If we want to increment this, there are some things that must be done first:
612                    //  - Audit the behavioral differences between the previous and new API versions.
613                    //  - Audit all extensions used by this backend:
614                    //    - 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.
615                    //    - If any were obsoleted in the new API version, we must implement a fallback for the new API version
616                    //    - If any are non-KHR-vendored, we must ensure the new behavior is still correct (since backwards-compatibility is not guaranteed).
617                    vk::API_VERSION_1_3
618                },
619            );
620
621        let extensions = Self::desired_extensions(&entry, instance_api_version, desc.flags)?;
622
623        let instance_layers = {
624            profiling::scope!("vkEnumerateInstanceLayerProperties");
625            unsafe { entry.enumerate_instance_layer_properties() }
626        };
627        let instance_layers = instance_layers.map_err(|e| {
628            log::debug!("enumerate_instance_layer_properties: {:?}", e);
629            crate::InstanceError::with_source(
630                String::from("enumerate_instance_layer_properties() failed"),
631                e,
632            )
633        })?;
634
635        fn find_layer<'layers>(
636            instance_layers: &'layers [vk::LayerProperties],
637            name: &CStr,
638        ) -> Option<&'layers vk::LayerProperties> {
639            instance_layers
640                .iter()
641                .find(|inst_layer| inst_layer.layer_name_as_c_str() == Ok(name))
642        }
643
644        let validation_layer_name =
645            CStr::from_bytes_with_nul(b"VK_LAYER_KHRONOS_validation\0").unwrap();
646        let validation_layer_properties = find_layer(&instance_layers, validation_layer_name);
647
648        // Determine if VK_EXT_validation_features is available, so we can enable
649        // GPU assisted validation and synchronization validation.
650        let validation_features_are_enabled = if validation_layer_properties.is_some() {
651            // Get the all the instance extension properties.
652            let exts =
653                Self::enumerate_instance_extension_properties(&entry, Some(validation_layer_name))?;
654            // Convert all the names of the extensions into an iterator of CStrs.
655            let mut ext_names = exts
656                .iter()
657                .filter_map(|ext| ext.extension_name_as_c_str().ok());
658            // Find the validation features extension.
659            ext_names.any(|ext_name| ext_name == ext::validation_features::NAME)
660        } else {
661            false
662        };
663
664        let should_enable_gpu_based_validation = desc
665            .flags
666            .intersects(wgt::InstanceFlags::GPU_BASED_VALIDATION)
667            && validation_features_are_enabled;
668
669        let nv_optimus_layer = CStr::from_bytes_with_nul(b"VK_LAYER_NV_optimus\0").unwrap();
670        let has_nv_optimus = find_layer(&instance_layers, nv_optimus_layer).is_some();
671
672        let obs_layer = CStr::from_bytes_with_nul(b"VK_LAYER_OBS_HOOK\0").unwrap();
673        let has_obs_layer = find_layer(&instance_layers, obs_layer).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::warn!(
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            let mut create_info = vk::InstanceCreateInfo::default()
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                has_nv_optimus,
841                None,
842            )
843        }
844    }
845
846    unsafe fn create_surface(
847        &self,
848        display_handle: raw_window_handle::RawDisplayHandle,
849        window_handle: raw_window_handle::RawWindowHandle,
850    ) -> Result<super::Surface, crate::InstanceError> {
851        use raw_window_handle::{RawDisplayHandle as Rdh, RawWindowHandle as Rwh};
852
853        // TODO: Replace with ash-window, which also lazy-loads the extension based on handle type
854
855        match (window_handle, display_handle) {
856            (Rwh::Wayland(handle), Rdh::Wayland(display)) => {
857                self.create_surface_from_wayland(display.display.as_ptr(), handle.surface.as_ptr())
858            }
859            (Rwh::Xlib(handle), Rdh::Xlib(display)) => {
860                let display = display.display.expect("Display pointer is not set.");
861                self.create_surface_from_xlib(display.as_ptr(), handle.window)
862            }
863            (Rwh::Xcb(handle), Rdh::Xcb(display)) => {
864                let connection = display.connection.expect("Pointer to X-Server is not set.");
865                self.create_surface_from_xcb(connection.as_ptr(), handle.window.get())
866            }
867            (Rwh::AndroidNdk(handle), _) => {
868                self.create_surface_android(handle.a_native_window.as_ptr())
869            }
870            (Rwh::Win32(handle), _) => {
871                let hinstance = handle.hinstance.ok_or_else(|| {
872                    crate::InstanceError::new(String::from(
873                        "Vulkan requires raw-window-handle's Win32::hinstance to be set",
874                    ))
875                })?;
876                self.create_surface_from_hwnd(hinstance.get(), handle.hwnd.get())
877            }
878            #[cfg(all(target_os = "macos", feature = "metal"))]
879            (Rwh::AppKit(handle), _)
880                if self.shared.extensions.contains(&ext::metal_surface::NAME) =>
881            {
882                self.create_surface_from_view(handle.ns_view)
883            }
884            #[cfg(all(any(target_os = "ios", target_os = "visionos"), feature = "metal"))]
885            (Rwh::UiKit(handle), _)
886                if self.shared.extensions.contains(&ext::metal_surface::NAME) =>
887            {
888                self.create_surface_from_view(handle.ui_view)
889            }
890            (_, _) => Err(crate::InstanceError::new(format!(
891                "window handle {window_handle:?} is not a Vulkan-compatible handle"
892            ))),
893        }
894    }
895
896    unsafe fn enumerate_adapters(
897        &self,
898        _surface_hint: Option<&super::Surface>,
899    ) -> Vec<crate::ExposedAdapter<super::Api>> {
900        use crate::auxil::db;
901
902        let raw_devices = match unsafe { self.shared.raw.enumerate_physical_devices() } {
903            Ok(devices) => devices,
904            Err(err) => {
905                log::error!("enumerate_adapters: {}", err);
906                Vec::new()
907            }
908        };
909
910        let mut exposed_adapters = raw_devices
911            .into_iter()
912            .flat_map(|device| self.expose_adapter(device))
913            .collect::<Vec<_>>();
914
915        // Detect if it's an Intel + NVidia configuration with Optimus
916        let has_nvidia_dgpu = exposed_adapters.iter().any(|exposed| {
917            exposed.info.device_type == wgt::DeviceType::DiscreteGpu
918                && exposed.info.vendor == db::nvidia::VENDOR
919        });
920        if cfg!(target_os = "linux") && has_nvidia_dgpu && self.shared.has_nv_optimus {
921            for exposed in exposed_adapters.iter_mut() {
922                if exposed.info.device_type == wgt::DeviceType::IntegratedGpu
923                    && exposed.info.vendor == db::intel::VENDOR
924                {
925                    // Check if mesa driver and version less than 21.2
926                    if let Some(version) = exposed.info.driver_info.split_once("Mesa ").map(|s| {
927                        let mut components = s.1.split('.');
928                        let major = components.next().and_then(|s| u8::from_str(s).ok());
929                        let minor = components.next().and_then(|s| u8::from_str(s).ok());
930                        if let (Some(major), Some(minor)) = (major, minor) {
931                            (major, minor)
932                        } else {
933                            (0, 0)
934                        }
935                    }) {
936                        if version < (21, 2) {
937                            // See https://gitlab.freedesktop.org/mesa/mesa/-/issues/4688
938                            log::warn!(
939                                concat!(
940                                    "Disabling presentation on '{}' (id {:?}) ",
941                                    "due to NV Optimus and Intel Mesa < v21.2"
942                                ),
943                                exposed.info.name,
944                                exposed.adapter.raw
945                            );
946                            exposed.adapter.private_caps.can_present = false;
947                        }
948                    }
949                }
950            }
951        }
952
953        exposed_adapters
954    }
955}
956
957impl Drop for super::Surface {
958    fn drop(&mut self) {
959        unsafe { self.functor.destroy_surface(self.raw, None) };
960    }
961}
962
963impl crate::Surface for super::Surface {
964    type A = super::Api;
965
966    unsafe fn configure(
967        &self,
968        device: &super::Device,
969        config: &crate::SurfaceConfiguration,
970    ) -> Result<(), crate::SurfaceError> {
971        // SAFETY: `configure`'s contract guarantees there are no resources derived from the swapchain in use.
972        let mut swap_chain = self.swapchain.write();
973        let old = swap_chain
974            .take()
975            .map(|sc| unsafe { sc.release_resources(&device.shared.raw) });
976
977        let swapchain = unsafe { device.create_swapchain(self, config, old)? };
978        *swap_chain = Some(swapchain);
979
980        Ok(())
981    }
982
983    unsafe fn unconfigure(&self, device: &super::Device) {
984        if let Some(sc) = self.swapchain.write().take() {
985            // SAFETY: `unconfigure`'s contract guarantees there are no resources derived from the swapchain in use.
986            let swapchain = unsafe { sc.release_resources(&device.shared.raw) };
987            unsafe { swapchain.functor.destroy_swapchain(swapchain.raw, None) };
988        }
989    }
990
991    unsafe fn acquire_texture(
992        &self,
993        timeout: Option<std::time::Duration>,
994        fence: &super::Fence,
995    ) -> Result<Option<crate::AcquiredSurfaceTexture<super::Api>>, crate::SurfaceError> {
996        let mut swapchain = self.swapchain.write();
997        let swapchain = swapchain.as_mut().unwrap();
998
999        let mut timeout_ns = match timeout {
1000            Some(duration) => duration.as_nanos() as u64,
1001            None => u64::MAX,
1002        };
1003
1004        // AcquireNextImageKHR on Android (prior to Android 11) doesn't support timeouts
1005        // and will also log verbose warnings if tying to use a timeout.
1006        //
1007        // Android 10 implementation for reference:
1008        // https://android.googlesource.com/platform/frameworks/native/+/refs/tags/android-mainline-10.0.0_r13/vulkan/libvulkan/swapchain.cpp#1426
1009        // Android 11 implementation for reference:
1010        // https://android.googlesource.com/platform/frameworks/native/+/refs/tags/android-mainline-11.0.0_r45/vulkan/libvulkan/swapchain.cpp#1438
1011        //
1012        // Android 11 corresponds to an SDK_INT/ro.build.version.sdk of 30
1013        if cfg!(target_os = "android") && self.instance.android_sdk_version < 30 {
1014            timeout_ns = u64::MAX;
1015        }
1016
1017        let swapchain_semaphores_arc = swapchain.get_surface_semaphores();
1018        // Nothing should be using this, so we don't block, but panic if we fail to lock.
1019        let locked_swapchain_semaphores = swapchain_semaphores_arc
1020            .try_lock()
1021            .expect("Failed to lock a SwapchainSemaphores.");
1022
1023        // Wait for all commands writing to the previously acquired image to
1024        // complete.
1025        //
1026        // Almost all the steps in the usual acquire-draw-present flow are
1027        // asynchronous: they get something started on the presentation engine
1028        // or the GPU, but on the CPU, control returns immediately. Without some
1029        // sort of intervention, the CPU could crank out frames much faster than
1030        // the presentation engine can display them.
1031        //
1032        // This is the intervention: if any submissions drew on this image, and
1033        // thus waited for `locked_swapchain_semaphores.acquire`, wait for all
1034        // of them to finish, thus ensuring that it's okay to pass `acquire` to
1035        // `vkAcquireNextImageKHR` again.
1036        swapchain.device.wait_for_fence(
1037            fence,
1038            locked_swapchain_semaphores.previously_used_submission_index,
1039            timeout_ns,
1040        )?;
1041
1042        // will block if no image is available
1043        let (index, suboptimal) = match unsafe {
1044            profiling::scope!("vkAcquireNextImageKHR");
1045            swapchain.functor.acquire_next_image(
1046                swapchain.raw,
1047                timeout_ns,
1048                locked_swapchain_semaphores.acquire,
1049                vk::Fence::null(),
1050            )
1051        } {
1052            // We treat `VK_SUBOPTIMAL_KHR` as `VK_SUCCESS` on Android.
1053            // See the comment in `Queue::present`.
1054            #[cfg(target_os = "android")]
1055            Ok((index, _)) => (index, false),
1056            #[cfg(not(target_os = "android"))]
1057            Ok(pair) => pair,
1058            Err(error) => {
1059                return match error {
1060                    vk::Result::TIMEOUT => Ok(None),
1061                    vk::Result::NOT_READY | vk::Result::ERROR_OUT_OF_DATE_KHR => {
1062                        Err(crate::SurfaceError::Outdated)
1063                    }
1064                    vk::Result::ERROR_SURFACE_LOST_KHR => Err(crate::SurfaceError::Lost),
1065                    // We don't use VK_EXT_full_screen_exclusive
1066                    // VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT
1067                    other => Err(super::map_host_device_oom_and_lost_err(other).into()),
1068                };
1069            }
1070        };
1071
1072        drop(locked_swapchain_semaphores);
1073        // We only advance the surface semaphores if we successfully acquired an image, otherwise
1074        // we should try to re-acquire using the same semaphores.
1075        swapchain.advance_surface_semaphores();
1076
1077        // special case for Intel Vulkan returning bizarre values (ugh)
1078        if swapchain.device.vendor_id == crate::auxil::db::intel::VENDOR && index > 0x100 {
1079            return Err(crate::SurfaceError::Outdated);
1080        }
1081
1082        // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkRenderPassBeginInfo.html#VUID-VkRenderPassBeginInfo-framebuffer-03209
1083        let raw_flags = if swapchain
1084            .raw_flags
1085            .contains(vk::SwapchainCreateFlagsKHR::MUTABLE_FORMAT)
1086        {
1087            vk::ImageCreateFlags::MUTABLE_FORMAT | vk::ImageCreateFlags::EXTENDED_USAGE
1088        } else {
1089            vk::ImageCreateFlags::empty()
1090        };
1091
1092        let texture = super::SurfaceTexture {
1093            index,
1094            texture: super::Texture {
1095                raw: swapchain.images[index as usize],
1096                drop_guard: None,
1097                block: None,
1098                external_memory: None,
1099                usage: swapchain.config.usage,
1100                format: swapchain.config.format,
1101                raw_flags,
1102                copy_size: crate::CopyExtent {
1103                    width: swapchain.config.extent.width,
1104                    height: swapchain.config.extent.height,
1105                    depth: 1,
1106                },
1107                view_formats: swapchain.view_formats.clone(),
1108            },
1109            surface_semaphores: swapchain_semaphores_arc,
1110        };
1111        Ok(Some(crate::AcquiredSurfaceTexture {
1112            texture,
1113            suboptimal,
1114        }))
1115    }
1116
1117    unsafe fn discard_texture(&self, _texture: super::SurfaceTexture) {}
1118}