From 0779e82d911156135a95f101c6230faf3c6d0ae7 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Fri, 21 Nov 2025 15:21:53 -0500 Subject: [PATCH] Provide a Vulkan path for create_surface_metal Signed-off-by: Isaac Marovitz --- wgpu-core/src/instance.rs | 94 +++++++++++++++++++++++++-------- wgpu-hal/src/vulkan/instance.rs | 20 ++++--- 2 files changed, 86 insertions(+), 28 deletions(-) diff --git a/wgpu-core/src/instance.rs b/wgpu-core/src/instance.rs index 4f5c831736c..b043fa4727e 100644 --- a/wgpu-core/src/instance.rs +++ b/wgpu-core/src/instance.rs @@ -317,31 +317,81 @@ impl Instance { ) -> Result { profiling::scope!("Instance::create_surface_metal"); - let instance = unsafe { self.as_hal::() } - .ok_or(CreateSurfaceError::BackendNotEnabled(Backend::Metal))?; + // HashMap unused in Metal-only path + #[allow(unused_mut)] + let mut errors = HashMap::default(); + let mut surface_per_backend: HashMap> = + HashMap::default(); - let layer = layer.cast(); - // SAFETY: We do this cast and deref. (rather than using `metal` to get the - // object we want) to avoid direct coupling on the `metal` crate. - // - // To wit, this pointer… - // - // - …is properly aligned. - // - …is dereferenceable to a `MetalLayerRef` as an invariant of the `metal` - // field. - // - …points to an _initialized_ `MetalLayerRef`. - // - …is only ever aliased via an immutable reference that lives within this - // lexical scope. - let layer = unsafe { &*layer }; - let raw_surface: Box = - Box::new(instance.create_surface_from_layer(layer)); + for (backend, instance) in &self.instance_per_backend { + match *backend { + #[cfg(vulkan)] + Backend::Vulkan => { + // Downcast to Vulkan instance + let vk_instance = instance + .as_any() + .downcast_ref::() + .expect("Backend mismatch"); + + if let Some(mut layer) = core::ptr::NonNull::new(layer) { + unsafe { + match vk_instance.create_surface_from_layer(layer.as_mut()) { + Ok(raw) => { + surface_per_backend.insert(*backend, Box::new(raw)); + } + Err(err) => { + log::debug!( + "Instance::create_surface_metal: failed to create Vulkan surface: {err:?}" + ); + errors.insert(*backend, err); + } + } + } + } + } + #[cfg(metal)] + Backend::Metal => { + // Downcast to Metal instance + let metal_instance = instance + .as_any() + .downcast_ref::() + .expect("Backend mismatch"); + + let layer_ref = layer.cast(); + // SAFETY: We do this cast and deref. (rather than using `metal` to get the + // object we want) to avoid direct coupling on the `metal` crate. + // + // To wit, this pointer… + // + // - …is properly aligned. + // - …is dereferenceable to a `MetalLayerRef` as an invariant of the `metal` + // field. + // - …points to an _initialized_ `MetalLayerRef`. + // - …is only ever aliased via an immutable reference that lives within this + // lexical scope. + let layer_ref = unsafe { &*layer_ref }; + let raw = metal_instance.create_surface_from_layer(layer_ref); + surface_per_backend.insert(*backend, Box::new(raw)); + } + _ => { + // Other backends don't support Metal layer input + continue; + } + } + } - let surface = Surface { - presentation: Mutex::new(rank::SURFACE_PRESENTATION, None), - surface_per_backend: core::iter::once((Backend::Metal, raw_surface)).collect(), - }; + if surface_per_backend.is_empty() { + Err(CreateSurfaceError::FailedToCreateSurfaceForAnyBackend( + errors, + )) + } else { + let surface = Surface { + presentation: Mutex::new(rank::SURFACE_PRESENTATION, None), + surface_per_backend, + }; - Ok(surface) + Ok(surface) + } } #[cfg(dx12)] diff --git a/wgpu-hal/src/vulkan/instance.rs b/wgpu-hal/src/vulkan/instance.rs index 6225371b042..616bb2c99e6 100644 --- a/wgpu-hal/src/vulkan/instance.rs +++ b/wgpu-hal/src/vulkan/instance.rs @@ -519,6 +519,19 @@ impl super::Instance { fn create_surface_from_view( &self, view: core::ptr::NonNull, + ) -> Result { + let layer = unsafe { crate::metal::Surface::get_metal_layer(view.cast()) }; + // NOTE: The layer is retained by Vulkan's `vkCreateMetalSurfaceEXT`, + // so no need to retain it beyond the scope of this function. + let layer_ptr = (*layer).cast(); + + self.create_surface_from_layer(layer_ptr) + } + + #[cfg(metal)] + pub fn create_surface_from_layer( + &self, + layer: *mut vk::CAMetalLayer, ) -> Result { if !self.shared.extensions.contains(&ext::metal_surface::NAME) { return Err(crate::InstanceError::new(String::from( @@ -526,17 +539,12 @@ impl super::Instance { ))); } - let layer = unsafe { crate::metal::Surface::get_metal_layer(view.cast()) }; - // NOTE: The layer is retained by Vulkan's `vkCreateMetalSurfaceEXT`, - // so no need to retain it beyond the scope of this function. - let layer_ptr = (*layer).cast(); - let surface = { let metal_loader = ext::metal_surface::Instance::new(&self.shared.entry, &self.shared.raw); let vk_info = vk::MetalSurfaceCreateInfoEXT::default() .flags(vk::MetalSurfaceCreateFlagsEXT::empty()) - .layer(layer_ptr); + .layer(layer); unsafe { metal_loader.create_metal_surface(&vk_info, None).unwrap() } };