Allow WebGPU & WebGL in same wasm and detect WebGPU availability (#5044)

* Rename backends: web -> webgpu, direct -> wgpu_core

* rename context objects for web & core

* allow webgpu & webgl features side-by-side

* make sure webgl ci doesn't use webgpu

* update any_backend_feature_enabled

* add panicing generate_report method for compatibility

* RequestDeviceErrorKind::Web rename, fixup various cfg attributes

* automatic webgpu support detection

* changelog entry

* fix emscripten

* fix weird cfg, fix comment typo

* remove try_generate_report again

* Make get_mapped_range_as_array_buffer WebGPU only again
This commit is contained in:
Andreas Reich 2024-01-14 10:45:52 +01:00 committed by GitHub
parent f89bd3b978
commit 7774f31021
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 291 additions and 212 deletions

View File

@ -367,7 +367,7 @@ jobs:
- name: execute tests
run: |
cd wgpu
wasm-pack test --headless --chrome --features webgl --workspace
wasm-pack test --headless --chrome --no-default-features --features wgsl,webgl --workspace
gpu-test:
# runtime is normally 5-15 minutes

View File

@ -67,6 +67,15 @@ Conversion to `wgpu::SurfaceTarget` is automatic for anything implementing `raw-
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)
### WebGPU & WebGL in the same binary
Enabling `webgl` no longer removes the `webgpu` backend.
Instead, there's a new (default enabled) `webgpu` feature that allows to explicitly opt-out of `webgpu` if so desired.
If both `webgl` & `webgpu` are enabled, `wgpu::Instance` decides upon creation whether to target wgpu-core/WebGL or WebGPU.
This means that adapter selection is not handled as with regular adapters, but still allows to decide at runtime whether
`webgpu` or the `webgl` backend should be used using a single wasm binary.
By @wumpf in [#5044](https://github.com/gfx-rs/wgpu/pull/5044)
### New Features
#### General

View File

@ -174,7 +174,7 @@ To run the test suite on WebGL (currently incomplete):
```
cd wgpu
wasm-pack test --headless --chrome --features webgl --workspace
wasm-pack test --headless --chrome --no-default-features --features webgl --workspace
```
This will automatically run the tests using a packaged browser. Remove `--headless` to run the tests with whatever browser you wish at `http://localhost:8000`.

View File

@ -16,7 +16,17 @@ pub fn initialize_instance() -> Instance {
//
// We can potentially work support back into the test runner in the future, but as the adapters are matched up
// based on adapter index, removing some backends messes up the indexes in annoying ways.
let backends = Backends::all();
//
// WORKAROUND for https://github.com/rust-lang/cargo/issues/7160:
// `--no-default-features` is not passed through correctly to the test runner.
// We use it whenever we want to explicitly run with webgl instead of webgpu.
// To "disable" webgpu regardless, we do this by removing the webgpu backend whenever we see
// the webgl feature.
let backends = if cfg!(feature = "webgl") {
Backends::all() - Backends::BROWSER_WEBGPU
} else {
Backends::all()
};
let dx12_shader_compiler = wgpu::util::dx12_shader_compiler_from_env().unwrap_or_default();
let gles_minor_version = wgpu::util::gles_minor_version_from_env().unwrap_or_default();
Instance::new(wgpu::InstanceDescriptor {

View File

@ -52,11 +52,11 @@ fn device_lifetime_check() {
instance.poll_all(false);
let pre_report = instance.generate_report();
let pre_report = instance.generate_report().unwrap().unwrap();
drop(queue);
drop(device);
let post_report = instance.generate_report();
let post_report = instance.generate_report().unwrap().unwrap();
assert_ne!(
pre_report, post_report,
"Queue and Device has not been dropped as expected"

View File

@ -12,7 +12,7 @@ async fn draw_test_with_reports(
use wgpu::util::DeviceExt;
let global_report = ctx.instance.generate_report();
let global_report = ctx.instance.generate_report().unwrap();
let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.devices.num_allocated, 1);
assert_eq!(report.queues.num_allocated, 1);
@ -21,7 +21,7 @@ async fn draw_test_with_reports(
.device
.create_shader_module(wgpu::include_wgsl!("./vertex_indices/draw.vert.wgsl"));
let global_report = ctx.instance.generate_report();
let global_report = ctx.instance.generate_report().unwrap();
let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.shader_modules.num_allocated, 1);
@ -41,7 +41,7 @@ async fn draw_test_with_reports(
}],
});
let global_report = ctx.instance.generate_report();
let global_report = ctx.instance.generate_report().unwrap();
let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.buffers.num_allocated, 0);
assert_eq!(report.bind_groups.num_allocated, 0);
@ -54,7 +54,7 @@ async fn draw_test_with_reports(
mapped_at_creation: false,
});
let global_report = ctx.instance.generate_report();
let global_report = ctx.instance.generate_report().unwrap();
let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.buffers.num_allocated, 1);
@ -67,7 +67,7 @@ async fn draw_test_with_reports(
}],
});
let global_report = ctx.instance.generate_report();
let global_report = ctx.instance.generate_report().unwrap();
let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.buffers.num_allocated, 1);
assert_eq!(report.bind_groups.num_allocated, 1);
@ -81,7 +81,7 @@ async fn draw_test_with_reports(
push_constant_ranges: &[],
});
let global_report = ctx.instance.generate_report();
let global_report = ctx.instance.generate_report().unwrap();
let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.buffers.num_allocated, 1);
assert_eq!(report.pipeline_layouts.num_allocated, 1);
@ -113,7 +113,7 @@ async fn draw_test_with_reports(
multiview: None,
});
let global_report = ctx.instance.generate_report();
let global_report = ctx.instance.generate_report().unwrap();
let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.buffers.num_allocated, 1);
assert_eq!(report.bind_groups.num_allocated, 1);
@ -125,7 +125,7 @@ async fn draw_test_with_reports(
drop(shader);
let global_report = ctx.instance.generate_report();
let global_report = ctx.instance.generate_report().unwrap();
let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.shader_modules.num_allocated, 1);
assert_eq!(report.shader_modules.num_kept_from_user, 0);
@ -153,7 +153,7 @@ async fn draw_test_with_reports(
);
let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let global_report = ctx.instance.generate_report();
let global_report = ctx.instance.generate_report().unwrap();
let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.buffers.num_allocated, 1);
assert_eq!(report.texture_views.num_allocated, 1);
@ -161,7 +161,7 @@ async fn draw_test_with_reports(
drop(texture);
let global_report = ctx.instance.generate_report();
let global_report = ctx.instance.generate_report().unwrap();
let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.buffers.num_allocated, 1);
assert_eq!(report.texture_views.num_allocated, 1);
@ -173,7 +173,7 @@ async fn draw_test_with_reports(
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
let global_report = ctx.instance.generate_report();
let global_report = ctx.instance.generate_report().unwrap();
let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.command_buffers.num_allocated, 1);
assert_eq!(report.buffers.num_allocated, 1);
@ -193,7 +193,7 @@ async fn draw_test_with_reports(
rpass.set_pipeline(&pipeline);
rpass.set_bind_group(0, &bg, &[]);
let global_report = ctx.instance.generate_report();
let global_report = ctx.instance.generate_report().unwrap();
let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.buffers.num_allocated, 1);
assert_eq!(report.bind_groups.num_allocated, 1);
@ -216,7 +216,7 @@ async fn draw_test_with_reports(
drop(bg);
drop(buffer);
let global_report = ctx.instance.generate_report();
let global_report = ctx.instance.generate_report().unwrap();
let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.command_buffers.num_kept_from_user, 1);
assert_eq!(report.render_pipelines.num_kept_from_user, 0);
@ -237,7 +237,7 @@ async fn draw_test_with_reports(
let submit_index = ctx.queue.submit(Some(encoder.finish()));
let global_report = ctx.instance.generate_report();
let global_report = ctx.instance.generate_report().unwrap();
let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.command_buffers.num_allocated, 0);
@ -245,7 +245,7 @@ async fn draw_test_with_reports(
.await
.panic_on_timeout();
let global_report = ctx.instance.generate_report();
let global_report = ctx.instance.generate_report().unwrap();
let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.render_pipelines.num_allocated, 0);
@ -260,7 +260,7 @@ async fn draw_test_with_reports(
drop(ctx.device);
drop(ctx.adapter);
let global_report = ctx.instance.generate_report();
let global_report = ctx.instance.generate_report().unwrap();
let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.queues.num_kept_from_user, 0);

View File

@ -155,7 +155,12 @@ bitflags::bitflags! {
const METAL = 1 << Backend::Metal as u32;
/// Supported on Windows 10
const DX12 = 1 << Backend::Dx12 as u32;
/// Supported when targeting the web through webassembly
/// Supported when targeting the web through webassembly with the `webgpu` feature enabled.
///
/// The WebGPU backend is special in several ways:
/// It is not not implemented by `wgpu_core` and instead by the higher level `wgpu` crate.
/// Whether WebGPU is targeted is decided upon the creation of the `wgpu::Instance`,
/// *not* upon adapter creation. See `wgpu::Instance::new`.
const BROWSER_WEBGPU = 1 << Backend::BrowserWebGpu as u32;
/// All the apis that wgpu offers first tier of support for.
///

View File

@ -24,7 +24,7 @@ targets = [
[lib]
[features]
default = ["wgsl", "dx12", "metal"]
default = ["wgsl", "dx12", "metal", "webgpu"]
#! ### Backends
# --------------------------------------------------------------------
@ -44,10 +44,12 @@ angle = ["wgc?/gles"]
## Enables the Vulkan backend on macOS & iOS.
vulkan-portability = ["wgc?/vulkan"]
## Enables the WebGPU backend on Wasm. Disabled when targeting `emscripten`.
webgpu = []
## Enables the GLES backend on Wasm
##
## * ⚠️ WIP: Currently will also enable GLES dependencies on any other targets.
## * ⚠️ WIP: This automatically disables use of WebGPU. See [#2804](https://github.com/gfx-rs/wgpu/issues/3514).
webgl = ["hal", "wgc/gles"]
#! ### Shading language support

View File

@ -2,8 +2,9 @@ fn main() {
cfg_aliases::cfg_aliases! {
native: { not(target_arch = "wasm32") },
webgl: { all(target_arch = "wasm32", not(target_os = "emscripten"), feature = "webgl") },
webgpu: { all(target_arch = "wasm32", not(target_os = "emscripten"), not(feature = "webgl")) },
webgpu: { all(target_arch = "wasm32", not(target_os = "emscripten"), feature = "webgpu") },
Emscripten: { all(target_arch = "wasm32", target_os = "emscripten") },
wgpu_core: { any(native, webgl, emscripten) },
send_sync: { any(
not(target_arch = "wasm32"),
all(feature = "fragile-send-sync-non-atomic-wasm", not(target_feature = "atomics"))

View File

@ -1,9 +1,9 @@
#[cfg(webgpu)]
mod web;
mod webgpu;
#[cfg(webgpu)]
pub(crate) use web::Context;
pub(crate) use webgpu::{get_browser_gpu_property, ContextWebGpu};
#[cfg(not(webgpu))]
mod direct;
#[cfg(not(webgpu))]
pub(crate) use direct::Context;
#[cfg(wgpu_core)]
mod wgpu_core;
#[cfg(wgpu_core)]
pub(crate) use wgpu_core::ContextWgpuCore;

View File

@ -69,19 +69,21 @@ unsafe impl<T> Send for Identified<T> {}
#[cfg(send_sync)]
unsafe impl<T> Sync for Identified<T> {}
pub(crate) struct Context(web_sys::Gpu);
pub(crate) struct ContextWebGpu(web_sys::Gpu);
#[cfg(send_sync)]
unsafe impl Send for Context {}
unsafe impl Send for ContextWebGpu {}
#[cfg(send_sync)]
unsafe impl Sync for Context {}
unsafe impl Sync for ContextWebGpu {}
#[cfg(send_sync)]
unsafe impl Send for BufferMappedRange {}
#[cfg(send_sync)]
unsafe impl Sync for BufferMappedRange {}
impl fmt::Debug for Context {
impl fmt::Debug for ContextWebGpu {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Context").field("type", &"Web").finish()
f.debug_struct("ContextWebGpu")
.field("type", &"Web")
.finish()
}
}
@ -540,7 +542,8 @@ fn map_texture_view_dimension(
}
fn map_buffer_copy_view(view: crate::ImageCopyBuffer<'_>) -> web_sys::GpuImageCopyBuffer {
let buffer: &<Context as crate::Context>::BufferData = downcast_ref(view.buffer.data.as_ref());
let buffer: &<ContextWebGpu as crate::Context>::BufferData =
downcast_ref(view.buffer.data.as_ref());
let mut mapped = web_sys::GpuImageCopyBuffer::new(&buffer.0.buffer);
if let Some(bytes_per_row) = view.layout.bytes_per_row {
mapped.bytes_per_row(bytes_per_row);
@ -553,7 +556,7 @@ fn map_buffer_copy_view(view: crate::ImageCopyBuffer<'_>) -> web_sys::GpuImageCo
}
fn map_texture_copy_view(view: crate::ImageCopyTexture<'_>) -> web_sys::GpuImageCopyTexture {
let texture: &<Context as crate::Context>::TextureData =
let texture: &<ContextWebGpu as crate::Context>::TextureData =
downcast_ref(view.texture.data.as_ref());
let mut mapped = web_sys::GpuImageCopyTexture::new(&texture.0);
mapped.mip_level(view.mip_level);
@ -564,7 +567,7 @@ fn map_texture_copy_view(view: crate::ImageCopyTexture<'_>) -> web_sys::GpuImage
fn map_tagged_texture_copy_view(
view: crate::ImageCopyTextureTagged<'_>,
) -> web_sys::GpuImageCopyTextureTagged {
let texture: &<Context as crate::Context>::TextureData =
let texture: &<ContextWebGpu as crate::Context>::TextureData =
downcast_ref(view.texture.data.as_ref());
let mut mapped = web_sys::GpuImageCopyTextureTagged::new(&texture.0);
mapped.mip_level(view.mip_level);
@ -821,7 +824,7 @@ fn future_request_device(
(device_id, device_data, queue_id, queue_data)
})
.map_err(|error_value| crate::RequestDeviceError {
inner: crate::RequestDeviceErrorKind::Web(error_value),
inner: crate::RequestDeviceErrorKind::WebGpu(error_value),
})
}
@ -877,7 +880,7 @@ where
*rc_callback.borrow_mut() = Some((closure_success, closure_rejected, callback));
}
impl Context {
impl ContextWebGpu {
/// Common portion of the internal branches of the public `instance_create_surface` function.
///
/// Note: Analogous code also exists in the WebGL2 backend at
@ -929,6 +932,15 @@ impl Context {
Ok(create_identified((canvas, context)))
}
/// Get mapped buffer range directly as a `js_sys::ArrayBuffer`.
pub fn buffer_get_mapped_range_as_array_buffer(
&self,
buffer_data: &<ContextWebGpu as crate::Context>::BufferData,
sub_range: Range<wgt::BufferAddress>,
) -> js_sys::ArrayBuffer {
buffer_data.0.get_mapped_array_buffer(sub_range)
}
}
// Represents the global object in the JavaScript context.
@ -952,7 +964,31 @@ pub enum Canvas {
Offscreen(web_sys::OffscreenCanvas),
}
impl crate::context::Context for Context {
/// Returns the browsers gpu object or `None` if the current context is neither the main thread nor a dedicated worker.
///
/// If WebGPU is not supported, the Gpu property is `undefined` (but *not* necessarily `None`).
///
/// See:
/// * <https://developer.mozilla.org/en-US/docs/Web/API/Navigator/gpu>
/// * <https://developer.mozilla.org/en-US/docs/Web/API/WorkerNavigator/gpu>
pub fn get_browser_gpu_property() -> Option<web_sys::Gpu> {
let global: Global = js_sys::global().unchecked_into();
if !global.window().is_undefined() {
Some(global.unchecked_into::<web_sys::Window>().navigator().gpu())
} else if !global.worker().is_undefined() {
Some(
global
.unchecked_into::<web_sys::WorkerGlobalScope>()
.navigator()
.gpu(),
)
} else {
None
}
}
impl crate::context::Context for ContextWebGpu {
type AdapterId = Identified<web_sys::GpuAdapter>;
type AdapterData = Sendable<web_sys::GpuAdapter>;
type DeviceId = Identified<web_sys::GpuDevice>;
@ -1022,20 +1058,12 @@ impl crate::context::Context for Context {
MakeSendFuture<wasm_bindgen_futures::JsFuture, fn(JsFutureResult) -> Option<crate::Error>>;
fn init(_instance_desc: wgt::InstanceDescriptor) -> Self {
let global: Global = js_sys::global().unchecked_into();
let gpu = if !global.window().is_undefined() {
global.unchecked_into::<web_sys::Window>().navigator().gpu()
} else if !global.worker().is_undefined() {
global
.unchecked_into::<web_sys::WorkerGlobalScope>()
.navigator()
.gpu()
} else {
let Some(gpu) = get_browser_gpu_property() else {
panic!(
"Accessing the GPU is only supported on the main thread or from a dedicated worker"
);
};
Context(gpu)
ContextWebGpu(gpu)
}
unsafe fn instance_create_surface(
@ -1587,7 +1615,7 @@ impl crate::context::Context for Context {
offset,
size,
}) => {
let buffer: &<Context as crate::Context>::BufferData =
let buffer: &<ContextWebGpu as crate::Context>::BufferData =
downcast_ref(buffer.data.as_ref());
let mut mapped_buffer_binding =
web_sys::GpuBufferBinding::new(&buffer.0.buffer);
@ -1601,7 +1629,7 @@ impl crate::context::Context for Context {
panic!("Web backend does not support arrays of buffers")
}
crate::BindingResource::Sampler(sampler) => {
let sampler: &<Context as crate::Context>::SamplerData =
let sampler: &<ContextWebGpu as crate::Context>::SamplerData =
downcast_ref(sampler.data.as_ref());
JsValue::from(&sampler.0)
}
@ -1609,7 +1637,7 @@ impl crate::context::Context for Context {
panic!("Web backend does not support arrays of samplers")
}
crate::BindingResource::TextureView(texture_view) => {
let texture_view: &<Context as crate::Context>::TextureViewData =
let texture_view: &<ContextWebGpu as crate::Context>::TextureViewData =
downcast_ref(texture_view.data.as_ref());
JsValue::from(&texture_view.0)
}
@ -1622,7 +1650,7 @@ impl crate::context::Context for Context {
})
.collect::<js_sys::Array>();
let bgl: &<Context as crate::Context>::BindGroupLayoutData =
let bgl: &<ContextWebGpu as crate::Context>::BindGroupLayoutData =
downcast_ref(desc.layout.data.as_ref());
let mut mapped_desc = web_sys::GpuBindGroupDescriptor::new(&mapped_entries, &bgl.0);
if let Some(label) = desc.label {
@ -1641,7 +1669,7 @@ impl crate::context::Context for Context {
.bind_group_layouts
.iter()
.map(|bgl| {
let bgl: &<Context as crate::Context>::BindGroupLayoutData =
let bgl: &<ContextWebGpu as crate::Context>::BindGroupLayoutData =
downcast_ref(bgl.data.as_ref());
&bgl.0
})
@ -1659,7 +1687,7 @@ impl crate::context::Context for Context {
device_data: &Self::DeviceData,
desc: &crate::RenderPipelineDescriptor<'_>,
) -> (Self::RenderPipelineId, Self::RenderPipelineData) {
let module: &<Context as crate::Context>::ShaderModuleData =
let module: &<ContextWebGpu as crate::Context>::ShaderModuleData =
downcast_ref(desc.vertex.module.data.as_ref());
let mut mapped_vertex_state =
web_sys::GpuVertexState::new(desc.vertex.entry_point, &module.0);
@ -1696,7 +1724,7 @@ impl crate::context::Context for Context {
let mut mapped_desc = web_sys::GpuRenderPipelineDescriptor::new(
&match desc.layout {
Some(layout) => {
let layout: &<Context as crate::Context>::PipelineLayoutData =
let layout: &<ContextWebGpu as crate::Context>::PipelineLayoutData =
downcast_ref(layout.data.as_ref());
JsValue::from(&layout.0)
}
@ -1734,7 +1762,7 @@ impl crate::context::Context for Context {
None => wasm_bindgen::JsValue::null(),
})
.collect::<js_sys::Array>();
let module: &<Context as crate::Context>::ShaderModuleData =
let module: &<ContextWebGpu as crate::Context>::ShaderModuleData =
downcast_ref(frag.module.data.as_ref());
let mapped_fragment_desc =
web_sys::GpuFragmentState::new(frag.entry_point, &module.0, &targets);
@ -1759,7 +1787,7 @@ impl crate::context::Context for Context {
device_data: &Self::DeviceData,
desc: &crate::ComputePipelineDescriptor<'_>,
) -> (Self::ComputePipelineId, Self::ComputePipelineData) {
let shader_module: &<Context as crate::Context>::ShaderModuleData =
let shader_module: &<ContextWebGpu as crate::Context>::ShaderModuleData =
downcast_ref(desc.module.data.as_ref());
let mapped_compute_stage =
web_sys::GpuProgrammableStage::new(desc.entry_point, &shader_module.0);
@ -1767,7 +1795,7 @@ impl crate::context::Context for Context {
let mut mapped_desc = web_sys::GpuComputePipelineDescriptor::new(
&match desc.layout {
Some(layout) => {
let layout: &<Context as crate::Context>::PipelineLayoutData =
let layout: &<ContextWebGpu as crate::Context>::PipelineLayoutData =
downcast_ref(layout.data.as_ref());
JsValue::from(&layout.0)
}
@ -2029,15 +2057,6 @@ impl crate::context::Context for Context {
})
}
fn buffer_get_mapped_range_as_array_buffer(
&self,
_buffer: &Self::BufferId,
buffer_data: &Self::BufferData,
sub_range: Range<wgt::BufferAddress>,
) -> js_sys::ArrayBuffer {
buffer_data.0.get_mapped_array_buffer(sub_range)
}
fn buffer_unmap(&self, _buffer: &Self::BufferId, buffer_data: &Self::BufferData) {
buffer_data.0.buffer.unmap();
buffer_data.0.mapping.borrow_mut().mapped_buffer = None;
@ -2322,7 +2341,7 @@ impl crate::context::Context for Context {
crate::LoadOp::Load => web_sys::GpuLoadOp::Load,
};
let view: &<Context as crate::Context>::TextureViewData =
let view: &<ContextWebGpu as crate::Context>::TextureViewData =
downcast_ref(ca.view.data.as_ref());
let mut mapped_color_attachment = web_sys::GpuRenderPassColorAttachment::new(
@ -2334,7 +2353,7 @@ impl crate::context::Context for Context {
mapped_color_attachment.clear_value(&cv);
}
if let Some(rt) = ca.resolve_target {
let resolve_target_view: &<Context as crate::Context>::TextureViewData =
let resolve_target_view: &<ContextWebGpu as crate::Context>::TextureViewData =
downcast_ref(rt.data.as_ref());
mapped_color_attachment.resolve_target(&resolve_target_view.0);
}
@ -2353,7 +2372,7 @@ impl crate::context::Context for Context {
}
if let Some(dsa) = &desc.depth_stencil_attachment {
let depth_stencil_attachment: &<Context as crate::Context>::TextureViewData =
let depth_stencil_attachment: &<ContextWebGpu as crate::Context>::TextureViewData =
downcast_ref(dsa.view.data.as_ref());
let mut mapped_depth_stencil_attachment =
web_sys::GpuRenderPassDepthStencilAttachment::new(&depth_stencil_attachment.0);
@ -2430,7 +2449,8 @@ impl crate::context::Context for Context {
offset: wgt::BufferAddress,
size: Option<wgt::BufferAddress>,
) {
let buffer: &<Context as crate::Context>::BufferData = downcast_ref(buffer.data.as_ref());
let buffer: &<ContextWebGpu as crate::Context>::BufferData =
downcast_ref(buffer.data.as_ref());
match size {
Some(size) => encoder_data.0.clear_buffer_with_f64_and_f64(
&buffer.0.buffer,

View File

@ -29,21 +29,23 @@ use wgt::WasmNotSendSync;
const LABEL: &str = "label";
pub struct Context(wgc::global::Global<wgc::identity::IdentityManagerFactory>);
pub struct ContextWgpuCore(wgc::global::Global<wgc::identity::IdentityManagerFactory>);
impl Drop for Context {
impl Drop for ContextWgpuCore {
fn drop(&mut self) {
//nothing
}
}
impl fmt::Debug for Context {
impl fmt::Debug for ContextWgpuCore {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Context").field("type", &"Native").finish()
f.debug_struct("ContextWgpuCore")
.field("type", &"Native")
.finish()
}
}
impl Context {
impl ContextWgpuCore {
pub unsafe fn from_hal_instance<A: wgc::hal_api::HalApi>(hal_instance: A::Instance) -> Self {
Self(unsafe {
wgc::global::Global::from_hal_instance::<A>(
@ -443,7 +445,7 @@ pub struct CommandEncoder {
open: bool,
}
impl crate::Context for Context {
impl crate::Context for ContextWgpuCore {
type AdapterId = wgc::id::AdapterId;
type AdapterData = ();
type DeviceId = wgc::id::DeviceId;

View File

@ -320,13 +320,6 @@ pub trait Context: Debug + WasmNotSendSync + Sized {
buffer_data: &Self::BufferData,
sub_range: Range<BufferAddress>,
) -> Box<dyn BufferMappedRange>;
#[cfg(webgpu)]
fn buffer_get_mapped_range_as_array_buffer(
&self,
buffer: &Self::BufferId,
buffer_data: &Self::BufferData,
sub_range: Range<BufferAddress>,
) -> js_sys::ArrayBuffer;
fn buffer_unmap(&self, buffer: &Self::BufferId, buffer_data: &Self::BufferData);
fn texture_create_view(
&self,
@ -1037,6 +1030,7 @@ impl ObjectId {
global_id: None,
};
#[allow(dead_code)]
pub fn new(id: NonZeroU64, global_id: NonZeroU64) -> Self {
Self {
id: Some(id),
@ -1346,13 +1340,6 @@ pub(crate) trait DynContext: Debug + WasmNotSendSync {
buffer_data: &crate::Data,
sub_range: Range<BufferAddress>,
) -> Box<dyn BufferMappedRange>;
#[cfg(webgpu)]
fn buffer_get_mapped_range_as_array_buffer(
&self,
buffer: &ObjectId,
buffer_data: &crate::Data,
sub_range: Range<BufferAddress>,
) -> js_sys::ArrayBuffer;
fn buffer_unmap(&self, buffer: &ObjectId, buffer_data: &crate::Data);
fn texture_create_view(
&self,
@ -2465,18 +2452,6 @@ where
Context::buffer_get_mapped_range(self, &buffer, buffer_data, sub_range)
}
#[cfg(webgpu)]
fn buffer_get_mapped_range_as_array_buffer(
&self,
buffer: &ObjectId,
buffer_data: &crate::Data,
sub_range: Range<BufferAddress>,
) -> js_sys::ArrayBuffer {
let buffer = <T::BufferId>::from(*buffer);
let buffer_data = downcast_ref(buffer_data);
Context::buffer_get_mapped_range_as_array_buffer(self, &buffer, buffer_data, sub_range)
}
fn buffer_unmap(&self, buffer: &ObjectId, buffer_data: &crate::Data) {
let buffer = <T::BufferId>::from(*buffer);
let buffer_data = downcast_ref(buffer_data);

View File

@ -16,11 +16,10 @@
//! - **`angle`** --- Enables the GLES backend via [ANGLE](https://github.com/google/angle) on macOS
//! using.
//! - **`vulkan-portability`** --- Enables the Vulkan backend on macOS & iOS.
//! - **`webgpu`** --- Enables the WebGPU backend on Wasm. Disabled when targeting `emscripten`.
//! - **`webgl`** --- Enables the GLES backend on Wasm
//!
//! - ⚠️ WIP: Currently will also enable GLES dependencies on any other targets.
//! - ⚠️ WIP: This automatically disables use of WebGPU. See
//! [#2804](https://github.com/gfx-rs/wgpu/issues/3514).
//!
//! ### Shading language support
//!
@ -73,7 +72,10 @@ use std::{
thread,
};
use context::{Context, DeviceRequest, DynContext, ObjectId};
#[allow(unused_imports)] // Unused if all backends are disabled.
use context::Context;
use context::{DeviceRequest, DynContext, ObjectId};
use parking_lot::Mutex;
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
@ -1722,8 +1724,6 @@ impl Instance {
/// See <https://github.com/gfx-rs/wgpu/issues/3514>
/// * Windows: always enables Vulkan and GLES with no way to opt out
/// * Linux: always enables Vulkan and GLES with no way to opt out
/// * Web: either targets WebGPU backend or, if `webgl` enabled, WebGL
/// * TODO: Support both WebGPU and WebGL at the same time, see <https://github.com/gfx-rs/wgpu/issues/2804>
pub const fn any_backend_feature_enabled() -> bool {
// Method intentionally kept verbose to keep it a bit easier to follow!
@ -1733,6 +1733,9 @@ impl Instance {
cfg!(feature = "metal")
|| cfg!(feature = "vulkan-portability")
|| cfg!(feature = "angle")
// On the web, either WebGPU or WebGL must be enabled.
} else if cfg!(target_arch = "wasm32") {
cfg!(feature = "webgpu") || cfg!(feature = "webgl")
} else {
true
}
@ -1745,11 +1748,21 @@ impl Instance {
/// - `instance_desc` - Has fields for which [backends][Backends] wgpu will choose
/// during instantiation, and which [DX12 shader compiler][Dx12Compiler] wgpu will use.
///
/// [`Backends::BROWSER_WEBGPU`] takes a special role:
/// If it is set and WebGPU support is detected, this instance will *only* be able to create
/// WebGPU adapters. If you instead want to force use of WebGL, either
/// disable the `webgpu` compile-time feature or do add the [`Backends::BROWSER_WEBGPU`]
/// flag to the the `instance_desc`'s `backends` field.
/// If it is set and WebGPU support is *not* detected, the instance will use wgpu-core
/// to create adapters. Meaning that if the `webgl` feature is enabled, it is able to create
/// a WebGL adapter.
///
/// # Panics
///
/// If no backend feature for the active target platform is enabled,
/// this method will panic, see [`Instance::any_backend_feature_enabled()`].
pub fn new(instance_desc: InstanceDescriptor) -> Self {
#[allow(unreachable_code)]
pub fn new(_instance_desc: InstanceDescriptor) -> Self {
if !Self::any_backend_feature_enabled() {
panic!(
"No wgpu backend feature that is implemented for the target platform was enabled. \
@ -1757,9 +1770,25 @@ impl Instance {
);
}
Self {
context: Arc::from(crate::backend::Context::init(instance_desc)),
#[cfg(webgpu)]
if _instance_desc.backends.contains(Backends::BROWSER_WEBGPU)
&& crate::backend::get_browser_gpu_property().map_or(false, |gpu| !gpu.is_undefined())
{
return Self {
context: Arc::from(crate::backend::ContextWebGpu::init(_instance_desc)),
};
}
#[cfg(wgpu_core)]
{
return Self {
context: Arc::from(crate::backend::ContextWgpuCore::init(_instance_desc)),
};
}
unreachable!(
"Earlier check of `any_backend_feature_enabled` should have prevented getting here!"
);
}
/// Create an new instance of wgpu from a wgpu-hal instance.
@ -1771,11 +1800,11 @@ impl Instance {
/// # Safety
///
/// Refer to the creation of wgpu-hal Instance for every backend.
#[cfg(not(webgpu))]
#[cfg(wgpu_core)]
pub unsafe fn from_hal<A: wgc::hal_api::HalApi>(hal_instance: A::Instance) -> Self {
Self {
context: Arc::new(unsafe {
crate::backend::Context::from_hal_instance::<A>(hal_instance)
crate::backend::ContextWgpuCore::from_hal_instance::<A>(hal_instance)
}),
}
}
@ -1790,15 +1819,13 @@ impl Instance {
/// - The raw instance handle returned must not be manually destroyed.
///
/// [`Instance`]: hal::Api::Instance
#[cfg(not(webgpu))]
#[cfg(wgpu_core)]
pub unsafe fn as_hal<A: wgc::hal_api::HalApi>(&self) -> Option<&A::Instance> {
unsafe {
self.context
.as_any()
.downcast_ref::<crate::backend::Context>()
.unwrap()
.instance_as_hal::<A>()
}
// If we don't have a wgpu-core instance, we don't have a hal instance either.
.downcast_ref::<crate::backend::ContextWgpuCore>()
.and_then(|ctx| unsafe { ctx.instance_as_hal::<A>() })
}
/// Create an new instance of wgpu from a wgpu-core instance.
@ -1810,34 +1837,40 @@ impl Instance {
/// # Safety
///
/// Refer to the creation of wgpu-core Instance.
#[cfg(not(webgpu))]
#[cfg(wgpu_core)]
pub unsafe fn from_core(core_instance: wgc::instance::Instance) -> Self {
Self {
context: Arc::new(unsafe {
crate::backend::Context::from_core_instance(core_instance)
crate::backend::ContextWgpuCore::from_core_instance(core_instance)
}),
}
}
/// Retrieves all available [`Adapter`]s that match the given [`Backends`].
///
/// Always returns an empty vector if the instance decided upon creation to
/// target WebGPU since adapter creation is always async on WebGPU.
///
/// # Arguments
///
/// - `backends` - Backends from which to enumerate adapters.
#[cfg(not(webgpu))]
pub fn enumerate_adapters(&self, backends: Backends) -> impl ExactSizeIterator<Item = Adapter> {
#[cfg(wgpu_core)]
pub fn enumerate_adapters(&self, backends: Backends) -> Vec<Adapter> {
let context = Arc::clone(&self.context);
self.context
.as_any()
.downcast_ref::<crate::backend::Context>()
.unwrap()
.enumerate_adapters(backends)
.downcast_ref::<crate::backend::ContextWgpuCore>()
.map(|ctx| {
ctx.enumerate_adapters(backends)
.into_iter()
.map(move |id| crate::Adapter {
context: Arc::clone(&context),
id: ObjectId::from(id),
data: Box::new(()),
})
.collect()
})
.unwrap_or_default()
}
/// Retrieves an [`Adapter`] which matches the given [`RequestAdapterOptions`].
@ -1863,7 +1896,7 @@ impl Instance {
/// # Safety
///
/// `hal_adapter` must be created from this instance internal handle.
#[cfg(not(webgpu))]
#[cfg(wgpu_core)]
pub unsafe fn create_adapter_from_hal<A: wgc::hal_api::HalApi>(
&self,
hal_adapter: hal::ExposedAdapter<A>,
@ -1872,7 +1905,7 @@ impl Instance {
let id = unsafe {
context
.as_any()
.downcast_ref::<crate::backend::Context>()
.downcast_ref::<crate::backend::ContextWgpuCore>()
.unwrap()
.create_adapter_from_hal(hal_adapter)
.into()
@ -2000,13 +2033,15 @@ impl Instance {
}
/// Generates memory report.
#[cfg(not(webgpu))]
pub fn generate_report(&self) -> wgc::global::GlobalReport {
///
/// Returns `None` if the feature is not supported by the backend
/// which happens only when WebGPU is pre-selected by the instance creation.
#[cfg(wgpu_core)]
pub fn generate_report(&self) -> Option<wgc::global::GlobalReport> {
self.context
.as_any()
.downcast_ref::<crate::backend::Context>()
.unwrap()
.generate_report()
.downcast_ref::<crate::backend::ContextWgpuCore>()
.map(|ctx| ctx.generate_report())
}
}
@ -2071,7 +2106,7 @@ impl Adapter {
///
/// - `hal_device` must be created from this adapter internal handle.
/// - `desc.features` must be a subset of `hal_device` features.
#[cfg(not(webgpu))]
#[cfg(wgpu_core)]
pub unsafe fn create_device_from_hal<A: wgc::hal_api::HalApi>(
&self,
hal_device: hal::OpenDevice<A>,
@ -2082,7 +2117,9 @@ impl Adapter {
unsafe {
self.context
.as_any()
.downcast_ref::<crate::backend::Context>()
.downcast_ref::<crate::backend::ContextWgpuCore>()
// Part of the safety requirements is that the device was generated from the same adapter.
// Therefore, unwrap is fine here since only WgpuCoreContext based adapters have the ability to create hal devices.
.unwrap()
.create_device_from_hal(&self.id.into(), hal_device, desc, trace_path)
}
@ -2121,17 +2158,19 @@ impl Adapter {
/// - The raw handle passed to the callback must not be manually destroyed.
///
/// [`A::Adapter`]: hal::Api::Adapter
#[cfg(not(webgpu))]
#[cfg(wgpu_core)]
pub unsafe fn as_hal<A: wgc::hal_api::HalApi, F: FnOnce(Option<&A::Adapter>) -> R, R>(
&self,
hal_adapter_callback: F,
) -> R {
unsafe {
self.context
if let Some(ctx) = self
.context
.as_any()
.downcast_ref::<crate::backend::Context>()
.unwrap()
.adapter_as_hal::<A, F, R>(self.id.into(), hal_adapter_callback)
.downcast_ref::<crate::backend::ContextWgpuCore>()
{
unsafe { ctx.adapter_as_hal::<A, F, R>(self.id.into(), hal_adapter_callback) }
} else {
hal_adapter_callback(None)
}
}
@ -2463,7 +2502,7 @@ impl Device {
/// - `hal_texture` must be created from this device internal handle
/// - `hal_texture` must be created respecting `desc`
/// - `hal_texture` must be initialized
#[cfg(not(webgpu))]
#[cfg(wgpu_core)]
pub unsafe fn create_texture_from_hal<A: wgc::hal_api::HalApi>(
&self,
hal_texture: A::Texture,
@ -2472,7 +2511,9 @@ impl Device {
let texture = unsafe {
self.context
.as_any()
.downcast_ref::<crate::backend::Context>()
.downcast_ref::<crate::backend::ContextWgpuCore>()
// Part of the safety requirements is that the texture was generated from the same hal device.
// Therefore, unwrap is fine here since only WgpuCoreContext has the ability to create hal textures.
.unwrap()
.create_texture_from_hal::<A>(
hal_texture,
@ -2500,7 +2541,7 @@ impl Device {
/// - `hal_buffer` must be created from this device internal handle
/// - `hal_buffer` must be created respecting `desc`
/// - `hal_buffer` must be initialized
#[cfg(not(webgpu))]
#[cfg(wgpu_core)]
pub unsafe fn create_buffer_from_hal<A: wgc::hal_api::HalApi>(
&self,
hal_buffer: A::Buffer,
@ -2514,7 +2555,9 @@ impl Device {
let (id, buffer) = unsafe {
self.context
.as_any()
.downcast_ref::<crate::backend::Context>()
.downcast_ref::<crate::backend::ContextWgpuCore>()
// Part of the safety requirements is that the buffer was generated from the same hal device.
// Therefore, unwrap is fine here since only WgpuCoreContext has the ability to create hal buffers.
.unwrap()
.create_buffer_from_hal::<A>(
hal_buffer,
@ -2604,21 +2647,20 @@ impl Device {
/// - The raw handle passed to the callback must not be manually destroyed.
///
/// [`A::Device`]: hal::Api::Device
#[cfg(not(webgpu))]
#[cfg(wgpu_core)]
pub unsafe fn as_hal<A: wgc::hal_api::HalApi, F: FnOnce(Option<&A::Device>) -> R, R>(
&self,
hal_device_callback: F,
) -> R {
unsafe {
) -> Option<R> {
self.context
.as_any()
.downcast_ref::<crate::backend::Context>()
.unwrap()
.device_as_hal::<A, F, R>(
.downcast_ref::<crate::backend::ContextWgpuCore>()
.map(|ctx| unsafe {
ctx.device_as_hal::<A, F, R>(
self.data.as_ref().downcast_ref().unwrap(),
hal_device_callback,
)
}
})
}
/// Destroy this device.
@ -2657,14 +2699,14 @@ pub struct RequestDeviceError {
enum RequestDeviceErrorKind {
/// Error from [`wgpu_core`].
// must match dependency cfg
#[cfg(not(webgpu))]
Core(core::instance::RequestDeviceError),
#[cfg(wgpu_core)]
Core(wgc::instance::RequestDeviceError),
/// Error from web API that was called by `wgpu` to request a device.
///
/// (This is currently never used by the webgl backend, but it could be.)
#[cfg(webgpu)]
Web(wasm_bindgen::JsValue),
WebGpu(wasm_bindgen::JsValue),
}
#[cfg(send_sync)]
@ -2676,15 +2718,17 @@ unsafe impl Sync for RequestDeviceErrorKind {}
static_assertions::assert_impl_all!(RequestDeviceError: Send, Sync);
impl fmt::Display for RequestDeviceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.inner {
#[cfg(not(webgpu))]
RequestDeviceErrorKind::Core(error) => error.fmt(f),
#[cfg(wgpu_core)]
RequestDeviceErrorKind::Core(error) => error.fmt(_f),
#[cfg(webgpu)]
RequestDeviceErrorKind::Web(error_js_value) => {
RequestDeviceErrorKind::WebGpu(error_js_value) => {
// wasm-bindgen provides a reasonable error stringification via `Debug` impl
write!(f, "{error_js_value:?}")
write!(_f, "{error_js_value:?}")
}
#[cfg(not(any(webgpu, wgpu_core)))]
_ => unimplemented!("unknown `RequestDeviceErrorKind`"),
}
}
}
@ -2692,17 +2736,19 @@ impl fmt::Display for RequestDeviceError {
impl error::Error for RequestDeviceError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match &self.inner {
#[cfg(not(webgpu))]
#[cfg(wgpu_core)]
RequestDeviceErrorKind::Core(error) => error.source(),
#[cfg(webgpu)]
RequestDeviceErrorKind::Web(_) => None,
RequestDeviceErrorKind::WebGpu(_) => None,
#[cfg(not(any(webgpu, wgpu_core)))]
_ => unimplemented!("unknown `RequestDeviceErrorKind`"),
}
}
}
#[cfg(not(webgpu))]
impl From<core::instance::RequestDeviceError> for RequestDeviceError {
fn from(error: core::instance::RequestDeviceError) -> Self {
#[cfg(wgpu_core)]
impl From<wgc::instance::RequestDeviceError> for RequestDeviceError {
fn from(error: wgc::instance::RequestDeviceError) -> Self {
Self {
inner: RequestDeviceErrorKind::Core(error),
}
@ -2718,7 +2764,7 @@ pub struct CreateSurfaceError {
#[derive(Clone, Debug)]
enum CreateSurfaceErrorKind {
/// Error from [`wgpu_hal`].
#[cfg(not(webgpu))]
#[cfg(wgpu_core)]
Hal(hal::InstanceError),
/// Error from WebGPU surface creation.
@ -2734,7 +2780,7 @@ static_assertions::assert_impl_all!(CreateSurfaceError: Send, Sync);
impl fmt::Display for CreateSurfaceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.inner {
#[cfg(not(webgpu))]
#[cfg(wgpu_core)]
CreateSurfaceErrorKind::Hal(e) => e.fmt(f),
CreateSurfaceErrorKind::Web(e) => e.fmt(f),
CreateSurfaceErrorKind::RawHandle(e) => e.fmt(f),
@ -2745,7 +2791,7 @@ impl fmt::Display for CreateSurfaceError {
impl error::Error for CreateSurfaceError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match &self.inner {
#[cfg(not(webgpu))]
#[cfg(wgpu_core)]
CreateSurfaceErrorKind::Hal(e) => e.source(),
CreateSurfaceErrorKind::Web(_) => None,
CreateSurfaceErrorKind::RawHandle(e) => e.source(),
@ -2753,7 +2799,7 @@ impl error::Error for CreateSurfaceError {
}
}
#[cfg(not(webgpu))]
#[cfg(wgpu_core)]
impl From<hal::InstanceError> for CreateSurfaceError {
fn from(e: hal::InstanceError) -> Self {
Self {
@ -2986,20 +3032,24 @@ impl<'a> BufferSlice<'a> {
}
/// Synchronously and immediately map a buffer for reading. If the buffer is not immediately mappable
/// through [`BufferDescriptor::mapped_at_creation`] or [`BufferSlice::map_async`], will panic.
/// through [`BufferDescriptor::mapped_at_creation`] or [`BufferSlice::map_async`], will fail.
///
/// This is useful in wasm builds when you want to pass mapped data directly to js. Unlike `get_mapped_range`
/// which unconditionally copies mapped data into the wasm heap, this function directly hands you the
/// ArrayBuffer that we mapped the data into in js.
/// This is useful when targeting WebGPU and you want to pass mapped data directly to js.
/// Unlike `get_mapped_range` which unconditionally copies mapped data into the wasm heap,
/// this function directly hands you the ArrayBuffer that we mapped the data into in js.
///
/// This is only available on WebGPU, on any other backends this will return `None`.
#[cfg(webgpu)]
pub fn get_mapped_range_as_array_buffer(&self) -> js_sys::ArrayBuffer {
pub fn get_mapped_range_as_array_buffer(&self) -> Option<js_sys::ArrayBuffer> {
self.buffer
.context
.as_any()
.downcast_ref::<crate::backend::ContextWebGpu>()
.map(|ctx| {
let buffer_data = crate::context::downcast_ref(self.buffer.data.as_ref());
let end = self.buffer.map_context.lock().add(self.offset, self.size);
DynContext::buffer_get_mapped_range_as_array_buffer(
&*self.buffer.context,
&self.buffer.id,
self.buffer.data.as_ref(),
self.offset..end,
)
ctx.buffer_get_mapped_range_as_array_buffer(buffer_data, self.offset..end)
})
}
/// Synchronously and immediately map a buffer for writing. If the buffer is not immediately mappable
@ -3035,18 +3085,21 @@ impl Texture {
/// # Safety
///
/// - The raw handle obtained from the hal Texture must not be manually destroyed
#[cfg(not(webgpu))]
#[cfg(wgpu_core)]
pub unsafe fn as_hal<A: wgc::hal_api::HalApi, F: FnOnce(Option<&A::Texture>)>(
&self,
hal_texture_callback: F,
) {
let texture = self.data.as_ref().downcast_ref().unwrap();
unsafe {
self.context
if let Some(ctx) = self
.context
.as_any()
.downcast_ref::<crate::backend::Context>()
.unwrap()
.texture_as_hal::<A, F>(texture, hal_texture_callback)
.downcast_ref::<crate::backend::ContextWgpuCore>()
{
unsafe { ctx.texture_as_hal::<A, F>(texture, hal_texture_callback) }
} else {
hal_texture_callback(None)
}
}
@ -4783,18 +4836,20 @@ impl Surface<'_> {
/// # Safety
///
/// - The raw handle obtained from the hal Surface must not be manually destroyed
#[cfg(not(webgpu))]
#[cfg(wgpu_core)]
pub unsafe fn as_hal<A: wgc::hal_api::HalApi, F: FnOnce(Option<&A::Surface>) -> R, R>(
&mut self,
hal_surface_callback: F,
) -> R {
unsafe {
) -> Option<R> {
self.context
.as_any()
.downcast_ref::<crate::backend::Context>()
.unwrap()
.surface_as_hal::<A, F, R>(self.data.downcast_ref().unwrap(), hal_surface_callback)
}
.downcast_ref::<crate::backend::ContextWgpuCore>()
.map(|ctx| unsafe {
ctx.surface_as_hal::<A, F, R>(
self.data.downcast_ref().unwrap(),
hal_surface_callback,
)
})
}
}

View File

@ -61,7 +61,7 @@ pub(crate) fn run_wasm(mut args: Arguments) -> Result<(), anyhow::Error> {
xshell::cmd!(
shell,
"cargo build --target wasm32-unknown-unknown --bin wgpu-examples --features webgl {release_flag...}"
"cargo build --target wasm32-unknown-unknown --bin wgpu-examples --no-default-features --features wgsl,webgl {release_flag...}"
)
.args(&cargo_args)
.quiet()