li_wgpu_hal/vulkan/
instance.rs

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