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 webgpu_traits::{
12    PopError, WebGPU, WebGPUComputePipeline, WebGPUComputePipelineResponse, WebGPUDevice,
13    WebGPUPoppedErrorScopeResponse, WebGPUQueue, WebGPURenderPipeline,
14    WebGPURenderPipelineResponse, WebGPURequest,
15};
16use wgpu_core::id::{BindGroupLayoutId, PipelineLayoutId};
17use wgpu_core::pipeline as wgpu_pipe;
18use wgpu_core::pipeline::RenderPipelineDescriptor;
19use wgpu_types::{self, TextureFormat};
20
21use super::gpudevicelostinfo::GPUDeviceLostInfo;
22use super::gpuerror::AsWebGpu;
23use super::gpupipelineerror::GPUPipelineError;
24use super::gpusupportedlimits::GPUSupportedLimits;
25use crate::conversions::Convert;
26use crate::dom::bindings::cell::DomRefCell;
27use crate::dom::bindings::codegen::Bindings::EventBinding::EventInit;
28use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
29    GPUAdapterMethods, GPUBindGroupDescriptor, GPUBindGroupLayoutDescriptor, GPUBufferDescriptor,
30    GPUCommandEncoderDescriptor, GPUComputePipelineDescriptor, GPUDeviceLostReason,
31    GPUDeviceMethods, GPUErrorFilter, GPUPipelineErrorReason, GPUPipelineLayoutDescriptor,
32    GPURenderBundleEncoderDescriptor, GPURenderPipelineDescriptor, GPUSamplerDescriptor,
33    GPUShaderModuleDescriptor, GPUSupportedLimitsMethods, GPUTextureDescriptor, GPUTextureFormat,
34    GPUUncapturedErrorEventInit, GPUVertexStepMode,
35};
36use crate::dom::bindings::codegen::UnionTypes::GPUPipelineLayoutOrGPUAutoLayoutMode;
37use crate::dom::bindings::error::{Error, Fallible};
38use crate::dom::bindings::inheritance::Castable;
39use crate::dom::bindings::refcounted::Trusted;
40use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
41use crate::dom::bindings::root::{Dom, DomRoot};
42use crate::dom::bindings::str::{DOMString, USVString};
43use crate::dom::bindings::trace::RootedTraceableBox;
44use crate::dom::event::Event;
45use crate::dom::eventtarget::EventTarget;
46use crate::dom::globalscope::GlobalScope;
47use crate::dom::promise::Promise;
48use crate::dom::types::GPUError;
49use crate::dom::webgpu::gpuadapter::GPUAdapter;
50use crate::dom::webgpu::gpuadapterinfo::GPUAdapterInfo;
51use crate::dom::webgpu::gpubindgroup::GPUBindGroup;
52use crate::dom::webgpu::gpubindgrouplayout::GPUBindGroupLayout;
53use crate::dom::webgpu::gpubuffer::GPUBuffer;
54use crate::dom::webgpu::gpucommandencoder::GPUCommandEncoder;
55use crate::dom::webgpu::gpucomputepipeline::GPUComputePipeline;
56use crate::dom::webgpu::gpupipelinelayout::GPUPipelineLayout;
57use crate::dom::webgpu::gpuqueue::GPUQueue;
58use crate::dom::webgpu::gpurenderbundleencoder::GPURenderBundleEncoder;
59use crate::dom::webgpu::gpurenderpipeline::GPURenderPipeline;
60use crate::dom::webgpu::gpusampler::GPUSampler;
61use crate::dom::webgpu::gpushadermodule::GPUShaderModule;
62use crate::dom::webgpu::gpusupportedfeatures::GPUSupportedFeatures;
63use crate::dom::webgpu::gputexture::GPUTexture;
64use crate::dom::webgpu::gpuuncapturederrorevent::GPUUncapturedErrorEvent;
65use crate::realms::InRealm;
66use crate::routed_promise::{RoutedPromiseListener, route_promise};
67use crate::script_runtime::CanGc;
68
69#[dom_struct]
70pub(crate) struct GPUDevice {
71    eventtarget: EventTarget,
72    #[ignore_malloc_size_of = "channels are hard"]
73    #[no_trace]
74    channel: WebGPU,
75    adapter: Dom<GPUAdapter>,
76    #[ignore_malloc_size_of = "mozjs"]
77    extensions: Heap<*mut JSObject>,
78    features: Dom<GPUSupportedFeatures>,
79    limits: Dom<GPUSupportedLimits>,
80    adapter_info: Dom<GPUAdapterInfo>,
81    label: DomRefCell<USVString>,
82    #[no_trace]
83    device: WebGPUDevice,
84    default_queue: Dom<GPUQueue>,
85    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-lost>
86    #[conditional_malloc_size_of]
87    lost_promise: DomRefCell<Rc<Promise>>,
88    valid: Cell<bool>,
89}
90
91pub(crate) enum PipelineLayout {
92    Implicit(PipelineLayoutId, Vec<BindGroupLayoutId>),
93    Explicit(PipelineLayoutId),
94}
95
96impl PipelineLayout {
97    pub(crate) fn explicit(&self) -> Option<PipelineLayoutId> {
98        match self {
99            PipelineLayout::Explicit(layout_id) => Some(*layout_id),
100            _ => None,
101        }
102    }
103
104    pub(crate) fn implicit(self) -> Option<(PipelineLayoutId, Vec<BindGroupLayoutId>)> {
105        match self {
106            PipelineLayout::Implicit(layout_id, bind_group_layout_ids) => {
107                Some((layout_id, bind_group_layout_ids))
108            },
109            _ => None,
110        }
111    }
112}
113
114impl GPUDevice {
115    #[allow(clippy::too_many_arguments)]
116    fn new_inherited(
117        channel: WebGPU,
118        adapter: &GPUAdapter,
119        features: &GPUSupportedFeatures,
120        limits: &GPUSupportedLimits,
121        adapter_info: &GPUAdapterInfo,
122        device: WebGPUDevice,
123        queue: &GPUQueue,
124        label: String,
125        lost_promise: Rc<Promise>,
126    ) -> Self {
127        Self {
128            eventtarget: EventTarget::new_inherited(),
129            channel,
130            adapter: Dom::from_ref(adapter),
131            extensions: Heap::default(),
132            features: Dom::from_ref(features),
133            limits: Dom::from_ref(limits),
134            adapter_info: Dom::from_ref(adapter_info),
135            label: DomRefCell::new(USVString::from(label)),
136            device,
137            default_queue: Dom::from_ref(queue),
138            lost_promise: DomRefCell::new(lost_promise),
139            valid: Cell::new(true),
140        }
141    }
142
143    #[allow(clippy::too_many_arguments)]
144    pub(crate) fn new(
145        global: &GlobalScope,
146        channel: WebGPU,
147        adapter: &GPUAdapter,
148        extensions: HandleObject,
149        features: wgpu_types::Features,
150        limits: wgpu_types::Limits,
151        device: WebGPUDevice,
152        queue: WebGPUQueue,
153        label: String,
154        can_gc: CanGc,
155    ) -> DomRoot<Self> {
156        let queue = GPUQueue::new(global, channel.clone(), queue, can_gc);
157        let limits = GPUSupportedLimits::new(global, limits, can_gc);
158        let features = GPUSupportedFeatures::Constructor(global, None, features, can_gc).unwrap();
159        let adapter_info = GPUAdapterInfo::clone_from(global, &adapter.Info(), can_gc);
160        let lost_promise = Promise::new(global, can_gc);
161        let device = reflect_dom_object(
162            Box::new(GPUDevice::new_inherited(
163                channel,
164                adapter,
165                &features,
166                &limits,
167                &adapter_info,
168                device,
169                &queue,
170                label,
171                lost_promise,
172            )),
173            global,
174            can_gc,
175        );
176        queue.set_device(&device);
177        device.extensions.set(*extensions);
178        device
179    }
180}
181
182impl GPUDevice {
183    pub(crate) fn id(&self) -> WebGPUDevice {
184        self.device
185    }
186
187    pub(crate) fn queue_id(&self) -> WebGPUQueue {
188        self.default_queue.id()
189    }
190
191    pub(crate) fn channel(&self) -> WebGPU {
192        self.channel.clone()
193    }
194
195    pub(crate) fn dispatch_error(&self, error: webgpu_traits::Error) {
196        if let Err(e) = self.channel.0.send(WebGPURequest::DispatchError {
197            device_id: self.device.0,
198            error,
199        }) {
200            warn!("Failed to send WebGPURequest::DispatchError due to {e:?}");
201        }
202    }
203
204    /// <https://gpuweb.github.io/gpuweb/#eventdef-gpudevice-uncapturederror>
205    pub(crate) fn fire_uncaptured_error(&self, error: webgpu_traits::Error) {
206        let this = Trusted::new(self);
207
208        // Queue a global task, using the webgpu task source, to fire an event named
209        // uncapturederror at a GPUDevice using GPUUncapturedErrorEvent.
210        self.global().task_manager().webgpu_task_source().queue(
211            task!(fire_uncaptured_error: move || {
212                let this = this.root();
213                let error = GPUError::from_error(&this.global(), error, CanGc::note());
214
215                let event = GPUUncapturedErrorEvent::new(
216                    &this.global(),
217                    DOMString::from("uncapturederror"),
218                    &GPUUncapturedErrorEventInit {
219                        error,
220                        parent: EventInit::empty(),
221                    },
222                    CanGc::note(),
223                );
224
225                event.upcast::<Event>().fire(this.upcast(), CanGc::note());
226            }),
227        );
228    }
229
230    /// <https://gpuweb.github.io/gpuweb/#abstract-opdef-validate-texture-format-required-features>
231    ///
232    /// Validates that the device suppports required features,
233    /// and if so returns an ok containing wgpu's `TextureFormat`
234    pub(crate) fn validate_texture_format_required_features(
235        &self,
236        format: &GPUTextureFormat,
237    ) -> Fallible<TextureFormat> {
238        let texture_format: TextureFormat = (*format).convert();
239        if self
240            .features
241            .wgpu_features()
242            .contains(texture_format.required_features())
243        {
244            Ok(texture_format)
245        } else {
246            Err(Error::Type(format!(
247                "{texture_format:?} is not supported by this GPUDevice"
248            )))
249        }
250    }
251
252    pub(crate) fn is_lost(&self) -> bool {
253        self.lost_promise.borrow().is_fulfilled()
254    }
255
256    pub(crate) fn get_pipeline_layout_data(
257        &self,
258        layout: &GPUPipelineLayoutOrGPUAutoLayoutMode,
259    ) -> PipelineLayout {
260        if let GPUPipelineLayoutOrGPUAutoLayoutMode::GPUPipelineLayout(layout) = layout {
261            PipelineLayout::Explicit(layout.id().0)
262        } else {
263            let layout_id = self.global().wgpu_id_hub().create_pipeline_layout_id();
264            let max_bind_grps = self.limits.MaxBindGroups();
265            let mut bgl_ids = Vec::with_capacity(max_bind_grps as usize);
266            for _ in 0..max_bind_grps {
267                let bgl = self.global().wgpu_id_hub().create_bind_group_layout_id();
268                bgl_ids.push(bgl);
269            }
270            PipelineLayout::Implicit(layout_id, bgl_ids)
271        }
272    }
273
274    pub(crate) fn parse_render_pipeline<'a>(
275        &self,
276        descriptor: &GPURenderPipelineDescriptor,
277    ) -> Fallible<(PipelineLayout, RenderPipelineDescriptor<'a>)> {
278        let pipeline_layout = self.get_pipeline_layout_data(&descriptor.parent.layout);
279
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.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: None,
386        };
387        Ok((pipeline_layout, 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::note());
402                lost_promise.resolve_native(&*lost, CanGc::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::note())
447    }
448
449    /// <https://gpuweb.github.io/gpuweb/#GPUDevice-createBindGroupLayout>
450    #[allow(non_snake_case)]
451    fn CreateBindGroupLayout(
452        &self,
453        descriptor: &GPUBindGroupLayoutDescriptor,
454    ) -> Fallible<DomRoot<GPUBindGroupLayout>> {
455        GPUBindGroupLayout::create(self, descriptor, CanGc::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::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::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::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 sender = route_promise(
505            &promise,
506            self,
507            self.global().task_manager().dom_manipulation_task_source(),
508        );
509        GPUComputePipeline::create(self, descriptor, Some(sender));
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::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::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::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 (pipeline_layout, desc) = self.parse_render_pipeline(descriptor)?;
537        let render_pipeline = GPURenderPipeline::create(self, pipeline_layout, desc, None)?;
538        Ok(GPURenderPipeline::new(
539            &self.global(),
540            render_pipeline,
541            descriptor.parent.parent.label.clone(),
542            self,
543            CanGc::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 (implicit_ids, desc) = self.parse_render_pipeline(descriptor)?;
555        let promise = Promise::new_in_current_realm(comp, can_gc);
556        let sender = route_promise(
557            &promise,
558            self,
559            self.global().task_manager().dom_manipulation_task_source(),
560        );
561        GPURenderPipeline::create(self, implicit_ids, desc, Some(sender))?;
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::note())
571    }
572
573    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-pusherrorscope>
574    fn PushErrorScope(&self, filter: GPUErrorFilter) {
575        if self
576            .channel
577            .0
578            .send(WebGPURequest::PushErrorScope {
579                device_id: self.device.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 sender = route_promise(
592            &promise,
593            self,
594            self.global().task_manager().dom_manipulation_task_source(),
595        );
596        if self
597            .channel
598            .0
599            .send(WebGPURequest::PopErrorScope {
600                device_id: self.device.0,
601                sender,
602            })
603            .is_err()
604        {
605            warn!("Error when sending WebGPURequest::PopErrorScope");
606        }
607        promise
608    }
609
610    // https://gpuweb.github.io/gpuweb/#dom-gpudevice-onuncapturederror
611    event_handler!(uncapturederror, GetOnuncapturederror, SetOnuncapturederror);
612
613    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-destroy>
614    fn Destroy(&self) {
615        if self.valid.get() {
616            self.valid.set(false);
617
618            if let Err(e) = self
619                .channel
620                .0
621                .send(WebGPURequest::DestroyDevice(self.device.0))
622            {
623                warn!("Failed to send DestroyDevice ({:?}) ({})", self.device.0, e);
624            }
625        }
626    }
627}
628
629impl RoutedPromiseListener<WebGPUPoppedErrorScopeResponse> for GPUDevice {
630    fn handle_response(
631        &self,
632        response: WebGPUPoppedErrorScopeResponse,
633        promise: &Rc<Promise>,
634        can_gc: CanGc,
635    ) {
636        match response {
637            Ok(None) | Err(PopError::Lost) => {
638                promise.resolve_native(&None::<Option<GPUError>>, can_gc)
639            },
640            Err(PopError::Empty) => promise.reject_error(Error::Operation(None), can_gc),
641            Ok(Some(error)) => {
642                let error = GPUError::from_error(&self.global(), error, can_gc);
643                promise.resolve_native(&error, can_gc);
644            },
645        }
646    }
647}
648
649impl RoutedPromiseListener<WebGPUComputePipelineResponse> for GPUDevice {
650    fn handle_response(
651        &self,
652        response: WebGPUComputePipelineResponse,
653        promise: &Rc<Promise>,
654        can_gc: CanGc,
655    ) {
656        match response {
657            Ok(pipeline) => promise.resolve_native(
658                &GPUComputePipeline::new(
659                    &self.global(),
660                    WebGPUComputePipeline(pipeline.id),
661                    pipeline.label.into(),
662                    self,
663                    can_gc,
664                ),
665                can_gc,
666            ),
667            Err(webgpu_traits::Error::Validation(msg)) => promise.reject_native(
668                &GPUPipelineError::new(
669                    &self.global(),
670                    msg.into(),
671                    GPUPipelineErrorReason::Validation,
672                    can_gc,
673                ),
674                can_gc,
675            ),
676            Err(webgpu_traits::Error::OutOfMemory(msg) | webgpu_traits::Error::Internal(msg)) => {
677                promise.reject_native(
678                    &GPUPipelineError::new(
679                        &self.global(),
680                        msg.into(),
681                        GPUPipelineErrorReason::Internal,
682                        can_gc,
683                    ),
684                    can_gc,
685                )
686            },
687        }
688    }
689}
690
691impl RoutedPromiseListener<WebGPURenderPipelineResponse> for GPUDevice {
692    fn handle_response(
693        &self,
694        response: WebGPURenderPipelineResponse,
695        promise: &Rc<Promise>,
696        can_gc: CanGc,
697    ) {
698        match response {
699            Ok(pipeline) => promise.resolve_native(
700                &GPURenderPipeline::new(
701                    &self.global(),
702                    WebGPURenderPipeline(pipeline.id),
703                    pipeline.label.into(),
704                    self,
705                    can_gc,
706                ),
707                can_gc,
708            ),
709            Err(webgpu_traits::Error::Validation(msg)) => promise.reject_native(
710                &GPUPipelineError::new(
711                    &self.global(),
712                    msg.into(),
713                    GPUPipelineErrorReason::Validation,
714                    can_gc,
715                ),
716                can_gc,
717            ),
718            Err(webgpu_traits::Error::OutOfMemory(msg) | webgpu_traits::Error::Internal(msg)) => {
719                promise.reject_native(
720                    &GPUPipelineError::new(
721                        &self.global(),
722                        msg.into(),
723                        GPUPipelineErrorReason::Internal,
724                        can_gc,
725                    ),
726                    can_gc,
727                )
728            },
729        }
730    }
731}
732
733impl Drop for GPUDevice {
734    fn drop(&mut self) {
735        if let Err(e) = self
736            .channel
737            .0
738            .send(WebGPURequest::DropDevice(self.device.0))
739        {
740            warn!("Failed to send DropDevice ({:?}) ({})", self.device.0, e);
741        }
742    }
743}