player/
lib.rs

1//! This is a player library for WebGPU traces.
2
3#![cfg(not(target_arch = "wasm32"))]
4#![warn(clippy::allow_attributes, unsafe_op_in_unsafe_fn)]
5
6extern crate wgpu_core as wgc;
7extern crate wgpu_types as wgt;
8
9use std::{borrow::Cow, convert::Infallible, fs, path::Path, sync::Arc};
10
11use hashbrown::HashMap;
12
13use wgc::{
14    binding_model::BindingResource,
15    command::{ArcCommand, ArcReferences, BasePass, Command, PointerReferences},
16    device::trace,
17    id::PointerId,
18};
19
20pub struct Player {
21    pipeline_layouts: HashMap<
22        wgc::id::PointerId<wgc::id::markers::PipelineLayout>,
23        Arc<wgc::binding_model::PipelineLayout>,
24    >,
25    shader_modules: HashMap<
26        wgc::id::PointerId<wgc::id::markers::ShaderModule>,
27        Arc<wgc::pipeline::ShaderModule>,
28    >,
29    bind_group_layouts: HashMap<
30        wgc::id::PointerId<wgc::id::markers::BindGroupLayout>,
31        Arc<wgc::binding_model::BindGroupLayout>,
32    >,
33    bind_groups: HashMap<
34        wgc::id::PointerId<wgc::id::markers::BindGroup>,
35        Arc<wgc::binding_model::BindGroup>,
36    >,
37    render_bundles: HashMap<
38        wgc::id::PointerId<wgc::id::markers::RenderBundle>,
39        Arc<wgc::command::RenderBundle>,
40    >,
41    render_pipelines: HashMap<
42        wgc::id::PointerId<wgc::id::markers::RenderPipeline>,
43        Arc<wgc::pipeline::RenderPipeline>,
44    >,
45    compute_pipelines: HashMap<
46        wgc::id::PointerId<wgc::id::markers::ComputePipeline>,
47        Arc<wgc::pipeline::ComputePipeline>,
48    >,
49    pipeline_caches: HashMap<
50        wgc::id::PointerId<wgc::id::markers::PipelineCache>,
51        Arc<wgc::pipeline::PipelineCache>,
52    >,
53    query_sets:
54        HashMap<wgc::id::PointerId<wgc::id::markers::QuerySet>, Arc<wgc::resource::QuerySet>>,
55    buffers: HashMap<wgc::id::PointerId<wgc::id::markers::Buffer>, Arc<wgc::resource::Buffer>>,
56    textures: HashMap<wgc::id::PointerId<wgc::id::markers::Texture>, Arc<wgc::resource::Texture>>,
57    texture_views:
58        HashMap<wgc::id::PointerId<wgc::id::markers::TextureView>, Arc<wgc::resource::TextureView>>,
59    external_textures: HashMap<
60        wgc::id::PointerId<wgc::id::markers::ExternalTexture>,
61        Arc<wgc::resource::ExternalTexture>,
62    >,
63    samplers: HashMap<wgc::id::PointerId<wgc::id::markers::Sampler>, Arc<wgc::resource::Sampler>>,
64    blas_s: HashMap<wgc::id::PointerId<wgc::id::markers::Blas>, Arc<wgc::resource::Blas>>,
65    tlas_s: HashMap<wgc::id::PointerId<wgc::id::markers::Tlas>, Arc<wgc::resource::Tlas>>,
66}
67
68impl Default for Player {
69    fn default() -> Self {
70        Self {
71            pipeline_layouts: HashMap::new(),
72            shader_modules: HashMap::new(),
73            bind_group_layouts: HashMap::new(),
74            bind_groups: HashMap::new(),
75            render_bundles: HashMap::new(),
76            render_pipelines: HashMap::new(),
77            compute_pipelines: HashMap::new(),
78            pipeline_caches: HashMap::new(),
79            query_sets: HashMap::new(),
80            buffers: HashMap::new(),
81            textures: HashMap::new(),
82            texture_views: HashMap::new(),
83            external_textures: HashMap::new(),
84            samplers: HashMap::new(),
85            blas_s: HashMap::new(),
86            tlas_s: HashMap::new(),
87        }
88    }
89}
90
91impl Player {
92    pub fn process(
93        &mut self,
94        device: &Arc<wgc::device::Device>,
95        queue: &Arc<wgc::device::queue::Queue>,
96        action: trace::Action<PointerReferences>,
97        dir: &Path,
98    ) {
99        use wgc::device::trace::Action;
100        log::debug!("action {action:?}");
101        match action {
102            Action::Init { .. } => {
103                panic!("Unexpected Action::Init: has to be the first action only")
104            }
105            Action::ConfigureSurface { .. }
106            | Action::Present(_)
107            | Action::DiscardSurfaceTexture(_) => {
108                panic!("Unexpected Surface action: winit feature is not enabled")
109            }
110            Action::CreateBuffer(id, desc) => {
111                let buffer = device.create_buffer(&desc).expect("create_buffer error");
112                self.buffers.insert(id, buffer);
113            }
114            Action::FreeBuffer(id) => {
115                // Note: buffer remains in the HashMap. "Free" and "Destroy"
116                // mean the opposite from WebGPU.
117                let buffer = self.buffers.get(&id).expect("invalid buffer");
118                buffer.destroy();
119            }
120            Action::DestroyBuffer(id) => {
121                let buffer = self.buffers.remove(&id).expect("invalid buffer");
122                let _ = buffer.unmap();
123            }
124            Action::CreateTexture(id, desc) => {
125                let texture = device.create_texture(&desc).expect("create_texture error");
126                self.textures.insert(id, texture);
127            }
128            Action::FreeTexture(id) => {
129                // Note: texture remains in the HashMap. "Free" and "Destroy"
130                // mean the opposite from WebGPU.
131                let texture = self.textures.get(&id).expect("invalid texture");
132                texture.destroy();
133            }
134            Action::DestroyTexture(id) => {
135                self.textures.remove(&id).expect("invalid texture");
136            }
137            Action::CreateTextureView { id, parent, desc } => {
138                let parent_texture = self.resolve_texture_id(parent);
139                let texture_view = device
140                    .create_texture_view(&parent_texture, &desc)
141                    .expect("create_texture_view error");
142                self.texture_views.insert(id, texture_view);
143            }
144            Action::DestroyTextureView(id) => {
145                self.texture_views
146                    .remove(&id)
147                    .expect("invalid texture view");
148            }
149            Action::CreateExternalTexture { id, desc, planes } => {
150                let planes = planes
151                    .iter()
152                    .map(|&id| self.resolve_texture_view_id(id))
153                    .collect::<Vec<_>>();
154                let external_texture = device
155                    .create_external_texture(&desc, &planes)
156                    .expect("create_external_texture error");
157                self.external_textures.insert(id, external_texture);
158            }
159            Action::FreeExternalTexture(id) => {
160                // Note: external texture remains in the HashMap. "Free" and "Destroy"
161                // mean the opposite from WebGPU.
162                let external_texture = self
163                    .external_textures
164                    .get(&id)
165                    .expect("invalid external texture");
166                external_texture.destroy();
167            }
168            Action::DestroyExternalTexture(id) => {
169                self.external_textures
170                    .remove(&id)
171                    .expect("invalid external texture");
172            }
173            Action::CreateSampler(id, desc) => {
174                let sampler = device.create_sampler(&desc).expect("create_sampler error");
175                self.samplers.insert(id, sampler);
176            }
177            Action::DestroySampler(id) => {
178                self.samplers.remove(&id).expect("invalid sampler");
179            }
180            Action::GetSurfaceTexture { .. } => {
181                unimplemented!()
182            }
183            Action::CreateBindGroupLayout(id, desc) => {
184                let bind_group_layout = device
185                    .create_bind_group_layout(&desc)
186                    .expect("create_bind_group_layout error");
187                self.bind_group_layouts.insert(id, bind_group_layout);
188            }
189            Action::DestroyBindGroupLayout(id) => {
190                self.bind_group_layouts
191                    .remove(&id)
192                    .expect("invalid bind group layout");
193            }
194            Action::CreatePipelineLayout(id, desc) => {
195                let bind_group_layouts: Vec<Arc<wgc::binding_model::BindGroupLayout>> = desc
196                    .bind_group_layouts
197                    .to_vec()
198                    .into_iter()
199                    .map(|bgl_id| self.resolve_bind_group_layout_id(bgl_id))
200                    .collect();
201
202                let resolved_desc = wgc::binding_model::ResolvedPipelineLayoutDescriptor {
203                    label: desc.label.clone(),
204                    bind_group_layouts: Cow::from(&bind_group_layouts),
205                    push_constant_ranges: Cow::Borrowed(&*desc.push_constant_ranges),
206                };
207
208                let pipeline_layout = device
209                    .create_pipeline_layout(&resolved_desc)
210                    .expect("create_pipeline_layout error");
211                self.pipeline_layouts.insert(id, pipeline_layout);
212            }
213            Action::DestroyPipelineLayout(id) => {
214                self.pipeline_layouts
215                    .remove(&id)
216                    .expect("invalid pipeline layout");
217            }
218            Action::CreateBindGroup(id, desc) => {
219                let resolved_desc = self.resolve_bind_group_descriptor(desc);
220                let bind_group = device
221                    .create_bind_group(resolved_desc)
222                    .expect("create_bind_group error");
223                self.bind_groups.insert(id, bind_group);
224            }
225            Action::DestroyBindGroup(id) => {
226                let _bind_group = self.bind_groups.remove(&id).expect("invalid bind group");
227            }
228            Action::CreateShaderModule { id, desc, data } => {
229                log::debug!("Creating shader from {data}");
230                let code = fs::read_to_string(dir.join(&data)).unwrap();
231                let source = if data.ends_with(".wgsl") {
232                    wgc::pipeline::ShaderModuleSource::Wgsl(Cow::Owned(code.clone()))
233                } else if data.ends_with(".ron") {
234                    let module = ron::de::from_str(&code).unwrap();
235                    wgc::pipeline::ShaderModuleSource::Naga(module)
236                } else {
237                    panic!("Unknown shader {data}");
238                };
239                match device.create_shader_module(&desc, source) {
240                    Ok(module) => self.shader_modules.insert(id, module),
241                    Err(e) => panic!("shader compilation error:\n---{code}\n---\n{e}"),
242                };
243            }
244            Action::CreateShaderModulePassthrough {
245                id,
246                data,
247                entry_point,
248                label,
249                num_workgroups,
250                runtime_checks,
251            } => {
252                let spirv = data.iter().find_map(|a| {
253                    if a.ends_with(".spv") {
254                        let data = fs::read(dir.join(a)).unwrap();
255                        assert!(data.len() % 4 == 0);
256
257                        Some(Cow::Owned(bytemuck::pod_collect_to_vec(&data)))
258                    } else {
259                        None
260                    }
261                });
262                let dxil = data.iter().find_map(|a| {
263                    if a.ends_with(".dxil") {
264                        let vec = std::fs::read(dir.join(a)).unwrap();
265                        Some(Cow::Owned(vec))
266                    } else {
267                        None
268                    }
269                });
270                let hlsl = data.iter().find_map(|a| {
271                    if a.ends_with(".hlsl") {
272                        let code = fs::read_to_string(dir.join(a)).unwrap();
273                        Some(Cow::Owned(code))
274                    } else {
275                        None
276                    }
277                });
278                let msl = data.iter().find_map(|a| {
279                    if a.ends_with(".msl") {
280                        let code = fs::read_to_string(dir.join(a)).unwrap();
281                        Some(Cow::Owned(code))
282                    } else {
283                        None
284                    }
285                });
286                let glsl = data.iter().find_map(|a| {
287                    if a.ends_with(".glsl") {
288                        let code = fs::read_to_string(dir.join(a)).unwrap();
289                        Some(Cow::Owned(code))
290                    } else {
291                        None
292                    }
293                });
294                let wgsl = data.iter().find_map(|a| {
295                    if a.ends_with(".wgsl") {
296                        let code = fs::read_to_string(dir.join(a)).unwrap();
297                        Some(Cow::Owned(code))
298                    } else {
299                        None
300                    }
301                });
302                let desc = wgt::CreateShaderModuleDescriptorPassthrough {
303                    entry_point,
304                    label,
305                    num_workgroups,
306                    runtime_checks,
307
308                    spirv,
309                    dxil,
310                    hlsl,
311                    msl,
312                    glsl,
313                    wgsl,
314                };
315                match unsafe { device.create_shader_module_passthrough(&desc) } {
316                    Ok(module) => self.shader_modules.insert(id, module),
317                    Err(e) => panic!("shader compilation error:\n{e}"),
318                };
319            }
320            Action::DestroyShaderModule(id) => {
321                self.shader_modules
322                    .remove(&id)
323                    .expect("invalid shader module");
324            }
325            Action::CreateComputePipeline { id, desc } => {
326                let resolved_desc = self.resolve_compute_pipeline_descriptor(desc);
327                let pipeline = device
328                    .create_compute_pipeline(resolved_desc)
329                    .expect("create_compute_pipeline error");
330                self.compute_pipelines.insert(id, pipeline);
331            }
332            Action::DestroyComputePipeline(id) => {
333                self.compute_pipelines
334                    .remove(&id)
335                    .expect("invalid compute pipeline");
336            }
337            Action::CreateGeneralRenderPipeline { id, desc } => {
338                // Note that this is the `General` version of the render
339                // pipeline descriptor that can represent either a conventional
340                // pipeline or a mesh shading pipeline.
341                let resolved_desc = self.resolve_render_pipeline_descriptor(desc);
342                let pipeline = device
343                    .create_render_pipeline(resolved_desc)
344                    .expect("create_render_pipeline error");
345                self.render_pipelines.insert(id, pipeline);
346            }
347            Action::DestroyRenderPipeline(id) => {
348                self.render_pipelines
349                    .remove(&id)
350                    .expect("invalid render pipeline");
351            }
352            Action::CreatePipelineCache { id, desc } => {
353                let cache = unsafe { device.create_pipeline_cache(&desc) }.unwrap();
354                self.pipeline_caches.insert(id, cache);
355            }
356            Action::DestroyPipelineCache(id) => {
357                self.pipeline_caches
358                    .remove(&id)
359                    .expect("invalid pipeline cache");
360            }
361            Action::CreateRenderBundle { .. } => {
362                unimplemented!("traced render bundles are not supported");
363            }
364            Action::DestroyRenderBundle(id) => {
365                self.render_bundles
366                    .remove(&id)
367                    .expect("invalid render bundle");
368            }
369            Action::CreateQuerySet { id, desc } => {
370                let query_set = device
371                    .create_query_set(&desc)
372                    .expect("create_query_set error");
373                self.query_sets.insert(id, query_set);
374            }
375            Action::DestroyQuerySet(id) => {
376                self.query_sets.remove(&id).expect("invalid query set");
377            }
378            Action::WriteBuffer {
379                id,
380                data,
381                range,
382                queued,
383            } => {
384                let buffer = self.resolve_buffer_id(id);
385                let bin = std::fs::read(dir.join(data)).unwrap();
386                let size = (range.end - range.start) as usize;
387                if queued {
388                    queue
389                        .write_buffer(buffer, range.start, &bin)
390                        .expect("Queue::write_buffer error");
391                } else {
392                    device
393                        .set_buffer_data(&buffer, range.start, &bin[..size])
394                        .expect("Device::set_buffer_data error");
395                }
396            }
397            Action::WriteTexture {
398                to,
399                data,
400                layout,
401                size,
402            } => {
403                let to = self.resolve_texel_copy_texture_info(to);
404                let bin = std::fs::read(dir.join(data)).unwrap();
405                queue
406                    .write_texture(to, &bin, &layout, &size)
407                    .expect("Queue::write_texture error");
408            }
409            Action::Submit(_index, ref commands) if commands.is_empty() => {
410                queue.submit(&[]).unwrap();
411            }
412            Action::Submit(_index, commands) => {
413                let resolved_commands: Vec<_> = commands
414                    .into_iter()
415                    .map(|cmd| self.resolve_command(cmd))
416                    .collect();
417                let buffer = wgc::command::CommandBuffer::from_trace(device, resolved_commands);
418                queue.submit(&[buffer]).unwrap();
419            }
420            Action::CreateBlas { id, desc, sizes } => {
421                let blas = device.create_blas(&desc, sizes).expect("create_blas error");
422                self.blas_s.insert(id, blas);
423            }
424            Action::DestroyBlas(id) => {
425                self.blas_s.remove(&id).expect("invalid blas");
426            }
427            Action::CreateTlas { id, desc } => {
428                let tlas = device.create_tlas(&desc).expect("create_tlas error");
429                self.tlas_s.insert(id, tlas);
430            }
431            Action::DestroyTlas(id) => {
432                self.tlas_s.remove(&id).expect("invalid tlas");
433            }
434        }
435    }
436
437    pub fn resolve_buffer_id(
438        &self,
439        id: wgc::id::PointerId<wgc::id::markers::Buffer>,
440    ) -> Arc<wgc::resource::Buffer> {
441        self.buffers.get(&id).expect("invalid buffer").clone()
442    }
443
444    fn resolve_texture_id(
445        &self,
446        id: wgc::id::PointerId<wgc::id::markers::Texture>,
447    ) -> Arc<wgc::resource::Texture> {
448        self.textures.get(&id).expect("invalid texture").clone()
449    }
450
451    fn resolve_texture_view_id(
452        &self,
453        id: wgc::id::PointerId<wgc::id::markers::TextureView>,
454    ) -> Arc<wgc::resource::TextureView> {
455        self.texture_views
456            .get(&id)
457            .expect("invalid texture view")
458            .clone()
459    }
460
461    fn resolve_external_texture_id(
462        &self,
463        id: wgc::id::PointerId<wgc::id::markers::ExternalTexture>,
464    ) -> Arc<wgc::resource::ExternalTexture> {
465        self.external_textures
466            .get(&id)
467            .expect("invalid external texture")
468            .clone()
469    }
470
471    fn resolve_sampler_id(
472        &self,
473        id: wgc::id::PointerId<wgc::id::markers::Sampler>,
474    ) -> Arc<wgc::resource::Sampler> {
475        self.samplers.get(&id).expect("invalid sampler").clone()
476    }
477
478    fn resolve_bind_group_layout_id(
479        &self,
480        id: wgc::id::PointerId<wgc::id::markers::BindGroupLayout>,
481    ) -> Arc<wgc::binding_model::BindGroupLayout> {
482        self.bind_group_layouts
483            .get(&id)
484            .expect("invalid bind group layout")
485            .clone()
486    }
487
488    fn resolve_bind_group_id(
489        &self,
490        id: wgc::id::PointerId<wgc::id::markers::BindGroup>,
491    ) -> Arc<wgc::binding_model::BindGroup> {
492        self.bind_groups
493            .get(&id)
494            .expect("invalid bind group")
495            .clone()
496    }
497
498    fn resolve_pipeline_layout_id(
499        &self,
500        id: wgc::id::PointerId<wgc::id::markers::PipelineLayout>,
501    ) -> Arc<wgc::binding_model::PipelineLayout> {
502        self.pipeline_layouts
503            .get(&id)
504            .expect("invalid pipeline layout")
505            .clone()
506    }
507
508    fn resolve_shader_module_id(
509        &self,
510        id: wgc::id::PointerId<wgc::id::markers::ShaderModule>,
511    ) -> Arc<wgc::pipeline::ShaderModule> {
512        self.shader_modules
513            .get(&id)
514            .expect("invalid shader module")
515            .clone()
516    }
517
518    fn resolve_render_pipeline_id(
519        &self,
520        id: wgc::id::PointerId<wgc::id::markers::RenderPipeline>,
521    ) -> Arc<wgc::pipeline::RenderPipeline> {
522        self.render_pipelines
523            .get(&id)
524            .expect("invalid render pipeline")
525            .clone()
526    }
527
528    fn resolve_compute_pipeline_id(
529        &self,
530        id: wgc::id::PointerId<wgc::id::markers::ComputePipeline>,
531    ) -> Arc<wgc::pipeline::ComputePipeline> {
532        self.compute_pipelines
533            .get(&id)
534            .expect("invalid compute pipeline")
535            .clone()
536    }
537
538    fn resolve_pipeline_cache_id(
539        &self,
540        id: wgc::id::PointerId<wgc::id::markers::PipelineCache>,
541    ) -> Arc<wgc::pipeline::PipelineCache> {
542        self.pipeline_caches
543            .get(&id)
544            .expect("invalid pipeline cache")
545            .clone()
546    }
547
548    fn resolve_render_bundle_id(
549        &self,
550        id: wgc::id::PointerId<wgc::id::markers::RenderBundle>,
551    ) -> Arc<wgc::command::RenderBundle> {
552        self.render_bundles
553            .get(&id)
554            .expect("invalid render bundle")
555            .clone()
556    }
557
558    fn resolve_query_set_id(
559        &self,
560        id: wgc::id::PointerId<wgc::id::markers::QuerySet>,
561    ) -> Arc<wgc::resource::QuerySet> {
562        self.query_sets.get(&id).expect("invalid query set").clone()
563    }
564
565    fn resolve_blas_id(
566        &self,
567        id: wgc::id::PointerId<wgc::id::markers::Blas>,
568    ) -> Arc<wgc::resource::Blas> {
569        self.blas_s.get(&id).expect("invalid blas").clone()
570    }
571
572    fn resolve_tlas_id(
573        &self,
574        id: wgc::id::PointerId<wgc::id::markers::Tlas>,
575    ) -> Arc<wgc::resource::Tlas> {
576        self.tlas_s.get(&id).expect("invalid tlas").clone()
577    }
578
579    fn resolve_texel_copy_texture_info(
580        &self,
581        info: wgt::TexelCopyTextureInfo<wgc::id::PointerId<wgc::id::markers::Texture>>,
582    ) -> wgt::TexelCopyTextureInfo<Arc<wgc::resource::Texture>> {
583        wgt::TexelCopyTextureInfo {
584            texture: self.resolve_texture_id(info.texture),
585            mip_level: info.mip_level,
586            origin: info.origin,
587            aspect: info.aspect,
588        }
589    }
590
591    fn resolve_compute_pipeline_descriptor<'a>(
592        &self,
593        desc: wgc::device::trace::TraceComputePipelineDescriptor<'a>,
594    ) -> wgc::pipeline::ResolvedComputePipelineDescriptor<'a> {
595        wgc::pipeline::ResolvedComputePipelineDescriptor {
596            label: desc.label,
597            layout: desc.layout.map(|id| self.resolve_pipeline_layout_id(id)),
598            stage: wgc::pipeline::ResolvedProgrammableStageDescriptor {
599                module: self.resolve_shader_module_id(desc.stage.module),
600                entry_point: desc.stage.entry_point,
601                constants: desc.stage.constants,
602                zero_initialize_workgroup_memory: desc.stage.zero_initialize_workgroup_memory,
603            },
604            cache: desc.cache.map(|id| self.resolve_pipeline_cache_id(id)),
605        }
606    }
607
608    fn resolve_render_pipeline_descriptor<'a>(
609        &self,
610        desc: wgc::device::trace::TraceGeneralRenderPipelineDescriptor<'a>,
611    ) -> wgc::pipeline::ResolvedGeneralRenderPipelineDescriptor<'a> {
612        let layout = desc.layout.map(|id| self.resolve_pipeline_layout_id(id));
613
614        let vertex = match desc.vertex {
615            wgc::pipeline::RenderPipelineVertexProcessor::Vertex(vertex_state) => {
616                wgc::pipeline::RenderPipelineVertexProcessor::Vertex(
617                    wgc::pipeline::ResolvedVertexState {
618                        stage: wgc::pipeline::ResolvedProgrammableStageDescriptor {
619                            module: self.resolve_shader_module_id(vertex_state.stage.module),
620                            entry_point: vertex_state.stage.entry_point,
621                            constants: vertex_state.stage.constants,
622                            zero_initialize_workgroup_memory: vertex_state
623                                .stage
624                                .zero_initialize_workgroup_memory,
625                        },
626                        buffers: vertex_state.buffers,
627                    },
628                )
629            }
630            wgc::pipeline::RenderPipelineVertexProcessor::Mesh(task_state, mesh_state) => {
631                let resolved_task = task_state.map(|task| wgc::pipeline::ResolvedTaskState {
632                    stage: wgc::pipeline::ResolvedProgrammableStageDescriptor {
633                        module: self.resolve_shader_module_id(task.stage.module),
634                        entry_point: task.stage.entry_point,
635                        constants: task.stage.constants,
636                        zero_initialize_workgroup_memory: task
637                            .stage
638                            .zero_initialize_workgroup_memory,
639                    },
640                });
641                let resolved_mesh = wgc::pipeline::ResolvedMeshState {
642                    stage: wgc::pipeline::ResolvedProgrammableStageDescriptor {
643                        module: self.resolve_shader_module_id(mesh_state.stage.module),
644                        entry_point: mesh_state.stage.entry_point,
645                        constants: mesh_state.stage.constants,
646                        zero_initialize_workgroup_memory: mesh_state
647                            .stage
648                            .zero_initialize_workgroup_memory,
649                    },
650                };
651                wgc::pipeline::RenderPipelineVertexProcessor::Mesh(resolved_task, resolved_mesh)
652            }
653        };
654
655        let fragment = desc
656            .fragment
657            .map(|fragment_state| wgc::pipeline::ResolvedFragmentState {
658                stage: wgc::pipeline::ResolvedProgrammableStageDescriptor {
659                    module: self.resolve_shader_module_id(fragment_state.stage.module),
660                    entry_point: fragment_state.stage.entry_point,
661                    constants: fragment_state.stage.constants,
662                    zero_initialize_workgroup_memory: fragment_state
663                        .stage
664                        .zero_initialize_workgroup_memory,
665                },
666                targets: fragment_state.targets,
667            });
668
669        wgc::pipeline::ResolvedGeneralRenderPipelineDescriptor {
670            label: desc.label,
671            layout,
672            vertex,
673            primitive: desc.primitive,
674            depth_stencil: desc.depth_stencil,
675            multisample: desc.multisample,
676            fragment,
677            multiview_mask: desc.multiview_mask,
678            cache: desc.cache.map(|id| self.resolve_pipeline_cache_id(id)),
679        }
680    }
681
682    fn resolve_bind_group_descriptor<'a>(
683        &self,
684        desc: wgc::device::trace::TraceBindGroupDescriptor<'a>,
685    ) -> wgc::binding_model::ResolvedBindGroupDescriptor<'a> {
686        let layout = self.resolve_bind_group_layout_id(desc.layout);
687
688        let entries: Vec<wgc::binding_model::ResolvedBindGroupEntry> = desc
689            .entries
690            .to_vec()
691            .into_iter()
692            .map(|entry| {
693                let resource = match entry.resource {
694                    BindingResource::Buffer(buffer_binding) => {
695                        let buffer = self.resolve_buffer_id(buffer_binding.buffer);
696                        wgc::binding_model::ResolvedBindingResource::Buffer(
697                            wgc::binding_model::ResolvedBufferBinding {
698                                buffer,
699                                offset: buffer_binding.offset,
700                                size: buffer_binding.size,
701                            },
702                        )
703                    }
704                    BindingResource::BufferArray(buffer_bindings) => {
705                        let resolved_buffers: Vec<_> = buffer_bindings
706                            .to_vec()
707                            .into_iter()
708                            .map(|bb| {
709                                let buffer = self.resolve_buffer_id(bb.buffer);
710                                wgc::binding_model::ResolvedBufferBinding {
711                                    buffer,
712                                    offset: bb.offset,
713                                    size: bb.size,
714                                }
715                            })
716                            .collect();
717                        wgc::binding_model::ResolvedBindingResource::BufferArray(Cow::Owned(
718                            resolved_buffers,
719                        ))
720                    }
721                    BindingResource::Sampler(sampler_id) => {
722                        let sampler = self.resolve_sampler_id(sampler_id);
723                        wgc::binding_model::ResolvedBindingResource::Sampler(sampler)
724                    }
725                    BindingResource::SamplerArray(sampler_ids) => {
726                        let resolved_samplers: Vec<_> = sampler_ids
727                            .to_vec()
728                            .into_iter()
729                            .map(|id| self.resolve_sampler_id(id))
730                            .collect();
731                        wgc::binding_model::ResolvedBindingResource::SamplerArray(Cow::Owned(
732                            resolved_samplers,
733                        ))
734                    }
735                    BindingResource::TextureView(texture_view_id) => {
736                        let texture_view = self.resolve_texture_view_id(texture_view_id);
737                        wgc::binding_model::ResolvedBindingResource::TextureView(texture_view)
738                    }
739                    BindingResource::TextureViewArray(texture_view_ids) => {
740                        let resolved_views: Vec<_> = texture_view_ids
741                            .to_vec()
742                            .into_iter()
743                            .map(|id| self.resolve_texture_view_id(id))
744                            .collect();
745                        wgc::binding_model::ResolvedBindingResource::TextureViewArray(Cow::Owned(
746                            resolved_views,
747                        ))
748                    }
749                    BindingResource::AccelerationStructure(tlas_id) => {
750                        let tlas = self.resolve_tlas_id(tlas_id);
751                        wgc::binding_model::ResolvedBindingResource::AccelerationStructure(tlas)
752                    }
753                    BindingResource::ExternalTexture(external_texture_id) => {
754                        let external_texture =
755                            self.resolve_external_texture_id(external_texture_id);
756                        wgc::binding_model::ResolvedBindingResource::ExternalTexture(
757                            external_texture,
758                        )
759                    }
760                };
761
762                wgc::binding_model::ResolvedBindGroupEntry {
763                    binding: entry.binding,
764                    resource,
765                }
766            })
767            .collect();
768
769        wgc::binding_model::ResolvedBindGroupDescriptor {
770            label: desc.label.clone(),
771            layout,
772            entries: entries.into(),
773        }
774    }
775
776    fn resolve_command(&self, command: Command<PointerReferences>) -> ArcCommand {
777        match command {
778            Command::CopyBufferToBuffer {
779                src,
780                src_offset,
781                dst,
782                dst_offset,
783                size,
784            } => Command::CopyBufferToBuffer {
785                src: self.resolve_buffer_id(src),
786                src_offset,
787                dst: self.resolve_buffer_id(dst),
788                dst_offset,
789                size,
790            },
791            Command::CopyBufferToTexture { src, dst, size } => Command::CopyBufferToTexture {
792                src: self.resolve_texel_copy_buffer_info(src),
793                dst: self.resolve_texel_copy_texture_info(dst),
794                size,
795            },
796            Command::CopyTextureToBuffer { src, dst, size } => Command::CopyTextureToBuffer {
797                src: self.resolve_texel_copy_texture_info(src),
798                dst: self.resolve_texel_copy_buffer_info(dst),
799                size,
800            },
801            Command::CopyTextureToTexture { src, dst, size } => Command::CopyTextureToTexture {
802                src: self.resolve_texel_copy_texture_info(src),
803                dst: self.resolve_texel_copy_texture_info(dst),
804                size,
805            },
806            Command::ClearBuffer { dst, offset, size } => Command::ClearBuffer {
807                dst: self.resolve_buffer_id(dst),
808                offset,
809                size,
810            },
811            Command::ClearTexture {
812                dst,
813                subresource_range,
814            } => Command::ClearTexture {
815                dst: self.resolve_texture_id(dst),
816                subresource_range,
817            },
818            Command::WriteTimestamp {
819                query_set,
820                query_index,
821            } => Command::WriteTimestamp {
822                query_set: self.resolve_query_set_id(query_set),
823                query_index,
824            },
825            Command::ResolveQuerySet {
826                query_set,
827                start_query,
828                query_count,
829                destination,
830                destination_offset,
831            } => Command::ResolveQuerySet {
832                query_set: self.resolve_query_set_id(query_set),
833                start_query,
834                query_count,
835                destination: self.resolve_buffer_id(destination),
836                destination_offset,
837            },
838            Command::PushDebugGroup(label) => Command::PushDebugGroup(label.clone()),
839            Command::PopDebugGroup => Command::PopDebugGroup,
840            Command::InsertDebugMarker(label) => Command::InsertDebugMarker(label.clone()),
841            Command::RunComputePass {
842                pass,
843                timestamp_writes,
844            } => Command::RunComputePass {
845                pass: self.resolve_compute_pass(pass),
846                timestamp_writes: timestamp_writes.map(|tw| self.resolve_pass_timestamp_writes(tw)),
847            },
848            Command::RunRenderPass {
849                pass,
850                color_attachments,
851                depth_stencil_attachment,
852                timestamp_writes,
853                occlusion_query_set,
854                multiview_mask,
855            } => Command::RunRenderPass {
856                pass: self.resolve_render_pass(pass),
857                color_attachments: self.resolve_color_attachments(color_attachments),
858                depth_stencil_attachment: depth_stencil_attachment
859                    .map(|att| self.resolve_depth_stencil_attachment(att)),
860                timestamp_writes: timestamp_writes.map(|tw| self.resolve_pass_timestamp_writes(tw)),
861                occlusion_query_set: occlusion_query_set.map(|qs| self.resolve_query_set_id(qs)),
862                multiview_mask,
863            },
864            Command::BuildAccelerationStructures { blas, tlas } => {
865                Command::BuildAccelerationStructures {
866                    blas: blas
867                        .into_iter()
868                        .map(|entry| self.resolve_blas_build_entry(entry))
869                        .collect(),
870                    tlas: tlas
871                        .into_iter()
872                        .map(|package| self.resolve_tlas_package(package))
873                        .collect(),
874                }
875            }
876            Command::TransitionResources {
877                buffer_transitions,
878                texture_transitions,
879            } => Command::TransitionResources {
880                buffer_transitions: buffer_transitions
881                    .into_iter()
882                    .map(|trans| self.resolve_buffer_transition(trans))
883                    .collect(),
884                texture_transitions: texture_transitions
885                    .into_iter()
886                    .map(|trans| self.resolve_texture_transition(trans))
887                    .collect(),
888            },
889        }
890    }
891
892    // Helper methods for command resolution
893    fn resolve_texel_copy_buffer_info(
894        &self,
895        info: wgt::TexelCopyBufferInfo<PointerId<wgc::id::markers::Buffer>>,
896    ) -> wgt::TexelCopyBufferInfo<Arc<wgc::resource::Buffer>> {
897        wgt::TexelCopyBufferInfo {
898            buffer: self
899                .buffers
900                .get(&info.buffer)
901                .cloned()
902                .expect("invalid buffer"),
903            layout: info.layout,
904        }
905    }
906
907    fn resolve_compute_pass(
908        &self,
909        pass: BasePass<wgc::command::ComputeCommand<PointerReferences>, Infallible>,
910    ) -> BasePass<wgc::command::ComputeCommand<ArcReferences>, Infallible> {
911        let BasePass {
912            label,
913            error,
914            commands,
915            dynamic_offsets,
916            push_constant_data,
917            string_data,
918        } = pass;
919
920        BasePass {
921            label,
922            error,
923            commands: commands
924                .into_iter()
925                .map(|cmd| self.resolve_compute_command(cmd))
926                .collect(),
927            dynamic_offsets,
928            push_constant_data,
929            string_data,
930        }
931    }
932
933    fn resolve_render_pass(
934        &self,
935        pass: BasePass<wgc::command::RenderCommand<PointerReferences>, Infallible>,
936    ) -> BasePass<wgc::command::RenderCommand<ArcReferences>, Infallible> {
937        let BasePass {
938            label,
939            error,
940            commands,
941            dynamic_offsets,
942            push_constant_data,
943            string_data,
944        } = pass;
945
946        BasePass {
947            label,
948            error,
949            commands: commands
950                .into_iter()
951                .map(|cmd| self.resolve_render_command(cmd))
952                .collect(),
953            dynamic_offsets,
954            push_constant_data,
955            string_data,
956        }
957    }
958
959    fn resolve_compute_command(
960        &self,
961        command: wgc::command::ComputeCommand<PointerReferences>,
962    ) -> wgc::command::ComputeCommand<ArcReferences> {
963        use wgc::command::ComputeCommand as C;
964        match command {
965            C::SetBindGroup {
966                index,
967                num_dynamic_offsets,
968                bind_group,
969            } => C::SetBindGroup {
970                index,
971                num_dynamic_offsets,
972                bind_group: bind_group.map(|bg| self.resolve_bind_group_id(bg)),
973            },
974            C::SetPipeline(id) => C::SetPipeline(self.resolve_compute_pipeline_id(id)),
975            C::SetPushConstant {
976                offset,
977                size_bytes,
978                values_offset,
979            } => C::SetPushConstant {
980                offset,
981                size_bytes,
982                values_offset,
983            },
984            C::Dispatch(groups) => C::Dispatch(groups),
985            C::DispatchIndirect { buffer, offset } => C::DispatchIndirect {
986                buffer: self.resolve_buffer_id(buffer),
987                offset,
988            },
989            C::PushDebugGroup { color, len } => C::PushDebugGroup { color, len },
990            C::PopDebugGroup => C::PopDebugGroup,
991            C::InsertDebugMarker { color, len } => C::InsertDebugMarker { color, len },
992            C::WriteTimestamp {
993                query_set,
994                query_index,
995            } => C::WriteTimestamp {
996                query_set: self.resolve_query_set_id(query_set),
997                query_index,
998            },
999            C::BeginPipelineStatisticsQuery {
1000                query_set,
1001                query_index,
1002            } => C::BeginPipelineStatisticsQuery {
1003                query_set: self.resolve_query_set_id(query_set),
1004                query_index,
1005            },
1006            C::EndPipelineStatisticsQuery => C::EndPipelineStatisticsQuery,
1007        }
1008    }
1009
1010    fn resolve_render_command(
1011        &self,
1012        command: wgc::command::RenderCommand<PointerReferences>,
1013    ) -> wgc::command::RenderCommand<ArcReferences> {
1014        use wgc::command::RenderCommand as C;
1015        match command {
1016            C::SetBindGroup {
1017                index,
1018                num_dynamic_offsets,
1019                bind_group,
1020            } => C::SetBindGroup {
1021                index,
1022                num_dynamic_offsets,
1023                bind_group: bind_group.map(|bg| self.resolve_bind_group_id(bg)),
1024            },
1025            C::SetPipeline(id) => C::SetPipeline(self.resolve_render_pipeline_id(id)),
1026            C::SetIndexBuffer {
1027                buffer,
1028                index_format,
1029                offset,
1030                size,
1031            } => C::SetIndexBuffer {
1032                buffer: self.resolve_buffer_id(buffer),
1033                index_format,
1034                offset,
1035                size,
1036            },
1037            C::SetVertexBuffer {
1038                slot,
1039                buffer,
1040                offset,
1041                size,
1042            } => C::SetVertexBuffer {
1043                slot,
1044                buffer: self.resolve_buffer_id(buffer),
1045                offset,
1046                size,
1047            },
1048            C::SetBlendConstant(color) => C::SetBlendConstant(color),
1049            C::SetStencilReference(val) => C::SetStencilReference(val),
1050            C::SetViewport {
1051                rect,
1052                depth_min,
1053                depth_max,
1054            } => C::SetViewport {
1055                rect,
1056                depth_min,
1057                depth_max,
1058            },
1059            C::SetScissor(rect) => C::SetScissor(rect),
1060            C::SetPushConstant {
1061                stages,
1062                offset,
1063                size_bytes,
1064                values_offset,
1065            } => C::SetPushConstant {
1066                stages,
1067                offset,
1068                size_bytes,
1069                values_offset,
1070            },
1071            C::Draw {
1072                vertex_count,
1073                instance_count,
1074                first_vertex,
1075                first_instance,
1076            } => C::Draw {
1077                vertex_count,
1078                instance_count,
1079                first_vertex,
1080                first_instance,
1081            },
1082            C::DrawIndexed {
1083                index_count,
1084                instance_count,
1085                first_index,
1086                base_vertex,
1087                first_instance,
1088            } => C::DrawIndexed {
1089                index_count,
1090                instance_count,
1091                first_index,
1092                base_vertex,
1093                first_instance,
1094            },
1095            C::DrawMeshTasks {
1096                group_count_x,
1097                group_count_y,
1098                group_count_z,
1099            } => C::DrawMeshTasks {
1100                group_count_x,
1101                group_count_y,
1102                group_count_z,
1103            },
1104            C::DrawIndirect {
1105                buffer,
1106                offset,
1107                count,
1108                family,
1109                vertex_or_index_limit,
1110                instance_limit,
1111            } => C::DrawIndirect {
1112                buffer: self.resolve_buffer_id(buffer),
1113                offset,
1114                count,
1115                family,
1116                vertex_or_index_limit,
1117                instance_limit,
1118            },
1119            C::MultiDrawIndirectCount {
1120                buffer,
1121                offset,
1122                count_buffer,
1123                count_buffer_offset,
1124                max_count,
1125                family,
1126            } => C::MultiDrawIndirectCount {
1127                buffer: self.resolve_buffer_id(buffer),
1128                offset,
1129                count_buffer: self.resolve_buffer_id(count_buffer),
1130                count_buffer_offset,
1131                max_count,
1132                family,
1133            },
1134            C::PushDebugGroup { color, len } => C::PushDebugGroup { color, len },
1135            C::PopDebugGroup => C::PopDebugGroup,
1136            C::InsertDebugMarker { color, len } => C::InsertDebugMarker { color, len },
1137            C::WriteTimestamp {
1138                query_set,
1139                query_index,
1140            } => C::WriteTimestamp {
1141                query_set: self.resolve_query_set_id(query_set),
1142                query_index,
1143            },
1144            C::BeginOcclusionQuery { query_index } => C::BeginOcclusionQuery { query_index },
1145            C::EndOcclusionQuery => C::EndOcclusionQuery,
1146            C::BeginPipelineStatisticsQuery {
1147                query_set,
1148                query_index,
1149            } => C::BeginPipelineStatisticsQuery {
1150                query_set: self.resolve_query_set_id(query_set),
1151                query_index,
1152            },
1153            C::EndPipelineStatisticsQuery => C::EndPipelineStatisticsQuery,
1154            C::ExecuteBundle(bundle) => C::ExecuteBundle(self.resolve_render_bundle_id(bundle)),
1155        }
1156    }
1157
1158    fn resolve_pass_timestamp_writes(
1159        &self,
1160        writes: wgc::command::PassTimestampWrites<PointerId<wgc::id::markers::QuerySet>>,
1161    ) -> wgc::command::PassTimestampWrites<Arc<wgc::resource::QuerySet>> {
1162        wgc::command::PassTimestampWrites {
1163            query_set: self.resolve_query_set_id(writes.query_set),
1164            beginning_of_pass_write_index: writes.beginning_of_pass_write_index,
1165            end_of_pass_write_index: writes.end_of_pass_write_index,
1166        }
1167    }
1168
1169    fn resolve_color_attachments(
1170        &self,
1171        attachments: wgc::command::ColorAttachments<PointerId<wgc::id::markers::TextureView>>,
1172    ) -> wgc::command::ColorAttachments<Arc<wgc::resource::TextureView>> {
1173        attachments
1174            .into_iter()
1175            .map(|opt| {
1176                opt.map(|att| wgc::command::RenderPassColorAttachment {
1177                    view: self.resolve_texture_view_id(att.view),
1178                    depth_slice: att.depth_slice,
1179                    resolve_target: att
1180                        .resolve_target
1181                        .map(|rt| self.resolve_texture_view_id(rt)),
1182                    load_op: att.load_op,
1183                    store_op: att.store_op,
1184                })
1185            })
1186            .collect()
1187    }
1188
1189    fn resolve_depth_stencil_attachment(
1190        &self,
1191        attachment: wgc::command::ResolvedRenderPassDepthStencilAttachment<
1192            PointerId<wgc::id::markers::TextureView>,
1193        >,
1194    ) -> wgc::command::ResolvedRenderPassDepthStencilAttachment<Arc<wgc::resource::TextureView>>
1195    {
1196        wgc::command::ResolvedRenderPassDepthStencilAttachment {
1197            view: self.resolve_texture_view_id(attachment.view),
1198            depth: attachment.depth,
1199            stencil: attachment.stencil,
1200        }
1201    }
1202
1203    fn resolve_blas_build_entry(
1204        &self,
1205        entry: wgc::ray_tracing::OwnedBlasBuildEntry<PointerReferences>,
1206    ) -> wgc::ray_tracing::OwnedBlasBuildEntry<ArcReferences> {
1207        wgc::ray_tracing::OwnedBlasBuildEntry {
1208            blas: self.resolve_blas_id(entry.blas),
1209            geometries: self.resolve_blas_geometries(entry.geometries),
1210        }
1211    }
1212
1213    fn resolve_tlas_package(
1214        &self,
1215        package: wgc::ray_tracing::OwnedTlasPackage<PointerReferences>,
1216    ) -> wgc::ray_tracing::OwnedTlasPackage<ArcReferences> {
1217        wgc::ray_tracing::OwnedTlasPackage {
1218            tlas: self.resolve_tlas_id(package.tlas),
1219            instances: package
1220                .instances
1221                .into_iter()
1222                .map(|opt| opt.map(|inst| self.resolve_tlas_instance(inst)))
1223                .collect(),
1224            lowest_unmodified: package.lowest_unmodified,
1225        }
1226    }
1227
1228    // Helper functions for ray tracing structures
1229    fn resolve_blas_geometries(
1230        &self,
1231        geometries: wgc::ray_tracing::OwnedBlasGeometries<PointerReferences>,
1232    ) -> wgc::ray_tracing::OwnedBlasGeometries<ArcReferences> {
1233        match geometries {
1234            wgc::ray_tracing::OwnedBlasGeometries::TriangleGeometries(geos) => {
1235                wgc::ray_tracing::OwnedBlasGeometries::TriangleGeometries(
1236                    geos.into_iter()
1237                        .map(|geo| self.resolve_blas_triangle_geometry(geo))
1238                        .collect(),
1239                )
1240            }
1241        }
1242    }
1243
1244    fn resolve_blas_triangle_geometry(
1245        &self,
1246        geometry: wgc::ray_tracing::OwnedBlasTriangleGeometry<PointerReferences>,
1247    ) -> wgc::ray_tracing::OwnedBlasTriangleGeometry<ArcReferences> {
1248        wgc::ray_tracing::OwnedBlasTriangleGeometry {
1249            size: geometry.size,
1250            vertex_buffer: self.resolve_buffer_id(geometry.vertex_buffer),
1251            index_buffer: geometry.index_buffer.map(|buf| self.resolve_buffer_id(buf)),
1252            transform_buffer: geometry
1253                .transform_buffer
1254                .map(|buf| self.resolve_buffer_id(buf)),
1255            first_vertex: geometry.first_vertex,
1256            vertex_stride: geometry.vertex_stride,
1257            first_index: geometry.first_index,
1258            transform_buffer_offset: geometry.transform_buffer_offset,
1259        }
1260    }
1261
1262    fn resolve_tlas_instance(
1263        &self,
1264        instance: wgc::ray_tracing::OwnedTlasInstance<PointerReferences>,
1265    ) -> wgc::ray_tracing::OwnedTlasInstance<ArcReferences> {
1266        wgc::ray_tracing::OwnedTlasInstance {
1267            blas: self.resolve_blas_id(instance.blas),
1268            transform: instance.transform,
1269            custom_data: instance.custom_data,
1270            mask: instance.mask,
1271        }
1272    }
1273
1274    fn resolve_buffer_transition(
1275        &self,
1276        trans: wgt::BufferTransition<PointerId<wgc::id::markers::Buffer>>,
1277    ) -> wgt::BufferTransition<Arc<wgc::resource::Buffer>> {
1278        wgt::BufferTransition {
1279            buffer: self
1280                .buffers
1281                .get(&trans.buffer)
1282                .cloned()
1283                .expect("invalid buffer"),
1284            state: trans.state,
1285        }
1286    }
1287
1288    fn resolve_texture_transition(
1289        &self,
1290        trans: wgt::TextureTransition<PointerId<wgc::id::markers::Texture>>,
1291    ) -> wgt::TextureTransition<Arc<wgc::resource::Texture>> {
1292        wgt::TextureTransition {
1293            texture: self
1294                .textures
1295                .get(&trans.texture)
1296                .cloned()
1297                .expect("invalid texture"),
1298            selector: trans.selector.clone(),
1299            state: trans.state,
1300        }
1301    }
1302}