Skip to main content

webgpu/
wgpu_thread.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
5//! Data and main loop of WebGPU thread.
6
7use std::borrow::Cow;
8use std::slice;
9use std::sync::{Arc, Mutex};
10
11use log::{info, warn};
12use paint_api::{CrossProcessPaintApi, WebRenderExternalImageIdManager, WebRenderImageHandlerType};
13use rustc_hash::FxHashMap;
14use servo_base::generic_channel::{GenericReceiver, GenericSender, GenericSharedMemory};
15use servo_base::id::PipelineId;
16use servo_config::pref;
17use webgpu_traits::{
18    Adapter, DeviceLostReason, Error, ErrorScope, Mapping, Pipeline, PopError,
19    ShaderCompilationInfo, WebGPU, WebGPUAdapter, WebGPUContextId, WebGPUDevice, WebGPUMsg,
20    WebGPUQueue, WebGPURequest, apply_render_bundle_command, apply_render_command,
21};
22use webrender_api::ExternalImageId;
23use wgc::command::ComputePassDescriptor;
24use wgc::device::DeviceDescriptor;
25use wgc::id;
26use wgc::id::DeviceId;
27use wgc::pipeline::ShaderModuleDescriptor;
28use wgc::resource::BufferMapOperation;
29pub use wgpu_core as wgc;
30use wgpu_core::command::{RenderPassDescriptor, TexelCopyTextureInfo};
31use wgpu_core::resource::{BufferAccessResult, TextureViewDescriptor};
32pub use wgpu_types as wgt;
33use wgpu_types::error::WebGpuError;
34use wgpu_types::{
35    ExperimentalFeatures, Extent3d, ExternalTextureDescriptor, ExternalTextureFormat,
36    ExternalTextureTransferFunction, MemoryHints, Origin3d, TexelCopyBufferLayout, TextureAspect,
37    TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
38};
39use wgt::InstanceDescriptor;
40
41use crate::canvas_context::WebGpuExternalImageMap;
42use crate::poll_thread::Poller;
43
44#[derive(Eq, Hash, PartialEq)]
45pub(crate) struct DeviceScope {
46    pub device_id: DeviceId,
47    pub pipeline_id: PipelineId,
48    /// <https://www.w3.org/TR/webgpu/#dom-gpudevice-errorscopestack-slot>
49    ///
50    /// Is `None` if device is lost
51    pub error_scope_stack: Option<Vec<ErrorScope>>,
52    // TODO:
53    // Queue for this device (to remove transmutes)
54    // queue_id: QueueId,
55    // Poller for this device
56    // poller: Poller,
57}
58
59impl DeviceScope {
60    pub fn new(device_id: DeviceId, pipeline_id: PipelineId) -> Self {
61        Self {
62            device_id,
63            pipeline_id,
64            error_scope_stack: Some(Vec::new()),
65        }
66    }
67}
68
69#[expect(clippy::upper_case_acronyms)] // Name of the library
70pub(crate) struct WGPU {
71    receiver: GenericReceiver<WebGPURequest>,
72    sender: GenericSender<WebGPURequest>,
73    pub(crate) script_sender: GenericSender<WebGPUMsg>,
74    pub(crate) global: Arc<wgc::global::Global>,
75    devices: Arc<Mutex<FxHashMap<DeviceId, DeviceScope>>>,
76    pub(crate) paint_api: CrossProcessPaintApi,
77    pub(crate) webrender_external_image_id_manager: WebRenderExternalImageIdManager,
78    pub(crate) wgpu_image_map: WebGpuExternalImageMap,
79    /// Provides access to poller thread
80    pub(crate) poller: Poller,
81}
82
83impl WGPU {
84    pub(crate) fn new(
85        receiver: GenericReceiver<WebGPURequest>,
86        sender: GenericSender<WebGPURequest>,
87        script_sender: GenericSender<WebGPUMsg>,
88        paint_api: CrossProcessPaintApi,
89        webrender_external_image_id_manager: WebRenderExternalImageIdManager,
90        wgpu_image_map: WebGpuExternalImageMap,
91    ) -> Self {
92        let backend_pref = pref!(dom_webgpu_wgpu_backend);
93        let backends = if backend_pref.is_empty() {
94            wgt::Backends::PRIMARY
95        } else {
96            info!(
97                "Selecting backends based on dom.webgpu.wgpu_backend pref: {:?}",
98                backend_pref
99            );
100            wgt::Backends::from_comma_list(&backend_pref)
101        };
102        let global = Arc::new(wgc::global::Global::new(
103            "wgpu-core",
104            InstanceDescriptor {
105                backends,
106                backend_options: wgt::BackendOptions {
107                    gl: wgt::GlBackendOptions {
108                        gles_minor_version: wgt::Gles3MinorVersion::Automatic,
109                        fence_behavior: wgt::GlFenceBehavior::Normal,
110                        debug_fns: wgt::GlDebugFns::Auto,
111                    },
112                    dx12: wgt::Dx12BackendOptions {
113                        ..Default::default()
114                    },
115                    noop: wgt::NoopBackendOptions::default(),
116                },
117
118                flags: wgt::InstanceFlags::from_build_config() |
119                    wgt::InstanceFlags::AUTOMATIC_TIMESTAMP_NORMALIZATION |
120                    wgt::InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
121                // TODO(sagudev): firefox actually sets this, but it can cause OOM for us
122                // meaning that we are likely leaking something
123                memory_budget_thresholds: wgt::MemoryBudgetThresholds {
124                    for_resource_creation: Some(95),
125                    for_device_loss: Some(99),
126                },
127                display: None,
128            },
129            None,
130        ));
131        WGPU {
132            poller: Poller::new(Arc::clone(&global)),
133            receiver,
134            sender,
135            script_sender,
136            global,
137            devices: Arc::new(Mutex::new(FxHashMap::default())),
138            paint_api,
139            webrender_external_image_id_manager,
140            wgpu_image_map,
141        }
142    }
143
144    pub(crate) fn run(&mut self) {
145        loop {
146            if let Ok(msg) = self.receiver.recv() {
147                log::trace!("recv: {msg:?}");
148                match msg {
149                    WebGPURequest::SetImageKey {
150                        context_id,
151                        image_key,
152                    } => self.set_image_key(context_id, image_key),
153                    WebGPURequest::BufferMapAsync {
154                        callback: sender,
155                        buffer_id,
156                        device_id,
157                        host_map,
158                        offset,
159                        size,
160                    } => {
161                        let glob = Arc::clone(&self.global);
162                        let resp_sender = sender.clone();
163                        let token = self.poller.token();
164                        let callback = Box::from(move |result: BufferAccessResult| {
165                            drop(token);
166                            let response = result.and_then(|_| {
167                                let global = &glob;
168                                let (slice_pointer, range_size) =
169                                    global.buffer_get_mapped_range(buffer_id, offset, size)?;
170                                // SAFETY: guarantee to be safe from wgpu
171                                let data = unsafe {
172                                    slice::from_raw_parts(
173                                        slice_pointer.as_ptr(),
174                                        range_size as usize,
175                                    )
176                                };
177
178                                Ok(Mapping {
179                                    data: GenericSharedMemory::from_bytes(data),
180                                    range: offset..offset + range_size,
181                                    mode: host_map,
182                                })
183                            });
184                            if let Err(e) = resp_sender.send(response) {
185                                warn!("Could not send BufferMapAsync Response ({})", e);
186                            }
187                        });
188
189                        let operation = BufferMapOperation {
190                            host: host_map,
191                            callback: Some(callback),
192                        };
193                        let global = &self.global;
194                        let result = global.buffer_map_async(buffer_id, offset, size, operation);
195                        self.poller.wake();
196                        // Per spec we also need to raise validation error here
197                        self.maybe_dispatch_wgpu_error(device_id, result.err());
198                    },
199                    WebGPURequest::CommandEncoderFinish {
200                        command_encoder_id,
201                        device_id,
202                        desc,
203                        command_buffer_id,
204                    } => {
205                        let global = &self.global;
206                        let (_, error) = global.command_encoder_finish(
207                            command_encoder_id,
208                            &desc,
209                            Some(command_buffer_id),
210                        );
211                        self.maybe_dispatch_wgpu_error(device_id, error.map(|(_, e)| e));
212                    },
213                    WebGPURequest::CopyBufferToBuffer {
214                        device_id,
215                        command_encoder_id,
216                        source_id,
217                        source_offset,
218                        destination_id,
219                        destination_offset,
220                        size,
221                    } => {
222                        let global = &self.global;
223                        let result = global.command_encoder_copy_buffer_to_buffer(
224                            command_encoder_id,
225                            source_id,
226                            source_offset,
227                            destination_id,
228                            destination_offset,
229                            Some(size),
230                        );
231                        self.maybe_dispatch_wgpu_error(device_id, result.err());
232                    },
233                    WebGPURequest::CopyBufferToTexture {
234                        device_id,
235                        command_encoder_id,
236                        source,
237                        destination,
238                        copy_size,
239                    } => {
240                        let global = &self.global;
241                        let result = global.command_encoder_copy_buffer_to_texture(
242                            command_encoder_id,
243                            &source,
244                            &destination,
245                            &copy_size,
246                        );
247                        self.maybe_dispatch_wgpu_error(device_id, result.err());
248                    },
249                    WebGPURequest::CopyTextureToBuffer {
250                        device_id,
251                        command_encoder_id,
252                        source,
253                        destination,
254                        copy_size,
255                    } => {
256                        let global = &self.global;
257                        let result = global.command_encoder_copy_texture_to_buffer(
258                            command_encoder_id,
259                            &source,
260                            &destination,
261                            &copy_size,
262                        );
263                        self.maybe_dispatch_wgpu_error(device_id, result.err());
264                    },
265                    WebGPURequest::CopyTextureToTexture {
266                        device_id,
267                        command_encoder_id,
268                        source,
269                        destination,
270                        copy_size,
271                    } => {
272                        let global = &self.global;
273                        let result = global.command_encoder_copy_texture_to_texture(
274                            command_encoder_id,
275                            &source,
276                            &destination,
277                            &copy_size,
278                        );
279                        self.maybe_dispatch_wgpu_error(device_id, result.err());
280                    },
281                    WebGPURequest::CommandEncoderPushDebugGroup {
282                        device_id,
283                        command_encoder_id,
284                        label,
285                    } => {
286                        let result = self
287                            .global
288                            .command_encoder_push_debug_group(command_encoder_id, &label);
289                        self.maybe_dispatch_wgpu_error(device_id, result.err());
290                    },
291                    WebGPURequest::CommandEncoderPopDebugGroup {
292                        device_id,
293                        command_encoder_id,
294                    } => {
295                        let result = self
296                            .global
297                            .command_encoder_pop_debug_group(command_encoder_id);
298                        self.maybe_dispatch_wgpu_error(device_id, result.err());
299                    },
300                    WebGPURequest::CommandEncoderInsertDebugMarker {
301                        device_id,
302                        command_encoder_id,
303                        label,
304                    } => {
305                        let result = self
306                            .global
307                            .command_encoder_insert_debug_marker(command_encoder_id, &label);
308                        self.maybe_dispatch_wgpu_error(device_id, result.err());
309                    },
310                    WebGPURequest::CreateBindGroup {
311                        device_id,
312                        bind_group_id,
313                        descriptor,
314                    } => {
315                        let global = &self.global;
316                        let (_, error) = global.device_create_bind_group(
317                            device_id,
318                            &descriptor,
319                            Some(bind_group_id),
320                        );
321                        self.maybe_dispatch_wgpu_error(device_id, error);
322                    },
323                    WebGPURequest::CreateBindGroupLayout {
324                        device_id,
325                        bind_group_layout_id,
326                        descriptor,
327                    } => {
328                        let global = &self.global;
329                        if let Some(desc) = descriptor {
330                            let (_, error) = global.device_create_bind_group_layout(
331                                device_id,
332                                &desc,
333                                Some(bind_group_layout_id),
334                            );
335
336                            self.maybe_dispatch_wgpu_error(device_id, error);
337                        }
338                    },
339                    WebGPURequest::CreateBuffer {
340                        device_id,
341                        buffer_id,
342                        descriptor,
343                    } => {
344                        let global = &self.global;
345                        let (_, error) =
346                            global.device_create_buffer(device_id, &descriptor, Some(buffer_id));
347
348                        self.maybe_dispatch_wgpu_error(device_id, error);
349                    },
350                    WebGPURequest::CreateCommandEncoder {
351                        device_id,
352                        command_encoder_id,
353                        desc,
354                    } => {
355                        let global = &self.global;
356                        let (_, error) = global.device_create_command_encoder(
357                            device_id,
358                            &desc,
359                            Some(command_encoder_id),
360                        );
361
362                        self.maybe_dispatch_wgpu_error(device_id, error);
363                    },
364                    WebGPURequest::CreateComputePipeline {
365                        device_id,
366                        compute_pipeline_id,
367                        descriptor,
368                        async_sender: sender,
369                    } => {
370                        let global = &self.global;
371                        let (_, error) = global.device_create_compute_pipeline(
372                            device_id,
373                            &descriptor,
374                            Some(compute_pipeline_id),
375                        );
376                        if let Some(sender) = sender {
377                            let res = match error.and_then(Error::from_wgpu_error) {
378                                // if device is lost we must return pipeline and not raise any error
379                                None => Ok(Pipeline {
380                                    id: compute_pipeline_id,
381                                    label: descriptor.label.unwrap_or_default().to_string(),
382                                }),
383                                Some(e) => Err(e),
384                            };
385                            if let Err(e) = sender.send(res) {
386                                warn!("Failed sending WebGPUComputePipelineResponse {e:?}");
387                            }
388                        } else {
389                            self.maybe_dispatch_wgpu_error(device_id, error);
390                        }
391                    },
392                    WebGPURequest::CreatePipelineLayout {
393                        device_id,
394                        pipeline_layout_id,
395                        descriptor,
396                    } => {
397                        let global = &self.global;
398                        let (_, error) = global.device_create_pipeline_layout(
399                            device_id,
400                            &descriptor,
401                            Some(pipeline_layout_id),
402                        );
403                        self.maybe_dispatch_wgpu_error(device_id, error);
404                    },
405                    WebGPURequest::CreateRenderPipeline {
406                        device_id,
407                        render_pipeline_id,
408                        descriptor,
409                        async_sender: sender,
410                    } => {
411                        let global = &self.global;
412                        let (_, error) = global.device_create_render_pipeline(
413                            device_id,
414                            &descriptor,
415                            Some(render_pipeline_id),
416                        );
417
418                        if let Some(sender) = sender {
419                            let res = match error.and_then(Error::from_wgpu_error) {
420                                // if device is lost we must return pipeline and not raise any error
421                                None => Ok(Pipeline {
422                                    id: render_pipeline_id,
423                                    label: descriptor.label.unwrap_or_default().to_string(),
424                                }),
425                                Some(e) => Err(e),
426                            };
427                            if let Err(e) = sender.send(res) {
428                                warn!("Failed sending WebGPURenderPipelineResponse {e:?}");
429                            }
430                        } else {
431                            self.maybe_dispatch_wgpu_error(device_id, error);
432                        }
433                    },
434                    WebGPURequest::CreateSampler {
435                        device_id,
436                        sampler_id,
437                        descriptor,
438                    } => {
439                        let global = &self.global;
440                        let (_, error) =
441                            global.device_create_sampler(device_id, &descriptor, Some(sampler_id));
442                        self.maybe_dispatch_wgpu_error(device_id, error);
443                    },
444                    WebGPURequest::CreateShaderModule {
445                        device_id,
446                        program_id,
447                        program,
448                        label,
449                        callback: sender,
450                    } => {
451                        let global = &self.global;
452                        let source =
453                            wgpu_core::pipeline::ShaderModuleSource::Wgsl(Cow::Borrowed(&program));
454                        let desc = ShaderModuleDescriptor {
455                            label: label.map(|s| s.into()),
456                            runtime_checks: wgt::ShaderRuntimeChecks::checked(),
457                        };
458                        let (_, error) = global.device_create_shader_module(
459                            device_id,
460                            &desc,
461                            source,
462                            Some(program_id),
463                        );
464                        if let Err(e) = sender.send(
465                            error
466                                .as_ref()
467                                .map(|e| ShaderCompilationInfo::from(e, &program)),
468                        ) {
469                            warn!("Failed to send CompilationInfo {e:?}");
470                        }
471                        self.maybe_dispatch_wgpu_error(device_id, error);
472                    },
473                    WebGPURequest::CreateContext {
474                        buffer_ids,
475                        size,
476                        sender,
477                    } => {
478                        let id = self
479                            .webrender_external_image_id_manager
480                            .next_id(WebRenderImageHandlerType::WebGpu);
481                        let context_id = WebGPUContextId(id.0);
482
483                        if let Err(error) = sender.send(context_id) {
484                            warn!("Failed to send ContextId to new context ({error})");
485                        };
486
487                        self.create_context(context_id, size, buffer_ids);
488                    },
489                    WebGPURequest::Present {
490                        context_id,
491                        pending_texture,
492                        size,
493                        canvas_epoch,
494                    } => {
495                        self.present(context_id, pending_texture, size, canvas_epoch);
496                    },
497                    WebGPURequest::GetImage {
498                        context_id,
499                        pending_texture,
500                        sender,
501                    } => self.get_image(context_id, pending_texture, sender),
502                    WebGPURequest::ValidateTextureDescriptor {
503                        device_id,
504                        texture_id,
505                        descriptor,
506                    } => {
507                        // https://gpuweb.github.io/gpuweb/#dom-gpucanvascontext-configure
508                        // validating TextureDescriptor by creating dummy texture
509                        let global = &self.global;
510                        let (_, error) =
511                            global.device_create_texture(device_id, &descriptor, Some(texture_id));
512                        global.texture_drop(texture_id);
513                        self.poller.wake();
514                        if let Err(e) = self.script_sender.send(WebGPUMsg::FreeTexture(texture_id))
515                        {
516                            warn!("Unable to send FreeTexture({:?}) ({:?})", texture_id, e);
517                        };
518                        self.maybe_dispatch_wgpu_error(device_id, error);
519                    },
520                    WebGPURequest::DestroyContext { context_id } => {
521                        self.destroy_context(context_id);
522                        self.webrender_external_image_id_manager
523                            .remove(&ExternalImageId(context_id.0));
524                    },
525                    WebGPURequest::CreateTexture {
526                        device_id,
527                        texture_id,
528                        descriptor,
529                    } => {
530                        let global = &self.global;
531                        let (_, error) =
532                            global.device_create_texture(device_id, &descriptor, Some(texture_id));
533                        self.maybe_dispatch_wgpu_error(device_id, error);
534                    },
535                    WebGPURequest::CreateTextureView {
536                        texture_id,
537                        texture_view_id,
538                        device_id,
539                        descriptor,
540                    } => {
541                        let global = &self.global;
542                        if let Some(desc) = descriptor {
543                            let (_, error) = global.texture_create_view(
544                                texture_id,
545                                &desc,
546                                Some(texture_view_id),
547                            );
548
549                            self.maybe_dispatch_wgpu_error(device_id, error);
550                        }
551                    },
552                    WebGPURequest::DestroyBuffer(buffer) => {
553                        let global = &self.global;
554                        global.buffer_destroy(buffer);
555                    },
556                    WebGPURequest::DestroyDevice(device) => {
557                        let global = &self.global;
558                        global.device_destroy(device);
559                        // Wake poller thread to trigger DeviceLostClosure
560                        self.poller.wake();
561                    },
562                    WebGPURequest::DestroyTexture(texture_id) => {
563                        let global = &self.global;
564                        global.texture_destroy(texture_id);
565                    },
566                    WebGPURequest::Exit(sender) => {
567                        if let Err(e) = sender.send(()) {
568                            warn!("Failed to send response to WebGPURequest::Exit ({})", e)
569                        }
570                        break;
571                    },
572                    WebGPURequest::DropCommandEncoder(id) => {
573                        let global = &self.global;
574                        global.command_encoder_drop(id);
575                        if let Err(e) = self.script_sender.send(WebGPUMsg::FreeCommandEncoder(id)) {
576                            warn!("Unable to send FreeCommandEncoder({:?}) ({:?})", id, e);
577                        };
578                    },
579                    WebGPURequest::DropCommandBuffer(id) => {
580                        let global = &self.global;
581                        global.command_buffer_drop(id);
582                        if let Err(e) = self.script_sender.send(WebGPUMsg::FreeCommandBuffer(id)) {
583                            warn!("Unable to send FreeCommandBuffer({:?}) ({:?})", id, e);
584                        };
585                    },
586                    WebGPURequest::DropDevice(device_id) => {
587                        let global = &self.global;
588                        global.device_drop(device_id);
589                        let device_scope = self
590                            .devices
591                            .lock()
592                            .unwrap()
593                            .remove(&device_id)
594                            .expect("Device should not be dropped by this point");
595                        if let Err(e) = self.script_sender.send(WebGPUMsg::FreeDevice {
596                            device_id,
597                            pipeline_id: device_scope.pipeline_id,
598                        }) {
599                            warn!("Unable to send FreeDevice({:?}) ({:?})", device_id, e);
600                        };
601                    },
602                    WebGPURequest::RequestAdapter {
603                        sender,
604                        options,
605                        adapter_id,
606                    } => {
607                        let global = &self.global;
608                        let response = self
609                            .global
610                            .request_adapter(&options, wgt::Backends::all(), Some(adapter_id))
611                            .map(|adapter_id| {
612                                // TODO: can we do this lazily
613                                let adapter_info = global.adapter_get_info(adapter_id);
614                                let limits = global.adapter_limits(adapter_id);
615                                let features = global.adapter_features(adapter_id);
616                                Adapter {
617                                    adapter_info,
618                                    adapter_id: WebGPUAdapter(adapter_id),
619                                    features,
620                                    limits,
621                                    channel: WebGPU(self.sender.clone()),
622                                }
623                            })
624                            .map_err(|err| err.to_string());
625
626                        if let Err(e) = sender.send(Some(response)) {
627                            warn!(
628                                "Failed to send response to WebGPURequest::RequestAdapter ({})",
629                                e
630                            )
631                        }
632                    },
633                    WebGPURequest::RequestDevice {
634                        sender,
635                        adapter_id,
636                        descriptor,
637                        device_id,
638                        queue_id,
639                        pipeline_id,
640                    } => {
641                        let mut desc = DeviceDescriptor {
642                            label: descriptor.label.as_ref().map(crate::Cow::from),
643                            required_features: descriptor.required_features,
644                            required_limits: descriptor.required_limits.clone(),
645                            memory_hints: MemoryHints::MemoryUsage,
646                            trace: wgpu_types::Trace::Off,
647                            experimental_features: ExperimentalFeatures::disabled(),
648                        };
649                        let global = &self.global;
650                        // enable external texture support if available
651                        let features = global.adapter_features(adapter_id.0);
652                        if features.contains(wgpu_types::Features::EXTERNAL_TEXTURE) {
653                            desc.required_features |= wgpu_types::Features::EXTERNAL_TEXTURE;
654                        }
655                        let device = WebGPUDevice(device_id);
656                        let queue = WebGPUQueue(queue_id);
657                        let result = global
658                            .adapter_request_device(
659                                adapter_id.0,
660                                &desc,
661                                Some(device_id),
662                                Some(queue_id),
663                            )
664                            .map(|_| {
665                                {
666                                    self.devices.lock().unwrap().insert(
667                                        device_id,
668                                        DeviceScope::new(device_id, pipeline_id),
669                                    );
670                                }
671                                let script_sender = self.script_sender.clone();
672                                let devices = Arc::clone(&self.devices);
673                                let callback = Box::from(move |reason, msg| {
674                                    let reason = match reason {
675                                        wgt::DeviceLostReason::Unknown => DeviceLostReason::Unknown,
676                                        wgt::DeviceLostReason::Destroyed => {
677                                            DeviceLostReason::Destroyed
678                                        },
679                                    };
680                                    // make device lost by removing error scopes stack
681                                    let _ = devices
682                                        .lock()
683                                        .unwrap()
684                                        .get_mut(&device_id)
685                                        .expect("Device should not be dropped by this point")
686                                        .error_scope_stack
687                                        .take();
688                                    if let Err(e) = script_sender.send(WebGPUMsg::DeviceLost {
689                                        device,
690                                        pipeline_id,
691                                        reason,
692                                        msg,
693                                    }) {
694                                        warn!("Failed to send WebGPUMsg::DeviceLost: {e}");
695                                    }
696                                });
697                                global.device_set_device_lost_closure(device_id, callback);
698                                let mut descriptor = descriptor;
699                                descriptor.required_limits = global.device_limits(device_id);
700                                descriptor.required_features = global.device_features(device_id);
701                                descriptor
702                            })
703                            .map_err(Into::into);
704
705                        if let Err(e) = sender.send((device, queue, result)) {
706                            warn!(
707                                "Failed to send response to WebGPURequest::RequestDevice ({})",
708                                e
709                            )
710                        }
711                    },
712                    WebGPURequest::BeginComputePass {
713                        command_encoder_id,
714                        compute_pass_id,
715                        label,
716                        timestamp_writes,
717                        device_id,
718                    } => {
719                        let global = &self.global;
720                        let (_, error) = global.command_encoder_begin_compute_pass_with_id(
721                            command_encoder_id,
722                            &ComputePassDescriptor {
723                                label,
724                                timestamp_writes,
725                            },
726                            Some(compute_pass_id),
727                        );
728                        self.maybe_dispatch_wgpu_error(device_id, error);
729                    },
730                    WebGPURequest::ComputePassSetPipeline {
731                        compute_pass_id,
732                        pipeline_id,
733                        device_id,
734                    } => {
735                        let result = self
736                            .global
737                            .compute_pass_set_pipeline_with_id(compute_pass_id, pipeline_id);
738                        self.maybe_dispatch_wgpu_error(device_id, result.err());
739                    },
740                    WebGPURequest::ComputePassSetBindGroup {
741                        compute_pass_id,
742                        index,
743                        bind_group_id,
744                        offsets,
745                        device_id,
746                    } => {
747                        let result = self.global.compute_pass_set_bind_group_with_id(
748                            compute_pass_id,
749                            index,
750                            Some(bind_group_id),
751                            &offsets,
752                        );
753                        self.maybe_dispatch_wgpu_error(device_id, result.err());
754                    },
755                    WebGPURequest::ComputePassDispatchWorkgroups {
756                        compute_pass_id,
757                        x,
758                        y,
759                        z,
760                        device_id,
761                    } => {
762                        let result = self.global.compute_pass_dispatch_workgroups_with_id(
763                            compute_pass_id,
764                            x,
765                            y,
766                            z,
767                        );
768                        self.maybe_dispatch_wgpu_error(device_id, result.err());
769                    },
770                    WebGPURequest::ComputePassDispatchWorkgroupsIndirect {
771                        compute_pass_id,
772                        buffer_id,
773                        offset,
774                        device_id,
775                    } => {
776                        let result = self
777                            .global
778                            .compute_pass_dispatch_workgroups_indirect_with_id(
779                                compute_pass_id,
780                                buffer_id,
781                                offset,
782                            );
783                        self.maybe_dispatch_wgpu_error(device_id, result.err());
784                    },
785                    WebGPURequest::ComputePassPushDebugGroup {
786                        compute_pass_id,
787                        label,
788                        device_id,
789                    } => {
790                        let result = self.global.compute_pass_push_debug_group_with_id(
791                            compute_pass_id,
792                            &label,
793                            0,
794                        );
795                        self.maybe_dispatch_wgpu_error(device_id, result.err());
796                    },
797                    WebGPURequest::ComputePassPopDebugGroup {
798                        compute_pass_id,
799                        device_id,
800                    } => {
801                        let result = self
802                            .global
803                            .compute_pass_pop_debug_group_with_id(compute_pass_id);
804                        self.maybe_dispatch_wgpu_error(device_id, result.err());
805                    },
806                    WebGPURequest::ComputePassInsertDebugMarker {
807                        compute_pass_id,
808                        label,
809                        device_id,
810                    } => {
811                        let result = self.global.compute_pass_insert_debug_marker_with_id(
812                            compute_pass_id,
813                            &label,
814                            0,
815                        );
816                        self.maybe_dispatch_wgpu_error(device_id, result.err());
817                    },
818                    WebGPURequest::EndComputePass {
819                        compute_pass_id,
820                        device_id,
821                    } => {
822                        // https://www.w3.org/TR/2024/WD-webgpu-20240703/#dom-gpucomputepassencoder-end
823                        let result = self.global.compute_pass_end_with_id(compute_pass_id);
824                        self.maybe_dispatch_wgpu_error(device_id, result.err());
825                    },
826                    WebGPURequest::BeginRenderPass {
827                        command_encoder_id,
828                        render_pass_id,
829                        label,
830                        color_attachments,
831                        depth_stencil_attachment,
832                        timestamp_writes,
833                        device_id,
834                    } => {
835                        let global = &self.global;
836                        let desc = &RenderPassDescriptor {
837                            label,
838                            color_attachments: color_attachments.into(),
839                            depth_stencil_attachment,
840                            timestamp_writes,
841                            occlusion_query_set: None,
842                            multiview_mask: None,
843                        };
844                        let (_, error) = global.command_encoder_begin_render_pass_with_id(
845                            command_encoder_id,
846                            desc,
847                            Some(render_pass_id),
848                        );
849                        self.maybe_dispatch_wgpu_error(device_id, error);
850                    },
851                    WebGPURequest::RenderPassCommand {
852                        render_pass_id,
853                        render_command,
854                        device_id,
855                    } => {
856                        let result =
857                            apply_render_command(&self.global, render_pass_id, render_command);
858                        self.maybe_dispatch_wgpu_error(device_id, result.err());
859                    },
860                    WebGPURequest::EndRenderPass {
861                        render_pass_id,
862                        device_id,
863                    } => {
864                        // https://www.w3.org/TR/2024/WD-webgpu-20240703/#dom-gpurenderpassencoder-end
865                        let result = self.global.render_pass_end_with_id(render_pass_id);
866                        self.maybe_dispatch_wgpu_error(device_id, result.err());
867                    },
868                    WebGPURequest::Submit {
869                        device_id,
870                        queue_id,
871                        command_buffers,
872                    } => {
873                        let global = &self.global;
874                        let result = {
875                            let _guard = self.poller.lock();
876                            global.queue_submit(queue_id, &command_buffers)
877                        };
878                        self.maybe_dispatch_wgpu_error(device_id, result.err().map(|(_, x)| x));
879                    },
880                    WebGPURequest::UnmapBuffer { buffer_id, mapping } => {
881                        let global = &self.global;
882                        if let Some(mapping) = mapping &&
883                            let Ok((slice_pointer, range_size)) = global.buffer_get_mapped_range(
884                                buffer_id,
885                                mapping.range.start,
886                                Some(mapping.range.end - mapping.range.start),
887                            )
888                        {
889                            unsafe {
890                                slice::from_raw_parts_mut(
891                                    slice_pointer.as_ptr(),
892                                    range_size as usize,
893                                )
894                            }
895                            .copy_from_slice(&mapping.data);
896                        }
897                        // Ignore result because this operation always succeed from user perspective
898                        let _result = global.buffer_unmap(buffer_id);
899                    },
900                    WebGPURequest::WriteBuffer {
901                        device_id,
902                        queue_id,
903                        buffer_id,
904                        buffer_offset,
905                        data,
906                    } => {
907                        let global = &self.global;
908                        let result = global.queue_write_buffer(
909                            queue_id,
910                            buffer_id,
911                            buffer_offset as wgt::BufferAddress,
912                            &data,
913                        );
914                        self.maybe_dispatch_wgpu_error(device_id, result.err());
915                    },
916                    WebGPURequest::WriteTexture {
917                        device_id,
918                        queue_id,
919                        texture_cv,
920                        data_layout,
921                        size,
922                        data,
923                    } => {
924                        let global = &self.global;
925                        let _guard = self.poller.lock();
926                        // TODO: Report result to content process
927                        let result = global.queue_write_texture(
928                            queue_id,
929                            &texture_cv,
930                            &data,
931                            &data_layout,
932                            &size,
933                        );
934                        drop(_guard);
935                        self.maybe_dispatch_wgpu_error(device_id, result.err());
936                    },
937                    WebGPURequest::CopyExternalImageToTexture {
938                        device_id,
939                        queue_id,
940                        usable_source,
941                        destination,
942                        dest_tex_descriptor,
943                        copy_size,
944                    } => {
945                        // device and queue timeline of https://www.w3.org/TR/webgpu/#dom-gpuqueue-copyexternalimagetotexture
946                        let global = &self.global;
947                        // If any of the following requirements are unmet, generate a validation error and return.
948                        // usability must be good.
949                        let Some(source) = usable_source else {
950                            self.maybe_dispatch_error(
951                                device_id,
952                                Some(Error::Validation("Source is not usable".to_string())),
953                            );
954                            continue;
955                        };
956                        // texture.usage must include both RENDER_ATTACHMENT
957                        if !dest_tex_descriptor
958                            .usage
959                            .contains(TextureUsages::RENDER_ATTACHMENT)
960                        {
961                            self.maybe_dispatch_error(
962                                device_id,
963                                Some(Error::Validation(
964                                    "Texture usage must include RENDER_ATTACHMENT".to_string(),
965                                )),
966                            );
967                            continue;
968                        }
969                        // texture.dimension must be "2d".
970                        if dest_tex_descriptor.dimension != TextureDimension::D2 {
971                            self.maybe_dispatch_error(
972                                device_id,
973                                Some(Error::Validation(
974                                    "Texture dimension must be 2d".to_string(),
975                                )),
976                            );
977                            continue;
978                        }
979                        // texture.format must be a plain color format supporting RENDER_ATTACHMENT and be a unorm/unorm-srgb or float/ufloat format (not snorm, uint, or sint).
980                        // currently to to hard to check
981                        // the rest will be checked as part of write texture
982                        let _guard = self.poller.lock();
983                        let result = global.queue_write_texture(
984                            queue_id,
985                            &destination,
986                            source.data(),
987                            &TexelCopyBufferLayout {
988                                offset: 0,
989                                bytes_per_row: Some(source.size().width * 4),
990                                rows_per_image: None,
991                            },
992                            &copy_size,
993                        );
994                        drop(_guard);
995                        self.maybe_dispatch_wgpu_error(device_id, result.err());
996                    },
997                    WebGPURequest::QueueOnSubmittedWorkDone { sender, queue_id } => {
998                        let global = &self.global;
999                        let token = self.poller.token();
1000                        let callback = Box::from(move || {
1001                            drop(token);
1002                            if let Err(e) = sender.send(()) {
1003                                warn!("Could not send SubmittedWorkDone Response ({})", e);
1004                            }
1005                        });
1006                        global.queue_on_submitted_work_done(queue_id, callback);
1007                        self.poller.wake();
1008                    },
1009                    WebGPURequest::DropTexture(id) => {
1010                        let global = &self.global;
1011                        global.texture_drop(id);
1012                        self.poller.wake();
1013                        if let Err(e) = self.script_sender.send(WebGPUMsg::FreeTexture(id)) {
1014                            warn!("Unable to send FreeTexture({:?}) ({:?})", id, e);
1015                        };
1016                    },
1017                    WebGPURequest::DropAdapter(id) => {
1018                        let global = &self.global;
1019                        global.adapter_drop(id);
1020                        if let Err(e) = self.script_sender.send(WebGPUMsg::FreeAdapter(id)) {
1021                            warn!("Unable to send FreeAdapter({:?}) ({:?})", id, e);
1022                        };
1023                    },
1024                    WebGPURequest::DropBuffer(id) => {
1025                        let global = &self.global;
1026                        global.buffer_drop(id);
1027                        self.poller.wake();
1028                        if let Err(e) = self.script_sender.send(WebGPUMsg::FreeBuffer(id)) {
1029                            warn!("Unable to send FreeBuffer({:?}) ({:?})", id, e);
1030                        };
1031                    },
1032                    WebGPURequest::DropPipelineLayout(id) => {
1033                        let global = &self.global;
1034                        global.pipeline_layout_drop(id);
1035                        if let Err(e) = self.script_sender.send(WebGPUMsg::FreePipelineLayout(id)) {
1036                            warn!("Unable to send FreePipelineLayout({:?}) ({:?})", id, e);
1037                        };
1038                    },
1039                    WebGPURequest::DropComputePipeline(id) => {
1040                        let global = &self.global;
1041                        global.compute_pipeline_drop(id);
1042                        if let Err(e) = self.script_sender.send(WebGPUMsg::FreeComputePipeline(id))
1043                        {
1044                            warn!("Unable to send FreeComputePipeline({:?}) ({:?})", id, e);
1045                        };
1046                    },
1047                    WebGPURequest::DropComputePass(id) => {
1048                        self.global.compute_pass_drop(id);
1049                        if let Err(e) = self.script_sender.send(WebGPUMsg::FreeComputePass(id)) {
1050                            warn!("Unable to send FreeComputePass({:?}) ({:?})", id, e);
1051                        };
1052                    },
1053                    WebGPURequest::DropRenderPass(id) => {
1054                        self.global.render_pass_drop(id);
1055                        if let Err(e) = self.script_sender.send(WebGPUMsg::FreeRenderPass(id)) {
1056                            warn!("Unable to send FreeRenderPass({:?}) ({:?})", id, e);
1057                        };
1058                    },
1059                    WebGPURequest::DropRenderPipeline(id) => {
1060                        let global = &self.global;
1061                        global.render_pipeline_drop(id);
1062                        if let Err(e) = self.script_sender.send(WebGPUMsg::FreeRenderPipeline(id)) {
1063                            warn!("Unable to send FreeRenderPipeline({:?}) ({:?})", id, e);
1064                        };
1065                    },
1066                    WebGPURequest::DropBindGroup(id) => {
1067                        let global = &self.global;
1068                        global.bind_group_drop(id);
1069                        if let Err(e) = self.script_sender.send(WebGPUMsg::FreeBindGroup(id)) {
1070                            warn!("Unable to send FreeBindGroup({:?}) ({:?})", id, e);
1071                        };
1072                    },
1073                    WebGPURequest::DropBindGroupLayout(id) => {
1074                        let global = &self.global;
1075                        global.bind_group_layout_drop(id);
1076                        if let Err(e) = self.script_sender.send(WebGPUMsg::FreeBindGroupLayout(id))
1077                        {
1078                            warn!("Unable to send FreeBindGroupLayout({:?}) ({:?})", id, e);
1079                        };
1080                    },
1081                    WebGPURequest::DropTextureView(id) => {
1082                        let global = &self.global;
1083                        global.texture_view_drop(id);
1084                        self.poller.wake();
1085                        if let Err(e) = self.script_sender.send(WebGPUMsg::FreeTextureView(id)) {
1086                            warn!("Unable to send FreeTextureView({:?}) ({:?})", id, e);
1087                        };
1088                    },
1089                    WebGPURequest::DropSampler(id) => {
1090                        let global = &self.global;
1091                        global.sampler_drop(id);
1092                        if let Err(e) = self.script_sender.send(WebGPUMsg::FreeSampler(id)) {
1093                            warn!("Unable to send FreeSampler({:?}) ({:?})", id, e);
1094                        };
1095                    },
1096                    WebGPURequest::DropShaderModule(id) => {
1097                        let global = &self.global;
1098                        global.shader_module_drop(id);
1099                        if let Err(e) = self.script_sender.send(WebGPUMsg::FreeShaderModule(id)) {
1100                            warn!("Unable to send FreeShaderModule({:?}) ({:?})", id, e);
1101                        };
1102                    },
1103                    WebGPURequest::DropRenderBundleEncoder(id) => {
1104                        let global = &self.global;
1105                        global.render_bundle_encoder_drop(id);
1106                        if let Err(e) = self
1107                            .script_sender
1108                            .send(WebGPUMsg::FreeRenderBundleEncoder(id))
1109                        {
1110                            warn!("Unable to send FreeRenderBundleEncoder({:?}) ({:?})", id, e);
1111                        };
1112                    },
1113                    WebGPURequest::DropRenderBundle(id) => {
1114                        let global = &self.global;
1115                        global.render_bundle_drop(id);
1116                        if let Err(e) = self.script_sender.send(WebGPUMsg::FreeRenderBundle(id)) {
1117                            warn!("Unable to send FreeRenderBundle({:?}) ({:?})", id, e);
1118                        };
1119                    },
1120                    WebGPURequest::DropQuerySet(id) => {
1121                        let global = &self.global;
1122                        global.query_set_drop(id);
1123                        if let Err(e) = self.script_sender.send(WebGPUMsg::FreeQuerySet(id)) {
1124                            warn!("Unable to send FreeQuerySet({:?}) ({:?})", id, e);
1125                        };
1126                    },
1127                    WebGPURequest::PushErrorScope { device_id, filter } => {
1128                        // <https://www.w3.org/TR/webgpu/#dom-gpudevice-pusherrorscope>
1129                        let mut devices = self.devices.lock().unwrap();
1130                        let device_scope = devices
1131                            .get_mut(&device_id)
1132                            .expect("Device should not be dropped by this point");
1133                        if let Some(error_scope_stack) = &mut device_scope.error_scope_stack {
1134                            error_scope_stack.push(ErrorScope::new(filter));
1135                        } // else device is lost
1136                    },
1137                    WebGPURequest::DispatchError { device_id, error } => {
1138                        self.dispatch_error(device_id, error);
1139                    },
1140                    WebGPURequest::PopErrorScope {
1141                        device_id,
1142                        callback: sender,
1143                    } => {
1144                        // <https://www.w3.org/TR/webgpu/#dom-gpudevice-poperrorscope>
1145                        let mut devices = self.devices.lock().unwrap();
1146                        let device_scope = devices
1147                            .get_mut(&device_id)
1148                            .expect("Device should not be dropped by this point");
1149                        let result =
1150                            if let Some(error_scope_stack) = &mut device_scope.error_scope_stack {
1151                                if let Some(error_scope) = error_scope_stack.pop() {
1152                                    Ok(
1153                                        // TODO: Do actual selection instead of selecting first error
1154                                        error_scope.errors.first().cloned(),
1155                                    )
1156                                } else {
1157                                    Err(PopError::Empty)
1158                                }
1159                            } else {
1160                                // This means the device has been lost.
1161                                Err(PopError::Lost)
1162                            };
1163                        if let Err(error) = sender.send(result) {
1164                            warn!("Error while sending PopErrorScope result: {error}");
1165                        }
1166                    },
1167                    WebGPURequest::ComputeGetBindGroupLayout {
1168                        device_id,
1169                        pipeline_id,
1170                        index,
1171                        id,
1172                    } => {
1173                        let global = &self.global;
1174                        let (_, error) = global.compute_pipeline_get_bind_group_layout(
1175                            pipeline_id,
1176                            index,
1177                            Some(id),
1178                        );
1179                        self.maybe_dispatch_wgpu_error(device_id, error);
1180                    },
1181                    WebGPURequest::RenderGetBindGroupLayout {
1182                        device_id,
1183                        pipeline_id,
1184                        index,
1185                        id,
1186                    } => {
1187                        let global = &self.global;
1188                        let (_, error) = global.render_pipeline_get_bind_group_layout(
1189                            pipeline_id,
1190                            index,
1191                            Some(id),
1192                        );
1193                        self.maybe_dispatch_wgpu_error(device_id, error);
1194                    },
1195                    WebGPURequest::CreateQuerySet {
1196                        device_id,
1197                        query_set_id,
1198                        descriptor,
1199                    } => {
1200                        let global = &self.global;
1201                        let (_, error) = global.device_create_query_set(
1202                            device_id,
1203                            &descriptor,
1204                            Some(query_set_id),
1205                        );
1206                        self.maybe_dispatch_wgpu_error(device_id, error);
1207                    },
1208                    WebGPURequest::ResolveQuerySet {
1209                        device_id,
1210                        command_encoder_id,
1211                        query_set_id,
1212                        start_query,
1213                        query_count,
1214                        destination,
1215                        destination_offset,
1216                    } => {
1217                        let global = &self.global;
1218                        let result = global.command_encoder_resolve_query_set(
1219                            command_encoder_id,
1220                            query_set_id,
1221                            start_query,
1222                            query_count,
1223                            destination,
1224                            destination_offset,
1225                        );
1226                        self.maybe_dispatch_wgpu_error(device_id, result.err());
1227                    },
1228                    WebGPURequest::CreatePlanarTexture {
1229                        device_id,
1230                        size,
1231                        format,
1232                        texture_id,
1233                        texture_view_id,
1234                    } => {
1235                        let (_, maybe_error) = self.global.device_create_texture(
1236                            device_id,
1237                            &TextureDescriptor {
1238                                label: None,
1239                                size: Extent3d {
1240                                    width: size.width,
1241                                    height: size.height,
1242                                    depth_or_array_layers: 1,
1243                                },
1244                                mip_level_count: 1,
1245                                sample_count: 1,
1246                                dimension: TextureDimension::D2,
1247                                format: match format {
1248                                    pixels::SnapshotPixelFormat::RGBA => TextureFormat::Rgba8Unorm,
1249                                    pixels::SnapshotPixelFormat::BGRA => TextureFormat::Bgra8Unorm,
1250                                },
1251                                usage: TextureUsages::COPY_DST | TextureUsages::TEXTURE_BINDING,
1252                                view_formats: Vec::new(),
1253                            },
1254                            Some(texture_id),
1255                        );
1256                        self.maybe_dispatch_error(
1257                            device_id,
1258                            maybe_error.map(|error| {
1259                                Error::Internal(format!(
1260                                    "Failed to create planar texture: {error:?}"
1261                                ))
1262                            }),
1263                        );
1264                        let (_, maybe_error) = self.global.texture_create_view(
1265                            texture_id,
1266                            &TextureViewDescriptor {
1267                                ..Default::default()
1268                            },
1269                            Some(texture_view_id),
1270                        );
1271                        self.maybe_dispatch_error(
1272                            device_id,
1273                            maybe_error.map(|error| {
1274                                Error::Internal(format!(
1275                                    "Failed to create planar texture view: {error:?}"
1276                                ))
1277                            }),
1278                        );
1279                    },
1280                    WebGPURequest::UpdatePlanarTexture {
1281                        device_id,
1282                        queue_id,
1283                        texture_id,
1284                        snapshot,
1285                    } => {
1286                        let result = self.global.queue_write_texture(
1287                            queue_id,
1288                            &TexelCopyTextureInfo {
1289                                texture: texture_id,
1290                                mip_level: 0,
1291                                origin: Origin3d::ZERO,
1292                                aspect: TextureAspect::All,
1293                            },
1294                            snapshot.data(),
1295                            &TexelCopyBufferLayout {
1296                                offset: 0,
1297                                bytes_per_row: Some(snapshot.size().width * 4),
1298                                rows_per_image: None,
1299                            },
1300                            &Extent3d {
1301                                width: snapshot.size().width,
1302                                height: snapshot.size().height,
1303                                depth_or_array_layers: 1,
1304                            },
1305                        );
1306                        self.maybe_dispatch_error(
1307                            device_id,
1308                            result.err().map(|error| {
1309                                Error::Internal(format!(
1310                                    "Failed to write planar texture: {error:?}"
1311                                ))
1312                            }),
1313                        );
1314                    },
1315                    WebGPURequest::DropPlanarTexture(id, view_id) => {
1316                        self.global.texture_view_drop(view_id);
1317                        self.global.texture_drop(id);
1318                        self.poller.wake();
1319                        if let Err(e) = self.script_sender.send(WebGPUMsg::FreeTextureView(view_id))
1320                        {
1321                            warn!("Unable to send FreeTextureView({:?}) ({:?})", view_id, e);
1322                        };
1323                        if let Err(e) = self.script_sender.send(WebGPUMsg::FreeTexture(id)) {
1324                            warn!("Unable to send FreeTexture({:?}) ({:?})", id, e);
1325                        };
1326                    },
1327                    WebGPURequest::ImportExternalTexture {
1328                        device_id,
1329                        external_texture_id,
1330                        size,
1331                        label,
1332                        plane0,
1333                    } => {
1334                        let desc = ExternalTextureDescriptor {
1335                            label: Some(label.into()),
1336                            width: size.width,
1337                            height: size.height,
1338                            format: ExternalTextureFormat::Rgba,
1339                            yuv_conversion_matrix: [0.; 16],
1340                            gamut_conversion_matrix: [
1341                                1., 0., 0., //
1342                                0., 1., 0., //
1343                                0., 0., 1., //
1344                            ],
1345                            src_transfer_function: ExternalTextureTransferFunction::default(),
1346                            dst_transfer_function: ExternalTextureTransferFunction::default(),
1347                            sample_transform: [
1348                                1., 0., //
1349                                0., 1., //
1350                                0., 0., //
1351                            ],
1352                            load_transform: [
1353                                1., 0., //
1354                                0., 1., //
1355                                0., 0., //
1356                            ],
1357                        };
1358                        if let Some(plane0) = plane0 {
1359                            let (_, maybe_error) = self.global.device_create_external_texture(
1360                                device_id,
1361                                &desc,
1362                                &[plane0],
1363                                Some(external_texture_id),
1364                            );
1365                            self.maybe_dispatch_error(
1366                                device_id,
1367                                maybe_error.map(|error| {
1368                                    Error::Internal(format!(
1369                                        "Failed to import external texture: {error:?}"
1370                                    ))
1371                                }),
1372                            );
1373                        } else {
1374                            self.global
1375                                .create_external_texture_error(Some(external_texture_id), &desc);
1376                            self.maybe_dispatch_error(
1377                                device_id,
1378                                Some(Error::Validation("Usability is not good".to_string())),
1379                            );
1380                        }
1381                    },
1382                    WebGPURequest::DestroyExternalTexture(id) => {
1383                        self.global.external_texture_destroy(id);
1384                    },
1385                    WebGPURequest::DropExternalTexture(id) => {
1386                        self.global.external_texture_drop(id);
1387                        if let Err(e) = self.script_sender.send(WebGPUMsg::FreeExternalTexture(id))
1388                        {
1389                            warn!("Unable to send FreeExternalTexture({:?}) ({:?})", id, e);
1390                        };
1391                    },
1392                    WebGPURequest::DestroyQuerySet(query_set_id) => {
1393                        self.global.query_set_destroy(query_set_id);
1394                    },
1395                    WebGPURequest::RenderBundleEncoderFinish {
1396                        render_bundle_encoder_id,
1397                        descriptor,
1398                        render_bundle_id,
1399                        device_id,
1400                    } => {
1401                        let global = &self.global;
1402                        let (_, error) = global.render_bundle_encoder_finish_with_id(
1403                            render_bundle_encoder_id,
1404                            &descriptor,
1405                            Some(render_bundle_id),
1406                        );
1407
1408                        self.maybe_dispatch_wgpu_error(device_id, error);
1409                    },
1410                    WebGPURequest::CreateRenderBundleEncoder {
1411                        device_id,
1412                        render_bundle_encoder_id,
1413                        desc,
1414                    } => {
1415                        let (_, error) = self.global.device_create_render_bundle_encoder_with_id(
1416                            device_id,
1417                            &desc,
1418                            Some(render_bundle_encoder_id),
1419                        );
1420                        self.maybe_dispatch_wgpu_error(device_id, error);
1421                    },
1422                    WebGPURequest::RenderBundleEncoderCommand {
1423                        render_bundle_encoder_id,
1424                        render_command,
1425                        device_id,
1426                    } => {
1427                        let result = apply_render_bundle_command(
1428                            &self.global,
1429                            render_bundle_encoder_id,
1430                            render_command,
1431                        );
1432                        self.maybe_dispatch_wgpu_error(device_id, result.err());
1433                    },
1434                }
1435            }
1436        }
1437        if let Err(e) = self.script_sender.send(WebGPUMsg::Exit) {
1438            warn!("Failed to send WebGPUMsg::Exit to script ({})", e);
1439        }
1440    }
1441
1442    #[inline]
1443    fn maybe_dispatch_wgpu_error<E: WebGpuError>(
1444        &mut self,
1445        device_id: id::DeviceId,
1446        error: Option<E>,
1447    ) {
1448        self.maybe_dispatch_error(device_id, error.and_then(Error::from_wgpu_error))
1449    }
1450
1451    /// Dispatches error (if there is any)
1452    fn maybe_dispatch_error(&mut self, device_id: id::DeviceId, error: Option<Error>) {
1453        if let Some(error) = error {
1454            self.dispatch_error(device_id, error);
1455        }
1456    }
1457
1458    /// <https://www.w3.org/TR/webgpu/#abstract-opdef-dispatch-error>
1459    fn dispatch_error(&mut self, device_id: id::DeviceId, error: Error) {
1460        log::trace!("Dispatching error for device {:?}: {:?}", device_id, error);
1461        let mut devices = self.devices.lock().unwrap();
1462        let device_scope = devices
1463            .get_mut(&device_id)
1464            .expect("Device should not be dropped by this point");
1465        if let Some(error_scope_stack) = &mut device_scope.error_scope_stack {
1466            if let Some(error_scope) = error_scope_stack
1467                .iter_mut()
1468                .rev()
1469                .find(|error_scope| error_scope.filter == error.filter())
1470            {
1471                error_scope.errors.push(error);
1472            } else if self
1473                .script_sender
1474                .send(WebGPUMsg::UncapturedError {
1475                    device: WebGPUDevice(device_id),
1476                    pipeline_id: device_scope.pipeline_id,
1477                    error: error.clone(),
1478                })
1479                .is_err()
1480            {
1481                warn!("Failed to send WebGPUMsg::UncapturedError: {error:?}");
1482            }
1483        } // else device is lost
1484    }
1485}