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 |cx| {
220                let this = this.root();
221                let error = GPUError::from_error(&this.global(), error, CanGc::from_cx(cx));
222
223                let event = GPUUncapturedErrorEvent::new(
224                    &this.global(),
225                    atom!("uncapturederror"),
226                    &GPUUncapturedErrorEventInit {
227                        error,
228                        parent: EventInit::empty(),
229                    },
230                    CanGc::from_cx(cx),
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(&self, descriptor: &GPUBufferDescriptor) -> Fallible<DomRoot<GPUBuffer>> {
446        GPUBuffer::create(self, descriptor, CanGc::deprecated_note())
447    }
448
449    /// <https://gpuweb.github.io/gpuweb/#GPUDevice-createBindGroupLayout>
450    fn CreateBindGroupLayout(
451        &self,
452        descriptor: &GPUBindGroupLayoutDescriptor,
453    ) -> Fallible<DomRoot<GPUBindGroupLayout>> {
454        GPUBindGroupLayout::create(self, descriptor, CanGc::deprecated_note())
455    }
456
457    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createpipelinelayout>
458    fn CreatePipelineLayout(
459        &self,
460        descriptor: &GPUPipelineLayoutDescriptor,
461    ) -> DomRoot<GPUPipelineLayout> {
462        GPUPipelineLayout::create(self, descriptor, CanGc::deprecated_note())
463    }
464
465    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createbindgroup>
466    fn CreateBindGroup(&self, descriptor: &GPUBindGroupDescriptor) -> DomRoot<GPUBindGroup> {
467        GPUBindGroup::create(self, descriptor, CanGc::deprecated_note())
468    }
469
470    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createshadermodule>
471    fn CreateShaderModule(
472        &self,
473        descriptor: RootedTraceableBox<GPUShaderModuleDescriptor>,
474        comp: InRealm,
475        can_gc: CanGc,
476    ) -> DomRoot<GPUShaderModule> {
477        GPUShaderModule::create(self, descriptor, comp, can_gc)
478    }
479
480    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcomputepipeline>
481    fn CreateComputePipeline(
482        &self,
483        descriptor: &GPUComputePipelineDescriptor,
484    ) -> DomRoot<GPUComputePipeline> {
485        let compute_pipeline = GPUComputePipeline::create(self, descriptor, None);
486        GPUComputePipeline::new(
487            &self.global(),
488            compute_pipeline,
489            descriptor.parent.parent.label.clone(),
490            self,
491            CanGc::deprecated_note(),
492        )
493    }
494
495    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcomputepipelineasync>
496    fn CreateComputePipelineAsync(
497        &self,
498        descriptor: &GPUComputePipelineDescriptor,
499        comp: InRealm,
500        can_gc: CanGc,
501    ) -> Rc<Promise> {
502        let promise = Promise::new_in_current_realm(comp, can_gc);
503        let callback = callback_promise(
504            &promise,
505            self,
506            self.global().task_manager().dom_manipulation_task_source(),
507        );
508        GPUComputePipeline::create(self, descriptor, Some(callback));
509        promise
510    }
511
512    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcommandencoder>
513    fn CreateCommandEncoder(
514        &self,
515        descriptor: &GPUCommandEncoderDescriptor,
516    ) -> DomRoot<GPUCommandEncoder> {
517        GPUCommandEncoder::create(self, descriptor, CanGc::deprecated_note())
518    }
519
520    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createtexture>
521    fn CreateTexture(&self, descriptor: &GPUTextureDescriptor) -> Fallible<DomRoot<GPUTexture>> {
522        GPUTexture::create(self, descriptor, CanGc::deprecated_note())
523    }
524
525    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createsampler>
526    fn CreateSampler(&self, descriptor: &GPUSamplerDescriptor) -> DomRoot<GPUSampler> {
527        GPUSampler::create(self, descriptor, CanGc::deprecated_note())
528    }
529
530    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createrenderpipeline>
531    fn CreateRenderPipeline(
532        &self,
533        descriptor: &GPURenderPipelineDescriptor,
534    ) -> Fallible<DomRoot<GPURenderPipeline>> {
535        let desc = self.parse_render_pipeline(descriptor)?;
536        let render_pipeline = GPURenderPipeline::create(self, desc, None)?;
537        Ok(GPURenderPipeline::new(
538            &self.global(),
539            render_pipeline,
540            descriptor.parent.parent.label.clone(),
541            self,
542            CanGc::deprecated_note(),
543        ))
544    }
545
546    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createrenderpipelineasync>
547    fn CreateRenderPipelineAsync(
548        &self,
549        descriptor: &GPURenderPipelineDescriptor,
550        comp: InRealm,
551        can_gc: CanGc,
552    ) -> Fallible<Rc<Promise>> {
553        let desc = self.parse_render_pipeline(descriptor)?;
554        let promise = Promise::new_in_current_realm(comp, can_gc);
555        let callback = callback_promise(
556            &promise,
557            self,
558            self.global().task_manager().dom_manipulation_task_source(),
559        );
560        GPURenderPipeline::create(self, desc, Some(callback))?;
561        Ok(promise)
562    }
563
564    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createrenderbundleencoder>
565    fn CreateRenderBundleEncoder(
566        &self,
567        descriptor: &GPURenderBundleEncoderDescriptor,
568    ) -> Fallible<DomRoot<GPURenderBundleEncoder>> {
569        GPURenderBundleEncoder::create(self, descriptor, CanGc::deprecated_note())
570    }
571
572    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-pusherrorscope>
573    fn PushErrorScope(&self, filter: GPUErrorFilter) {
574        if self
575            .droppable
576            .channel
577            .0
578            .send(WebGPURequest::PushErrorScope {
579                device_id: self.id().0,
580                filter: filter.as_webgpu(),
581            })
582            .is_err()
583        {
584            warn!("Failed sending WebGPURequest::PushErrorScope");
585        }
586    }
587
588    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-poperrorscope>
589    fn PopErrorScope(&self, comp: InRealm, can_gc: CanGc) -> Rc<Promise> {
590        let promise = Promise::new_in_current_realm(comp, can_gc);
591        let callback = callback_promise(
592            &promise,
593            self,
594            self.global().task_manager().dom_manipulation_task_source(),
595        );
596        if self
597            .droppable
598            .channel
599            .0
600            .send(WebGPURequest::PopErrorScope {
601                device_id: self.id().0,
602                callback,
603            })
604            .is_err()
605        {
606            warn!("Error when sending WebGPURequest::PopErrorScope");
607        }
608        promise
609    }
610
611    // https://gpuweb.github.io/gpuweb/#dom-gpudevice-onuncapturederror
612    event_handler!(uncapturederror, GetOnuncapturederror, SetOnuncapturederror);
613
614    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-destroy>
615    fn Destroy(&self) {
616        if self.valid.get() {
617            self.valid.set(false);
618
619            if let Err(e) = self
620                .droppable
621                .channel
622                .0
623                .send(WebGPURequest::DestroyDevice(self.id().0))
624            {
625                warn!("Failed to send DestroyDevice ({:?}) ({})", self.id().0, e);
626            }
627        }
628    }
629}
630
631impl RoutedPromiseListener<WebGPUPoppedErrorScopeResponse> for GPUDevice {
632    fn handle_response(
633        &self,
634        cx: &mut js::context::JSContext,
635        response: WebGPUPoppedErrorScopeResponse,
636        promise: &Rc<Promise>,
637    ) {
638        match response {
639            Ok(None) | Err(PopError::Lost) => {
640                promise.resolve_native(&None::<Option<GPUError>>, CanGc::from_cx(cx))
641            },
642            Err(PopError::Empty) => promise.reject_error_with_cx(cx, Error::Operation(None)),
643            Ok(Some(error)) => {
644                let error = GPUError::from_error(&self.global(), error, CanGc::from_cx(cx));
645                promise.resolve_native_with_cx(cx, &error);
646            },
647        }
648    }
649}
650
651impl RoutedPromiseListener<WebGPUComputePipelineResponse> for GPUDevice {
652    fn handle_response(
653        &self,
654        cx: &mut js::context::JSContext,
655        response: WebGPUComputePipelineResponse,
656        promise: &Rc<Promise>,
657    ) {
658        match response {
659            Ok(pipeline) => promise.resolve_native(
660                &GPUComputePipeline::new(
661                    &self.global(),
662                    WebGPUComputePipeline(pipeline.id),
663                    pipeline.label.into(),
664                    self,
665                    CanGc::from_cx(cx),
666                ),
667                CanGc::from_cx(cx),
668            ),
669            Err(webgpu_traits::Error::Validation(msg)) => promise.reject_native(
670                &GPUPipelineError::new(
671                    &self.global(),
672                    msg.into(),
673                    GPUPipelineErrorReason::Validation,
674                    CanGc::from_cx(cx),
675                ),
676                CanGc::from_cx(cx),
677            ),
678            Err(webgpu_traits::Error::OutOfMemory(msg) | webgpu_traits::Error::Internal(msg)) => {
679                promise.reject_native(
680                    &GPUPipelineError::new(
681                        &self.global(),
682                        msg.into(),
683                        GPUPipelineErrorReason::Internal,
684                        CanGc::from_cx(cx),
685                    ),
686                    CanGc::from_cx(cx),
687                )
688            },
689        }
690    }
691}
692
693impl RoutedPromiseListener<WebGPURenderPipelineResponse> for GPUDevice {
694    fn handle_response(
695        &self,
696        cx: &mut js::context::JSContext,
697        response: WebGPURenderPipelineResponse,
698        promise: &Rc<Promise>,
699    ) {
700        match response {
701            Ok(pipeline) => promise.resolve_native(
702                &GPURenderPipeline::new(
703                    &self.global(),
704                    WebGPURenderPipeline(pipeline.id),
705                    pipeline.label.into(),
706                    self,
707                    CanGc::from_cx(cx),
708                ),
709                CanGc::from_cx(cx),
710            ),
711            Err(webgpu_traits::Error::Validation(msg)) => promise.reject_native(
712                &GPUPipelineError::new(
713                    &self.global(),
714                    msg.into(),
715                    GPUPipelineErrorReason::Validation,
716                    CanGc::from_cx(cx),
717                ),
718                CanGc::from_cx(cx),
719            ),
720            Err(webgpu_traits::Error::OutOfMemory(msg) | webgpu_traits::Error::Internal(msg)) => {
721                promise.reject_native(
722                    &GPUPipelineError::new(
723                        &self.global(),
724                        msg.into(),
725                        GPUPipelineErrorReason::Internal,
726                        CanGc::from_cx(cx),
727                    ),
728                    CanGc::from_cx(cx),
729                )
730            },
731        }
732    }
733}