Skip to main content

wgpu_hal/vulkan/
instance.rs

1use alloc::{borrow::ToOwned as _, boxed::Box, ffi::CString, string::String, sync::Arc, vec::Vec};
2use core::{
3    ffi::{c_void, CStr},
4    marker::PhantomData,
5    slice,
6    str::FromStr,
7};
8use std::thread;
9
10use arrayvec::ArrayVec;
11use ash::{ext, khr, vk};
12use wgpu_sync::RwLock;
13
14/// Name of the `VK_OHOS_surface` extension. Used with [`super::Instance::create_surface_ohos`].
15#[cfg(target_env = "ohos")]
16const OHOS_SURFACE_EXTENSION_NAME: &CStr = c"VK_OHOS_surface";
17
18unsafe extern "system" fn debug_utils_messenger_callback(
19    message_severity: vk::DebugUtilsMessageSeverityFlagsEXT,
20    message_type: vk::DebugUtilsMessageTypeFlagsEXT,
21    callback_data_ptr: *const vk::DebugUtilsMessengerCallbackDataEXT,
22    user_data: *mut c_void,
23) -> vk::Bool32 {
24    use alloc::borrow::Cow;
25
26    if thread::panicking() {
27        return vk::FALSE;
28    }
29
30    let cd = unsafe { &*callback_data_ptr };
31    let user_data = unsafe { &*user_data.cast::<super::DebugUtilsMessengerUserData>() };
32
33    const VUID_VKCMDENDDEBUGUTILSLABELEXT_COMMANDBUFFER_01912: i32 = 0x56146426;
34    if cd.message_id_number == VUID_VKCMDENDDEBUGUTILSLABELEXT_COMMANDBUFFER_01912 {
35        // https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/5671
36        // Versions 1.3.240 through 1.3.250 return a spurious error here if
37        // the debug range start and end appear in different command buffers.
38        if let Some(layer_properties) = user_data.validation_layer_properties.as_ref() {
39            if layer_properties.layer_description.as_ref() == c"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 a 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    // Silence Vulkan Validation error "VUID-vkCmdCopyImageToBuffer-pRegions-00184".
70    // While we aren't sure yet, we suspect this is probably a VVL issue.
71    // https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/9276
72    const VUID_VKCMDCOPYIMAGETOBUFFER_PREGIONS_00184: i32 = 0x45ef177c;
73    if cd.message_id_number == VUID_VKCMDCOPYIMAGETOBUFFER_PREGIONS_00184 {
74        return vk::FALSE;
75    }
76
77    // Silence Vulkan Validation error "VUID-StandaloneSpirv-None-10684".
78    //
79    // This is a bug. To prevent massive noise in the tests, lets suppress it for now.
80    // https://github.com/gfx-rs/wgpu/issues/7696
81    const VUID_STANDALONESPIRV_NONE_10684: i32 = 0xb210f7c2_u32 as i32;
82    if cd.message_id_number == VUID_STANDALONESPIRV_NONE_10684 {
83        return vk::FALSE;
84    }
85
86    // The validation layers deliver the output of a shader's `debugPrintf` through the
87    // regular debug messenger callback, rather than through a dedicated channel.
88    //the layers tag them with this magic `messageIdNumber`, which they document precisely so that
89    // callbacks like this one can pick them out.
90    // See https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/main/docs/debug_printf.md
91    const DEBUG_PRINTF_MESSAGE_ID: i32 = 0x4fe1fef9;
92    if cd.message_id_number == DEBUG_PRINTF_MESSAGE_ID {
93        // SAFETY: Vulkan keeps the null-terminated message valid for this callback.
94        let message =
95            unsafe { cd.message_as_c_str() }.map_or(Cow::Borrowed(""), CStr::to_string_lossy);
96
97        log::info!("[shader debugPrintf] {}", message);
98
99        return vk::FALSE;
100    }
101
102    let level = match message_severity {
103        // We intentionally suppress info messages down to debug
104        // so that users are not innundated with info messages from the runtime.
105        vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => log::Level::Trace,
106        vk::DebugUtilsMessageSeverityFlagsEXT::INFO => log::Level::Debug,
107        vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => log::Level::Warn,
108        vk::DebugUtilsMessageSeverityFlagsEXT::ERROR => log::Level::Error,
109        _ => log::Level::Warn,
110    };
111
112    let message_id_name =
113        unsafe { cd.message_id_name_as_c_str() }.map_or(Cow::Borrowed(""), CStr::to_string_lossy);
114    let message = unsafe { cd.message_as_c_str() }.map_or(Cow::Borrowed(""), CStr::to_string_lossy);
115
116    let _ = std::panic::catch_unwind(|| {
117        log::log!(
118            level,
119            "{:?} [{} (0x{:x})]\n\t{}",
120            message_type,
121            message_id_name,
122            cd.message_id_number,
123            message,
124        );
125    });
126
127    if cd.queue_label_count != 0 {
128        let labels =
129            unsafe { slice::from_raw_parts(cd.p_queue_labels, cd.queue_label_count as usize) };
130        let names = labels
131            .iter()
132            .flat_map(|dul_obj| unsafe { dul_obj.label_name_as_c_str() }.map(CStr::to_string_lossy))
133            .collect::<Vec<_>>();
134
135        let _ = std::panic::catch_unwind(|| {
136            log::log!(level, "\tqueues: {}", names.join(", "));
137        });
138    }
139
140    if cd.cmd_buf_label_count != 0 {
141        let labels =
142            unsafe { slice::from_raw_parts(cd.p_cmd_buf_labels, cd.cmd_buf_label_count as usize) };
143        let names = labels
144            .iter()
145            .flat_map(|dul_obj| unsafe { dul_obj.label_name_as_c_str() }.map(CStr::to_string_lossy))
146            .collect::<Vec<_>>();
147
148        let _ = std::panic::catch_unwind(|| {
149            log::log!(level, "\tcommand buffers: {}", names.join(", "));
150        });
151    }
152
153    if cd.object_count != 0 {
154        let labels = unsafe { slice::from_raw_parts(cd.p_objects, cd.object_count as usize) };
155        //TODO: use color fields of `vk::DebugUtilsLabelExt`?
156        let names = labels
157            .iter()
158            .map(|obj_info| {
159                let name = unsafe { obj_info.object_name_as_c_str() }
160                    .map_or(Cow::Borrowed("?"), CStr::to_string_lossy);
161
162                format!(
163                    "(type: {:?}, hndl: 0x{:x}, name: {})",
164                    obj_info.object_type, obj_info.object_handle, name
165                )
166            })
167            .collect::<Vec<_>>();
168        let _ = std::panic::catch_unwind(|| {
169            log::log!(level, "\tobjects: {}", names.join(", "));
170        });
171    }
172
173    #[cfg(feature = "validation_canary")]
174    if cfg!(debug_assertions) && level == log::Level::Error {
175        use alloc::string::ToString as _;
176
177        // Set canary and continue
178        crate::VALIDATION_CANARY.add(message.to_string());
179    }
180
181    // We disable this on Apple because there are numerous issues, some of
182    // which raise generic errors that we can't match by VUID. See
183    // <https://github.com/gfx-rs/wgpu/issues/9184> and
184    // <https://github.com/gfx-rs/wgpu/issues/9187>.
185    #[cfg(all(
186        debug_assertions,
187        feature = "internal_error_panic",
188        not(target_vendor = "apple")
189    ))]
190    if level == log::Level::Error
191        && message_type.contains(vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION)
192        && !cts_error_is_waived(cd.message_id_number)
193    {
194        use alloc::string::ToString as _;
195        panic!("{}", message.to_string());
196    }
197
198    vk::FALSE
199}
200
201/// Validation errors known to fire when running the CTS.
202///
203/// These waivers are keyed off the `WGPU_CTS_XTASK` environment variable, which
204/// is set in `xtask/src/cts.rs`.
205#[cfg(all(
206    debug_assertions,
207    feature = "internal_error_panic",
208    not(target_vendor = "apple")
209))]
210fn cts_error_is_waived(message_id_number: i32) -> bool {
211    use wgpu_sync::Lazy;
212
213    static WGPU_CTS_XTASK: Lazy<bool> = Lazy::new(|| std::env::var_os("WGPU_CTS_XTASK").is_some());
214
215    if !*WGPU_CTS_XTASK {
216        return false;
217    }
218
219    const WAIVED_MESSAGE_IDS: &[i32] = &[
220        // VUID-SampleMask-SampleMask-04359
221        // e.g. webgpu:api,validation,render_pipeline,inter_stage:max_variables_count,*
222        0x34d444b2_u32 as i32,
223        // VUID-vkCmdCopyImage-srcImage-01728
224        // e.g. webgpu:api,validation,encoding,cmds,copyTextureToTexture:*
225        0x6b654496_u32 as i32,
226        // VUID-StandaloneSpirv-OpImageQuerySizeLod-04659
227        // e.g. webgpu:shader,execution,expression,call,builtin,textureNumLayers:*
228        0x82396078_u32 as i32,
229        // VUID-RuntimeSpirv-Location-06272
230        // e.g. webgpu:api,validation,render_pipeline,inter_stage:max_variables_count,*
231        0xa3614f8b_u32 as i32,
232        // VUID-VkViewport-width-01770
233        // e.g. webgpu:api,validation,encoding,cmds,render,dynamic_state:*
234        0xa4164ba5_u32 as i32,
235        // VUID-VkImageViewCreateInfo-image-04441
236        // e.g. webgpu:api,validation,createView:texture_view_usage:*
237        0xb75da543_u32 as i32,
238        // VUID-VkBufferCreateInfo-None-09500
239        // e.g. webgpu:api,validation,buffer,create:usage,*
240        0xf6d454db_u32 as i32,
241    ];
242
243    WAIVED_MESSAGE_IDS.contains(&message_id_number)
244}
245
246impl super::DebugUtilsCreateInfo {
247    fn to_vk_create_info(&self) -> vk::DebugUtilsMessengerCreateInfoEXT<'_> {
248        let user_data_ptr: *const super::DebugUtilsMessengerUserData = &*self.callback_data;
249        vk::DebugUtilsMessengerCreateInfoEXT::default()
250            .message_severity(self.severity)
251            .message_type(self.message_type)
252            .user_data(user_data_ptr as *mut _)
253            .pfn_user_callback(Some(debug_utils_messenger_callback))
254    }
255}
256
257impl super::InstanceShared {
258    pub fn entry(&self) -> &ash::Entry {
259        &self.entry
260    }
261
262    pub fn raw_instance(&self) -> &ash::Instance {
263        &self.raw
264    }
265
266    pub fn instance_api_version(&self) -> u32 {
267        self.instance_api_version
268    }
269
270    pub fn extensions(&self) -> &[&'static CStr] {
271        &self.extensions[..]
272    }
273}
274
275impl super::Instance {
276    pub fn shared_instance(&self) -> &super::InstanceShared {
277        &self.shared
278    }
279
280    fn enumerate_instance_extension_properties(
281        entry: &ash::Entry,
282        layer_name: Option<&CStr>,
283    ) -> Result<Vec<vk::ExtensionProperties>, crate::InstanceError> {
284        let instance_extensions = {
285            profiling::scope!("vkEnumerateInstanceExtensionProperties");
286            unsafe { entry.enumerate_instance_extension_properties(layer_name) }
287        };
288        instance_extensions.map_err(|e| {
289            crate::InstanceError::with_source(
290                String::from("enumerate_instance_extension_properties() failed"),
291                e,
292            )
293        })
294    }
295
296    /// Return the instance extension names wgpu would like to enable.
297    ///
298    /// Return a vector of the names of instance extensions actually available
299    /// on `entry` that wgpu would like to enable.
300    ///
301    /// The `instance_api_version` argument should be the instance's Vulkan API
302    /// version, as obtained from `vkEnumerateInstanceVersion`. This is the same
303    /// space of values as the `VK_API_VERSION` constants.
304    ///
305    /// Note that wgpu can function without many of these extensions (for
306    /// example, `VK_KHR_wayland_surface` is certainly not going to be available
307    /// everywhere), but if one of these extensions is available at all, wgpu
308    /// assumes that it has been enabled.
309    pub fn desired_extensions(
310        entry: &ash::Entry,
311        _instance_api_version: u32,
312        flags: wgt::InstanceFlags,
313    ) -> Result<Vec<&'static CStr>, crate::InstanceError> {
314        let instance_extensions = Self::enumerate_instance_extension_properties(entry, None)?;
315
316        // Check our extensions against the available extensions
317        let mut extensions: Vec<&'static CStr> = Vec::new();
318
319        // VK_KHR_surface
320        extensions.push(khr::surface::NAME);
321
322        // Platform-specific WSI extensions
323        if cfg!(all(
324            unix,
325            not(target_os = "android"),
326            not(target_os = "macos"),
327            // NOTE: OpenHarmony (`target_env = "ohos"`) reports `target_os = "linux"` and is
328            // unix, but has neither X11 nor Wayland.
329            not(target_env = "ohos")
330        )) {
331            // VK_KHR_xlib_surface
332            extensions.push(khr::xlib_surface::NAME);
333            // VK_KHR_xcb_surface
334            extensions.push(khr::xcb_surface::NAME);
335            // VK_KHR_wayland_surface
336            extensions.push(khr::wayland_surface::NAME);
337        }
338        if cfg!(target_os = "android") {
339            // VK_KHR_android_surface
340            extensions.push(khr::android_surface::NAME);
341        }
342        #[cfg(target_env = "ohos")]
343        {
344            // VK_OHOS_surface: surfaces are created from an XComponent's `OHNativeWindow`.
345            extensions.push(OHOS_SURFACE_EXTENSION_NAME);
346        }
347        if cfg!(target_os = "windows") {
348            // VK_KHR_win32_surface
349            extensions.push(khr::win32_surface::NAME);
350        }
351        if cfg!(target_os = "macos") {
352            // VK_EXT_metal_surface
353            extensions.push(ext::metal_surface::NAME);
354            extensions.push(khr::portability_enumeration::NAME);
355        }
356        if cfg!(drm) {
357            // VK_EXT_acquire_drm_display -> VK_EXT_direct_mode_display -> VK_KHR_display
358            extensions.push(ext::acquire_drm_display::NAME);
359            extensions.push(ext::direct_mode_display::NAME);
360            extensions.push(khr::display::NAME);
361            extensions.push(khr::get_physical_device_properties2::NAME);
362            extensions.push(khr::get_display_properties2::NAME);
363        }
364
365        if flags.intersects(wgt::InstanceFlags::DEBUG | wgt::InstanceFlags::DEBUG_PRINTF) {
366            // VK_EXT_debug_utils
367            extensions.push(ext::debug_utils::NAME);
368        }
369
370        // VK_EXT_swapchain_colorspace
371        // Provides wide color gamut
372        extensions.push(ext::swapchain_colorspace::NAME);
373
374        // VK_KHR_get_physical_device_properties2
375        // Even though the extension was promoted to Vulkan 1.1, we still require the extension
376        // so that we don't have to conditionally use the functions provided by the 1.1 instance
377        extensions.push(khr::get_physical_device_properties2::NAME);
378
379        // Only keep available extensions.
380        extensions.retain(|&ext| {
381            if instance_extensions
382                .iter()
383                .any(|inst_ext| inst_ext.extension_name_as_c_str() == Ok(ext))
384            {
385                true
386            } else {
387                log::debug!("Unable to find extension: {}", ext.to_string_lossy());
388                false
389            }
390        });
391        Ok(extensions)
392    }
393
394    /// # Safety
395    ///
396    /// - `raw_instance` must be created from `entry`
397    /// - `raw_instance` must be created respecting `instance_api_version`, `extensions` and `flags`
398    /// - `extensions` must be a superset of `desired_extensions()` and must be created from the
399    ///   same entry, `instance_api_version`` and flags.
400    /// - `android_sdk_version` is ignored and can be `0` for all platforms besides Android
401    /// - If `drop_callback` is [`None`], wgpu-hal will take ownership of `raw_instance`. If
402    ///   `drop_callback` is [`Some`], `raw_instance` must be valid until the callback is called.
403    ///
404    /// If `debug_utils_user_data` is `Some`, then the validation layer is
405    /// available, so create a [`vk::DebugUtilsMessengerEXT`].
406    #[allow(clippy::too_many_arguments)]
407    pub unsafe fn from_raw(
408        entry: ash::Entry,
409        raw_instance: ash::Instance,
410        instance_api_version: u32,
411        android_sdk_version: u32,
412        debug_utils_create_info: Option<super::DebugUtilsCreateInfo>,
413        extensions: Vec<&'static CStr>,
414        flags: wgt::InstanceFlags,
415        memory_budget_thresholds: wgt::MemoryBudgetThresholds,
416        has_nv_optimus: bool,
417        drop_callback: Option<crate::DropCallback>,
418    ) -> Result<Self, crate::InstanceError> {
419        log::debug!("Instance version: 0x{instance_api_version:x}");
420
421        let debug_utils = if let Some(debug_utils_create_info) = debug_utils_create_info {
422            if extensions.contains(&ext::debug_utils::NAME) {
423                log::debug!("Enabling debug utils");
424
425                let extension = ext::debug_utils::Instance::new(&entry, &raw_instance);
426                let vk_info = debug_utils_create_info.to_vk_create_info();
427                let messenger =
428                    unsafe { extension.create_debug_utils_messenger(&vk_info, None) }.unwrap();
429
430                Some(super::DebugUtils {
431                    extension,
432                    messenger,
433                    callback_data: debug_utils_create_info.callback_data,
434                })
435            } else {
436                log::debug!("Debug utils not enabled: extension not listed");
437                None
438            }
439        } else {
440            log::debug!(
441                "Debug utils not enabled: \
442                        debug_utils_user_data not passed to Instance::from_raw"
443            );
444            None
445        };
446
447        let get_physical_device_properties =
448            if extensions.contains(&khr::get_physical_device_properties2::NAME) {
449                log::debug!("Enabling device properties2");
450                Some(khr::get_physical_device_properties2::Instance::new(
451                    &entry,
452                    &raw_instance,
453                ))
454            } else {
455                None
456            };
457
458        let drop_guard = crate::DropGuard::from_option(drop_callback);
459
460        Ok(Self {
461            shared: Arc::new(super::InstanceShared {
462                raw: raw_instance,
463                extensions,
464                drop_guard,
465                flags,
466                memory_budget_thresholds,
467                debug_utils,
468                get_physical_device_properties,
469                entry,
470                has_nv_optimus,
471                instance_api_version,
472                android_sdk_version,
473            }),
474        })
475    }
476
477    fn create_surface_from_xlib(
478        &self,
479        dpy: *mut vk::Display,
480        window: vk::Window,
481    ) -> Result<super::Surface, crate::InstanceError> {
482        if !self.shared.extensions.contains(&khr::xlib_surface::NAME) {
483            return Err(crate::InstanceError::new(String::from(
484                "Vulkan driver does not support VK_KHR_xlib_surface",
485            )));
486        }
487
488        let surface = {
489            let xlib_loader =
490                khr::xlib_surface::Instance::new(&self.shared.entry, &self.shared.raw);
491            let info = vk::XlibSurfaceCreateInfoKHR::default()
492                .flags(vk::XlibSurfaceCreateFlagsKHR::empty())
493                .window(window)
494                .dpy(dpy);
495
496            unsafe { xlib_loader.create_xlib_surface(&info, None) }
497        }
498        .map_err(|err| {
499            crate::InstanceError::with_source(
500                String::from("XlibSurface::create_xlib_surface() failed"),
501                err,
502            )
503        })?;
504
505        Ok(self.create_surface_from_vk_surface_khr(surface, None))
506    }
507
508    fn create_surface_from_xcb(
509        &self,
510        connection: *mut vk::xcb_connection_t,
511        window: vk::xcb_window_t,
512    ) -> Result<super::Surface, crate::InstanceError> {
513        if !self.shared.extensions.contains(&khr::xcb_surface::NAME) {
514            return Err(crate::InstanceError::new(String::from(
515                "Vulkan driver does not support VK_KHR_xcb_surface",
516            )));
517        }
518
519        let surface = {
520            let xcb_loader = khr::xcb_surface::Instance::new(&self.shared.entry, &self.shared.raw);
521            let info = vk::XcbSurfaceCreateInfoKHR::default()
522                .flags(vk::XcbSurfaceCreateFlagsKHR::empty())
523                .window(window)
524                .connection(connection);
525
526            unsafe { xcb_loader.create_xcb_surface(&info, None) }
527        }
528        .map_err(|err| {
529            crate::InstanceError::with_source(
530                String::from("XcbSurface::create_xcb_surface() failed"),
531                err,
532            )
533        })?;
534
535        Ok(self.create_surface_from_vk_surface_khr(surface, None))
536    }
537
538    fn create_surface_from_wayland(
539        &self,
540        display: *mut vk::wl_display,
541        surface: *mut vk::wl_surface,
542    ) -> Result<super::Surface, crate::InstanceError> {
543        if !self.shared.extensions.contains(&khr::wayland_surface::NAME) {
544            return Err(crate::InstanceError::new(String::from(
545                "Vulkan driver does not support VK_KHR_wayland_surface",
546            )));
547        }
548
549        let surface = {
550            let w_loader =
551                khr::wayland_surface::Instance::new(&self.shared.entry, &self.shared.raw);
552            let info = vk::WaylandSurfaceCreateInfoKHR::default()
553                .flags(vk::WaylandSurfaceCreateFlagsKHR::empty())
554                .display(display)
555                .surface(surface);
556
557            unsafe { w_loader.create_wayland_surface(&info, None) }
558        }
559        .map_err(|err| {
560            crate::InstanceError::with_source(String::from("WaylandSurface failed"), err)
561        })?;
562
563        Ok(self.create_surface_from_vk_surface_khr(surface, None))
564    }
565
566    fn create_surface_android(
567        &self,
568        window: *mut vk::ANativeWindow,
569    ) -> Result<super::Surface, crate::InstanceError> {
570        if !self.shared.extensions.contains(&khr::android_surface::NAME) {
571            return Err(crate::InstanceError::new(String::from(
572                "Vulkan driver does not support VK_KHR_android_surface",
573            )));
574        }
575
576        let surface = {
577            let a_loader =
578                khr::android_surface::Instance::new(&self.shared.entry, &self.shared.raw);
579            let info = vk::AndroidSurfaceCreateInfoKHR::default()
580                .flags(vk::AndroidSurfaceCreateFlagsKHR::empty())
581                .window(window);
582
583            unsafe { a_loader.create_android_surface(&info, None) }
584        }
585        .map_err(|err| {
586            crate::InstanceError::with_source(String::from("AndroidSurface failed"), err)
587        })?;
588
589        Ok(self.create_surface_from_vk_surface_khr(surface, None))
590    }
591
592    /// OpenHarmony window-system integration, using the `VK_OHOS_surface` extension.
593    ///
594    /// `ash` has no bindings for this, so we create bindings ad-hoc as needed. See also:
595    ///
596    /// - <https://docs.vulkan.org/refpages/latest/refpages/source/VK_OHOS_surface.html>
597    /// - [`vulkan_ohos.h`](https://github.com/KhronosGroup/Vulkan-Headers/blob/e3b1eec08173d6b825cd3ac88c885a63b621504a/include/vulkan/vulkan_ohos.h)
598    ///
599    /// `window` is the `OHNativeWindow*` handed out by an XComponent.
600    #[cfg(target_env = "ohos")]
601    fn create_surface_ohos(
602        &self,
603        window: *mut c_void,
604    ) -> Result<super::Surface, crate::InstanceError> {
605        // - Upstream docs:
606        // <https://docs.vulkan.org/refpages/latest/refpages/source/VkSurfaceCreateInfoOHOS.html>
607        #[repr(C)]
608        struct VkSurfaceCreateInfoOHOS {
609            s_type: vk::StructureType,
610            p_next: *const c_void,
611            flags: vk::Flags,
612            window: *mut c_void,
613        }
614
615        // - Upstream docs: Search for term `VK_STRUCTURE_TYPE_SURFACE_CREATE_INFO_OHOS` in
616        // <https://docs.vulkan.org/refpages/latest/refpages/source/VkStructureType.html>.
617        const S_TYPE_SURFACE_CREATE_INFO_OHOS: vk::StructureType =
618            vk::StructureType::from_raw(1000685000);
619
620        // - Upstream docs:
621        // <https://docs.vulkan.org/refpages/latest/refpages/source/vkCreateSurfaceOHOS.html>
622        type PfnCreateSurfaceOHOS = unsafe extern "system" fn(
623            vk::Instance,
624            *const VkSurfaceCreateInfoOHOS,
625            *const vk::AllocationCallbacks,
626            *mut vk::SurfaceKHR,
627        ) -> vk::Result;
628
629        if !self
630            .shared
631            .extensions
632            .contains(&OHOS_SURFACE_EXTENSION_NAME)
633        {
634            return Err(crate::InstanceError::new(String::from(
635                "Vulkan driver does not support VK_OHOS_surface",
636            )));
637        }
638
639        let raw_instance = self.shared.raw.handle();
640
641        // SAFETY: This is safe because:
642        //
643        // - `raw_instance` is a valid Vulkan instance, and the string we're asking for is
644        //   properly encoded and NUL-terminated.
645        let create = unsafe {
646            self.shared
647                .entry
648                .get_instance_proc_addr(raw_instance, c"vkCreateSurfaceOHOS".as_ptr())
649        };
650        let create =
651            // SAFETY: This function is safe, because we `transmute` between two function pointers
652            // with the same ABI, with the same validity, size, and alignment before and after.
653            unsafe { core::mem::transmute::<vk::PFN_vkVoidFunction, Option<PfnCreateSurfaceOHOS>>(create) };
654        let Some(create) = create else {
655            return Err(crate::InstanceError::new(String::from(
656                "vkCreateSurfaceOHOS not exposed by Vulkan driver",
657            )));
658        };
659
660        let info = VkSurfaceCreateInfoOHOS {
661            s_type: S_TYPE_SURFACE_CREATE_INFO_OHOS,
662            p_next: core::ptr::null(),
663            flags: 0,
664            window,
665        };
666        let mut surface = vk::SurfaceKHR::null();
667        // SAFETY: This is safe because:
668        //
669        // - This function signature is specced to match the signature we casted it to.
670        // - During the previous `transmute` operation, we took care to keep the same ABI (see also
671        // <https://doc.rust-lang.org/nightly/std/primitive.fn.html#abi-compatibility>).
672        let result = unsafe { create(raw_instance, &info, core::ptr::null(), &mut surface) };
673        if result != vk::Result::SUCCESS {
674            return Err(crate::InstanceError::new(format!(
675                "vkCreateSurfaceOHOS failed: {result:?}"
676            )));
677        }
678
679        Ok(self.create_surface_from_vk_surface_khr(surface, None))
680    }
681
682    fn create_surface_from_hwnd(
683        &self,
684        hinstance: vk::HINSTANCE,
685        hwnd: vk::HWND,
686    ) -> Result<super::Surface, crate::InstanceError> {
687        if !self.shared.extensions.contains(&khr::win32_surface::NAME) {
688            return Err(crate::InstanceError::new(String::from(
689                "Vulkan driver does not support VK_KHR_win32_surface",
690            )));
691        }
692
693        let surface = {
694            let info = vk::Win32SurfaceCreateInfoKHR::default()
695                .flags(vk::Win32SurfaceCreateFlagsKHR::empty())
696                .hinstance(hinstance)
697                .hwnd(hwnd);
698            let win32_loader =
699                khr::win32_surface::Instance::new(&self.shared.entry, &self.shared.raw);
700            unsafe { win32_loader.create_win32_surface(&info, None) }
701        }
702        .map_err(|err| {
703            crate::InstanceError::with_source(String::from("Unable to create Win32 surface"), err)
704        })?;
705
706        // Wrap ash's `isize` `HWND` in `WindowHandle`; on Windows the
707        // `NativeSurface` builds its DXGI HDR source from it.
708        #[cfg(windows)]
709        let window_handle = Some(crate::vulkan::swapchain::WindowHandle(
710            windows::Win32::Foundation::HWND(hwnd as *mut c_void),
711        ));
712        #[cfg(not(windows))]
713        let window_handle: Option<crate::vulkan::swapchain::WindowHandle> = None;
714        Ok(self.create_surface_from_vk_surface_khr(surface, window_handle))
715    }
716
717    #[cfg(target_vendor = "apple")]
718    fn create_surface_from_layer(
719        &self,
720        layer: raw_window_metal::Layer,
721    ) -> Result<super::Surface, crate::InstanceError> {
722        if !self.shared.extensions.contains(&ext::metal_surface::NAME) {
723            return Err(crate::InstanceError::new(String::from(
724                "Vulkan driver does not support VK_EXT_metal_surface",
725            )));
726        }
727
728        // NOTE: The layer is retained by Vulkan's `vkCreateMetalSurfaceEXT`,
729        // so no need to retain it beyond the scope of this function.
730        let surface = {
731            let metal_loader =
732                ext::metal_surface::Instance::new(&self.shared.entry, &self.shared.raw);
733            let vk_info = vk::MetalSurfaceCreateInfoEXT::default()
734                .flags(vk::MetalSurfaceCreateFlagsEXT::empty())
735                .layer(layer.as_ptr().as_ptr());
736
737            unsafe { metal_loader.create_metal_surface(&vk_info, None).unwrap() }
738        };
739
740        Ok(self.create_surface_from_vk_surface_khr(surface, None))
741    }
742
743    pub(super) fn create_surface_from_vk_surface_khr(
744        &self,
745        surface: vk::SurfaceKHR,
746        hwnd: Option<crate::vulkan::swapchain::WindowHandle>,
747    ) -> super::Surface {
748        let native_surface =
749            crate::vulkan::swapchain::NativeSurface::from_vk_surface_khr(self, surface, hwnd);
750
751        super::Surface {
752            swapchain: RwLock::new(None),
753            inner: Box::new(native_surface),
754        }
755    }
756
757    /// `Instance::init` but with a callback.
758    /// If you want to add extensions, add the to the `Vec<'static CStr>` not the create info, otherwise
759    /// it will be overwritten
760    ///
761    /// # Safety:
762    /// Same as `init` but additionally
763    /// - Callback must not remove features.
764    /// - Callback must not change anything to what the instance does not support.
765    pub unsafe fn init_with_callback(
766        desc: &crate::InstanceDescriptor<'_>,
767        callback: Option<Box<super::CreateInstanceCallback>>,
768    ) -> Result<Self, crate::InstanceError> {
769        profiling::scope!("Init Vulkan Backend");
770
771        let entry = unsafe {
772            profiling::scope!("Load vk library");
773            // ohos support is already fixed on ash main, but it's unclear when
774            // a new release can happen.
775            #[cfg(target_env = "ohos")]
776            let loaded = ash::Entry::load_from("libvulkan.so");
777            #[cfg(not(target_env = "ohos"))]
778            let loaded = ash::Entry::load();
779            loaded
780        }
781        .map_err(|err| {
782            crate::InstanceError::with_source(String::from("missing Vulkan entry points"), err)
783        })?;
784        let version = {
785            profiling::scope!("vkEnumerateInstanceVersion");
786            unsafe { entry.try_enumerate_instance_version() }
787        };
788        let instance_api_version = match version {
789            // Vulkan 1.1+
790            Ok(Some(version)) => version,
791            Ok(None) => vk::API_VERSION_1_0,
792            Err(err) => {
793                return Err(crate::InstanceError::with_source(
794                    String::from("try_enumerate_instance_version() failed"),
795                    err,
796                ));
797            }
798        };
799
800        let app_name = CString::new(desc.name).unwrap();
801        let app_info = vk::ApplicationInfo::default()
802            .application_name(app_name.as_c_str())
803            .application_version(1)
804            .engine_name(c"wgpu-hal")
805            .engine_version(2)
806            .api_version(
807                // Vulkan 1.0 doesn't like anything but 1.0 passed in here...
808                if instance_api_version < vk::API_VERSION_1_1 {
809                    vk::API_VERSION_1_0
810                } else {
811                    // This is the max Vulkan API version supported by `wgpu-hal`.
812                    //
813                    // If we want to increment this, there are some things that must be done first:
814                    //  - Audit the behavioral differences between the previous and new API versions.
815                    //  - Audit all extensions used by this backend:
816                    //    - 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.
817                    //    - If any were obsoleted in the new API version, we must implement a fallback for the new API version
818                    //    - If any are non-KHR-vendored, we must ensure the new behavior is still correct (since backwards-compatibility is not guaranteed).
819                    vk::API_VERSION_1_3
820                },
821            );
822
823        let mut extensions = Self::desired_extensions(&entry, instance_api_version, desc.flags)?;
824        let mut create_info = vk::InstanceCreateInfo::default();
825
826        if let Some(callback) = callback {
827            callback(super::CreateInstanceCallbackArgs {
828                extensions: &mut extensions,
829                create_info: &mut create_info,
830                entry: &entry,
831                _phantom: PhantomData,
832            });
833        }
834
835        let instance_layers = {
836            profiling::scope!("vkEnumerateInstanceLayerProperties");
837            unsafe { entry.enumerate_instance_layer_properties() }
838        };
839        let instance_layers = instance_layers.map_err(|e| {
840            log::debug!("enumerate_instance_layer_properties: {e:?}");
841            crate::InstanceError::with_source(
842                String::from("enumerate_instance_layer_properties() failed"),
843                e,
844            )
845        })?;
846
847        fn find_layer<'layers>(
848            instance_layers: &'layers [vk::LayerProperties],
849            name: &CStr,
850        ) -> Option<&'layers vk::LayerProperties> {
851            instance_layers
852                .iter()
853                .find(|inst_layer| inst_layer.layer_name_as_c_str() == Ok(name))
854        }
855
856        let validation_layer_name = c"VK_LAYER_KHRONOS_validation";
857        let validation_layer_properties = find_layer(&instance_layers, validation_layer_name);
858
859        // Determine if VK_EXT_validation_features is available, so we can enable
860        // GPU assisted validation and synchronization validation.
861        let validation_features_are_enabled = if validation_layer_properties.is_some() {
862            // Get the all the instance extension properties.
863            let exts =
864                Self::enumerate_instance_extension_properties(&entry, Some(validation_layer_name))?;
865            // Convert all the names of the extensions into an iterator of CStrs.
866            let mut ext_names = exts
867                .iter()
868                .filter_map(|ext| ext.extension_name_as_c_str().ok());
869            // Find the validation features extension.
870            ext_names.any(|ext_name| ext_name == ext::validation_features::NAME)
871        } else {
872            false
873        };
874
875        let should_enable_gpu_based_validation = desc
876            .flags
877            .intersects(wgt::InstanceFlags::GPU_BASED_VALIDATION)
878            && validation_features_are_enabled;
879
880        // Debug printf is implemented on top of the same shader instrumentation as
881        // GPU-assisted validation, so it needs the validation layers to be present. Older
882        // validation layers refused to run the two at once; current ones don't, so we ask
883        // for both if both were requested and let the layers sort it out.
884        let should_enable_debug_printf = desc.flags.intersects(wgt::InstanceFlags::DEBUG_PRINTF)
885            && validation_features_are_enabled;
886
887        if desc.flags.intersects(wgt::InstanceFlags::DEBUG_PRINTF) && !should_enable_debug_printf {
888            log::warn!(
889                "InstanceFlags::DEBUG_PRINTF was requested, but shader debug printf output will \
890                 not be captured: it requires the validation layers, which are not available."
891            );
892        }
893
894        let has_nv_optimus = find_layer(&instance_layers, c"VK_LAYER_NV_optimus").is_some();
895
896        let has_obs_layer = find_layer(&instance_layers, c"VK_LAYER_OBS_HOOK").is_some();
897
898        let mut layers: Vec<&'static CStr> = Vec::new();
899
900        let has_debug_extension = extensions.contains(&ext::debug_utils::NAME);
901        let mut debug_user_data = has_debug_extension.then(|| {
902            // Put the callback data on the heap, to ensure it will never be
903            // moved.
904            Box::new(super::DebugUtilsMessengerUserData {
905                validation_layer_properties: None,
906                has_obs_layer,
907            })
908        });
909
910        // Request validation layer if asked.
911        if desc.flags.intersects(wgt::InstanceFlags::VALIDATION)
912            || should_enable_gpu_based_validation
913        {
914            if let Some(layer_properties) = validation_layer_properties {
915                layers.push(validation_layer_name);
916
917                if let Some(debug_user_data) = debug_user_data.as_mut() {
918                    debug_user_data.validation_layer_properties =
919                        Some(super::ValidationLayerProperties {
920                            layer_description: layer_properties
921                                .description_as_c_str()
922                                .unwrap()
923                                .to_owned(),
924                            layer_spec_version: layer_properties.spec_version,
925                        });
926                }
927            } else {
928                log::debug!(
929                    "InstanceFlags::VALIDATION requested, but unable to find layer: {}",
930                    validation_layer_name.to_string_lossy()
931                );
932            }
933        }
934        let mut debug_utils = if let Some(callback_data) = debug_user_data {
935            // having ERROR unconditionally because Vk doesn't like empty flags
936            let mut severity = vk::DebugUtilsMessageSeverityFlagsEXT::ERROR;
937            if log::max_level() >= log::LevelFilter::Debug {
938                severity |= vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE;
939            }
940            if log::max_level() >= log::LevelFilter::Info {
941                severity |= vk::DebugUtilsMessageSeverityFlagsEXT::INFO;
942            }
943            if log::max_level() >= log::LevelFilter::Warn {
944                severity |= vk::DebugUtilsMessageSeverityFlagsEXT::WARNING;
945            }
946
947            let message_type = vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
948                | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION
949                | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE;
950
951            let create_info = super::DebugUtilsCreateInfo {
952                severity,
953                message_type,
954                callback_data,
955            };
956
957            Some(create_info)
958        } else {
959            None
960        };
961
962        #[cfg(target_os = "android")]
963        let android_sdk_version = {
964            let properties = android_system_properties::AndroidSystemProperties::new();
965            // See: https://developer.android.com/reference/android/os/Build.VERSION_CODES
966            if let Some(val) = properties.get("ro.build.version.sdk") {
967                match val.parse::<u32>() {
968                    Ok(sdk_ver) => sdk_ver,
969                    Err(err) => {
970                        log::error!(
971                            concat!(
972                                "Couldn't parse Android's ",
973                                "ro.build.version.sdk system property ({}): {}",
974                            ),
975                            val,
976                            err,
977                        );
978                        0
979                    }
980                }
981            } else {
982                log::error!("Couldn't read Android's ro.build.version.sdk system property");
983                0
984            }
985        };
986        #[cfg(not(target_os = "android"))]
987        let android_sdk_version = 0;
988
989        let mut flags = vk::InstanceCreateFlags::empty();
990
991        // Avoid VUID-VkInstanceCreateInfo-flags-06559: Only ask the instance to
992        // enumerate incomplete Vulkan implementations (which we need on Mac) if
993        // we managed to find the extension that provides the flag.
994        if extensions.contains(&khr::portability_enumeration::NAME) {
995            flags |= vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR;
996        }
997        let vk_instance = {
998            let str_pointers = layers
999                .iter()
1000                .chain(extensions.iter())
1001                .map(|&s: &&'static _| {
1002                    // Safe because `layers` and `extensions` entries have static lifetime.
1003                    s.as_ptr()
1004                })
1005                .collect::<Vec<_>>();
1006
1007            create_info = create_info
1008                .flags(flags)
1009                .application_info(&app_info)
1010                .enabled_layer_names(&str_pointers[..layers.len()])
1011                .enabled_extension_names(&str_pointers[layers.len()..]);
1012
1013            let mut debug_utils_create_info = debug_utils
1014                .as_mut()
1015                .map(|create_info| create_info.to_vk_create_info());
1016            if let Some(debug_utils_create_info) = debug_utils_create_info.as_mut() {
1017                create_info = create_info.push_next(debug_utils_create_info);
1018            }
1019
1020            // Enable explicit validation features if available
1021            let mut validation_features;
1022            let mut validation_feature_list: ArrayVec<_, 4>;
1023            if validation_features_are_enabled {
1024                validation_feature_list = ArrayVec::new();
1025
1026                // Always enable synchronization validation
1027                validation_feature_list
1028                    .push(vk::ValidationFeatureEnableEXT::SYNCHRONIZATION_VALIDATION);
1029
1030                // Only enable GPU assisted validation if requested.
1031                if should_enable_gpu_based_validation {
1032                    validation_feature_list.push(vk::ValidationFeatureEnableEXT::GPU_ASSISTED);
1033                    validation_feature_list
1034                        .push(vk::ValidationFeatureEnableEXT::GPU_ASSISTED_RESERVE_BINDING_SLOT);
1035                }
1036
1037                // Only capture shader `debugPrintf` output if requested.
1038                if should_enable_debug_printf {
1039                    validation_feature_list.push(vk::ValidationFeatureEnableEXT::DEBUG_PRINTF);
1040                }
1041
1042                validation_features = vk::ValidationFeaturesEXT::default()
1043                    .enabled_validation_features(&validation_feature_list);
1044                create_info = create_info.push_next(&mut validation_features);
1045            }
1046
1047            unsafe {
1048                profiling::scope!("vkCreateInstance");
1049                entry.create_instance(&create_info, None)
1050            }
1051            .map_err(|e| {
1052                crate::InstanceError::with_source(
1053                    String::from("Entry::create_instance() failed"),
1054                    e,
1055                )
1056            })?
1057        };
1058
1059        unsafe {
1060            Self::from_raw(
1061                entry,
1062                vk_instance,
1063                instance_api_version,
1064                android_sdk_version,
1065                debug_utils,
1066                extensions,
1067                desc.flags,
1068                desc.memory_budget_thresholds,
1069                has_nv_optimus,
1070                None,
1071            )
1072        }
1073    }
1074}
1075
1076impl Drop for super::InstanceShared {
1077    fn drop(&mut self) {
1078        unsafe {
1079            // Keep du alive since destroy_instance may also log
1080            let _du = self.debug_utils.take().inspect(|du| {
1081                du.extension
1082                    .destroy_debug_utils_messenger(du.messenger, None);
1083            });
1084            if self.drop_guard.is_none() {
1085                self.raw.destroy_instance(None);
1086            }
1087        }
1088    }
1089}
1090
1091impl crate::Instance for super::Instance {
1092    type A = super::Api;
1093
1094    unsafe fn init(desc: &crate::InstanceDescriptor<'_>) -> Result<Self, crate::InstanceError> {
1095        unsafe { Self::init_with_callback(desc, None) }
1096    }
1097
1098    unsafe fn create_surface(
1099        &self,
1100        display_handle: raw_window_handle::RawDisplayHandle,
1101        window_handle: raw_window_handle::RawWindowHandle,
1102    ) -> Result<super::Surface, crate::InstanceError> {
1103        use raw_window_handle::{RawDisplayHandle as Rdh, RawWindowHandle as Rwh};
1104
1105        // TODO: Replace with ash-window, which also lazy-loads the extension based on handle type
1106
1107        match (window_handle, display_handle) {
1108            (Rwh::Wayland(handle), Rdh::Wayland(display)) => {
1109                self.create_surface_from_wayland(display.display.as_ptr(), handle.surface.as_ptr())
1110            }
1111            (Rwh::Xlib(handle), Rdh::Xlib(display)) => {
1112                let display = display.display.expect("Display pointer is not set.");
1113                self.create_surface_from_xlib(display.as_ptr(), handle.window)
1114            }
1115            (Rwh::Xcb(handle), Rdh::Xcb(display)) => {
1116                let connection = display.connection.expect("Pointer to X-Server is not set.");
1117                self.create_surface_from_xcb(connection.as_ptr(), handle.window.get())
1118            }
1119            #[cfg(drm)]
1120            (Rwh::Drm(handle), Rdh::Drm(display)) => {
1121                self.create_surface_from_drm_plane(display.fd, handle.plane)
1122            }
1123            (Rwh::AndroidNdk(handle), _) => {
1124                self.create_surface_android(handle.a_native_window.as_ptr())
1125            }
1126            #[cfg(target_env = "ohos")]
1127            (Rwh::OhosNdk(handle), _) => self.create_surface_ohos(handle.native_window.as_ptr()),
1128            (Rwh::Win32(handle), _) => {
1129                let hinstance = handle.hinstance.ok_or_else(|| {
1130                    crate::InstanceError::new(String::from(
1131                        "Vulkan requires raw-window-handle's Win32::hinstance to be set",
1132                    ))
1133                })?;
1134                self.create_surface_from_hwnd(hinstance.get(), handle.hwnd.get())
1135            }
1136            #[cfg(target_vendor = "apple")]
1137            (Rwh::AppKit(handle), _)
1138                if self.shared.extensions.contains(&ext::metal_surface::NAME) =>
1139            {
1140                let layer = unsafe { raw_window_metal::Layer::from_ns_view(handle.ns_view) };
1141                self.create_surface_from_layer(layer)
1142            }
1143            #[cfg(target_vendor = "apple")]
1144            (Rwh::UiKit(handle), _)
1145                if self.shared.extensions.contains(&ext::metal_surface::NAME) =>
1146            {
1147                let layer = unsafe { raw_window_metal::Layer::from_ui_view(handle.ui_view) };
1148                self.create_surface_from_layer(layer)
1149            }
1150            (_, _) => Err(crate::InstanceError::new(format!(
1151                "window handle {window_handle:?} is not a Vulkan-compatible handle"
1152            ))),
1153        }
1154    }
1155
1156    unsafe fn enumerate_adapters(
1157        &self,
1158        _surface_hint: Option<&super::Surface>,
1159    ) -> Vec<crate::ExposedAdapter<super::Api>> {
1160        use crate::auxil::db;
1161
1162        let raw_devices = match unsafe { self.shared.raw.enumerate_physical_devices() } {
1163            Ok(devices) => devices,
1164            Err(err) => {
1165                log::error!("enumerate_adapters: {err}");
1166                Vec::new()
1167            }
1168        };
1169
1170        let mut exposed_adapters = raw_devices
1171            .into_iter()
1172            .flat_map(|device| self.expose_adapter(device))
1173            .collect::<Vec<_>>();
1174
1175        // Detect if it's an Intel + NVidia configuration with Optimus
1176        let has_nvidia_dgpu = exposed_adapters.iter().any(|exposed| {
1177            exposed.info.device_type == wgt::DeviceType::DiscreteGpu
1178                && exposed.info.vendor == db::nvidia::VENDOR
1179        });
1180        if cfg!(target_os = "linux") && has_nvidia_dgpu && self.shared.has_nv_optimus {
1181            for exposed in exposed_adapters.iter_mut() {
1182                if exposed.info.device_type == wgt::DeviceType::IntegratedGpu
1183                    && exposed.info.vendor == db::intel::VENDOR
1184                {
1185                    // Check if mesa driver and version less than 21.2
1186                    if let Some(version) = exposed.info.driver_info.split_once("Mesa ").map(|s| {
1187                        let mut components = s.1.split('.');
1188                        let major = components.next().and_then(|s| u8::from_str(s).ok());
1189                        let minor = components.next().and_then(|s| u8::from_str(s).ok());
1190                        if let (Some(major), Some(minor)) = (major, minor) {
1191                            (major, minor)
1192                        } else {
1193                            (0, 0)
1194                        }
1195                    }) {
1196                        if version < (21, 2) {
1197                            // See https://gitlab.freedesktop.org/mesa/mesa/-/issues/4688
1198                            log::debug!(
1199                                concat!(
1200                                    "Disabling presentation on '{}' (id {:?}) ",
1201                                    "due to NV Optimus and Intel Mesa < v21.2"
1202                                ),
1203                                exposed.info.name,
1204                                exposed.adapter.raw
1205                            );
1206                            exposed.adapter.private_caps.can_present = false;
1207                        }
1208                    }
1209                }
1210            }
1211        }
1212
1213        exposed_adapters
1214    }
1215}
1216
1217impl crate::Surface for super::Surface {
1218    type A = super::Api;
1219
1220    unsafe fn configure(
1221        &self,
1222        device: &super::Device,
1223        config: &crate::SurfaceConfiguration,
1224    ) -> Result<(), crate::SurfaceError> {
1225        // SAFETY: `configure`'s contract guarantees there are no resources derived from the swapchain in use.
1226        let mut swap_chain = self.swapchain.write();
1227
1228        let mut old = swap_chain.take();
1229        if let Some(ref mut old) = old {
1230            unsafe { old.release_resources(device) };
1231        }
1232
1233        let swapchain = unsafe { self.inner.create_swapchain(device, config, old)? };
1234        *swap_chain = Some(swapchain);
1235
1236        Ok(())
1237    }
1238
1239    unsafe fn unconfigure(&self, device: &super::Device) {
1240        if let Some(mut sc) = self.swapchain.write().take() {
1241            // SAFETY: `unconfigure`'s contract guarantees there are no resources derived from the swapchain in use.
1242            unsafe { sc.release_resources(device) };
1243        }
1244    }
1245
1246    unsafe fn acquire_texture(
1247        &self,
1248        timeout: Option<core::time::Duration>,
1249        fence: &super::Fence,
1250    ) -> Result<crate::AcquiredSurfaceTexture<super::Api>, crate::SurfaceError> {
1251        let mut swapchain = self.swapchain.write();
1252        let swapchain = swapchain.as_mut().unwrap();
1253
1254        unsafe { swapchain.acquire(timeout, fence) }
1255    }
1256
1257    unsafe fn discard_texture(&self, texture: super::SurfaceTexture) {
1258        unsafe {
1259            self.swapchain
1260                .write()
1261                .as_mut()
1262                .unwrap()
1263                .discard_texture(texture)
1264                .unwrap()
1265        };
1266    }
1267}