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, callback_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    fn CreateBindGroupLayout(
451        &self,
452        descriptor: &GPUBindGroupLayoutDescriptor,
453    ) -> Fallible<DomRoot<GPUBindGroupLayout>> {
454        GPUBindGroupLayout::create(self, descriptor, CanGc::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::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::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::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::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::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::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 (pipeline_layout, desc) = self.parse_render_pipeline(descriptor)?;
536        let render_pipeline = GPURenderPipeline::create(self, pipeline_layout, desc, None)?;
537        Ok(GPURenderPipeline::new(
538            &self.global(),
539            render_pipeline,
540            descriptor.parent.parent.label.clone(),
541            self,
542            CanGc::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 (implicit_ids, 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, implicit_ids, 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::note())
570    }
571
572    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-pusherrorscope>
573    fn PushErrorScope(&self, filter: GPUErrorFilter) {
574        if self
575            .channel
576            .0
577            .send(WebGPURequest::PushErrorScope {
578                device_id: self.device.0,
579                filter: filter.as_webgpu(),
580            })
581            .is_err()
582        {
583            warn!("Failed sending WebGPURequest::PushErrorScope");
584        }
585    }
586
587    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-poperrorscope>
588    fn PopErrorScope(&self, comp: InRealm, can_gc: CanGc) -> Rc<Promise> {
589        let promise = Promise::new_in_current_realm(comp, can_gc);
590        let callback = callback_promise(
591            &promise,
592            self,
593            self.global().task_manager().dom_manipulation_task_source(),
594        );
595        if self
596            .channel
597            .0
598            .send(WebGPURequest::PopErrorScope {
599                device_id: self.device.0,
600                callback,
601            })
602            .is_err()
603        {
604            warn!("Error when sending WebGPURequest::PopErrorScope");
605        }
606        promise
607    }
608
609    // https://gpuweb.github.io/gpuweb/#dom-gpudevice-onuncapturederror
610    event_handler!(uncapturederror, GetOnuncapturederror, SetOnuncapturederror);
611
612    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-destroy>
613    fn Destroy(&self) {
614        if self.valid.get() {
615            self.valid.set(false);
616
617            if let Err(e) = self
618                .channel
619                .0
620                .send(WebGPURequest::DestroyDevice(self.device.0))
621            {
622                warn!("Failed to send DestroyDevice ({:?}) ({})", self.device.0, e);
623            }
624        }
625    }
626}
627
628impl RoutedPromiseListener<WebGPUPoppedErrorScopeResponse> for GPUDevice {
629    fn handle_response(
630        &self,
631        response: WebGPUPoppedErrorScopeResponse,
632        promise: &Rc<Promise>,
633        can_gc: CanGc,
634    ) {
635        match response {
636            Ok(None) | Err(PopError::Lost) => {
637                promise.resolve_native(&None::<Option<GPUError>>, can_gc)
638            },
639            Err(PopError::Empty) => promise.reject_error(Error::Operation(None), can_gc),
640            Ok(Some(error)) => {
641                let error = GPUError::from_error(&self.global(), error, can_gc);
642                promise.resolve_native(&error, can_gc);
643            },
644        }
645    }
646}
647
648impl RoutedPromiseListener<WebGPUComputePipelineResponse> for GPUDevice {
649    fn handle_response(
650        &self,
651        response: WebGPUComputePipelineResponse,
652        promise: &Rc<Promise>,
653        can_gc: CanGc,
654    ) {
655        match response {
656            Ok(pipeline) => promise.resolve_native(
657                &GPUComputePipeline::new(
658                    &self.global(),
659                    WebGPUComputePipeline(pipeline.id),
660                    pipeline.label.into(),
661                    self,
662                    can_gc,
663                ),
664                can_gc,
665            ),
666            Err(webgpu_traits::Error::Validation(msg)) => promise.reject_native(
667                &GPUPipelineError::new(
668                    &self.global(),
669                    msg.into(),
670                    GPUPipelineErrorReason::Validation,
671                    can_gc,
672                ),
673                can_gc,
674            ),
675            Err(webgpu_traits::Error::OutOfMemory(msg) | webgpu_traits::Error::Internal(msg)) => {
676                promise.reject_native(
677                    &GPUPipelineError::new(
678                        &self.global(),
679                        msg.into(),
680                        GPUPipelineErrorReason::Internal,
681                        can_gc,
682                    ),
683                    can_gc,
684                )
685            },
686        }
687    }
688}
689
690impl RoutedPromiseListener<WebGPURenderPipelineResponse> for GPUDevice {
691    fn handle_response(
692        &self,
693        response: WebGPURenderPipelineResponse,
694        promise: &Rc<Promise>,
695        can_gc: CanGc,
696    ) {
697        match response {
698            Ok(pipeline) => promise.resolve_native(
699                &GPURenderPipeline::new(
700                    &self.global(),
701                    WebGPURenderPipeline(pipeline.id),
702                    pipeline.label.into(),
703                    self,
704                    can_gc,
705                ),
706                can_gc,
707            ),
708            Err(webgpu_traits::Error::Validation(msg)) => promise.reject_native(
709                &GPUPipelineError::new(
710                    &self.global(),
711                    msg.into(),
712                    GPUPipelineErrorReason::Validation,
713                    can_gc,
714                ),
715                can_gc,
716            ),
717            Err(webgpu_traits::Error::OutOfMemory(msg) | webgpu_traits::Error::Internal(msg)) => {
718                promise.reject_native(
719                    &GPUPipelineError::new(
720                        &self.global(),
721                        msg.into(),
722                        GPUPipelineErrorReason::Internal,
723                        can_gc,
724                    ),
725                    can_gc,
726                )
727            },
728        }
729    }
730}
731
732impl Drop for GPUDevice {
733    fn drop(&mut self) {
734        if let Err(e) = self
735            .channel
736            .0
737            .send(WebGPURequest::DropDevice(self.device.0))
738        {
739            warn!("Failed to send DropDevice ({:?}) ({})", self.device.0, e);
740        }
741        let reflector = script_bindings::reflector::DomObject::reflector(self);
742        reflector.drop_memory(self);
743    }
744}