wgpu_hal/vulkan/
instance.rs

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