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 - name: execute tests
run: | run: |
cd wgpu cd wgpu
wasm-pack test --headless --chrome --features webgl --workspace wasm-pack test --headless --chrome --no-default-features --features wgsl,webgl --workspace
gpu-test: gpu-test:
# runtime is normally 5-15 minutes # 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. 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) 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 ### New Features
#### General #### General

View File

@ -174,7 +174,7 @@ To run the test suite on WebGL (currently incomplete):
``` ```
cd wgpu 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`. 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 // 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. // 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 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(); let gles_minor_version = wgpu::util::gles_minor_version_from_env().unwrap_or_default();
Instance::new(wgpu::InstanceDescriptor { Instance::new(wgpu::InstanceDescriptor {

View File

@ -52,11 +52,11 @@ fn device_lifetime_check() {
instance.poll_all(false); instance.poll_all(false);
let pre_report = instance.generate_report(); let pre_report = instance.generate_report().unwrap().unwrap();
drop(queue); drop(queue);
drop(device); drop(device);
let post_report = instance.generate_report(); let post_report = instance.generate_report().unwrap().unwrap();
assert_ne!( assert_ne!(
pre_report, post_report, pre_report, post_report,
"Queue and Device has not been dropped as expected" "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; 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); let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.devices.num_allocated, 1); assert_eq!(report.devices.num_allocated, 1);
assert_eq!(report.queues.num_allocated, 1); assert_eq!(report.queues.num_allocated, 1);
@ -21,7 +21,7 @@ async fn draw_test_with_reports(
.device .device
.create_shader_module(wgpu::include_wgsl!("./vertex_indices/draw.vert.wgsl")); .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); let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.shader_modules.num_allocated, 1); 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); let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.buffers.num_allocated, 0); assert_eq!(report.buffers.num_allocated, 0);
assert_eq!(report.bind_groups.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, 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); let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.buffers.num_allocated, 1); 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); let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.buffers.num_allocated, 1); assert_eq!(report.buffers.num_allocated, 1);
assert_eq!(report.bind_groups.num_allocated, 1); assert_eq!(report.bind_groups.num_allocated, 1);
@ -81,7 +81,7 @@ async fn draw_test_with_reports(
push_constant_ranges: &[], 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); let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.buffers.num_allocated, 1); assert_eq!(report.buffers.num_allocated, 1);
assert_eq!(report.pipeline_layouts.num_allocated, 1); assert_eq!(report.pipeline_layouts.num_allocated, 1);
@ -113,7 +113,7 @@ async fn draw_test_with_reports(
multiview: None, 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); let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.buffers.num_allocated, 1); assert_eq!(report.buffers.num_allocated, 1);
assert_eq!(report.bind_groups.num_allocated, 1); assert_eq!(report.bind_groups.num_allocated, 1);
@ -125,7 +125,7 @@ async fn draw_test_with_reports(
drop(shader); 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); let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.shader_modules.num_allocated, 1); assert_eq!(report.shader_modules.num_allocated, 1);
assert_eq!(report.shader_modules.num_kept_from_user, 0); 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 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); let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.buffers.num_allocated, 1); assert_eq!(report.buffers.num_allocated, 1);
assert_eq!(report.texture_views.num_allocated, 1); assert_eq!(report.texture_views.num_allocated, 1);
@ -161,7 +161,7 @@ async fn draw_test_with_reports(
drop(texture); 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); let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.buffers.num_allocated, 1); assert_eq!(report.buffers.num_allocated, 1);
assert_eq!(report.texture_views.num_allocated, 1); assert_eq!(report.texture_views.num_allocated, 1);
@ -173,7 +173,7 @@ async fn draw_test_with_reports(
.device .device
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); .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); let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.command_buffers.num_allocated, 1); assert_eq!(report.command_buffers.num_allocated, 1);
assert_eq!(report.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_pipeline(&pipeline);
rpass.set_bind_group(0, &bg, &[]); 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); let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.buffers.num_allocated, 1); assert_eq!(report.buffers.num_allocated, 1);
assert_eq!(report.bind_groups.num_allocated, 1); assert_eq!(report.bind_groups.num_allocated, 1);
@ -216,7 +216,7 @@ async fn draw_test_with_reports(
drop(bg); drop(bg);
drop(buffer); 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); let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.command_buffers.num_kept_from_user, 1); assert_eq!(report.command_buffers.num_kept_from_user, 1);
assert_eq!(report.render_pipelines.num_kept_from_user, 0); 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 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); let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.command_buffers.num_allocated, 0); assert_eq!(report.command_buffers.num_allocated, 0);
@ -245,7 +245,7 @@ async fn draw_test_with_reports(
.await .await
.panic_on_timeout(); .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); let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.render_pipelines.num_allocated, 0); assert_eq!(report.render_pipelines.num_allocated, 0);
@ -260,7 +260,7 @@ async fn draw_test_with_reports(
drop(ctx.device); drop(ctx.device);
drop(ctx.adapter); 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); let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.queues.num_kept_from_user, 0); assert_eq!(report.queues.num_kept_from_user, 0);

View File

@ -155,7 +155,12 @@ bitflags::bitflags! {
const METAL = 1 << Backend::Metal as u32; const METAL = 1 << Backend::Metal as u32;
/// Supported on Windows 10 /// Supported on Windows 10
const DX12 = 1 << Backend::Dx12 as u32; 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; const BROWSER_WEBGPU = 1 << Backend::BrowserWebGpu as u32;
/// All the apis that wgpu offers first tier of support for. /// All the apis that wgpu offers first tier of support for.
/// ///

View File

@ -24,7 +24,7 @@ targets = [
[lib] [lib]
[features] [features]
default = ["wgsl", "dx12", "metal"] default = ["wgsl", "dx12", "metal", "webgpu"]
#! ### Backends #! ### Backends
# -------------------------------------------------------------------- # --------------------------------------------------------------------
@ -44,10 +44,12 @@ angle = ["wgc?/gles"]
## Enables the Vulkan backend on macOS & iOS. ## Enables the Vulkan backend on macOS & iOS.
vulkan-portability = ["wgc?/vulkan"] vulkan-portability = ["wgc?/vulkan"]
## Enables the WebGPU backend on Wasm. Disabled when targeting `emscripten`.
webgpu = []
## Enables the GLES backend on Wasm ## Enables the GLES backend on Wasm
## ##
## * ⚠️ WIP: Currently will also enable GLES dependencies on any other targets. ## * ⚠️ 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"] webgl = ["hal", "wgc/gles"]
#! ### Shading language support #! ### Shading language support

View File

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

View File

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

View File

@ -69,19 +69,21 @@ unsafe impl<T> Send for Identified<T> {}
#[cfg(send_sync)] #[cfg(send_sync)]
unsafe impl<T> Sync for Identified<T> {} unsafe impl<T> Sync for Identified<T> {}
pub(crate) struct Context(web_sys::Gpu); pub(crate) struct ContextWebGpu(web_sys::Gpu);
#[cfg(send_sync)] #[cfg(send_sync)]
unsafe impl Send for Context {} unsafe impl Send for ContextWebGpu {}
#[cfg(send_sync)] #[cfg(send_sync)]
unsafe impl Sync for Context {} unsafe impl Sync for ContextWebGpu {}
#[cfg(send_sync)] #[cfg(send_sync)]
unsafe impl Send for BufferMappedRange {} unsafe impl Send for BufferMappedRange {}
#[cfg(send_sync)] #[cfg(send_sync)]
unsafe impl Sync for BufferMappedRange {} unsafe impl Sync for BufferMappedRange {}
impl fmt::Debug for Context { impl fmt::Debug for ContextWebGpu {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 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 { 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); let mut mapped = web_sys::GpuImageCopyBuffer::new(&buffer.0.buffer);
if let Some(bytes_per_row) = view.layout.bytes_per_row { if let Some(bytes_per_row) = view.layout.bytes_per_row {
mapped.bytes_per_row(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 { 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()); downcast_ref(view.texture.data.as_ref());
let mut mapped = web_sys::GpuImageCopyTexture::new(&texture.0); let mut mapped = web_sys::GpuImageCopyTexture::new(&texture.0);
mapped.mip_level(view.mip_level); 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( fn map_tagged_texture_copy_view(
view: crate::ImageCopyTextureTagged<'_>, view: crate::ImageCopyTextureTagged<'_>,
) -> web_sys::GpuImageCopyTextureTagged { ) -> web_sys::GpuImageCopyTextureTagged {
let texture: &<Context as crate::Context>::TextureData = let texture: &<ContextWebGpu as crate::Context>::TextureData =
downcast_ref(view.texture.data.as_ref()); downcast_ref(view.texture.data.as_ref());
let mut mapped = web_sys::GpuImageCopyTextureTagged::new(&texture.0); let mut mapped = web_sys::GpuImageCopyTextureTagged::new(&texture.0);
mapped.mip_level(view.mip_level); mapped.mip_level(view.mip_level);
@ -821,7 +824,7 @@ fn future_request_device(
(device_id, device_data, queue_id, queue_data) (device_id, device_data, queue_id, queue_data)
}) })
.map_err(|error_value| crate::RequestDeviceError { .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)); *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. /// Common portion of the internal branches of the public `instance_create_surface` function.
/// ///
/// Note: Analogous code also exists in the WebGL2 backend at /// Note: Analogous code also exists in the WebGL2 backend at
@ -929,6 +932,15 @@ impl Context {
Ok(create_identified((canvas, 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. // Represents the global object in the JavaScript context.
@ -952,7 +964,31 @@ pub enum Canvas {
Offscreen(web_sys::OffscreenCanvas), 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 AdapterId = Identified<web_sys::GpuAdapter>;
type AdapterData = Sendable<web_sys::GpuAdapter>; type AdapterData = Sendable<web_sys::GpuAdapter>;
type DeviceId = Identified<web_sys::GpuDevice>; 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>>; MakeSendFuture<wasm_bindgen_futures::JsFuture, fn(JsFutureResult) -> Option<crate::Error>>;
fn init(_instance_desc: wgt::InstanceDescriptor) -> Self { fn init(_instance_desc: wgt::InstanceDescriptor) -> Self {
let global: Global = js_sys::global().unchecked_into(); let Some(gpu) = get_browser_gpu_property() else {
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 {
panic!( panic!(
"Accessing the GPU is only supported on the main thread or from a dedicated worker" "Accessing the GPU is only supported on the main thread or from a dedicated worker"
); );
}; };
Context(gpu) ContextWebGpu(gpu)
} }
unsafe fn instance_create_surface( unsafe fn instance_create_surface(
@ -1587,7 +1615,7 @@ impl crate::context::Context for Context {
offset, offset,
size, size,
}) => { }) => {
let buffer: &<Context as crate::Context>::BufferData = let buffer: &<ContextWebGpu as crate::Context>::BufferData =
downcast_ref(buffer.data.as_ref()); downcast_ref(buffer.data.as_ref());
let mut mapped_buffer_binding = let mut mapped_buffer_binding =
web_sys::GpuBufferBinding::new(&buffer.0.buffer); 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") panic!("Web backend does not support arrays of buffers")
} }
crate::BindingResource::Sampler(sampler) => { crate::BindingResource::Sampler(sampler) => {
let sampler: &<Context as crate::Context>::SamplerData = let sampler: &<ContextWebGpu as crate::Context>::SamplerData =
downcast_ref(sampler.data.as_ref()); downcast_ref(sampler.data.as_ref());
JsValue::from(&sampler.0) JsValue::from(&sampler.0)
} }
@ -1609,7 +1637,7 @@ impl crate::context::Context for Context {
panic!("Web backend does not support arrays of samplers") panic!("Web backend does not support arrays of samplers")
} }
crate::BindingResource::TextureView(texture_view) => { 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()); downcast_ref(texture_view.data.as_ref());
JsValue::from(&texture_view.0) JsValue::from(&texture_view.0)
} }
@ -1622,7 +1650,7 @@ impl crate::context::Context for Context {
}) })
.collect::<js_sys::Array>(); .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()); downcast_ref(desc.layout.data.as_ref());
let mut mapped_desc = web_sys::GpuBindGroupDescriptor::new(&mapped_entries, &bgl.0); let mut mapped_desc = web_sys::GpuBindGroupDescriptor::new(&mapped_entries, &bgl.0);
if let Some(label) = desc.label { if let Some(label) = desc.label {
@ -1641,7 +1669,7 @@ impl crate::context::Context for Context {
.bind_group_layouts .bind_group_layouts
.iter() .iter()
.map(|bgl| { .map(|bgl| {
let bgl: &<Context as crate::Context>::BindGroupLayoutData = let bgl: &<ContextWebGpu as crate::Context>::BindGroupLayoutData =
downcast_ref(bgl.data.as_ref()); downcast_ref(bgl.data.as_ref());
&bgl.0 &bgl.0
}) })
@ -1659,7 +1687,7 @@ impl crate::context::Context for Context {
device_data: &Self::DeviceData, device_data: &Self::DeviceData,
desc: &crate::RenderPipelineDescriptor<'_>, desc: &crate::RenderPipelineDescriptor<'_>,
) -> (Self::RenderPipelineId, Self::RenderPipelineData) { ) -> (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()); downcast_ref(desc.vertex.module.data.as_ref());
let mut mapped_vertex_state = let mut mapped_vertex_state =
web_sys::GpuVertexState::new(desc.vertex.entry_point, &module.0); 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( let mut mapped_desc = web_sys::GpuRenderPipelineDescriptor::new(
&match desc.layout { &match desc.layout {
Some(layout) => { Some(layout) => {
let layout: &<Context as crate::Context>::PipelineLayoutData = let layout: &<ContextWebGpu as crate::Context>::PipelineLayoutData =
downcast_ref(layout.data.as_ref()); downcast_ref(layout.data.as_ref());
JsValue::from(&layout.0) JsValue::from(&layout.0)
} }
@ -1734,7 +1762,7 @@ impl crate::context::Context for Context {
None => wasm_bindgen::JsValue::null(), None => wasm_bindgen::JsValue::null(),
}) })
.collect::<js_sys::Array>(); .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()); downcast_ref(frag.module.data.as_ref());
let mapped_fragment_desc = let mapped_fragment_desc =
web_sys::GpuFragmentState::new(frag.entry_point, &module.0, &targets); web_sys::GpuFragmentState::new(frag.entry_point, &module.0, &targets);
@ -1759,7 +1787,7 @@ impl crate::context::Context for Context {
device_data: &Self::DeviceData, device_data: &Self::DeviceData,
desc: &crate::ComputePipelineDescriptor<'_>, desc: &crate::ComputePipelineDescriptor<'_>,
) -> (Self::ComputePipelineId, Self::ComputePipelineData) { ) -> (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()); downcast_ref(desc.module.data.as_ref());
let mapped_compute_stage = let mapped_compute_stage =
web_sys::GpuProgrammableStage::new(desc.entry_point, &shader_module.0); 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( let mut mapped_desc = web_sys::GpuComputePipelineDescriptor::new(
&match desc.layout { &match desc.layout {
Some(layout) => { Some(layout) => {
let layout: &<Context as crate::Context>::PipelineLayoutData = let layout: &<ContextWebGpu as crate::Context>::PipelineLayoutData =
downcast_ref(layout.data.as_ref()); downcast_ref(layout.data.as_ref());
JsValue::from(&layout.0) 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) { fn buffer_unmap(&self, _buffer: &Self::BufferId, buffer_data: &Self::BufferData) {
buffer_data.0.buffer.unmap(); buffer_data.0.buffer.unmap();
buffer_data.0.mapping.borrow_mut().mapped_buffer = None; 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, 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()); downcast_ref(ca.view.data.as_ref());
let mut mapped_color_attachment = web_sys::GpuRenderPassColorAttachment::new( 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); mapped_color_attachment.clear_value(&cv);
} }
if let Some(rt) = ca.resolve_target { 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()); downcast_ref(rt.data.as_ref());
mapped_color_attachment.resolve_target(&resolve_target_view.0); 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 { 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()); downcast_ref(dsa.view.data.as_ref());
let mut mapped_depth_stencil_attachment = let mut mapped_depth_stencil_attachment =
web_sys::GpuRenderPassDepthStencilAttachment::new(&depth_stencil_attachment.0); web_sys::GpuRenderPassDepthStencilAttachment::new(&depth_stencil_attachment.0);
@ -2430,7 +2449,8 @@ impl crate::context::Context for Context {
offset: wgt::BufferAddress, offset: wgt::BufferAddress,
size: Option<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 { match size {
Some(size) => encoder_data.0.clear_buffer_with_f64_and_f64( Some(size) => encoder_data.0.clear_buffer_with_f64_and_f64(
&buffer.0.buffer, &buffer.0.buffer,

View File

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

View File

@ -320,13 +320,6 @@ pub trait Context: Debug + WasmNotSendSync + Sized {
buffer_data: &Self::BufferData, buffer_data: &Self::BufferData,
sub_range: Range<BufferAddress>, sub_range: Range<BufferAddress>,
) -> Box<dyn BufferMappedRange>; ) -> 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 buffer_unmap(&self, buffer: &Self::BufferId, buffer_data: &Self::BufferData);
fn texture_create_view( fn texture_create_view(
&self, &self,
@ -1037,6 +1030,7 @@ impl ObjectId {
global_id: None, global_id: None,
}; };
#[allow(dead_code)]
pub fn new(id: NonZeroU64, global_id: NonZeroU64) -> Self { pub fn new(id: NonZeroU64, global_id: NonZeroU64) -> Self {
Self { Self {
id: Some(id), id: Some(id),
@ -1346,13 +1340,6 @@ pub(crate) trait DynContext: Debug + WasmNotSendSync {
buffer_data: &crate::Data, buffer_data: &crate::Data,
sub_range: Range<BufferAddress>, sub_range: Range<BufferAddress>,
) -> Box<dyn BufferMappedRange>; ) -> 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 buffer_unmap(&self, buffer: &ObjectId, buffer_data: &crate::Data);
fn texture_create_view( fn texture_create_view(
&self, &self,
@ -2465,18 +2452,6 @@ where
Context::buffer_get_mapped_range(self, &buffer, buffer_data, sub_range) 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) { fn buffer_unmap(&self, buffer: &ObjectId, buffer_data: &crate::Data) {
let buffer = <T::BufferId>::from(*buffer); let buffer = <T::BufferId>::from(*buffer);
let buffer_data = downcast_ref(buffer_data); 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 //! - **`angle`** --- Enables the GLES backend via [ANGLE](https://github.com/google/angle) on macOS
//! using. //! using.
//! - **`vulkan-portability`** --- Enables the Vulkan backend on macOS & iOS. //! - **`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 //! - **`webgl`** --- Enables the GLES backend on Wasm
//! //!
//! - ⚠️ WIP: Currently will also enable GLES dependencies on any other targets. //! - ⚠️ 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 //! ### Shading language support
//! //!
@ -73,7 +72,10 @@ use std::{
thread, 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 parking_lot::Mutex;
use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
@ -1722,8 +1724,6 @@ impl Instance {
/// See <https://github.com/gfx-rs/wgpu/issues/3514> /// See <https://github.com/gfx-rs/wgpu/issues/3514>
/// * Windows: always enables Vulkan and GLES with no way to opt out /// * Windows: always enables Vulkan and GLES with no way to opt out
/// * Linux: 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 { pub const fn any_backend_feature_enabled() -> bool {
// Method intentionally kept verbose to keep it a bit easier to follow! // Method intentionally kept verbose to keep it a bit easier to follow!
@ -1733,6 +1733,9 @@ impl Instance {
cfg!(feature = "metal") cfg!(feature = "metal")
|| cfg!(feature = "vulkan-portability") || cfg!(feature = "vulkan-portability")
|| cfg!(feature = "angle") || 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 { } else {
true true
} }
@ -1745,11 +1748,21 @@ impl Instance {
/// - `instance_desc` - Has fields for which [backends][Backends] wgpu will choose /// - `instance_desc` - Has fields for which [backends][Backends] wgpu will choose
/// during instantiation, and which [DX12 shader compiler][Dx12Compiler] wgpu will use. /// 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 /// # Panics
/// ///
/// If no backend feature for the active target platform is enabled, /// If no backend feature for the active target platform is enabled,
/// this method will panic, see [`Instance::any_backend_feature_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() { if !Self::any_backend_feature_enabled() {
panic!( panic!(
"No wgpu backend feature that is implemented for the target platform was enabled. \ "No wgpu backend feature that is implemented for the target platform was enabled. \
@ -1757,9 +1770,25 @@ impl Instance {
); );
} }
Self { #[cfg(webgpu)]
context: Arc::from(crate::backend::Context::init(instance_desc)), 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. /// Create an new instance of wgpu from a wgpu-hal instance.
@ -1771,11 +1800,11 @@ impl Instance {
/// # Safety /// # Safety
/// ///
/// Refer to the creation of wgpu-hal Instance for every backend. /// 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 { pub unsafe fn from_hal<A: wgc::hal_api::HalApi>(hal_instance: A::Instance) -> Self {
Self { Self {
context: Arc::new(unsafe { 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. /// - The raw instance handle returned must not be manually destroyed.
/// ///
/// [`Instance`]: hal::Api::Instance /// [`Instance`]: hal::Api::Instance
#[cfg(not(webgpu))] #[cfg(wgpu_core)]
pub unsafe fn as_hal<A: wgc::hal_api::HalApi>(&self) -> Option<&A::Instance> { pub unsafe fn as_hal<A: wgc::hal_api::HalApi>(&self) -> Option<&A::Instance> {
unsafe { self.context
self.context .as_any()
.as_any() // If we don't have a wgpu-core instance, we don't have a hal instance either.
.downcast_ref::<crate::backend::Context>() .downcast_ref::<crate::backend::ContextWgpuCore>()
.unwrap() .and_then(|ctx| unsafe { ctx.instance_as_hal::<A>() })
.instance_as_hal::<A>()
}
} }
/// Create an new instance of wgpu from a wgpu-core instance. /// Create an new instance of wgpu from a wgpu-core instance.
@ -1810,34 +1837,40 @@ impl Instance {
/// # Safety /// # Safety
/// ///
/// Refer to the creation of wgpu-core Instance. /// 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 { pub unsafe fn from_core(core_instance: wgc::instance::Instance) -> Self {
Self { Self {
context: Arc::new(unsafe { 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`]. /// 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 /// # Arguments
/// ///
/// - `backends` - Backends from which to enumerate adapters. /// - `backends` - Backends from which to enumerate adapters.
#[cfg(not(webgpu))] #[cfg(wgpu_core)]
pub fn enumerate_adapters(&self, backends: Backends) -> impl ExactSizeIterator<Item = Adapter> { pub fn enumerate_adapters(&self, backends: Backends) -> Vec<Adapter> {
let context = Arc::clone(&self.context); let context = Arc::clone(&self.context);
self.context self.context
.as_any() .as_any()
.downcast_ref::<crate::backend::Context>() .downcast_ref::<crate::backend::ContextWgpuCore>()
.unwrap() .map(|ctx| {
.enumerate_adapters(backends) ctx.enumerate_adapters(backends)
.into_iter() .into_iter()
.map(move |id| crate::Adapter { .map(move |id| crate::Adapter {
context: Arc::clone(&context), context: Arc::clone(&context),
id: ObjectId::from(id), id: ObjectId::from(id),
data: Box::new(()), data: Box::new(()),
})
.collect()
}) })
.unwrap_or_default()
} }
/// Retrieves an [`Adapter`] which matches the given [`RequestAdapterOptions`]. /// Retrieves an [`Adapter`] which matches the given [`RequestAdapterOptions`].
@ -1863,7 +1896,7 @@ impl Instance {
/// # Safety /// # Safety
/// ///
/// `hal_adapter` must be created from this instance internal handle. /// `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>( pub unsafe fn create_adapter_from_hal<A: wgc::hal_api::HalApi>(
&self, &self,
hal_adapter: hal::ExposedAdapter<A>, hal_adapter: hal::ExposedAdapter<A>,
@ -1872,7 +1905,7 @@ impl Instance {
let id = unsafe { let id = unsafe {
context context
.as_any() .as_any()
.downcast_ref::<crate::backend::Context>() .downcast_ref::<crate::backend::ContextWgpuCore>()
.unwrap() .unwrap()
.create_adapter_from_hal(hal_adapter) .create_adapter_from_hal(hal_adapter)
.into() .into()
@ -2000,13 +2033,15 @@ impl Instance {
} }
/// Generates memory report. /// 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 self.context
.as_any() .as_any()
.downcast_ref::<crate::backend::Context>() .downcast_ref::<crate::backend::ContextWgpuCore>()
.unwrap() .map(|ctx| ctx.generate_report())
.generate_report()
} }
} }
@ -2071,7 +2106,7 @@ impl Adapter {
/// ///
/// - `hal_device` must be created from this adapter internal handle. /// - `hal_device` must be created from this adapter internal handle.
/// - `desc.features` must be a subset of `hal_device` features. /// - `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>( pub unsafe fn create_device_from_hal<A: wgc::hal_api::HalApi>(
&self, &self,
hal_device: hal::OpenDevice<A>, hal_device: hal::OpenDevice<A>,
@ -2082,7 +2117,9 @@ impl Adapter {
unsafe { unsafe {
self.context self.context
.as_any() .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() .unwrap()
.create_device_from_hal(&self.id.into(), hal_device, desc, trace_path) .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. /// - The raw handle passed to the callback must not be manually destroyed.
/// ///
/// [`A::Adapter`]: hal::Api::Adapter /// [`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>( pub unsafe fn as_hal<A: wgc::hal_api::HalApi, F: FnOnce(Option<&A::Adapter>) -> R, R>(
&self, &self,
hal_adapter_callback: F, hal_adapter_callback: F,
) -> R { ) -> R {
unsafe { if let Some(ctx) = self
self.context .context
.as_any() .as_any()
.downcast_ref::<crate::backend::Context>() .downcast_ref::<crate::backend::ContextWgpuCore>()
.unwrap() {
.adapter_as_hal::<A, F, R>(self.id.into(), hal_adapter_callback) 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 from this device internal handle
/// - `hal_texture` must be created respecting `desc` /// - `hal_texture` must be created respecting `desc`
/// - `hal_texture` must be initialized /// - `hal_texture` must be initialized
#[cfg(not(webgpu))] #[cfg(wgpu_core)]
pub unsafe fn create_texture_from_hal<A: wgc::hal_api::HalApi>( pub unsafe fn create_texture_from_hal<A: wgc::hal_api::HalApi>(
&self, &self,
hal_texture: A::Texture, hal_texture: A::Texture,
@ -2472,7 +2511,9 @@ impl Device {
let texture = unsafe { let texture = unsafe {
self.context self.context
.as_any() .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() .unwrap()
.create_texture_from_hal::<A>( .create_texture_from_hal::<A>(
hal_texture, hal_texture,
@ -2500,7 +2541,7 @@ impl Device {
/// - `hal_buffer` must be created from this device internal handle /// - `hal_buffer` must be created from this device internal handle
/// - `hal_buffer` must be created respecting `desc` /// - `hal_buffer` must be created respecting `desc`
/// - `hal_buffer` must be initialized /// - `hal_buffer` must be initialized
#[cfg(not(webgpu))] #[cfg(wgpu_core)]
pub unsafe fn create_buffer_from_hal<A: wgc::hal_api::HalApi>( pub unsafe fn create_buffer_from_hal<A: wgc::hal_api::HalApi>(
&self, &self,
hal_buffer: A::Buffer, hal_buffer: A::Buffer,
@ -2514,7 +2555,9 @@ impl Device {
let (id, buffer) = unsafe { let (id, buffer) = unsafe {
self.context self.context
.as_any() .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() .unwrap()
.create_buffer_from_hal::<A>( .create_buffer_from_hal::<A>(
hal_buffer, hal_buffer,
@ -2604,21 +2647,20 @@ impl Device {
/// - The raw handle passed to the callback must not be manually destroyed. /// - The raw handle passed to the callback must not be manually destroyed.
/// ///
/// [`A::Device`]: hal::Api::Device /// [`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>( pub unsafe fn as_hal<A: wgc::hal_api::HalApi, F: FnOnce(Option<&A::Device>) -> R, R>(
&self, &self,
hal_device_callback: F, hal_device_callback: F,
) -> R { ) -> Option<R> {
unsafe { self.context
self.context .as_any()
.as_any() .downcast_ref::<crate::backend::ContextWgpuCore>()
.downcast_ref::<crate::backend::Context>() .map(|ctx| unsafe {
.unwrap() ctx.device_as_hal::<A, F, R>(
.device_as_hal::<A, F, R>(
self.data.as_ref().downcast_ref().unwrap(), self.data.as_ref().downcast_ref().unwrap(),
hal_device_callback, hal_device_callback,
) )
} })
} }
/// Destroy this device. /// Destroy this device.
@ -2657,14 +2699,14 @@ pub struct RequestDeviceError {
enum RequestDeviceErrorKind { enum RequestDeviceErrorKind {
/// Error from [`wgpu_core`]. /// Error from [`wgpu_core`].
// must match dependency cfg // must match dependency cfg
#[cfg(not(webgpu))] #[cfg(wgpu_core)]
Core(core::instance::RequestDeviceError), Core(wgc::instance::RequestDeviceError),
/// Error from web API that was called by `wgpu` to request a device. /// 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.) /// (This is currently never used by the webgl backend, but it could be.)
#[cfg(webgpu)] #[cfg(webgpu)]
Web(wasm_bindgen::JsValue), WebGpu(wasm_bindgen::JsValue),
} }
#[cfg(send_sync)] #[cfg(send_sync)]
@ -2676,15 +2718,17 @@ unsafe impl Sync for RequestDeviceErrorKind {}
static_assertions::assert_impl_all!(RequestDeviceError: Send, Sync); static_assertions::assert_impl_all!(RequestDeviceError: Send, Sync);
impl fmt::Display for RequestDeviceError { 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 { match &self.inner {
#[cfg(not(webgpu))] #[cfg(wgpu_core)]
RequestDeviceErrorKind::Core(error) => error.fmt(f), RequestDeviceErrorKind::Core(error) => error.fmt(_f),
#[cfg(webgpu)] #[cfg(webgpu)]
RequestDeviceErrorKind::Web(error_js_value) => { RequestDeviceErrorKind::WebGpu(error_js_value) => {
// wasm-bindgen provides a reasonable error stringification via `Debug` impl // 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 { impl error::Error for RequestDeviceError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> { fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match &self.inner { match &self.inner {
#[cfg(not(webgpu))] #[cfg(wgpu_core)]
RequestDeviceErrorKind::Core(error) => error.source(), RequestDeviceErrorKind::Core(error) => error.source(),
#[cfg(webgpu)] #[cfg(webgpu)]
RequestDeviceErrorKind::Web(_) => None, RequestDeviceErrorKind::WebGpu(_) => None,
#[cfg(not(any(webgpu, wgpu_core)))]
_ => unimplemented!("unknown `RequestDeviceErrorKind`"),
} }
} }
} }
#[cfg(not(webgpu))] #[cfg(wgpu_core)]
impl From<core::instance::RequestDeviceError> for RequestDeviceError { impl From<wgc::instance::RequestDeviceError> for RequestDeviceError {
fn from(error: core::instance::RequestDeviceError) -> Self { fn from(error: wgc::instance::RequestDeviceError) -> Self {
Self { Self {
inner: RequestDeviceErrorKind::Core(error), inner: RequestDeviceErrorKind::Core(error),
} }
@ -2718,7 +2764,7 @@ pub struct CreateSurfaceError {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum CreateSurfaceErrorKind { enum CreateSurfaceErrorKind {
/// Error from [`wgpu_hal`]. /// Error from [`wgpu_hal`].
#[cfg(not(webgpu))] #[cfg(wgpu_core)]
Hal(hal::InstanceError), Hal(hal::InstanceError),
/// Error from WebGPU surface creation. /// Error from WebGPU surface creation.
@ -2734,7 +2780,7 @@ static_assertions::assert_impl_all!(CreateSurfaceError: Send, Sync);
impl fmt::Display for CreateSurfaceError { impl fmt::Display for CreateSurfaceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.inner { match &self.inner {
#[cfg(not(webgpu))] #[cfg(wgpu_core)]
CreateSurfaceErrorKind::Hal(e) => e.fmt(f), CreateSurfaceErrorKind::Hal(e) => e.fmt(f),
CreateSurfaceErrorKind::Web(e) => e.fmt(f), CreateSurfaceErrorKind::Web(e) => e.fmt(f),
CreateSurfaceErrorKind::RawHandle(e) => e.fmt(f), CreateSurfaceErrorKind::RawHandle(e) => e.fmt(f),
@ -2745,7 +2791,7 @@ impl fmt::Display for CreateSurfaceError {
impl error::Error for CreateSurfaceError { impl error::Error for CreateSurfaceError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> { fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match &self.inner { match &self.inner {
#[cfg(not(webgpu))] #[cfg(wgpu_core)]
CreateSurfaceErrorKind::Hal(e) => e.source(), CreateSurfaceErrorKind::Hal(e) => e.source(),
CreateSurfaceErrorKind::Web(_) => None, CreateSurfaceErrorKind::Web(_) => None,
CreateSurfaceErrorKind::RawHandle(e) => e.source(), 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 { impl From<hal::InstanceError> for CreateSurfaceError {
fn from(e: hal::InstanceError) -> Self { fn from(e: hal::InstanceError) -> Self {
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 /// 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` /// This is useful when targeting WebGPU and you want to pass mapped data directly to js.
/// which unconditionally copies mapped data into the wasm heap, this function directly hands you the /// Unlike `get_mapped_range` which unconditionally copies mapped data into the wasm heap,
/// ArrayBuffer that we mapped the data into in js. /// 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)] #[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> {
let end = self.buffer.map_context.lock().add(self.offset, self.size); self.buffer
DynContext::buffer_get_mapped_range_as_array_buffer( .context
&*self.buffer.context, .as_any()
&self.buffer.id, .downcast_ref::<crate::backend::ContextWebGpu>()
self.buffer.data.as_ref(), .map(|ctx| {
self.offset..end, let buffer_data = crate::context::downcast_ref(self.buffer.data.as_ref());
) let end = self.buffer.map_context.lock().add(self.offset, self.size);
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 /// Synchronously and immediately map a buffer for writing. If the buffer is not immediately mappable
@ -3035,18 +3085,21 @@ impl Texture {
/// # Safety /// # Safety
/// ///
/// - The raw handle obtained from the hal Texture must not be manually destroyed /// - 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>)>( pub unsafe fn as_hal<A: wgc::hal_api::HalApi, F: FnOnce(Option<&A::Texture>)>(
&self, &self,
hal_texture_callback: F, hal_texture_callback: F,
) { ) {
let texture = self.data.as_ref().downcast_ref().unwrap(); let texture = self.data.as_ref().downcast_ref().unwrap();
unsafe {
self.context if let Some(ctx) = self
.as_any() .context
.downcast_ref::<crate::backend::Context>() .as_any()
.unwrap() .downcast_ref::<crate::backend::ContextWgpuCore>()
.texture_as_hal::<A, F>(texture, hal_texture_callback) {
unsafe { ctx.texture_as_hal::<A, F>(texture, hal_texture_callback) }
} else {
hal_texture_callback(None)
} }
} }
@ -4783,18 +4836,20 @@ impl Surface<'_> {
/// # Safety /// # Safety
/// ///
/// - The raw handle obtained from the hal Surface must not be manually destroyed /// - 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>( pub unsafe fn as_hal<A: wgc::hal_api::HalApi, F: FnOnce(Option<&A::Surface>) -> R, R>(
&mut self, &mut self,
hal_surface_callback: F, hal_surface_callback: F,
) -> R { ) -> Option<R> {
unsafe { self.context
self.context .as_any()
.as_any() .downcast_ref::<crate::backend::ContextWgpuCore>()
.downcast_ref::<crate::backend::Context>() .map(|ctx| unsafe {
.unwrap() ctx.surface_as_hal::<A, F, R>(
.surface_as_hal::<A, F, R>(self.data.downcast_ref().unwrap(), hal_surface_callback) 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!( xshell::cmd!(
shell, 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) .args(&cargo_args)
.quiet() .quiet()