Skip to content

Commit 44c6649

Browse files
committed
[deno] Use error impl from core
Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
1 parent 8a91e74 commit 44c6649

4 files changed

Lines changed: 124 additions & 106 deletions

File tree

deno_webgpu/adapter.rs

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ use super::device::GPUDevice;
2020
use super::device::DEVICE_EXTERNAL_MEMORY_SIZE;
2121
use super::queue::GPUQueue;
2222
use crate::device::GPUDeviceLostInfo;
23+
use crate::error::GPUError;
2324
use crate::error::GPUGenericError;
2425
use crate::webidl::GPUFeatureName;
2526
use crate::LostPromiseResolverHM;
27+
use crate::WeakDeviceHM;
2628

2729
#[derive(WebIDL)]
2830
#[webidl(dictionary)]
@@ -251,6 +253,9 @@ impl GPUAdapter {
251253
op_state
252254
.borrow_mut::<LostPromiseResolverHM>()
253255
.remove(&wgpu_device_id);
256+
op_state
257+
.borrow_mut::<WeakDeviceHM>()
258+
.remove(&wgpu_device_id);
254259
}),
255260
);
256261

@@ -260,7 +265,57 @@ impl GPUAdapter {
260265
let device_ref =
261266
deno_core::cppgc::try_unwrap_cppgc_object::<GPUDevice>(scope, device)
262267
.unwrap();
263-
device_ref.error_handler.set_device(weak_device);
268+
269+
let cross_thread_spawner =
270+
state.borrow::<V8CrossThreadTaskSpawner>().clone();
271+
state
272+
.borrow_mut::<WeakDeviceHM>()
273+
.insert(wgpu_device_id, weak_device);
274+
let wake = Arc::new(Mutex::new(move |error: wgpu_types::error::Error| {
275+
cross_thread_spawner.spawn(move |scope| {
276+
let state = JsRuntime::op_state_from(&*scope);
277+
let state = state.borrow();
278+
let weak_device =
279+
state.borrow::<WeakDeviceHM>().get(&wgpu_device_id).unwrap();
280+
let err: GPUError = error.into();
281+
let weak_device = weak_device.clone();
282+
283+
let Some(device) = weak_device.to_local(scope) else {
284+
// The device has already gone away, so we don't have
285+
// anywhere to report the error.
286+
return;
287+
};
288+
let key = v8::String::new(scope, "dispatchEvent").unwrap();
289+
let val = device.get(scope, key.into()).unwrap();
290+
let func =
291+
v8::Global::new(scope, val.try_cast::<v8::Function>().unwrap());
292+
let device = v8::Global::new(scope, device.cast::<v8::Value>());
293+
let error_event_class =
294+
state.borrow::<crate::ErrorEventClass>().0.clone();
295+
296+
let error = deno_core::error::to_v8_error(scope, &err);
297+
298+
let error_event_class =
299+
v8::Local::new(scope, error_event_class.clone());
300+
let constructor =
301+
v8::Local::<v8::Function>::try_from(error_event_class).unwrap();
302+
let kind = v8::String::new(scope, "uncapturederror").unwrap();
303+
304+
let obj = v8::Object::new(scope);
305+
let key = v8::String::new(scope, "error").unwrap();
306+
obj.set(scope, key.into(), error);
307+
308+
let event = constructor
309+
.new_instance(scope, &[kind.into(), obj.into()])
310+
.unwrap();
311+
312+
let recv = v8::Local::new(scope, device);
313+
func.open(scope).call(scope, recv, &[event.into()]);
314+
});
315+
}));
316+
wgpu_device.on_uncaptured_error(Arc::new(move |error| {
317+
wake.lock().unwrap()(error);
318+
}));
264319
device_ref.weak.set(finalizer).unwrap();
265320

266321
Ok(v8::Global::new(scope, device))

deno_webgpu/device.rs

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use deno_core::webidl::WebIdlInterfaceConverter;
1313
use deno_core::GarbageCollected;
1414
use deno_error::JsErrorBox;
1515
use wgpu_core::binding_model::BindingResource;
16+
use wgpu_core::error::EmptyErrorScopeStack;
1617
use wgpu_core::pipeline::ProgrammableStageDescriptor;
1718
use wgpu_types::BindingType;
1819

@@ -29,7 +30,7 @@ use crate::adapter::GPUAdapterInfo;
2930
use crate::adapter::GPUSupportedFeatures;
3031
use crate::adapter::GPUSupportedLimits;
3132
use crate::command_encoder::GPUCommandEncoder;
32-
use crate::error::{fmt_err, make_pipeline_error};
33+
use crate::error::{fmt_err, make_pipeline_error, GPUError};
3334
use crate::error::{GPUGenericError, GPUPipelineErrorReason};
3435
use crate::query_set::GPUQuerySet;
3536
use crate::render_bundle::GPURenderBundleEncoder;
@@ -688,12 +689,7 @@ impl GPUDevice {
688689
#[required(1)]
689690
#[undefined]
690691
fn push_error_scope(&self, #[webidl] filter: super::error::GPUErrorFilter) {
691-
self
692-
.error_handler
693-
.scopes
694-
.lock()
695-
.unwrap()
696-
.push((filter, None));
692+
self.wgpu_device.push_error_scope(filter.into());
697693
}
698694

699695
#[async_method(fake)]
@@ -702,26 +698,21 @@ impl GPUDevice {
702698
&self,
703699
scope: &mut v8::HandleScope,
704700
) -> Result<v8::Global<v8::Value>, JsErrorBox> {
705-
if !self.wgpu_device.is_valid() {
706-
let val = v8::null(scope).cast::<v8::Value>();
707-
return Ok(v8::Global::new(scope, val));
708-
}
709-
710-
let Some((_, error)) = self.error_handler.scopes.lock().unwrap().pop()
711-
else {
712-
return Err(JsErrorBox::new(
701+
match self.wgpu_device.pop_error_scope() {
702+
Ok(maybe_error) => {
703+
let val = if let Some(err) = maybe_error {
704+
let err: GPUError = err.into();
705+
deno_core::error::to_v8_error(scope, &err)
706+
} else {
707+
v8::null(scope).cast::<v8::Value>()
708+
};
709+
Ok(v8::Global::new(scope, val))
710+
}
711+
Err(EmptyErrorScopeStack {}) => Err(JsErrorBox::new(
713712
"DOMExceptionOperationError",
714713
"There are no error scopes on the error scope stack",
715-
));
716-
};
717-
718-
let val = if let Some(err) = error {
719-
deno_core::error::to_v8_error(scope, &err)
720-
} else {
721-
v8::null(scope).into()
722-
};
723-
724-
Ok(v8::Global::new(scope, val))
714+
)),
715+
}
725716
}
726717

727718
#[fast]

deno_webgpu/error.rs

Lines changed: 49 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@
22

33
use std::fmt::Display;
44
use std::fmt::Formatter;
5-
use std::sync::Mutex;
6-
use std::sync::OnceLock;
5+
use std::sync::Arc;
76

87
use deno_core::v8;
98

109
use deno_core::JsRuntime;
11-
use deno_core::V8TaskSpawner;
1210
use wgpu_core::binding_model::CreateBindGroupError;
1311
use wgpu_core::binding_model::CreateBindGroupLayoutError;
1412
use wgpu_core::binding_model::CreatePipelineLayoutError;
@@ -24,6 +22,7 @@ use wgpu_core::command::RenderBundleError;
2422
use wgpu_core::command::RenderPassError;
2523
use wgpu_core::device::queue::QueueSubmitError;
2624
use wgpu_core::device::queue::QueueWriteError;
25+
use wgpu_core::device::Device;
2726
use wgpu_core::device::DeviceError;
2827
use wgpu_core::pipeline::CreateComputePipelineError;
2928
use wgpu_core::pipeline::CreateRenderPipelineError;
@@ -35,31 +34,18 @@ use wgpu_core::resource::CreateQuerySetError;
3534
use wgpu_core::resource::CreateSamplerError;
3635
use wgpu_core::resource::CreateTextureError;
3736
use wgpu_core::resource::CreateTextureViewError;
37+
use wgpu_types::error::ErrorFilter;
3838
use wgpu_types::error::{ErrorType, WebGpuError};
3939

4040
pub type ErrorHandler = std::rc::Rc<DeviceErrorHandler>;
4141

4242
pub struct DeviceErrorHandler {
43-
pub scopes: Mutex<Vec<(GPUErrorFilter, Option<GPUError>)>>,
44-
spawner: V8TaskSpawner,
45-
46-
// The error handler is constructed before the device. A weak
47-
// reference to the device is placed here with `set_device`
48-
// after the device is constructed.
49-
device: OnceLock<v8::Weak<v8::Object>>,
43+
device: Arc<Device>,
5044
}
5145

5246
impl DeviceErrorHandler {
53-
pub fn new(spawner: V8TaskSpawner) -> Self {
54-
Self {
55-
scopes: Mutex::new(vec![]),
56-
device: OnceLock::new(),
57-
spawner,
58-
}
59-
}
60-
61-
pub fn set_device(&self, device: v8::Weak<v8::Object>) {
62-
self.device.set(device).unwrap()
47+
pub fn new(device: Arc<Device>) -> Self {
48+
Self { device }
6349
}
6450

6551
pub fn push_error<E: Into<GPUError>>(&self, err: Option<E>) {
@@ -69,64 +55,7 @@ impl DeviceErrorHandler {
6955

7056
let err = err.into();
7157

72-
let error_filter = match err {
73-
GPUError::Lost => return,
74-
GPUError::Validation(_) => GPUErrorFilter::Validation,
75-
GPUError::OutOfMemory => GPUErrorFilter::OutOfMemory,
76-
GPUError::Internal => GPUErrorFilter::Internal,
77-
};
78-
79-
let mut scopes = self.scopes.lock().unwrap();
80-
let scope = scopes
81-
.iter_mut()
82-
.rfind(|(filter, _)| filter == &error_filter);
83-
84-
if let Some(scope) = scope {
85-
// Only saving the first error in the scope as it's likely the culprit.
86-
if scope.1.is_none() {
87-
scope.1 = Some(err);
88-
}
89-
} else {
90-
let device = self
91-
.device
92-
.get()
93-
.expect("set_device was not called")
94-
.clone();
95-
self.spawner.spawn(move |scope| {
96-
let state = JsRuntime::op_state_from(&*scope);
97-
let Some(device) = device.to_local(scope) else {
98-
// The device has already gone away, so we don't have
99-
// anywhere to report the error.
100-
return;
101-
};
102-
let key = v8::String::new(scope, "dispatchEvent").unwrap();
103-
let val = device.get(scope, key.into()).unwrap();
104-
let func =
105-
v8::Global::new(scope, val.try_cast::<v8::Function>().unwrap());
106-
let device = v8::Global::new(scope, device.cast::<v8::Value>());
107-
let error_event_class =
108-
state.borrow().borrow::<crate::ErrorEventClass>().0.clone();
109-
110-
let error = deno_core::error::to_v8_error(scope, &err);
111-
112-
let error_event_class =
113-
v8::Local::new(scope, error_event_class.clone());
114-
let constructor =
115-
v8::Local::<v8::Function>::try_from(error_event_class).unwrap();
116-
let kind = v8::String::new(scope, "uncapturederror").unwrap();
117-
118-
let obj = v8::Object::new(scope);
119-
let key = v8::String::new(scope, "error").unwrap();
120-
obj.set(scope, key.into(), error);
121-
122-
let event = constructor
123-
.new_instance(scope, &[kind.into(), obj.into()])
124-
.unwrap();
125-
126-
let recv = v8::Local::new(scope, device);
127-
func.open(scope).call(scope, recv, &[event.into()]);
128-
});
129-
}
58+
self.device.handle_error_nolabel(err, "");
13059
}
13160
}
13261

@@ -138,6 +67,16 @@ pub enum GPUErrorFilter {
13867
Internal,
13968
}
14069

70+
impl From<GPUErrorFilter> for ErrorFilter {
71+
fn from(filter: GPUErrorFilter) -> Self {
72+
match filter {
73+
GPUErrorFilter::Validation => ErrorFilter::Validation,
74+
GPUErrorFilter::OutOfMemory => ErrorFilter::OutOfMemory,
75+
GPUErrorFilter::Internal => ErrorFilter::Internal,
76+
}
77+
}
78+
}
79+
14180
#[derive(Debug, deno_error::JsError)]
14281
pub enum GPUError {
14382
// TODO(@crowlKats): consider adding an unreachable value that uses unreachable!()
@@ -165,16 +104,46 @@ impl Display for GPUError {
165104
impl std::error::Error for GPUError {}
166105

167106
impl GPUError {
168-
fn from_webgpu(e: impl WebGpuError) -> Self {
107+
pub(crate) fn from_webgpu(e: impl WebGpuError) -> Self {
169108
match e.webgpu_error_type() {
170109
ErrorType::Internal => GPUError::Internal,
171-
ErrorType::DeviceLost => GPUError::Lost, // TODO: this variant should be ignored, register the lost callback instead.
110+
ErrorType::DeviceLost => GPUError::Lost, // this will be ignored by handle_error in wgpu-core
172111
ErrorType::OutOfMemory => GPUError::OutOfMemory,
173112
ErrorType::Validation => GPUError::Validation(fmt_err(&e)),
174113
}
175114
}
176115
}
177116

117+
impl From<wgpu_types::error::Error> for GPUError {
118+
fn from(err: wgpu_types::error::Error) -> Self {
119+
match err {
120+
wgpu_types::error::Error::Validation {
121+
description,
122+
source,
123+
} => {
124+
if let Some(e) = source.source() {
125+
GPUError::Validation(fmt_err(e))
126+
} else {
127+
GPUError::Validation(description)
128+
}
129+
}
130+
wgpu_types::error::Error::OutOfMemory { .. } => GPUError::OutOfMemory,
131+
wgpu_types::error::Error::Internal { .. } => GPUError::Internal,
132+
}
133+
}
134+
}
135+
136+
impl WebGpuError for GPUError {
137+
fn webgpu_error_type(&self) -> ErrorType {
138+
match self {
139+
GPUError::Lost => ErrorType::DeviceLost,
140+
GPUError::Validation(_) => ErrorType::Validation,
141+
GPUError::OutOfMemory => ErrorType::OutOfMemory,
142+
GPUError::Internal => ErrorType::Internal,
143+
}
144+
}
145+
}
146+
178147
pub(crate) fn fmt_err(err: &(dyn std::error::Error + 'static)) -> String {
179148
let mut output = err.to_string();
180149

deno_webgpu/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright 2018-2025 the Deno authors. MIT license.
22
#![cfg(not(target_arch = "wasm32"))]
33
#![warn(unsafe_op_in_unsafe_fn)]
4+
#![allow(clippy::disallowed_types)]
45

56
use std::cell::RefCell;
67
use std::collections::HashMap;
@@ -115,6 +116,7 @@ deno_core::extension!(
115116
lazy_loaded_esm = ["01_webgpu.js"],
116117
);
117118

119+
pub(crate) type WeakDeviceHM = HashMap<usize, v8::Weak<v8::Object>>;
118120
pub(crate) type LostPromiseResolverHM =
119121
HashMap<usize, v8::Global<v8::PromiseResolver>>;
120122

@@ -128,6 +130,7 @@ pub fn op_create_gpu(
128130
uncaptured_error_event_class: v8::Local<v8::Value>,
129131
pipeline_error_class: v8::Local<v8::Value>,
130132
) -> GPU {
133+
state.put(WeakDeviceHM::new());
131134
state.put(LostPromiseResolverHM::new());
132135
state.put(EventTargetSetup {
133136
brand: v8::Global::new(scope, webidl_brand),

0 commit comments

Comments
 (0)