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::codegen::GenericBindings::WebGPUBinding::GPUAdapterMethods;
16use script_bindings::reflector::reflect_weak_referenceable_dom_object;
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    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_weak_referenceable_dom_object(
172            cx,
173            Rc::new(GPUDevice::new_inherited(
174                channel,
175                adapter,
176                &features,
177                &limits,
178                &adapter_info,
179                device,
180                &queue,
181                label,
182                lost_promise,
183            )),
184            global,
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                        // FIXME: webidl has `sequence<GPUVertexBufferLayout?> buffers`
294                        // but we get no option here so it must be eaten by codegen
295                        .map(|buffer| {
296                            Some(wgpu_pipe::VertexBufferLayout {
297                                array_stride: buffer.arrayStride,
298                                step_mode: match buffer.stepMode {
299                                    GPUVertexStepMode::Vertex => wgpu_types::VertexStepMode::Vertex,
300                                    GPUVertexStepMode::Instance => {
301                                        wgpu_types::VertexStepMode::Instance
302                                    },
303                                },
304                                attributes: Cow::Owned(
305                                    buffer
306                                        .attributes
307                                        .iter()
308                                        .map(|att| wgpu_types::VertexAttribute {
309                                            format: att.format.convert(),
310                                            offset: att.offset,
311                                            shader_location: att.shaderLocation,
312                                        })
313                                        .collect::<Vec<_>>(),
314                                ),
315                            })
316                        })
317                        .collect::<Vec<_>>(),
318                ),
319            },
320            fragment: descriptor
321                .fragment
322                .as_ref()
323                .map(|stage| -> Fallible<wgpu_pipe::FragmentState> {
324                    Ok(wgpu_pipe::FragmentState {
325                        stage: (&stage.parent).convert(),
326                        targets: Cow::Owned(
327                            stage
328                                .targets
329                                .iter()
330                                .map(|state| {
331                                    self.validate_texture_format_required_features(&state.format)
332                                        .map(|format| {
333                                            Some(wgpu_types::ColorTargetState {
334                                                format,
335                                                write_mask:
336                                                    wgpu_types::ColorWrites::from_bits_retain(
337                                                        state.writeMask,
338                                                    ),
339                                                blend: state.blend.as_ref().map(|blend| {
340                                                    wgpu_types::BlendState {
341                                                        color: (&blend.color).convert(),
342                                                        alpha: (&blend.alpha).convert(),
343                                                    }
344                                                }),
345                                            })
346                                        })
347                                })
348                                .collect::<Result<Vec<_>, _>>()?,
349                        ),
350                    })
351                })
352                .transpose()?,
353            primitive: (&descriptor.primitive).convert(),
354            depth_stencil: descriptor
355                .depthStencil
356                .as_ref()
357                .map(|dss_desc| {
358                    self.validate_texture_format_required_features(&dss_desc.format)
359                        .map(|format| wgpu_types::DepthStencilState {
360                            format,
361                            depth_write_enabled: dss_desc.depthWriteEnabled,
362                            depth_compare: dss_desc.depthCompare.map(|dc| dc.convert()),
363                            stencil: wgpu_types::StencilState {
364                                front: wgpu_types::StencilFaceState {
365                                    compare: dss_desc.stencilFront.compare.convert(),
366
367                                    fail_op: dss_desc.stencilFront.failOp.convert(),
368                                    depth_fail_op: dss_desc.stencilFront.depthFailOp.convert(),
369                                    pass_op: dss_desc.stencilFront.passOp.convert(),
370                                },
371                                back: wgpu_types::StencilFaceState {
372                                    compare: dss_desc.stencilBack.compare.convert(),
373                                    fail_op: dss_desc.stencilBack.failOp.convert(),
374                                    depth_fail_op: dss_desc.stencilBack.depthFailOp.convert(),
375                                    pass_op: dss_desc.stencilBack.passOp.convert(),
376                                },
377                                read_mask: dss_desc.stencilReadMask,
378                                write_mask: dss_desc.stencilWriteMask,
379                            },
380                            bias: wgpu_types::DepthBiasState {
381                                constant: dss_desc.depthBias,
382                                slope_scale: *dss_desc.depthBiasSlopeScale,
383                                clamp: *dss_desc.depthBiasClamp,
384                            },
385                        })
386                })
387                .transpose()?,
388            multisample: wgpu_types::MultisampleState {
389                count: descriptor.multisample.count,
390                mask: descriptor.multisample.mask as u64,
391                alpha_to_coverage_enabled: descriptor.multisample.alphaToCoverageEnabled,
392            },
393            multiview_mask: None,
394        };
395        Ok(desc)
396    }
397
398    /// <https://gpuweb.github.io/gpuweb/#lose-the-device>
399    pub(crate) fn lose(&self, reason: GPUDeviceLostReason, msg: String) {
400        let this = Trusted::new(self);
401
402        // Queue a global task, using the webgpu task source, to resolve device.lost
403        // promise with a new GPUDeviceLostInfo with reason and message.
404        self.global().task_manager().webgpu_task_source().queue(
405            task!(resolve_device_lost: move |cx| {
406                let this = this.root();
407
408                let lost_promise = &(*this.lost_promise.borrow());
409                let lost = GPUDeviceLostInfo::new(cx, &this.global(), msg.into(), reason);
410                lost_promise.resolve_native(cx, &*lost);
411            }),
412        );
413    }
414}
415
416impl GPUDeviceMethods<crate::DomTypeHolder> for GPUDevice {
417    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-features>
418    fn Features(&self) -> DomRoot<GPUSupportedFeatures> {
419        DomRoot::from_ref(&self.features)
420    }
421
422    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-limits>
423    fn Limits(&self) -> DomRoot<GPUSupportedLimits> {
424        DomRoot::from_ref(&self.limits)
425    }
426
427    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-adapterinfo>
428    fn AdapterInfo(&self) -> DomRoot<GPUAdapterInfo> {
429        DomRoot::from_ref(&self.adapter_info)
430    }
431
432    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-queue>
433    fn GetQueue(&self) -> DomRoot<GPUQueue> {
434        DomRoot::from_ref(&self.default_queue)
435    }
436
437    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
438    fn Label(&self) -> USVString {
439        self.label.borrow().clone()
440    }
441
442    /// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
443    fn SetLabel(&self, no_gc: &NoGC, value: USVString) {
444        *self.label.safe_borrow_mut(no_gc) = value;
445    }
446
447    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-lost>
448    fn Lost(&self) -> Rc<Promise> {
449        self.lost_promise.borrow().clone()
450    }
451
452    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createbuffer>
453    fn CreateBuffer(
454        &self,
455        cx: &mut JSContext,
456        descriptor: &GPUBufferDescriptor,
457    ) -> Fallible<DomRoot<GPUBuffer>> {
458        GPUBuffer::create(cx, self, descriptor)
459    }
460
461    /// <https://gpuweb.github.io/gpuweb/#GPUDevice-createBindGroupLayout>
462    fn CreateBindGroupLayout(
463        &self,
464        cx: &mut JSContext,
465        descriptor: &GPUBindGroupLayoutDescriptor,
466    ) -> Fallible<DomRoot<GPUBindGroupLayout>> {
467        GPUBindGroupLayout::create(cx, self, descriptor)
468    }
469
470    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createpipelinelayout>
471    fn CreatePipelineLayout(
472        &self,
473        cx: &mut JSContext,
474        descriptor: &GPUPipelineLayoutDescriptor,
475    ) -> DomRoot<GPUPipelineLayout> {
476        GPUPipelineLayout::create(cx, self, descriptor)
477    }
478
479    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createbindgroup>
480    fn CreateBindGroup(
481        &self,
482        cx: &mut JSContext,
483        descriptor: &GPUBindGroupDescriptor,
484    ) -> DomRoot<GPUBindGroup> {
485        GPUBindGroup::create(cx, self, descriptor)
486    }
487
488    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createshadermodule>
489    fn CreateShaderModule(
490        &self,
491        cx: &mut CurrentRealm<'_>,
492        descriptor: RootedTraceableBox<GPUShaderModuleDescriptor>,
493    ) -> DomRoot<GPUShaderModule> {
494        GPUShaderModule::create(cx, self, descriptor)
495    }
496
497    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcomputepipeline>
498    fn CreateComputePipeline(
499        &self,
500        cx: &mut JSContext,
501        descriptor: &GPUComputePipelineDescriptor,
502    ) -> DomRoot<GPUComputePipeline> {
503        let compute_pipeline = GPUComputePipeline::create(self, descriptor, None);
504        GPUComputePipeline::new(
505            cx,
506            &self.global(),
507            compute_pipeline,
508            descriptor.parent.parent.label.clone(),
509            self,
510        )
511    }
512
513    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcomputepipelineasync>
514    fn CreateComputePipelineAsync(
515        &self,
516        cx: &mut CurrentRealm<'_>,
517        descriptor: &GPUComputePipelineDescriptor,
518    ) -> Rc<Promise> {
519        let promise = Promise::new_in_realm(cx);
520        let callback = callback_promise(
521            &promise,
522            self,
523            self.global().task_manager().dom_manipulation_task_source(),
524        );
525        GPUComputePipeline::create(self, descriptor, Some(callback));
526        promise
527    }
528
529    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcommandencoder>
530    fn CreateCommandEncoder(
531        &self,
532        cx: &mut JSContext,
533        descriptor: &GPUCommandEncoderDescriptor,
534    ) -> DomRoot<GPUCommandEncoder> {
535        GPUCommandEncoder::create(cx, self, descriptor)
536    }
537
538    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createtexture>
539    fn CreateTexture(
540        &self,
541        cx: &mut JSContext,
542        descriptor: &GPUTextureDescriptor,
543    ) -> Fallible<DomRoot<GPUTexture>> {
544        GPUTexture::create(cx, self, descriptor)
545    }
546
547    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createsampler>
548    fn CreateSampler(
549        &self,
550        cx: &mut JSContext,
551        descriptor: &GPUSamplerDescriptor,
552    ) -> DomRoot<GPUSampler> {
553        GPUSampler::create(cx, self, descriptor)
554    }
555
556    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createrenderpipeline>
557    fn CreateRenderPipeline(
558        &self,
559        cx: &mut JSContext,
560        descriptor: &GPURenderPipelineDescriptor,
561    ) -> Fallible<DomRoot<GPURenderPipeline>> {
562        let desc = self.parse_render_pipeline(descriptor)?;
563        let render_pipeline = GPURenderPipeline::create(self, desc, None)?;
564        Ok(GPURenderPipeline::new(
565            cx,
566            &self.global(),
567            render_pipeline,
568            descriptor.parent.parent.label.clone(),
569            self,
570        ))
571    }
572
573    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createrenderpipelineasync>
574    fn CreateRenderPipelineAsync(
575        &self,
576        cx: &mut CurrentRealm<'_>,
577        descriptor: &GPURenderPipelineDescriptor,
578    ) -> Fallible<Rc<Promise>> {
579        let desc = self.parse_render_pipeline(descriptor)?;
580        let promise = Promise::new_in_realm(cx);
581        let callback = callback_promise(
582            &promise,
583            self,
584            self.global().task_manager().dom_manipulation_task_source(),
585        );
586        GPURenderPipeline::create(self, desc, Some(callback))?;
587        Ok(promise)
588    }
589
590    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createrenderbundleencoder>
591    fn CreateRenderBundleEncoder(
592        &self,
593        cx: &mut JSContext,
594        descriptor: &GPURenderBundleEncoderDescriptor,
595    ) -> Fallible<DomRoot<GPURenderBundleEncoder>> {
596        GPURenderBundleEncoder::create(cx, self, descriptor)
597    }
598
599    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createqueryset>
600    fn CreateQuerySet(
601        &self,
602        cx: &mut JSContext,
603        descriptor: &GPUQuerySetDescriptor,
604    ) -> Fallible<DomRoot<GPUQuerySet>> {
605        GPUQuerySet::create(cx, self, descriptor)
606    }
607
608    /// <https://www.w3.org/TR/webgpu/#dom-gpudevice-importexternaltexture>
609    fn ImportExternalTexture(
610        &self,
611        cx: &mut JSContext,
612        descriptor: &GPUExternalTextureDescriptor,
613    ) -> Fallible<DomRoot<GPUExternalTexture>> {
614        GPUExternalTexture::create(cx, self, descriptor)
615    }
616
617    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-pusherrorscope>
618    fn PushErrorScope(&self, filter: GPUErrorFilter) {
619        if self
620            .droppable
621            .channel
622            .0
623            .send(WebGPURequest::PushErrorScope {
624                device_id: self.id().0,
625                filter: filter.as_webgpu(),
626            })
627            .is_err()
628        {
629            warn!("Failed sending WebGPURequest::PushErrorScope");
630        }
631    }
632
633    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-poperrorscope>
634    fn PopErrorScope(&self, cx: &mut CurrentRealm<'_>) -> Rc<Promise> {
635        let promise = Promise::new_in_realm(cx);
636        let callback = callback_promise(
637            &promise,
638            self,
639            self.global().task_manager().dom_manipulation_task_source(),
640        );
641        if self
642            .droppable
643            .channel
644            .0
645            .send(WebGPURequest::PopErrorScope {
646                device_id: self.id().0,
647                callback,
648            })
649            .is_err()
650        {
651            warn!("Error when sending WebGPURequest::PopErrorScope");
652        }
653        promise
654    }
655
656    // https://gpuweb.github.io/gpuweb/#dom-gpudevice-onuncapturederror
657    event_handler!(uncapturederror, GetOnuncapturederror, SetOnuncapturederror);
658
659    /// <https://gpuweb.github.io/gpuweb/#dom-gpudevice-destroy>
660    fn Destroy(&self) {
661        if self.valid.get() {
662            self.valid.set(false);
663
664            if let Err(e) = self
665                .droppable
666                .channel
667                .0
668                .send(WebGPURequest::DestroyDevice(self.id().0))
669            {
670                warn!("Failed to send DestroyDevice ({:?}) ({})", self.id().0, e);
671            }
672        }
673    }
674}
675
676impl RoutedPromiseListener<WebGPUPoppedErrorScopeResponse> for GPUDevice {
677    fn handle_response(
678        &self,
679        cx: &mut js::context::JSContext,
680        response: WebGPUPoppedErrorScopeResponse,
681        promise: &Rc<Promise>,
682    ) {
683        match response {
684            Ok(None) | Err(PopError::Lost) => promise.resolve_native(cx, &None::<Option<GPUError>>),
685            Err(PopError::Empty) => promise.reject_error(cx, Error::Operation(None)),
686            Ok(Some(error)) => {
687                let error = GPUError::from_error(cx, &self.global(), error);
688                promise.resolve_native(cx, &error);
689            },
690        }
691    }
692}
693
694impl RoutedPromiseListener<WebGPUComputePipelineResponse> for GPUDevice {
695    fn handle_response(
696        &self,
697        cx: &mut js::context::JSContext,
698        response: WebGPUComputePipelineResponse,
699        promise: &Rc<Promise>,
700    ) {
701        match response {
702            Ok(pipeline) => {
703                let gpu_compute_pipeline = GPUComputePipeline::new(
704                    cx,
705                    &self.global(),
706                    WebGPUComputePipeline(pipeline.id),
707                    pipeline.label.into(),
708                    self,
709                );
710                promise.resolve_native(cx, &gpu_compute_pipeline)
711            },
712            Err(webgpu_traits::Error::Validation(msg)) => {
713                let gpu_pipeline_error = GPUPipelineError::new(
714                    cx,
715                    &self.global(),
716                    msg.into(),
717                    GPUPipelineErrorReason::Validation,
718                );
719                promise.reject_native(cx, &gpu_pipeline_error)
720            },
721            Err(webgpu_traits::Error::OutOfMemory(msg) | webgpu_traits::Error::Internal(msg)) => {
722                let gpu_pipeline_error = GPUPipelineError::new(
723                    cx,
724                    &self.global(),
725                    msg.into(),
726                    GPUPipelineErrorReason::Internal,
727                );
728                promise.reject_native(cx, &gpu_pipeline_error)
729            },
730        }
731    }
732}
733
734impl RoutedPromiseListener<WebGPURenderPipelineResponse> for GPUDevice {
735    fn handle_response(
736        &self,
737        cx: &mut js::context::JSContext,
738        response: WebGPURenderPipelineResponse,
739        promise: &Rc<Promise>,
740    ) {
741        match response {
742            Ok(pipeline) => {
743                let gpu_pipeline = GPURenderPipeline::new(
744                    cx,
745                    &self.global(),
746                    WebGPURenderPipeline(pipeline.id),
747                    pipeline.label.into(),
748                    self,
749                );
750                promise.resolve_native(cx, &gpu_pipeline)
751            },
752            Err(webgpu_traits::Error::Validation(msg)) => {
753                let pipeline_error = GPUPipelineError::new(
754                    cx,
755                    &self.global(),
756                    msg.into(),
757                    GPUPipelineErrorReason::Validation,
758                );
759
760                promise.reject_native(cx, &pipeline_error)
761            },
762            Err(webgpu_traits::Error::OutOfMemory(msg) | webgpu_traits::Error::Internal(msg)) => {
763                let pipeline_error = GPUPipelineError::new(
764                    cx,
765                    &self.global(),
766                    msg.into(),
767                    GPUPipelineErrorReason::Internal,
768                );
769                promise.reject_native(cx, &pipeline_error)
770            },
771        }
772    }
773}