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).
- 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 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
- 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 suf = A::get_surface(surface);
let suf = A::surface_as_hal(surface);
let adapter = &device.adapter;
match adapter.raw.adapter.surface_capabilities(suf.unwrap()) {
Some(caps) => caps,
@ -2055,7 +2055,7 @@ impl Global {
// https://github.com/gfx-rs/wgpu/issues/4105
match unsafe {
A::get_surface(surface)
A::surface_as_hal(surface)
.unwrap()
.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 instance_as_hal(instance: &Instance) -> Option<&Self::Instance>;
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 {
@ -25,7 +25,7 @@ impl HalApi for hal::api::Empty {
fn hub(_: &Global) -> &Hub<Self> {
unimplemented!("called empty api")
}
fn get_surface(_: &Surface) -> Option<&Self::Surface> {
fn surface_as_hal(_: &Surface) -> Option<&Self::Surface> {
unimplemented!("called empty api")
}
}
@ -46,8 +46,8 @@ impl HalApi for hal::api::Vulkan {
fn hub(global: &Global) -> &Hub<Self> {
&global.hubs.vulkan
}
fn get_surface(surface: &Surface) -> Option<&Self::Surface> {
surface.raw.downcast_ref::<Self>()
fn surface_as_hal(surface: &Surface) -> Option<&Self::Surface> {
surface.vulkan.as_ref()
}
}
@ -67,8 +67,8 @@ impl HalApi for hal::api::Metal {
fn hub(global: &Global) -> &Hub<Self> {
&global.hubs.metal
}
fn get_surface(surface: &Surface) -> Option<&Self::Surface> {
surface.raw.downcast_ref::<Self>()
fn surface_as_hal(surface: &Surface) -> Option<&Self::Surface> {
surface.metal.as_ref()
}
}
@ -88,8 +88,8 @@ impl HalApi for hal::api::Dx12 {
fn hub(global: &Global) -> &Hub<Self> {
&global.hubs.dx12
}
fn get_surface(surface: &Surface) -> Option<&Self::Surface> {
surface.raw.downcast_ref::<Self>()
fn surface_as_hal(surface: &Surface) -> Option<&Self::Surface> {
surface.dx12.as_ref()
}
}
@ -110,7 +110,7 @@ impl HalApi for hal::api::Gles {
fn hub(global: &Global) -> &Hub<Self> {
&global.hubs.gl
}
fn get_surface(surface: &Surface) -> Option<&Self::Surface> {
surface.raw.downcast_ref::<Self>()
fn surface_as_hal(surface: &Surface) -> Option<&Self::Surface> {
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 Some(ref mut present) = surface.presentation.lock().take() {
if let Some(device) = present.device.downcast_ref::<A>() {
let suf = A::get_surface(surface);
let suf = A::surface_as_hal(surface);
unsafe {
suf.unwrap().unconfigure(device.raw());
//TODO: we could destroy the surface here

View File

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

View File

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

View File

@ -157,7 +157,7 @@ impl Global {
#[cfg(not(feature = "trace"))]
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 {
suf.unwrap()
.acquire_texture(Some(std::time::Duration::from_millis(
@ -324,7 +324,7 @@ impl Global {
.textures
.remove(texture.info.tracker_index());
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 inner = inner.as_mut().unwrap();
@ -418,7 +418,7 @@ impl Global {
.lock()
.textures
.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();
match texture.inner.snatch(exclusive_snatch_guard).unwrap() {
resource::TextureInner::Surface { mut raw, parent_id } => {

View File

@ -1023,7 +1023,9 @@ impl Global {
profiling::scope!("Surface::as_hal");
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)
}

View File

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

View File

@ -366,9 +366,19 @@ static_assertions::assert_impl_all!(SurfaceConfiguration: Send, Sync);
/// serves a similar role.
pub struct Surface<'window> {
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,
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`.
// It is required to set the attributes of the `SurfaceTexture` in the
// `Surface::get_current_texture` method.
@ -385,15 +395,15 @@ impl<'window> fmt::Debug for Surface<'window> {
f.debug_struct("Surface")
.field("context", &self.context)
.field(
"_surface",
&if self._surface.is_some() {
"_handle_source",
&if self._handle_source.is_some() {
"Some"
} else {
"None"
},
)
.field("id", &self.id)
.field("data", &self.data)
.field("data", &self.surface_data)
.field("config", &self.config)
.finish()
}
@ -405,7 +415,8 @@ static_assertions::assert_impl_all!(Surface<'_>: Send, Sync);
impl Drop for Surface<'_> {
fn drop(&mut self) {
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..
///
/// Internally, this creates surfaces for all backends that are enabled for this instance.
///
/// See [`SurfaceTarget`] for what targets are supported.
/// See [`Instance::create_surface_unsafe`] for surface creation with unsafe target variants.
///
@ -1977,7 +1990,7 @@ impl Instance {
target: impl Into<SurfaceTarget<'window>>,
) -> Result<Surface<'window>, CreateSurfaceError> {
// 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 mut surface = match target {
@ -1987,14 +2000,14 @@ impl Instance {
inner: CreateSurfaceErrorKind::RawHandle(e),
})?,
);
handle_origin = Some(window);
handle_source = Some(window);
surface
}?,
#[cfg(any(webgpu, webgl))]
SurfaceTarget::Canvas(canvas) => {
handle_origin = None;
handle_source = None;
let value: &wasm_bindgen::JsValue = &canvas;
let obj = std::ptr::NonNull::from(value).cast();
@ -2013,7 +2026,7 @@ impl Instance {
#[cfg(any(webgpu, webgl))]
SurfaceTarget::OffscreenCanvas(canvas) => {
handle_origin = None;
handle_source = None;
let value: &wasm_bindgen::JsValue = &canvas;
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)
}
/// 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 [`Instance::create_surface`] for surface creation with safe target variants.
///
@ -2053,9 +2068,9 @@ impl Instance {
Ok(Surface {
context: Arc::clone(&self.context),
_surface: None,
_handle_source: None,
id,
data,
surface_data: data,
config: Mutex::new(None),
})
}
@ -2229,7 +2244,7 @@ impl Adapter {
&self.id,
self.data.as_ref(),
&surface.id,
surface.data.as_ref(),
surface.surface_data.as_ref(),
)
}
@ -4833,7 +4848,7 @@ impl Surface<'_> {
DynContext::surface_get_capabilities(
&*self.context,
&self.id,
self.data.as_ref(),
self.surface_data.as_ref(),
&adapter.id,
adapter.data.as_ref(),
)
@ -4872,7 +4887,7 @@ impl Surface<'_> {
DynContext::surface_configure(
&*self.context,
&self.id,
self.data.as_ref(),
self.surface_data.as_ref(),
&device.id,
device.data.as_ref(),
config,
@ -4891,8 +4906,11 @@ impl Surface<'_> {
/// If a SurfaceTexture referencing this surface is alive when the swapchain is recreated,
/// recreating the swapchain will panic.
pub fn get_current_texture(&self) -> Result<SurfaceTexture, SurfaceError> {
let (texture_id, texture_data, status, detail) =
DynContext::surface_get_current_texture(&*self.context, &self.id, self.data.as_ref());
let (texture_id, texture_data, status, detail) = DynContext::surface_get_current_texture(
&*self.context,
&self.id,
self.surface_data.as_ref(),
);
let suboptimal = match status {
SurfaceStatus::Good => false,
@ -4955,7 +4973,7 @@ impl Surface<'_> {
.downcast_ref::<crate::backend::ContextWgpuCore>()
.map(|ctx| unsafe {
ctx.surface_as_hal::<A, F, R>(
self.data.downcast_ref().unwrap(),
self.surface_data.downcast_ref().unwrap(),
hal_surface_callback,
)
})