Skip to content

Commit

Permalink
Add details to RequestDeviceError.
Browse files Browse the repository at this point in the history
  • Loading branch information
kpreid committed Sep 15, 2023
1 parent 40cc2ee commit 5724386
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 9 deletions.
2 changes: 1 addition & 1 deletion tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(Te
}
}

fn initialize_adapter() -> (Adapter, Option<SurfaceGuard>) {
pub fn initialize_adapter() -> (Adapter, Option<SurfaceGuard>) {
let instance = initialize_instance();
let surface_guard: Option<SurfaceGuard>;
let compatible_surface;
Expand Down
51 changes: 51 additions & 0 deletions tests/tests/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,54 @@ fn device_mismatch() {
},
);
}

#[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))]
#[test]
fn request_device_error_on_native() {
pollster::block_on(request_device_error_message());
}

/// Check that `RequestDeviceError`s produced have some diagnostic information.
///
/// Note: this is a wasm *and* native test. On wasm it is run directly; on native, indirectly
#[wasm_bindgen_test::wasm_bindgen_test]
async fn request_device_error_message() {
// Not using initialize_test() because that doesn't let us catch the error
// nor .await anything
let (adapter, _surface_guard) = wgpu_test::initialize_adapter();

let device_error = adapter
.request_device(
&wgpu::DeviceDescriptor {
// Force a failure by requesting absurd limits.
features: wgpu::Features::all(),
limits: wgpu::Limits {
max_texture_dimension_1d: u32::MAX,
max_texture_dimension_2d: u32::MAX,
max_texture_dimension_3d: u32::MAX,
max_bind_groups: u32::MAX,
max_push_constant_size: u32::MAX,
..Default::default()
},
..Default::default()
},
None,
)
.await
.unwrap_err();

let device_error = device_error.to_string();
cfg_if::cfg_if! {
if #[cfg(all(target_arch = "wasm32", not(feature = "webgl")))] {
// On WebGPU, so the error we get will be from the browser WebGPU API.
// Per the WebGPU specification this should be a `TypeError` when features are not
// available, <https://gpuweb.github.io/gpuweb/#dom-gpuadapter-requestdevice>,
// and the stringification it goes through for Rust should put that in the message.
let expected = "TypeError";
} else {
// This message appears whenever wgpu-core is used as the implementation.
let expected = "Unsupported features were requested: Features(";
}
}
assert!(device_error.contains(expected), "{device_error}");
}
3 changes: 1 addition & 2 deletions wgpu/src/backend/direct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -622,8 +622,7 @@ impl crate::Context for Context {
()
));
if let Some(err) = error {
log::error!("Error in Adapter::request_device: {}", err);
return ready(Err(crate::RequestDeviceError));
return ready(Err(err.into()));
}
let error_sink = Arc::new(Mutex::new(ErrorSinkRaw::new()));
let device = Device {
Expand Down
4 changes: 3 additions & 1 deletion wgpu/src/backend/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -812,7 +812,9 @@ fn future_request_device(

(device_id, device_data, queue_id, queue_data)
})
.map_err(|_| crate::RequestDeviceError)
.map_err(|error_value| crate::RequestDeviceError {
inner: crate::RequestDeviceErrorKind::Web(error_value),
})
}

fn future_pop_error_scope(result: JsFutureResult) -> Option<crate::Error> {
Expand Down
95 changes: 90 additions & 5 deletions wgpu/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2738,18 +2738,103 @@ impl Drop for Device {
}
}

/// Requesting a device failed.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct RequestDeviceError;
/// Requesting a device from an [`Adapter`] failed.
#[derive(Clone, Debug)]
pub struct RequestDeviceError {
inner: RequestDeviceErrorKind,
}
#[derive(Clone, Debug)]
enum RequestDeviceErrorKind {
/// Error from [`wgpu_core`].
// must match dependency cfg
#[cfg(any(
not(target_arch = "wasm32"),
feature = "webgl",
target_os = "emscripten"
))]
Core(core::instance::RequestDeviceError),

/// Error from web API that was called by `wgpu` to request a device.
///
/// (This is currently never used by the webgl backend, but it could be.)
#[cfg(all(
target_arch = "wasm32",
not(any(target_os = "emscripten", feature = "webgl"))
))]
Web(wasm_bindgen::JsValue),
}

#[cfg(all(
feature = "fragile-send-sync-non-atomic-wasm",
not(target_feature = "atomics")
))]
unsafe impl Send for RequestDeviceErrorKind {}
#[cfg(all(
feature = "fragile-send-sync-non-atomic-wasm",
not(target_feature = "atomics")
))]
unsafe impl Sync for RequestDeviceErrorKind {}

#[cfg(any(
not(target_arch = "wasm32"),
all(
feature = "fragile-send-sync-non-atomic-wasm",
not(target_feature = "atomics")
)
))]
static_assertions::assert_impl_all!(RequestDeviceError: Send, Sync);

impl fmt::Display for RequestDeviceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Requesting a device failed")
match &self.inner {
#[cfg(any(
not(target_arch = "wasm32"),
feature = "webgl",
target_os = "emscripten"
))]
RequestDeviceErrorKind::Core(error) => error.fmt(f),
#[cfg(all(
target_arch = "wasm32",
not(any(target_os = "emscripten", feature = "webgl"))
))]
RequestDeviceErrorKind::Web(error_js_value) => {
// wasm-bindgen provides a reasonable error stringification via `Debug` impl
write!(f, "{error_js_value:?}")
}
}
}
}

impl error::Error for RequestDeviceError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match &self.inner {
#[cfg(any(
not(target_arch = "wasm32"),
feature = "webgl",
target_os = "emscripten"
))]
RequestDeviceErrorKind::Core(error) => error.source(),
#[cfg(all(
target_arch = "wasm32",
not(any(target_os = "emscripten", feature = "webgl"))
))]
RequestDeviceErrorKind::Web(_) => None,
}
}
}

impl error::Error for RequestDeviceError {}
#[cfg(any(
not(target_arch = "wasm32"),
feature = "webgl",
target_os = "emscripten"
))]
impl From<core::instance::RequestDeviceError> for RequestDeviceError {
fn from(error: core::instance::RequestDeviceError) -> Self {
Self {
inner: RequestDeviceErrorKind::Core(error),
}
}
}

/// [`Instance::create_surface()`] or a related function failed.
#[derive(Clone, Debug)]
Expand Down

0 comments on commit 5724386

Please sign in to comment.