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::jsapi::{HandleObject, Heap, JSObject};
11use script_bindings::cell::DomRefCell;
12use script_bindings::cformat;
13use script_bindings::reflector::reflect_dom_object;
14use webgpu_traits::{
15    PopError, WebGPU, WebGPUComputePipeline, WebGPUComputePipelineResponse, WebGPUDevice,
16    WebGPUPoppedErrorScopeResponse, WebGPUQueue, WebGPURenderPipeline,
17    WebGPURenderPipelineResponse, WebGPURequest,
18};
19use wgpu_core::id::PipelineLayoutId;
20use wgpu_core::pipeline as wgpu_pipe;
21use wgpu_core::pipeline::RenderPipelineDescriptor;
22use wgpu_types::{self, TextureFormat};
23
24use super::gpudevicelostinfo::GPUDeviceLostInfo;
25use super::gpuerror::AsWebGpu;
26use super::gpupipelineerror::GPUPipelineError;
27use super::gpusupportedlimits::GPUSupportedLimits;
28use crate::conversions::Convert;
29use crate::dom::bindings::codegen::Bindings::EventBinding::EventInit;
30use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
31    GPUAdapterMethods, GPUBindGroupDescriptor, GPUBindGroupLayoutDescriptor, GPUBufferDescriptor,
32    GPUCommandEncoderDescriptor, GPUComputePipelineDescriptor, GPUDeviceLostReason,
33    GPUDeviceMethods, GPUErrorFilter, GPUPipelineErrorReason, GPUPipelineLayoutDescriptor,
34    GPURenderBundleEncoderDescriptor, GPURenderPipelineDescriptor, GPUSamplerDescriptor,
35    GPUShaderModuleDescriptor, GPUTextureDescriptor, GPUTextureFormat, GPUUncapturedErrorEventInit,
36    GPUVertexStepMode,
37};
38use crate::dom::bindings::codegen::UnionTypes::GPUPipelineLayoutOrGPUAutoLayoutMode;
39use crate::dom::bindings::error::{Error, Fallible};
40use crate::dom::bindings::inheritance::Castable;
41use crate::dom::bindings::refcounted::Trusted;
42use crate::dom::bindings::reflector::DomGlobal;
43use crate::dom::bindings::root::{Dom, DomRoot};
44use crate::dom::bindings::str::USVString;
45use crate::dom::bindings::trace::RootedTraceableBox;
46use crate::dom::event::Event;
47use crate::dom::eventtarget::EventTarget;
48use crate::dom::globalscope::GlobalScope;
49use crate::dom::promise::Promise;
50use crate::dom::types::GPUError;
51use crate::dom::webgpu::gpuadapter::GPUAdapter;
52use crate::dom::webgpu::gpuadapterinfo::GPUAdapterInfo;
53use crate::dom::webgpu::gpubindgroup::GPUBindGroup;
54use crate::dom::webgpu::gpubindgrouplayout::GPUBindGroupLayout;
55use crate::dom::webgpu::gpubuffer::GPUBuffer;
56use crate::dom::webgpu::gpucommandencoder::GPUCommandEncoder;
57use crate::dom::webgpu::gpucomputepipeline::GPUComputePipeline;
58use crate::dom::webgpu::gpupipelinelayout::GPUPipelineLayout;
59use crate::dom::webgpu::gpuqueue::GPUQueue;
60use crate::dom::webgpu::gpurenderbundleencoder::GPURenderBundleEncoder;
61use crate::dom::webgpu::gpurenderpipeline::GPURenderPipeline;
62use crate::dom::webgpu::gpusampler::GPUSampler;
63use crate::dom::webgpu::gpushadermodule::GPUShaderModule;
64use crate::dom::webgpu::gpusupportedfeatures::GPUSupportedFeatures;
65use crate::dom::webgpu::gputexture::GPUTexture;
66use crate::dom::webgpu::gpuuncapturederrorevent::GPUUncapturedErrorEvent;
67use crate::realms::InRealm;
68use crate::routed_promise::{RoutedPromiseListener, callback_promise};
69use crate::script_runtime::CanGc;
70
71#[derive(JSTraceable, MallocSizeOf)]
72struct DroppableGPUDevice {
73    #[no_trace]
74    channel: WebGPU,
75    #[no_trace]
76    device: WebGPUDevice,
77}
78
79impl Drop for DroppableGPUDevice {
80    fn drop(&mut self) {
81        if let Err(e) = self
82            .channel
83            .0
84            .send(WebGPURequest::DropDevice(self.device.0))
85        {
86            warn!("Failed to send DropDevice ({:?}) ({})", self.device.0, e);
87        }
88    }
89}
90
91#[dom_struct]
92pub(crate) struct GPUDevice {
93    eventtarget: EventTarget,
94    adapter: Dom<GPUAdapter>,
95    #[ignore_malloc_size_of = "mozjs"]
96    extensions: Heap<*mut JSObject>,
97    features: Dom<GPUSupportedFeatures>,
98    limits: Dom<GPUSupportedLimits>,
99    adapter_info: Dom<GPUAdapterInfo>,
100    label: DomRefCell<USVString>,
101    default_queue: Dom<GPUQueue>,
102    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-lost>
103    #[conditional_malloc_size_of]
104    lost_promise: DomRefCell<Rc<Promise>>,
105    valid: Cell<bool>,
106    droppable: DroppableGPUDevice,
107}
108
109pub(crate) enum PipelineLayout {
110    Implicit,
111    Explicit(PipelineLayoutId),
112}
113
114impl PipelineLayout {
115    pub(crate) fn explicit(&self) -> Option<PipelineLayoutId> {
116        match self {
117            PipelineLayout::Explicit(layout_id) => Some(*layout_id),
118            PipelineLayout::Implicit => None,
119        }
120    }
121}
122
123impl GPUDevice {
124    #[allow(clippy::too_many_arguments)]
125    fn new_inherited(
126        channel: WebGPU,
127        adapter: &GPUAdapter,
128        features: &GPUSupportedFeatures,
129        limits: &GPUSupportedLimits,
130        adapter_info: &GPUAdapterInfo,
131        device: WebGPUDevice,
132        queue: &GPUQueue,
133        label: String,
134        lost_promise: Rc<Promise>,
135    ) -> Self {
136        Self {
137            eventtarget: EventTarget::new_inherited(),
138            adapter: Dom::from_ref(adapter),
139            extensions: Heap::default(),
140            features: Dom::from_ref(features),
141            limits: Dom::from_ref(limits),
142            adapter_info: Dom::from_ref(adapter_info),
143            label: DomRefCell::new(USVString::from(label)),
144            default_queue: Dom::from_ref(queue),
145            lost_promise: DomRefCell::new(lost_promise),
146            valid: Cell::new(true),
147            droppable: DroppableGPUDevice { channel, device },
148        }
149    }
150
151    #[allow(clippy::too_many_arguments)]
152    pub(crate) fn new(
153        global: &GlobalScope,
154        channel: WebGPU,
155        adapter: &GPUAdapter,
156        extensions: HandleObject,
157        features: wgpu_types::Features,
158        limits: wgpu_types::Limits,
159        device: WebGPUDevice,
160        queue: WebGPUQueue,
161        label: String,
162        can_gc: CanGc,
163    ) -> DomRoot<Self> {
164        let queue = GPUQueue::new(global, channel.clone(), queue, can_gc);
165        let limits = GPUSupportedLimits::new(global, limits, can_gc);
166        let features = GPUSupportedFeatures::Constructor(global, None, features, can_gc).unwrap();
167        let adapter_info = GPUAdapterInfo::clone_from(global, &adapter.Info(), can_gc);
168        let lost_promise = Promise::new(global, can_gc);
169        let device = reflect_dom_object(
170            Box::new(GPUDevice::new_inherited(
171                channel,
172                adapter,
173                &features,
174                &limits,
175                &adapter_info,
176                device,
177                &queue,
178                label,
179                lost_promise,
180            )),
181            global,
182            can_gc,
183        );
184        queue.set_device(&device);
185        device.extensions.set(*extensions);
186        device
187    }
188}
189
190impl GPUDevice {
191    pub(crate) fn id(&self) -> WebGPUDevice {
192        self.droppable.device
193    }
194
195    pub(crate) fn queue_id(&self) -> WebGPUQueue {
196        self.default_queue.id()
197    }
198
199    pub(crate) fn channel(&self) -> WebGPU {
200        self.droppable.channel.clone()
201    }
202
203    pub(crate) fn dispatch_error(&self, error: webgpu_traits::Error) {
204        if let Err(e) = self.droppable.channel.0.send(WebGPURequest::DispatchError {
205            device_id: self.id().0,
206            error,
207        }) {
208            warn!("Failed to send WebGPURequest::DispatchError due to {e:?}");
209        }
210    }
211
212    /// <https://gpuweb.github.io/gpuweb/#eventdef-gpudevice-uncapturederror>
213    pub(crate) fn fire_uncaptured_error(&self, error: webgpu_traits::Error) {
214        let this = Trusted::new(self);
215
216        // Queue a global task, using the webgpu task source, to fire an event named
217        // uncapturederror at a GPUDevice using GPUUncapturedErrorEvent.
218        self.global().task_manager().webgpu_task_source().queue(
219            task!(fire_uncaptured_error: move || {
220                let this = this.root();
221                let error = GPUError::from_error(&this.global(), error, CanGc::deprecated_note());
222
223                let event = GPUUncapturedErrorEvent::new(
224                    &this.global(),
225                    atom!("uncapturederror"),
226                    &GPUUncapturedErrorEventInit {
227                        error,
228                        parent: EventInit::empty(),
229                    },
230                    CanGc::deprecated_note(),
231                );
232
233                event.upcast::<Event>().fire(this.upcast(), CanGc::deprecated_note());
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                            // TODO(sagudev): these need webidl sync
354                            depth_write_enabled: Some(dss_desc.depthWriteEnabled),
355                            depth_compare: Some(dss_desc.depthCompare.convert()),
356                            stencil: wgpu_types::StencilState {
357                                front: wgpu_types::StencilFaceState {
358                                    compare: dss_desc.stencilFront.compare.convert(),
359
360                                    fail_op: dss_desc.stencilFront.failOp.convert(),
361                                    depth_fail_op: dss_desc.stencilFront.depthFailOp.convert(),
362                                    pass_op: dss_desc.stencilFront.passOp.convert(),
363                                },
364                                back: wgpu_types::StencilFaceState {
365                                    compare: dss_desc.stencilBack.compare.convert(),
366                                    fail_op: dss_desc.stencilBack.failOp.convert(),
367                                    depth_fail_op: dss_desc.stencilBack.depthFailOp.convert(),
368                                    pass_op: dss_desc.stencilBack.passOp.convert(),
369                                },
370                                read_mask: dss_desc.stencilReadMask,
371                                write_mask: dss_desc.stencilWriteMask,
372                            },
373                            bias: wgpu_types::DepthBiasState {
374                                constant: dss_desc.depthBias,
375                                slope_scale: *dss_desc.depthBiasSlopeScale,
376                                clamp: *dss_desc.depthBiasClamp,
377                            },
378                        })
379                })
380                .transpose()?,
381            multisample: wgpu_types::MultisampleState {
382                count: descriptor.multisample.count,
383                mask: descriptor.multisample.mask as u64,
384                alpha_to_coverage_enabled: descriptor.multisample.alphaToCoverageEnabled,
385            },
386            multiview_mask: None,
387        };
388        Ok(desc)
389    }
390
391    /// <https://gpuweb.github.io/gpuweb/#lose-the-device>
392    pub(crate) fn lose(&self, reason: GPUDeviceLostReason, msg: String) {
393        let this = Trusted::new(self);
394
395        // Queue a global task, using the webgpu task source, to resolve device.lost
396        // promise with a new GPUDeviceLostInfo with reason and message.
397        self.global().task_manager().webgpu_task_source().queue(
398            task!(resolve_device_lost: move || {
399                let this = this.root();
400
401                let lost_promise = &(*this.lost_promise.borrow());
402                let lost = GPUDeviceLostInfo::new(&this.global(), msg.into(), reason, CanGc::deprecated_note());
403                lost_promise.resolve_native(&*lost, CanGc::deprecated_note());
404            }),
405        );
406    }
407}
408
409impl GPUDeviceMethods<crate::DomTypeHolder> for GPUDevice {
410    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-features>
411    fn Features(&self) -> DomRoot<GPUSupportedFeatures> {
412        DomRoot::from_ref(&self.features)
413    }
414
415    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-limits>
416    fn Limits(&self) -> DomRoot<GPUSupportedLimits> {
417        DomRoot::from_ref(&self.limits)
418    }
419
420    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-adapterinfo>
421    fn AdapterInfo(&self) -> DomRoot<GPUAdapterInfo> {
422        DomRoot::from_ref(&self.adapter_info)
423    }
424
425    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-queue>
426    fn GetQueue(&self) -> DomRoot<GPUQueue> {
427        DomRoot::from_ref(&self.default_queue)
428    }
429
430    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
431    fn Label(&self) -> USVString {
432        self.label.borrow().clone()
433    }
434
435    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
436    fn SetLabel(&self, value: USVString) {
437        *self.label.borrow_mut() = value;
438    }
439
440    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-lost>
441    fn Lost(&self) -> Rc<Promise> {
442        self.lost_promise.borrow().clone()
443    }
444
445    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createbuffer>
446    fn CreateBuffer(&self, descriptor: &GPUBufferDescriptor) -> Fallible<DomRoot<GPUBuffer>> {
447        GPUBuffer::create(self, descriptor, CanGc::deprecated_note())
448    }
449
450    /// <https://gpuweb.github.io/gpuweb/#GPUDevice-createBindGroupLayout>
451    fn CreateBindGroupLayout(
452        &self,
453        descriptor: &GPUBindGroupLayoutDescriptor,
454    ) -> Fallible<DomRoot<GPUBindGroupLayout>> {
455        GPUBindGroupLayout::create(self, descriptor, CanGc::deprecated_note())
456    }
457
458    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createpipelinelayout>
459    fn CreatePipelineLayout(
460        &self,
461        descriptor: &GPUPipelineLayoutDescriptor,
462    ) -> DomRoot<GPUPipelineLayout> {
463        GPUPipelineLayout::create(self, descriptor, CanGc::deprecated_note())
464    }
465
466    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createbindgroup>
467    fn CreateBindGroup(&self, descriptor: &GPUBindGroupDescriptor) -> DomRoot<GPUBindGroup> {
468        GPUBindGroup::create(self, descriptor, CanGc::deprecated_note())
469    }
470
471    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createshadermodule>
472    fn CreateShaderModule(
473        &self,
474        descriptor: RootedTraceableBox<GPUShaderModuleDescriptor>,
475        comp: InRealm,
476        can_gc: CanGc,
477    ) -> DomRoot<GPUShaderModule> {
478        GPUShaderModule::create(self, descriptor, comp, can_gc)
479    }
480
481    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcomputepipeline>
482    fn CreateComputePipeline(
483        &self,
484        descriptor: &GPUComputePipelineDescriptor,
485    ) -> DomRoot<GPUComputePipeline> {
486        let compute_pipeline = GPUComputePipeline::create(self, descriptor, None);
487        GPUComputePipeline::new(
488            &self.global(),
489            compute_pipeline,
490            descriptor.parent.parent.label.clone(),
491            self,
492            CanGc::deprecated_note(),
493        )
494    }
495
496    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcomputepipelineasync>
497    fn CreateComputePipelineAsync(
498        &self,
499        descriptor: &GPUComputePipelineDescriptor,
500        comp: InRealm,
501        can_gc: CanGc,
502    ) -> Rc<Promise> {
503        let promise = Promise::new_in_current_realm(comp, can_gc);
504        let callback = callback_promise(
505            &promise,
506            self,
507            self.global().task_manager().dom_manipulation_task_source(),
508        );
509        GPUComputePipeline::create(self, descriptor, Some(callback));
510        promise
511    }
512
513    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcommandencoder>
514    fn CreateCommandEncoder(
515        &self,
516        descriptor: &GPUCommandEncoderDescriptor,
517    ) -> DomRoot<GPUCommandEncoder> {
518        GPUCommandEncoder::create(self, descriptor, CanGc::deprecated_note())
519    }
520
521    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createtexture>
522    fn CreateTexture(&self, descriptor: &GPUTextureDescriptor) -> Fallible<DomRoot<GPUTexture>> {
523        GPUTexture::create(self, descriptor, CanGc::deprecated_note())
524    }
525
526    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createsampler>
527    fn CreateSampler(&self, descriptor: &GPUSamplerDescriptor) -> DomRoot<GPUSampler> {
528        GPUSampler::create(self, descriptor, CanGc::deprecated_note())
529    }
530
531    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createrenderpipeline>
532    fn CreateRenderPipeline(
533        &self,
534        descriptor: &GPURenderPipelineDescriptor,
535    ) -> Fallible<DomRoot<GPURenderPipeline>> {
536        let desc = self.parse_render_pipeline(descriptor)?;
537        let render_pipeline = GPURenderPipeline::create(self, desc, None)?;
538        Ok(GPURenderPipeline::new(
539            &self.global(),
540            render_pipeline,
541            descriptor.parent.parent.label.clone(),
542            self,
543            CanGc::deprecated_note(),
544        ))
545    }
546
547    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createrenderpipelineasync>
548    fn CreateRenderPipelineAsync(
549        &self,
550        descriptor: &GPURenderPipelineDescriptor,
551        comp: InRealm,
552        can_gc: CanGc,
553    ) -> Fallible<Rc<Promise>> {
554        let desc = self.parse_render_pipeline(descriptor)?;
555        let promise = Promise::new_in_current_realm(comp, can_gc);
556        let callback = callback_promise(
557            &promise,
558            self,
559            self.global().task_manager().dom_manipulation_task_source(),
560        );
561        GPURenderPipeline::create(self, desc, Some(callback))?;
562        Ok(promise)
563    }
564
565    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createrenderbundleencoder>
566    fn CreateRenderBundleEncoder(
567        &self,
568        descriptor: &GPURenderBundleEncoderDescriptor,
569    ) -> Fallible<DomRoot<GPURenderBundleEncoder>> {
570        GPURenderBundleEncoder::create(self, descriptor, CanGc::deprecated_note())
571    }
572
573    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-pusherrorscope>
574    fn PushErrorScope(&self, filter: GPUErrorFilter) {
575        if self
576            .droppable
577            .channel
578            .0
579            .send(WebGPURequest::PushErrorScope {
580                device_id: self.id().0,
581                filter: filter.as_webgpu(),
582            })
583            .is_err()
584        {
585            warn!("Failed sending WebGPURequest::PushErrorScope");
586        }
587    }
588
589    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-poperrorscope>
590    fn PopErrorScope(&self, comp: InRealm, can_gc: CanGc) -> Rc<Promise> {
591        let promise = Promise::new_in_current_realm(comp, can_gc);
592        let callback = callback_promise(
593            &promise,
594            self,
595            self.global().task_manager().dom_manipulation_task_source(),
596        );
597        if self
598            .droppable
599            .channel
600            .0
601            .send(WebGPURequest::PopErrorScope {
602                device_id: self.id().0,
603                callback,
604            })
605            .is_err()
606        {
607            warn!("Error when sending WebGPURequest::PopErrorScope");
608        }
609        promise
610    }
611
612    // https://gpuweb.github.io/gpuweb/#dom-gpudevice-onuncapturederror
613    event_handler!(uncapturederror, GetOnuncapturederror, SetOnuncapturederror);
614
615    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-destroy>
616    fn Destroy(&self) {
617        if self.valid.get() {
618            self.valid.set(false);
619
620            if let Err(e) = self
621                .droppable
622                .channel
623                .0
624                .send(WebGPURequest::DestroyDevice(self.id().0))
625            {
626                warn!("Failed to send DestroyDevice ({:?}) ({})", self.id().0, e);
627            }
628        }
629    }
630}
631
632impl RoutedPromiseListener<WebGPUPoppedErrorScopeResponse> for GPUDevice {
633    fn handle_response(
634        &self,
635        cx: &mut js::context::JSContext,
636        response: WebGPUPoppedErrorScopeResponse,
637        promise: &Rc<Promise>,
638    ) {
639        match response {
640            Ok(None) | Err(PopError::Lost) => {
641                promise.resolve_native(&None::<Option<GPUError>>, CanGc::from_cx(cx))
642            },
643            Err(PopError::Empty) => {
644                promise.reject_error(Error::Operation(None), CanGc::from_cx(cx))
645            },
646            Ok(Some(error)) => {
647                let error = GPUError::from_error(&self.global(), error, CanGc::from_cx(cx));
648                promise.resolve_native(&error, CanGc::from_cx(cx));
649            },
650        }
651    }
652}
653
654impl RoutedPromiseListener<WebGPUComputePipelineResponse> for GPUDevice {
655    fn handle_response(
656        &self,
657        cx: &mut js::context::JSContext,
658        response: WebGPUComputePipelineResponse,
659        promise: &Rc<Promise>,
660    ) {
661        match response {
662            Ok(pipeline) => promise.resolve_native(
663                &GPUComputePipeline::new(
664                    &self.global(),
665                    WebGPUComputePipeline(pipeline.id),
666                    pipeline.label.into(),
667                    self,
668                    CanGc::from_cx(cx),
669                ),
670                CanGc::from_cx(cx),
671            ),
672            Err(webgpu_traits::Error::Validation(msg)) => promise.reject_native(
673                &GPUPipelineError::new(
674                    &self.global(),
675                    msg.into(),
676                    GPUPipelineErrorReason::Validation,
677                    CanGc::from_cx(cx),
678                ),
679                CanGc::from_cx(cx),
680            ),
681            Err(webgpu_traits::Error::OutOfMemory(msg) | webgpu_traits::Error::Internal(msg)) => {
682                promise.reject_native(
683                    &GPUPipelineError::new(
684                        &self.global(),
685                        msg.into(),
686                        GPUPipelineErrorReason::Internal,
687                        CanGc::from_cx(cx),
688                    ),
689                    CanGc::from_cx(cx),
690                )
691            },
692        }
693    }
694}
695
696impl RoutedPromiseListener<WebGPURenderPipelineResponse> for GPUDevice {
697    fn handle_response(
698        &self,
699        cx: &mut js::context::JSContext,
700        response: WebGPURenderPipelineResponse,
701        promise: &Rc<Promise>,
702    ) {
703        match response {
704            Ok(pipeline) => promise.resolve_native(
705                &GPURenderPipeline::new(
706                    &self.global(),
707                    WebGPURenderPipeline(pipeline.id),
708                    pipeline.label.into(),
709                    self,
710                    CanGc::from_cx(cx),
711                ),
712                CanGc::from_cx(cx),
713            ),
714            Err(webgpu_traits::Error::Validation(msg)) => promise.reject_native(
715                &GPUPipelineError::new(
716                    &self.global(),
717                    msg.into(),
718                    GPUPipelineErrorReason::Validation,
719                    CanGc::from_cx(cx),
720                ),
721                CanGc::from_cx(cx),
722            ),
723            Err(webgpu_traits::Error::OutOfMemory(msg) | webgpu_traits::Error::Internal(msg)) => {
724                promise.reject_native(
725                    &GPUPipelineError::new(
726                        &self.global(),
727                        msg.into(),
728                        GPUPipelineErrorReason::Internal,
729                        CanGc::from_cx(cx),
730                    ),
731                    CanGc::from_cx(cx),
732                )
733            },
734        }
735    }
736}