script/dom/webgpu/
gpudevice.rs

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