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