trace: player skeleton

This commit is contained in:
Dzmitry Malyshau 2020-04-29 00:42:35 -04:00 committed by Dzmitry Malyshau
parent 622d9ecc74
commit 47f37ad78e
11 changed files with 272 additions and 19 deletions

View File

@ -1,5 +1,6 @@
[workspace]
members = [
"player",
"wgpu-core",
"wgpu-types",
]

32
player/Cargo.toml Normal file
View File

@ -0,0 +1,32 @@
[package]
name = "player"
version = "0.1.0"
authors = [
"Dzmitry Malyshau <kvark@mozilla.com>",
]
edition = "2018"
description = "WebGPU trace player"
homepage = "https://github.com/gfx-rs/wgpu"
repository = "https://github.com/gfx-rs/wgpu"
keywords = ["graphics"]
license = "MPL-2.0"
publish = false
[features]
[dependencies]
env_logger = "0.7"
ron = { version = "0.5" }
winit = { version = "0.22" }
[dependencies.wgt]
path = "../wgpu-types"
package = "wgpu-types"
version = "0.5"
features = ["replay"]
[dependencies.wgc]
path = "../wgpu-core"
package = "wgpu-core"
version = "0.5"
features = ["replay", "raw-window-handle"]

121
player/src/main.rs Normal file
View File

@ -0,0 +1,121 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 wgc::device::trace;
use std::{
fmt::Debug,
fs::File,
marker::PhantomData,
path::{Path, PathBuf},
};
use winit::{event_loop::EventLoop, window::WindowBuilder};
macro_rules! gfx_select {
($id:expr => $global:ident.$method:ident( $($param:expr),+ )) => {
match $id.backend() {
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
wgt::Backend::Vulkan => $global.$method::<wgc::backend::Vulkan>( $($param),+ ),
#[cfg(any(target_os = "ios", target_os = "macos"))]
wgt::Backend::Metal => $global.$method::<wgc::backend::Metal>( $($param),+ ),
#[cfg(windows)]
wgt::Backend::Dx12 => $global.$method::<wgc::backend::Dx12>( $($param),+ ),
#[cfg(windows)]
wgt::Backend::Dx11 => $global.$method::<wgc::backend::Dx11>( $($param),+ ),
_ => unreachable!()
}
};
}
#[derive(Debug)]
struct IdentityPassThrough<I>(PhantomData<I>);
impl<I: Clone + Debug + wgc::id::TypedId> wgc::hub::IdentityHandler<I> for IdentityPassThrough<I> {
type Input = I;
fn process(&self, id: I, backend: wgt::Backend) -> I {
let (index, epoch, _backend) = id.unzip();
I::zip(index, epoch, backend)
}
fn free(&self, _id: I) {}
}
struct IdentityPassThroughFactory;
impl<I: Clone + Debug + wgc::id::TypedId> wgc::hub::IdentityHandlerFactory<I>
for IdentityPassThroughFactory
{
type Filter = IdentityPassThrough<I>;
fn spawn(&self, _min_index: u32) -> Self::Filter {
IdentityPassThrough(PhantomData)
}
}
impl wgc::hub::GlobalIdentityHandlerFactory for IdentityPassThroughFactory {}
fn main() {
env_logger::init();
let folder = match std::env::args().nth(1) {
Some(arg) if Path::new(&arg).is_dir() => PathBuf::from(arg),
_ => panic!("Provide the folder path as the parameter"),
};
let file = File::open(folder.join(trace::FILE_NAME)).unwrap();
let actions: Vec<trace::Action> = ron::de::from_reader(file).unwrap();
let event_loop = EventLoop::new();
let mut builder = WindowBuilder::new();
builder = builder.with_title("wgpu player");
let window = builder.build(&event_loop).unwrap();
let global = wgc::hub::Global::new("player", IdentityPassThroughFactory);
let mut surface_id_manager = wgc::hub::IdentityManager::from_index(1);
let mut adapter_id_manager = wgc::hub::IdentityManager::default();
let mut device_id_manager = wgc::hub::IdentityManager::default();
let (_size, surface) = {
let size = window.inner_size();
let id = surface_id_manager.alloc(wgt::Backend::Empty);
let surface = global.instance_create_surface(&window, id);
(size, surface)
};
let adapter = global
.pick_adapter(
&wgc::instance::RequestAdapterOptions {
power_preference: wgt::PowerPreference::Default,
compatible_surface: Some(surface),
},
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| wgc::id::TypedId::unzip(*id).2,
),
)
.unwrap();
let limits = match actions.first() {
Some(&trace::Action::Init { ref limits }) => limits.clone(),
other => panic!("Unsupported first action: {:?}", other),
};
let device = gfx_select!(adapter => global.adapter_request_device(
adapter,
&wgt::DeviceDescriptor {
extensions: wgt::Extensions {
anisotropic_filtering: false,
},
limits,
},
device_id_manager.alloc(wgt::Backend::Vulkan)
));
for action in actions.into_iter().skip(1) {
match action {
_ => {}
}
}
}

View File

@ -34,6 +34,7 @@ gfx-descriptor = "0.1"
gfx-memory = "0.1"
parking_lot = "0.10"
peek-poke = "0.2"
raw-window-handle = { version = "0.3", optional = true }
ron = { version = "0.5", optional = true }
serde = { version = "1.0", features = ["serde_derive"], optional = true }
smallvec = "1"

View File

@ -28,7 +28,7 @@ enum PipelineState {
#[derive(Clone, Copy, Debug, PeekPoke)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
pub(crate) enum ComputeCommand {
pub enum ComputeCommand {
SetBindGroup {
index: u8,
num_dynamic_offsets: u8,

View File

@ -27,7 +27,7 @@ use peek_poke::PeekPoke;
use std::{marker::PhantomData, mem, ptr, slice, thread::ThreadId};
#[derive(Clone, Copy, Debug, PeekPoke)]
pub(crate) struct PhantomSlice<T>(PhantomData<T>);
pub struct PhantomSlice<T>(PhantomData<T>);
impl<T> Default for PhantomSlice<T> {
fn default() -> Self {

View File

@ -58,7 +58,7 @@ pub struct Rect<T> {
#[derive(Clone, Copy, Debug, PeekPoke)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
pub(crate) enum RenderCommand {
pub enum RenderCommand {
SetBindGroup {
index: u8,
num_dynamic_offsets: u8,

View File

@ -31,8 +31,8 @@ use std::{
};
mod life;
#[cfg(feature = "trace")]
pub(crate) mod trace;
#[cfg(any(feature = "trace", feature = "replay"))]
pub mod trace;
#[cfg(feature = "trace")]
use trace::{Action, Trace};

View File

@ -6,12 +6,19 @@ use crate::{
command::{BufferCopyView, TextureCopyView},
id,
};
use std::{io::Write as _, ops::Range};
use std::ops::Range;
#[cfg(feature = "trace")]
use std::io::Write as _;
//TODO: consider a readable Id that doesn't include the backend
type FileName = String;
#[derive(serde::Serialize)]
pub const FILE_NAME: &str = "trace.ron";
#[derive(Debug)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
pub enum BindingResource {
Buffer {
id: id::BufferId,
@ -22,8 +29,10 @@ pub enum BindingResource {
TextureView(id::TextureViewId),
}
#[derive(serde::Serialize)]
pub(crate) enum Action {
#[derive(Debug)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
pub enum Action {
Init {
limits: wgt::Limits,
},
@ -88,8 +97,10 @@ pub(crate) enum Action {
Submit(Vec<Command>),
}
#[derive(Debug, serde::Serialize)]
pub(crate) enum Command {
#[derive(Debug)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
pub enum Command {
CopyBufferToBuffer {
src: id::BufferId,
src_offset: wgt::BufferAddress,
@ -124,6 +135,7 @@ pub(crate) enum Command {
},
}
#[cfg(feature = "trace")]
#[derive(Debug)]
pub struct Trace {
path: std::path::PathBuf,
@ -132,10 +144,11 @@ pub struct Trace {
binary_id: usize,
}
#[cfg(feature = "trace")]
impl Trace {
pub fn new(path: &std::path::Path) -> Result<Self, std::io::Error> {
log::info!("Tracing into '{:?}'", path);
let mut file = std::fs::File::create(path.join("trace.ron"))?;
let mut file = std::fs::File::create(path.join(FILE_NAME))?;
file.write(b"[\n")?;
Ok(Trace {
path: path.to_path_buf(),
@ -164,6 +177,7 @@ impl Trace {
}
}
#[cfg(feature = "trace")]
impl Drop for Trace {
fn drop(&mut self) {
let _ = self.file.write(b"]");

View File

@ -25,7 +25,7 @@ use wgt::Backend;
#[cfg(debug_assertions)]
use std::cell::Cell;
use std::{fmt::Debug, iter, marker::PhantomData, ops};
use std::{fmt::Debug, marker::PhantomData, ops};
/// A simple structure to manage identities of objects.
#[derive(Debug)]
@ -44,6 +44,13 @@ impl Default for IdentityManager {
}
impl IdentityManager {
pub fn from_index(min_index: u32) -> Self {
IdentityManager {
free: (0 .. min_index).collect(),
epochs: vec![1; min_index as usize],
}
}
pub fn alloc<I: TypedId>(&mut self, backend: Backend) -> I {
match self.free.pop() {
Some(index) => I::zip(index, self.epochs[index as usize], backend),
@ -262,10 +269,7 @@ pub struct IdentityManagerFactory;
impl<I: TypedId + Debug> IdentityHandlerFactory<I> for IdentityManagerFactory {
type Filter = Mutex<IdentityManager>;
fn spawn(&self, min_index: Index) -> Self::Filter {
let mut man = IdentityManager::default();
man.free.extend(0..min_index);
man.epochs.extend(iter::repeat(1).take(min_index as usize));
Mutex::new(man)
Mutex::new(IdentityManager::from_index(min_index))
}
}
@ -549,6 +553,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
impl<G: GlobalIdentityHandlerFactory> Drop for Global<G> {
fn drop(&mut self) {
log::info!("Dropping Global");
// destroy surfaces
for (_, (surface, _)) in self.surfaces.data.write().map.drain() {
self.instance.destroy_surface(surface);

View File

@ -188,7 +188,7 @@ impl From<HalDeviceType> for DeviceType {
pub enum AdapterInputs<'a, I> {
IdSet(&'a [I], fn(&I) -> Backend),
Mask(BackendBit, fn() -> I),
Mask(BackendBit, fn(Backend) -> I),
}
impl<I: Clone> AdapterInputs<'_, I> {
@ -197,7 +197,7 @@ impl<I: Clone> AdapterInputs<'_, I> {
AdapterInputs::IdSet(ids, ref fun) => ids.iter().find(|id| fun(id) == b).cloned(),
AdapterInputs::Mask(bits, ref fun) => {
if bits.contains(b.into()) {
Some(fun())
Some(fun(b))
} else {
None
}
@ -207,6 +207,85 @@ impl<I: Clone> AdapterInputs<'_, I> {
}
impl<G: GlobalIdentityHandlerFactory> Global<G> {
#[cfg(feature = "raw-window-handle")]
pub fn instance_create_surface<W: raw_window_handle::HasRawWindowHandle>(
&self,
window: &W,
id_in: Input<G, SurfaceId>,
) -> SurfaceId {
use raw_window_handle::RawWindowHandle as Rwh;
let surface = match window.raw_window_handle() {
#[cfg(target_os = "ios")]
Rwh::IOS(h) => Surface {
#[cfg(feature = "gfx-backend-vulkan")]
vulkan: None,
metal: self
.instance
.metal
.create_surface_from_uiview(h.ui_view, cfg!(debug_assertions)),
},
#[cfg(target_os = "macos")]
Rwh::MacOS(h) => {
//TODO: figure out when this is needed, and how to get that without `objc`
//use objc::{msg_send, runtime::Object, sel, sel_impl};
//let ns_view = if h.ns_view.is_null() {
// let ns_window = h.ns_window as *mut Object;
// unsafe { msg_send![ns_window, contentView] }
//} else {
// h.ns_view
//};
Surface {
#[cfg(feature = "gfx-backend-vulkan")]
vulkan: self
.instance
.vulkan
.as_ref()
.map(|inst| inst.create_surface_from_ns_view(h.ns_view)),
metal: self
.instance
.metal
.create_surface_from_nsview(h.ns_view, cfg!(debug_assertions)),
}
}
#[cfg(all(unix, not(target_os = "ios"), not(target_os = "macos")))]
Rwh::Xlib(h) => Surface {
vulkan: self
.instance
.vulkan
.as_ref()
.map(|inst| inst.create_surface_from_xlib(h.display as _, h.window as _)),
},
#[cfg(all(unix, not(target_os = "ios"), not(target_os = "macos")))]
Rwh::Wayland(h) => Surface {
vulkan: self
.instance
.vulkan
.as_ref()
.map(|inst| inst.create_surface_from_wayland(h.display, h.surface)),
},
#[cfg(windows)]
Rwh::Windows(h) => Surface {
vulkan: self
.instance
.vulkan
.as_ref()
.map(|inst| inst.create_surface_from_hwnd(std::ptr::null_mut(), h.hwnd)),
dx12: self
.instance
.dx12
.as_ref()
.map(|inst| inst.create_surface_from_hwnd(h.hwnd)),
dx11: self.instance.dx11.create_surface_from_hwnd(h.hwnd),
},
_ => panic!("Unsupported window handle"),
};
let mut token = Token::root();
self.surfaces
.register_identity(id_in, surface, &mut token)
}
pub fn enumerate_adapters(&self, inputs: AdapterInputs<Input<G, AdapterId>>) -> Vec<AdapterId> {
let instance = &self.instance;
let mut token = Token::root();