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