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