Fix surfaces only compatible with first enabled backend (#5535)

This commit is contained in:
Andreas Reich 2024-04-17 21:32:04 +02:00 committed by GitHub
parent ea77d5674d
commit cbace631ec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 237 additions and 271 deletions

View File

@ -153,6 +153,7 @@ Bottom level categories:
- Failing to set the device lost closure will call the closure before returning. By @bradwerth in [#5358](https://github.com/gfx-rs/wgpu/pull/5358). - Failing to set the device lost closure will call the closure before returning. By @bradwerth in [#5358](https://github.com/gfx-rs/wgpu/pull/5358).
- Use memory pooling for UsageScopes to avoid frequent large allocations. by @robtfm in [#5414](https://github.com/gfx-rs/wgpu/pull/5414) - Use memory pooling for UsageScopes to avoid frequent large allocations. by @robtfm in [#5414](https://github.com/gfx-rs/wgpu/pull/5414)
- Fix deadlocks caused by recursive read-write lock acquisitions [#5426](https://github.com/gfx-rs/wgpu/pull/5426). - Fix deadlocks caused by recursive read-write lock acquisitions [#5426](https://github.com/gfx-rs/wgpu/pull/5426).
- Fix surfaces being only compatible with first backend enabled on an instance, causing failures when manually specifying an adapter. By @Wumpf in [#5535](https://github.com/gfx-rs/wgpu/pull/5535).
#### Naga #### Naga
- In spv-in, remove unnecessary "gl_PerVertex" name check so unused builtins will always be skipped. By @Imberflur in [#5227](https://github.com/gfx-rs/wgpu/pull/5227). - In spv-in, remove unnecessary "gl_PerVertex" name check so unused builtins will always be skipped. By @Imberflur in [#5227](https://github.com/gfx-rs/wgpu/pull/5227).

View File

@ -1,95 +0,0 @@
use wgt::Backend;
/// The `AnySurface` type: a `Arc` of a `A::Surface` for any backend `A`.
use crate::hal_api::HalApi;
use std::fmt;
use std::mem::ManuallyDrop;
use std::ptr::NonNull;
struct AnySurfaceVtable {
// We oppurtunistically store the backend here, since we now it will be used
// with backend selection and it can be stored in static memory.
backend: Backend,
// Drop glue which knows how to drop the stored data.
drop: unsafe fn(*mut ()),
}
/// An `A::Surface`, for any backend `A`.
///
/// Any `AnySurface` is just like an `A::Surface`, except that the `A` type
/// parameter is erased. To access the `Surface`, you must downcast to a
/// particular backend with the \[`downcast_ref`\] or \[`take`\] methods.
pub struct AnySurface {
data: NonNull<()>,
vtable: &'static AnySurfaceVtable,
}
impl AnySurface {
/// Construct an `AnySurface` that owns an `A::Surface`.
pub fn new<A: HalApi>(surface: A::Surface) -> AnySurface {
unsafe fn drop_glue<A: HalApi>(ptr: *mut ()) {
unsafe {
_ = Box::from_raw(ptr.cast::<A::Surface>());
}
}
let data = NonNull::from(Box::leak(Box::new(surface)));
AnySurface {
data: data.cast(),
vtable: &AnySurfaceVtable {
backend: A::VARIANT,
drop: drop_glue::<A>,
},
}
}
/// Get the backend this surface was created through.
pub fn backend(&self) -> Backend {
self.vtable.backend
}
/// If `self` refers to an `A::Surface`, returns a reference to it.
pub fn downcast_ref<A: HalApi>(&self) -> Option<&A::Surface> {
if A::VARIANT != self.vtable.backend {
return None;
}
// SAFETY: We just checked the instance above implicitly by the backend
// that it was statically constructed through.
Some(unsafe { &*self.data.as_ptr().cast::<A::Surface>() })
}
/// If `self` is an `Arc<A::Surface>`, returns that.
pub fn take<A: HalApi>(self) -> Option<A::Surface> {
if A::VARIANT != self.vtable.backend {
return None;
}
// Disable drop glue, since we're returning the owned surface. The
// caller will be responsible for dropping it.
let this = ManuallyDrop::new(self);
// SAFETY: We just checked the instance above implicitly by the backend
// that it was statically constructed through.
Some(unsafe { *Box::from_raw(this.data.as_ptr().cast::<A::Surface>()) })
}
}
impl Drop for AnySurface {
fn drop(&mut self) {
unsafe { (self.vtable.drop)(self.data.as_ptr()) }
}
}
impl fmt::Debug for AnySurface {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "AnySurface<{}>", self.vtable.backend)
}
}
#[cfg(send_sync)]
unsafe impl Send for AnySurface {}
#[cfg(send_sync)]
unsafe impl Sync for AnySurface {}

View File

@ -1969,7 +1969,7 @@ impl Global {
}; };
let caps = unsafe { let caps = unsafe {
let suf = A::get_surface(surface); let suf = A::surface_as_hal(surface);
let adapter = &device.adapter; let adapter = &device.adapter;
match adapter.raw.adapter.surface_capabilities(suf.unwrap()) { match adapter.raw.adapter.surface_capabilities(suf.unwrap()) {
Some(caps) => caps, Some(caps) => caps,
@ -2055,7 +2055,7 @@ impl Global {
// https://github.com/gfx-rs/wgpu/issues/4105 // https://github.com/gfx-rs/wgpu/issues/4105
match unsafe { match unsafe {
A::get_surface(surface) A::surface_as_hal(surface)
.unwrap() .unwrap()
.configure(device.raw(), &hal_config) .configure(device.raw(), &hal_config)
} { } {

View File

@ -11,7 +11,7 @@ pub trait HalApi: hal::Api + 'static + WasmNotSendSync {
fn create_instance_from_hal(name: &str, hal_instance: Self::Instance) -> Instance; fn create_instance_from_hal(name: &str, hal_instance: Self::Instance) -> Instance;
fn instance_as_hal(instance: &Instance) -> Option<&Self::Instance>; fn instance_as_hal(instance: &Instance) -> Option<&Self::Instance>;
fn hub(global: &Global) -> &Hub<Self>; fn hub(global: &Global) -> &Hub<Self>;
fn get_surface(surface: &Surface) -> Option<&Self::Surface>; fn surface_as_hal(surface: &Surface) -> Option<&Self::Surface>;
} }
impl HalApi for hal::api::Empty { impl HalApi for hal::api::Empty {
@ -25,7 +25,7 @@ impl HalApi for hal::api::Empty {
fn hub(_: &Global) -> &Hub<Self> { fn hub(_: &Global) -> &Hub<Self> {
unimplemented!("called empty api") unimplemented!("called empty api")
} }
fn get_surface(_: &Surface) -> Option<&Self::Surface> { fn surface_as_hal(_: &Surface) -> Option<&Self::Surface> {
unimplemented!("called empty api") unimplemented!("called empty api")
} }
} }
@ -46,8 +46,8 @@ impl HalApi for hal::api::Vulkan {
fn hub(global: &Global) -> &Hub<Self> { fn hub(global: &Global) -> &Hub<Self> {
&global.hubs.vulkan &global.hubs.vulkan
} }
fn get_surface(surface: &Surface) -> Option<&Self::Surface> { fn surface_as_hal(surface: &Surface) -> Option<&Self::Surface> {
surface.raw.downcast_ref::<Self>() surface.vulkan.as_ref()
} }
} }
@ -67,8 +67,8 @@ impl HalApi for hal::api::Metal {
fn hub(global: &Global) -> &Hub<Self> { fn hub(global: &Global) -> &Hub<Self> {
&global.hubs.metal &global.hubs.metal
} }
fn get_surface(surface: &Surface) -> Option<&Self::Surface> { fn surface_as_hal(surface: &Surface) -> Option<&Self::Surface> {
surface.raw.downcast_ref::<Self>() surface.metal.as_ref()
} }
} }
@ -88,8 +88,8 @@ impl HalApi for hal::api::Dx12 {
fn hub(global: &Global) -> &Hub<Self> { fn hub(global: &Global) -> &Hub<Self> {
&global.hubs.dx12 &global.hubs.dx12
} }
fn get_surface(surface: &Surface) -> Option<&Self::Surface> { fn surface_as_hal(surface: &Surface) -> Option<&Self::Surface> {
surface.raw.downcast_ref::<Self>() surface.dx12.as_ref()
} }
} }
@ -110,7 +110,7 @@ impl HalApi for hal::api::Gles {
fn hub(global: &Global) -> &Hub<Self> { fn hub(global: &Global) -> &Hub<Self> {
&global.hubs.gl &global.hubs.gl
} }
fn get_surface(surface: &Surface) -> Option<&Self::Surface> { fn surface_as_hal(surface: &Surface) -> Option<&Self::Surface> {
surface.raw.downcast_ref::<Self>() surface.gl.as_ref()
} }
} }

View File

@ -241,7 +241,7 @@ impl<A: HalApi> Hub<A> {
if let Element::Occupied(ref surface, _epoch) = *element { if let Element::Occupied(ref surface, _epoch) = *element {
if let Some(ref mut present) = surface.presentation.lock().take() { if let Some(ref mut present) = surface.presentation.lock().take() {
if let Some(device) = present.device.downcast_ref::<A>() { if let Some(device) = present.device.downcast_ref::<A>() {
let suf = A::get_surface(surface); let suf = A::surface_as_hal(surface);
unsafe { unsafe {
suf.unwrap().unconfigure(device.raw()); suf.unwrap().unconfigure(device.raw());
//TODO: we could destroy the surface here //TODO: we could destroy the surface here

View File

@ -1,13 +1,12 @@
use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use crate::{ use crate::{
any_surface::AnySurface,
api_log, api_log,
device::{queue::Queue, resource::Device, DeviceDescriptor}, device::{queue::Queue, resource::Device, DeviceDescriptor},
global::Global, global::Global,
hal_api::HalApi, hal_api::HalApi,
id::markers, id::{markers, AdapterId, DeviceId, Id, Marker, QueueId, SurfaceId},
id::{AdapterId, DeviceId, Id, Marker, QueueId, SurfaceId},
present::Presentation, present::Presentation,
resource::{Resource, ResourceInfo, ResourceType}, resource::{Resource, ResourceInfo, ResourceType},
resource_log, LabelHelpers, DOWNLEVEL_WARNING_MESSAGE, resource_log, LabelHelpers, DOWNLEVEL_WARNING_MESSAGE,
@ -21,6 +20,7 @@ use thiserror::Error;
pub type RequestAdapterOptions = wgt::RequestAdapterOptions<SurfaceId>; pub type RequestAdapterOptions = wgt::RequestAdapterOptions<SurfaceId>;
type HalInstance<A> = <A as hal::Api>::Instance; type HalInstance<A> = <A as hal::Api>::Instance;
type HalSurface<A> = <A as hal::Api>::Surface;
#[derive(Clone, Debug, Error)] #[derive(Clone, Debug, Error)]
#[error("Limit '{name}' value {requested} is better than allowed {allowed}")] #[error("Limit '{name}' value {requested} is better than allowed {allowed}")]
@ -113,31 +113,36 @@ impl Instance {
} }
pub(crate) fn destroy_surface(&self, surface: Surface) { pub(crate) fn destroy_surface(&self, surface: Surface) {
fn destroy<A: HalApi>(instance: &Option<A::Instance>, surface: AnySurface) { fn destroy<A: HalApi>(instance: &Option<A::Instance>, mut surface: Option<HalSurface<A>>) {
unsafe { if let Some(surface) = surface.take() {
if let Some(suf) = surface.take::<A>() { unsafe {
instance.as_ref().unwrap().destroy_surface(suf); instance.as_ref().unwrap().destroy_surface(surface);
} }
} }
} }
match surface.raw.backend() { #[cfg(vulkan)]
#[cfg(vulkan)] destroy::<hal::api::Vulkan>(&self.vulkan, surface.vulkan);
Backend::Vulkan => destroy::<hal::api::Vulkan>(&self.vulkan, surface.raw), #[cfg(metal)]
#[cfg(metal)] destroy::<hal::api::Metal>(&self.metal, surface.metal);
Backend::Metal => destroy::<hal::api::Metal>(&self.metal, surface.raw), #[cfg(dx12)]
#[cfg(dx12)] destroy::<hal::api::Dx12>(&self.dx12, surface.dx12);
Backend::Dx12 => destroy::<hal::api::Dx12>(&self.dx12, surface.raw), #[cfg(gles)]
#[cfg(gles)] destroy::<hal::api::Gles>(&self.gl, surface.gl);
Backend::Gl => destroy::<hal::api::Gles>(&self.gl, surface.raw),
_ => unreachable!(),
}
} }
} }
pub struct Surface { pub struct Surface {
pub(crate) presentation: Mutex<Option<Presentation>>, pub(crate) presentation: Mutex<Option<Presentation>>,
pub(crate) info: ResourceInfo<Surface>, pub(crate) info: ResourceInfo<Surface>,
pub(crate) raw: AnySurface,
#[cfg(vulkan)]
pub vulkan: Option<HalSurface<hal::api::Vulkan>>,
#[cfg(metal)]
pub metal: Option<HalSurface<hal::api::Metal>>,
#[cfg(dx12)]
pub dx12: Option<HalSurface<hal::api::Dx12>>,
#[cfg(gles)]
pub gl: Option<HalSurface<hal::api::Gles>>,
} }
impl Resource for Surface { impl Resource for Surface {
@ -163,7 +168,7 @@ impl Surface {
&self, &self,
adapter: &Adapter<A>, adapter: &Adapter<A>,
) -> Result<hal::SurfaceCapabilities, GetSurfaceSupportError> { ) -> Result<hal::SurfaceCapabilities, GetSurfaceSupportError> {
let suf = A::get_surface(self).ok_or(GetSurfaceSupportError::Unsupported)?; let suf = A::surface_as_hal(self).ok_or(GetSurfaceSupportError::Unsupported)?;
profiling::scope!("surface_capabilities"); profiling::scope!("surface_capabilities");
let caps = unsafe { let caps = unsafe {
adapter adapter
@ -203,7 +208,7 @@ impl<A: HalApi> Adapter<A> {
} }
pub fn is_surface_supported(&self, surface: &Surface) -> bool { pub fn is_surface_supported(&self, surface: &Surface) -> bool {
let suf = A::get_surface(surface); let suf = A::surface_as_hal(surface);
// If get_surface returns None, then the API does not advertise support for the surface. // If get_surface returns None, then the API does not advertise support for the surface.
// //
@ -461,13 +466,25 @@ pub enum RequestAdapterError {
#[derive(Clone, Debug, Error)] #[derive(Clone, Debug, Error)]
#[non_exhaustive] #[non_exhaustive]
pub enum CreateSurfaceError { pub enum CreateSurfaceError {
#[error("No backend is available")] #[error("The backend {0} was not enabled on the instance.")]
NoSupportedBackend, BackendNotEnabled(Backend),
#[error(transparent)] #[error("Failed to create surface for any enabled backend: {0:?}")]
InstanceError(#[from] hal::InstanceError), FailedToCreateSurfaceForAnyBackend(HashMap<Backend, hal::InstanceError>),
} }
impl Global { impl Global {
/// Creates a new surface targeting the given display/window handles.
///
/// Internally attempts to create hal surfaces for all enabled backends.
///
/// Fails only if creation for surfaces for all enabled backends fails in which case
/// the error for each enabled backend is listed.
/// Vice versa, if creation for any backend succeeds, success is returned.
/// Surface creation errors are logged to the debug log in any case.
///
/// id_in:
/// - If `Some`, the id to assign to the surface. A new one will be generated otherwise.
///
/// # Safety /// # Safety
/// ///
/// - `display_handle` must be a valid object to create a surface upon. /// - `display_handle` must be a valid object to create a surface upon.
@ -483,51 +500,86 @@ impl Global {
profiling::scope!("Instance::create_surface"); profiling::scope!("Instance::create_surface");
fn init<A: HalApi>( fn init<A: HalApi>(
errors: &mut HashMap<Backend, hal::InstanceError>,
any_created: &mut bool,
backend: Backend,
inst: &Option<A::Instance>, inst: &Option<A::Instance>,
display_handle: raw_window_handle::RawDisplayHandle, display_handle: raw_window_handle::RawDisplayHandle,
window_handle: raw_window_handle::RawWindowHandle, window_handle: raw_window_handle::RawWindowHandle,
) -> Option<Result<AnySurface, hal::InstanceError>> { ) -> Option<HalSurface<A>> {
inst.as_ref().map(|inst| unsafe { inst.as_ref().and_then(|inst| {
match inst.create_surface(display_handle, window_handle) { match unsafe { inst.create_surface(display_handle, window_handle) } {
Ok(raw) => Ok(AnySurface::new::<A>(raw)), Ok(raw) => {
Err(e) => Err(e), *any_created = true;
Some(raw)
}
Err(err) => {
log::debug!(
"Instance::create_surface: failed to create surface for {:?}: {:?}",
backend,
err
);
errors.insert(backend, err);
None
}
} }
}) })
} }
let mut hal_surface: Option<Result<AnySurface, hal::InstanceError>> = None; let mut errors = HashMap::default();
let mut any_created = false;
#[cfg(vulkan)]
if hal_surface.is_none() {
hal_surface =
init::<hal::api::Vulkan>(&self.instance.vulkan, display_handle, window_handle);
}
#[cfg(metal)]
if hal_surface.is_none() {
hal_surface =
init::<hal::api::Metal>(&self.instance.metal, display_handle, window_handle);
}
#[cfg(dx12)]
if hal_surface.is_none() {
hal_surface =
init::<hal::api::Dx12>(&self.instance.dx12, display_handle, window_handle);
}
#[cfg(gles)]
if hal_surface.is_none() {
hal_surface = init::<hal::api::Gles>(&self.instance.gl, display_handle, window_handle);
}
let hal_surface = hal_surface.ok_or(CreateSurfaceError::NoSupportedBackend)??;
let surface = Surface { let surface = Surface {
presentation: Mutex::new(None), presentation: Mutex::new(None),
info: ResourceInfo::new("<Surface>", None), info: ResourceInfo::new("<Surface>", None),
raw: hal_surface,
#[cfg(vulkan)]
vulkan: init::<hal::api::Vulkan>(
&mut errors,
&mut any_created,
Backend::Vulkan,
&self.instance.vulkan,
display_handle,
window_handle,
),
#[cfg(metal)]
metal: init::<hal::api::Metal>(
&mut errors,
&mut any_created,
Backend::Metal,
&self.instance.metal,
display_handle,
window_handle,
),
#[cfg(dx12)]
dx12: init::<hal::api::Dx12>(
&mut errors,
&mut any_created,
Backend::Dx12,
&self.instance.dx12,
display_handle,
window_handle,
),
#[cfg(gles)]
gl: init::<hal::api::Gles>(
&mut errors,
&mut any_created,
Backend::Gl,
&self.instance.gl,
display_handle,
window_handle,
),
}; };
#[allow(clippy::arc_with_non_send_sync)] if any_created {
let (id, _) = self.surfaces.prepare(id_in).assign(Arc::new(surface)); #[allow(clippy::arc_with_non_send_sync)]
Ok(id) let (id, _) = self.surfaces.prepare(id_in).assign(Arc::new(surface));
Ok(id)
} else {
Err(CreateSurfaceError::FailedToCreateSurfaceForAnyBackend(
errors,
))
}
} }
/// # Safety /// # Safety
@ -538,29 +590,57 @@ impl Global {
&self, &self,
layer: *mut std::ffi::c_void, layer: *mut std::ffi::c_void,
id_in: Option<SurfaceId>, id_in: Option<SurfaceId>,
) -> SurfaceId { ) -> Result<SurfaceId, CreateSurfaceError> {
profiling::scope!("Instance::create_surface_metal"); profiling::scope!("Instance::create_surface_metal");
let surface = Surface { let surface = Surface {
presentation: Mutex::new(None), presentation: Mutex::new(None),
info: ResourceInfo::new("<Surface>", None), info: ResourceInfo::new("<Surface>", None),
raw: { metal: Some(self.instance.metal.as_ref().map_or(
let hal_surface = self Err(CreateSurfaceError::BackendNotEnabled(Backend::Metal)),
.instance |inst| {
.metal // we don't want to link to metal-rs for this
.as_ref() #[allow(clippy::transmute_ptr_to_ref)]
.map(|inst| { Ok(inst.create_surface_from_layer(unsafe { std::mem::transmute(layer) }))
// we don't want to link to metal-rs for this },
#[allow(clippy::transmute_ptr_to_ref)] )?),
inst.create_surface_from_layer(unsafe { std::mem::transmute(layer) }) #[cfg(dx12)]
}) dx12: None,
.unwrap(); #[cfg(vulkan)]
AnySurface::new::<hal::api::Metal>(hal_surface) vulkan: None,
}, #[cfg(gles)]
gl: None,
}; };
let (id, _) = self.surfaces.prepare(id_in).assign(Arc::new(surface)); let (id, _) = self.surfaces.prepare(id_in).assign(Arc::new(surface));
id Ok(id)
}
#[cfg(dx12)]
fn instance_create_surface_dx12(
&self,
id_in: Option<SurfaceId>,
create_surface_func: impl FnOnce(&HalInstance<hal::api::Dx12>) -> HalSurface<hal::api::Dx12>,
) -> Result<SurfaceId, CreateSurfaceError> {
let surface = Surface {
presentation: Mutex::new(None),
info: ResourceInfo::new("<Surface>", None),
dx12: Some(create_surface_func(
self.instance
.dx12
.as_ref()
.ok_or(CreateSurfaceError::BackendNotEnabled(Backend::Dx12))?,
)),
#[cfg(metal)]
metal: None,
#[cfg(vulkan)]
vulkan: None,
#[cfg(gles)]
gl: None,
};
let (id, _) = self.surfaces.prepare(id_in).assign(Arc::new(surface));
Ok(id)
} }
#[cfg(dx12)] #[cfg(dx12)]
@ -571,25 +651,11 @@ impl Global {
&self, &self,
visual: *mut std::ffi::c_void, visual: *mut std::ffi::c_void,
id_in: Option<SurfaceId>, id_in: Option<SurfaceId>,
) -> SurfaceId { ) -> Result<SurfaceId, CreateSurfaceError> {
profiling::scope!("Instance::instance_create_surface_from_visual"); profiling::scope!("Instance::instance_create_surface_from_visual");
self.instance_create_surface_dx12(id_in, |inst| unsafe {
let surface = Surface { inst.create_surface_from_visual(visual as _)
presentation: Mutex::new(None), })
info: ResourceInfo::new("<Surface>", None),
raw: {
let hal_surface = self
.instance
.dx12
.as_ref()
.map(|inst| unsafe { inst.create_surface_from_visual(visual as _) })
.unwrap();
AnySurface::new::<hal::api::Dx12>(hal_surface)
},
};
let (id, _) = self.surfaces.prepare(id_in).assign(Arc::new(surface));
id
} }
#[cfg(dx12)] #[cfg(dx12)]
@ -600,25 +666,11 @@ impl Global {
&self, &self,
surface_handle: *mut std::ffi::c_void, surface_handle: *mut std::ffi::c_void,
id_in: Option<SurfaceId>, id_in: Option<SurfaceId>,
) -> SurfaceId { ) -> Result<SurfaceId, CreateSurfaceError> {
profiling::scope!("Instance::instance_create_surface_from_surface_handle"); profiling::scope!("Instance::instance_create_surface_from_surface_handle");
self.instance_create_surface_dx12(id_in, |inst| unsafe {
let surface = Surface { inst.create_surface_from_surface_handle(surface_handle)
presentation: Mutex::new(None), })
info: ResourceInfo::new("<Surface>", None),
raw: {
let hal_surface = self
.instance
.dx12
.as_ref()
.map(|inst| unsafe { inst.create_surface_from_surface_handle(surface_handle) })
.unwrap();
AnySurface::new::<hal::api::Dx12>(hal_surface)
},
};
let (id, _) = self.surfaces.prepare(id_in).assign(Arc::new(surface));
id
} }
#[cfg(dx12)] #[cfg(dx12)]
@ -629,27 +681,11 @@ impl Global {
&self, &self,
swap_chain_panel: *mut std::ffi::c_void, swap_chain_panel: *mut std::ffi::c_void,
id_in: Option<SurfaceId>, id_in: Option<SurfaceId>,
) -> SurfaceId { ) -> Result<SurfaceId, CreateSurfaceError> {
profiling::scope!("Instance::instance_create_surface_from_swap_chain_panel"); profiling::scope!("Instance::instance_create_surface_from_swap_chain_panel");
self.instance_create_surface_dx12(id_in, |inst| unsafe {
let surface = Surface { inst.create_surface_from_swap_chain_panel(swap_chain_panel as _)
presentation: Mutex::new(None), })
info: ResourceInfo::new("<Surface>", None),
raw: {
let hal_surface = self
.instance
.dx12
.as_ref()
.map(|inst| unsafe {
inst.create_surface_from_swap_chain_panel(swap_chain_panel as _)
})
.unwrap();
AnySurface::new::<hal::api::Dx12>(hal_surface)
},
};
let (id, _) = self.surfaces.prepare(id_in).assign(Arc::new(surface));
id
} }
pub fn surface_drop(&self, id: SurfaceId) { pub fn surface_drop(&self, id: SurfaceId) {
@ -657,11 +693,15 @@ impl Global {
api_log!("Surface::drop {id:?}"); api_log!("Surface::drop {id:?}");
fn unconfigure<A: HalApi>(global: &Global, surface: &AnySurface, present: &Presentation) { fn unconfigure<A: HalApi>(
let hub = HalApi::hub(global); global: &Global,
if let Some(hal_surface) = surface.downcast_ref::<A>() { surface: &Option<HalSurface<A>>,
present: &Presentation,
) {
if let Some(surface) = surface {
let hub = HalApi::hub(global);
if let Some(device) = present.device.downcast_ref::<A>() { if let Some(device) = present.device.downcast_ref::<A>() {
hub.surface_unconfigure(device, hal_surface); hub.surface_unconfigure(device, surface);
} }
} }
} }
@ -669,15 +709,16 @@ impl Global {
let surface = self.surfaces.unregister(id); let surface = self.surfaces.unregister(id);
let surface = Arc::into_inner(surface.unwrap()) let surface = Arc::into_inner(surface.unwrap())
.expect("Surface cannot be destroyed because is still in use"); .expect("Surface cannot be destroyed because is still in use");
if let Some(present) = surface.presentation.lock().take() { if let Some(present) = surface.presentation.lock().take() {
#[cfg(vulkan)] #[cfg(vulkan)]
unconfigure::<hal::api::Vulkan>(self, &surface.raw, &present); unconfigure::<hal::api::Vulkan>(self, &surface.vulkan, &present);
#[cfg(metal)] #[cfg(metal)]
unconfigure::<hal::api::Metal>(self, &surface.raw, &present); unconfigure::<hal::api::Metal>(self, &surface.metal, &present);
#[cfg(dx12)] #[cfg(dx12)]
unconfigure::<hal::api::Dx12>(self, &surface.raw, &present); unconfigure::<hal::api::Dx12>(self, &surface.dx12, &present);
#[cfg(gles)] #[cfg(gles)]
unconfigure::<hal::api::Gles>(self, &surface.raw, &present); unconfigure::<hal::api::Gles>(self, &surface.gl, &present);
} }
self.instance.destroy_surface(surface); self.instance.destroy_surface(surface);
} }
@ -785,7 +826,7 @@ impl Global {
adapters.retain(|exposed| exposed.info.device_type == wgt::DeviceType::Cpu); adapters.retain(|exposed| exposed.info.device_type == wgt::DeviceType::Cpu);
} }
if let Some(surface) = compatible_surface { if let Some(surface) = compatible_surface {
let surface = &A::get_surface(surface); let surface = &A::surface_as_hal(surface);
adapters.retain(|exposed| unsafe { adapters.retain(|exposed| unsafe {
// If the surface does not exist for this backend, // If the surface does not exist for this backend,
// then the surface is not supported. // then the surface is not supported.

View File

@ -50,7 +50,6 @@
unused_qualifications unused_qualifications
)] )]
pub mod any_surface;
pub mod binding_model; pub mod binding_model;
pub mod command; pub mod command;
mod conv; mod conv;

View File

@ -157,7 +157,7 @@ impl Global {
#[cfg(not(feature = "trace"))] #[cfg(not(feature = "trace"))]
let _ = device; let _ = device;
let suf = A::get_surface(surface.as_ref()); let suf = A::surface_as_hal(surface.as_ref());
let (texture_id, status) = match unsafe { let (texture_id, status) = match unsafe {
suf.unwrap() suf.unwrap()
.acquire_texture(Some(std::time::Duration::from_millis( .acquire_texture(Some(std::time::Duration::from_millis(
@ -324,7 +324,7 @@ impl Global {
.textures .textures
.remove(texture.info.tracker_index()); .remove(texture.info.tracker_index());
let mut exclusive_snatch_guard = device.snatchable_lock.write(); let mut exclusive_snatch_guard = device.snatchable_lock.write();
let suf = A::get_surface(&surface); let suf = A::surface_as_hal(&surface);
let mut inner = texture.inner_mut(&mut exclusive_snatch_guard); let mut inner = texture.inner_mut(&mut exclusive_snatch_guard);
let inner = inner.as_mut().unwrap(); let inner = inner.as_mut().unwrap();
@ -418,7 +418,7 @@ impl Global {
.lock() .lock()
.textures .textures
.remove(texture.info.tracker_index()); .remove(texture.info.tracker_index());
let suf = A::get_surface(&surface); let suf = A::surface_as_hal(&surface);
let exclusive_snatch_guard = device.snatchable_lock.write(); let exclusive_snatch_guard = device.snatchable_lock.write();
match texture.inner.snatch(exclusive_snatch_guard).unwrap() { match texture.inner.snatch(exclusive_snatch_guard).unwrap() {
resource::TextureInner::Surface { mut raw, parent_id } => { resource::TextureInner::Surface { mut raw, parent_id } => {

View File

@ -1023,7 +1023,9 @@ impl Global {
profiling::scope!("Surface::as_hal"); profiling::scope!("Surface::as_hal");
let surface = self.surfaces.get(id).ok(); let surface = self.surfaces.get(id).ok();
let hal_surface = surface.as_ref().and_then(|surface| A::get_surface(surface)); let hal_surface = surface
.as_ref()
.and_then(|surface| A::surface_as_hal(surface));
hal_surface_callback(hal_surface) hal_surface_callback(hal_surface)
} }

View File

@ -554,7 +554,7 @@ impl crate::Context for ContextWgpuCore {
raw_window_handle, raw_window_handle,
} => unsafe { } => unsafe {
self.0 self.0
.instance_create_surface(raw_display_handle, raw_window_handle, None)? .instance_create_surface(raw_display_handle, raw_window_handle, None)
}, },
#[cfg(metal)] #[cfg(metal)]
@ -578,7 +578,7 @@ impl crate::Context for ContextWgpuCore {
self.0 self.0
.instance_create_surface_from_swap_chain_panel(swap_chain_panel, None) .instance_create_surface_from_swap_chain_panel(swap_chain_panel, None)
}, },
}; }?;
Ok(( Ok((
id, id,

View File

@ -366,9 +366,19 @@ static_assertions::assert_impl_all!(SurfaceConfiguration: Send, Sync);
/// serves a similar role. /// serves a similar role.
pub struct Surface<'window> { pub struct Surface<'window> {
context: Arc<C>, context: Arc<C>,
_surface: Option<Box<dyn WindowHandle + 'window>>,
/// Optionally, keep the source of the handle used for the surface alive.
///
/// This is useful for platforms where the surface is created from a window and the surface
/// would become invalid when the window is dropped.
_handle_source: Option<Box<dyn WindowHandle + 'window>>,
/// Wgpu-core surface id.
id: ObjectId, id: ObjectId,
data: Box<Data>,
/// Additional surface data returned by [`DynContext::instance_create_surface`].
surface_data: Box<Data>,
// Stores the latest `SurfaceConfiguration` that was set using `Surface::configure`. // Stores the latest `SurfaceConfiguration` that was set using `Surface::configure`.
// It is required to set the attributes of the `SurfaceTexture` in the // It is required to set the attributes of the `SurfaceTexture` in the
// `Surface::get_current_texture` method. // `Surface::get_current_texture` method.
@ -385,15 +395,15 @@ impl<'window> fmt::Debug for Surface<'window> {
f.debug_struct("Surface") f.debug_struct("Surface")
.field("context", &self.context) .field("context", &self.context)
.field( .field(
"_surface", "_handle_source",
&if self._surface.is_some() { &if self._handle_source.is_some() {
"Some" "Some"
} else { } else {
"None" "None"
}, },
) )
.field("id", &self.id) .field("id", &self.id)
.field("data", &self.data) .field("data", &self.surface_data)
.field("config", &self.config) .field("config", &self.config)
.finish() .finish()
} }
@ -405,7 +415,8 @@ static_assertions::assert_impl_all!(Surface<'_>: Send, Sync);
impl Drop for Surface<'_> { impl Drop for Surface<'_> {
fn drop(&mut self) { fn drop(&mut self) {
if !thread::panicking() { if !thread::panicking() {
self.context.surface_drop(&self.id, self.data.as_ref()) self.context
.surface_drop(&self.id, self.surface_data.as_ref())
} }
} }
} }
@ -1967,6 +1978,8 @@ impl Instance {
/// Creates a new surface targeting a given window/canvas/surface/etc.. /// Creates a new surface targeting a given window/canvas/surface/etc..
/// ///
/// Internally, this creates surfaces for all backends that are enabled for this instance.
///
/// See [`SurfaceTarget`] for what targets are supported. /// See [`SurfaceTarget`] for what targets are supported.
/// See [`Instance::create_surface_unsafe`] for surface creation with unsafe target variants. /// See [`Instance::create_surface_unsafe`] for surface creation with unsafe target variants.
/// ///
@ -1977,7 +1990,7 @@ impl Instance {
target: impl Into<SurfaceTarget<'window>>, target: impl Into<SurfaceTarget<'window>>,
) -> Result<Surface<'window>, CreateSurfaceError> { ) -> Result<Surface<'window>, CreateSurfaceError> {
// Handle origin (i.e. window) to optionally take ownership of to make the surface outlast the window. // Handle origin (i.e. window) to optionally take ownership of to make the surface outlast the window.
let handle_origin; let handle_source;
let target = target.into(); let target = target.into();
let mut surface = match target { let mut surface = match target {
@ -1987,14 +2000,14 @@ impl Instance {
inner: CreateSurfaceErrorKind::RawHandle(e), inner: CreateSurfaceErrorKind::RawHandle(e),
})?, })?,
); );
handle_origin = Some(window); handle_source = Some(window);
surface surface
}?, }?,
#[cfg(any(webgpu, webgl))] #[cfg(any(webgpu, webgl))]
SurfaceTarget::Canvas(canvas) => { SurfaceTarget::Canvas(canvas) => {
handle_origin = None; handle_source = None;
let value: &wasm_bindgen::JsValue = &canvas; let value: &wasm_bindgen::JsValue = &canvas;
let obj = std::ptr::NonNull::from(value).cast(); let obj = std::ptr::NonNull::from(value).cast();
@ -2013,7 +2026,7 @@ impl Instance {
#[cfg(any(webgpu, webgl))] #[cfg(any(webgpu, webgl))]
SurfaceTarget::OffscreenCanvas(canvas) => { SurfaceTarget::OffscreenCanvas(canvas) => {
handle_origin = None; handle_source = None;
let value: &wasm_bindgen::JsValue = &canvas; let value: &wasm_bindgen::JsValue = &canvas;
let obj = std::ptr::NonNull::from(value).cast(); let obj = std::ptr::NonNull::from(value).cast();
@ -2032,13 +2045,15 @@ impl Instance {
} }
}; };
surface._surface = handle_origin; surface._handle_source = handle_source;
Ok(surface) Ok(surface)
} }
/// Creates a new surface targeting a given window/canvas/surface/etc. using an unsafe target. /// Creates a new surface targeting a given window/canvas/surface/etc. using an unsafe target.
/// ///
/// Internally, this creates surfaces for all backends that are enabled for this instance.
///
/// See [`SurfaceTargetUnsafe`] for what targets are supported. /// See [`SurfaceTargetUnsafe`] for what targets are supported.
/// See [`Instance::create_surface`] for surface creation with safe target variants. /// See [`Instance::create_surface`] for surface creation with safe target variants.
/// ///
@ -2053,9 +2068,9 @@ impl Instance {
Ok(Surface { Ok(Surface {
context: Arc::clone(&self.context), context: Arc::clone(&self.context),
_surface: None, _handle_source: None,
id, id,
data, surface_data: data,
config: Mutex::new(None), config: Mutex::new(None),
}) })
} }
@ -2229,7 +2244,7 @@ impl Adapter {
&self.id, &self.id,
self.data.as_ref(), self.data.as_ref(),
&surface.id, &surface.id,
surface.data.as_ref(), surface.surface_data.as_ref(),
) )
} }
@ -4833,7 +4848,7 @@ impl Surface<'_> {
DynContext::surface_get_capabilities( DynContext::surface_get_capabilities(
&*self.context, &*self.context,
&self.id, &self.id,
self.data.as_ref(), self.surface_data.as_ref(),
&adapter.id, &adapter.id,
adapter.data.as_ref(), adapter.data.as_ref(),
) )
@ -4872,7 +4887,7 @@ impl Surface<'_> {
DynContext::surface_configure( DynContext::surface_configure(
&*self.context, &*self.context,
&self.id, &self.id,
self.data.as_ref(), self.surface_data.as_ref(),
&device.id, &device.id,
device.data.as_ref(), device.data.as_ref(),
config, config,
@ -4891,8 +4906,11 @@ impl Surface<'_> {
/// If a SurfaceTexture referencing this surface is alive when the swapchain is recreated, /// If a SurfaceTexture referencing this surface is alive when the swapchain is recreated,
/// recreating the swapchain will panic. /// recreating the swapchain will panic.
pub fn get_current_texture(&self) -> Result<SurfaceTexture, SurfaceError> { pub fn get_current_texture(&self) -> Result<SurfaceTexture, SurfaceError> {
let (texture_id, texture_data, status, detail) = let (texture_id, texture_data, status, detail) = DynContext::surface_get_current_texture(
DynContext::surface_get_current_texture(&*self.context, &self.id, self.data.as_ref()); &*self.context,
&self.id,
self.surface_data.as_ref(),
);
let suboptimal = match status { let suboptimal = match status {
SurfaceStatus::Good => false, SurfaceStatus::Good => false,
@ -4955,7 +4973,7 @@ impl Surface<'_> {
.downcast_ref::<crate::backend::ContextWgpuCore>() .downcast_ref::<crate::backend::ContextWgpuCore>()
.map(|ctx| unsafe { .map(|ctx| unsafe {
ctx.surface_as_hal::<A, F, R>( ctx.surface_as_hal::<A, F, R>(
self.data.downcast_ref().unwrap(), self.surface_data.downcast_ref().unwrap(),
hal_surface_callback, hal_surface_callback,
) )
}) })