Make request_adapter() report which backends were tried.

This will help users determine whether the problem is:

* a backend is not statically enabled
* a backend is not dynamically enabled
* no drivers or physical adapters are present for that backend
  (further distinction would be useful here)
* no adapters met the required criteria

There are deficiencies in the reporting of WebGPU vs. WebGL support.
Those would best be fixed by also fixing the mutual exclusion of those
backends.
This commit is contained in:
Kevin Reid 2025-03-13 09:58:54 -07:00 committed by Nicolas Silva
parent a369bfa8d1
commit fbe005f11a
15 changed files with 381 additions and 142 deletions

View File

@ -198,6 +198,7 @@ By @wumpf in [#7144](https://github.com/gfx-rs/wgpu/pull/7144)
#### General
- `wgpu::Instance::request_adapter()` now returns `Result` instead of `Option`; the error provides information about why no suitable adapter was returned. By @kpreid in [#7330](https://github.com/gfx-rs/wgpu/pull/7330).
- Support BLAS compaction in wgpu-hal. By @Vecvec in [#7101](https://github.com/gfx-rs/wgpu/pull/7101).
- Avoid using default features in many dependencies, etc. By Brody in [#7031](https://github.com/gfx-rs/wgpu/pull/7031)
- Use `hashbrown` to simplify no-std support. By Brody in [#6938](https://github.com/gfx-rs/wgpu/pull/6938) & [#6925](https://github.com/gfx-rs/wgpu/pull/6925).

View File

@ -43,7 +43,7 @@ impl InstanceInterface for CustomInstance {
&self,
_options: &wgpu::RequestAdapterOptions<'_, '_>,
) -> std::pin::Pin<Box<dyn RequestAdapterFuture>> {
Box::pin(std::future::ready(Some(DispatchAdapter::custom(
Box::pin(std::future::ready(Ok(DispatchAdapter::custom(
CustomAdapter(self.0.clone()),
))))
}

View File

@ -0,0 +1,80 @@
mod request_adapter_error {
fn id(backends: wgpu::Backends) -> wgpu::InstanceDescriptor {
wgpu::InstanceDescriptor {
backends,
flags: wgpu::InstanceFlags::default(),
backend_options: wgpu::BackendOptions::default(),
}
}
fn adapter_error(desc: &wgpu::InstanceDescriptor) -> String {
let instance = wgpu::Instance::new(desc);
pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::default(),
force_fallback_adapter: false,
compatible_surface: None,
}))
.unwrap_err()
.to_string()
}
#[test]
fn no_backends_requested() {
assert_eq!(
adapter_error(&id(wgpu::Backends::empty())),
"No suitable graphics adapter found; \
noop not requested, \
vulkan not requested, \
metal not requested, \
dx12 not requested, \
gl not requested, \
webgpu not requested"
);
}
/// This test nominally tests the noop backend, but it also exercises the logic for other backends
/// that fail to return any adapter.
#[test]
fn noop_not_enabled() {
assert_eq!(
adapter_error(&id(wgpu::Backends::NOOP)),
"No suitable graphics adapter found; \
noop not explicitly enabled, \
vulkan not requested, \
metal not requested, \
dx12 not requested, \
gl not requested, \
webgpu not requested"
);
}
#[test]
fn no_compiled_support() {
// Whichever platform we are on, try asking for a backend that definitely will be
// cfged out regardless of feature flags. (Not that these tests run on wasm at all yet.)
#[cfg(target_family = "wasm")]
assert_eq!(
adapter_error(&id(wgpu::Backends::METAL)),
"No suitable graphics adapter found; \
noop not requested, \
vulkan not requested, \
metal support not compiled in, \
dx12 not requested, \
gl not requested, \
webgpu not requested"
);
#[cfg(not(target_family = "wasm"))]
assert_eq!(
adapter_error(&id(wgpu::Backends::BROWSER_WEBGPU)),
"No suitable graphics adapter found; \
noop not requested, \
vulkan not requested, \
metal not requested, \
dx12 not requested, \
gl not requested, \
webgpu support not compiled in"
);
}
}

View File

@ -1,4 +1,5 @@
mod binding_arrays;
mod buffer;
mod buffer_slice;
mod instance;
mod texture;

View File

@ -10,11 +10,8 @@ fn device_is_not_available_by_default() {
..Default::default()
});
assert_eq!(
pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions::default())),
None,
"noop backend adapter present when it should not be"
);
pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions::default()))
.expect_err("noop backend adapter present when it should not be");
}
#[test]

View File

@ -1,5 +1,5 @@
use alloc::{borrow::ToOwned as _, boxed::Box, sync::Arc};
use core::{fmt, iter};
use alloc::{borrow::ToOwned as _, sync::Arc};
use core::fmt;
use crate::{
hal_api::HalApi,
@ -47,13 +47,8 @@ impl Global {
pub unsafe fn from_hal_instance<A: HalApi>(name: &str, hal_instance: A::Instance) -> Self {
profiling::scope!("Global::new");
let dyn_instance: Box<dyn hal::DynInstance> = Box::new(hal_instance);
Self {
instance: Instance {
name: name.to_owned(),
instance_per_backend: iter::once((A::VARIANT, dyn_instance)).collect(),
..Default::default()
},
instance: Instance::from_hal_instance::<A>(name.to_owned(), hal_instance),
surfaces: Registry::new(),
hub: Hub::new(),
}

View File

@ -3,10 +3,12 @@ use alloc::{
boxed::Box,
string::String,
sync::Arc,
vec,
vec::Vec,
};
use hashbrown::HashMap;
use thiserror::Error;
use crate::{
api_log, api_log_debug,
@ -22,8 +24,6 @@ use crate::{
use wgt::{Backend, Backends, PowerPreference};
use thiserror::Error;
pub type RequestAdapterOptions = wgt::RequestAdapterOptions<SurfaceId>;
#[derive(Clone, Debug, Error)]
@ -61,64 +61,96 @@ fn downlevel_default_limits_less_than_default_limits() {
#[derive(Default)]
pub struct Instance {
#[allow(dead_code)]
pub name: String,
/// List of instances per backend.
name: String,
/// List of instances per `wgpu-hal` backend.
///
/// The ordering in this list implies prioritization and needs to be preserved.
pub instance_per_backend: Vec<(Backend, Box<dyn hal::DynInstance>)>,
pub flags: wgt::InstanceFlags,
instance_per_backend: Vec<(Backend, Box<dyn hal::DynInstance>)>,
/// The backends that were requested by the user.
requested_backends: Backends,
/// The backends that we could have attempted to obtain from `wgpu-hal` —
/// those for which support is compiled in, currently.
///
/// The union of this and `requested_backends` is the set of backends that would be used,
/// independent of whether accessing the drivers/hardware for them succeeds.
/// To obtain the set of backends actually in use by this instance, check
/// `instance_per_backend` instead.
supported_backends: Backends,
flags: wgt::InstanceFlags,
}
impl Instance {
pub fn new(name: &str, instance_desc: &wgt::InstanceDescriptor) -> Self {
fn init<A: HalApi>(
_: A,
instance_desc: &wgt::InstanceDescriptor,
instance_per_backend: &mut Vec<(Backend, Box<dyn hal::DynInstance>)>,
) {
if instance_desc.backends.contains(A::VARIANT.into()) {
let hal_desc = hal::InstanceDescriptor {
name: "wgpu",
flags: instance_desc.flags,
backend_options: instance_desc.backend_options.clone(),
};
use hal::Instance as _;
match unsafe { A::Instance::init(&hal_desc) } {
Ok(instance) => {
log::debug!("Instance::new: created {:?} backend", A::VARIANT);
instance_per_backend.push((A::VARIANT, Box::new(instance)));
}
Err(err) => {
log::debug!(
"Instance::new: failed to create {:?} backend: {:?}",
A::VARIANT,
err
);
}
}
} else {
log::trace!("Instance::new: backend {:?} not requested", A::VARIANT);
}
}
let mut instance_per_backend = Vec::new();
let mut this = Self {
name: name.to_owned(),
instance_per_backend: Vec::new(),
requested_backends: instance_desc.backends,
supported_backends: Backends::empty(),
flags: instance_desc.flags,
};
#[cfg(vulkan)]
init(hal::api::Vulkan, instance_desc, &mut instance_per_backend);
this.try_add_hal(hal::api::Vulkan, instance_desc);
#[cfg(metal)]
init(hal::api::Metal, instance_desc, &mut instance_per_backend);
this.try_add_hal(hal::api::Metal, instance_desc);
#[cfg(dx12)]
init(hal::api::Dx12, instance_desc, &mut instance_per_backend);
this.try_add_hal(hal::api::Dx12, instance_desc);
#[cfg(gles)]
init(hal::api::Gles, instance_desc, &mut instance_per_backend);
this.try_add_hal(hal::api::Gles, instance_desc);
#[cfg(feature = "noop")]
init(hal::api::Noop, instance_desc, &mut instance_per_backend);
this.try_add_hal(hal::api::Noop, instance_desc);
this
}
/// Helper for `Instance::new()`; attempts to add a single `wgpu-hal` backend to this instance.
fn try_add_hal<A: HalApi>(&mut self, _: A, instance_desc: &wgt::InstanceDescriptor) {
// Whether or not the backend was requested, and whether or not it succeeds,
// note that we *could* try it.
self.supported_backends |= A::VARIANT.into();
if !instance_desc.backends.contains(A::VARIANT.into()) {
log::trace!("Instance::new: backend {:?} not requested", A::VARIANT);
return;
}
let hal_desc = hal::InstanceDescriptor {
name: "wgpu",
flags: self.flags,
backend_options: instance_desc.backend_options.clone(),
};
use hal::Instance as _;
match unsafe { A::Instance::init(&hal_desc) } {
Ok(instance) => {
log::debug!("Instance::new: created {:?} backend", A::VARIANT);
self.instance_per_backend
.push((A::VARIANT, Box::new(instance)));
}
Err(err) => {
log::debug!(
"Instance::new: failed to create {:?} backend: {:?}",
A::VARIANT,
err
);
}
}
}
pub(crate) fn from_hal_instance<A: HalApi>(
name: String,
hal_instance: <A as hal::Api>::Instance,
) -> Self {
Self {
name: name.to_owned(),
instance_per_backend,
flags: instance_desc.flags,
name,
instance_per_backend: vec![(A::VARIANT, Box::new(hal_instance))],
requested_backends: A::VARIANT.into(),
supported_backends: A::VARIANT.into(),
flags: wgt::InstanceFlags::default(),
}
}
@ -390,25 +422,40 @@ impl Instance {
&self,
desc: &wgt::RequestAdapterOptions<&Surface>,
backends: Backends,
) -> Result<Adapter, RequestAdapterError> {
) -> Result<Adapter, wgt::RequestAdapterError> {
profiling::scope!("Instance::request_adapter");
api_log!("Instance::request_adapter");
let mut adapters = Vec::new();
let mut incompatible_surface_backends = Backends::empty();
let mut no_fallback_backends = Backends::empty();
let mut no_adapter_backends = Backends::empty();
for (backend, instance) in self
for &(backend, ref instance) in self
.instance_per_backend
.iter()
.filter(|(backend, _)| backends.contains(Backends::from(*backend)))
.filter(|&&(backend, _)| backends.contains(Backends::from(backend)))
{
let compatible_hal_surface = desc
.compatible_surface
.and_then(|surface| surface.raw(*backend));
.and_then(|surface| surface.raw(backend));
let mut backend_adapters =
unsafe { instance.enumerate_adapters(compatible_hal_surface) };
if backend_adapters.is_empty() {
no_adapter_backends |= Backends::from(backend);
// by continuing, we avoid setting the further error bits below
continue;
}
if desc.force_fallback_adapter {
backend_adapters.retain(|exposed| exposed.info.device_type == wgt::DeviceType::Cpu);
if backend_adapters.is_empty() {
no_fallback_backends |= Backends::from(backend);
continue;
}
}
if let Some(surface) = desc.compatible_surface {
backend_adapters.retain(|exposed| {
let capabilities = surface.get_capabilities_with_raw(exposed);
@ -418,11 +465,16 @@ impl Instance {
exposed.info,
err
);
incompatible_surface_backends |= Backends::from(backend);
false
} else {
true
}
});
if backend_adapters.is_empty() {
incompatible_surface_backends |= Backends::from(backend);
continue;
}
}
adapters.extend(backend_adapters);
}
@ -482,9 +534,23 @@ impl Instance {
let adapter = Adapter::new(adapter);
Ok(adapter)
} else {
Err(RequestAdapterError::NotFound)
Err(wgt::RequestAdapterError::NotFound {
supported_backends: self.supported_backends,
requested_backends: self.requested_backends,
active_backends: self.active_backends(),
no_fallback_backends,
no_adapter_backends,
incompatible_surface_backends,
})
}
}
fn active_backends(&self) -> Backends {
self.instance_per_backend
.iter()
.map(|&(backend, _)| Backends::from(backend))
.collect()
}
}
pub struct Surface {
@ -783,14 +849,6 @@ pub enum RequestDeviceError {
UnsupportedFeature(wgt::Features),
}
#[derive(Clone, Debug, Error)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum RequestAdapterError {
#[error("No suitable adapter found")]
NotFound,
}
#[derive(Clone, Debug, Error)]
#[non_exhaustive]
pub enum CreateSurfaceError {
@ -949,7 +1007,7 @@ impl Global {
desc: &RequestAdapterOptions,
backends: Backends,
id_in: Option<AdapterId>,
) -> Result<AdapterId, RequestAdapterError> {
) -> Result<AdapterId, wgt::RequestAdapterError> {
let compatible_surface = desc.compatible_surface.map(|id| self.surfaces.get(id));
let desc = wgt::RequestAdapterOptions {
power_preference: desc.power_preference,

View File

@ -56,7 +56,7 @@ impl InstanceDescriptor {
bitflags::bitflags! {
/// Instance debugging flags.
///
/// These are not part of the webgpu standard.
/// These are not part of the WebGPU standard.
///
/// Defaults to enabling debugging-related flags if the build configuration has `debug_assertions`.
#[repr(transparent)]

View File

@ -16,7 +16,9 @@ extern crate alloc;
use alloc::{string::String, vec, vec::Vec};
use core::{
fmt,
hash::{Hash, Hasher},
mem,
num::NonZeroU32,
ops::Range,
};
@ -146,6 +148,16 @@ pub enum Backend {
}
impl Backend {
/// Array of all [`Backend`] values, corresponding to [`Backends::all()`].
pub const ALL: [Backend; Backends::all().bits().count_ones() as usize] = [
Self::Noop,
Self::Vulkan,
Self::Metal,
Self::Dx12,
Self::Gl,
Self::BrowserWebGpu,
];
/// Returns the string name of the backend.
#[must_use]
pub const fn to_str(self) -> &'static str {
@ -348,6 +360,88 @@ impl<S> Default for RequestAdapterOptions<S> {
}
}
/// Error when [`Instance::request_adapter()`] fails.
///
/// This type is not part of the WebGPU standard, where `requestAdapter()` would simply return null.
///
/// [`Instance::request_adapter()`]: ../wgpu/struct.Instance.html#method.request_adapter
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum RequestAdapterError {
/// No adapter available via the instances backends matched the requests adapter criteria.
NotFound {
// These fields must be set by wgpu-core and wgpu, but are not intended to be stable API,
// only data for the production of the error message.
#[doc(hidden)]
active_backends: Backends,
#[doc(hidden)]
requested_backends: Backends,
#[doc(hidden)]
supported_backends: Backends,
#[doc(hidden)]
no_fallback_backends: Backends,
#[doc(hidden)]
no_adapter_backends: Backends,
#[doc(hidden)]
incompatible_surface_backends: Backends,
},
/// Attempted to obtain adapter specified by environment variable, but the environment variable
/// was not set.
EnvNotSet,
}
impl core::error::Error for RequestAdapterError {}
impl fmt::Display for RequestAdapterError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RequestAdapterError::NotFound {
active_backends,
requested_backends,
supported_backends,
no_fallback_backends,
no_adapter_backends,
incompatible_surface_backends,
} => {
write!(f, "No suitable graphics adapter found; ")?;
let mut first = true;
for backend in Backend::ALL {
let bit = Backends::from(backend);
let comma = if mem::take(&mut first) { "" } else { ", " };
let explanation = if !requested_backends.contains(bit) {
// We prefer reporting this, because it makes the error most stable with
// respect to what is directly controllable by the caller, as opposed to
// compilation options or the run-time environment.
"not requested"
} else if !supported_backends.contains(bit) {
"support not compiled in"
} else if no_adapter_backends.contains(bit) {
"found no adapters"
} else if incompatible_surface_backends.contains(bit) {
"not compatible with provided surface"
} else if no_fallback_backends.contains(bit) {
"had no fallback adapters"
} else if !active_backends.contains(bit) {
// Backend requested but not active in this instance
if backend == Backend::Noop {
"not explicitly enabled"
} else {
"drivers/libraries could not be loaded"
}
} else {
// This path should be unreachable, but don't crash.
"[unknown reason]"
};
write!(f, "{comma}{backend} {explanation}")?;
}
}
RequestAdapterError::EnvNotSet => f.write_str("WGPU_ADAPTER_NAME not set")?,
}
Ok(())
}
}
/// Represents the sets of limits an adapter/device supports.
///
/// We provide three different defaults.

View File

@ -236,13 +236,14 @@ impl Instance {
///
/// Some options are "soft", so treated as non-mandatory. Others are "hard".
///
/// If no adapters are found that suffice all the "hard" options, `None` is returned.
/// If no adapters are found that satisfy all the "hard" options, an error is returned.
///
/// A `compatible_surface` is required when targeting WebGL2.
/// When targeting WebGL2, a [`compatible_surface`](RequestAdapterOptions::compatible_surface)
/// must be specified; using `RequestAdapterOptions::default()` will not succeed.
pub fn request_adapter(
&self,
options: &RequestAdapterOptions<'_, '_>,
) -> impl Future<Output = Option<Adapter>> + WasmNotSend {
) -> impl Future<Output = Result<Adapter, RequestAdapterError>> + WasmNotSend {
let future = self.inner.request_adapter(options);
async move { future.await.map(|adapter| Adapter { inner: adapter }) }
}

View File

@ -21,6 +21,7 @@ use core::{
pin::Pin,
task::{self, Poll},
};
use wgt::Backends;
use js_sys::Promise;
use wasm_bindgen::{prelude::*, JsCast};
@ -50,6 +51,10 @@ pub struct ContextWebGpu {
gpu: Option<DefinedNonNullJsValue<webgpu_sys::Gpu>>,
/// Unique identifier for this context.
ident: crate::cmp::Identifier,
/// Backends requested in the [`crate::InstanceDescriptor`].
/// Remembered for error reporting even though this itself is strictly
/// [`Backends::BROWSER_WEBGPU`].
requested_backends: Backends,
}
impl fmt::Debug for ContextWebGpu {
@ -195,36 +200,6 @@ impl<F, M> MakeSendFuture<F, M> {
#[cfg(send_sync)]
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 {
use webgpu_sys::GpuTextureFormat as tf;
use wgt::TextureFormat;
@ -900,16 +875,35 @@ fn map_js_sys_limits(limits: &wgt::Limits) -> js_sys::Object {
type JsFutureResult = Result<wasm_bindgen::JsValue, wasm_bindgen::JsValue>;
fn future_request_adapter(result: JsFutureResult) -> Option<dispatch::DispatchAdapter> {
fn future_request_adapter(
result: JsFutureResult,
requested_backends: Backends,
) -> Result<dispatch::DispatchAdapter, wgt::RequestAdapterError> {
let web_adapter: Option<webgpu_sys::GpuAdapter> =
result.and_then(wasm_bindgen::JsCast::dyn_into).ok();
web_adapter.map(|adapter| {
WebAdapter {
inner: adapter,
ident: crate::cmp::Identifier::create(),
}
.into()
})
web_adapter
.map(|adapter| {
WebAdapter {
inner: adapter,
ident: crate::cmp::Identifier::create(),
}
.into()
})
.ok_or_else(|| request_adapter_null_error(requested_backends))
}
// Translate WebGPUs null return into our error.
fn request_adapter_null_error(requested_backends: Backends) -> wgt::RequestAdapterError {
wgt::RequestAdapterError::NotFound {
active_backends: Backends::BROWSER_WEBGPU,
requested_backends,
// TODO: supported_backends should also include wgpu-core-based backends,
// if they were compiled in.
supported_backends: Backends::BROWSER_WEBGPU,
no_fallback_backends: Backends::empty(),
no_adapter_backends: Backends::BROWSER_WEBGPU,
incompatible_surface_backends: Backends::empty(),
}
}
fn future_request_device(
@ -1444,7 +1438,7 @@ crate::cmp::impl_eq_ord_hash_proxy!(WebQueueWriteBuffer => .ident);
crate::cmp::impl_eq_ord_hash_proxy!(WebBufferMappedRange => .ident);
impl dispatch::InstanceInterface for ContextWebGpu {
fn new(_desc: &crate::InstanceDescriptor) -> Self
fn new(desc: &crate::InstanceDescriptor) -> Self
where
Self: Sized,
{
@ -1456,6 +1450,7 @@ impl dispatch::InstanceInterface for ContextWebGpu {
ContextWebGpu {
gpu,
requested_backends: desc.backends,
ident: crate::cmp::Identifier::create(),
}
}
@ -1512,10 +1507,25 @@ impl dispatch::InstanceInterface for ContextWebGpu {
&self,
options: &crate::RequestAdapterOptions<'_, '_>,
) -> Pin<Box<dyn dispatch::RequestAdapterFuture>> {
let requested_backends = self.requested_backends;
//TODO: support this check, return `None` if the flag is not set.
// It's not trivial, since we need the Future logic to have this check,
// and currently the Future here has no room for extra parameter `backends`.
//assert!(backends.contains(wgt::Backends::BROWSER_WEBGPU));
if !(requested_backends.contains(wgt::Backends::BROWSER_WEBGPU)) {
return Box::pin(core::future::ready(Err(
wgt::RequestAdapterError::NotFound {
active_backends: Backends::BROWSER_WEBGPU,
requested_backends,
// TODO: supported_backends should also include wgpu-core-based backends,
// if they were compiled in.
supported_backends: Backends::BROWSER_WEBGPU,
no_fallback_backends: Backends::default(),
no_adapter_backends: Backends::default(),
incompatible_surface_backends: Backends::default(),
},
)));
}
let mapped_options = webgpu_sys::GpuRequestAdapterOptions::new();
let mapped_power_preference = match options.power_preference {
wgt::PowerPreference::None => None,
@ -1527,18 +1537,20 @@ impl dispatch::InstanceInterface for ContextWebGpu {
if let Some(mapped_pref) = mapped_power_preference {
mapped_options.set_power_preference(mapped_pref);
}
let future = if let Some(gpu) = &self.gpu {
if let Some(gpu) = &self.gpu {
let adapter_promise = gpu.request_adapter_with_options(&mapped_options);
OptionFuture::some(MakeSendFuture::new(
Box::pin(MakeSendFuture::new(
wasm_bindgen_futures::JsFuture::from(adapter_promise),
future_request_adapter,
move |result| future_request_adapter(result, requested_backends),
))
} else {
// Gpu is undefined; WebGPU is not supported in this browser.
OptionFuture::none()
};
Box::pin(future)
// Treat this exactly like requestAdapter() returned null.
Box::pin(core::future::ready(Err(request_adapter_null_error(
requested_backends,
))))
}
}
fn poll_all_devices(&self, _force_wait: bool) -> bool {

View File

@ -850,7 +850,7 @@ impl dispatch::InstanceInterface for ContextWgpuCore {
let generic: dispatch::DispatchAdapter = core.into();
generic
});
Box::pin(ready(adapter.ok()))
Box::pin(ready(adapter))
}
fn poll_all_devices(&self, force_wait: bool) -> bool {

View File

@ -35,7 +35,7 @@ macro_rules! trait_alias {
}
// Various return futures in the API.
trait_alias!(RequestAdapterFuture: Future<Output = Option<DispatchAdapter>> + WasmNotSend + 'static);
trait_alias!(RequestAdapterFuture: Future<Output = Result<DispatchAdapter, wgt::RequestAdapterError>> + WasmNotSend + 'static);
trait_alias!(RequestDeviceFuture: Future<Output = Result<(DispatchDevice, DispatchQueue), crate::RequestDeviceError>> + WasmNotSend + 'static);
trait_alias!(PopErrorScopeFuture: Future<Output = Option<crate::Error>> + WasmNotSend + 'static);
trait_alias!(ShaderCompilationInfoFuture: Future<Output = crate::CompilationInfo> + WasmNotSend + 'static);

View File

@ -80,13 +80,13 @@ pub use wgt::{
InstanceFlags, InternalCounters, Limits, MemoryHints, MultisampleState, NoopBackendOptions,
Origin2d, Origin3d, PipelineStatisticsTypes, PollError, PollStatus, PolygonMode,
PowerPreference, PredefinedColorSpace, PresentMode, PresentationTimestamp, PrimitiveState,
PrimitiveTopology, PushConstantRange, QueryType, RenderBundleDepthStencil, SamplerBindingType,
SamplerBorderColor, ShaderLocation, ShaderModel, ShaderRuntimeChecks, ShaderStages,
StencilFaceState, StencilOperation, StencilState, StorageTextureAccess, SurfaceCapabilities,
SurfaceStatus, TexelCopyBufferLayout, TextureAspect, TextureDimension, TextureFormat,
TextureFormatFeatureFlags, TextureFormatFeatures, TextureSampleType, TextureTransition,
TextureUsages, TextureUses, TextureViewDimension, Trace, VertexAttribute, VertexFormat,
VertexStepMode, WasmNotSend, WasmNotSendSync, WasmNotSync, COPY_BUFFER_ALIGNMENT,
PrimitiveTopology, PushConstantRange, QueryType, RenderBundleDepthStencil, RequestAdapterError,
SamplerBindingType, SamplerBorderColor, ShaderLocation, ShaderModel, ShaderRuntimeChecks,
ShaderStages, StencilFaceState, StencilOperation, StencilState, StorageTextureAccess,
SurfaceCapabilities, SurfaceStatus, TexelCopyBufferLayout, TextureAspect, TextureDimension,
TextureFormat, TextureFormatFeatureFlags, TextureFormatFeatures, TextureSampleType,
TextureTransition, TextureUsages, TextureUses, TextureViewDimension, Trace, VertexAttribute,
VertexFormat, VertexStepMode, WasmNotSend, WasmNotSendSync, WasmNotSync, COPY_BUFFER_ALIGNMENT,
COPY_BYTES_PER_ROW_ALIGNMENT, MAP_ALIGNMENT, PUSH_CONSTANT_ALIGNMENT,
QUERY_RESOLVE_BUFFER_ALIGNMENT, QUERY_SET_MAX_QUERIES, QUERY_SIZE, VERTEX_STRIDE_ALIGNMENT,
};

View File

@ -8,11 +8,11 @@ use crate::Backends;
pub fn initialize_adapter_from_env(
instance: &Instance,
compatible_surface: Option<&Surface<'_>>,
) -> Option<Adapter> {
) -> Result<Adapter, wgt::RequestAdapterError> {
let desired_adapter_name = std::env::var("WGPU_ADAPTER_NAME")
.as_deref()
.map(str::to_lowercase)
.ok()?;
.map_err(|_| wgt::RequestAdapterError::EnvNotSet)?;
let adapters = instance.enumerate_adapters(crate::Backends::all());
@ -32,7 +32,7 @@ pub fn initialize_adapter_from_env(
}
}
Some(chosen_adapter.expect("WGPU_ADAPTER_NAME set but no matching adapter found!"))
Ok(chosen_adapter.expect("WGPU_ADAPTER_NAME set but no matching adapter found!"))
}
/// Initialize the adapter obeying the `WGPU_ADAPTER_NAME` environment variable.
@ -40,18 +40,18 @@ pub fn initialize_adapter_from_env(
pub fn initialize_adapter_from_env(
_instance: &Instance,
_compatible_surface: Option<&Surface<'_>>,
) -> Option<Adapter> {
None
) -> Result<Adapter, wgt::RequestAdapterError> {
Err(wgt::RequestAdapterError::EnvNotSet)
}
/// Initialize the adapter obeying the `WGPU_ADAPTER_NAME` environment variable and if it doesn't exist fall back on a default adapter.
pub async fn initialize_adapter_from_env_or_default(
instance: &Instance,
compatible_surface: Option<&Surface<'_>>,
) -> Option<Adapter> {
) -> Result<Adapter, wgt::RequestAdapterError> {
match initialize_adapter_from_env(instance, compatible_surface) {
Some(a) => Some(a),
None => {
Ok(a) => Ok(a),
Err(_) => {
instance
.request_adapter(&RequestAdapterOptions {
power_preference: crate::PowerPreference::from_env().unwrap_or_default(),