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