Add utils for WebGPU support detection & Instance creation (#6371)

* Utils for WebGPU support detection & Instance creation
Clarifies the docs on `wgpu::Instance` accordingly
* changelog entry
* fix adapter check

---------

Co-authored-by: Teodor Tanasoaia <28601907+teoxoy@users.noreply.github.com>
This commit is contained in:
Andreas Reich 2024-10-15 13:15:37 +02:00 committed by GitHub
parent fe7fbd4a4a
commit 59f56e0263
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 74 additions and 6 deletions

View File

@ -89,6 +89,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

View File

@ -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.

View File

@ -1087,8 +1087,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)`.
/// 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_browser_webgpu_supported`] function.
///
/// See:
/// * <https://developer.mozilla.org/en-US/docs/Web/API/Navigator/gpu>

View File

@ -135,3 +135,61 @@ 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
.map_or(false, |adapter| {
!adapter.is_undefined() && !adapter.is_null()
})
}
#[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)
}