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