mirror of
https://github.com/gfx-rs/wgpu.git
synced 2024-11-25 08:13:27 +00:00
Unify surface creation by introducing new SurfaceTarget
enum (#4984)
This commit is contained in:
parent
bc65d84cdb
commit
4fd4a7142e
@ -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
|
||||
|
@ -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 }) => {
|
||||
|
@ -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 });
|
||||
|
@ -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!(
|
||||
|
@ -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
|
||||
///
|
||||
|
@ -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(),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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 _))
|
||||
}
|
||||
|
||||
|
456
wgpu/src/lib.rs
456
wgpu/src/lib.rs
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user