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