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