1use std::borrow::Cow;
6use std::cell::Cell;
7use std::rc::Rc;
8
9use dom_struct::dom_struct;
10use js::context::{JSContext, NoGC};
11use js::jsapi::{HandleObject, Heap, JSObject};
12use js::realm::CurrentRealm;
13use jstraceable_derive::JSTraceable;
14use log::warn;
15use malloc_size_of_derive::MallocSizeOf;
16use script_bindings::callback::CallbackContainer;
17use script_bindings::cell::DomRefCell;
18use script_bindings::codegen::GenericBindings::EventBinding::EventInit;
19use script_bindings::codegen::GenericBindings::EventHandlerBinding::EventHandlerNonNull;
20use script_bindings::codegen::GenericBindings::WebGPUBinding::{
21 GPUAdapterMethods, GPUBindGroupDescriptor, GPUBindGroupLayoutDescriptor, GPUBufferDescriptor,
22 GPUCommandEncoderDescriptor, GPUComputePipelineDescriptor, GPUDeviceLostReason,
23 GPUDeviceMethods, GPUDeviceWrap, GPUErrorFilter, GPUExternalTextureDescriptor,
24 GPUPipelineLayoutDescriptor, GPUQuerySetDescriptor, GPURenderBundleEncoderDescriptor,
25 GPURenderPipelineDescriptor, GPUSamplerDescriptor, GPUShaderModuleDescriptor,
26 GPUTextureDescriptor, GPUTextureFormat, GPUUncapturedErrorEventInit, GPUVertexStepMode,
27};
28use script_bindings::codegen::GenericUnionTypes::GPUPipelineLayoutOrGPUAutoLayoutMode;
29use script_bindings::error::Error;
30use script_bindings::inheritance::Castable;
31use script_bindings::interfaces::{
32 HeapTracedPromiseHelpers, PromiseHelpers, StackRootPromiseHelpers,
33};
34use script_bindings::reflector::{
35 DomGlobalGeneric, reflect_weak_referenceable_dom_object_with_cx_and_wrap,
36};
37use script_bindings::traits::DomEventTrait;
38use script_bindings::{DomTypes, cformat, task};
39use stylo_atoms::atom;
40use webgpu_traits::{
41 BlendState, ColorTargetState, ColorWrites, DepthBiasState, DepthStencilState, Features,
42 FragmentState, Limits, MultisampleState, RenderPipelineDescriptor, StencilFaceState,
43 StencilState, TextureFormat, VertexAttribute, VertexBufferLayout, VertexState, VertexStepMode,
44 WebGPU, WebGPUDevice, WebGPUQueue, WebGPURequest,
45};
46
47use super::gpudevicelostinfo::GPUDeviceLostInfo;
48use crate::PipelineLayout;
49use crate::dom::bindings::error::Fallible;
50use crate::dom::bindings::refcounted::Trusted;
51use crate::dom::bindings::root::{Dom, DomRoot};
52use crate::dom::bindings::str::USVString;
53use crate::dom::bindings::trace::RootedTraceableBox;
54use crate::gpuadapter::GPUAdapter;
55use crate::gpuadapterinfo::GPUAdapterInfo;
56use crate::gpubindgroup::GPUBindGroup;
57use crate::gpubindgrouplayout::GPUBindGroupLayout;
58use crate::gpubuffer::GPUBuffer;
59use crate::gpucommandencoder::GPUCommandEncoder;
60use crate::gpucomputepipeline::GPUComputePipeline;
61use crate::gpuconvert::WebGPUConvert;
62use crate::gpuerror::{AsWebGpu, GPUError};
63use crate::gpuexternaltexture::GPUExternalTexture;
64use crate::gpupipelinelayout::GPUPipelineLayout;
65use crate::gpuqueryset::GPUQuerySet;
66use crate::gpurenderbundleencoder::GPURenderBundleEncoder;
67use crate::gpurenderpipeline::GPURenderPipeline;
68use crate::gpusampler::GPUSampler;
69use crate::gpushadermodule::GPUShaderModule;
70use crate::gpusupportedfeatures::GPUSupportedFeatures;
71use crate::gpusupportedlimits::GPUSupportedLimits;
72use crate::gputexture::GPUTexture;
73use crate::gpuuncapturederrorevent::GPUUncapturedErrorEvent;
74use crate::traits::{
75 Equivalence, EventTargetTrait, WebGPUGlobalTrait, WebGPUPromise, WebGPUPromiseCallbackTrait,
76 WebGPURootedPromiseTrait, WebGPUTracedPromiseTrait,
77};
78
79macro_rules! event_handler(
80 ($event_type: ident, $getter: ident, $setter: ident) => (
81 define_event_handler!(
82 script_bindings::codegen::GenericBindings::EventHandlerBinding::EventHandlerNonNull<D>,
83 $event_type,
84 $getter,
85 $setter,
86 set_event_handler_common
87 );
88 )
89);
90
91macro_rules! define_event_handler(
93 ($handler: ty, $event_type: ident, $getter: ident, $setter: ident, $setter_fn: ident) => (
94 fn $getter(&self, cx: &mut js::context::JSContext) -> Option<::std::rc::Rc<$handler>> {
95 use crate::dom::bindings::inheritance::Castable;
96 let eventtarget = self.upcast::<D::EventTarget>();
97 D::EventTarget::get_event_handler_common(eventtarget, cx, stringify!($event_type))
98 }
99
100 fn $setter(&self, cx: &mut js::context::JSContext, listener: Option<::std::rc::Rc<$handler>>) {
101 use crate::dom::bindings::inheritance::Castable;
102 let eventtarget = self.upcast::<D::EventTarget>();
103 eventtarget.$setter_fn(cx, stringify!($event_type), listener)
104 }
105 )
106);
107
108#[derive(JSTraceable, MallocSizeOf)]
109struct DroppableGPUDevice {
110 #[no_trace]
111 channel: WebGPU,
112 #[no_trace]
113 device: WebGPUDevice,
114}
115
116impl Drop for DroppableGPUDevice {
117 fn drop(&mut self) {
118 if let Err(e) = self
119 .channel
120 .0
121 .send(WebGPURequest::DropDevice(self.device.0))
122 {
123 warn!("Failed to send DropDevice ({:?}) ({})", self.device.0, e);
124 }
125 }
126}
127
128#[dom_struct]
129pub struct GPUDevice<D: DomTypes> {
130 eventtarget: D::EventTarget,
131 adapter: Dom<GPUAdapter<D>>,
132 #[ignore_malloc_size_of = "mozjs"]
133 extensions: Heap<*mut JSObject>,
134 features: Dom<GPUSupportedFeatures<D>>,
135 limits: Dom<GPUSupportedLimits<D>>,
136 adapter_info: Dom<GPUAdapterInfo<D>>,
137 label: DomRefCell<USVString>,
138 default_queue: Dom<D::GPUQueue>,
139 lost_promise: DomRefCell<<D::Promise as PromiseHelpers<D>>::HeapTraced>,
141 valid: Cell<bool>,
142 droppable: DroppableGPUDevice,
143}
144
145impl<D> GPUDevice<D>
146where
147 D: Equivalence,
148 <D::Promise as PromiseHelpers<D>>::StackRoot: WebGPUPromise<D>,
149 EventHandlerNonNull<D>: CallbackContainer<D>,
150{
151 #[allow(clippy::too_many_arguments)]
152 fn new_inherited(
153 channel: WebGPU,
154 adapter: &GPUAdapter<D>,
155 features: &GPUSupportedFeatures<D>,
156 limits: &GPUSupportedLimits<D>,
157 adapter_info: &GPUAdapterInfo<D>,
158 device: WebGPUDevice,
159 queue: &D::GPUQueue,
160 label: String,
161 lost_promise: &<D::Promise as PromiseHelpers<D>>::StackRoot,
162 ) -> Self {
163 Self {
164 eventtarget: D::EventTarget::new_inherited(),
165 adapter: Dom::from_ref(adapter),
166 extensions: Heap::default(),
167 features: Dom::from_ref(features),
168 limits: Dom::from_ref(limits),
169 adapter_info: Dom::from_ref(adapter_info),
170 label: DomRefCell::new(USVString::from(label)),
171 default_queue: Dom::from_ref(queue),
172 lost_promise: DomRefCell::new(lost_promise.to_traced()),
173 valid: Cell::new(true),
174 droppable: DroppableGPUDevice { channel, device },
175 }
176 }
177
178 #[allow(clippy::too_many_arguments)]
179 pub fn new(
180 cx: &mut JSContext,
181 global: &D::GlobalScope,
182 channel: WebGPU,
183 adapter: &GPUAdapter<D>,
184 extensions: HandleObject,
185 features: Features,
186 limits: Limits,
187 device: WebGPUDevice,
188 queue: WebGPUQueue,
189 label: String,
190 ) -> DomRoot<Self> {
191 let queue = D::GPUQueue::new(cx, global, channel.clone(), queue);
192 let limits = GPUSupportedLimits::new(cx, global, limits);
193 let features = GPUSupportedFeatures::Constructor(cx, global, None, features).unwrap();
194 let adapter_info = GPUAdapterInfo::clone_from(cx, global, &adapter.Info());
195 let lost_promise = <D::Promise as PromiseHelpers<D>>::StackRoot::new_rooted(cx, global);
196 let device = reflect_weak_referenceable_dom_object_with_cx_and_wrap::<D, _, _>(
197 cx,
198 Rc::new(GPUDevice::new_inherited(
199 channel,
200 adapter,
201 &features,
202 &limits,
203 &adapter_info,
204 device,
205 &queue,
206 label,
207 &lost_promise,
208 )),
209 global,
210 GPUDeviceWrap::<D>,
211 );
212 queue.set_device(cx, &device);
213 device.extensions.set(*extensions);
214 device
215 }
216}
217
218impl<D> GPUDevice<D>
219where
220 D: Equivalence,
221 <D::Promise as PromiseHelpers<D>>::StackRoot: WebGPUPromise<D>,
222 EventHandlerNonNull<D>: CallbackContainer<D>,
223{
224 pub fn id(&self) -> WebGPUDevice {
225 self.droppable.device
226 }
227
228 pub fn queue_id(&self) -> WebGPUQueue {
229 self.default_queue.id()
230 }
231
232 pub fn channel(&self) -> WebGPU {
233 self.droppable.channel.clone()
234 }
235
236 pub(crate) fn dispatch_error(&self, error: webgpu_traits::Error) {
237 if let Err(e) = self.droppable.channel.0.send(WebGPURequest::DispatchError {
238 device_id: self.id().0,
239 error,
240 }) {
241 warn!("Failed to send WebGPURequest::DispatchError due to {e:?}");
242 }
243 }
244
245 pub fn fire_uncaptured_error(&self, error: webgpu_traits::Error) {
247 let this = Trusted::new(self);
248
249 self.global_from_reflector().queue_webgpu_task_source(task!(
252 fire_uncaptured_error: move |cx| {
253 let this = this.root();
254 let error = GPUError::from_error(cx, &*this.global_from_reflector(), error);
255
256 let event = GPUUncapturedErrorEvent::new(
257 cx,
258 &*this.global_from_reflector(),
259 atom!("uncapturederror"),
260 &GPUUncapturedErrorEventInit::<D> {
261 error,
262 parent: EventInit::empty(),
263 },
264 );
265
266 event.upcast::<D::Event>().fire(cx, this.upcast());
267 }
268 ));
269 }
270
271 pub(crate) fn validate_texture_format_required_features(
276 &self,
277 format: &GPUTextureFormat,
278 ) -> Fallible<TextureFormat> {
279 let texture_format: TextureFormat = (*format).convert();
280 if self
281 .features
282 .wgpu_features()
283 .contains(texture_format.required_features())
284 {
285 Ok(texture_format)
286 } else {
287 Err(Error::Type(cformat!(
288 "{texture_format:?} is not supported by this GPUDevice"
289 )))
290 }
291 }
292
293 pub(crate) fn is_lost(&self) -> bool {
294 self.lost_promise.borrow().is_fulfilled()
295 }
296
297 pub(crate) fn get_pipeline_layout_data(
298 &self,
299 layout: &GPUPipelineLayoutOrGPUAutoLayoutMode<D>,
300 ) -> PipelineLayout {
301 if let GPUPipelineLayoutOrGPUAutoLayoutMode::GPUPipelineLayout(layout) = layout {
302 PipelineLayout::Explicit(layout.id().0)
303 } else {
304 PipelineLayout::Implicit
305 }
306 }
307
308 pub(crate) fn parse_render_pipeline<'a>(
309 &self,
310 descriptor: &GPURenderPipelineDescriptor<D>,
311 ) -> Fallible<RenderPipelineDescriptor<'a>> {
312 let pipeline_layout = self.get_pipeline_layout_data(&descriptor.parent.layout);
313 let desc = RenderPipelineDescriptor {
314 label: (&descriptor.parent.parent).convert(),
315 layout: pipeline_layout.explicit(),
316 cache: None,
317 vertex: VertexState {
318 stage: (&descriptor.vertex.parent).convert(),
319 buffers: Cow::Owned(
320 descriptor
321 .vertex
322 .buffers
323 .iter()
324 .map(|buffer| {
327 Some(VertexBufferLayout {
328 array_stride: buffer.arrayStride,
329 step_mode: match buffer.stepMode {
330 GPUVertexStepMode::Vertex => VertexStepMode::Vertex,
331 GPUVertexStepMode::Instance => VertexStepMode::Instance,
332 },
333 attributes: Cow::Owned(
334 buffer
335 .attributes
336 .iter()
337 .map(|att| VertexAttribute {
338 format: att.format.convert(),
339 offset: att.offset,
340 shader_location: att.shaderLocation,
341 })
342 .collect::<Vec<_>>(),
343 ),
344 })
345 })
346 .collect::<Vec<_>>(),
347 ),
348 },
349 fragment: descriptor
350 .fragment
351 .as_ref()
352 .map(|stage| -> Fallible<FragmentState> {
353 Ok(FragmentState {
354 stage: (&stage.parent).convert(),
355 targets: Cow::Owned(
356 stage
357 .targets
358 .iter()
359 .map(|state| {
360 self.validate_texture_format_required_features(&state.format)
361 .map(|format| {
362 Some(ColorTargetState {
363 format,
364 write_mask: ColorWrites::from_bits_retain(
365 state.writeMask,
366 ),
367 blend: state.blend.as_ref().map(|blend| {
368 BlendState {
369 color: (&blend.color).convert(),
370 alpha: (&blend.alpha).convert(),
371 }
372 }),
373 })
374 })
375 })
376 .collect::<Result<Vec<_>, _>>()?,
377 ),
378 })
379 })
380 .transpose()?,
381 primitive: (&descriptor.primitive).convert(),
382 depth_stencil: descriptor
383 .depthStencil
384 .as_ref()
385 .map(|dss_desc| {
386 self.validate_texture_format_required_features(&dss_desc.format)
387 .map(|format| DepthStencilState {
388 format,
389 depth_write_enabled: dss_desc.depthWriteEnabled,
390 depth_compare: dss_desc.depthCompare.map(|dc| dc.convert()),
391 stencil: StencilState {
392 front: StencilFaceState {
393 compare: dss_desc.stencilFront.compare.convert(),
394
395 fail_op: dss_desc.stencilFront.failOp.convert(),
396 depth_fail_op: dss_desc.stencilFront.depthFailOp.convert(),
397 pass_op: dss_desc.stencilFront.passOp.convert(),
398 },
399 back: StencilFaceState {
400 compare: dss_desc.stencilBack.compare.convert(),
401 fail_op: dss_desc.stencilBack.failOp.convert(),
402 depth_fail_op: dss_desc.stencilBack.depthFailOp.convert(),
403 pass_op: dss_desc.stencilBack.passOp.convert(),
404 },
405 read_mask: dss_desc.stencilReadMask,
406 write_mask: dss_desc.stencilWriteMask,
407 },
408 bias: DepthBiasState {
409 constant: dss_desc.depthBias,
410 slope_scale: *dss_desc.depthBiasSlopeScale,
411 clamp: *dss_desc.depthBiasClamp,
412 },
413 })
414 })
415 .transpose()?,
416 multisample: MultisampleState {
417 count: descriptor.multisample.count,
418 mask: descriptor.multisample.mask as u64,
419 alpha_to_coverage_enabled: descriptor.multisample.alphaToCoverageEnabled,
420 },
421 multiview_mask: None,
422 };
423 Ok(desc)
424 }
425
426 pub fn lose(&self, reason: GPUDeviceLostReason, msg: String) {
428 let this = Trusted::new(self);
429
430 let global: DomRoot<D::GlobalScope> = self.global_from_reflector();
433 global.queue_webgpu_task_source(task!(resolve_device_lost: move |cx| {
434 let this = this.root();
435
436 let lost_promise = &(*this.lost_promise.borrow());
437 let lost =
438 GPUDeviceLostInfo::<D>::new(cx, &*this.global_from_reflector(), msg.into(), reason);
439 lost_promise.resolve_native(cx, &*lost);
440 }));
441 }
442}
443
444impl<D> GPUDeviceMethods<D> for GPUDevice<D>
445where
446 D: Equivalence,
447 <D::Promise as PromiseHelpers<D>>::StackRoot: WebGPUPromise<D>,
448 <D::Promise as PromiseHelpers<D>>::HeapTraced: HeapTracedPromiseHelpers<D>,
449{
450 fn Features(&self) -> DomRoot<GPUSupportedFeatures<D>> {
452 DomRoot::from_ref(&self.features)
453 }
454
455 fn Limits(&self) -> DomRoot<GPUSupportedLimits<D>> {
457 DomRoot::from_ref(&*self.limits)
458 }
459
460 fn AdapterInfo(&self) -> DomRoot<GPUAdapterInfo<D>> {
462 DomRoot::from_ref(&self.adapter_info)
463 }
464
465 fn GetQueue(&self) -> DomRoot<D::GPUQueue> {
467 DomRoot::from_ref(&self.default_queue)
468 }
469
470 fn Label(&self) -> USVString {
472 self.label.borrow().clone()
473 }
474
475 fn SetLabel(&self, no_gc: &NoGC, value: USVString) {
477 *self.label.safe_borrow_mut(no_gc) = value;
478 }
479
480 fn Lost(&self, cx: &JSContext) -> <<D as script_bindings::DomTypes>::Promise as script_bindings::interfaces::PromiseHelpers<D>>::StackRoot{
482 self.lost_promise.borrow().root(cx)
483 }
484
485 fn CreateBuffer(
487 &self,
488 cx: &mut JSContext,
489 descriptor: &GPUBufferDescriptor,
490 ) -> Fallible<DomRoot<GPUBuffer<D>>> {
491 GPUBuffer::create(cx, self, descriptor)
492 }
493
494 fn CreateBindGroupLayout(
496 &self,
497 cx: &mut JSContext,
498 descriptor: &GPUBindGroupLayoutDescriptor,
499 ) -> Fallible<DomRoot<GPUBindGroupLayout<D>>> {
500 GPUBindGroupLayout::create(cx, self, descriptor)
501 }
502
503 fn CreatePipelineLayout(
505 &self,
506 cx: &mut JSContext,
507 descriptor: &GPUPipelineLayoutDescriptor<D>,
508 ) -> DomRoot<GPUPipelineLayout<D>> {
509 GPUPipelineLayout::create(cx, self, descriptor)
510 }
511
512 fn CreateBindGroup(
514 &self,
515 cx: &mut JSContext,
516 descriptor: &GPUBindGroupDescriptor<D>,
517 ) -> DomRoot<GPUBindGroup<D>> {
518 GPUBindGroup::create(cx, self, descriptor)
519 }
520
521 fn CreateShaderModule(
523 &self,
524 cx: &mut CurrentRealm<'_>,
525 descriptor: RootedTraceableBox<GPUShaderModuleDescriptor>,
526 ) -> DomRoot<GPUShaderModule<D>> {
527 GPUShaderModule::create(cx, self, descriptor)
528 }
529
530 fn CreateComputePipeline(
532 &self,
533 cx: &mut JSContext,
534 descriptor: &GPUComputePipelineDescriptor<D>,
535 ) -> DomRoot<GPUComputePipeline<D>> {
536 let compute_pipeline = GPUComputePipeline::create(self, descriptor, None);
537 GPUComputePipeline::new(
538 cx,
539 &*self.global_from_reflector(),
540 compute_pipeline,
541 descriptor.parent.parent.label.clone(),
542 self,
543 )
544 }
545
546 fn CreateComputePipelineAsync(
548 &self,
549 cx: &mut CurrentRealm<'_>,
550 descriptor: &GPUComputePipelineDescriptor<D>,
551 ) -> <<D as script_bindings::DomTypes>::Promise as script_bindings::interfaces::PromiseHelpers<D>>::StackRoot{
552 let promise = D::Promise::new_in_realm_rooted(cx);
553 let callback =
554 <D::Promise as PromiseHelpers<D>>::StackRoot::callback_promise_dom_manipulation_task_source(&promise, self);
555 GPUComputePipeline::create(self, descriptor, Some(callback));
556 promise
557 }
558
559 fn CreateCommandEncoder(
561 &self,
562 cx: &mut JSContext,
563 descriptor: &GPUCommandEncoderDescriptor,
564 ) -> DomRoot<GPUCommandEncoder<D>> {
565 GPUCommandEncoder::create(cx, self, descriptor)
566 }
567
568 fn CreateTexture(
570 &self,
571 cx: &mut JSContext,
572 descriptor: &GPUTextureDescriptor,
573 ) -> Fallible<DomRoot<GPUTexture<D>>> {
574 GPUTexture::create(cx, self, descriptor)
575 }
576
577 fn CreateSampler(
579 &self,
580 cx: &mut JSContext,
581 descriptor: &GPUSamplerDescriptor,
582 ) -> DomRoot<GPUSampler<D>> {
583 GPUSampler::create(cx, self, descriptor)
584 }
585
586 fn CreateRenderPipeline(
588 &self,
589 cx: &mut JSContext,
590 descriptor: &GPURenderPipelineDescriptor<D>,
591 ) -> Fallible<DomRoot<GPURenderPipeline<D>>> {
592 let desc = self.parse_render_pipeline(descriptor)?;
593 let render_pipeline = GPURenderPipeline::create(self, desc, None)?;
594 Ok(GPURenderPipeline::new(
595 cx,
596 &*self.global_from_reflector(),
597 render_pipeline,
598 descriptor.parent.parent.label.clone(),
599 self,
600 ))
601 }
602
603 fn CreateRenderPipelineAsync(
605 &self,
606 cx: &mut CurrentRealm<'_>,
607 descriptor: &GPURenderPipelineDescriptor<D>,
608 ) -> Fallible<<<D as script_bindings::DomTypes>::Promise as script_bindings::interfaces::PromiseHelpers<D>>::StackRoot>{
609 let desc = self.parse_render_pipeline(descriptor)?;
610 let promise = D::Promise::new_in_realm_rooted(cx);
611 let callback = <D::Promise as PromiseHelpers<D>>::StackRoot::callback_promise_dom_manipulation_task_source(
612 &promise, self,
613 );
614 GPURenderPipeline::create(self, desc, Some(callback))?;
615 Ok(promise)
616 }
617
618 fn CreateRenderBundleEncoder(
620 &self,
621 cx: &mut JSContext,
622 descriptor: &GPURenderBundleEncoderDescriptor,
623 ) -> Fallible<DomRoot<GPURenderBundleEncoder<D>>> {
624 GPURenderBundleEncoder::create(cx, self, descriptor)
625 }
626
627 fn CreateQuerySet(
629 &self,
630 cx: &mut JSContext,
631 descriptor: &GPUQuerySetDescriptor,
632 ) -> Fallible<DomRoot<GPUQuerySet<D>>> {
633 GPUQuerySet::create(cx, self, descriptor)
634 }
635
636 fn ImportExternalTexture(
638 &self,
639 cx: &mut JSContext,
640 descriptor: &GPUExternalTextureDescriptor<D>,
641 ) -> Fallible<DomRoot<GPUExternalTexture<D>>> {
642 GPUExternalTexture::create(cx, self, descriptor)
643 }
644
645 fn PushErrorScope(&self, filter: GPUErrorFilter) {
647 if self
648 .droppable
649 .channel
650 .0
651 .send(WebGPURequest::PushErrorScope {
652 device_id: self.id().0,
653 filter: filter.as_webgpu(),
654 })
655 .is_err()
656 {
657 warn!("Failed sending WebGPURequest::PushErrorScope");
658 }
659 }
660
661 fn PopErrorScope(&self, cx: &mut CurrentRealm<'_>) -> <<D as script_bindings::DomTypes>::Promise as script_bindings::interfaces::PromiseHelpers<D>>::StackRoot{
663 let promise = D::Promise::new_in_realm_rooted(cx);
664 let callback = <D::Promise as PromiseHelpers<D>>::StackRoot::callback_promise_dom_manipulation_task_source(
665 &promise, self,
666 );
667 if self
668 .droppable
669 .channel
670 .0
671 .send(WebGPURequest::PopErrorScope {
672 device_id: self.id().0,
673 callback,
674 })
675 .is_err()
676 {
677 warn!("Error when sending WebGPURequest::PopErrorScope");
678 }
679 promise
680 }
681
682 event_handler!(uncapturederror, GetOnuncapturederror, SetOnuncapturederror);
684
685 fn Destroy(&self) {
687 if self.valid.get() {
688 self.valid.set(false);
689
690 if let Err(e) = self
691 .droppable
692 .channel
693 .0
694 .send(WebGPURequest::DestroyDevice(self.id().0))
695 {
696 warn!("Failed to send DestroyDevice ({:?}) ({})", self.id().0, e);
697 }
698 }
699 }
700}