Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add utils for WebGPU support detection & Instance creation #6371

Draft
wants to merge 2 commits into
base: trunk
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216).
#### General

- Add `VideoFrame` to `ExternalImageSource` enum. By @jprochazk in [#6170](https://github.com/gfx-rs/wgpu/pull/6170)
- Add `wgpu::util::new_instance_with_webgpu_detection` & `wgpu::util::is_browser_webgpu_supported` to make it easier to support WebGPU & WebGL in the same binary. By @wumpf in [#6371](https://github.com/gfx-rs/wgpu/pull/6371)

#### Vulkan

Expand Down
13 changes: 9 additions & 4 deletions wgpu/src/api/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,15 @@ impl Instance {
/// 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 don't add the [`Backends::BROWSER_WEBGPU`]
/// flag to the the `instance_desc`'s `backends` field.
/// If it is set and a [`navigator.gpu`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/gpu)
/// object is present, this instance will *only* be able to create WebGPU adapters.
///
/// ⚠️ On some browsers this check is insufficient to determine whether WebGPU is supported,
/// as the browser may define the `navigator.gpu` object, but be unable to create any WebGPU adapters.
/// For targeting _both_ WebGPU & WebGL is recommended to use [`crate::util::new_instance_with_webgpu_detection`].
///
/// If you instead want to force use of WebGL, either disable the `webgpu` compile-time feature
/// or don't 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.
Expand Down
8 changes: 6 additions & 2 deletions wgpu/src/backend/webgpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1088,8 +1088,12 @@ pub struct BrowserGpuPropertyInaccessible;
/// Returns the browser's gpu object or `Err(BrowserGpuPropertyInaccessible)` if
/// the current context is neither the main thread nor a dedicated worker.
///
/// If WebGPU is not supported, the Gpu property is `undefined`, and so this
/// function will return `Ok(None)`.
/// If WebGPU is not supported, the Gpu property may (!) be `undefined`,
/// and so this function will return `Ok(None)` function will return `Ok(None)`.
/// Note that this check is insufficient to determine whether WebGPU is
/// supported, as the browser may define the Gpu property, but be unable to
/// create any WebGPU adapters.
/// To detect whether WebGPU is supported, use the [`crate::utils::is_webgpu_supported`] function.
///
/// See:
/// * <https://developer.mozilla.org/en-US/docs/Web/API/Navigator/gpu>
Expand Down
56 changes: 56 additions & 0 deletions wgpu/src/util/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,59 @@ pub fn gles_minor_version_from_env() -> Option<wgt::Gles3MinorVersion> {
},
)
}

/// Determines whether the [`Backends::BROWSER_WEBGPU`] backend is supported.
///
/// The result can only be true if this is called from the main thread or a dedicated worker.
/// For convenience, this is also supported on non-wasm targets, always returning false there.
pub async fn is_browser_webgpu_supported() -> bool {
#[cfg(webgpu)]
{
// In theory it should be enough to check for the presence of the `gpu` property...
let gpu = crate::backend::get_browser_gpu_property();
let Ok(Some(gpu)) = gpu else {
return false;
};

// ...but in practice, we also have to try to create an adapter, since as of writing
// Chrome on Linux has the `gpu` property but doesn't support WebGPU.
let adapter_promise = gpu.request_adapter();
wasm_bindgen_futures::JsFuture::from(adapter_promise)
.await
.is_ok()
}
#[cfg(not(webgpu))]
{
false
}
}

/// Create an new instance of wgpu, but disabling [`Backends::BROWSER_WEBGPU`] if no WebGPU support was detected.
///
/// If the instance descriptor enables [`Backends::BROWSER_WEBGPU`],
/// this checks via [`is_browser_webgpu_supported`] for WebGPU support before forwarding
/// the descriptor with or without [`Backends::BROWSER_WEBGPU`] respecitively to [`Instance::new`].
///
/// You should prefer this method over [`Instance::new`] if you want to target WebGPU and automatically
/// fall back to WebGL if WebGPU is not available.
/// This is because WebGPU support has to be decided upon instance creation and [`Instance::new`]
/// (being a `sync` function) can't establish WebGPU support (details see [`is_browser_webgpu_supported`]).
///
/// # Panics
///
/// If no backend feature for the active target platform is enabled,
/// this method will panic, see [`Instance::enabled_backend_features()`].
#[allow(unused_mut)]
pub async fn new_instance_with_webgpu_detection(
mut instance_desc: wgt::InstanceDescriptor,
) -> crate::Instance {
if instance_desc
.backends
.contains(wgt::Backends::BROWSER_WEBGPU)
&& !is_browser_webgpu_supported().await
{
instance_desc.backends.remove(wgt::Backends::BROWSER_WEBGPU);
}

crate::Instance::new(instance_desc)
}