Skip to main content

webgpu/
wgpu_thread.rs

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