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