wgpu_hal/vulkan/
instance.rs

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