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        }
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::ResolvedPipelineLayoutDescriptor<'_> {
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::ResolvedBindGroupDescriptor<'a> {
676    type Output = TraceBindGroupDescriptor<'a>;
677
678    fn into_trace(self) -> Self::Output {
679        use crate::binding_model::{
680            BindGroupEntry, BindingResource, BufferBinding, ResolvedBindingResource,
681        };
682        TraceBindGroupDescriptor {
683            label: self.label.clone(),
684            layout: self.layout.to_trace(),
685            entries: Cow::Owned(
686                self.entries
687                    .iter()
688                    .map(|entry| {
689                        let resource = match &entry.resource {
690                            ResolvedBindingResource::Buffer(buffer_binding) => {
691                                BindingResource::Buffer(BufferBinding {
692                                    buffer: buffer_binding.buffer.to_trace(),
693                                    offset: buffer_binding.offset,
694                                    size: buffer_binding.size,
695                                })
696                            }
697                            ResolvedBindingResource::BufferArray(buffer_bindings) => {
698                                let resolved_buffers: Vec<_> = buffer_bindings
699                                    .iter()
700                                    .map(|bb| BufferBinding {
701                                        buffer: bb.buffer.to_trace(),
702                                        offset: bb.offset,
703                                        size: bb.size,
704                                    })
705                                    .collect();
706                                BindingResource::BufferArray(Cow::Owned(resolved_buffers))
707                            }
708                            ResolvedBindingResource::Sampler(sampler_id) => {
709                                BindingResource::Sampler(sampler_id.to_trace())
710                            }
711                            ResolvedBindingResource::SamplerArray(sampler_ids) => {
712                                let resolved: Vec<_> =
713                                    sampler_ids.iter().map(|id| id.to_trace()).collect();
714                                BindingResource::SamplerArray(Cow::Owned(resolved))
715                            }
716                            ResolvedBindingResource::TextureView(texture_view_id) => {
717                                BindingResource::TextureView(texture_view_id.to_trace())
718                            }
719                            ResolvedBindingResource::TextureViewArray(texture_view_ids) => {
720                                let resolved: Vec<_> =
721                                    texture_view_ids.iter().map(|id| id.to_trace()).collect();
722                                BindingResource::TextureViewArray(Cow::Owned(resolved))
723                            }
724                            ResolvedBindingResource::AccelerationStructure(tlas_id) => {
725                                BindingResource::AccelerationStructure(tlas_id.to_trace())
726                            }
727                            ResolvedBindingResource::AccelerationStructureArray(tlas_ids) => {
728                                let resolved: Vec<_> =
729                                    tlas_ids.iter().map(|id| id.to_trace()).collect();
730                                BindingResource::AccelerationStructureArray(Cow::Owned(resolved))
731                            }
732                            ResolvedBindingResource::ExternalTexture(external_texture_id) => {
733                                BindingResource::ExternalTexture(external_texture_id.to_trace())
734                            }
735                        };
736                        BindGroupEntry {
737                            binding: entry.binding,
738                            resource,
739                        }
740                    })
741                    .collect(),
742            ),
743        }
744    }
745}
746
747impl<'a> IntoTrace for crate::pipeline::ResolvedGeneralRenderPipelineDescriptor<'a> {
748    type Output = TraceGeneralRenderPipelineDescriptor<'a>;
749
750    fn into_trace(self) -> Self::Output {
751        TraceGeneralRenderPipelineDescriptor {
752            label: self.label,
753            layout: self.layout.into_trace(),
754            vertex: self.vertex.into_trace(),
755            primitive: self.primitive,
756            depth_stencil: self.depth_stencil,
757            multisample: self.multisample,
758            fragment: self.fragment.map(|f| f.into_trace()),
759            multiview_mask: self.multiview_mask,
760            cache: self.cache.map(|c| c.into_trace()),
761        }
762    }
763}
764
765impl<'a> IntoTrace for crate::pipeline::ResolvedComputePipelineDescriptor<'a> {
766    type Output = TraceComputePipelineDescriptor<'a>;
767
768    fn into_trace(self) -> Self::Output {
769        TraceComputePipelineDescriptor {
770            label: self.label,
771            layout: self.layout.into_trace(),
772            stage: self.stage.into_trace(),
773            cache: self.cache.map(|c| c.into_trace()),
774        }
775    }
776}
777
778impl<'a> IntoTrace for crate::pipeline::ResolvedProgrammableStageDescriptor<'a> {
779    type Output =
780        crate::pipeline::ProgrammableStageDescriptor<'a, PointerId<markers::ShaderModule>>;
781    fn into_trace(self) -> Self::Output {
782        crate::pipeline::ProgrammableStageDescriptor {
783            module: self.module.into_trace(),
784            entry_point: self.entry_point,
785            constants: self.constants,
786            zero_initialize_workgroup_memory: self.zero_initialize_workgroup_memory,
787        }
788    }
789}
790
791impl<'a> IntoTrace
792    for crate::pipeline::RenderPipelineVertexProcessor<'a, Arc<crate::pipeline::ShaderModule>>
793{
794    type Output =
795        crate::pipeline::RenderPipelineVertexProcessor<'a, PointerId<markers::ShaderModule>>;
796    fn into_trace(self) -> Self::Output {
797        match self {
798            crate::pipeline::RenderPipelineVertexProcessor::Vertex(vertex) => {
799                crate::pipeline::RenderPipelineVertexProcessor::Vertex(vertex.into_trace())
800            }
801            crate::pipeline::RenderPipelineVertexProcessor::Mesh(task, mesh) => {
802                crate::pipeline::RenderPipelineVertexProcessor::Mesh(
803                    task.map(|t| t.into_trace()),
804                    mesh.into_trace(),
805                )
806            }
807        }
808    }
809}
810
811impl<'a> IntoTrace for crate::pipeline::ResolvedTaskState<'a> {
812    type Output = crate::pipeline::TaskState<'a, PointerId<markers::ShaderModule>>;
813    fn into_trace(self) -> Self::Output {
814        crate::pipeline::TaskState {
815            stage: self.stage.into_trace(),
816        }
817    }
818}
819
820impl<'a> IntoTrace for crate::pipeline::ResolvedMeshState<'a> {
821    type Output = crate::pipeline::MeshState<'a, PointerId<markers::ShaderModule>>;
822    fn into_trace(self) -> Self::Output {
823        crate::pipeline::MeshState {
824            stage: self.stage.into_trace(),
825        }
826    }
827}
828
829impl<'a> IntoTrace for crate::pipeline::ResolvedVertexState<'a> {
830    type Output = crate::pipeline::VertexState<'a, PointerId<markers::ShaderModule>>;
831    fn into_trace(self) -> Self::Output {
832        crate::pipeline::VertexState {
833            stage: self.stage.into_trace(),
834            buffers: self.buffers,
835        }
836    }
837}
838
839impl<'a> IntoTrace for crate::pipeline::ResolvedFragmentState<'a> {
840    type Output = crate::pipeline::FragmentState<'a, PointerId<markers::ShaderModule>>;
841    fn into_trace(self) -> Self::Output {
842        crate::pipeline::FragmentState {
843            stage: self.stage.into_trace(),
844            targets: self.targets,
845        }
846    }
847}
848
849impl<T: IntoTrace> IntoTrace for Option<T> {
850    type Output = Option<T::Output>;
851    fn into_trace(self) -> Self::Output {
852        self.map(|v| v.into_trace())
853    }
854}
855
856/// Return a copy of [`Action`] with `'static` lifetime.
857///
858/// This is used for in-memory tracing.
859fn action_to_owned(action: Action<'_, PointerReferences>) -> Action<'static, PointerReferences> {
860    use Action as A;
861    match action {
862        A::Init { desc, backend } => A::Init {
863            desc: desc.map_label(owned_label),
864            backend,
865        },
866        A::ConfigureSurface(surface, config) => A::ConfigureSurface(surface, config),
867        A::CreateBuffer(buffer, desc) => A::CreateBuffer(buffer, desc.map_label(owned_label)),
868        A::DestroyBuffer(buffer) => A::DestroyBuffer(buffer),
869        A::DropBuffer(buffer) => A::DropBuffer(buffer),
870        A::DestroyTexture(texture) => A::DestroyTexture(texture),
871        A::DropTexture(texture) => A::DropTexture(texture),
872        A::DropTextureView(texture_view) => A::DropTextureView(texture_view),
873        A::DestroyExternalTexture(external_texture) => A::DestroyExternalTexture(external_texture),
874        A::DropExternalTexture(external_texture) => A::DropExternalTexture(external_texture),
875        A::DropSampler(sampler) => A::DropSampler(sampler),
876        A::GetSurfaceTexture { id, parent } => A::GetSurfaceTexture { id, parent },
877        A::Present(surface) => A::Present(surface),
878        A::DiscardSurfaceTexture(surface) => A::DiscardSurfaceTexture(surface),
879        A::ReleaseSurfaceTexture(surface) => A::ReleaseSurfaceTexture(surface),
880        A::DropBindGroupLayout(layout) => A::DropBindGroupLayout(layout),
881        A::GetRenderPipelineBindGroupLayout {
882            id,
883            pipeline,
884            index,
885        } => A::GetRenderPipelineBindGroupLayout {
886            id,
887            pipeline,
888            index,
889        },
890        A::GetComputePipelineBindGroupLayout {
891            id,
892            pipeline,
893            index,
894        } => A::GetComputePipelineBindGroupLayout {
895            id,
896            pipeline,
897            index,
898        },
899        A::DropPipelineLayout(layout) => A::DropPipelineLayout(layout),
900        A::DropBindGroup(bind_group) => A::DropBindGroup(bind_group),
901        A::DropShaderModule(shader_module) => A::DropShaderModule(shader_module),
902        A::DropComputePipeline(pipeline) => A::DropComputePipeline(pipeline),
903        A::DropRenderPipeline(pipeline) => A::DropRenderPipeline(pipeline),
904        A::DropPipelineCache(cache) => A::DropPipelineCache(cache),
905        A::DropRenderBundle(render_bundle) => A::DropRenderBundle(render_bundle),
906        A::DestroyQuerySet(query_set) => A::DestroyQuerySet(query_set),
907        A::DropQuerySet(query_set) => A::DropQuerySet(query_set),
908        A::WriteBuffer {
909            id,
910            data,
911            offset,
912            size,
913            queued,
914        } => A::WriteBuffer {
915            id,
916            data,
917            offset,
918            size,
919            queued,
920        },
921        A::WriteTexture {
922            to,
923            data,
924            layout,
925            size,
926        } => A::WriteTexture {
927            to,
928            data,
929            layout,
930            size,
931        },
932        A::Submit(index, commands) => A::Submit(index, commands),
933        A::FailedCommands {
934            commands,
935            failed_at_submit,
936            error,
937        } => A::FailedCommands {
938            commands,
939            failed_at_submit,
940            error,
941        },
942        A::DropBlas(blas) => A::DropBlas(blas),
943        A::DropTlas(tlas) => A::DropTlas(tlas),
944
945        A::CreateTexture(id, desc) => A::CreateTexture(id, desc.map_label(owned_label)),
946        A::CreateTextureError(id, desc) => A::CreateTextureError(id, desc.map_label(owned_label)),
947        A::CreateTextureView { id, parent, desc } => A::CreateTextureView {
948            id,
949            parent,
950            desc: crate::resource::TextureViewDescriptor {
951                label: owned_label(&desc.label),
952                format: desc.format,
953                dimension: desc.dimension,
954                usage: desc.usage,
955                range: desc.range,
956            },
957        },
958        A::CreateExternalTexture { id, desc, planes } => A::CreateExternalTexture {
959            id,
960            desc: desc.map_label(owned_label),
961            planes,
962        },
963        A::CreateSampler(id, desc) => A::CreateSampler(
964            id,
965            crate::resource::SamplerDescriptor {
966                label: owned_label(&desc.label),
967                address_modes: desc.address_modes,
968                mag_filter: desc.mag_filter,
969                min_filter: desc.min_filter,
970                mipmap_filter: desc.mipmap_filter,
971                lod_min_clamp: desc.lod_min_clamp,
972                lod_max_clamp: desc.lod_max_clamp,
973                compare: desc.compare,
974                anisotropy_clamp: desc.anisotropy_clamp,
975                border_color: desc.border_color,
976            },
977        ),
978        A::CreateBindGroupLayout(id, desc) => A::CreateBindGroupLayout(
979            id,
980            crate::binding_model::BindGroupLayoutDescriptor {
981                label: owned_label(&desc.label),
982                entries: Cow::Owned(desc.entries.into_owned()),
983            },
984        ),
985        A::CreatePipelineLayout(id, desc) => A::CreatePipelineLayout(
986            id,
987            crate::binding_model::PipelineLayoutDescriptor {
988                label: owned_label(&desc.label),
989                bind_group_layouts: Cow::Owned(desc.bind_group_layouts.into_owned()),
990                immediate_size: desc.immediate_size,
991            },
992        ),
993        A::CreateBindGroup(id, desc) => A::CreateBindGroup(
994            id,
995            crate::binding_model::BindGroupDescriptor {
996                label: owned_label(&desc.label),
997                layout: desc.layout,
998                entries: desc
999                    .entries
1000                    .iter()
1001                    .map(|e| crate::binding_model::BindGroupEntry {
1002                        binding: e.binding,
1003                        resource: match &e.resource {
1004                            crate::binding_model::BindingResource::Buffer(buffer_binding) => {
1005                                crate::binding_model::BindingResource::Buffer(
1006                                    buffer_binding.clone(),
1007                                )
1008                            }
1009                            crate::binding_model::BindingResource::BufferArray(cow) => {
1010                                crate::binding_model::BindingResource::BufferArray(Cow::Owned(
1011                                    cow.clone().into_owned(),
1012                                ))
1013                            }
1014                            crate::binding_model::BindingResource::Sampler(sampler) => {
1015                                crate::binding_model::BindingResource::Sampler(*sampler)
1016                            }
1017                            crate::binding_model::BindingResource::SamplerArray(cow) => {
1018                                crate::binding_model::BindingResource::SamplerArray(Cow::Owned(
1019                                    cow.clone().into_owned(),
1020                                ))
1021                            }
1022                            crate::binding_model::BindingResource::TextureView(texture_view) => {
1023                                crate::binding_model::BindingResource::TextureView(*texture_view)
1024                            }
1025                            crate::binding_model::BindingResource::TextureViewArray(cow) => {
1026                                crate::binding_model::BindingResource::TextureViewArray(Cow::Owned(
1027                                    cow.clone().into_owned(),
1028                                ))
1029                            }
1030                            crate::binding_model::BindingResource::AccelerationStructure(
1031                                acceleration_structure,
1032                            ) => crate::binding_model::BindingResource::AccelerationStructure(
1033                                *acceleration_structure,
1034                            ),
1035                            crate::binding_model::BindingResource::AccelerationStructureArray(
1036                                cow,
1037                            ) => crate::binding_model::BindingResource::AccelerationStructureArray(
1038                                Cow::Owned(cow.clone().into_owned()),
1039                            ),
1040                            crate::binding_model::BindingResource::ExternalTexture(
1041                                external_texture,
1042                            ) => crate::binding_model::BindingResource::ExternalTexture(
1043                                *external_texture,
1044                            ),
1045                        },
1046                    })
1047                    .collect(),
1048            },
1049        ),
1050        A::CreateShaderModule { id, desc, data } => A::CreateShaderModule {
1051            id,
1052            desc: crate::pipeline::ShaderModuleDescriptor {
1053                label: owned_label(&desc.label),
1054                runtime_checks: desc.runtime_checks,
1055            },
1056            data,
1057        },
1058        A::CreateShaderModulePassthrough {
1059            id,
1060            data,
1061            label,
1062            entry_points,
1063        } => A::CreateShaderModulePassthrough {
1064            id,
1065            data,
1066            label: owned_label(&label),
1067            entry_points: entry_points
1068                .iter()
1069                .map(|ep| wgt::PassthroughShaderEntryPoint {
1070                    name: Cow::Owned(ep.name.to_string()),
1071                    workgroup_size: ep.workgroup_size,
1072                })
1073                .collect(),
1074        },
1075        A::CreateComputePipeline { id, desc } => A::CreateComputePipeline {
1076            id,
1077            desc: crate::pipeline::ComputePipelineDescriptor {
1078                label: owned_label(&desc.label),
1079                layout: desc.layout,
1080                stage: owned_stage(desc.stage),
1081                cache: desc.cache,
1082            },
1083        },
1084        A::CreateGeneralRenderPipeline { id, desc } => A::CreateGeneralRenderPipeline {
1085            id,
1086            desc: crate::pipeline::GeneralRenderPipelineDescriptor {
1087                label: owned_label(&desc.label),
1088                layout: desc.layout,
1089                vertex: match desc.vertex {
1090                    crate::pipeline::RenderPipelineVertexProcessor::Vertex(
1091                        crate::pipeline::VertexState { stage, buffers },
1092                    ) => crate::pipeline::RenderPipelineVertexProcessor::Vertex(
1093                        crate::pipeline::VertexState {
1094                            stage: owned_stage(stage),
1095                            buffers: buffers
1096                                .iter()
1097                                .map(|b| {
1098                                    b.clone().map(|buffer| crate::pipeline::VertexBufferLayout {
1099                                        array_stride: buffer.array_stride,
1100                                        step_mode: buffer.step_mode,
1101                                        attributes: Cow::Owned(buffer.attributes.into_owned()),
1102                                    })
1103                                })
1104                                .collect(),
1105                        },
1106                    ),
1107                    crate::pipeline::RenderPipelineVertexProcessor::Mesh(task, mesh) => {
1108                        crate::pipeline::RenderPipelineVertexProcessor::Mesh(
1109                            task.map(|t| crate::pipeline::TaskState {
1110                                stage: owned_stage(t.stage),
1111                            }),
1112                            crate::pipeline::MeshState {
1113                                stage: owned_stage(mesh.stage),
1114                            },
1115                        )
1116                    }
1117                },
1118                primitive: desc.primitive,
1119                depth_stencil: desc.depth_stencil,
1120                multisample: desc.multisample,
1121                fragment: desc.fragment.map(|f| crate::pipeline::FragmentState {
1122                    stage: owned_stage(f.stage),
1123                    targets: Cow::Owned(f.targets.into_owned()),
1124                }),
1125                multiview_mask: desc.multiview_mask,
1126                cache: desc.cache,
1127            },
1128        },
1129        A::CreatePipelineCache { id, desc } => A::CreatePipelineCache {
1130            id,
1131            desc: crate::pipeline::PipelineCacheDescriptor {
1132                label: owned_label(&desc.label),
1133                data: desc.data.map(|d| Cow::Owned(d.to_vec())),
1134                fallback: desc.fallback,
1135            },
1136        },
1137        A::CreateRenderBundle { id, desc, base } => A::CreateRenderBundle {
1138            id,
1139            desc: crate::command::RenderBundleEncoderDescriptor {
1140                label: owned_label(&desc.label),
1141                color_formats: Cow::Owned(desc.color_formats.into_owned()),
1142                depth_stencil: desc.depth_stencil,
1143                sample_count: desc.sample_count,
1144                multiview: desc.multiview,
1145            },
1146            base,
1147        },
1148        A::CreateQuerySet { id, desc } => A::CreateQuerySet {
1149            id,
1150            desc: desc.map_label(owned_label),
1151        },
1152        A::CreateBlas { id, desc, sizes } => A::CreateBlas {
1153            id,
1154            desc: desc.map_label(owned_label),
1155            sizes,
1156        },
1157        A::CreateTlas { id, desc } => A::CreateTlas {
1158            id,
1159            desc: desc.map_label(owned_label),
1160        },
1161    }
1162}
1163
1164fn owned_stage<SM>(
1165    stage: crate::pipeline::ProgrammableStageDescriptor<'_, SM>,
1166) -> crate::pipeline::ProgrammableStageDescriptor<'static, SM> {
1167    crate::pipeline::ProgrammableStageDescriptor {
1168        module: stage.module,
1169        entry_point: owned_label(&stage.entry_point),
1170        constants: stage.constants,
1171        zero_initialize_workgroup_memory: stage.zero_initialize_workgroup_memory,
1172    }
1173}
1174
1175fn owned_label(l: &Option<Cow<'_, str>>) -> Option<Cow<'static, str>> {
1176    l.as_ref().map(|l| Cow::Owned(l.to_string()))
1177}