1use std::borrow::Cow;
6use std::cell::Cell;
7use std::rc::Rc;
8
9use dom_struct::dom_struct;
10use js::context::JSContext;
11use js::jsapi::{HandleObject, Heap, JSObject};
12use js::realm::CurrentRealm;
13use script_bindings::cell::DomRefCell;
14use script_bindings::cformat;
15use script_bindings::reflector::reflect_dom_object_with_cx;
16use script_bindings::script_runtime::CanGc;
17use webgpu_traits::{
18 PopError, WebGPU, WebGPUComputePipeline, WebGPUComputePipelineResponse, WebGPUDevice,
19 WebGPUPoppedErrorScopeResponse, WebGPUQueue, WebGPURenderPipeline,
20 WebGPURenderPipelineResponse, WebGPURequest,
21};
22use wgpu_core::id::PipelineLayoutId;
23use wgpu_core::pipeline as wgpu_pipe;
24use wgpu_core::pipeline::RenderPipelineDescriptor;
25use wgpu_types::{self, TextureFormat};
26
27use super::gpudevicelostinfo::GPUDeviceLostInfo;
28use super::gpuerror::AsWebGpu;
29use super::gpupipelineerror::GPUPipelineError;
30use super::gpusupportedlimits::GPUSupportedLimits;
31use crate::conversions::Convert;
32use crate::dom::bindings::codegen::Bindings::EventBinding::EventInit;
33use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
34 GPUAdapterMethods, GPUBindGroupDescriptor, GPUBindGroupLayoutDescriptor, GPUBufferDescriptor,
35 GPUCommandEncoderDescriptor, GPUComputePipelineDescriptor, GPUDeviceLostReason,
36 GPUDeviceMethods, GPUErrorFilter, GPUPipelineErrorReason, GPUPipelineLayoutDescriptor,
37 GPURenderBundleEncoderDescriptor, GPURenderPipelineDescriptor, GPUSamplerDescriptor,
38 GPUShaderModuleDescriptor, GPUTextureDescriptor, GPUTextureFormat, GPUUncapturedErrorEventInit,
39 GPUVertexStepMode,
40};
41use crate::dom::bindings::codegen::UnionTypes::GPUPipelineLayoutOrGPUAutoLayoutMode;
42use crate::dom::bindings::error::{Error, Fallible};
43use crate::dom::bindings::inheritance::Castable;
44use crate::dom::bindings::refcounted::Trusted;
45use crate::dom::bindings::reflector::DomGlobal;
46use crate::dom::bindings::root::{Dom, DomRoot};
47use crate::dom::bindings::str::USVString;
48use crate::dom::bindings::trace::RootedTraceableBox;
49use crate::dom::event::Event;
50use crate::dom::eventtarget::EventTarget;
51use crate::dom::globalscope::GlobalScope;
52use crate::dom::promise::Promise;
53use crate::dom::types::GPUError;
54use crate::dom::webgpu::gpuadapter::GPUAdapter;
55use crate::dom::webgpu::gpuadapterinfo::GPUAdapterInfo;
56use crate::dom::webgpu::gpubindgroup::GPUBindGroup;
57use crate::dom::webgpu::gpubindgrouplayout::GPUBindGroupLayout;
58use crate::dom::webgpu::gpubuffer::GPUBuffer;
59use crate::dom::webgpu::gpucommandencoder::GPUCommandEncoder;
60use crate::dom::webgpu::gpucomputepipeline::GPUComputePipeline;
61use crate::dom::webgpu::gpupipelinelayout::GPUPipelineLayout;
62use crate::dom::webgpu::gpuqueue::GPUQueue;
63use crate::dom::webgpu::gpurenderbundleencoder::GPURenderBundleEncoder;
64use crate::dom::webgpu::gpurenderpipeline::GPURenderPipeline;
65use crate::dom::webgpu::gpusampler::GPUSampler;
66use crate::dom::webgpu::gpushadermodule::GPUShaderModule;
67use crate::dom::webgpu::gpusupportedfeatures::GPUSupportedFeatures;
68use crate::dom::webgpu::gputexture::GPUTexture;
69use crate::dom::webgpu::gpuuncapturederrorevent::GPUUncapturedErrorEvent;
70use crate::routed_promise::{RoutedPromiseListener, callback_promise};
71
72#[derive(JSTraceable, MallocSizeOf)]
73struct DroppableGPUDevice {
74 #[no_trace]
75 channel: WebGPU,
76 #[no_trace]
77 device: WebGPUDevice,
78}
79
80impl Drop for DroppableGPUDevice {
81 fn drop(&mut self) {
82 if let Err(e) = self
83 .channel
84 .0
85 .send(WebGPURequest::DropDevice(self.device.0))
86 {
87 warn!("Failed to send DropDevice ({:?}) ({})", self.device.0, e);
88 }
89 }
90}
91
92#[dom_struct]
93pub(crate) struct GPUDevice {
94 eventtarget: EventTarget,
95 adapter: Dom<GPUAdapter>,
96 #[ignore_malloc_size_of = "mozjs"]
97 extensions: Heap<*mut JSObject>,
98 features: Dom<GPUSupportedFeatures>,
99 limits: Dom<GPUSupportedLimits>,
100 adapter_info: Dom<GPUAdapterInfo>,
101 label: DomRefCell<USVString>,
102 default_queue: Dom<GPUQueue>,
103 #[conditional_malloc_size_of]
105 lost_promise: DomRefCell<Rc<Promise>>,
106 valid: Cell<bool>,
107 droppable: DroppableGPUDevice,
108}
109
110pub(crate) enum PipelineLayout {
111 Implicit,
112 Explicit(PipelineLayoutId),
113}
114
115impl PipelineLayout {
116 pub(crate) fn explicit(&self) -> Option<PipelineLayoutId> {
117 match self {
118 PipelineLayout::Explicit(layout_id) => Some(*layout_id),
119 PipelineLayout::Implicit => None,
120 }
121 }
122}
123
124impl GPUDevice {
125 #[allow(clippy::too_many_arguments)]
126 fn new_inherited(
127 channel: WebGPU,
128 adapter: &GPUAdapter,
129 features: &GPUSupportedFeatures,
130 limits: &GPUSupportedLimits,
131 adapter_info: &GPUAdapterInfo,
132 device: WebGPUDevice,
133 queue: &GPUQueue,
134 label: String,
135 lost_promise: Rc<Promise>,
136 ) -> Self {
137 Self {
138 eventtarget: EventTarget::new_inherited(),
139 adapter: Dom::from_ref(adapter),
140 extensions: Heap::default(),
141 features: Dom::from_ref(features),
142 limits: Dom::from_ref(limits),
143 adapter_info: Dom::from_ref(adapter_info),
144 label: DomRefCell::new(USVString::from(label)),
145 default_queue: Dom::from_ref(queue),
146 lost_promise: DomRefCell::new(lost_promise),
147 valid: Cell::new(true),
148 droppable: DroppableGPUDevice { channel, device },
149 }
150 }
151
152 #[allow(clippy::too_many_arguments)]
153 pub(crate) fn new(
154 cx: &mut JSContext,
155 global: &GlobalScope,
156 channel: WebGPU,
157 adapter: &GPUAdapter,
158 extensions: HandleObject,
159 features: wgpu_types::Features,
160 limits: wgpu_types::Limits,
161 device: WebGPUDevice,
162 queue: WebGPUQueue,
163 label: String,
164 ) -> DomRoot<Self> {
165 let queue = GPUQueue::new(cx, global, channel.clone(), queue);
166 let limits = GPUSupportedLimits::new(cx, global, limits);
167 let features = GPUSupportedFeatures::Constructor(cx, global, None, features).unwrap();
168 let adapter_info = GPUAdapterInfo::clone_from(cx, global, &adapter.Info());
169 let lost_promise = Promise::new2(cx, global);
170 let device = reflect_dom_object_with_cx(
171 Box::new(GPUDevice::new_inherited(
172 channel,
173 adapter,
174 &features,
175 &limits,
176 &adapter_info,
177 device,
178 &queue,
179 label,
180 lost_promise,
181 )),
182 global,
183 cx,
184 );
185 queue.set_device(&device);
186 device.extensions.set(*extensions);
187 device
188 }
189}
190
191impl GPUDevice {
192 pub(crate) fn id(&self) -> WebGPUDevice {
193 self.droppable.device
194 }
195
196 pub(crate) fn queue_id(&self) -> WebGPUQueue {
197 self.default_queue.id()
198 }
199
200 pub(crate) fn channel(&self) -> WebGPU {
201 self.droppable.channel.clone()
202 }
203
204 pub(crate) fn dispatch_error(&self, error: webgpu_traits::Error) {
205 if let Err(e) = self.droppable.channel.0.send(WebGPURequest::DispatchError {
206 device_id: self.id().0,
207 error,
208 }) {
209 warn!("Failed to send WebGPURequest::DispatchError due to {e:?}");
210 }
211 }
212
213 pub(crate) fn fire_uncaptured_error(&self, error: webgpu_traits::Error) {
215 let this = Trusted::new(self);
216
217 self.global().task_manager().webgpu_task_source().queue(
220 task!(fire_uncaptured_error: move |cx| {
221 let this = this.root();
222 let error = GPUError::from_error(cx, &this.global(), error);
223
224 let event = GPUUncapturedErrorEvent::new(cx,
225 &this.global(),
226 atom!("uncapturederror"),
227 &GPUUncapturedErrorEventInit {
228 error,
229 parent: EventInit::empty(),
230 },
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(
446 &self,
447 cx: &mut JSContext,
448 descriptor: &GPUBufferDescriptor,
449 ) -> Fallible<DomRoot<GPUBuffer>> {
450 GPUBuffer::create(cx, self, descriptor)
451 }
452
453 fn CreateBindGroupLayout(
455 &self,
456 cx: &mut JSContext,
457 descriptor: &GPUBindGroupLayoutDescriptor,
458 ) -> Fallible<DomRoot<GPUBindGroupLayout>> {
459 GPUBindGroupLayout::create(cx, self, descriptor)
460 }
461
462 fn CreatePipelineLayout(
464 &self,
465 cx: &mut JSContext,
466 descriptor: &GPUPipelineLayoutDescriptor,
467 ) -> DomRoot<GPUPipelineLayout> {
468 GPUPipelineLayout::create(cx, self, descriptor)
469 }
470
471 fn CreateBindGroup(
473 &self,
474 cx: &mut JSContext,
475 descriptor: &GPUBindGroupDescriptor,
476 ) -> DomRoot<GPUBindGroup> {
477 GPUBindGroup::create(cx, self, descriptor)
478 }
479
480 fn CreateShaderModule(
482 &self,
483 cx: &mut CurrentRealm<'_>,
484 descriptor: RootedTraceableBox<GPUShaderModuleDescriptor>,
485 ) -> DomRoot<GPUShaderModule> {
486 GPUShaderModule::create(cx, self, descriptor)
487 }
488
489 fn CreateComputePipeline(
491 &self,
492 cx: &mut JSContext,
493 descriptor: &GPUComputePipelineDescriptor,
494 ) -> DomRoot<GPUComputePipeline> {
495 let compute_pipeline = GPUComputePipeline::create(self, descriptor, None);
496 GPUComputePipeline::new(
497 cx,
498 &self.global(),
499 compute_pipeline,
500 descriptor.parent.parent.label.clone(),
501 self,
502 )
503 }
504
505 fn CreateComputePipelineAsync(
507 &self,
508 cx: &mut CurrentRealm<'_>,
509 descriptor: &GPUComputePipelineDescriptor,
510 ) -> Rc<Promise> {
511 let promise = Promise::new_in_realm(cx);
512 let callback = callback_promise(
513 &promise,
514 self,
515 self.global().task_manager().dom_manipulation_task_source(),
516 );
517 GPUComputePipeline::create(self, descriptor, Some(callback));
518 promise
519 }
520
521 fn CreateCommandEncoder(
523 &self,
524 cx: &mut JSContext,
525 descriptor: &GPUCommandEncoderDescriptor,
526 ) -> DomRoot<GPUCommandEncoder> {
527 GPUCommandEncoder::create(cx, self, descriptor)
528 }
529
530 fn CreateTexture(
532 &self,
533 cx: &mut JSContext,
534 descriptor: &GPUTextureDescriptor,
535 ) -> Fallible<DomRoot<GPUTexture>> {
536 GPUTexture::create(cx, self, descriptor)
537 }
538
539 fn CreateSampler(
541 &self,
542 cx: &mut JSContext,
543 descriptor: &GPUSamplerDescriptor,
544 ) -> DomRoot<GPUSampler> {
545 GPUSampler::create(cx, self, descriptor)
546 }
547
548 fn CreateRenderPipeline(
550 &self,
551 cx: &mut JSContext,
552 descriptor: &GPURenderPipelineDescriptor,
553 ) -> Fallible<DomRoot<GPURenderPipeline>> {
554 let desc = self.parse_render_pipeline(descriptor)?;
555 let render_pipeline = GPURenderPipeline::create(self, desc, None)?;
556 Ok(GPURenderPipeline::new(
557 cx,
558 &self.global(),
559 render_pipeline,
560 descriptor.parent.parent.label.clone(),
561 self,
562 ))
563 }
564
565 fn CreateRenderPipelineAsync(
567 &self,
568 cx: &mut CurrentRealm<'_>,
569 descriptor: &GPURenderPipelineDescriptor,
570 ) -> Fallible<Rc<Promise>> {
571 let desc = self.parse_render_pipeline(descriptor)?;
572 let promise = Promise::new_in_realm(cx);
573 let callback = callback_promise(
574 &promise,
575 self,
576 self.global().task_manager().dom_manipulation_task_source(),
577 );
578 GPURenderPipeline::create(self, desc, Some(callback))?;
579 Ok(promise)
580 }
581
582 fn CreateRenderBundleEncoder(
584 &self,
585 cx: &mut JSContext,
586 descriptor: &GPURenderBundleEncoderDescriptor,
587 ) -> Fallible<DomRoot<GPURenderBundleEncoder>> {
588 GPURenderBundleEncoder::create(cx, self, descriptor)
589 }
590
591 fn PushErrorScope(&self, filter: GPUErrorFilter) {
593 if self
594 .droppable
595 .channel
596 .0
597 .send(WebGPURequest::PushErrorScope {
598 device_id: self.id().0,
599 filter: filter.as_webgpu(),
600 })
601 .is_err()
602 {
603 warn!("Failed sending WebGPURequest::PushErrorScope");
604 }
605 }
606
607 fn PopErrorScope(&self, cx: &mut CurrentRealm<'_>) -> Rc<Promise> {
609 let promise = Promise::new_in_realm(cx);
610 let callback = callback_promise(
611 &promise,
612 self,
613 self.global().task_manager().dom_manipulation_task_source(),
614 );
615 if self
616 .droppable
617 .channel
618 .0
619 .send(WebGPURequest::PopErrorScope {
620 device_id: self.id().0,
621 callback,
622 })
623 .is_err()
624 {
625 warn!("Error when sending WebGPURequest::PopErrorScope");
626 }
627 promise
628 }
629
630 event_handler!(uncapturederror, GetOnuncapturederror, SetOnuncapturederror);
632
633 fn Destroy(&self) {
635 if self.valid.get() {
636 self.valid.set(false);
637
638 if let Err(e) = self
639 .droppable
640 .channel
641 .0
642 .send(WebGPURequest::DestroyDevice(self.id().0))
643 {
644 warn!("Failed to send DestroyDevice ({:?}) ({})", self.id().0, e);
645 }
646 }
647 }
648}
649
650impl RoutedPromiseListener<WebGPUPoppedErrorScopeResponse> for GPUDevice {
651 fn handle_response(
652 &self,
653 cx: &mut js::context::JSContext,
654 response: WebGPUPoppedErrorScopeResponse,
655 promise: &Rc<Promise>,
656 ) {
657 match response {
658 Ok(None) | Err(PopError::Lost) => {
659 promise.resolve_native_with_cx(cx, &None::<Option<GPUError>>)
660 },
661 Err(PopError::Empty) => promise.reject_error_with_cx(cx, Error::Operation(None)),
662 Ok(Some(error)) => {
663 let error = GPUError::from_error(cx, &self.global(), error);
664 promise.resolve_native_with_cx(cx, &error);
665 },
666 }
667 }
668}
669
670impl RoutedPromiseListener<WebGPUComputePipelineResponse> for GPUDevice {
671 fn handle_response(
672 &self,
673 cx: &mut js::context::JSContext,
674 response: WebGPUComputePipelineResponse,
675 promise: &Rc<Promise>,
676 ) {
677 match response {
678 Ok(pipeline) => {
679 let gpu_compute_pipeline = GPUComputePipeline::new(
680 cx,
681 &self.global(),
682 WebGPUComputePipeline(pipeline.id),
683 pipeline.label.into(),
684 self,
685 );
686 promise.resolve_native_with_cx(cx, &gpu_compute_pipeline)
687 },
688 Err(webgpu_traits::Error::Validation(msg)) => {
689 let gpu_pipeline_error = GPUPipelineError::new(
690 cx,
691 &self.global(),
692 msg.into(),
693 GPUPipelineErrorReason::Validation,
694 );
695 promise.reject_native_with_cx(cx, &gpu_pipeline_error)
696 },
697 Err(webgpu_traits::Error::OutOfMemory(msg) | webgpu_traits::Error::Internal(msg)) => {
698 let gpu_pipeline_error = GPUPipelineError::new(
699 cx,
700 &self.global(),
701 msg.into(),
702 GPUPipelineErrorReason::Internal,
703 );
704 promise.reject_native_with_cx(cx, &gpu_pipeline_error)
705 },
706 }
707 }
708}
709
710impl RoutedPromiseListener<WebGPURenderPipelineResponse> for GPUDevice {
711 fn handle_response(
712 &self,
713 cx: &mut js::context::JSContext,
714 response: WebGPURenderPipelineResponse,
715 promise: &Rc<Promise>,
716 ) {
717 match response {
718 Ok(pipeline) => {
719 let gpu_pipeline = GPURenderPipeline::new(
720 cx,
721 &self.global(),
722 WebGPURenderPipeline(pipeline.id),
723 pipeline.label.into(),
724 self,
725 );
726 promise.resolve_native_with_cx(cx, &gpu_pipeline)
727 },
728 Err(webgpu_traits::Error::Validation(msg)) => {
729 let pipeline_error = GPUPipelineError::new(
730 cx,
731 &self.global(),
732 msg.into(),
733 GPUPipelineErrorReason::Validation,
734 );
735
736 promise.reject_native_with_cx(cx, &pipeline_error)
737 },
738 Err(webgpu_traits::Error::OutOfMemory(msg) | webgpu_traits::Error::Internal(msg)) => {
739 let pipeline_error = GPUPipelineError::new(
740 cx,
741 &self.global(),
742 msg.into(),
743 GPUPipelineErrorReason::Internal,
744 );
745 promise.reject_native_with_cx(cx, &pipeline_error)
746 },
747 }
748 }
749}