Skip to main content

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