Skip to main content

script/dom/webgpu/
gpudevice.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::borrow::Cow;
6use std::cell::Cell;
7use std::rc::Rc;
8
9use dom_struct::dom_struct;
10use js::context::JSContext;
11use js::jsapi::{HandleObject, Heap, JSObject};
12use js::realm::CurrentRealm;
13use script_bindings::cell::DomRefCell;
14use script_bindings::cformat;
15use script_bindings::reflector::reflect_dom_object_with_cx;
16use script_bindings::script_runtime::CanGc;
17use webgpu_traits::{
18    PopError, WebGPU, WebGPUComputePipeline, WebGPUComputePipelineResponse, WebGPUDevice,
19    WebGPUPoppedErrorScopeResponse, WebGPUQueue, WebGPURenderPipeline,
20    WebGPURenderPipelineResponse, WebGPURequest,
21};
22use wgpu_core::id::PipelineLayoutId;
23use wgpu_core::pipeline as wgpu_pipe;
24use wgpu_core::pipeline::RenderPipelineDescriptor;
25use wgpu_types::{self, TextureFormat};
26
27use super::gpudevicelostinfo::GPUDeviceLostInfo;
28use super::gpuerror::AsWebGpu;
29use super::gpupipelineerror::GPUPipelineError;
30use super::gpusupportedlimits::GPUSupportedLimits;
31use crate::conversions::Convert;
32use crate::dom::bindings::codegen::Bindings::EventBinding::EventInit;
33use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
34    GPUAdapterMethods, GPUBindGroupDescriptor, GPUBindGroupLayoutDescriptor, GPUBufferDescriptor,
35    GPUCommandEncoderDescriptor, GPUComputePipelineDescriptor, GPUDeviceLostReason,
36    GPUDeviceMethods, GPUErrorFilter, GPUPipelineErrorReason, GPUPipelineLayoutDescriptor,
37    GPURenderBundleEncoderDescriptor, GPURenderPipelineDescriptor, GPUSamplerDescriptor,
38    GPUShaderModuleDescriptor, GPUTextureDescriptor, GPUTextureFormat, GPUUncapturedErrorEventInit,
39    GPUVertexStepMode,
40};
41use crate::dom::bindings::codegen::UnionTypes::GPUPipelineLayoutOrGPUAutoLayoutMode;
42use crate::dom::bindings::error::{Error, Fallible};
43use crate::dom::bindings::inheritance::Castable;
44use crate::dom::bindings::refcounted::Trusted;
45use crate::dom::bindings::reflector::DomGlobal;
46use crate::dom::bindings::root::{Dom, DomRoot};
47use crate::dom::bindings::str::USVString;
48use crate::dom::bindings::trace::RootedTraceableBox;
49use crate::dom::event::Event;
50use crate::dom::eventtarget::EventTarget;
51use crate::dom::globalscope::GlobalScope;
52use crate::dom::promise::Promise;
53use crate::dom::types::GPUError;
54use crate::dom::webgpu::gpuadapter::GPUAdapter;
55use crate::dom::webgpu::gpuadapterinfo::GPUAdapterInfo;
56use crate::dom::webgpu::gpubindgroup::GPUBindGroup;
57use crate::dom::webgpu::gpubindgrouplayout::GPUBindGroupLayout;
58use crate::dom::webgpu::gpubuffer::GPUBuffer;
59use crate::dom::webgpu::gpucommandencoder::GPUCommandEncoder;
60use crate::dom::webgpu::gpucomputepipeline::GPUComputePipeline;
61use crate::dom::webgpu::gpupipelinelayout::GPUPipelineLayout;
62use crate::dom::webgpu::gpuqueue::GPUQueue;
63use crate::dom::webgpu::gpurenderbundleencoder::GPURenderBundleEncoder;
64use crate::dom::webgpu::gpurenderpipeline::GPURenderPipeline;
65use crate::dom::webgpu::gpusampler::GPUSampler;
66use crate::dom::webgpu::gpushadermodule::GPUShaderModule;
67use crate::dom::webgpu::gpusupportedfeatures::GPUSupportedFeatures;
68use crate::dom::webgpu::gputexture::GPUTexture;
69use crate::dom::webgpu::gpuuncapturederrorevent::GPUUncapturedErrorEvent;
70use crate::routed_promise::{RoutedPromiseListener, callback_promise};
71
72#[derive(JSTraceable, MallocSizeOf)]
73struct DroppableGPUDevice {
74    #[no_trace]
75    channel: WebGPU,
76    #[no_trace]
77    device: WebGPUDevice,
78}
79
80impl Drop for DroppableGPUDevice {
81    fn drop(&mut self) {
82        if let Err(e) = self
83            .channel
84            .0
85            .send(WebGPURequest::DropDevice(self.device.0))
86        {
87            warn!("Failed to send DropDevice ({:?}) ({})", self.device.0, e);
88        }
89    }
90}
91
92#[dom_struct]
93pub(crate) struct GPUDevice {
94    eventtarget: EventTarget,
95    adapter: Dom<GPUAdapter>,
96    #[ignore_malloc_size_of = "mozjs"]
97    extensions: Heap<*mut JSObject>,
98    features: Dom<GPUSupportedFeatures>,
99    limits: Dom<GPUSupportedLimits>,
100    adapter_info: Dom<GPUAdapterInfo>,
101    label: DomRefCell<USVString>,
102    default_queue: Dom<GPUQueue>,
103    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-lost>
104    #[conditional_malloc_size_of]
105    lost_promise: DomRefCell<Rc<Promise>>,
106    valid: Cell<bool>,
107    droppable: DroppableGPUDevice,
108}
109
110pub(crate) enum PipelineLayout {
111    Implicit,
112    Explicit(PipelineLayoutId),
113}
114
115impl PipelineLayout {
116    pub(crate) fn explicit(&self) -> Option<PipelineLayoutId> {
117        match self {
118            PipelineLayout::Explicit(layout_id) => Some(*layout_id),
119            PipelineLayout::Implicit => None,
120        }
121    }
122}
123
124impl GPUDevice {
125    #[allow(clippy::too_many_arguments)]
126    fn new_inherited(
127        channel: WebGPU,
128        adapter: &GPUAdapter,
129        features: &GPUSupportedFeatures,
130        limits: &GPUSupportedLimits,
131        adapter_info: &GPUAdapterInfo,
132        device: WebGPUDevice,
133        queue: &GPUQueue,
134        label: String,
135        lost_promise: Rc<Promise>,
136    ) -> Self {
137        Self {
138            eventtarget: EventTarget::new_inherited(),
139            adapter: Dom::from_ref(adapter),
140            extensions: Heap::default(),
141            features: Dom::from_ref(features),
142            limits: Dom::from_ref(limits),
143            adapter_info: Dom::from_ref(adapter_info),
144            label: DomRefCell::new(USVString::from(label)),
145            default_queue: Dom::from_ref(queue),
146            lost_promise: DomRefCell::new(lost_promise),
147            valid: Cell::new(true),
148            droppable: DroppableGPUDevice { channel, device },
149        }
150    }
151
152    #[allow(clippy::too_many_arguments)]
153    pub(crate) fn new(
154        cx: &mut JSContext,
155        global: &GlobalScope,
156        channel: WebGPU,
157        adapter: &GPUAdapter,
158        extensions: HandleObject,
159        features: wgpu_types::Features,
160        limits: wgpu_types::Limits,
161        device: WebGPUDevice,
162        queue: WebGPUQueue,
163        label: String,
164    ) -> DomRoot<Self> {
165        let queue = GPUQueue::new(cx, global, channel.clone(), queue);
166        let limits = GPUSupportedLimits::new(cx, global, limits);
167        let features = GPUSupportedFeatures::Constructor(cx, global, None, features).unwrap();
168        let adapter_info = GPUAdapterInfo::clone_from(cx, global, &adapter.Info());
169        let lost_promise = Promise::new2(cx, global);
170        let device = reflect_dom_object_with_cx(
171            Box::new(GPUDevice::new_inherited(
172                channel,
173                adapter,
174                &features,
175                &limits,
176                &adapter_info,
177                device,
178                &queue,
179                label,
180                lost_promise,
181            )),
182            global,
183            cx,
184        );
185        queue.set_device(&device);
186        device.extensions.set(*extensions);
187        device
188    }
189}
190
191impl GPUDevice {
192    pub(crate) fn id(&self) -> WebGPUDevice {
193        self.droppable.device
194    }
195
196    pub(crate) fn queue_id(&self) -> WebGPUQueue {
197        self.default_queue.id()
198    }
199
200    pub(crate) fn channel(&self) -> WebGPU {
201        self.droppable.channel.clone()
202    }
203
204    pub(crate) fn dispatch_error(&self, error: webgpu_traits::Error) {
205        if let Err(e) = self.droppable.channel.0.send(WebGPURequest::DispatchError {
206            device_id: self.id().0,
207            error,
208        }) {
209            warn!("Failed to send WebGPURequest::DispatchError due to {e:?}");
210        }
211    }
212
213    /// <https://gpuweb.github.io/gpuweb/#eventdef-gpudevice-uncapturederror>
214    pub(crate) fn fire_uncaptured_error(&self, error: webgpu_traits::Error) {
215        let this = Trusted::new(self);
216
217        // Queue a global task, using the webgpu task source, to fire an event named
218        // uncapturederror at a GPUDevice using GPUUncapturedErrorEvent.
219        self.global().task_manager().webgpu_task_source().queue(
220            task!(fire_uncaptured_error: move |cx| {
221                let this = this.root();
222                let error = GPUError::from_error(cx, &this.global(), error);
223
224                let event = GPUUncapturedErrorEvent::new(cx,
225                    &this.global(),
226                    atom!("uncapturederror"),
227                    &GPUUncapturedErrorEventInit {
228                        error,
229                        parent: EventInit::empty(),
230                    },
231                );
232
233                event.upcast::<Event>().fire(cx, this.upcast());
234            }),
235        );
236    }
237
238    /// <https://gpuweb.github.io/gpuweb/#abstract-opdef-validate-texture-format-required-features>
239    ///
240    /// Validates that the device suppports required features,
241    /// and if so returns an ok containing wgpu's `TextureFormat`
242    pub(crate) fn validate_texture_format_required_features(
243        &self,
244        format: &GPUTextureFormat,
245    ) -> Fallible<TextureFormat> {
246        let texture_format: TextureFormat = (*format).convert();
247        if self
248            .features
249            .wgpu_features()
250            .contains(texture_format.required_features())
251        {
252            Ok(texture_format)
253        } else {
254            Err(Error::Type(cformat!(
255                "{texture_format:?} is not supported by this GPUDevice"
256            )))
257        }
258    }
259
260    pub(crate) fn is_lost(&self) -> bool {
261        self.lost_promise.borrow().is_fulfilled()
262    }
263
264    pub(crate) fn get_pipeline_layout_data(
265        &self,
266        layout: &GPUPipelineLayoutOrGPUAutoLayoutMode,
267    ) -> PipelineLayout {
268        if let GPUPipelineLayoutOrGPUAutoLayoutMode::GPUPipelineLayout(layout) = layout {
269            PipelineLayout::Explicit(layout.id().0)
270        } else {
271            PipelineLayout::Implicit
272        }
273    }
274
275    pub(crate) fn parse_render_pipeline<'a>(
276        &self,
277        descriptor: &GPURenderPipelineDescriptor,
278    ) -> Fallible<RenderPipelineDescriptor<'a>> {
279        let pipeline_layout = self.get_pipeline_layout_data(&descriptor.parent.layout);
280        let desc = wgpu_pipe::RenderPipelineDescriptor {
281            label: (&descriptor.parent.parent).convert(),
282            layout: pipeline_layout.explicit(),
283            cache: None,
284            vertex: wgpu_pipe::VertexState {
285                stage: (&descriptor.vertex.parent).convert(),
286                buffers: Cow::Owned(
287                    descriptor
288                        .vertex
289                        .buffers
290                        .iter()
291                        .map(|buffer| wgpu_pipe::VertexBufferLayout {
292                            array_stride: buffer.arrayStride,
293                            step_mode: match buffer.stepMode {
294                                GPUVertexStepMode::Vertex => wgpu_types::VertexStepMode::Vertex,
295                                GPUVertexStepMode::Instance => wgpu_types::VertexStepMode::Instance,
296                            },
297                            attributes: Cow::Owned(
298                                buffer
299                                    .attributes
300                                    .iter()
301                                    .map(|att| wgpu_types::VertexAttribute {
302                                        format: att.format.convert(),
303                                        offset: att.offset,
304                                        shader_location: att.shaderLocation,
305                                    })
306                                    .collect::<Vec<_>>(),
307                            ),
308                        })
309                        .collect::<Vec<_>>(),
310                ),
311            },
312            fragment: descriptor
313                .fragment
314                .as_ref()
315                .map(|stage| -> Fallible<wgpu_pipe::FragmentState> {
316                    Ok(wgpu_pipe::FragmentState {
317                        stage: (&stage.parent).convert(),
318                        targets: Cow::Owned(
319                            stage
320                                .targets
321                                .iter()
322                                .map(|state| {
323                                    self.validate_texture_format_required_features(&state.format)
324                                        .map(|format| {
325                                            Some(wgpu_types::ColorTargetState {
326                                                format,
327                                                write_mask:
328                                                    wgpu_types::ColorWrites::from_bits_retain(
329                                                        state.writeMask,
330                                                    ),
331                                                blend: state.blend.as_ref().map(|blend| {
332                                                    wgpu_types::BlendState {
333                                                        color: (&blend.color).convert(),
334                                                        alpha: (&blend.alpha).convert(),
335                                                    }
336                                                }),
337                                            })
338                                        })
339                                })
340                                .collect::<Result<Vec<_>, _>>()?,
341                        ),
342                    })
343                })
344                .transpose()?,
345            primitive: (&descriptor.primitive).convert(),
346            depth_stencil: descriptor
347                .depthStencil
348                .as_ref()
349                .map(|dss_desc| {
350                    self.validate_texture_format_required_features(&dss_desc.format)
351                        .map(|format| wgpu_types::DepthStencilState {
352                            format,
353                            depth_write_enabled: dss_desc.depthWriteEnabled,
354                            depth_compare: dss_desc.depthCompare.map(|dc| dc.convert()),
355                            stencil: wgpu_types::StencilState {
356                                front: wgpu_types::StencilFaceState {
357                                    compare: dss_desc.stencilFront.compare.convert(),
358
359                                    fail_op: dss_desc.stencilFront.failOp.convert(),
360                                    depth_fail_op: dss_desc.stencilFront.depthFailOp.convert(),
361                                    pass_op: dss_desc.stencilFront.passOp.convert(),
362                                },
363                                back: wgpu_types::StencilFaceState {
364                                    compare: dss_desc.stencilBack.compare.convert(),
365                                    fail_op: dss_desc.stencilBack.failOp.convert(),
366                                    depth_fail_op: dss_desc.stencilBack.depthFailOp.convert(),
367                                    pass_op: dss_desc.stencilBack.passOp.convert(),
368                                },
369                                read_mask: dss_desc.stencilReadMask,
370                                write_mask: dss_desc.stencilWriteMask,
371                            },
372                            bias: wgpu_types::DepthBiasState {
373                                constant: dss_desc.depthBias,
374                                slope_scale: *dss_desc.depthBiasSlopeScale,
375                                clamp: *dss_desc.depthBiasClamp,
376                            },
377                        })
378                })
379                .transpose()?,
380            multisample: wgpu_types::MultisampleState {
381                count: descriptor.multisample.count,
382                mask: descriptor.multisample.mask as u64,
383                alpha_to_coverage_enabled: descriptor.multisample.alphaToCoverageEnabled,
384            },
385            multiview_mask: None,
386        };
387        Ok(desc)
388    }
389
390    /// <https://gpuweb.github.io/gpuweb/#lose-the-device>
391    pub(crate) fn lose(&self, reason: GPUDeviceLostReason, msg: String) {
392        let this = Trusted::new(self);
393
394        // Queue a global task, using the webgpu task source, to resolve device.lost
395        // promise with a new GPUDeviceLostInfo with reason and message.
396        self.global().task_manager().webgpu_task_source().queue(
397            task!(resolve_device_lost: move || {
398                let this = this.root();
399
400                let lost_promise = &(*this.lost_promise.borrow());
401                let lost = GPUDeviceLostInfo::new(&this.global(), msg.into(), reason, CanGc::deprecated_note());
402                lost_promise.resolve_native(&*lost, CanGc::deprecated_note());
403            }),
404        );
405    }
406}
407
408impl GPUDeviceMethods<crate::DomTypeHolder> for GPUDevice {
409    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-features>
410    fn Features(&self) -> DomRoot<GPUSupportedFeatures> {
411        DomRoot::from_ref(&self.features)
412    }
413
414    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-limits>
415    fn Limits(&self) -> DomRoot<GPUSupportedLimits> {
416        DomRoot::from_ref(&self.limits)
417    }
418
419    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-adapterinfo>
420    fn AdapterInfo(&self) -> DomRoot<GPUAdapterInfo> {
421        DomRoot::from_ref(&self.adapter_info)
422    }
423
424    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-queue>
425    fn GetQueue(&self) -> DomRoot<GPUQueue> {
426        DomRoot::from_ref(&self.default_queue)
427    }
428
429    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
430    fn Label(&self) -> USVString {
431        self.label.borrow().clone()
432    }
433
434    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
435    fn SetLabel(&self, value: USVString) {
436        *self.label.borrow_mut() = value;
437    }
438
439    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-lost>
440    fn Lost(&self) -> Rc<Promise> {
441        self.lost_promise.borrow().clone()
442    }
443
444    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createbuffer>
445    fn CreateBuffer(
446        &self,
447        cx: &mut JSContext,
448        descriptor: &GPUBufferDescriptor,
449    ) -> Fallible<DomRoot<GPUBuffer>> {
450        GPUBuffer::create(cx, self, descriptor)
451    }
452
453    /// <https://gpuweb.github.io/gpuweb/#GPUDevice-createBindGroupLayout>
454    fn CreateBindGroupLayout(
455        &self,
456        cx: &mut JSContext,
457        descriptor: &GPUBindGroupLayoutDescriptor,
458    ) -> Fallible<DomRoot<GPUBindGroupLayout>> {
459        GPUBindGroupLayout::create(cx, self, descriptor)
460    }
461
462    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createpipelinelayout>
463    fn CreatePipelineLayout(
464        &self,
465        cx: &mut JSContext,
466        descriptor: &GPUPipelineLayoutDescriptor,
467    ) -> DomRoot<GPUPipelineLayout> {
468        GPUPipelineLayout::create(cx, self, descriptor)
469    }
470
471    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createbindgroup>
472    fn CreateBindGroup(
473        &self,
474        cx: &mut JSContext,
475        descriptor: &GPUBindGroupDescriptor,
476    ) -> DomRoot<GPUBindGroup> {
477        GPUBindGroup::create(cx, self, descriptor)
478    }
479
480    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createshadermodule>
481    fn CreateShaderModule(
482        &self,
483        cx: &mut CurrentRealm<'_>,
484        descriptor: RootedTraceableBox<GPUShaderModuleDescriptor>,
485    ) -> DomRoot<GPUShaderModule> {
486        GPUShaderModule::create(cx, self, descriptor)
487    }
488
489    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcomputepipeline>
490    fn CreateComputePipeline(
491        &self,
492        cx: &mut JSContext,
493        descriptor: &GPUComputePipelineDescriptor,
494    ) -> DomRoot<GPUComputePipeline> {
495        let compute_pipeline = GPUComputePipeline::create(self, descriptor, None);
496        GPUComputePipeline::new(
497            cx,
498            &self.global(),
499            compute_pipeline,
500            descriptor.parent.parent.label.clone(),
501            self,
502        )
503    }
504
505    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcomputepipelineasync>
506    fn CreateComputePipelineAsync(
507        &self,
508        cx: &mut CurrentRealm<'_>,
509        descriptor: &GPUComputePipelineDescriptor,
510    ) -> Rc<Promise> {
511        let promise = Promise::new_in_realm(cx);
512        let callback = callback_promise(
513            &promise,
514            self,
515            self.global().task_manager().dom_manipulation_task_source(),
516        );
517        GPUComputePipeline::create(self, descriptor, Some(callback));
518        promise
519    }
520
521    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcommandencoder>
522    fn CreateCommandEncoder(
523        &self,
524        cx: &mut JSContext,
525        descriptor: &GPUCommandEncoderDescriptor,
526    ) -> DomRoot<GPUCommandEncoder> {
527        GPUCommandEncoder::create(cx, self, descriptor)
528    }
529
530    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createtexture>
531    fn CreateTexture(
532        &self,
533        cx: &mut JSContext,
534        descriptor: &GPUTextureDescriptor,
535    ) -> Fallible<DomRoot<GPUTexture>> {
536        GPUTexture::create(cx, self, descriptor)
537    }
538
539    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createsampler>
540    fn CreateSampler(
541        &self,
542        cx: &mut JSContext,
543        descriptor: &GPUSamplerDescriptor,
544    ) -> DomRoot<GPUSampler> {
545        GPUSampler::create(cx, self, descriptor)
546    }
547
548    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createrenderpipeline>
549    fn CreateRenderPipeline(
550        &self,
551        cx: &mut JSContext,
552        descriptor: &GPURenderPipelineDescriptor,
553    ) -> Fallible<DomRoot<GPURenderPipeline>> {
554        let desc = self.parse_render_pipeline(descriptor)?;
555        let render_pipeline = GPURenderPipeline::create(self, desc, None)?;
556        Ok(GPURenderPipeline::new(
557            cx,
558            &self.global(),
559            render_pipeline,
560            descriptor.parent.parent.label.clone(),
561            self,
562        ))
563    }
564
565    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createrenderpipelineasync>
566    fn CreateRenderPipelineAsync(
567        &self,
568        cx: &mut CurrentRealm<'_>,
569        descriptor: &GPURenderPipelineDescriptor,
570    ) -> Fallible<Rc<Promise>> {
571        let desc = self.parse_render_pipeline(descriptor)?;
572        let promise = Promise::new_in_realm(cx);
573        let callback = callback_promise(
574            &promise,
575            self,
576            self.global().task_manager().dom_manipulation_task_source(),
577        );
578        GPURenderPipeline::create(self, desc, Some(callback))?;
579        Ok(promise)
580    }
581
582    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createrenderbundleencoder>
583    fn CreateRenderBundleEncoder(
584        &self,
585        cx: &mut JSContext,
586        descriptor: &GPURenderBundleEncoderDescriptor,
587    ) -> Fallible<DomRoot<GPURenderBundleEncoder>> {
588        GPURenderBundleEncoder::create(cx, self, descriptor)
589    }
590
591    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-pusherrorscope>
592    fn PushErrorScope(&self, filter: GPUErrorFilter) {
593        if self
594            .droppable
595            .channel
596            .0
597            .send(WebGPURequest::PushErrorScope {
598                device_id: self.id().0,
599                filter: filter.as_webgpu(),
600            })
601            .is_err()
602        {
603            warn!("Failed sending WebGPURequest::PushErrorScope");
604        }
605    }
606
607    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-poperrorscope>
608    fn PopErrorScope(&self, cx: &mut CurrentRealm<'_>) -> Rc<Promise> {
609        let promise = Promise::new_in_realm(cx);
610        let callback = callback_promise(
611            &promise,
612            self,
613            self.global().task_manager().dom_manipulation_task_source(),
614        );
615        if self
616            .droppable
617            .channel
618            .0
619            .send(WebGPURequest::PopErrorScope {
620                device_id: self.id().0,
621                callback,
622            })
623            .is_err()
624        {
625            warn!("Error when sending WebGPURequest::PopErrorScope");
626        }
627        promise
628    }
629
630    // https://gpuweb.github.io/gpuweb/#dom-gpudevice-onuncapturederror
631    event_handler!(uncapturederror, GetOnuncapturederror, SetOnuncapturederror);
632
633    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-destroy>
634    fn Destroy(&self) {
635        if self.valid.get() {
636            self.valid.set(false);
637
638            if let Err(e) = self
639                .droppable
640                .channel
641                .0
642                .send(WebGPURequest::DestroyDevice(self.id().0))
643            {
644                warn!("Failed to send DestroyDevice ({:?}) ({})", self.id().0, e);
645            }
646        }
647    }
648}
649
650impl RoutedPromiseListener<WebGPUPoppedErrorScopeResponse> for GPUDevice {
651    fn handle_response(
652        &self,
653        cx: &mut js::context::JSContext,
654        response: WebGPUPoppedErrorScopeResponse,
655        promise: &Rc<Promise>,
656    ) {
657        match response {
658            Ok(None) | Err(PopError::Lost) => {
659                promise.resolve_native_with_cx(cx, &None::<Option<GPUError>>)
660            },
661            Err(PopError::Empty) => promise.reject_error_with_cx(cx, Error::Operation(None)),
662            Ok(Some(error)) => {
663                let error = GPUError::from_error(cx, &self.global(), error);
664                promise.resolve_native_with_cx(cx, &error);
665            },
666        }
667    }
668}
669
670impl RoutedPromiseListener<WebGPUComputePipelineResponse> for GPUDevice {
671    fn handle_response(
672        &self,
673        cx: &mut js::context::JSContext,
674        response: WebGPUComputePipelineResponse,
675        promise: &Rc<Promise>,
676    ) {
677        match response {
678            Ok(pipeline) => {
679                let gpu_compute_pipeline = GPUComputePipeline::new(
680                    cx,
681                    &self.global(),
682                    WebGPUComputePipeline(pipeline.id),
683                    pipeline.label.into(),
684                    self,
685                );
686                promise.resolve_native_with_cx(cx, &gpu_compute_pipeline)
687            },
688            Err(webgpu_traits::Error::Validation(msg)) => {
689                let gpu_pipeline_error = GPUPipelineError::new(
690                    cx,
691                    &self.global(),
692                    msg.into(),
693                    GPUPipelineErrorReason::Validation,
694                );
695                promise.reject_native_with_cx(cx, &gpu_pipeline_error)
696            },
697            Err(webgpu_traits::Error::OutOfMemory(msg) | webgpu_traits::Error::Internal(msg)) => {
698                let gpu_pipeline_error = GPUPipelineError::new(
699                    cx,
700                    &self.global(),
701                    msg.into(),
702                    GPUPipelineErrorReason::Internal,
703                );
704                promise.reject_native_with_cx(cx, &gpu_pipeline_error)
705            },
706        }
707    }
708}
709
710impl RoutedPromiseListener<WebGPURenderPipelineResponse> for GPUDevice {
711    fn handle_response(
712        &self,
713        cx: &mut js::context::JSContext,
714        response: WebGPURenderPipelineResponse,
715        promise: &Rc<Promise>,
716    ) {
717        match response {
718            Ok(pipeline) => {
719                let gpu_pipeline = GPURenderPipeline::new(
720                    cx,
721                    &self.global(),
722                    WebGPURenderPipeline(pipeline.id),
723                    pipeline.label.into(),
724                    self,
725                );
726                promise.resolve_native_with_cx(cx, &gpu_pipeline)
727            },
728            Err(webgpu_traits::Error::Validation(msg)) => {
729                let pipeline_error = GPUPipelineError::new(
730                    cx,
731                    &self.global(),
732                    msg.into(),
733                    GPUPipelineErrorReason::Validation,
734                );
735
736                promise.reject_native_with_cx(cx, &pipeline_error)
737            },
738            Err(webgpu_traits::Error::OutOfMemory(msg) | webgpu_traits::Error::Internal(msg)) => {
739                let pipeline_error = GPUPipelineError::new(
740                    cx,
741                    &self.global(),
742                    msg.into(),
743                    GPUPipelineErrorReason::Internal,
744                );
745                promise.reject_native_with_cx(cx, &pipeline_error)
746            },
747        }
748    }
749}