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