Skip to content

Commit

Permalink
automatic webgpu support detection
Browse files Browse the repository at this point in the history
  • Loading branch information
Wumpf committed Jan 11, 2024
1 parent c24a09e commit d2a139e
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 14 deletions.
12 changes: 11 additions & 1 deletion tests/src/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
7 changes: 6 additions & 1 deletion wgpu-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down
2 changes: 1 addition & 1 deletion wgpu/src/backend/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#[cfg(webgpu)]
mod webgpu;
#[cfg(webgpu)]
pub(crate) use webgpu::ContextWebGpu;
pub(crate) use webgpu::{get_browser_gpu_property, ContextWebGpu};

#[cfg(wgpu_core)]
mod wgpu_core;
Expand Down
34 changes: 25 additions & 9 deletions wgpu/src/backend/webgpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -955,6 +955,30 @@ pub enum Canvas {
Offscreen(web_sys::OffscreenCanvas),
}

/// 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>;
Expand Down Expand Up @@ -1025,15 +1049,7 @@ impl crate::context::Context for ContextWebGpu {
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"
);
Expand Down
13 changes: 11 additions & 2 deletions wgpu/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1742,6 +1742,15 @@ 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,
Expand All @@ -1756,9 +1765,9 @@ impl Instance {
}

#[cfg(webgpu)]
if instance_desc.backends.contains(Backends::BROWSER_WEBGPU)
&& crate::backend::get_browser_gpu_property().map_or(false, |gpu| !gpu.is_undefined())
{
// TODO: If both webgpu and webgl are enabled, check if browser supports WebGPU and fall through
// to wgpucore otherwise.
return Self {
context: Arc::from(crate::backend::ContextWebGpu::init(instance_desc)),
};
Expand Down

0 comments on commit d2a139e

Please sign in to comment.