trace: make Id serialization nicer

This commit is contained in:
Dzmitry Malyshau 2020-04-29 20:17:28 -04:00 committed by Dzmitry Malyshau
parent 3c68fb17e4
commit f64b2dd3bb
10 changed files with 142 additions and 106 deletions

View File

@ -17,6 +17,7 @@ The implementation consists of the following parts:
- `wgpu-core` - internal Rust API for WebGPU implementations to use
- `wgpu-types` - Rust types shared between `wgpu-core`, `wgpu-native`, and `wgpu-rs`
- `player` - application for replaying the API traces, uses `winit`
This repository is not meant for direct use by applications.
If you are looking for the user-facing Rust API, you need [wgpu-rs](https://github.com/gfx-rs/wgpu-rs).

12
player/README.md Normal file
View File

@ -0,0 +1,12 @@
# wgpu player
This is application that allows replaying the `wgpu` workloads recorded elsewhere.
Launch as:
```rust
player <trace-dir>
```
When built with "winit" feature, it's able to replay the workloads that operate on a swapchain. It renders each frame consequently, then waits for the user to close the window. When built without "winit", it launches in console mode and can replay any trace that doesn't use swapchains.
Note: replaying is currently restricted to the same backend, as one used for recording a trace. It is straightforward, however, to just replace the backend in RON, since it's serialized as plain text. Valid values are: Vulkan, Metal, Dx12, and Dx11.

View File

@ -454,7 +454,6 @@ fn main() {
.unwrap();
let global = wgc::hub::Global::new("player", IdentityPassThroughFactory);
let mut adapter_id_manager = wgc::hub::IdentityManager::default();
let mut command_buffer_id_manager = wgc::hub::IdentityManager::default();
#[cfg(feature = "winit")]
@ -463,37 +462,28 @@ fn main() {
wgc::id::TypedId::zip(0, 1, wgt::Backend::Empty),
);
let adapter = global
.pick_adapter(
&wgc::instance::RequestAdapterOptions {
power_preference: wgt::PowerPreference::Default,
#[cfg(feature = "winit")]
compatible_surface: Some(surface),
#[cfg(not(feature = "winit"))]
compatible_surface: None,
},
wgc::instance::AdapterInputs::IdSet(
&vec![
adapter_id_manager.alloc(wgt::Backend::Vulkan),
adapter_id_manager.alloc(wgt::Backend::Dx12),
adapter_id_manager.alloc(wgt::Backend::Metal),
],
|id| id.backend(),
),
)
.unwrap();
log::info!("Initializing the device");
let device = match actions.pop() {
Some(trace::Action::Init { limits }) => {
Some(trace::Action::Init { desc, backend }) => {
log::info!("Initializing the device for backend: {:?}", backend);
let adapter = global
.pick_adapter(
&wgc::instance::RequestAdapterOptions {
power_preference: wgt::PowerPreference::Default,
#[cfg(feature = "winit")]
compatible_surface: Some(surface),
#[cfg(not(feature = "winit"))]
compatible_surface: None,
},
wgc::instance::AdapterInputs::IdSet(
&[wgc::id::TypedId::zip(0, 0, backend)],
|id| id.backend(),
),
)
.expect("Unable to find an adapter for selected backend");
gfx_select!(adapter => global.adapter_request_device(
adapter,
&wgt::DeviceDescriptor {
extensions: wgt::Extensions {
anisotropic_filtering: false,
},
limits,
},
&desc,
None,
wgc::id::TypedId::zip(1, 0, wgt::Backend::Empty)
))
}
@ -501,72 +491,69 @@ fn main() {
};
log::info!("Executing actions");
#[cfg(feature = "winit")]
{
let mut frame_count = 0;
winit::platform::desktop::EventLoopExtDesktop::run_return(
&mut event_loop,
move |event, _, control_flow| {
use winit::{
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
event_loop::ControlFlow,
};
*control_flow = ControlFlow::Poll;
match event {
Event::MainEventsCleared => {
window.request_redraw();
}
Event::RedrawRequested(_) => loop {
match actions.pop() {
Some(trace::Action::CreateSwapChain { id, desc }) => {
log::info!("Initializing the swapchain");
assert_eq!(id.to_surface_id(), surface);
window.set_inner_size(winit::dpi::PhysicalSize::new(
desc.width,
desc.height,
));
gfx_select!(device => global.device_create_swap_chain(device, surface, &desc));
}
Some(trace::Action::PresentSwapChain(id)) => {
frame_count += 1;
log::debug!("Presenting frame {}", frame_count);
gfx_select!(device => global.swap_chain_present(id));
break;
}
Some(action) => {
gfx_select!(device => global.process(device, action, &dir, &mut command_buffer_id_manager));
}
None => break,
}
},
Event::WindowEvent { event, .. } => match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(VirtualKeyCode::Escape),
state: ElementState::Pressed,
..
},
..
}
| WindowEvent::CloseRequested => {
*control_flow = ControlFlow::Exit;
}
_ => {}
},
Event::LoopDestroyed => {
log::info!("Closing");
gfx_select!(device => global.device_poll(device, true));
}
_ => {}
}
},
);
}
#[cfg(not(feature = "winit"))]
while let Some(action) = actions.pop() {
gfx_select!(device => global.process(device, action, &dir, &mut command_buffer_id_manager));
}
#[cfg(feature = "winit")]
{
use winit::{
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
event_loop::ControlFlow,
platform::desktop::EventLoopExtDesktop,
};
let mut frame_count = 0;
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
match event {
Event::MainEventsCleared => {
window.request_redraw();
}
Event::RedrawRequested(_) => loop {
match actions.pop() {
Some(trace::Action::CreateSwapChain { id, desc }) => {
log::info!("Initializing the swapchain");
assert_eq!(id.to_surface_id(), surface);
window.set_inner_size(winit::dpi::PhysicalSize::new(
desc.width,
desc.height,
));
gfx_select!(device => global.device_create_swap_chain(device, surface, &desc));
}
Some(trace::Action::PresentSwapChain(id)) => {
frame_count += 1;
log::debug!("Presenting frame {}", frame_count);
gfx_select!(device => global.swap_chain_present(id));
break;
}
Some(action) => {
gfx_select!(device => global.process(device, action, &dir, &mut command_buffer_id_manager));
}
None => break,
}
},
Event::WindowEvent { event, .. } => match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(VirtualKeyCode::Escape),
state: ElementState::Pressed,
..
},
..
}
| WindowEvent::CloseRequested => {
*control_flow = ControlFlow::Exit;
}
_ => {}
},
Event::LoopDestroyed => {
log::info!("Closing");
gfx_select!(device => global.device_poll(device, true));
}
_ => {}
}
});
}
}

View File

@ -33,7 +33,7 @@ pub enum ComputeCommand {
index: u8,
num_dynamic_offsets: u8,
bind_group_id: id::BindGroupId,
#[cfg_attr(feature = "serde", serde(skip))]
#[cfg_attr(any(feature = "trace", feature = "replay"), serde(skip))]
phantom_offsets: PhantomSlice<DynamicOffset>,
},
SetPipeline(id::ComputePipelineId),

View File

@ -63,7 +63,7 @@ pub enum RenderCommand {
index: u8,
num_dynamic_offsets: u8,
bind_group_id: id::BindGroupId,
#[cfg_attr(feature = "serde", serde(skip))]
#[cfg_attr(any(feature = "trace", feature = "replay"), serde(skip))]
phantom_offsets: PhantomSlice<DynamicOffset>,
},
SetPipeline(id::RenderPipelineId),

View File

@ -199,6 +199,7 @@ pub struct Device<B: hal::Backend> {
temp_suspected: life::SuspectedResources,
pub(crate) private_features: PrivateFeatures,
limits: wgt::Limits,
extensions: wgt::Extensions,
#[cfg(feature = "trace")]
pub(crate) trace: Option<Mutex<Trace>>,
}
@ -211,8 +212,8 @@ impl<B: GfxBackend> Device<B> {
mem_props: hal::adapter::MemoryProperties,
non_coherent_atom_size: u64,
supports_texture_d24_s8: bool,
limits: wgt::Limits,
#[cfg(feature = "trace")] trace_path: Option<&std::path::Path>,
desc: &wgt::DeviceDescriptor,
trace_path: Option<&std::path::Path>,
) -> Self {
// don't start submission index at zero
let life_guard = LifeGuard::new();
@ -232,6 +233,11 @@ impl<B: GfxBackend> Device<B> {
non_coherent_atom_size,
)
};
#[cfg(not(feature = "trace"))]
match trace_path {
Some(_) => log::warn!("Tracing feature is not enabled"),
None => (),
}
Device {
raw,
@ -250,7 +256,8 @@ impl<B: GfxBackend> Device<B> {
trace: trace_path.and_then(|path| match Trace::new(path) {
Ok(mut trace) => {
trace.add(Action::Init {
limits: limits.clone(),
desc: desc.clone(),
backend: B::VARIANT,
});
Some(Mutex::new(trace))
}
@ -262,7 +269,8 @@ impl<B: GfxBackend> Device<B> {
private_features: PrivateFeatures {
supports_texture_d24_s8,
},
limits,
limits: desc.limits.clone(),
extensions: desc.extensions.clone(),
}
}
@ -2336,7 +2344,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
#[cfg(feature = "replay")]
/// Only triange suspected resource IDs. This helps us to avoid ID collisions
/// upon creating new resources when re-plaing a trace.
/// upon creating new resources when re-playing a trace.
pub fn device_maintain_ids<B: GfxBackend>(&self, device_id: id::DeviceId) {
let hub = B::hub(self);
let mut token = Token::root();

View File

@ -96,7 +96,8 @@ pub struct RenderPipelineDescriptor {
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
pub enum Action {
Init {
limits: wgt::Limits,
desc: wgt::DeviceDescriptor,
backend: wgt::Backend,
},
CreateBuffer {
id: id::BufferId,

View File

@ -11,9 +11,37 @@ const EPOCH_MASK: u32 = (1 << (32 - BACKEND_BITS)) - 1;
type Dummy = crate::backend::Empty;
#[repr(transparent)]
#[cfg_attr(feature = "trace", derive(serde::Serialize), serde(into = "SerialId"))]
#[cfg_attr(
feature = "replay",
derive(serde::Deserialize),
serde(from = "SerialId")
)]
pub struct Id<T>(NonZeroU64, PhantomData<T>);
// This type represents Id in a more readable (and editable) way.
#[allow(dead_code)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
pub struct Id<T>(NonZeroU64, PhantomData<T>);
enum SerialId {
// The only variant forces RON to not ignore "Id"
Id(Index, Epoch, Backend),
}
#[cfg(feature = "trace")]
impl<T> From<Id<T>> for SerialId {
fn from(id: Id<T>) -> Self {
let (index, epoch, backend) = id.unzip();
SerialId::Id(index, epoch, backend)
}
}
#[cfg(feature = "replay")]
impl<T> From<SerialId> for Id<T> {
fn from(id: SerialId) -> Self {
match id {
SerialId::Id(index, epoch, backend) => TypedId::zip(index, epoch, backend),
}
}
}
// required for PeekPoke
impl<T> Default for Id<T> {

View File

@ -580,7 +580,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
&self,
adapter_id: AdapterId,
desc: &DeviceDescriptor,
#[cfg(feature = "trace")] trace_path: Option<&std::path::Path>,
trace_path: Option<&std::path::Path>,
id_in: Input<G, DeviceId>,
) -> DeviceId {
let hub = B::hub(self);
@ -646,8 +646,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
mem_props,
limits.non_coherent_atom_size as u64,
supports_texture_d24_s8,
desc.limits.clone(),
#[cfg(feature = "trace")]
desc,
trace_path,
)
};

View File

@ -37,7 +37,7 @@ pub enum PowerPreference {
bitflags::bitflags! {
#[repr(transparent)]
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct BackendBit: u32 {
const VULKAN = 1 << Backend::Vulkan as u32;
const GL = 1 << Backend::Gl as u32;