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, ArcPassTimestampWrites, ArcReferences, ArcRenderCommand,
8        BasePass, ColorAttachments, Command, ComputeCommand, 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 ArcPassTimestampWrites {
317    type Output = crate::command::PassTimestampWrites<PointerId<markers::QuerySet>>;
318    fn into_trace(self) -> Self::Output {
319        crate::command::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            immediates_data: self.immediates_data,
453        }
454    }
455}
456
457impl IntoTrace for ArcComputeCommand {
458    type Output = ComputeCommand<PointerReferences>;
459    fn into_trace(self) -> Self::Output {
460        use ComputeCommand as C;
461        match self {
462            C::SetBindGroup {
463                index,
464                num_dynamic_offsets,
465                bind_group,
466            } => C::SetBindGroup {
467                index,
468                num_dynamic_offsets,
469                bind_group: bind_group.map(|bg| bg.into_trace()),
470            },
471            C::SetPipeline(id) => C::SetPipeline(id.into_trace()),
472            C::SetImmediate {
473                offset,
474                size_bytes,
475                values_offset,
476            } => C::SetImmediate {
477                offset,
478                size_bytes,
479                values_offset,
480            },
481            C::DispatchWorkgroups(groups) => C::DispatchWorkgroups(groups),
482            C::DispatchWorkgroupsIndirect { buffer, offset } => C::DispatchWorkgroupsIndirect {
483                buffer: buffer.into_trace(),
484                offset,
485            },
486            C::PushDebugGroup { color, len } => C::PushDebugGroup { color, len },
487            C::PopDebugGroup => C::PopDebugGroup,
488            C::InsertDebugMarker { color, len } => C::InsertDebugMarker { color, len },
489            C::WriteTimestamp {
490                query_set,
491                query_index,
492            } => C::WriteTimestamp {
493                query_set: query_set.into_trace(),
494                query_index,
495            },
496            C::BeginPipelineStatisticsQuery {
497                query_set,
498                query_index,
499            } => C::BeginPipelineStatisticsQuery {
500                query_set: query_set.into_trace(),
501                query_index,
502            },
503            C::EndPipelineStatisticsQuery => C::EndPipelineStatisticsQuery,
504            C::TransitionResources {
505                buffer_transitions,
506                texture_transitions,
507            } => C::TransitionResources {
508                buffer_transitions: buffer_transitions
509                    .into_iter()
510                    .map(|buffer_transition| wgt::BufferTransition {
511                        buffer: buffer_transition.buffer.into_trace(),
512                        state: buffer_transition.state,
513                    })
514                    .collect(),
515                texture_transitions: texture_transitions
516                    .into_iter()
517                    .map(|texture_transition| wgt::TextureTransition {
518                        texture: texture_transition.texture.into_trace(),
519                        selector: texture_transition.selector,
520                        state: texture_transition.state,
521                    })
522                    .collect(),
523            },
524        }
525    }
526}
527
528impl IntoTrace for ArcRenderCommand {
529    type Output = RenderCommand<PointerReferences>;
530    fn into_trace(self) -> Self::Output {
531        use RenderCommand as C;
532        match self {
533            C::SetBindGroup {
534                index,
535                num_dynamic_offsets,
536                bind_group,
537            } => C::SetBindGroup {
538                index,
539                num_dynamic_offsets,
540                bind_group: bind_group.map(|bg| bg.into_trace()),
541            },
542            C::SetPipeline(id) => C::SetPipeline(id.into_trace()),
543            C::SetIndexBuffer {
544                buffer,
545                index_format,
546                offset,
547                size,
548            } => C::SetIndexBuffer {
549                buffer: buffer.into_trace(),
550                index_format,
551                offset,
552                size,
553            },
554            C::SetVertexBuffer {
555                slot,
556                buffer,
557                offset,
558                size,
559            } => C::SetVertexBuffer {
560                slot,
561                buffer: buffer.into_trace(),
562                offset,
563                size,
564            },
565            C::SetBlendConstant(color) => C::SetBlendConstant(color),
566            C::SetStencilReference(val) => C::SetStencilReference(val),
567            C::SetViewport {
568                rect,
569                depth_min,
570                depth_max,
571            } => C::SetViewport {
572                rect,
573                depth_min,
574                depth_max,
575            },
576            C::SetScissor(rect) => C::SetScissor(rect),
577            C::SetImmediate {
578                offset,
579                size_bytes,
580                values_offset,
581            } => C::SetImmediate {
582                offset,
583                size_bytes,
584                values_offset,
585            },
586            C::Draw {
587                vertex_count,
588                instance_count,
589                first_vertex,
590                first_instance,
591            } => C::Draw {
592                vertex_count,
593                instance_count,
594                first_vertex,
595                first_instance,
596            },
597            C::DrawIndexed {
598                index_count,
599                instance_count,
600                first_index,
601                base_vertex,
602                first_instance,
603            } => C::DrawIndexed {
604                index_count,
605                instance_count,
606                first_index,
607                base_vertex,
608                first_instance,
609            },
610            C::DrawMeshTasks {
611                group_count_x,
612                group_count_y,
613                group_count_z,
614            } => C::DrawMeshTasks {
615                group_count_x,
616                group_count_y,
617                group_count_z,
618            },
619            C::DrawIndirect {
620                buffer,
621                offset,
622                count,
623                family,
624                vertex_or_index_limit,
625                instance_limit,
626            } => C::DrawIndirect {
627                buffer: buffer.into_trace(),
628                offset,
629                count,
630                family,
631                vertex_or_index_limit,
632                instance_limit,
633            },
634            C::MultiDrawIndirectCount {
635                buffer,
636                offset,
637                count_buffer,
638                count_buffer_offset,
639                max_count,
640                family,
641            } => C::MultiDrawIndirectCount {
642                buffer: buffer.into_trace(),
643                offset,
644                count_buffer: count_buffer.into_trace(),
645                count_buffer_offset,
646                max_count,
647                family,
648            },
649            C::PushDebugGroup { color, len } => C::PushDebugGroup { color, len },
650            C::PopDebugGroup => C::PopDebugGroup,
651            C::InsertDebugMarker { color, len } => C::InsertDebugMarker { color, len },
652            C::WriteTimestamp {
653                query_set,
654                query_index,
655            } => C::WriteTimestamp {
656                query_set: query_set.into_trace(),
657                query_index,
658            },
659            C::BeginOcclusionQuery { query_index } => C::BeginOcclusionQuery { query_index },
660            C::EndOcclusionQuery => C::EndOcclusionQuery,
661            C::BeginPipelineStatisticsQuery {
662                query_set,
663                query_index,
664            } => C::BeginPipelineStatisticsQuery {
665                query_set: query_set.into_trace(),
666                query_index,
667            },
668            C::EndPipelineStatisticsQuery => C::EndPipelineStatisticsQuery,
669            C::ExecuteBundle(bundle) => C::ExecuteBundle(bundle.into_trace()),
670        }
671    }
672}
673
674impl IntoTrace for crate::binding_model::ResolvedPipelineLayoutDescriptor<'_> {
675    type Output = crate::binding_model::PipelineLayoutDescriptor<
676        'static,
677        PointerId<markers::BindGroupLayout>,
678    >;
679    fn into_trace(self) -> Self::Output {
680        crate::binding_model::PipelineLayoutDescriptor {
681            label: self.label.map(|l| Cow::Owned(l.into_owned())),
682            bind_group_layouts: self
683                .bind_group_layouts
684                .iter()
685                .map(|bgl| bgl.to_trace())
686                .collect(),
687            immediate_size: self.immediate_size,
688        }
689    }
690}
691
692impl<'a> IntoTrace for &'_ crate::binding_model::ResolvedBindGroupDescriptor<'a> {
693    type Output = TraceBindGroupDescriptor<'a>;
694
695    fn into_trace(self) -> Self::Output {
696        use crate::binding_model::{
697            BindGroupEntry, BindingResource, BufferBinding, ResolvedBindingResource,
698        };
699        TraceBindGroupDescriptor {
700            label: self.label.clone(),
701            layout: self.layout.to_trace(),
702            entries: Cow::Owned(
703                self.entries
704                    .iter()
705                    .map(|entry| {
706                        let resource = match &entry.resource {
707                            ResolvedBindingResource::Buffer(buffer_binding) => {
708                                BindingResource::Buffer(BufferBinding {
709                                    buffer: buffer_binding.buffer.to_trace(),
710                                    offset: buffer_binding.offset,
711                                    size: buffer_binding.size,
712                                })
713                            }
714                            ResolvedBindingResource::BufferArray(buffer_bindings) => {
715                                let resolved_buffers: Vec<_> = buffer_bindings
716                                    .iter()
717                                    .map(|bb| BufferBinding {
718                                        buffer: bb.buffer.to_trace(),
719                                        offset: bb.offset,
720                                        size: bb.size,
721                                    })
722                                    .collect();
723                                BindingResource::BufferArray(Cow::Owned(resolved_buffers))
724                            }
725                            ResolvedBindingResource::Sampler(sampler_id) => {
726                                BindingResource::Sampler(sampler_id.to_trace())
727                            }
728                            ResolvedBindingResource::SamplerArray(sampler_ids) => {
729                                let resolved: Vec<_> =
730                                    sampler_ids.iter().map(|id| id.to_trace()).collect();
731                                BindingResource::SamplerArray(Cow::Owned(resolved))
732                            }
733                            ResolvedBindingResource::TextureView(texture_view_id) => {
734                                BindingResource::TextureView(texture_view_id.to_trace())
735                            }
736                            ResolvedBindingResource::TextureViewArray(texture_view_ids) => {
737                                let resolved: Vec<_> =
738                                    texture_view_ids.iter().map(|id| id.to_trace()).collect();
739                                BindingResource::TextureViewArray(Cow::Owned(resolved))
740                            }
741                            ResolvedBindingResource::AccelerationStructure(tlas_id) => {
742                                BindingResource::AccelerationStructure(tlas_id.to_trace())
743                            }
744                            ResolvedBindingResource::AccelerationStructureArray(tlas_ids) => {
745                                let resolved: Vec<_> =
746                                    tlas_ids.iter().map(|id| id.to_trace()).collect();
747                                BindingResource::AccelerationStructureArray(Cow::Owned(resolved))
748                            }
749                            ResolvedBindingResource::ExternalTexture(external_texture_id) => {
750                                BindingResource::ExternalTexture(external_texture_id.to_trace())
751                            }
752                        };
753                        BindGroupEntry {
754                            binding: entry.binding,
755                            resource,
756                        }
757                    })
758                    .collect(),
759            ),
760        }
761    }
762}
763
764impl<'a> IntoTrace for crate::pipeline::ResolvedGeneralRenderPipelineDescriptor<'a> {
765    type Output = TraceGeneralRenderPipelineDescriptor<'a>;
766
767    fn into_trace(self) -> Self::Output {
768        TraceGeneralRenderPipelineDescriptor {
769            label: self.label,
770            layout: self.layout.into_trace(),
771            vertex: self.vertex.into_trace(),
772            primitive: self.primitive,
773            depth_stencil: self.depth_stencil,
774            multisample: self.multisample,
775            fragment: self.fragment.map(|f| f.into_trace()),
776            multiview_mask: self.multiview_mask,
777            cache: self.cache.map(|c| c.into_trace()),
778        }
779    }
780}
781
782impl<'a> IntoTrace for crate::pipeline::ResolvedComputePipelineDescriptor<'a> {
783    type Output = TraceComputePipelineDescriptor<'a>;
784
785    fn into_trace(self) -> Self::Output {
786        TraceComputePipelineDescriptor {
787            label: self.label,
788            layout: self.layout.into_trace(),
789            stage: self.stage.into_trace(),
790            cache: self.cache.map(|c| c.into_trace()),
791        }
792    }
793}
794
795impl<'a> IntoTrace for crate::pipeline::ResolvedProgrammableStageDescriptor<'a> {
796    type Output =
797        crate::pipeline::ProgrammableStageDescriptor<'a, PointerId<markers::ShaderModule>>;
798    fn into_trace(self) -> Self::Output {
799        crate::pipeline::ProgrammableStageDescriptor {
800            module: self.module.into_trace(),
801            entry_point: self.entry_point,
802            constants: self.constants,
803            zero_initialize_workgroup_memory: self.zero_initialize_workgroup_memory,
804        }
805    }
806}
807
808impl<'a> IntoTrace
809    for crate::pipeline::RenderPipelineVertexProcessor<'a, Arc<crate::pipeline::ShaderModule>>
810{
811    type Output =
812        crate::pipeline::RenderPipelineVertexProcessor<'a, PointerId<markers::ShaderModule>>;
813    fn into_trace(self) -> Self::Output {
814        match self {
815            crate::pipeline::RenderPipelineVertexProcessor::Vertex(vertex) => {
816                crate::pipeline::RenderPipelineVertexProcessor::Vertex(vertex.into_trace())
817            }
818            crate::pipeline::RenderPipelineVertexProcessor::Mesh(task, mesh) => {
819                crate::pipeline::RenderPipelineVertexProcessor::Mesh(
820                    task.map(|t| t.into_trace()),
821                    mesh.into_trace(),
822                )
823            }
824        }
825    }
826}
827
828impl<'a> IntoTrace for crate::pipeline::ResolvedTaskState<'a> {
829    type Output = crate::pipeline::TaskState<'a, PointerId<markers::ShaderModule>>;
830    fn into_trace(self) -> Self::Output {
831        crate::pipeline::TaskState {
832            stage: self.stage.into_trace(),
833        }
834    }
835}
836
837impl<'a> IntoTrace for crate::pipeline::ResolvedMeshState<'a> {
838    type Output = crate::pipeline::MeshState<'a, PointerId<markers::ShaderModule>>;
839    fn into_trace(self) -> Self::Output {
840        crate::pipeline::MeshState {
841            stage: self.stage.into_trace(),
842        }
843    }
844}
845
846impl<'a> IntoTrace for crate::pipeline::ResolvedVertexState<'a> {
847    type Output = crate::pipeline::VertexState<'a, PointerId<markers::ShaderModule>>;
848    fn into_trace(self) -> Self::Output {
849        crate::pipeline::VertexState {
850            stage: self.stage.into_trace(),
851            buffers: self.buffers,
852        }
853    }
854}
855
856impl<'a> IntoTrace for crate::pipeline::ResolvedFragmentState<'a> {
857    type Output = crate::pipeline::FragmentState<'a, PointerId<markers::ShaderModule>>;
858    fn into_trace(self) -> Self::Output {
859        crate::pipeline::FragmentState {
860            stage: self.stage.into_trace(),
861            targets: self.targets,
862        }
863    }
864}
865
866impl<T: IntoTrace> IntoTrace for Option<T> {
867    type Output = Option<T::Output>;
868    fn into_trace(self) -> Self::Output {
869        self.map(|v| v.into_trace())
870    }
871}
872
873/// Return a copy of [`Action`] with `'static` lifetime.
874///
875/// This is used for in-memory tracing.
876fn action_to_owned(action: Action<'_, PointerReferences>) -> Action<'static, PointerReferences> {
877    use Action as A;
878    match action {
879        A::Init { desc, backend } => A::Init {
880            desc: desc.map_label(owned_label),
881            backend,
882        },
883        A::ConfigureSurface(surface, config) => A::ConfigureSurface(surface, config),
884        A::CreateBuffer(buffer, desc) => A::CreateBuffer(buffer, desc.map_label(owned_label)),
885        A::DestroyBuffer(buffer) => A::DestroyBuffer(buffer),
886        A::DropBuffer(buffer) => A::DropBuffer(buffer),
887        A::DestroyTexture(texture) => A::DestroyTexture(texture),
888        A::DropTexture(texture) => A::DropTexture(texture),
889        A::DropTextureView(texture_view) => A::DropTextureView(texture_view),
890        A::DestroyExternalTexture(external_texture) => A::DestroyExternalTexture(external_texture),
891        A::DropExternalTexture(external_texture) => A::DropExternalTexture(external_texture),
892        A::DropSampler(sampler) => A::DropSampler(sampler),
893        A::GetSurfaceTexture { id, parent } => A::GetSurfaceTexture { id, parent },
894        A::Present(surface) => A::Present(surface),
895        A::DiscardSurfaceTexture(surface) => A::DiscardSurfaceTexture(surface),
896        A::ReleaseSurfaceTexture(surface) => A::ReleaseSurfaceTexture(surface),
897        A::DropBindGroupLayout(layout) => A::DropBindGroupLayout(layout),
898        A::GetRenderPipelineBindGroupLayout {
899            id,
900            pipeline,
901            index,
902        } => A::GetRenderPipelineBindGroupLayout {
903            id,
904            pipeline,
905            index,
906        },
907        A::GetComputePipelineBindGroupLayout {
908            id,
909            pipeline,
910            index,
911        } => A::GetComputePipelineBindGroupLayout {
912            id,
913            pipeline,
914            index,
915        },
916        A::DropPipelineLayout(layout) => A::DropPipelineLayout(layout),
917        A::DropBindGroup(bind_group) => A::DropBindGroup(bind_group),
918        A::DropShaderModule(shader_module) => A::DropShaderModule(shader_module),
919        A::DropComputePipeline(pipeline) => A::DropComputePipeline(pipeline),
920        A::DropRenderPipeline(pipeline) => A::DropRenderPipeline(pipeline),
921        A::DropPipelineCache(cache) => A::DropPipelineCache(cache),
922        A::DropRenderBundle(render_bundle) => A::DropRenderBundle(render_bundle),
923        A::DestroyQuerySet(query_set) => A::DestroyQuerySet(query_set),
924        A::DropQuerySet(query_set) => A::DropQuerySet(query_set),
925        A::WriteBuffer {
926            id,
927            data,
928            offset,
929            size,
930            queued,
931        } => A::WriteBuffer {
932            id,
933            data,
934            offset,
935            size,
936            queued,
937        },
938        A::WriteTexture {
939            to,
940            data,
941            layout,
942            size,
943        } => A::WriteTexture {
944            to,
945            data,
946            layout,
947            size,
948        },
949        A::Submit(index, commands) => A::Submit(index, commands),
950        A::FailedCommands {
951            commands,
952            failed_at_submit,
953            error,
954        } => A::FailedCommands {
955            commands,
956            failed_at_submit,
957            error,
958        },
959        A::DropBlas(blas) => A::DropBlas(blas),
960        A::DropTlas(tlas) => A::DropTlas(tlas),
961
962        A::CreateTexture(id, desc) => A::CreateTexture(id, desc.map_label(owned_label)),
963        A::CreateTextureError(id, desc) => A::CreateTextureError(id, desc.map_label(owned_label)),
964        A::CreateTextureView { id, parent, desc } => A::CreateTextureView {
965            id,
966            parent,
967            desc: crate::resource::TextureViewDescriptor {
968                label: owned_label(&desc.label),
969                format: desc.format,
970                dimension: desc.dimension,
971                usage: desc.usage,
972                range: desc.range,
973            },
974        },
975        A::CreateExternalTexture { id, desc, planes } => A::CreateExternalTexture {
976            id,
977            desc: desc.map_label(owned_label),
978            planes,
979        },
980        A::CreateSampler(id, desc) => A::CreateSampler(
981            id,
982            crate::resource::SamplerDescriptor {
983                label: owned_label(&desc.label),
984                address_modes: desc.address_modes,
985                mag_filter: desc.mag_filter,
986                min_filter: desc.min_filter,
987                mipmap_filter: desc.mipmap_filter,
988                lod_min_clamp: desc.lod_min_clamp,
989                lod_max_clamp: desc.lod_max_clamp,
990                compare: desc.compare,
991                anisotropy_clamp: desc.anisotropy_clamp,
992                border_color: desc.border_color,
993            },
994        ),
995        A::CreateBindGroupLayout(id, desc) => A::CreateBindGroupLayout(
996            id,
997            crate::binding_model::BindGroupLayoutDescriptor {
998                label: owned_label(&desc.label),
999                entries: Cow::Owned(desc.entries.into_owned()),
1000            },
1001        ),
1002        A::CreatePipelineLayout(id, desc) => A::CreatePipelineLayout(
1003            id,
1004            crate::binding_model::PipelineLayoutDescriptor {
1005                label: owned_label(&desc.label),
1006                bind_group_layouts: Cow::Owned(desc.bind_group_layouts.into_owned()),
1007                immediate_size: desc.immediate_size,
1008            },
1009        ),
1010        A::CreateBindGroup(id, desc) => A::CreateBindGroup(
1011            id,
1012            crate::binding_model::BindGroupDescriptor {
1013                label: owned_label(&desc.label),
1014                layout: desc.layout,
1015                entries: desc
1016                    .entries
1017                    .iter()
1018                    .map(|e| crate::binding_model::BindGroupEntry {
1019                        binding: e.binding,
1020                        resource: match &e.resource {
1021                            crate::binding_model::BindingResource::Buffer(buffer_binding) => {
1022                                crate::binding_model::BindingResource::Buffer(
1023                                    buffer_binding.clone(),
1024                                )
1025                            }
1026                            crate::binding_model::BindingResource::BufferArray(cow) => {
1027                                crate::binding_model::BindingResource::BufferArray(Cow::Owned(
1028                                    cow.clone().into_owned(),
1029                                ))
1030                            }
1031                            crate::binding_model::BindingResource::Sampler(sampler) => {
1032                                crate::binding_model::BindingResource::Sampler(*sampler)
1033                            }
1034                            crate::binding_model::BindingResource::SamplerArray(cow) => {
1035                                crate::binding_model::BindingResource::SamplerArray(Cow::Owned(
1036                                    cow.clone().into_owned(),
1037                                ))
1038                            }
1039                            crate::binding_model::BindingResource::TextureView(texture_view) => {
1040                                crate::binding_model::BindingResource::TextureView(*texture_view)
1041                            }
1042                            crate::binding_model::BindingResource::TextureViewArray(cow) => {
1043                                crate::binding_model::BindingResource::TextureViewArray(Cow::Owned(
1044                                    cow.clone().into_owned(),
1045                                ))
1046                            }
1047                            crate::binding_model::BindingResource::AccelerationStructure(
1048                                acceleration_structure,
1049                            ) => crate::binding_model::BindingResource::AccelerationStructure(
1050                                *acceleration_structure,
1051                            ),
1052                            crate::binding_model::BindingResource::AccelerationStructureArray(
1053                                cow,
1054                            ) => crate::binding_model::BindingResource::AccelerationStructureArray(
1055                                Cow::Owned(cow.clone().into_owned()),
1056                            ),
1057                            crate::binding_model::BindingResource::ExternalTexture(
1058                                external_texture,
1059                            ) => crate::binding_model::BindingResource::ExternalTexture(
1060                                *external_texture,
1061                            ),
1062                        },
1063                    })
1064                    .collect(),
1065            },
1066        ),
1067        A::CreateShaderModule { id, desc, data } => A::CreateShaderModule {
1068            id,
1069            desc: crate::pipeline::ShaderModuleDescriptor {
1070                label: owned_label(&desc.label),
1071                runtime_checks: desc.runtime_checks,
1072            },
1073            data,
1074        },
1075        A::CreateShaderModulePassthrough {
1076            id,
1077            data,
1078            label,
1079            entry_points,
1080        } => A::CreateShaderModulePassthrough {
1081            id,
1082            data,
1083            label: owned_label(&label),
1084            entry_points: entry_points
1085                .iter()
1086                .map(|ep| wgt::PassthroughShaderEntryPoint {
1087                    name: Cow::Owned(ep.name.to_string()),
1088                    workgroup_size: ep.workgroup_size,
1089                })
1090                .collect(),
1091        },
1092        A::CreateComputePipeline { id, desc } => A::CreateComputePipeline {
1093            id,
1094            desc: crate::pipeline::ComputePipelineDescriptor {
1095                label: owned_label(&desc.label),
1096                layout: desc.layout,
1097                stage: owned_stage(desc.stage),
1098                cache: desc.cache,
1099            },
1100        },
1101        A::CreateGeneralRenderPipeline { id, desc } => A::CreateGeneralRenderPipeline {
1102            id,
1103            desc: crate::pipeline::GeneralRenderPipelineDescriptor {
1104                label: owned_label(&desc.label),
1105                layout: desc.layout,
1106                vertex: match desc.vertex {
1107                    crate::pipeline::RenderPipelineVertexProcessor::Vertex(
1108                        crate::pipeline::VertexState { stage, buffers },
1109                    ) => crate::pipeline::RenderPipelineVertexProcessor::Vertex(
1110                        crate::pipeline::VertexState {
1111                            stage: owned_stage(stage),
1112                            buffers: buffers
1113                                .iter()
1114                                .map(|b| {
1115                                    b.clone().map(|buffer| crate::pipeline::VertexBufferLayout {
1116                                        array_stride: buffer.array_stride,
1117                                        step_mode: buffer.step_mode,
1118                                        attributes: Cow::Owned(buffer.attributes.into_owned()),
1119                                    })
1120                                })
1121                                .collect(),
1122                        },
1123                    ),
1124                    crate::pipeline::RenderPipelineVertexProcessor::Mesh(task, mesh) => {
1125                        crate::pipeline::RenderPipelineVertexProcessor::Mesh(
1126                            task.map(|t| crate::pipeline::TaskState {
1127                                stage: owned_stage(t.stage),
1128                            }),
1129                            crate::pipeline::MeshState {
1130                                stage: owned_stage(mesh.stage),
1131                            },
1132                        )
1133                    }
1134                },
1135                primitive: desc.primitive,
1136                depth_stencil: desc.depth_stencil,
1137                multisample: desc.multisample,
1138                fragment: desc.fragment.map(|f| crate::pipeline::FragmentState {
1139                    stage: owned_stage(f.stage),
1140                    targets: Cow::Owned(f.targets.into_owned()),
1141                }),
1142                multiview_mask: desc.multiview_mask,
1143                cache: desc.cache,
1144            },
1145        },
1146        A::CreatePipelineCache { id, desc } => A::CreatePipelineCache {
1147            id,
1148            desc: crate::pipeline::PipelineCacheDescriptor {
1149                label: owned_label(&desc.label),
1150                data: desc.data.map(|d| Cow::Owned(d.to_vec())),
1151                fallback: desc.fallback,
1152            },
1153        },
1154        A::CreateRenderBundle { id, desc, base } => A::CreateRenderBundle {
1155            id,
1156            desc: crate::command::RenderBundleEncoderDescriptor {
1157                label: owned_label(&desc.label),
1158                color_formats: Cow::Owned(desc.color_formats.into_owned()),
1159                depth_stencil: desc.depth_stencil,
1160                sample_count: desc.sample_count,
1161                multiview: desc.multiview,
1162            },
1163            base,
1164        },
1165        A::CreateQuerySet { id, desc } => A::CreateQuerySet {
1166            id,
1167            desc: desc.map_label(owned_label),
1168        },
1169        A::CreateBlas { id, desc, sizes } => A::CreateBlas {
1170            id,
1171            desc: desc.map_label(owned_label),
1172            sizes,
1173        },
1174        A::CreateTlas { id, desc } => A::CreateTlas {
1175            id,
1176            desc: desc.map_label(owned_label),
1177        },
1178    }
1179}
1180
1181fn owned_stage<SM>(
1182    stage: crate::pipeline::ProgrammableStageDescriptor<'_, SM>,
1183) -> crate::pipeline::ProgrammableStageDescriptor<'static, SM> {
1184    crate::pipeline::ProgrammableStageDescriptor {
1185        module: stage.module,
1186        entry_point: owned_label(&stage.entry_point),
1187        constants: stage.constants,
1188        zero_initialize_workgroup_memory: stage.zero_initialize_workgroup_memory,
1189    }
1190}
1191
1192fn owned_label(l: &Option<Cow<'_, str>>) -> Option<Cow<'static, str>> {
1193    l.as_ref().map(|l| Cow::Owned(l.to_string()))
1194}