diff --git a/wgpu-core/src/command/allocator.rs b/wgpu-core/src/command/allocator.rs index 6eb18b185..1d89b3862 100644 --- a/wgpu-core/src/command/allocator.rs +++ b/wgpu-core/src/command/allocator.rs @@ -80,6 +80,7 @@ impl CommandAllocator { limits: wgt::Limits, private_features: PrivateFeatures, lowest_active_index: SubmissionIndex, + #[cfg(feature = "trace")] enable_tracing: bool, ) -> CommandBuffer { //debug_assert_eq!(device_id.backend(), B::VARIANT); let thread_id = thread::current().id(); @@ -112,6 +113,12 @@ impl CommandAllocator { used_swap_chain: None, limits, private_features, + #[cfg(feature = "trace")] + commands: if enable_tracing { + Some(Vec::new()) + } else { + None + }, } } } diff --git a/wgpu-core/src/command/compute.rs b/wgpu-core/src/command/compute.rs index 179190d4c..ea6e87d78 100644 --- a/wgpu-core/src/command/compute.rs +++ b/wgpu-core/src/command/compute.rs @@ -26,11 +26,14 @@ enum PipelineState { } #[derive(Clone, Copy, Debug, PeekPoke)] -enum ComputeCommand { +#[cfg_attr(feature = "trace", derive(serde::Serialize))] +#[cfg_attr(feature = "replay", derive(serde::Deserialize))] +pub(crate) enum ComputeCommand { SetBindGroup { index: u8, num_dynamic_offsets: u8, bind_group_id: id::BindGroupId, + #[cfg_attr(feature = "serde", serde(skip))] phantom_offsets: PhantomSlice, }, SetPipeline(id::ComputePipelineId), @@ -247,6 +250,43 @@ impl Global { ComputeCommand::End => break, } } + + #[cfg(feature = "trace")] + match cmb.commands { + Some(ref mut list) => { + let mut pass_commands = Vec::new(); + let mut pass_dynamic_offsets = Vec::new(); + peeker = raw_data.as_ptr(); + loop { + peeker = unsafe { ComputeCommand::peek_from(peeker, &mut command) }; + match command { + ComputeCommand::SetBindGroup { + num_dynamic_offsets, + phantom_offsets, + .. + } => { + let (new_peeker, offsets) = unsafe { + phantom_offsets.decode_unaligned( + peeker, + num_dynamic_offsets as usize, + raw_data_end, + ) + }; + peeker = new_peeker; + pass_dynamic_offsets.extend_from_slice(offsets); + } + ComputeCommand::End => break, + _ => {} + } + pass_commands.push(command); + } + list.push(crate::device::trace::Command::RunComputePass { + commands: pass_commands, + dynamic_offsets: pass_dynamic_offsets, + }); + } + None => {} + } } } diff --git a/wgpu-core/src/command/mod.rs b/wgpu-core/src/command/mod.rs index 8a57b2cf4..cf82f972a 100644 --- a/wgpu-core/src/command/mod.rs +++ b/wgpu-core/src/command/mod.rs @@ -27,7 +27,7 @@ use peek_poke::PeekPoke; use std::{marker::PhantomData, mem, ptr, slice, thread::ThreadId}; #[derive(Clone, Copy, Debug, PeekPoke)] -struct PhantomSlice(PhantomData); +pub(crate) struct PhantomSlice(PhantomData); impl Default for PhantomSlice { fn default() -> Self { @@ -140,6 +140,8 @@ pub struct CommandBuffer { pub(crate) used_swap_chain: Option<(Stored, B::Framebuffer)>, limits: wgt::Limits, private_features: PrivateFeatures, + #[cfg(feature = "trace")] + pub(crate) commands: Option>, } impl CommandBuffer { diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index 97520ca96..e74138f53 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -46,6 +46,8 @@ pub struct RenderPassDescriptor<'a> { } #[derive(Clone, Copy, Debug, Default, PeekPoke)] +#[cfg_attr(feature = "trace", derive(serde::Serialize))] +#[cfg_attr(feature = "replay", derive(serde::Deserialize))] pub struct Rect { pub x: T, pub y: T, @@ -54,11 +56,14 @@ pub struct Rect { } #[derive(Clone, Copy, Debug, PeekPoke)] -enum RenderCommand { +#[cfg_attr(feature = "trace", derive(serde::Serialize))] +#[cfg_attr(feature = "replay", derive(serde::Deserialize))] +pub(crate) enum RenderCommand { SetBindGroup { index: u8, num_dynamic_offsets: u8, bind_group_id: id::BindGroupId, + #[cfg_attr(feature = "serde", serde(skip))] phantom_offsets: PhantomSlice, }, SetPipeline(id::RenderPipelineId), @@ -329,6 +334,8 @@ impl Global { let mut targets: RawRenderTargets = unsafe { mem::zeroed() }; assert!(unsafe { peeker.add(RawRenderTargets::max_size()) <= raw_data_end }); peeker = unsafe { RawRenderTargets::peek_from(peeker, &mut targets) }; + #[cfg(feature = "trace")] + let command_peeker_base = peeker; let color_attachments = targets .colors @@ -1173,6 +1180,45 @@ impl Global { } } + #[cfg(feature = "trace")] + match cmb.commands { + Some(ref mut list) => { + let mut pass_commands = Vec::new(); + let mut pass_dynamic_offsets = Vec::new(); + peeker = command_peeker_base; + loop { + peeker = unsafe { RenderCommand::peek_from(peeker, &mut command) }; + match command { + RenderCommand::SetBindGroup { + num_dynamic_offsets, + phantom_offsets, + .. + } => { + let (new_peeker, offsets) = unsafe { + phantom_offsets.decode_unaligned( + peeker, + num_dynamic_offsets as usize, + raw_data_end, + ) + }; + peeker = new_peeker; + pass_dynamic_offsets.extend_from_slice(offsets); + } + RenderCommand::End => break, + _ => {} + } + pass_commands.push(command); + } + list.push(crate::device::trace::Command::RunRenderPass { + target_colors: color_attachments.into_iter().collect(), + target_depth_stencil: depth_stencil_attachment.cloned(), + commands: pass_commands, + dynamic_offsets: pass_dynamic_offsets, + }); + } + None => {} + } + log::trace!("Merging {:?} with the render pass", encoder_id); unsafe { raw.end_render_pass(); diff --git a/wgpu-core/src/command/transfer.rs b/wgpu-core/src/command/transfer.rs index e26d5a186..f3c8adc47 100644 --- a/wgpu-core/src/command/transfer.rs +++ b/wgpu-core/src/command/transfer.rs @@ -2,6 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#[cfg(feature = "trace")] +use crate::device::trace::Command as TraceCommand; use crate::{ conv, device::{all_buffer_stages, all_image_stages}, @@ -18,7 +20,9 @@ use std::iter; const BITS_PER_BYTE: u32 = 8; #[repr(C)] -#[derive(Debug)] +#[derive(Clone, Debug)] +#[cfg_attr(feature = "trace", derive(serde::Serialize))] +#[cfg_attr(feature = "replay", derive(serde::Deserialize))] pub struct BufferCopyView { pub buffer: BufferId, pub offset: BufferAddress, @@ -27,7 +31,9 @@ pub struct BufferCopyView { } #[repr(C)] -#[derive(Debug)] +#[derive(Clone, Debug)] +#[cfg_attr(feature = "trace", derive(serde::Serialize))] +#[cfg_attr(feature = "replay", derive(serde::Deserialize))] pub struct TextureCopyView { pub texture: TextureId, pub mip_level: u32, @@ -90,6 +96,18 @@ impl Global { // borrow the buffer tracker mutably... let mut barriers = Vec::new(); + #[cfg(feature = "trace")] + match cmb.commands { + Some(ref mut list) => list.push(TraceCommand::CopyBufferToBuffer { + src: source, + src_offset: source_offset, + dst: destination, + dst_offset: destination_offset, + size, + }), + None => (), + } + let (src_buffer, src_pending) = cmb.trackers .buffers @@ -135,6 +153,16 @@ impl Global { let (texture_guard, _) = hub.textures.read(&mut token); let aspects = texture_guard[destination.texture].full_range.aspects; + #[cfg(feature = "trace")] + match cmb.commands { + Some(ref mut list) => list.push(TraceCommand::CopyBufferToTexture { + src: source.clone(), + dst: destination.clone(), + size: copy_size, + }), + None => (), + } + let (src_buffer, src_pending) = cmb.trackers.buffers.use_replace( &*buffer_guard, source.buffer, @@ -199,6 +227,16 @@ impl Global { let (texture_guard, _) = hub.textures.read(&mut token); let aspects = texture_guard[source.texture].full_range.aspects; + #[cfg(feature = "trace")] + match cmb.commands { + Some(ref mut list) => list.push(TraceCommand::CopyTextureToBuffer { + src: source.clone(), + dst: destination.clone(), + size: copy_size, + }), + None => (), + } + let (src_texture, src_pending) = cmb.trackers.textures.use_replace( &*texture_guard, source.texture, @@ -268,6 +306,16 @@ impl Global { let aspects = texture_guard[source.texture].full_range.aspects & texture_guard[destination.texture].full_range.aspects; + #[cfg(feature = "trace")] + match cmb.commands { + Some(ref mut list) => list.push(TraceCommand::CopyTextureToTexture { + src: source.clone(), + dst: destination.clone(), + size: copy_size, + }), + None => (), + } + let (src_texture, src_pending) = cmb.trackers.textures.use_replace( &*texture_guard, source.texture, diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index 22ed28c63..981988c60 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -1536,6 +1536,8 @@ impl Global { device.limits.clone(), device.private_features, lowest_active_index, + #[cfg(feature = "trace")] + device.trace.is_some(), ); unsafe { let raw_command_buffer = command_buffer.raw.last_mut().unwrap(); @@ -1662,6 +1664,13 @@ impl Global { // finish all the command buffers first for &cmb_id in command_buffer_ids { let comb = &mut command_buffer_guard[cmb_id]; + #[cfg(feature = "trace")] + match device.trace { + Some(ref trace) => trace + .lock() + .add(Action::Submit(comb.commands.take().unwrap())), + None => (), + }; if let Some((sc_id, fbo)) = comb.used_swap_chain.take() { let sc = &mut swap_chain_guard[sc_id.value]; diff --git a/wgpu-core/src/device/trace.rs b/wgpu-core/src/device/trace.rs index 48c6d9c88..d0a594b17 100644 --- a/wgpu-core/src/device/trace.rs +++ b/wgpu-core/src/device/trace.rs @@ -2,7 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use crate::id; +use crate::{ + command::{BufferCopyView, TextureCopyView}, + id, +}; use std::{io::Write as _, ops::Range}; //TODO: consider a readable Id that doesn't include the backend @@ -20,7 +23,7 @@ pub enum BindingResource { } #[derive(serde::Serialize)] -pub enum Action { +pub(crate) enum Action { Init { limits: wgt::Limits, }, @@ -82,6 +85,43 @@ pub enum Action { data: FileName, range: Range, }, + Submit(Vec), +} + +#[derive(Debug, serde::Serialize)] +pub(crate) enum Command { + CopyBufferToBuffer { + src: id::BufferId, + src_offset: wgt::BufferAddress, + dst: id::BufferId, + dst_offset: wgt::BufferAddress, + size: wgt::BufferAddress, + }, + CopyBufferToTexture { + src: BufferCopyView, + dst: TextureCopyView, + size: wgt::Extent3d, + }, + CopyTextureToBuffer { + src: TextureCopyView, + dst: BufferCopyView, + size: wgt::Extent3d, + }, + CopyTextureToTexture { + src: TextureCopyView, + dst: TextureCopyView, + size: wgt::Extent3d, + }, + RunComputePass { + commands: Vec, + dynamic_offsets: Vec, + }, + RunRenderPass { + target_colors: Vec, + target_depth_stencil: Option, + commands: Vec, + dynamic_offsets: Vec, + }, } #[derive(Debug)] @@ -112,7 +152,7 @@ impl Trace { name } - pub fn add(&mut self, action: Action) { + pub(crate) fn add(&mut self, action: Action) { match ron::ser::to_string_pretty(&action, self.config.clone()) { Ok(string) => { let _ = write!(self.file, "{},\n", string);