Unify surface creation by introducing new SurfaceTarget enum (#4984)

This commit is contained in:
Andreas Reich 2024-01-12 04:24:01 +01:00 committed by GitHub
parent bc65d84cdb
commit 4fd4a7142e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 345 additions and 505 deletions

View File

@ -59,6 +59,14 @@ This feature allowed you to call `global_id` on any wgpu opaque handle to get a
Wgpu now exposes backend feature for the Direct3D 12 (`dx12`) and Metal (`metal`) backend. These are enabled by default, but don't do anything when not targetting the corresponding OS. By @daxpedda in [#4815](https://github.com/gfx-rs/wgpu/pull/4815)
### Unified surface creation
Previously, there were various specialized surface creation functions for various platform specific handles.
Now, `wgpu::Instance::create_surface` & `wgpu::Instance::create_surface_unsafe` instead each take a value that can be converted to the unified `wgpu::SurfaceTarget`/`wgpu::SurfaceTargetUnsafe` enums.
Conversion to `wgpu::SurfaceTarget` is automatic for anything implementing `raw-window-handle`'s `HasWindowHandle` & `HasDisplayHandle` traits,
meaning that you can continue to e.g. pass references to winit windows as before.
By @wumpf in [#4984](https://github.com/gfx-rs/wgpu/pull/4984)
### New Features
#### General

View File

@ -63,7 +63,8 @@ fn main() {
window.window_handle().unwrap().into(),
wgc::id::TypedId::zip(0, 1, wgt::Backend::Empty),
)
};
}
.unwrap();
let device = match actions.pop() {
Some(trace::Action::Init { desc, backend }) => {

View File

@ -51,7 +51,7 @@ pub async fn initialize_adapter(adapter_index: usize) -> (Instance, Adapter, Opt
let canvas = initialize_html_canvas();
_surface = instance
.create_surface_from_canvas(canvas.clone())
.create_surface(wgpu::SurfaceTarget::Canvas(canvas.clone()))
.expect("could not create surface from canvas");
surface_guard = Some(SurfaceGuard { canvas });

View File

@ -1,6 +1,6 @@
//! Test that `create_surface_*()` accurately reports those errors we can provoke.
/// This test applies to those cfgs that have a `create_surface_from_canvas` method, which
/// This test applies to those cfgs that can create a surface from a canvas, which
/// include WebGL and WebGPU, but *not* Emscripten GLES.
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
#[wasm_bindgen_test::wasm_bindgen_test]
@ -15,7 +15,7 @@ fn canvas_get_context_returned_null() {
#[allow(clippy::redundant_clone)] // false positive — can't and shouldn't move out.
let error = instance
.create_surface_from_canvas(canvas.clone())
.create_surface(wgpu::SurfaceTarget::Canvas(canvas.clone()))
.unwrap_err();
assert!(

View File

@ -479,68 +479,55 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
display_handle: raw_window_handle::RawDisplayHandle,
window_handle: raw_window_handle::RawWindowHandle,
id_in: Input<G, SurfaceId>,
) -> SurfaceId {
) -> Result<SurfaceId, hal::InstanceError> {
profiling::scope!("Instance::create_surface");
fn init<A: HalApi>(
any_surface: &mut Option<AnySurface>,
inst: &Option<A::Instance>,
display_handle: raw_window_handle::RawDisplayHandle,
window_handle: raw_window_handle::RawWindowHandle,
) {
if any_surface.is_none() {
if let Some(surface) = inst.as_ref().and_then(|inst| unsafe {
match inst.create_surface(display_handle, window_handle) {
Ok(raw) => Some(HalSurface::<A> { raw: Arc::new(raw) }),
Err(e) => {
log::warn!("Error: {:?}", e);
None
}
}
}) {
*any_surface = Some(AnySurface::new(surface));
) -> Option<Result<AnySurface, hal::InstanceError>> {
inst.as_ref().map(|inst| unsafe {
match inst.create_surface(display_handle, window_handle) {
Ok(raw) => Ok(AnySurface::new(HalSurface::<A> { raw: Arc::new(raw) })),
Err(e) => Err(e),
}
}
})
}
let mut hal_surface = None;
let mut hal_surface: Option<Result<AnySurface, hal::InstanceError>> = None;
#[cfg(all(feature = "vulkan", not(target_arch = "wasm32")))]
init::<hal::api::Vulkan>(
&mut hal_surface,
&self.instance.vulkan,
display_handle,
window_handle,
);
if hal_surface.is_none() {
hal_surface =
init::<hal::api::Vulkan>(&self.instance.vulkan, display_handle, window_handle);
}
#[cfg(all(feature = "metal", any(target_os = "macos", target_os = "ios")))]
init::<hal::api::Metal>(
&mut hal_surface,
&self.instance.metal,
display_handle,
window_handle,
);
if hal_surface.is_none() {
hal_surface =
init::<hal::api::Metal>(&self.instance.metal, display_handle, window_handle);
}
#[cfg(all(feature = "dx12", windows))]
init::<hal::api::Dx12>(
&mut hal_surface,
&self.instance.dx12,
display_handle,
window_handle,
);
if hal_surface.is_none() {
hal_surface =
init::<hal::api::Dx12>(&self.instance.dx12, display_handle, window_handle);
}
#[cfg(feature = "gles")]
init::<hal::api::Gles>(
&mut hal_surface,
&self.instance.gl,
display_handle,
window_handle,
);
if hal_surface.is_none() {
hal_surface = init::<hal::api::Gles>(&self.instance.gl, display_handle, window_handle);
}
// This is only None if there's no instance at all.
let hal_surface = hal_surface.unwrap()?;
let surface = Surface {
presentation: Mutex::new(None),
info: ResourceInfo::new("<Surface>"),
raw: hal_surface.unwrap(),
raw: hal_surface,
};
let (id, _) = self.surfaces.prepare::<G>(id_in).assign(surface);
id
Ok(id)
}
/// # Safety
@ -578,78 +565,6 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
id
}
#[cfg(all(
target_arch = "wasm32",
not(target_os = "emscripten"),
feature = "gles"
))]
pub fn create_surface_webgl_canvas(
&self,
canvas: web_sys::HtmlCanvasElement,
id_in: Input<G, SurfaceId>,
) -> Result<SurfaceId, hal::InstanceError> {
profiling::scope!("Instance::create_surface_webgl_canvas");
let surface = Surface {
presentation: Mutex::new(None),
info: ResourceInfo::new("<Surface>"),
raw: {
let hal_surface: HalSurface<hal::api::Gles> = self
.instance
.gl
.as_ref()
.map(|inst| {
let raw_surface = inst.create_surface_from_canvas(canvas)?;
Ok(HalSurface {
raw: Arc::new(raw_surface),
})
})
.transpose()?
.unwrap();
AnySurface::new(hal_surface)
},
};
let (id, _) = self.surfaces.prepare::<G>(id_in).assign(surface);
Ok(id)
}
#[cfg(all(
target_arch = "wasm32",
not(target_os = "emscripten"),
feature = "gles"
))]
pub fn create_surface_webgl_offscreen_canvas(
&self,
canvas: web_sys::OffscreenCanvas,
id_in: Input<G, SurfaceId>,
) -> Result<SurfaceId, hal::InstanceError> {
profiling::scope!("Instance::create_surface_webgl_offscreen_canvas");
let surface = Surface {
presentation: Mutex::new(None),
info: ResourceInfo::new("<Surface>"),
raw: {
let hal_surface: HalSurface<hal::api::Gles> = self
.instance
.gl
.as_ref()
.map(|inst| {
let raw_surface = inst.create_surface_from_offscreen_canvas(canvas)?;
Ok(HalSurface {
raw: Arc::new(raw_surface),
})
})
.transpose()?
.unwrap();
AnySurface::new(hal_surface)
},
};
let (id, _) = self.surfaces.prepare::<G>(id_in).assign(surface);
Ok(id)
}
#[cfg(all(feature = "dx12", windows))]
/// # Safety
///

View File

@ -7,7 +7,8 @@ use crate::{
DownlevelCapabilities, Features, Label, Limits, LoadOp, MapMode, Operations,
PipelineLayoutDescriptor, RenderBundleEncoderDescriptor, RenderPipelineDescriptor,
SamplerDescriptor, ShaderModuleDescriptor, ShaderModuleDescriptorSpirV, ShaderSource, StoreOp,
SurfaceStatus, TextureDescriptor, TextureViewDescriptor, UncapturedErrorHandler,
SurfaceStatus, SurfaceTargetUnsafe, TextureDescriptor, TextureViewDescriptor,
UncapturedErrorHandler,
};
use arrayvec::ArrayVec;
@ -231,81 +232,6 @@ impl Context {
self.0.generate_report()
}
#[cfg(metal)]
pub unsafe fn create_surface_from_core_animation_layer(
&self,
layer: *mut std::ffi::c_void,
) -> Surface {
let id = unsafe { self.0.instance_create_surface_metal(layer, ()) };
Surface {
id,
configured_device: Mutex::default(),
}
}
#[cfg(any(webgpu, webgl))]
pub fn instance_create_surface_from_canvas(
&self,
canvas: web_sys::HtmlCanvasElement,
) -> Result<Surface, crate::CreateSurfaceError> {
let id = self.0.create_surface_webgl_canvas(canvas, ())?;
Ok(Surface {
id,
configured_device: Mutex::default(),
})
}
#[cfg(any(webgpu, webgl))]
pub fn instance_create_surface_from_offscreen_canvas(
&self,
canvas: web_sys::OffscreenCanvas,
) -> Result<Surface, crate::CreateSurfaceError> {
let id = self.0.create_surface_webgl_offscreen_canvas(canvas, ())?;
Ok(Surface {
id,
configured_device: Mutex::default(),
})
}
#[cfg(dx12)]
pub unsafe fn create_surface_from_visual(&self, visual: *mut std::ffi::c_void) -> Surface {
let id = unsafe { self.0.instance_create_surface_from_visual(visual, ()) };
Surface {
id,
configured_device: Mutex::default(),
}
}
#[cfg(dx12)]
pub unsafe fn create_surface_from_surface_handle(
&self,
surface_handle: *mut std::ffi::c_void,
) -> Surface {
let id = unsafe {
self.0
.instance_create_surface_from_surface_handle(surface_handle, ())
};
Surface {
id,
configured_device: Mutex::default(),
}
}
#[cfg(dx12)]
pub unsafe fn create_surface_from_swap_chain_panel(
&self,
swap_chain_panel: *mut std::ffi::c_void,
) -> Surface {
let id = unsafe {
self.0
.instance_create_surface_from_swap_chain_panel(swap_chain_panel, ())
};
Surface {
id,
configured_device: Mutex::default(),
}
}
fn handle_error(
&self,
sink_mutex: &Mutex<ErrorSinkRaw>,
@ -594,19 +520,45 @@ impl crate::Context for Context {
unsafe fn instance_create_surface(
&self,
display_handle: raw_window_handle::RawDisplayHandle,
window_handle: raw_window_handle::RawWindowHandle,
target: SurfaceTargetUnsafe,
) -> Result<(Self::SurfaceId, Self::SurfaceData), crate::CreateSurfaceError> {
let id = unsafe {
self.0
.instance_create_surface(display_handle, window_handle, ())
let id = match target {
SurfaceTargetUnsafe::RawHandle {
raw_display_handle,
raw_window_handle,
} => unsafe {
self.0
.instance_create_surface(raw_display_handle, raw_window_handle, ())?
},
#[cfg(metal)]
SurfaceTargetUnsafe::CoreAnimationLayer(layer) => unsafe {
self.0.instance_create_surface_metal(layer, ())
},
#[cfg(dx12)]
SurfaceTargetUnsafe::CompositionVisual(visual) => unsafe {
self.0.instance_create_surface_from_visual(visual, ())
},
#[cfg(dx12)]
SurfaceTargetUnsafe::SurfaceHandle(surface_handle) => unsafe {
self.0
.instance_create_surface_from_surface_handle(surface_handle, ())
},
#[cfg(dx12)]
SurfaceTargetUnsafe::SwapChainPanel(swap_chain_panel) => unsafe {
self.0
.instance_create_surface_from_swap_chain_panel(swap_chain_panel, ())
},
};
Ok((
id,
Surface {
id,
configured_device: Mutex::new(None),
configured_device: Mutex::default(),
},
))
}

View File

@ -18,7 +18,7 @@ use wasm_bindgen::{prelude::*, JsCast};
use crate::{
context::{downcast_ref, ObjectId, QueueWriteBuffer, Unused},
UncapturedErrorHandler,
SurfaceTargetUnsafe, UncapturedErrorHandler,
};
fn create_identified<T>(value: T) -> (Identified<T>, Sendable<T>) {
@ -878,35 +878,7 @@ where
}
impl Context {
pub fn instance_create_surface_from_canvas(
&self,
canvas: web_sys::HtmlCanvasElement,
) -> Result<
(
<Self as crate::Context>::SurfaceId,
<Self as crate::Context>::SurfaceData,
),
crate::CreateSurfaceError,
> {
let result = canvas.get_context("webgpu");
self.create_surface_from_context(Canvas::Canvas(canvas), result)
}
pub fn instance_create_surface_from_offscreen_canvas(
&self,
canvas: web_sys::OffscreenCanvas,
) -> Result<
(
<Self as crate::Context>::SurfaceId,
<Self as crate::Context>::SurfaceData,
),
crate::CreateSurfaceError,
> {
let result = canvas.get_context("webgpu");
self.create_surface_from_context(Canvas::Offscreen(canvas), result)
}
/// Common portion of public `instance_create_surface_from_*` functions.
/// Common portion of the internal branches of the public `instance_create_surface` function.
///
/// Note: Analogous code also exists in the WebGL2 backend at
/// `wgpu_hal::gles::web::Instance`.
@ -1068,36 +1040,50 @@ impl crate::context::Context for Context {
unsafe fn instance_create_surface(
&self,
_display_handle: raw_window_handle::RawDisplayHandle,
window_handle: raw_window_handle::RawWindowHandle,
target: SurfaceTargetUnsafe,
) -> Result<(Self::SurfaceId, Self::SurfaceData), crate::CreateSurfaceError> {
let canvas_element: web_sys::HtmlCanvasElement = match window_handle {
raw_window_handle::RawWindowHandle::Web(handle) => {
let canvas_node: wasm_bindgen::JsValue = web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| {
doc.query_selector_all(&format!("[data-raw-handle=\"{}\"]", handle.id))
.ok()
})
.and_then(|nodes| nodes.get(0))
.expect("expected to find single canvas")
.into();
canvas_node.into()
}
raw_window_handle::RawWindowHandle::WebCanvas(handle) => {
let value: &JsValue = unsafe { handle.obj.cast().as_ref() };
value.clone().unchecked_into()
}
raw_window_handle::RawWindowHandle::WebOffscreenCanvas(handle) => {
let value: &JsValue = unsafe { handle.obj.cast().as_ref() };
let canvas: web_sys::OffscreenCanvas = value.clone().unchecked_into();
match target {
SurfaceTargetUnsafe::RawHandle {
raw_display_handle: _,
raw_window_handle,
} => {
let canvas_element: web_sys::HtmlCanvasElement = match raw_window_handle {
raw_window_handle::RawWindowHandle::Web(handle) => {
let canvas_node: wasm_bindgen::JsValue = web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| {
doc.query_selector_all(&format!(
"[data-raw-handle=\"{}\"]",
handle.id
))
.ok()
})
.and_then(|nodes| nodes.get(0))
.expect("expected to find single canvas")
.into();
canvas_node.into()
}
raw_window_handle::RawWindowHandle::WebCanvas(handle) => {
let value: &JsValue = unsafe { handle.obj.cast().as_ref() };
value.clone().unchecked_into()
}
raw_window_handle::RawWindowHandle::WebOffscreenCanvas(handle) => {
let value: &JsValue = unsafe { handle.obj.cast().as_ref() };
let canvas: web_sys::OffscreenCanvas = value.clone().unchecked_into();
let context_result = canvas.get_context("webgpu");
return self.instance_create_surface_from_offscreen_canvas(canvas);
}
_ => panic!("expected valid handle for canvas"),
};
return self.create_surface_from_context(
Canvas::Offscreen(canvas),
context_result,
);
}
_ => panic!("expected valid handle for canvas"),
};
self.instance_create_surface_from_canvas(canvas_element)
let context_result = canvas_element.get_context("webgpu");
self.create_surface_from_context(Canvas::Canvas(canvas_element), context_result)
}
}
}
fn instance_request_adapter(
@ -2146,7 +2132,7 @@ impl crate::context::Context for Context {
_pipeline_layout: &Self::PipelineLayoutId,
_pipeline_layout_data: &Self::PipelineLayoutData,
) {
// Dropped automatically
// Dropped automaticaly
}
fn shader_module_drop(

View File

@ -14,8 +14,8 @@ use crate::{
PipelineLayoutDescriptor, QuerySetDescriptor, RenderBundleDescriptor,
RenderBundleEncoderDescriptor, RenderPassDescriptor, RenderPipelineDescriptor,
RequestAdapterOptions, RequestDeviceError, SamplerDescriptor, ShaderModuleDescriptor,
ShaderModuleDescriptorSpirV, Texture, TextureDescriptor, TextureViewDescriptor,
UncapturedErrorHandler,
ShaderModuleDescriptorSpirV, SurfaceTargetUnsafe, Texture, TextureDescriptor,
TextureViewDescriptor, UncapturedErrorHandler,
};
/// Meta trait for an id tracked by a context.
@ -98,8 +98,7 @@ pub trait Context: Debug + WasmNotSendSync + Sized {
fn init(instance_desc: wgt::InstanceDescriptor) -> Self;
unsafe fn instance_create_surface(
&self,
display_handle: raw_window_handle::RawDisplayHandle,
window_handle: raw_window_handle::RawWindowHandle,
target: SurfaceTargetUnsafe,
) -> Result<(Self::SurfaceId, Self::SurfaceData), crate::CreateSurfaceError>;
fn instance_request_adapter(
&self,
@ -1143,8 +1142,7 @@ pub(crate) trait DynContext: Debug + WasmNotSendSync {
unsafe fn instance_create_surface(
&self,
display_handle: raw_window_handle::RawDisplayHandle,
window_handle: raw_window_handle::RawWindowHandle,
target: SurfaceTargetUnsafe,
) -> Result<(ObjectId, Box<crate::Data>), crate::CreateSurfaceError>;
#[allow(clippy::type_complexity)]
fn instance_request_adapter(
@ -2001,11 +1999,9 @@ where
unsafe fn instance_create_surface(
&self,
display_handle: raw_window_handle::RawDisplayHandle,
window_handle: raw_window_handle::RawWindowHandle,
target: SurfaceTargetUnsafe,
) -> Result<(ObjectId, Box<crate::Data>), crate::CreateSurfaceError> {
let (surface, data) =
unsafe { Context::instance_create_surface(self, display_handle, window_handle) }?;
let (surface, data) = unsafe { Context::instance_create_surface(self, target) }?;
Ok((surface.into(), Box::new(data) as _))
}

View File

@ -365,7 +365,7 @@ static_assertions::assert_impl_all!(SurfaceConfiguration: Send, Sync);
/// serves a similar role.
pub struct Surface<'window> {
context: Arc<C>,
_surface: Option<Box<dyn WasmNotSendSync + 'window>>,
_surface: Option<Box<dyn WindowHandle + 'window>>,
id: ObjectId,
data: Box<Data>,
// Stores the latest `SurfaceConfiguration` that was set using `Surface::configure`.
@ -409,6 +409,149 @@ impl Drop for Surface<'_> {
}
}
/// Super trait for window handles as used in [`SurfaceTarget`].
pub trait WindowHandle: HasWindowHandle + HasDisplayHandle + WasmNotSendSync {}
impl<T> WindowHandle for T where T: HasWindowHandle + HasDisplayHandle + WasmNotSendSync {}
/// The window/canvas/surface/swap-chain/etc. a surface is attached to, for use with safe surface creation.
///
/// This is either a window or an actual web canvas depending on the platform and
/// enabled features.
/// Refer to the individual variants for more information.
///
/// See also [`SurfaceTargetUnsafe`] for unsafe variants.
#[non_exhaustive]
pub enum SurfaceTarget<'window> {
/// Window handle producer.
///
/// If the specified display and window handle are not supported by any of the backends, then the surface
/// will not be supported by any adapters.
///
/// # Errors
///
/// - On WebGL2: surface creation returns an error if the browser does not support WebGL2,
/// or declines to provide GPU access (such as due to a resource shortage).
///
/// # Panics
///
/// - On macOS/Metal: will panic if not called on the main thread.
/// - On web: will panic if the `raw_window_handle` does not properly refer to a
/// canvas element.
Window(Box<dyn WindowHandle + 'window>),
/// Surface from a `web_sys::HtmlCanvasElement`.
///
/// The `canvas` argument must be a valid `<canvas>` element to
/// create a surface upon.
///
/// # Errors
///
/// - On WebGL2: surface creation will return an error if the browser does not support WebGL2,
/// or declines to provide GPU access (such as due to a resource shortage).
#[cfg(any(webgpu, webgl))]
Canvas(web_sys::HtmlCanvasElement),
/// Surface from a `web_sys::OffscreenCanvas`.
///
/// The `canvas` argument must be a valid `OffscreenCanvas` object
/// to create a surface upon.
///
/// # Errors
///
/// - On WebGL2: surface creation will return an error if the browser does not support WebGL2,
/// or declines to provide GPU access (such as due to a resource shortage).
#[cfg(any(webgpu, webgl))]
OffscreenCanvas(web_sys::OffscreenCanvas),
}
impl<'a, T> From<T> for SurfaceTarget<'a>
where
T: WindowHandle + 'a,
{
fn from(window: T) -> Self {
Self::Window(Box::new(window))
}
}
/// The window/canvas/surface/swap-chain/etc. a surface is attached to, for use with unsafe surface creation.
///
/// This is either a window or an actual web canvas depending on the platform and
/// enabled features.
/// Refer to the individual variants for more information.
///
/// See also [`SurfaceTarget`] for safe variants.
#[non_exhaustive]
pub enum SurfaceTargetUnsafe {
/// Raw window & display handle.
///
/// If the specified display and window handle are not supported by any of the backends, then the surface
/// will not be supported by any adapters.
///
/// # Safety
///
/// - `raw_window_handle` & `raw_display_handle` must be valid objects to create a surface upon.
/// - `raw_window_handle` & `raw_display_handle` must remain valid until after the returned
/// [`Surface`] is dropped.
RawHandle {
/// Raw display handle, underlying display must outlive the surface created from this.
raw_display_handle: raw_window_handle::RawDisplayHandle,
/// Raw display handle, underlying window must outlive the surface created from this.
raw_window_handle: raw_window_handle::RawWindowHandle,
},
/// Surface from `CoreAnimationLayer`.
///
/// # Safety
///
/// - layer must be a valid object to create a surface upon.
#[cfg(metal)]
CoreAnimationLayer(*mut std::ffi::c_void),
/// Surface from `IDCompositionVisual`.
///
/// # Safety
///
/// - visual must be a valid IDCompositionVisual to create a surface upon.
#[cfg(dx12)]
CompositionVisual(*mut std::ffi::c_void),
/// Surface from DX12 `SurfaceHandle`.
///
/// # Safety
///
/// - surface_handle must be a valid SurfaceHandle to create a surface upon.
#[cfg(dx12)]
SurfaceHandle(*mut std::ffi::c_void),
/// Surface from DX12 `SwapChainPanel`.
///
/// # Safety
///
/// - visual must be a valid SwapChainPanel to create a surface upon.
#[cfg(dx12)]
SwapChainPanel(*mut std::ffi::c_void),
}
impl SurfaceTargetUnsafe {
/// Creates a [`SurfaceTargetUnsafe::RawHandle`] from a window.
///
/// # Safety
///
/// - `window` must outlive the resulting surface target
/// (and subsequently the surface created for this target).
pub unsafe fn from_window<T>(window: &T) -> Result<Self, raw_window_handle::HandleError>
where
T: HasDisplayHandle + HasWindowHandle,
{
Ok(Self::RawHandle {
raw_display_handle: window.display_handle()?.as_raw(),
raw_window_handle: window.window_handle()?.as_raw(),
})
}
}
/// Handle to a binding group layout.
///
/// A `BindGroupLayout` is a handle to the GPU-side layout of a binding group. It can be used to
@ -1740,75 +1883,92 @@ impl Instance {
}
}
/// Creates a surface from a raw window handle.
/// Creates a new surface targeting a given window/canvas/surface/etc..
///
/// If the specified display and window handle are not supported by any of the backends, then the surface
/// will not be supported by any adapters.
/// See [`SurfaceTarget`] for what targets are supported.
/// See [`Instance::create_surface`] for surface creation with unsafe target variants.
///
/// If a reference is passed in `window`, the returned [`Surface`] will
/// hold a lifetime to it. Owned values will return a [`Surface<'static>`]
/// instead.
///
/// # Errors
///
/// - On WebGL2: Will return an error if the browser does not support WebGL2,
/// or declines to provide GPU access (such as due to a resource shortage).
///
/// # Panics
///
/// - On macOS/Metal: will panic if not called on the main thread.
/// - On web: will panic if the `raw_window_handle` does not properly refer to a
/// canvas element.
pub fn create_surface<'window, W>(
/// Most commonly used are window handles (or provider of windows handles)
/// which can be passed directly as they're automatically converted to [`SurfaceTarget`].
pub fn create_surface<'window>(
&self,
window: W,
) -> Result<Surface<'window>, CreateSurfaceError>
where
W: HasWindowHandle + HasDisplayHandle + WasmNotSendSync + 'window,
{
let mut surface = unsafe { self.create_surface_from_raw(&window) }?;
surface._surface = Some(Box::new(window));
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 target = target.into();
let mut surface = match target {
SurfaceTarget::Window(window) => unsafe {
let surface = self.create_surface_unsafe(
SurfaceTargetUnsafe::from_window(&window).map_err(|e| CreateSurfaceError {
inner: CreateSurfaceErrorKind::RawHandle(e),
})?,
);
handle_origin = Some(window);
surface
}?,
#[cfg(any(webgpu, webgl))]
SurfaceTarget::Canvas(canvas) => {
handle_origin = None;
let value: &wasm_bindgen::JsValue = &canvas;
let obj = std::ptr::NonNull::from(value).cast();
let raw_window_handle = raw_window_handle::WebCanvasWindowHandle::new(obj).into();
let raw_display_handle = raw_window_handle::WebDisplayHandle::new().into();
// Note that we need to call this while we still have `value` around.
// This is safe without storing canvas to `handle_origin` since the surface will create a copy internally.
unsafe {
self.create_surface_unsafe(SurfaceTargetUnsafe::RawHandle {
raw_display_handle,
raw_window_handle,
})
}?
}
#[cfg(any(webgpu, webgl))]
SurfaceTarget::OffscreenCanvas(canvas) => {
handle_origin = None;
let value: &wasm_bindgen::JsValue = &canvas;
let obj = std::ptr::NonNull::from(value).cast();
let raw_window_handle =
raw_window_handle::WebOffscreenCanvasWindowHandle::new(obj).into();
let raw_display_handle = raw_window_handle::WebDisplayHandle::new().into();
// Note that we need to call this while we still have `value` around.
// This is safe without storing canvas to `handle_origin` since the surface will create a copy internally.
unsafe {
self.create_surface_unsafe(SurfaceTargetUnsafe::RawHandle {
raw_display_handle,
raw_window_handle,
})
}?
}
};
surface._surface = handle_origin;
Ok(surface)
}
/// An alternative version to [`create_surface()`](Self::create_surface)
/// which has no lifetime requirements to `window` and doesn't require
/// [`Send`] or [`Sync`] (on non-Wasm targets). This makes it `unsafe`
/// instead and always returns a [`Surface<'static>`].
/// Creates a new surface targeting a given window/canvas/surface/etc. using an unsafe target.
///
/// See [`create_surface()`](Self::create_surface) for more details.
/// See [`SurfaceTargetUnsafe`] for what targets are supported.
/// See [`Instance::create_surface`] for surface creation with safe target variants.
///
/// # Safety
///
/// - `raw_window_handle` must be a valid object to create a surface upon.
/// - `raw_window_handle` must remain valid until after the returned [`Surface`] is
/// dropped.
pub unsafe fn create_surface_from_raw<W>(
/// - See respective [`SurfaceTargetUnsafe`] variants for safety requirements.
pub unsafe fn create_surface_unsafe<'window>(
&self,
window: &W,
) -> Result<Surface<'static>, CreateSurfaceError>
where
W: HasWindowHandle + HasDisplayHandle,
{
let raw_display_handle = window
.display_handle()
.map_err(|e| CreateSurfaceError {
inner: CreateSurfaceErrorKind::RawHandle(e),
})?
.as_raw();
let raw_window_handle = window
.window_handle()
.map_err(|e| CreateSurfaceError {
inner: CreateSurfaceErrorKind::RawHandle(e),
})?
.as_raw();
let (id, data) = unsafe {
DynContext::instance_create_surface(
&*self.context,
raw_display_handle,
raw_window_handle,
)
}?;
target: SurfaceTargetUnsafe,
) -> Result<Surface<'window>, CreateSurfaceError> {
let (id, data) = unsafe { self.context.instance_create_surface(target) }?;
Ok(Surface {
context: Arc::clone(&self.context),
_surface: None,
@ -1818,184 +1978,6 @@ impl Instance {
})
}
/// Creates a surface from `CoreAnimationLayer`.
///
/// # Safety
///
/// - layer must be a valid object to create a surface upon.
#[cfg(metal)]
pub unsafe fn create_surface_from_core_animation_layer(
&self,
layer: *mut std::ffi::c_void,
) -> Surface<'static> {
let surface = unsafe {
self.context
.as_any()
.downcast_ref::<crate::backend::Context>()
.unwrap()
.create_surface_from_core_animation_layer(layer)
};
Surface {
context: Arc::clone(&self.context),
_surface: None,
id: ObjectId::from(surface.id()),
data: Box::new(surface),
config: Mutex::new(None),
}
}
/// Creates a surface from `IDCompositionVisual`.
///
/// # Safety
///
/// - visual must be a valid IDCompositionVisual to create a surface upon.
#[cfg(dx12)]
pub unsafe fn create_surface_from_visual(
&self,
visual: *mut std::ffi::c_void,
) -> Surface<'static> {
let surface = unsafe {
self.context
.as_any()
.downcast_ref::<crate::backend::Context>()
.unwrap()
.create_surface_from_visual(visual)
};
Surface {
context: Arc::clone(&self.context),
_surface: None,
id: ObjectId::from(surface.id()),
data: Box::new(surface),
config: Mutex::new(None),
}
}
/// Creates a surface from `SurfaceHandle`.
///
/// # Safety
///
/// - surface_handle must be a valid SurfaceHandle to create a surface upon.
#[cfg(dx12)]
pub unsafe fn create_surface_from_surface_handle(
&self,
surface_handle: *mut std::ffi::c_void,
) -> Surface<'static> {
let surface = unsafe {
self.context
.as_any()
.downcast_ref::<crate::backend::Context>()
.unwrap()
.create_surface_from_surface_handle(surface_handle)
};
Surface {
context: Arc::clone(&self.context),
_surface: None,
id: ObjectId::from(surface.id()),
data: Box::new(surface),
config: Mutex::new(None),
}
}
/// Creates a surface from `SwapChainPanel`.
///
/// # Safety
///
/// - visual must be a valid SwapChainPanel to create a surface upon.
#[cfg(dx12)]
pub unsafe fn create_surface_from_swap_chain_panel(
&self,
swap_chain_panel: *mut std::ffi::c_void,
) -> Surface<'static> {
let surface = unsafe {
self.context
.as_any()
.downcast_ref::<crate::backend::Context>()
.unwrap()
.create_surface_from_swap_chain_panel(swap_chain_panel)
};
Surface {
context: Arc::clone(&self.context),
_surface: None,
id: ObjectId::from(surface.id()),
data: Box::new(surface),
config: Mutex::new(None),
}
}
/// Creates a surface from a `web_sys::HtmlCanvasElement`.
///
/// The `canvas` argument must be a valid `<canvas>` element to
/// create a surface upon.
///
/// # Errors
///
/// - On WebGL2: Will return an error if the browser does not support WebGL2,
/// or declines to provide GPU access (such as due to a resource shortage).
#[cfg(any(webgpu, webgl))]
pub fn create_surface_from_canvas(
&self,
canvas: web_sys::HtmlCanvasElement,
) -> Result<Surface<'static>, CreateSurfaceError> {
let surface = self
.context
.as_any()
.downcast_ref::<crate::backend::Context>()
.unwrap()
.instance_create_surface_from_canvas(canvas)?;
// TODO: This is ugly, a way to create things from a native context needs to be made nicer.
Ok(Surface {
context: Arc::clone(&self.context),
_surface: None,
#[cfg(webgl)]
id: ObjectId::from(surface.id()),
#[cfg(webgl)]
data: Box::new(surface),
#[cfg(webgpu)]
id: ObjectId::UNUSED,
#[cfg(webgpu)]
data: Box::new(surface.1),
config: Mutex::new(None),
})
}
/// Creates a surface from a `web_sys::OffscreenCanvas`.
///
/// The `canvas` argument must be a valid `OffscreenCanvas` object
/// to create a surface upon.
///
/// # Errors
///
/// - On WebGL2: Will return an error if the browser does not support WebGL2,
/// or declines to provide GPU access (such as due to a resource shortage).
#[cfg(any(webgpu, webgl))]
pub fn create_surface_from_offscreen_canvas(
&self,
canvas: web_sys::OffscreenCanvas,
) -> Result<Surface<'static>, CreateSurfaceError> {
let surface = self
.context
.as_any()
.downcast_ref::<crate::backend::Context>()
.unwrap()
.instance_create_surface_from_offscreen_canvas(canvas)?;
// TODO: This is ugly, a way to create things from a native context needs to be made nicer.
Ok(Surface {
context: Arc::clone(&self.context),
_surface: None,
#[cfg(webgl)]
id: ObjectId::from(surface.id()),
#[cfg(webgl)]
data: Box::new(surface),
#[cfg(webgpu)]
id: ObjectId::UNUSED,
#[cfg(webgpu)]
data: Box::new(surface.1),
config: Mutex::new(None),
})
}
/// Polls all devices.
///
/// If `force_wait` is true and this is not running on the web, then this