@@ -5,6 +5,11 @@ use std::fmt::Formatter;
5
5
use std:: sync:: Mutex ;
6
6
use std:: sync:: OnceLock ;
7
7
8
+ use deno_core:: cppgc:: make_cppgc_object;
9
+ use deno_core:: v8;
10
+
11
+ use deno_core:: JsRuntime ;
12
+ use deno_core:: V8TaskSpawner ;
8
13
use wgpu_core:: binding_model:: CreateBindGroupError ;
9
14
use wgpu_core:: binding_model:: CreateBindGroupLayoutError ;
10
15
use wgpu_core:: binding_model:: CreatePipelineLayoutError ;
@@ -31,41 +36,38 @@ use wgpu_core::resource::CreateSamplerError;
31
36
use wgpu_core:: resource:: CreateTextureError ;
32
37
use wgpu_core:: resource:: CreateTextureViewError ;
33
38
34
- pub type ErrorHandler = std:: sync:: Arc < DeviceErrorHandler > ;
39
+ use crate :: device:: GPUDeviceLostInfo ;
40
+ use crate :: device:: GPUDeviceLostReason ;
41
+
42
+ pub type ErrorHandler = std:: rc:: Rc < DeviceErrorHandler > ;
35
43
36
44
pub struct DeviceErrorHandler {
37
45
pub is_lost : OnceLock < ( ) > ,
38
- lost_sender : Mutex < Option < tokio:: sync:: oneshot:: Sender < ( ) > > > ,
39
- uncaptured_sender_is_closed : Mutex < Option < tokio:: sync:: oneshot:: Sender < ( ) > > > ,
40
-
41
- pub uncaptured_sender : tokio:: sync:: mpsc:: UnboundedSender < GPUError > ,
42
-
43
46
pub scopes : Mutex < Vec < ( GPUErrorFilter , Vec < GPUError > ) > > ,
44
- }
47
+ lost_resolver : Mutex < Option < v8:: Global < v8:: PromiseResolver > > > ,
48
+ spawner : V8TaskSpawner ,
45
49
46
- impl Drop for DeviceErrorHandler {
47
- fn drop ( & mut self ) {
48
- if let Some ( sender) = self . uncaptured_sender_is_closed . lock ( ) . unwrap ( ) . take ( ) {
49
- let _ = sender. send ( ( ) ) ;
50
- }
51
- }
50
+ // The error handler is constructed before the device. A weak
51
+ // reference to the device is placed here with `set_device`
52
+ // after the device is constructed.
53
+ device : OnceLock < v8:: Weak < v8:: Object > > ,
52
54
}
53
55
54
56
impl DeviceErrorHandler {
55
- pub fn new (
56
- lost_sender : tokio:: sync:: oneshot:: Sender < ( ) > ,
57
- uncaptured_sender : tokio:: sync:: mpsc:: UnboundedSender < GPUError > ,
58
- uncaptured_sender_is_closed : tokio:: sync:: oneshot:: Sender < ( ) > ,
59
- ) -> Self {
57
+ pub fn new ( lost_resolver : v8:: Global < v8:: PromiseResolver > , spawner : V8TaskSpawner ) -> Self {
60
58
Self {
61
59
is_lost : Default :: default ( ) ,
62
- lost_sender : Mutex :: new ( Some ( lost_sender) ) ,
63
- uncaptured_sender,
64
- uncaptured_sender_is_closed : Mutex :: new ( Some ( uncaptured_sender_is_closed) ) ,
65
60
scopes : Mutex :: new ( vec ! [ ] ) ,
61
+ lost_resolver : Mutex :: new ( Some ( lost_resolver) ) ,
62
+ device : OnceLock :: new ( ) ,
63
+ spawner,
66
64
}
67
65
}
68
66
67
+ pub fn set_device ( & self , device : v8:: Weak < v8:: Object > ) {
68
+ self . device . set ( device) . unwrap ( )
69
+ }
70
+
69
71
pub fn push_error < E : Into < GPUError > > ( & self , err : Option < E > ) {
70
72
let Some ( err) = err else {
71
73
return ;
@@ -77,17 +79,22 @@ impl DeviceErrorHandler {
77
79
78
80
let err = err. into ( ) ;
79
81
80
- if matches ! ( err , GPUError :: Lost ) {
82
+ if let GPUError :: Lost ( reason ) = err {
81
83
let _ = self . is_lost . set ( ( ) ) ;
82
-
83
- if let Some ( sender) = self . lost_sender . lock ( ) . unwrap ( ) . take ( ) {
84
- let _ = sender. send ( ( ) ) ;
84
+ if let Some ( resolver) = self . lost_resolver . lock ( ) . unwrap ( ) . take ( ) {
85
+ self . spawner . spawn ( move |scope| {
86
+ let resolver = v8:: Local :: new ( scope, resolver) ;
87
+ let info = make_cppgc_object ( scope, GPUDeviceLostInfo { reason } ) ;
88
+ let info = v8:: Local :: new ( scope, info) ;
89
+ resolver. resolve ( scope, info. into ( ) ) ;
90
+ } ) ;
85
91
}
92
+
86
93
return ;
87
94
}
88
95
89
96
let error_filter = match err {
90
- GPUError :: Lost => unreachable ! ( ) ,
97
+ GPUError :: Lost ( _ ) => unreachable ! ( ) ,
91
98
GPUError :: Validation ( _) => GPUErrorFilter :: Validation ,
92
99
GPUError :: OutOfMemory => GPUErrorFilter :: OutOfMemory ,
93
100
GPUError :: Internal => GPUErrorFilter :: Internal ,
@@ -101,7 +108,41 @@ impl DeviceErrorHandler {
101
108
if let Some ( scope) = scope {
102
109
scope. 1 . push ( err) ;
103
110
} else {
104
- self . uncaptured_sender . send ( err) . unwrap ( ) ;
111
+ let device = self
112
+ . device
113
+ . get ( )
114
+ . expect ( "set_device was not called" )
115
+ . clone ( ) ;
116
+ self . spawner . spawn ( move |scope| {
117
+ let state = JsRuntime :: op_state_from ( & * scope) ;
118
+ let Some ( device) = device. to_local ( scope) else {
119
+ // The device has already gone away, so we don't have
120
+ // anywhere to report the error.
121
+ return ;
122
+ } ;
123
+ let key = v8:: String :: new ( scope, "dispatchEvent" ) . unwrap ( ) ;
124
+ let val = device. get ( scope, key. into ( ) ) . unwrap ( ) ;
125
+ let func = v8:: Global :: new ( scope, val. try_cast :: < v8:: Function > ( ) . unwrap ( ) ) ;
126
+ let device = v8:: Global :: new ( scope, device. cast :: < v8:: Value > ( ) ) ;
127
+ let error_event_class = state. borrow ( ) . borrow :: < crate :: ErrorEventClass > ( ) . 0 . clone ( ) ;
128
+
129
+ let error = deno_core:: error:: to_v8_error ( scope, & err) ;
130
+
131
+ let error_event_class = v8:: Local :: new ( scope, error_event_class. clone ( ) ) ;
132
+ let constructor = v8:: Local :: < v8:: Function > :: try_from ( error_event_class) . unwrap ( ) ;
133
+ let kind = v8:: String :: new ( scope, "uncapturederror" ) . unwrap ( ) ;
134
+
135
+ let obj = v8:: Object :: new ( scope) ;
136
+ let key = v8:: String :: new ( scope, "error" ) . unwrap ( ) ;
137
+ obj. set ( scope, key. into ( ) , error) ;
138
+
139
+ let event = constructor
140
+ . new_instance ( scope, & [ kind. into ( ) , obj. into ( ) ] )
141
+ . unwrap ( ) ;
142
+
143
+ let recv = v8:: Local :: new ( scope, device) ;
144
+ func. open ( scope) . call ( scope, recv, & [ event. into ( ) ] ) ;
145
+ } ) ;
105
146
}
106
147
}
107
148
}
@@ -118,7 +159,7 @@ pub enum GPUErrorFilter {
118
159
pub enum GPUError {
119
160
// TODO(@crowlKats): consider adding an unreachable value that uses unreachable!()
120
161
#[ class( "UNREACHABLE" ) ]
121
- Lost ,
162
+ Lost ( GPUDeviceLostReason ) ,
122
163
#[ class( "GPUValidationError" ) ]
123
164
Validation ( String ) ,
124
165
#[ class( "GPUOutOfMemoryError" ) ]
@@ -131,7 +172,7 @@ pub enum GPUError {
131
172
impl Display for GPUError {
132
173
fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
133
174
match self {
134
- GPUError :: Lost => Ok ( ( ) ) ,
175
+ GPUError :: Lost ( _ ) => Ok ( ( ) ) ,
135
176
GPUError :: Validation ( s) => f. write_str ( s) ,
136
177
GPUError :: OutOfMemory => f. write_str ( "not enough memory left" ) ,
137
178
GPUError :: Internal => Ok ( ( ) ) ,
@@ -170,7 +211,7 @@ impl From<CreateBufferError> for GPUError {
170
211
impl From < DeviceError > for GPUError {
171
212
fn from ( err : DeviceError ) -> Self {
172
213
match err {
173
- DeviceError :: Lost => GPUError :: Lost ,
214
+ DeviceError :: Lost => GPUError :: Lost ( GPUDeviceLostReason :: Unknown ) ,
174
215
DeviceError :: OutOfMemory => GPUError :: OutOfMemory ,
175
216
_ => GPUError :: Validation ( fmt_err ( & err) ) ,
176
217
}
0 commit comments