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