Skip to main content

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::context::{JSContext, NoGC};
11use js::jsapi::{HandleObject, Heap, JSObject};
12use js::realm::CurrentRealm;
13use script_bindings::cell::DomRefCell;
14use script_bindings::cformat;
15use script_bindings::reflector::reflect_dom_object_with_cx;
16use script_bindings::script_runtime::CanGc;
17use webgpu_traits::{
18    PopError, WebGPU, WebGPUComputePipeline, WebGPUComputePipelineResponse, WebGPUDevice,
19    WebGPUPoppedErrorScopeResponse, WebGPUQueue, WebGPURenderPipeline,
20    WebGPURenderPipelineResponse, WebGPURequest,
21};
22use wgpu_core::id::PipelineLayoutId;
23use wgpu_core::pipeline as wgpu_pipe;
24use wgpu_core::pipeline::RenderPipelineDescriptor;
25use wgpu_types::{self, TextureFormat};
26
27use super::gpudevicelostinfo::GPUDeviceLostInfo;
28use super::gpuerror::AsWebGpu;
29use super::gpupipelineerror::GPUPipelineError;
30use super::gpusupportedlimits::GPUSupportedLimits;
31use crate::conversions::Convert;
32use crate::dom::bindings::codegen::Bindings::EventBinding::EventInit;
33use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
34    GPUAdapterMethods, GPUBindGroupDescriptor, GPUBindGroupLayoutDescriptor, GPUBufferDescriptor,
35    GPUCommandEncoderDescriptor, GPUComputePipelineDescriptor, GPUDeviceLostReason,
36    GPUDeviceMethods, GPUErrorFilter, GPUExternalTextureDescriptor, GPUPipelineErrorReason,
37    GPUPipelineLayoutDescriptor, GPUQuerySetDescriptor, GPURenderBundleEncoderDescriptor,
38    GPURenderPipelineDescriptor, GPUSamplerDescriptor, GPUShaderModuleDescriptor,
39    GPUTextureDescriptor, GPUTextureFormat, GPUUncapturedErrorEventInit, GPUVertexStepMode,
40};
41use crate::dom::bindings::codegen::UnionTypes::GPUPipelineLayoutOrGPUAutoLayoutMode;
42use crate::dom::bindings::error::{Error, Fallible};
43use crate::dom::bindings::inheritance::Castable;
44use crate::dom::bindings::refcounted::Trusted;
45use crate::dom::bindings::reflector::DomGlobal;
46use crate::dom::bindings::root::{Dom, DomRoot};
47use crate::dom::bindings::str::USVString;
48use crate::dom::bindings::trace::RootedTraceableBox;
49use crate::dom::event::Event;
50use crate::dom::eventtarget::EventTarget;
51use crate::dom::globalscope::GlobalScope;
52use crate::dom::promise::Promise;
53use crate::dom::types::{GPUError, GPUQuerySet};
54use crate::dom::webgpu::gpuadapter::GPUAdapter;
55use crate::dom::webgpu::gpuadapterinfo::GPUAdapterInfo;
56use crate::dom::webgpu::gpubindgroup::GPUBindGroup;
57use crate::dom::webgpu::gpubindgrouplayout::GPUBindGroupLayout;
58use crate::dom::webgpu::gpubuffer::GPUBuffer;
59use crate::dom::webgpu::gpucommandencoder::GPUCommandEncoder;
60use crate::dom::webgpu::gpucomputepipeline::GPUComputePipeline;
61use crate::dom::webgpu::gpuexternaltexture::GPUExternalTexture;
62use crate::dom::webgpu::gpupipelinelayout::GPUPipelineLayout;
63use crate::dom::webgpu::gpuqueue::GPUQueue;
64use crate::dom::webgpu::gpurenderbundleencoder::GPURenderBundleEncoder;
65use crate::dom::webgpu::gpurenderpipeline::GPURenderPipeline;
66use crate::dom::webgpu::gpusampler::GPUSampler;
67use crate::dom::webgpu::gpushadermodule::GPUShaderModule;
68use crate::dom::webgpu::gpusupportedfeatures::GPUSupportedFeatures;
69use crate::dom::webgpu::gputexture::GPUTexture;
70use crate::dom::webgpu::gpuuncapturederrorevent::GPUUncapturedErrorEvent;
71use crate::routed_promise::{RoutedPromiseListener, callback_promise};
72
73#[derive(JSTraceable, MallocSizeOf)]
74struct DroppableGPUDevice {
75    #[no_trace]
76    channel: WebGPU,
77    #[no_trace]
78    device: WebGPUDevice,
79}
80
81impl Drop for DroppableGPUDevice {
82    fn drop(&mut self) {
83        if let Err(e) = self
84            .channel
85            .0
86            .send(WebGPURequest::DropDevice(self.device.0))
87        {
88            warn!("Failed to send DropDevice ({:?}) ({})", self.device.0, e);
89        }
90    }
91}
92
93#[dom_struct]
94pub(crate) struct GPUDevice {
95    eventtarget: EventTarget,
96    adapter: Dom<GPUAdapter>,
97    #[ignore_malloc_size_of = "mozjs"]
98    extensions: Heap<*mut JSObject>,
99    features: Dom<GPUSupportedFeatures>,
100    limits: Dom<GPUSupportedLimits>,
101    adapter_info: Dom<GPUAdapterInfo>,
102    label: DomRefCell<USVString>,
103    default_queue: Dom<GPUQueue>,
104    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-lost>
105    #[conditional_malloc_size_of]
106    lost_promise: DomRefCell<Rc<Promise>>,
107    valid: Cell<bool>,
108    droppable: DroppableGPUDevice,
109}
110
111pub(crate) enum PipelineLayout {
112    Implicit,
113    Explicit(PipelineLayoutId),
114}
115
116impl PipelineLayout {
117    pub(crate) fn explicit(&self) -> Option<PipelineLayoutId> {
118        match self {
119            PipelineLayout::Explicit(layout_id) => Some(*layout_id),
120            PipelineLayout::Implicit => None,
121        }
122    }
123}
124
125impl GPUDevice {
126    #[allow(clippy::too_many_arguments)]
127    fn new_inherited(
128        channel: WebGPU,
129        adapter: &GPUAdapter,
130        features: &GPUSupportedFeatures,
131        limits: &GPUSupportedLimits,
132        adapter_info: &GPUAdapterInfo,
133        device: WebGPUDevice,
134        queue: &GPUQueue,
135        label: String,
136        lost_promise: Rc<Promise>,
137    ) -> Self {
138        Self {
139            eventtarget: EventTarget::new_inherited(),
140            adapter: Dom::from_ref(adapter),
141            extensions: Heap::default(),
142            features: Dom::from_ref(features),
143            limits: Dom::from_ref(limits),
144            adapter_info: Dom::from_ref(adapter_info),
145            label: DomRefCell::new(USVString::from(label)),
146            default_queue: Dom::from_ref(queue),
147            lost_promise: DomRefCell::new(lost_promise),
148            valid: Cell::new(true),
149            droppable: DroppableGPUDevice { channel, device },
150        }
151    }
152
153    #[allow(clippy::too_many_arguments)]
154    pub(crate) fn new(
155        cx: &mut JSContext,
156        global: &GlobalScope,
157        channel: WebGPU,
158        adapter: &GPUAdapter,
159        extensions: HandleObject,
160        features: wgpu_types::Features,
161        limits: wgpu_types::Limits,
162        device: WebGPUDevice,
163        queue: WebGPUQueue,
164        label: String,
165    ) -> DomRoot<Self> {
166        let queue = GPUQueue::new(cx, global, channel.clone(), queue);
167        let limits = GPUSupportedLimits::new(cx, global, limits);
168        let features = GPUSupportedFeatures::Constructor(cx, global, None, features).unwrap();
169        let adapter_info = GPUAdapterInfo::clone_from(cx, global, &adapter.Info());
170        let lost_promise = Promise::new(cx, global);
171        let device = reflect_dom_object_with_cx(
172            Box::new(GPUDevice::new_inherited(
173                channel,
174                adapter,
175                &features,
176                &limits,
177                &adapter_info,
178                device,
179                &queue,
180                label,
181                lost_promise,
182            )),
183            global,
184            cx,
185        );
186        queue.set_device(cx, &device);
187        device.extensions.set(*extensions);
188        device
189    }
190}
191
192impl GPUDevice {
193    pub(crate) fn id(&self) -> WebGPUDevice {
194        self.droppable.device
195    }
196
197    pub(crate) fn queue_id(&self) -> WebGPUQueue {
198        self.default_queue.id()
199    }
200
201    pub(crate) fn channel(&self) -> WebGPU {
202        self.droppable.channel.clone()
203    }
204
205    pub(crate) fn dispatch_error(&self, error: webgpu_traits::Error) {
206        if let Err(e) = self.droppable.channel.0.send(WebGPURequest::DispatchError {
207            device_id: self.id().0,
208            error,
209        }) {
210            warn!("Failed to send WebGPURequest::DispatchError due to {e:?}");
211        }
212    }
213
214    /// <https://gpuweb.github.io/gpuweb/#eventdef-gpudevice-uncapturederror>
215    pub(crate) fn fire_uncaptured_error(&self, error: webgpu_traits::Error) {
216        let this = Trusted::new(self);
217
218        // Queue a global task, using the webgpu task source, to fire an event named
219        // uncapturederror at a GPUDevice using GPUUncapturedErrorEvent.
220        self.global().task_manager().webgpu_task_source().queue(
221            task!(fire_uncaptured_error: move |cx| {
222                let this = this.root();
223                let error = GPUError::from_error(cx, &this.global(), error);
224
225                let event = GPUUncapturedErrorEvent::new(
226                    cx,
227                    &this.global(),
228                    atom!("uncapturederror"),
229                    &GPUUncapturedErrorEventInit {
230                        error,
231                        parent: EventInit::empty(),
232                    },
233                );
234
235                event.upcast::<Event>().fire(cx, this.upcast());
236            }),
237        );
238    }
239
240    /// <https://gpuweb.github.io/gpuweb/#abstract-opdef-validate-texture-format-required-features>
241    ///
242    /// Validates that the device suppports required features,
243    /// and if so returns an ok containing wgpu's `TextureFormat`
244    pub(crate) fn validate_texture_format_required_features(
245        &self,
246        format: &GPUTextureFormat,
247    ) -> Fallible<TextureFormat> {
248        let texture_format: TextureFormat = (*format).convert();
249        if self
250            .features
251            .wgpu_features()
252            .contains(texture_format.required_features())
253        {
254            Ok(texture_format)
255        } else {
256            Err(Error::Type(cformat!(
257                "{texture_format:?} is not supported by this GPUDevice"
258            )))
259        }
260    }
261
262    pub(crate) fn is_lost(&self) -> bool {
263        self.lost_promise.borrow().is_fulfilled()
264    }
265
266    pub(crate) fn get_pipeline_layout_data(
267        &self,
268        layout: &GPUPipelineLayoutOrGPUAutoLayoutMode,
269    ) -> PipelineLayout {
270        if let GPUPipelineLayoutOrGPUAutoLayoutMode::GPUPipelineLayout(layout) = layout {
271            PipelineLayout::Explicit(layout.id().0)
272        } else {
273            PipelineLayout::Implicit
274        }
275    }
276
277    pub(crate) fn parse_render_pipeline<'a>(
278        &self,
279        descriptor: &GPURenderPipelineDescriptor,
280    ) -> Fallible<RenderPipelineDescriptor<'a>> {
281        let pipeline_layout = self.get_pipeline_layout_data(&descriptor.parent.layout);
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.map(|dc| dc.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_mask: None,
388        };
389        Ok(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 |cx| {
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::deprecated_note());
404                lost_promise.resolve_native(cx, &*lost);
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, no_gc: &NoGC, value: USVString) {
438        *self.label.safe_borrow_mut(no_gc) = 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(
448        &self,
449        cx: &mut JSContext,
450        descriptor: &GPUBufferDescriptor,
451    ) -> Fallible<DomRoot<GPUBuffer>> {
452        GPUBuffer::create(cx, self, descriptor)
453    }
454
455    /// <https://gpuweb.github.io/gpuweb/#GPUDevice-createBindGroupLayout>
456    fn CreateBindGroupLayout(
457        &self,
458        cx: &mut JSContext,
459        descriptor: &GPUBindGroupLayoutDescriptor,
460    ) -> Fallible<DomRoot<GPUBindGroupLayout>> {
461        GPUBindGroupLayout::create(cx, self, descriptor)
462    }
463
464    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createpipelinelayout>
465    fn CreatePipelineLayout(
466        &self,
467        cx: &mut JSContext,
468        descriptor: &GPUPipelineLayoutDescriptor,
469    ) -> DomRoot<GPUPipelineLayout> {
470        GPUPipelineLayout::create(cx, self, descriptor)
471    }
472
473    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createbindgroup>
474    fn CreateBindGroup(
475        &self,
476        cx: &mut JSContext,
477        descriptor: &GPUBindGroupDescriptor,
478    ) -> DomRoot<GPUBindGroup> {
479        GPUBindGroup::create(cx, self, descriptor)
480    }
481
482    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createshadermodule>
483    fn CreateShaderModule(
484        &self,
485        cx: &mut CurrentRealm<'_>,
486        descriptor: RootedTraceableBox<GPUShaderModuleDescriptor>,
487    ) -> DomRoot<GPUShaderModule> {
488        GPUShaderModule::create(cx, self, descriptor)
489    }
490
491    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcomputepipeline>
492    fn CreateComputePipeline(
493        &self,
494        cx: &mut JSContext,
495        descriptor: &GPUComputePipelineDescriptor,
496    ) -> DomRoot<GPUComputePipeline> {
497        let compute_pipeline = GPUComputePipeline::create(self, descriptor, None);
498        GPUComputePipeline::new(
499            cx,
500            &self.global(),
501            compute_pipeline,
502            descriptor.parent.parent.label.clone(),
503            self,
504        )
505    }
506
507    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcomputepipelineasync>
508    fn CreateComputePipelineAsync(
509        &self,
510        cx: &mut CurrentRealm<'_>,
511        descriptor: &GPUComputePipelineDescriptor,
512    ) -> Rc<Promise> {
513        let promise = Promise::new_in_realm(cx);
514        let callback = callback_promise(
515            &promise,
516            self,
517            self.global().task_manager().dom_manipulation_task_source(),
518        );
519        GPUComputePipeline::create(self, descriptor, Some(callback));
520        promise
521    }
522
523    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcommandencoder>
524    fn CreateCommandEncoder(
525        &self,
526        cx: &mut JSContext,
527        descriptor: &GPUCommandEncoderDescriptor,
528    ) -> DomRoot<GPUCommandEncoder> {
529        GPUCommandEncoder::create(cx, self, descriptor)
530    }
531
532    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createtexture>
533    fn CreateTexture(
534        &self,
535        cx: &mut JSContext,
536        descriptor: &GPUTextureDescriptor,
537    ) -> Fallible<DomRoot<GPUTexture>> {
538        GPUTexture::create(cx, self, descriptor)
539    }
540
541    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createsampler>
542    fn CreateSampler(
543        &self,
544        cx: &mut JSContext,
545        descriptor: &GPUSamplerDescriptor,
546    ) -> DomRoot<GPUSampler> {
547        GPUSampler::create(cx, self, descriptor)
548    }
549
550    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createrenderpipeline>
551    fn CreateRenderPipeline(
552        &self,
553        cx: &mut JSContext,
554        descriptor: &GPURenderPipelineDescriptor,
555    ) -> Fallible<DomRoot<GPURenderPipeline>> {
556        let desc = self.parse_render_pipeline(descriptor)?;
557        let render_pipeline = GPURenderPipeline::create(self, desc, None)?;
558        Ok(GPURenderPipeline::new(
559            cx,
560            &self.global(),
561            render_pipeline,
562            descriptor.parent.parent.label.clone(),
563            self,
564        ))
565    }
566
567    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createrenderpipelineasync>
568    fn CreateRenderPipelineAsync(
569        &self,
570        cx: &mut CurrentRealm<'_>,
571        descriptor: &GPURenderPipelineDescriptor,
572    ) -> Fallible<Rc<Promise>> {
573        let desc = self.parse_render_pipeline(descriptor)?;
574        let promise = Promise::new_in_realm(cx);
575        let callback = callback_promise(
576            &promise,
577            self,
578            self.global().task_manager().dom_manipulation_task_source(),
579        );
580        GPURenderPipeline::create(self, desc, Some(callback))?;
581        Ok(promise)
582    }
583
584    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createrenderbundleencoder>
585    fn CreateRenderBundleEncoder(
586        &self,
587        cx: &mut JSContext,
588        descriptor: &GPURenderBundleEncoderDescriptor,
589    ) -> Fallible<DomRoot<GPURenderBundleEncoder>> {
590        GPURenderBundleEncoder::create(cx, self, descriptor)
591    }
592
593    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createqueryset>
594    fn CreateQuerySet(
595        &self,
596        cx: &mut JSContext,
597        descriptor: &GPUQuerySetDescriptor,
598    ) -> Fallible<DomRoot<GPUQuerySet>> {
599        GPUQuerySet::create(cx, self, descriptor)
600    }
601
602    /// <https://www.w3.org/TR/webgpu/#dom-gpudevice-importexternaltexture>
603    fn ImportExternalTexture(
604        &self,
605        cx: &mut JSContext,
606        descriptor: &GPUExternalTextureDescriptor,
607    ) -> Fallible<DomRoot<GPUExternalTexture>> {
608        GPUExternalTexture::create(cx, self, descriptor)
609    }
610
611    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-pusherrorscope>
612    fn PushErrorScope(&self, filter: GPUErrorFilter) {
613        if self
614            .droppable
615            .channel
616            .0
617            .send(WebGPURequest::PushErrorScope {
618                device_id: self.id().0,
619                filter: filter.as_webgpu(),
620            })
621            .is_err()
622        {
623            warn!("Failed sending WebGPURequest::PushErrorScope");
624        }
625    }
626
627    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-poperrorscope>
628    fn PopErrorScope(&self, cx: &mut CurrentRealm<'_>) -> Rc<Promise> {
629        let promise = Promise::new_in_realm(cx);
630        let callback = callback_promise(
631            &promise,
632            self,
633            self.global().task_manager().dom_manipulation_task_source(),
634        );
635        if self
636            .droppable
637            .channel
638            .0
639            .send(WebGPURequest::PopErrorScope {
640                device_id: self.id().0,
641                callback,
642            })
643            .is_err()
644        {
645            warn!("Error when sending WebGPURequest::PopErrorScope");
646        }
647        promise
648    }
649
650    // https://gpuweb.github.io/gpuweb/#dom-gpudevice-onuncapturederror
651    event_handler!(uncapturederror, GetOnuncapturederror, SetOnuncapturederror);
652
653    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-destroy>
654    fn Destroy(&self) {
655        if self.valid.get() {
656            self.valid.set(false);
657
658            if let Err(e) = self
659                .droppable
660                .channel
661                .0
662                .send(WebGPURequest::DestroyDevice(self.id().0))
663            {
664                warn!("Failed to send DestroyDevice ({:?}) ({})", self.id().0, e);
665            }
666        }
667    }
668}
669
670impl RoutedPromiseListener<WebGPUPoppedErrorScopeResponse> for GPUDevice {
671    fn handle_response(
672        &self,
673        cx: &mut js::context::JSContext,
674        response: WebGPUPoppedErrorScopeResponse,
675        promise: &Rc<Promise>,
676    ) {
677        match response {
678            Ok(None) | Err(PopError::Lost) => promise.resolve_native(cx, &None::<Option<GPUError>>),
679            Err(PopError::Empty) => promise.reject_error(cx, Error::Operation(None)),
680            Ok(Some(error)) => {
681                let error = GPUError::from_error(cx, &self.global(), error);
682                promise.resolve_native(cx, &error);
683            },
684        }
685    }
686}
687
688impl RoutedPromiseListener<WebGPUComputePipelineResponse> for GPUDevice {
689    fn handle_response(
690        &self,
691        cx: &mut js::context::JSContext,
692        response: WebGPUComputePipelineResponse,
693        promise: &Rc<Promise>,
694    ) {
695        match response {
696            Ok(pipeline) => {
697                let gpu_compute_pipeline = GPUComputePipeline::new(
698                    cx,
699                    &self.global(),
700                    WebGPUComputePipeline(pipeline.id),
701                    pipeline.label.into(),
702                    self,
703                );
704                promise.resolve_native(cx, &gpu_compute_pipeline)
705            },
706            Err(webgpu_traits::Error::Validation(msg)) => {
707                let gpu_pipeline_error = GPUPipelineError::new(
708                    cx,
709                    &self.global(),
710                    msg.into(),
711                    GPUPipelineErrorReason::Validation,
712                );
713                promise.reject_native(cx, &gpu_pipeline_error)
714            },
715            Err(webgpu_traits::Error::OutOfMemory(msg) | webgpu_traits::Error::Internal(msg)) => {
716                let gpu_pipeline_error = GPUPipelineError::new(
717                    cx,
718                    &self.global(),
719                    msg.into(),
720                    GPUPipelineErrorReason::Internal,
721                );
722                promise.reject_native(cx, &gpu_pipeline_error)
723            },
724        }
725    }
726}
727
728impl RoutedPromiseListener<WebGPURenderPipelineResponse> for GPUDevice {
729    fn handle_response(
730        &self,
731        cx: &mut js::context::JSContext,
732        response: WebGPURenderPipelineResponse,
733        promise: &Rc<Promise>,
734    ) {
735        match response {
736            Ok(pipeline) => {
737                let gpu_pipeline = GPURenderPipeline::new(
738                    cx,
739                    &self.global(),
740                    WebGPURenderPipeline(pipeline.id),
741                    pipeline.label.into(),
742                    self,
743                );
744                promise.resolve_native(cx, &gpu_pipeline)
745            },
746            Err(webgpu_traits::Error::Validation(msg)) => {
747                let pipeline_error = GPUPipelineError::new(
748                    cx,
749                    &self.global(),
750                    msg.into(),
751                    GPUPipelineErrorReason::Validation,
752                );
753
754                promise.reject_native(cx, &pipeline_error)
755            },
756            Err(webgpu_traits::Error::OutOfMemory(msg) | webgpu_traits::Error::Internal(msg)) => {
757                let pipeline_error = GPUPipelineError::new(
758                    cx,
759                    &self.global(),
760                    msg.into(),
761                    GPUPipelineErrorReason::Internal,
762                );
763                promise.reject_native(cx, &pipeline_error)
764            },
765        }
766    }
767}