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 || {
220 let this = this.root();
221 let error = GPUError::from_error(&this.global(), error, CanGc::deprecated_note());
222
223 let event = GPUUncapturedErrorEvent::new(
224 &this.global(),
225 atom!("uncapturederror"),
226 &GPUUncapturedErrorEventInit {
227 error,
228 parent: EventInit::empty(),
229 },
230 CanGc::deprecated_note(),
231 );
232
233 event.upcast::<Event>().fire(this.upcast(), CanGc::deprecated_note());
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: Some(dss_desc.depthWriteEnabled),
355 depth_compare: Some(dss_desc.depthCompare.convert()),
356 stencil: wgpu_types::StencilState {
357 front: wgpu_types::StencilFaceState {
358 compare: dss_desc.stencilFront.compare.convert(),
359
360 fail_op: dss_desc.stencilFront.failOp.convert(),
361 depth_fail_op: dss_desc.stencilFront.depthFailOp.convert(),
362 pass_op: dss_desc.stencilFront.passOp.convert(),
363 },
364 back: wgpu_types::StencilFaceState {
365 compare: dss_desc.stencilBack.compare.convert(),
366 fail_op: dss_desc.stencilBack.failOp.convert(),
367 depth_fail_op: dss_desc.stencilBack.depthFailOp.convert(),
368 pass_op: dss_desc.stencilBack.passOp.convert(),
369 },
370 read_mask: dss_desc.stencilReadMask,
371 write_mask: dss_desc.stencilWriteMask,
372 },
373 bias: wgpu_types::DepthBiasState {
374 constant: dss_desc.depthBias,
375 slope_scale: *dss_desc.depthBiasSlopeScale,
376 clamp: *dss_desc.depthBiasClamp,
377 },
378 })
379 })
380 .transpose()?,
381 multisample: wgpu_types::MultisampleState {
382 count: descriptor.multisample.count,
383 mask: descriptor.multisample.mask as u64,
384 alpha_to_coverage_enabled: descriptor.multisample.alphaToCoverageEnabled,
385 },
386 multiview_mask: None,
387 };
388 Ok(desc)
389 }
390
391 pub(crate) fn lose(&self, reason: GPUDeviceLostReason, msg: String) {
393 let this = Trusted::new(self);
394
395 self.global().task_manager().webgpu_task_source().queue(
398 task!(resolve_device_lost: move || {
399 let this = this.root();
400
401 let lost_promise = &(*this.lost_promise.borrow());
402 let lost = GPUDeviceLostInfo::new(&this.global(), msg.into(), reason, CanGc::deprecated_note());
403 lost_promise.resolve_native(&*lost, CanGc::deprecated_note());
404 }),
405 );
406 }
407}
408
409impl GPUDeviceMethods<crate::DomTypeHolder> for GPUDevice {
410 fn Features(&self) -> DomRoot<GPUSupportedFeatures> {
412 DomRoot::from_ref(&self.features)
413 }
414
415 fn Limits(&self) -> DomRoot<GPUSupportedLimits> {
417 DomRoot::from_ref(&self.limits)
418 }
419
420 fn AdapterInfo(&self) -> DomRoot<GPUAdapterInfo> {
422 DomRoot::from_ref(&self.adapter_info)
423 }
424
425 fn GetQueue(&self) -> DomRoot<GPUQueue> {
427 DomRoot::from_ref(&self.default_queue)
428 }
429
430 fn Label(&self) -> USVString {
432 self.label.borrow().clone()
433 }
434
435 fn SetLabel(&self, value: USVString) {
437 *self.label.borrow_mut() = value;
438 }
439
440 fn Lost(&self) -> Rc<Promise> {
442 self.lost_promise.borrow().clone()
443 }
444
445 fn CreateBuffer(&self, descriptor: &GPUBufferDescriptor) -> Fallible<DomRoot<GPUBuffer>> {
447 GPUBuffer::create(self, descriptor, CanGc::deprecated_note())
448 }
449
450 fn CreateBindGroupLayout(
452 &self,
453 descriptor: &GPUBindGroupLayoutDescriptor,
454 ) -> Fallible<DomRoot<GPUBindGroupLayout>> {
455 GPUBindGroupLayout::create(self, descriptor, CanGc::deprecated_note())
456 }
457
458 fn CreatePipelineLayout(
460 &self,
461 descriptor: &GPUPipelineLayoutDescriptor,
462 ) -> DomRoot<GPUPipelineLayout> {
463 GPUPipelineLayout::create(self, descriptor, CanGc::deprecated_note())
464 }
465
466 fn CreateBindGroup(&self, descriptor: &GPUBindGroupDescriptor) -> DomRoot<GPUBindGroup> {
468 GPUBindGroup::create(self, descriptor, CanGc::deprecated_note())
469 }
470
471 fn CreateShaderModule(
473 &self,
474 descriptor: RootedTraceableBox<GPUShaderModuleDescriptor>,
475 comp: InRealm,
476 can_gc: CanGc,
477 ) -> DomRoot<GPUShaderModule> {
478 GPUShaderModule::create(self, descriptor, comp, can_gc)
479 }
480
481 fn CreateComputePipeline(
483 &self,
484 descriptor: &GPUComputePipelineDescriptor,
485 ) -> DomRoot<GPUComputePipeline> {
486 let compute_pipeline = GPUComputePipeline::create(self, descriptor, None);
487 GPUComputePipeline::new(
488 &self.global(),
489 compute_pipeline,
490 descriptor.parent.parent.label.clone(),
491 self,
492 CanGc::deprecated_note(),
493 )
494 }
495
496 fn CreateComputePipelineAsync(
498 &self,
499 descriptor: &GPUComputePipelineDescriptor,
500 comp: InRealm,
501 can_gc: CanGc,
502 ) -> Rc<Promise> {
503 let promise = Promise::new_in_current_realm(comp, can_gc);
504 let callback = callback_promise(
505 &promise,
506 self,
507 self.global().task_manager().dom_manipulation_task_source(),
508 );
509 GPUComputePipeline::create(self, descriptor, Some(callback));
510 promise
511 }
512
513 fn CreateCommandEncoder(
515 &self,
516 descriptor: &GPUCommandEncoderDescriptor,
517 ) -> DomRoot<GPUCommandEncoder> {
518 GPUCommandEncoder::create(self, descriptor, CanGc::deprecated_note())
519 }
520
521 fn CreateTexture(&self, descriptor: &GPUTextureDescriptor) -> Fallible<DomRoot<GPUTexture>> {
523 GPUTexture::create(self, descriptor, CanGc::deprecated_note())
524 }
525
526 fn CreateSampler(&self, descriptor: &GPUSamplerDescriptor) -> DomRoot<GPUSampler> {
528 GPUSampler::create(self, descriptor, CanGc::deprecated_note())
529 }
530
531 fn CreateRenderPipeline(
533 &self,
534 descriptor: &GPURenderPipelineDescriptor,
535 ) -> Fallible<DomRoot<GPURenderPipeline>> {
536 let desc = self.parse_render_pipeline(descriptor)?;
537 let render_pipeline = GPURenderPipeline::create(self, desc, None)?;
538 Ok(GPURenderPipeline::new(
539 &self.global(),
540 render_pipeline,
541 descriptor.parent.parent.label.clone(),
542 self,
543 CanGc::deprecated_note(),
544 ))
545 }
546
547 fn CreateRenderPipelineAsync(
549 &self,
550 descriptor: &GPURenderPipelineDescriptor,
551 comp: InRealm,
552 can_gc: CanGc,
553 ) -> Fallible<Rc<Promise>> {
554 let desc = self.parse_render_pipeline(descriptor)?;
555 let promise = Promise::new_in_current_realm(comp, can_gc);
556 let callback = callback_promise(
557 &promise,
558 self,
559 self.global().task_manager().dom_manipulation_task_source(),
560 );
561 GPURenderPipeline::create(self, desc, Some(callback))?;
562 Ok(promise)
563 }
564
565 fn CreateRenderBundleEncoder(
567 &self,
568 descriptor: &GPURenderBundleEncoderDescriptor,
569 ) -> Fallible<DomRoot<GPURenderBundleEncoder>> {
570 GPURenderBundleEncoder::create(self, descriptor, CanGc::deprecated_note())
571 }
572
573 fn PushErrorScope(&self, filter: GPUErrorFilter) {
575 if self
576 .droppable
577 .channel
578 .0
579 .send(WebGPURequest::PushErrorScope {
580 device_id: self.id().0,
581 filter: filter.as_webgpu(),
582 })
583 .is_err()
584 {
585 warn!("Failed sending WebGPURequest::PushErrorScope");
586 }
587 }
588
589 fn PopErrorScope(&self, comp: InRealm, can_gc: CanGc) -> Rc<Promise> {
591 let promise = Promise::new_in_current_realm(comp, can_gc);
592 let callback = callback_promise(
593 &promise,
594 self,
595 self.global().task_manager().dom_manipulation_task_source(),
596 );
597 if self
598 .droppable
599 .channel
600 .0
601 .send(WebGPURequest::PopErrorScope {
602 device_id: self.id().0,
603 callback,
604 })
605 .is_err()
606 {
607 warn!("Error when sending WebGPURequest::PopErrorScope");
608 }
609 promise
610 }
611
612 event_handler!(uncapturederror, GetOnuncapturederror, SetOnuncapturederror);
614
615 fn Destroy(&self) {
617 if self.valid.get() {
618 self.valid.set(false);
619
620 if let Err(e) = self
621 .droppable
622 .channel
623 .0
624 .send(WebGPURequest::DestroyDevice(self.id().0))
625 {
626 warn!("Failed to send DestroyDevice ({:?}) ({})", self.id().0, e);
627 }
628 }
629 }
630}
631
632impl RoutedPromiseListener<WebGPUPoppedErrorScopeResponse> for GPUDevice {
633 fn handle_response(
634 &self,
635 cx: &mut js::context::JSContext,
636 response: WebGPUPoppedErrorScopeResponse,
637 promise: &Rc<Promise>,
638 ) {
639 match response {
640 Ok(None) | Err(PopError::Lost) => {
641 promise.resolve_native(&None::<Option<GPUError>>, CanGc::from_cx(cx))
642 },
643 Err(PopError::Empty) => {
644 promise.reject_error(Error::Operation(None), CanGc::from_cx(cx))
645 },
646 Ok(Some(error)) => {
647 let error = GPUError::from_error(&self.global(), error, CanGc::from_cx(cx));
648 promise.resolve_native(&error, CanGc::from_cx(cx));
649 },
650 }
651 }
652}
653
654impl RoutedPromiseListener<WebGPUComputePipelineResponse> for GPUDevice {
655 fn handle_response(
656 &self,
657 cx: &mut js::context::JSContext,
658 response: WebGPUComputePipelineResponse,
659 promise: &Rc<Promise>,
660 ) {
661 match response {
662 Ok(pipeline) => promise.resolve_native(
663 &GPUComputePipeline::new(
664 &self.global(),
665 WebGPUComputePipeline(pipeline.id),
666 pipeline.label.into(),
667 self,
668 CanGc::from_cx(cx),
669 ),
670 CanGc::from_cx(cx),
671 ),
672 Err(webgpu_traits::Error::Validation(msg)) => promise.reject_native(
673 &GPUPipelineError::new(
674 &self.global(),
675 msg.into(),
676 GPUPipelineErrorReason::Validation,
677 CanGc::from_cx(cx),
678 ),
679 CanGc::from_cx(cx),
680 ),
681 Err(webgpu_traits::Error::OutOfMemory(msg) | webgpu_traits::Error::Internal(msg)) => {
682 promise.reject_native(
683 &GPUPipelineError::new(
684 &self.global(),
685 msg.into(),
686 GPUPipelineErrorReason::Internal,
687 CanGc::from_cx(cx),
688 ),
689 CanGc::from_cx(cx),
690 )
691 },
692 }
693 }
694}
695
696impl RoutedPromiseListener<WebGPURenderPipelineResponse> for GPUDevice {
697 fn handle_response(
698 &self,
699 cx: &mut js::context::JSContext,
700 response: WebGPURenderPipelineResponse,
701 promise: &Rc<Promise>,
702 ) {
703 match response {
704 Ok(pipeline) => promise.resolve_native(
705 &GPURenderPipeline::new(
706 &self.global(),
707 WebGPURenderPipeline(pipeline.id),
708 pipeline.label.into(),
709 self,
710 CanGc::from_cx(cx),
711 ),
712 CanGc::from_cx(cx),
713 ),
714 Err(webgpu_traits::Error::Validation(msg)) => promise.reject_native(
715 &GPUPipelineError::new(
716 &self.global(),
717 msg.into(),
718 GPUPipelineErrorReason::Validation,
719 CanGc::from_cx(cx),
720 ),
721 CanGc::from_cx(cx),
722 ),
723 Err(webgpu_traits::Error::OutOfMemory(msg) | webgpu_traits::Error::Internal(msg)) => {
724 promise.reject_native(
725 &GPUPipelineError::new(
726 &self.global(),
727 msg.into(),
728 GPUPipelineErrorReason::Internal,
729 CanGc::from_cx(cx),
730 ),
731 CanGc::from_cx(cx),
732 )
733 },
734 }
735 }
736}