wgpu_core/device/trace/
record.rs

1use alloc::{borrow::Cow, string::ToString, sync::Arc, vec::Vec};
2use core::{any::Any, convert::Infallible, marker::PhantomData};
3use std::io::Write as _;
4
5use crate::{
6    command::{
7        ArcCommand, ArcComputeCommand, ArcReferences, ArcRenderCommand, BasePass, ColorAttachments,
8        Command, ComputeCommand, PassTimestampWrites, PointerReferences, RenderCommand,
9        RenderPassColorAttachment, ResolvedRenderPassDepthStencilAttachment,
10    },
11    device::trace::{Data, DataKind},
12    id::{markers, PointerId},
13    storage::StorageItem,
14};
15
16use super::{
17    Action, TraceBindGroupDescriptor, TraceComputePipelineDescriptor,
18    TraceGeneralRenderPipelineDescriptor, FILE_NAME,
19};
20
21pub(crate) fn new_render_bundle_encoder_descriptor(
22    label: crate::Label<'_>,
23    context: &crate::device::RenderPassContext,
24    depth_read_only: bool,
25    stencil_read_only: bool,
26) -> crate::command::RenderBundleEncoderDescriptor<'static> {
27    crate::command::RenderBundleEncoderDescriptor {
28        label: label.map(|l| Cow::from(l.to_string())),
29        color_formats: Cow::from(context.attachments.colors.to_vec()),
30        depth_stencil: context.attachments.depth_stencil.map(|format| {
31            wgt::RenderBundleDepthStencil {
32                format,
33                depth_read_only,
34                stencil_read_only,
35            }
36        }),
37        sample_count: context.sample_count,
38        multiview: context.multiview_mask,
39    }
40}
41
42pub trait Trace: Any + Send + Sync {
43    fn make_binary(&mut self, kind: DataKind, data: &[u8]) -> Data;
44
45    fn make_string(&mut self, kind: DataKind, data: &str) -> Data;
46
47    fn add(&mut self, action: Action<'_, PointerReferences>)
48    where
49        for<'a> Action<'a, PointerReferences>: serde::Serialize;
50}
51
52#[derive(Debug)]
53pub struct DiskTrace {
54    path: std::path::PathBuf,
55    file: std::fs::File,
56    config: ron::ser::PrettyConfig,
57    data_id: usize,
58}
59
60impl DiskTrace {
61    pub fn new(path: std::path::PathBuf) -> Result<Self, std::io::Error> {
62        log::debug!("Tracing into '{path:?}'");
63        let mut file = std::fs::File::create(path.join(FILE_NAME))?;
64        file.write_all(b"[\n")?;
65        Ok(Self {
66            path,
67            file,
68            config: ron::ser::PrettyConfig::default(),
69            data_id: 0,
70        })
71    }
72}
73
74impl Trace for DiskTrace {
75    /// Store `[u8]` data in the trace.
76    ///
77    /// Using a string `kind` is probably a bug, but should work as long as the
78    /// data is UTF-8.
79    fn make_binary(&mut self, kind: DataKind, data: &[u8]) -> Data {
80        self.data_id += 1;
81        let name = std::format!("data{}.{}", self.data_id, kind);
82        let _ = std::fs::write(self.path.join(&name), data);
83        Data::File(name)
84    }
85
86    /// Store `str` data in the trace.
87    ///
88    /// Using a binary `kind` is fine, but it's not clear why not use
89    /// `make_binary` instead.
90    fn make_string(&mut self, kind: DataKind, data: &str) -> Data {
91        self.make_binary(kind, data.as_bytes())
92    }
93
94    fn add(&mut self, action: Action<'_, PointerReferences>)
95    where
96        for<'a> Action<'a, PointerReferences>: serde::Serialize,
97    {
98        match ron::ser::to_string_pretty(&action, self.config.clone()) {
99            Ok(string) => {
100                let _ = writeln!(self.file, "{string},");
101            }
102            Err(e) => {
103                log::warn!("RON serialization failure: {e:?}");
104            }
105        }
106    }
107}
108
109impl Drop for DiskTrace {
110    fn drop(&mut self) {
111        let _ = self.file.write_all(b"]");
112    }
113}
114
115#[derive(Default)]
116pub struct MemoryTrace {
117    actions: Vec<Action<'static, PointerReferences>>,
118}
119
120impl MemoryTrace {
121    pub fn new() -> Self {
122        Self::default()
123    }
124
125    pub fn actions(&self) -> &[Action<'static, PointerReferences>] {
126        &self.actions
127    }
128}
129
130impl Trace for MemoryTrace {
131    /// Store `[u8]` data in the trace.
132    ///
133    /// Using a string `kind` is probably a bug, but should work as long as the
134    /// data is UTF-8.
135    fn make_binary(&mut self, kind: DataKind, data: &[u8]) -> Data {
136        Data::Binary(kind, data.to_vec())
137    }
138
139    /// Store `str` data in the trace.
140    ///
141    /// Using a binary `kind` is fine, but it's not clear why not use
142    /// `make_binary` instead.
143    fn make_string(&mut self, kind: DataKind, data: &str) -> Data {
144        Data::String(kind, data.to_string())
145    }
146
147    fn add(&mut self, action: Action<'_, PointerReferences>)
148    where
149        for<'a> Action<'a, PointerReferences>: serde::Serialize,
150    {
151        self.actions.push(action_to_owned(action))
152    }
153}
154
155pub(crate) trait IntoTrace {
156    type Output;
157    fn into_trace(self) -> Self::Output;
158
159    fn to_trace(&self) -> Self::Output
160    where
161        Self: Sized + Clone,
162    {
163        self.clone().into_trace()
164    }
165}
166
167impl<T: StorageItem> IntoTrace for Arc<T> {
168    type Output = PointerId<T::Marker>;
169    fn into_trace(self) -> Self::Output {
170        self.to_trace()
171    }
172
173    fn to_trace(&self) -> Self::Output {
174        PointerId::from(self)
175    }
176}
177
178/// This will work as expected on heap-allocated types that are not moved around.
179pub(crate) unsafe fn to_trace<T: StorageItem>(t: &T) -> PointerId<T::Marker> {
180    PointerId::PointerId(
181        #[expect(trivial_casts)]
182        core::num::NonZeroUsize::new(t as *const T as usize).unwrap(),
183        PhantomData,
184    )
185}
186
187impl IntoTrace for ArcCommand {
188    type Output = Command<PointerReferences>;
189    fn into_trace(self) -> Self::Output {
190        match self {
191            ArcCommand::CopyBufferToBuffer {
192                src,
193                src_offset,
194                dst,
195                dst_offset,
196                size,
197            } => Command::CopyBufferToBuffer {
198                src: src.to_trace(),
199                src_offset,
200                dst: dst.to_trace(),
201                dst_offset,
202                size,
203            },
204            ArcCommand::CopyBufferToTexture { src, dst, size } => Command::CopyBufferToTexture {
205                src: src.into_trace(),
206                dst: dst.into_trace(),
207                size,
208            },
209            ArcCommand::CopyTextureToBuffer { src, dst, size } => Command::CopyTextureToBuffer {
210                src: src.into_trace(),
211                dst: dst.into_trace(),
212                size,
213            },
214            ArcCommand::CopyTextureToTexture { src, dst, size } => Command::CopyTextureToTexture {
215                src: src.into_trace(),
216                dst: dst.into_trace(),
217                size,
218            },
219            ArcCommand::ClearBuffer { dst, offset, size } => Command::ClearBuffer {
220                dst: dst.to_trace(),
221                offset,
222                size,
223            },
224            ArcCommand::ClearTexture {
225                dst,
226                subresource_range,
227            } => Command::ClearTexture {
228                dst: dst.to_trace(),
229                subresource_range,
230            },
231            ArcCommand::WriteTimestamp {
232                query_set,
233                query_index,
234            } => Command::WriteTimestamp {
235                query_set: query_set.to_trace(),
236                query_index,
237            },
238            ArcCommand::ResolveQuerySet {
239                query_set,
240                start_query,
241                query_count,
242                destination,
243                destination_offset,
244            } => Command::ResolveQuerySet {
245                query_set: query_set.to_trace(),
246                start_query,
247                query_count,
248                destination: destination.to_trace(),
249                destination_offset,
250            },
251            ArcCommand::PushDebugGroup(label) => Command::PushDebugGroup(label),
252            ArcCommand::PopDebugGroup => Command::PopDebugGroup,
253            ArcCommand::InsertDebugMarker(label) => Command::InsertDebugMarker(label),
254            ArcCommand::RunComputePass {
255                pass,
256                timestamp_writes,
257            } => Command::RunComputePass {
258                pass: pass.into_trace(),
259                timestamp_writes: timestamp_writes.map(|tw| tw.into_trace()),
260            },
261            ArcCommand::RunRenderPass {
262                pass,
263                color_attachments,
264                depth_stencil_attachment,
265                timestamp_writes,
266                occlusion_query_set,
267                multiview_mask,
268            } => Command::RunRenderPass {
269                pass: pass.into_trace(),
270                color_attachments: color_attachments.into_trace(),
271                depth_stencil_attachment: depth_stencil_attachment.map(|d| d.into_trace()),
272                timestamp_writes: timestamp_writes.map(|tw| tw.into_trace()),
273                occlusion_query_set: occlusion_query_set.map(|q| q.to_trace()),
274                multiview_mask,
275            },
276            ArcCommand::BuildAccelerationStructures { blas, tlas } => {
277                Command::BuildAccelerationStructures {
278                    blas: blas.into_iter().map(|b| b.into_trace()).collect(),
279                    tlas: tlas.into_iter().map(|b| b.into_trace()).collect(),
280                }
281            }
282            ArcCommand::TransitionResources {
283                buffer_transitions: _,
284                texture_transitions: _,
285            } => {
286                // TransitionResources does not exist in Command, so skip or handle as needed.
287                // If you want to ignore, you could panic or return a default.
288                panic!("TransitionResources cannot be converted to Command");
289            }
290        }
291    }
292}
293
294impl<T: IntoTrace> IntoTrace for wgt::TexelCopyBufferInfo<T> {
295    type Output = wgt::TexelCopyBufferInfo<T::Output>;
296    fn into_trace(self) -> Self::Output {
297        wgt::TexelCopyBufferInfo {
298            buffer: self.buffer.into_trace(),
299            layout: self.layout,
300        }
301    }
302}
303
304impl<T: IntoTrace> IntoTrace for wgt::TexelCopyTextureInfo<T> {
305    type Output = wgt::TexelCopyTextureInfo<T::Output>;
306    fn into_trace(self) -> Self::Output {
307        wgt::TexelCopyTextureInfo {
308            texture: self.texture.into_trace(),
309            mip_level: self.mip_level,
310            origin: self.origin,
311            aspect: self.aspect,
312        }
313    }
314}
315
316impl IntoTrace for PassTimestampWrites {
317    type Output = PassTimestampWrites<PointerId<markers::QuerySet>>;
318    fn into_trace(self) -> Self::Output {
319        PassTimestampWrites {
320            query_set: self.query_set.into_trace(),
321            beginning_of_pass_write_index: self.beginning_of_pass_write_index,
322            end_of_pass_write_index: self.end_of_pass_write_index,
323        }
324    }
325}
326
327impl IntoTrace for ColorAttachments {
328    type Output = ColorAttachments<PointerId<markers::TextureView>>;
329    fn into_trace(self) -> Self::Output {
330        self.into_iter()
331            .map(|opt| {
332                opt.map(|att| RenderPassColorAttachment {
333                    view: att.view.into_trace(),
334                    depth_slice: att.depth_slice,
335                    resolve_target: att.resolve_target.map(|r| r.into_trace()),
336                    load_op: att.load_op,
337                    store_op: att.store_op,
338                })
339            })
340            .collect()
341    }
342}
343
344impl<TV: IntoTrace> IntoTrace for ResolvedRenderPassDepthStencilAttachment<TV> {
345    type Output = ResolvedRenderPassDepthStencilAttachment<TV::Output>;
346    fn into_trace(self) -> Self::Output {
347        ResolvedRenderPassDepthStencilAttachment {
348            view: self.view.into_trace(),
349            depth: self.depth,
350            stencil: self.stencil,
351        }
352    }
353}
354
355impl IntoTrace for crate::ray_tracing::OwnedBlasBuildEntry<ArcReferences> {
356    type Output = crate::ray_tracing::OwnedBlasBuildEntry<PointerReferences>;
357    fn into_trace(self) -> Self::Output {
358        crate::ray_tracing::OwnedBlasBuildEntry {
359            blas: self.blas.into_trace(),
360            geometries: self.geometries.into_trace(),
361        }
362    }
363}
364
365impl IntoTrace for crate::ray_tracing::OwnedBlasGeometries<ArcReferences> {
366    type Output = crate::ray_tracing::OwnedBlasGeometries<PointerReferences>;
367    fn into_trace(self) -> Self::Output {
368        match self {
369            crate::ray_tracing::OwnedBlasGeometries::TriangleGeometries(geos) => {
370                crate::ray_tracing::OwnedBlasGeometries::TriangleGeometries(
371                    geos.into_iter().map(|g| g.into_trace()).collect(),
372                )
373            }
374            crate::ray_tracing::OwnedBlasGeometries::AabbGeometries(geos) => {
375                crate::ray_tracing::OwnedBlasGeometries::AabbGeometries(
376                    geos.into_iter().map(|g| g.into_trace()).collect(),
377                )
378            }
379        }
380    }
381}
382
383impl IntoTrace for crate::ray_tracing::OwnedBlasTriangleGeometry<ArcReferences> {
384    type Output = crate::ray_tracing::OwnedBlasTriangleGeometry<PointerReferences>;
385    fn into_trace(self) -> Self::Output {
386        crate::ray_tracing::OwnedBlasTriangleGeometry {
387            size: self.size,
388            vertex_buffer: self.vertex_buffer.into_trace(),
389            index_buffer: self.index_buffer.map(|b| b.into_trace()),
390            transform_buffer: self.transform_buffer.map(|b| b.into_trace()),
391            first_vertex: self.first_vertex,
392            vertex_stride: self.vertex_stride,
393            first_index: self.first_index,
394            transform_buffer_offset: self.transform_buffer_offset,
395        }
396    }
397}
398
399impl IntoTrace for crate::ray_tracing::OwnedBlasAabbGeometry<ArcReferences> {
400    type Output = crate::ray_tracing::OwnedBlasAabbGeometry<PointerReferences>;
401    fn into_trace(self) -> Self::Output {
402        crate::ray_tracing::OwnedBlasAabbGeometry {
403            size: self.size,
404            stride: self.stride,
405            aabb_buffer: self.aabb_buffer.into_trace(),
406            primitive_offset: self.primitive_offset,
407        }
408    }
409}
410
411impl IntoTrace for crate::ray_tracing::OwnedTlasPackage<ArcReferences> {
412    type Output = crate::ray_tracing::OwnedTlasPackage<PointerReferences>;
413    fn into_trace(self) -> Self::Output {
414        crate::ray_tracing::OwnedTlasPackage {
415            tlas: self.tlas.into_trace(),
416            instances: self
417                .instances
418                .into_iter()
419                .map(|opt| opt.map(|inst| inst.into_trace()))
420                .collect(),
421            lowest_unmodified: self.lowest_unmodified,
422        }
423    }
424}
425
426impl IntoTrace for crate::ray_tracing::OwnedTlasInstance<ArcReferences> {
427    type Output = crate::ray_tracing::OwnedTlasInstance<PointerReferences>;
428    fn into_trace(self) -> Self::Output {
429        crate::ray_tracing::OwnedTlasInstance {
430            blas: self.blas.into_trace(),
431            transform: self.transform,
432            custom_data: self.custom_data,
433            mask: self.mask,
434        }
435    }
436}
437
438impl<C: IntoTrace> IntoTrace for BasePass<C, Infallible> {
439    type Output = BasePass<C::Output, Infallible>;
440
441    fn into_trace(self) -> Self::Output {
442        BasePass {
443            label: self.label,
444            error: self.error,
445            commands: self
446                .commands
447                .into_iter()
448                .map(|cmd| cmd.into_trace())
449                .collect(),
450            dynamic_offsets: self.dynamic_offsets,
451            string_data: self.string_data,
452        }
453    }
454}
455
456impl IntoTrace for ArcComputeCommand {
457    type Output = ComputeCommand<PointerReferences>;
458    fn into_trace(self) -> Self::Output {
459        use ComputeCommand as C;
460        match self {
461            C::SetBindGroup {
462                index,
463                num_dynamic_offsets,
464                bind_group,
465            } => C::SetBindGroup {
466                index,
467                num_dynamic_offsets,
468                bind_group: bind_group.map(|bg| bg.into_trace()),
469            },
470            C::SetPipeline(id) => C::SetPipeline(id.into_trace()),
471            C::SetImmediate { offset, data } => C::SetImmediate { offset, data },
472            C::DispatchWorkgroups(groups) => C::DispatchWorkgroups(groups),
473            C::DispatchWorkgroupsIndirect { buffer, offset } => C::DispatchWorkgroupsIndirect {
474                buffer: buffer.into_trace(),
475                offset,
476            },
477            C::PushDebugGroup { color, len } => C::PushDebugGroup { color, len },
478            C::PopDebugGroup => C::PopDebugGroup,
479            C::InsertDebugMarker { color, len } => C::InsertDebugMarker { color, len },
480            C::WriteTimestamp {
481                query_set,
482                query_index,
483            } => C::WriteTimestamp {
484                query_set: query_set.into_trace(),
485                query_index,
486            },
487            C::BeginPipelineStatisticsQuery {
488                query_set,
489                query_index,
490            } => C::BeginPipelineStatisticsQuery {
491                query_set: query_set.into_trace(),
492                query_index,
493            },
494            C::EndPipelineStatisticsQuery => C::EndPipelineStatisticsQuery,
495            C::TransitionResources {
496                buffer_transitions,
497                texture_transitions,
498            } => C::TransitionResources {
499                buffer_transitions: buffer_transitions
500                    .into_iter()
501                    .map(|buffer_transition| wgt::BufferTransition {
502                        buffer: buffer_transition.buffer.into_trace(),
503                        state: buffer_transition.state,
504                    })
505                    .collect(),
506                texture_transitions: texture_transitions
507                    .into_iter()
508                    .map(|texture_transition| wgt::TextureTransition {
509                        texture: texture_transition.texture.into_trace(),
510                        selector: texture_transition.selector,
511                        state: texture_transition.state,
512                    })
513                    .collect(),
514            },
515        }
516    }
517}
518
519impl IntoTrace for ArcRenderCommand {
520    type Output = RenderCommand<PointerReferences>;
521    fn into_trace(self) -> Self::Output {
522        use RenderCommand as C;
523        match self {
524            C::SetBindGroup {
525                index,
526                num_dynamic_offsets,
527                bind_group,
528            } => C::SetBindGroup {
529                index,
530                num_dynamic_offsets,
531                bind_group: bind_group.map(|bg| bg.into_trace()),
532            },
533            C::SetPipeline(id) => C::SetPipeline(id.into_trace()),
534            C::SetIndexBuffer {
535                buffer,
536                index_format,
537                offset,
538                size,
539            } => C::SetIndexBuffer {
540                buffer: buffer.into_trace(),
541                index_format,
542                offset,
543                size,
544            },
545            C::SetVertexBuffer {
546                slot,
547                buffer,
548                offset,
549                size,
550            } => C::SetVertexBuffer {
551                slot,
552                buffer: buffer.into_trace(),
553                offset,
554                size,
555            },
556            C::SetBlendConstant(color) => C::SetBlendConstant(color),
557            C::SetStencilReference(val) => C::SetStencilReference(val),
558            C::SetViewport {
559                rect,
560                depth_min,
561                depth_max,
562            } => C::SetViewport {
563                rect,
564                depth_min,
565                depth_max,
566            },
567            C::SetScissor(rect) => C::SetScissor(rect),
568            C::SetImmediate { offset, data } => C::SetImmediate { offset, data },
569            C::Draw {
570                vertex_count,
571                instance_count,
572                first_vertex,
573                first_instance,
574            } => C::Draw {
575                vertex_count,
576                instance_count,
577                first_vertex,
578                first_instance,
579            },
580            C::DrawIndexed {
581                index_count,
582                instance_count,
583                first_index,
584                base_vertex,
585                first_instance,
586            } => C::DrawIndexed {
587                index_count,
588                instance_count,
589                first_index,
590                base_vertex,
591                first_instance,
592            },
593            C::DrawMeshTasks {
594                group_count_x,
595                group_count_y,
596                group_count_z,
597            } => C::DrawMeshTasks {
598                group_count_x,
599                group_count_y,
600                group_count_z,
601            },
602            C::DrawIndirect {
603                buffer,
604                offset,
605                count,
606                family,
607                vertex_or_index_limit,
608                instance_limit,
609            } => C::DrawIndirect {
610                buffer: buffer.into_trace(),
611                offset,
612                count,
613                family,
614                vertex_or_index_limit,
615                instance_limit,
616            },
617            C::MultiDrawIndirectCount {
618                buffer,
619                offset,
620                count_buffer,
621                count_buffer_offset,
622                max_count,
623                family,
624            } => C::MultiDrawIndirectCount {
625                buffer: buffer.into_trace(),
626                offset,
627                count_buffer: count_buffer.into_trace(),
628                count_buffer_offset,
629                max_count,
630                family,
631            },
632            C::PushDebugGroup { color, len } => C::PushDebugGroup { color, len },
633            C::PopDebugGroup => C::PopDebugGroup,
634            C::InsertDebugMarker { color, len } => C::InsertDebugMarker { color, len },
635            C::WriteTimestamp {
636                query_set,
637                query_index,
638            } => C::WriteTimestamp {
639                query_set: query_set.into_trace(),
640                query_index,
641            },
642            C::BeginOcclusionQuery { query_index } => C::BeginOcclusionQuery { query_index },
643            C::EndOcclusionQuery => C::EndOcclusionQuery,
644            C::BeginPipelineStatisticsQuery {
645                query_set,
646                query_index,
647            } => C::BeginPipelineStatisticsQuery {
648                query_set: query_set.into_trace(),
649                query_index,
650            },
651            C::EndPipelineStatisticsQuery => C::EndPipelineStatisticsQuery,
652            C::ExecuteBundle(bundle) => C::ExecuteBundle(bundle.into_trace()),
653        }
654    }
655}
656
657impl IntoTrace for crate::binding_model::PipelineLayoutDescriptor<'_> {
658    type Output = crate::binding_model::PipelineLayoutDescriptor<
659        'static,
660        PointerId<markers::BindGroupLayout>,
661    >;
662    fn into_trace(self) -> Self::Output {
663        crate::binding_model::PipelineLayoutDescriptor {
664            label: self.label.map(|l| Cow::Owned(l.into_owned())),
665            bind_group_layouts: self
666                .bind_group_layouts
667                .iter()
668                .map(|bgl| bgl.to_trace())
669                .collect(),
670            immediate_size: self.immediate_size,
671        }
672    }
673}
674
675impl<'a> IntoTrace for &'_ crate::binding_model::BindGroupDescriptor<'a> {
676    type Output = TraceBindGroupDescriptor<'a>;
677
678    fn into_trace(self) -> Self::Output {
679        use crate::binding_model::{BindGroupEntry, BindingResource, BufferBinding};
680        TraceBindGroupDescriptor {
681            label: self.label.clone(),
682            layout: self.layout.to_trace(),
683            entries: Cow::Owned(
684                self.entries
685                    .iter()
686                    .map(|entry| {
687                        let resource = match &entry.resource {
688                            BindingResource::Buffer(buffer_binding) => {
689                                BindingResource::Buffer(BufferBinding {
690                                    buffer: buffer_binding.buffer.to_trace(),
691                                    offset: buffer_binding.offset,
692                                    size: buffer_binding.size,
693                                })
694                            }
695                            BindingResource::BufferArray(buffer_bindings) => {
696                                let resolved_buffers: Vec<_> = buffer_bindings
697                                    .iter()
698                                    .map(|bb| BufferBinding {
699                                        buffer: bb.buffer.to_trace(),
700                                        offset: bb.offset,
701                                        size: bb.size,
702                                    })
703                                    .collect();
704                                BindingResource::BufferArray(Cow::Owned(resolved_buffers))
705                            }
706                            BindingResource::Sampler(sampler_id) => {
707                                BindingResource::Sampler(sampler_id.to_trace())
708                            }
709                            BindingResource::SamplerArray(sampler_ids) => {
710                                let resolved: Vec<_> =
711                                    sampler_ids.iter().map(|id| id.to_trace()).collect();
712                                BindingResource::SamplerArray(Cow::Owned(resolved))
713                            }
714                            BindingResource::TextureView(texture_view_id) => {
715                                BindingResource::TextureView(texture_view_id.to_trace())
716                            }
717                            BindingResource::TextureViewArray(texture_view_ids) => {
718                                let resolved: Vec<_> =
719                                    texture_view_ids.iter().map(|id| id.to_trace()).collect();
720                                BindingResource::TextureViewArray(Cow::Owned(resolved))
721                            }
722                            BindingResource::AccelerationStructure(tlas_id) => {
723                                BindingResource::AccelerationStructure(tlas_id.to_trace())
724                            }
725                            BindingResource::AccelerationStructureArray(tlas_ids) => {
726                                let resolved: Vec<_> =
727                                    tlas_ids.iter().map(|id| id.to_trace()).collect();
728                                BindingResource::AccelerationStructureArray(Cow::Owned(resolved))
729                            }
730                            BindingResource::ExternalTexture(external_texture_id) => {
731                                BindingResource::ExternalTexture(external_texture_id.to_trace())
732                            }
733                        };
734                        BindGroupEntry {
735                            binding: entry.binding,
736                            resource,
737                        }
738                    })
739                    .collect(),
740            ),
741        }
742    }
743}
744
745impl<'a> IntoTrace for crate::pipeline::ResolvedGeneralRenderPipelineDescriptor<'a> {
746    type Output = TraceGeneralRenderPipelineDescriptor<'a>;
747
748    fn into_trace(self) -> Self::Output {
749        TraceGeneralRenderPipelineDescriptor {
750            label: self.label,
751            layout: self.layout.into_trace(),
752            vertex: self.vertex.into_trace(),
753            primitive: self.primitive,
754            depth_stencil: self.depth_stencil,
755            multisample: self.multisample,
756            fragment: self.fragment.map(|f| f.into_trace()),
757            multiview_mask: self.multiview_mask,
758            cache: self.cache.map(|c| c.into_trace()),
759        }
760    }
761}
762
763impl<'a> IntoTrace for crate::pipeline::ComputePipelineDescriptor<'a> {
764    type Output = TraceComputePipelineDescriptor<'a>;
765
766    fn into_trace(self) -> Self::Output {
767        TraceComputePipelineDescriptor {
768            label: self.label,
769            layout: self.layout.into_trace(),
770            stage: self.stage.into_trace(),
771            cache: self.cache.map(|c| c.into_trace()),
772        }
773    }
774}
775
776impl<'a> IntoTrace for crate::pipeline::ProgrammableStageDescriptor<'a> {
777    type Output =
778        crate::pipeline::ProgrammableStageDescriptor<'a, PointerId<markers::ShaderModule>>;
779    fn into_trace(self) -> Self::Output {
780        crate::pipeline::ProgrammableStageDescriptor {
781            module: self.module.into_trace(),
782            entry_point: self.entry_point,
783            constants: self.constants,
784            zero_initialize_workgroup_memory: self.zero_initialize_workgroup_memory,
785        }
786    }
787}
788
789impl<'a> IntoTrace
790    for crate::pipeline::RenderPipelineVertexProcessor<'a, Arc<crate::pipeline::ShaderModule>>
791{
792    type Output =
793        crate::pipeline::RenderPipelineVertexProcessor<'a, PointerId<markers::ShaderModule>>;
794    fn into_trace(self) -> Self::Output {
795        match self {
796            crate::pipeline::RenderPipelineVertexProcessor::Vertex(vertex) => {
797                crate::pipeline::RenderPipelineVertexProcessor::Vertex(vertex.into_trace())
798            }
799            crate::pipeline::RenderPipelineVertexProcessor::Mesh(task, mesh) => {
800                crate::pipeline::RenderPipelineVertexProcessor::Mesh(
801                    task.map(|t| t.into_trace()),
802                    mesh.into_trace(),
803                )
804            }
805        }
806    }
807}
808
809impl<'a> IntoTrace for crate::pipeline::TaskState<'a> {
810    type Output = crate::pipeline::TaskState<'a, PointerId<markers::ShaderModule>>;
811    fn into_trace(self) -> Self::Output {
812        crate::pipeline::TaskState {
813            stage: self.stage.into_trace(),
814        }
815    }
816}
817
818impl<'a> IntoTrace for crate::pipeline::MeshState<'a> {
819    type Output = crate::pipeline::MeshState<'a, PointerId<markers::ShaderModule>>;
820    fn into_trace(self) -> Self::Output {
821        crate::pipeline::MeshState {
822            stage: self.stage.into_trace(),
823        }
824    }
825}
826
827impl<'a> IntoTrace for crate::pipeline::VertexState<'a> {
828    type Output = crate::pipeline::VertexState<'a, PointerId<markers::ShaderModule>>;
829    fn into_trace(self) -> Self::Output {
830        crate::pipeline::VertexState {
831            stage: self.stage.into_trace(),
832            buffers: self.buffers,
833        }
834    }
835}
836
837impl<'a> IntoTrace for crate::pipeline::FragmentState<'a> {
838    type Output = crate::pipeline::FragmentState<'a, PointerId<markers::ShaderModule>>;
839    fn into_trace(self) -> Self::Output {
840        crate::pipeline::FragmentState {
841            stage: self.stage.into_trace(),
842            targets: self.targets,
843        }
844    }
845}
846
847impl<T: IntoTrace> IntoTrace for Option<T> {
848    type Output = Option<T::Output>;
849    fn into_trace(self) -> Self::Output {
850        self.map(|v| v.into_trace())
851    }
852}
853
854/// Return a copy of [`Action`] with `'static` lifetime.
855///
856/// This is used for in-memory tracing.
857fn action_to_owned(action: Action<'_, PointerReferences>) -> Action<'static, PointerReferences> {
858    use Action as A;
859    match action {
860        A::Init { desc, backend } => A::Init {
861            desc: desc.map_label(owned_label),
862            backend,
863        },
864        A::ConfigureSurface(surface, config) => A::ConfigureSurface(surface, config),
865        A::CreateBuffer(buffer, desc) => A::CreateBuffer(buffer, desc.map_label(owned_label)),
866        A::DestroyBuffer(buffer) => A::DestroyBuffer(buffer),
867        A::DropBuffer(buffer) => A::DropBuffer(buffer),
868        A::DestroyTexture(texture) => A::DestroyTexture(texture),
869        A::DropTexture(texture) => A::DropTexture(texture),
870        A::DropTextureView(texture_view) => A::DropTextureView(texture_view),
871        A::DestroyExternalTexture(external_texture) => A::DestroyExternalTexture(external_texture),
872        A::DropExternalTexture(external_texture) => A::DropExternalTexture(external_texture),
873        A::DropSampler(sampler) => A::DropSampler(sampler),
874        A::GetSurfaceTexture { id, parent } => A::GetSurfaceTexture { id, parent },
875        A::Present(surface) => A::Present(surface),
876        A::DiscardSurfaceTexture(surface) => A::DiscardSurfaceTexture(surface),
877        A::ReleaseSurfaceTexture(surface) => A::ReleaseSurfaceTexture(surface),
878        A::DropBindGroupLayout(layout) => A::DropBindGroupLayout(layout),
879        A::GetRenderPipelineBindGroupLayout {
880            id,
881            pipeline,
882            index,
883        } => A::GetRenderPipelineBindGroupLayout {
884            id,
885            pipeline,
886            index,
887        },
888        A::GetComputePipelineBindGroupLayout {
889            id,
890            pipeline,
891            index,
892        } => A::GetComputePipelineBindGroupLayout {
893            id,
894            pipeline,
895            index,
896        },
897        A::DropPipelineLayout(layout) => A::DropPipelineLayout(layout),
898        A::DropBindGroup(bind_group) => A::DropBindGroup(bind_group),
899        A::DropShaderModule(shader_module) => A::DropShaderModule(shader_module),
900        A::DropComputePipeline(pipeline) => A::DropComputePipeline(pipeline),
901        A::DropRenderPipeline(pipeline) => A::DropRenderPipeline(pipeline),
902        A::DropPipelineCache(cache) => A::DropPipelineCache(cache),
903        A::DropRenderBundle(render_bundle) => A::DropRenderBundle(render_bundle),
904        A::DestroyQuerySet(query_set) => A::DestroyQuerySet(query_set),
905        A::DropQuerySet(query_set) => A::DropQuerySet(query_set),
906        A::WriteBuffer {
907            id,
908            data,
909            offset,
910            size,
911            queued,
912        } => A::WriteBuffer {
913            id,
914            data,
915            offset,
916            size,
917            queued,
918        },
919        A::WriteTexture {
920            to,
921            data,
922            layout,
923            size,
924        } => A::WriteTexture {
925            to,
926            data,
927            layout,
928            size,
929        },
930        A::Submit(index, commands) => A::Submit(index, commands),
931        A::FailedCommands {
932            commands,
933            failed_at_submit,
934            error,
935        } => A::FailedCommands {
936            commands,
937            failed_at_submit,
938            error,
939        },
940        A::DropBlas(blas) => A::DropBlas(blas),
941        A::DropTlas(tlas) => A::DropTlas(tlas),
942
943        A::CreateTexture(id, desc) => A::CreateTexture(id, desc.map_label(owned_label)),
944        A::CreateTextureError(id, desc) => A::CreateTextureError(id, desc.map_label(owned_label)),
945        A::CreateTextureView { id, parent, desc } => A::CreateTextureView {
946            id,
947            parent,
948            desc: crate::resource::TextureViewDescriptor {
949                label: owned_label(&desc.label),
950                format: desc.format,
951                dimension: desc.dimension,
952                usage: desc.usage,
953                range: desc.range,
954            },
955        },
956        A::CreateExternalTexture { id, desc, planes } => A::CreateExternalTexture {
957            id,
958            desc: desc.map_label(owned_label),
959            planes,
960        },
961        A::CreateSampler(id, desc) => A::CreateSampler(
962            id,
963            crate::resource::SamplerDescriptor {
964                label: owned_label(&desc.label),
965                address_modes: desc.address_modes,
966                mag_filter: desc.mag_filter,
967                min_filter: desc.min_filter,
968                mipmap_filter: desc.mipmap_filter,
969                lod_min_clamp: desc.lod_min_clamp,
970                lod_max_clamp: desc.lod_max_clamp,
971                compare: desc.compare,
972                anisotropy_clamp: desc.anisotropy_clamp,
973                border_color: desc.border_color,
974            },
975        ),
976        A::CreateBindGroupLayout(id, desc) => A::CreateBindGroupLayout(
977            id,
978            crate::binding_model::BindGroupLayoutDescriptor {
979                label: owned_label(&desc.label),
980                entries: Cow::Owned(desc.entries.into_owned()),
981            },
982        ),
983        A::CreatePipelineLayout(id, desc) => A::CreatePipelineLayout(
984            id,
985            crate::binding_model::PipelineLayoutDescriptor {
986                label: owned_label(&desc.label),
987                bind_group_layouts: Cow::Owned(desc.bind_group_layouts.into_owned()),
988                immediate_size: desc.immediate_size,
989            },
990        ),
991        A::CreateBindGroup(id, desc) => A::CreateBindGroup(
992            id,
993            crate::binding_model::BindGroupDescriptor {
994                label: owned_label(&desc.label),
995                layout: desc.layout,
996                entries: desc
997                    .entries
998                    .iter()
999                    .map(|e| crate::binding_model::BindGroupEntry {
1000                        binding: e.binding,
1001                        resource: match &e.resource {
1002                            crate::binding_model::BindingResource::Buffer(buffer_binding) => {
1003                                crate::binding_model::BindingResource::Buffer(
1004                                    buffer_binding.clone(),
1005                                )
1006                            }
1007                            crate::binding_model::BindingResource::BufferArray(cow) => {
1008                                crate::binding_model::BindingResource::BufferArray(Cow::Owned(
1009                                    cow.clone().into_owned(),
1010                                ))
1011                            }
1012                            crate::binding_model::BindingResource::Sampler(sampler) => {
1013                                crate::binding_model::BindingResource::Sampler(*sampler)
1014                            }
1015                            crate::binding_model::BindingResource::SamplerArray(cow) => {
1016                                crate::binding_model::BindingResource::SamplerArray(Cow::Owned(
1017                                    cow.clone().into_owned(),
1018                                ))
1019                            }
1020                            crate::binding_model::BindingResource::TextureView(texture_view) => {
1021                                crate::binding_model::BindingResource::TextureView(*texture_view)
1022                            }
1023                            crate::binding_model::BindingResource::TextureViewArray(cow) => {
1024                                crate::binding_model::BindingResource::TextureViewArray(Cow::Owned(
1025                                    cow.clone().into_owned(),
1026                                ))
1027                            }
1028                            crate::binding_model::BindingResource::AccelerationStructure(
1029                                acceleration_structure,
1030                            ) => crate::binding_model::BindingResource::AccelerationStructure(
1031                                *acceleration_structure,
1032                            ),
1033                            crate::binding_model::BindingResource::AccelerationStructureArray(
1034                                cow,
1035                            ) => crate::binding_model::BindingResource::AccelerationStructureArray(
1036                                Cow::Owned(cow.clone().into_owned()),
1037                            ),
1038                            crate::binding_model::BindingResource::ExternalTexture(
1039                                external_texture,
1040                            ) => crate::binding_model::BindingResource::ExternalTexture(
1041                                *external_texture,
1042                            ),
1043                        },
1044                    })
1045                    .collect(),
1046            },
1047        ),
1048        A::CreateShaderModule { id, desc, data } => A::CreateShaderModule {
1049            id,
1050            desc: crate::pipeline::ShaderModuleDescriptor {
1051                label: owned_label(&desc.label),
1052                runtime_checks: desc.runtime_checks,
1053            },
1054            data,
1055        },
1056        A::CreateShaderModulePassthrough {
1057            id,
1058            data,
1059            label,
1060            entry_points,
1061        } => A::CreateShaderModulePassthrough {
1062            id,
1063            data,
1064            label: owned_label(&label),
1065            entry_points: entry_points
1066                .iter()
1067                .map(|ep| wgt::PassthroughShaderEntryPoint {
1068                    name: Cow::Owned(ep.name.to_string()),
1069                    workgroup_size: ep.workgroup_size,
1070                })
1071                .collect(),
1072        },
1073        A::CreateComputePipeline { id, desc } => A::CreateComputePipeline {
1074            id,
1075            desc: crate::pipeline::ComputePipelineDescriptor {
1076                label: owned_label(&desc.label),
1077                layout: desc.layout,
1078                stage: owned_stage(desc.stage),
1079                cache: desc.cache,
1080            },
1081        },
1082        A::CreateGeneralRenderPipeline { id, desc } => A::CreateGeneralRenderPipeline {
1083            id,
1084            desc: crate::pipeline::GeneralRenderPipelineDescriptor {
1085                label: owned_label(&desc.label),
1086                layout: desc.layout,
1087                vertex: match desc.vertex {
1088                    crate::pipeline::RenderPipelineVertexProcessor::Vertex(
1089                        crate::pipeline::VertexState { stage, buffers },
1090                    ) => crate::pipeline::RenderPipelineVertexProcessor::Vertex(
1091                        crate::pipeline::VertexState {
1092                            stage: owned_stage(stage),
1093                            buffers: buffers
1094                                .iter()
1095                                .map(|b| {
1096                                    b.clone().map(|buffer| crate::pipeline::VertexBufferLayout {
1097                                        array_stride: buffer.array_stride,
1098                                        step_mode: buffer.step_mode,
1099                                        attributes: Cow::Owned(buffer.attributes.into_owned()),
1100                                    })
1101                                })
1102                                .collect(),
1103                        },
1104                    ),
1105                    crate::pipeline::RenderPipelineVertexProcessor::Mesh(task, mesh) => {
1106                        crate::pipeline::RenderPipelineVertexProcessor::Mesh(
1107                            task.map(|t| crate::pipeline::TaskState {
1108                                stage: owned_stage(t.stage),
1109                            }),
1110                            crate::pipeline::MeshState {
1111                                stage: owned_stage(mesh.stage),
1112                            },
1113                        )
1114                    }
1115                },
1116                primitive: desc.primitive,
1117                depth_stencil: desc.depth_stencil,
1118                multisample: desc.multisample,
1119                fragment: desc.fragment.map(|f| crate::pipeline::FragmentState {
1120                    stage: owned_stage(f.stage),
1121                    targets: Cow::Owned(f.targets.into_owned()),
1122                }),
1123                multiview_mask: desc.multiview_mask,
1124                cache: desc.cache,
1125            },
1126        },
1127        A::CreatePipelineCache { id, desc } => A::CreatePipelineCache {
1128            id,
1129            desc: crate::pipeline::PipelineCacheDescriptor {
1130                label: owned_label(&desc.label),
1131                data: desc.data.map(|d| Cow::Owned(d.to_vec())),
1132                fallback: desc.fallback,
1133            },
1134        },
1135        A::CreateRenderBundle { id, desc, base } => A::CreateRenderBundle {
1136            id,
1137            desc: crate::command::RenderBundleEncoderDescriptor {
1138                label: owned_label(&desc.label),
1139                color_formats: Cow::Owned(desc.color_formats.into_owned()),
1140                depth_stencil: desc.depth_stencil,
1141                sample_count: desc.sample_count,
1142                multiview: desc.multiview,
1143            },
1144            base,
1145        },
1146        A::CreateQuerySet { id, desc } => A::CreateQuerySet {
1147            id,
1148            desc: desc.map_label(owned_label),
1149        },
1150        A::CreateBlas { id, desc, sizes } => A::CreateBlas {
1151            id,
1152            desc: desc.map_label(owned_label),
1153            sizes,
1154        },
1155        A::CreateTlas { id, desc } => A::CreateTlas {
1156            id,
1157            desc: desc.map_label(owned_label),
1158        },
1159    }
1160}
1161
1162fn owned_stage<SM>(
1163    stage: crate::pipeline::ProgrammableStageDescriptor<'_, SM>,
1164) -> crate::pipeline::ProgrammableStageDescriptor<'static, SM> {
1165    crate::pipeline::ProgrammableStageDescriptor {
1166        module: stage.module,
1167        entry_point: owned_label(&stage.entry_point),
1168        constants: stage.constants,
1169        zero_initialize_workgroup_memory: stage.zero_initialize_workgroup_memory,
1170    }
1171}
1172
1173fn owned_label(l: &Option<Cow<'_, str>>) -> Option<Cow<'static, str>> {
1174    l.as_ref().map(|l| Cow::Owned(l.to_string()))
1175}