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