mirror of
https://github.com/gfx-rs/wgpu.git
synced 2024-11-25 00:03:29 +00:00
Properly handle the case where Navigator.gpu is undefined and WebGPU is the only compiled backend (#6197)
* Properly handle the case where `Navigator.gpu` is undefined and WebGPU is the only compiled backend. Previously, `Instance::request_adapter` would invoke a wasm binding with an undefined arg0, thus crashing the program. Now it will cleanly return `None` instead. Fixes #6196. * Fix typo in `Instance::new` doc comment. * Add note to CHANGELOG.md * Introduce `DefinedNonNullJsValue` type. * Assert definedness of self.gpu in surface_get_capabilities. * Use DefinedNonNullJsValue in signature of get_browser_gpu_property(). * Clarify meaning of gpu field with a comment.
This commit is contained in:
parent
d79ebc4db3
commit
2fac5e983e
@ -88,6 +88,7 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216).
|
|||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
- Fix incorrect hlsl image output type conversion. By @atlv24 in [#6123](https://github.com/gfx-rs/wgpu/pull/6123)
|
- Fix incorrect hlsl image output type conversion. By @atlv24 in [#6123](https://github.com/gfx-rs/wgpu/pull/6123)
|
||||||
|
- Fix JS `TypeError` exception in `Instance::request_adapter` when browser doesn't support WebGPU but `wgpu` not compiled with `webgl` support. By @bgr360 in [#6197](https://github.com/gfx-rs/wgpu/pull/6197).
|
||||||
|
|
||||||
#### Naga
|
#### Naga
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ impl Instance {
|
|||||||
/// [`Backends::BROWSER_WEBGPU`] takes a special role:
|
/// [`Backends::BROWSER_WEBGPU`] takes a special role:
|
||||||
/// If it is set and WebGPU support is detected, this instance will *only* be able to create
|
/// 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
|
/// 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`]
|
/// disable the `webgpu` compile-time feature or don't add the [`Backends::BROWSER_WEBGPU`]
|
||||||
/// flag to the the `instance_desc`'s `backends` field.
|
/// 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
|
/// 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
|
/// to create adapters. Meaning that if the `webgl` feature is enabled, it is able to create
|
||||||
@ -118,8 +118,9 @@ impl Instance {
|
|||||||
{
|
{
|
||||||
let is_only_available_backend = !cfg!(wgpu_core);
|
let is_only_available_backend = !cfg!(wgpu_core);
|
||||||
let requested_webgpu = _instance_desc.backends.contains(Backends::BROWSER_WEBGPU);
|
let requested_webgpu = _instance_desc.backends.contains(Backends::BROWSER_WEBGPU);
|
||||||
let support_webgpu =
|
let support_webgpu = crate::backend::get_browser_gpu_property()
|
||||||
crate::backend::get_browser_gpu_property().map_or(false, |gpu| !gpu.is_undefined());
|
.map(|maybe_gpu| maybe_gpu.is_some())
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
if is_only_available_backend || (requested_webgpu && support_webgpu) {
|
if is_only_available_backend || (requested_webgpu && support_webgpu) {
|
||||||
return Self {
|
return Self {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
|
|
||||||
|
mod defined_non_null_js_value;
|
||||||
mod ext_bindings;
|
mod ext_bindings;
|
||||||
mod webgpu_sys;
|
mod webgpu_sys;
|
||||||
|
|
||||||
@ -22,6 +23,8 @@ use crate::{
|
|||||||
CompilationInfo, SurfaceTargetUnsafe, UncapturedErrorHandler,
|
CompilationInfo, SurfaceTargetUnsafe, UncapturedErrorHandler,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use defined_non_null_js_value::DefinedNonNullJsValue;
|
||||||
|
|
||||||
// We need to make a wrapper for some of the handle types returned by the web backend to make them
|
// We need to make a wrapper for some of the handle types returned by the web backend to make them
|
||||||
// implement `Send` and `Sync` to match native.
|
// implement `Send` and `Sync` to match native.
|
||||||
//
|
//
|
||||||
@ -38,7 +41,10 @@ unsafe impl<T> Send for Sendable<T> {}
|
|||||||
#[cfg(send_sync)]
|
#[cfg(send_sync)]
|
||||||
unsafe impl<T> Sync for Sendable<T> {}
|
unsafe impl<T> Sync for Sendable<T> {}
|
||||||
|
|
||||||
pub(crate) struct ContextWebGpu(webgpu_sys::Gpu);
|
pub(crate) struct ContextWebGpu {
|
||||||
|
/// `None` if browser does not advertise support for WebGPU.
|
||||||
|
gpu: Option<DefinedNonNullJsValue<webgpu_sys::Gpu>>,
|
||||||
|
}
|
||||||
#[cfg(send_sync)]
|
#[cfg(send_sync)]
|
||||||
unsafe impl Send for ContextWebGpu {}
|
unsafe impl Send for ContextWebGpu {}
|
||||||
#[cfg(send_sync)]
|
#[cfg(send_sync)]
|
||||||
@ -189,6 +195,36 @@ impl<F, M> MakeSendFuture<F, M> {
|
|||||||
#[cfg(send_sync)]
|
#[cfg(send_sync)]
|
||||||
unsafe impl<F, M> Send for MakeSendFuture<F, M> {}
|
unsafe impl<F, M> Send for MakeSendFuture<F, M> {}
|
||||||
|
|
||||||
|
/// Wraps a future that returns `Option<T>` and adds the ability to immediately
|
||||||
|
/// return None.
|
||||||
|
pub(crate) struct OptionFuture<F>(Option<F>);
|
||||||
|
|
||||||
|
impl<F: Future<Output = Option<T>>, T> Future for OptionFuture<F> {
|
||||||
|
type Output = Option<T>;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
|
||||||
|
// This is safe because we have no Drop implementation to violate the Pin requirements and
|
||||||
|
// do not provide any means of moving the inner future.
|
||||||
|
unsafe {
|
||||||
|
let this = self.get_unchecked_mut();
|
||||||
|
match &mut this.0 {
|
||||||
|
Some(future) => Pin::new_unchecked(future).poll(cx),
|
||||||
|
None => task::Poll::Ready(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F> OptionFuture<F> {
|
||||||
|
fn some(future: F) -> Self {
|
||||||
|
Self(Some(future))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn none() -> Self {
|
||||||
|
Self(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn map_texture_format(texture_format: wgt::TextureFormat) -> webgpu_sys::GpuTextureFormat {
|
fn map_texture_format(texture_format: wgt::TextureFormat) -> webgpu_sys::GpuTextureFormat {
|
||||||
use webgpu_sys::GpuTextureFormat as tf;
|
use webgpu_sys::GpuTextureFormat as tf;
|
||||||
use wgt::TextureFormat;
|
use wgt::TextureFormat;
|
||||||
@ -1046,27 +1082,34 @@ pub enum Canvas {
|
|||||||
Offscreen(web_sys::OffscreenCanvas),
|
Offscreen(web_sys::OffscreenCanvas),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the browsers gpu object or `None` if the current context is neither the main thread nor a dedicated worker.
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
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` (but *not* necessarily `None`).
|
/// If WebGPU is not supported, the Gpu property is `undefined`, and so this
|
||||||
|
/// function will return `Ok(None)`.
|
||||||
///
|
///
|
||||||
/// See:
|
/// See:
|
||||||
/// * <https://developer.mozilla.org/en-US/docs/Web/API/Navigator/gpu>
|
/// * <https://developer.mozilla.org/en-US/docs/Web/API/Navigator/gpu>
|
||||||
/// * <https://developer.mozilla.org/en-US/docs/Web/API/WorkerNavigator/gpu>
|
/// * <https://developer.mozilla.org/en-US/docs/Web/API/WorkerNavigator/gpu>
|
||||||
pub fn get_browser_gpu_property() -> Option<webgpu_sys::Gpu> {
|
pub fn get_browser_gpu_property(
|
||||||
|
) -> Result<Option<DefinedNonNullJsValue<webgpu_sys::Gpu>>, BrowserGpuPropertyInaccessible> {
|
||||||
let global: Global = js_sys::global().unchecked_into();
|
let global: Global = js_sys::global().unchecked_into();
|
||||||
|
|
||||||
if !global.window().is_undefined() {
|
let maybe_undefined_gpu: webgpu_sys::Gpu = if !global.window().is_undefined() {
|
||||||
let navigator = global.unchecked_into::<web_sys::Window>().navigator();
|
let navigator = global.unchecked_into::<web_sys::Window>().navigator();
|
||||||
Some(ext_bindings::NavigatorGpu::gpu(&navigator))
|
ext_bindings::NavigatorGpu::gpu(&navigator)
|
||||||
} else if !global.worker().is_undefined() {
|
} else if !global.worker().is_undefined() {
|
||||||
let navigator = global
|
let navigator = global
|
||||||
.unchecked_into::<web_sys::WorkerGlobalScope>()
|
.unchecked_into::<web_sys::WorkerGlobalScope>()
|
||||||
.navigator();
|
.navigator();
|
||||||
Some(ext_bindings::NavigatorGpu::gpu(&navigator))
|
ext_bindings::NavigatorGpu::gpu(&navigator)
|
||||||
} else {
|
} else {
|
||||||
None
|
return Err(BrowserGpuPropertyInaccessible);
|
||||||
}
|
};
|
||||||
|
Ok(DefinedNonNullJsValue::new(maybe_undefined_gpu))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::context::Context for ContextWebGpu {
|
impl crate::context::Context for ContextWebGpu {
|
||||||
@ -1096,9 +1139,11 @@ impl crate::context::Context for ContextWebGpu {
|
|||||||
type SubmissionIndexData = ();
|
type SubmissionIndexData = ();
|
||||||
type PipelineCacheData = ();
|
type PipelineCacheData = ();
|
||||||
|
|
||||||
type RequestAdapterFuture = MakeSendFuture<
|
type RequestAdapterFuture = OptionFuture<
|
||||||
wasm_bindgen_futures::JsFuture,
|
MakeSendFuture<
|
||||||
fn(JsFutureResult) -> Option<Self::AdapterData>,
|
wasm_bindgen_futures::JsFuture,
|
||||||
|
fn(JsFutureResult) -> Option<Self::AdapterData>,
|
||||||
|
>,
|
||||||
>;
|
>;
|
||||||
type RequestDeviceFuture = MakeSendFuture<
|
type RequestDeviceFuture = MakeSendFuture<
|
||||||
wasm_bindgen_futures::JsFuture,
|
wasm_bindgen_futures::JsFuture,
|
||||||
@ -1115,12 +1160,13 @@ impl crate::context::Context for ContextWebGpu {
|
|||||||
>;
|
>;
|
||||||
|
|
||||||
fn init(_instance_desc: wgt::InstanceDescriptor) -> Self {
|
fn init(_instance_desc: wgt::InstanceDescriptor) -> Self {
|
||||||
let Some(gpu) = get_browser_gpu_property() else {
|
let Ok(gpu) = get_browser_gpu_property() 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"
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
ContextWebGpu(gpu)
|
|
||||||
|
ContextWebGpu { gpu }
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn instance_create_surface(
|
unsafe fn instance_create_surface(
|
||||||
@ -1190,12 +1236,16 @@ impl crate::context::Context for ContextWebGpu {
|
|||||||
if let Some(mapped_pref) = mapped_power_preference {
|
if let Some(mapped_pref) = mapped_power_preference {
|
||||||
mapped_options.power_preference(mapped_pref);
|
mapped_options.power_preference(mapped_pref);
|
||||||
}
|
}
|
||||||
let adapter_promise = self.0.request_adapter_with_options(&mapped_options);
|
if let Some(gpu) = &self.gpu {
|
||||||
|
let adapter_promise = gpu.request_adapter_with_options(&mapped_options);
|
||||||
MakeSendFuture::new(
|
OptionFuture::some(MakeSendFuture::new(
|
||||||
wasm_bindgen_futures::JsFuture::from(adapter_promise),
|
wasm_bindgen_futures::JsFuture::from(adapter_promise),
|
||||||
future_request_adapter,
|
future_request_adapter,
|
||||||
)
|
))
|
||||||
|
} else {
|
||||||
|
// Gpu is undefined; WebGPU is not supported in this browser.
|
||||||
|
OptionFuture::none()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn adapter_request_device(
|
fn adapter_request_device(
|
||||||
@ -1316,7 +1366,11 @@ impl crate::context::Context for ContextWebGpu {
|
|||||||
let mut mapped_formats = formats.iter().map(|format| map_texture_format(*format));
|
let mut mapped_formats = formats.iter().map(|format| map_texture_format(*format));
|
||||||
// Preferred canvas format will only be either "rgba8unorm" or "bgra8unorm".
|
// Preferred canvas format will only be either "rgba8unorm" or "bgra8unorm".
|
||||||
// https://www.w3.org/TR/webgpu/#dom-gpu-getpreferredcanvasformat
|
// https://www.w3.org/TR/webgpu/#dom-gpu-getpreferredcanvasformat
|
||||||
let preferred_format = self.0.get_preferred_canvas_format();
|
let gpu = self
|
||||||
|
.gpu
|
||||||
|
.as_ref()
|
||||||
|
.expect("Caller could not have created an adapter if gpu is undefined.");
|
||||||
|
let preferred_format = gpu.get_preferred_canvas_format();
|
||||||
if let Some(index) = mapped_formats.position(|format| format == preferred_format) {
|
if let Some(index) = mapped_formats.position(|format| format == preferred_format) {
|
||||||
formats.swap(0, index);
|
formats.swap(0, index);
|
||||||
}
|
}
|
||||||
|
46
wgpu/src/backend/webgpu/defined_non_null_js_value.rs
Normal file
46
wgpu/src/backend/webgpu/defined_non_null_js_value.rs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
use wasm_bindgen::JsValue;
|
||||||
|
|
||||||
|
/// Derefs to a [`JsValue`] that's known not to be `undefined` or `null`.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DefinedNonNullJsValue<T>(T);
|
||||||
|
|
||||||
|
impl<T> DefinedNonNullJsValue<T>
|
||||||
|
where
|
||||||
|
T: AsRef<JsValue>,
|
||||||
|
{
|
||||||
|
pub fn new(value: T) -> Option<Self> {
|
||||||
|
if value.as_ref().is_undefined() || value.as_ref().is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(Self(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for DefinedNonNullJsValue<T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DerefMut for DefinedNonNullJsValue<T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> AsRef<T> for DefinedNonNullJsValue<T> {
|
||||||
|
fn as_ref(&self) -> &T {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> AsMut<T> for DefinedNonNullJsValue<T> {
|
||||||
|
fn as_mut(&mut self) -> &mut T {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user