summaryrefslogtreecommitdiffstats
path: root/gfx/wgpu/player/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--gfx/wgpu/player/src/bin/play.rs179
-rw-r--r--gfx/wgpu/player/src/lib.rs313
2 files changed, 492 insertions, 0 deletions
diff --git a/gfx/wgpu/player/src/bin/play.rs b/gfx/wgpu/player/src/bin/play.rs
new file mode 100644
index 0000000000..522d7e64b7
--- /dev/null
+++ b/gfx/wgpu/player/src/bin/play.rs
@@ -0,0 +1,179 @@
+/* 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/. */
+
+/*! This is a player for WebGPU traces.
+!*/
+
+use player::{GlobalPlay as _, IdentityPassThroughFactory};
+use wgc::{device::trace, gfx_select};
+
+use std::{
+ fs,
+ path::{Path, PathBuf},
+};
+
+fn main() {
+ #[cfg(feature = "winit")]
+ use winit::{event_loop::EventLoop, window::WindowBuilder};
+
+ wgpu_subscriber::initialize_default_subscriber(
+ std::env::var("WGPU_CHROME_TRACE")
+ .as_ref()
+ .map(Path::new)
+ .ok(),
+ );
+
+ #[cfg(feature = "renderdoc")]
+ #[cfg_attr(feature = "winit", allow(unused))]
+ let mut rd = renderdoc::RenderDoc::<renderdoc::V110>::new()
+ .expect("Failed to connect to RenderDoc: are you running without it?");
+
+ //TODO: setting for the backend bits
+ //TODO: setting for the target frame, or controls
+
+ let dir = match std::env::args().nth(1) {
+ Some(arg) if Path::new(&arg).is_dir() => PathBuf::from(arg),
+ _ => panic!("Provide the dir path as the parameter"),
+ };
+
+ log::info!("Loading trace '{:?}'", dir);
+ let file = fs::File::open(dir.join(trace::FILE_NAME)).unwrap();
+ let mut actions: Vec<trace::Action> = ron::de::from_reader(file).unwrap();
+ actions.reverse(); // allows us to pop from the top
+ log::info!("Found {} actions", actions.len());
+
+ #[cfg(feature = "winit")]
+ let event_loop = {
+ log::info!("Creating a window");
+ EventLoop::new()
+ };
+ #[cfg(feature = "winit")]
+ let window = WindowBuilder::new()
+ .with_title("wgpu player")
+ .with_resizable(false)
+ .build(&event_loop)
+ .unwrap();
+
+ let global = wgc::hub::Global::new(
+ "player",
+ IdentityPassThroughFactory,
+ wgt::BackendBit::PRIMARY,
+ );
+ let mut command_buffer_id_manager = wgc::hub::IdentityManager::default();
+
+ #[cfg(feature = "winit")]
+ let surface =
+ global.instance_create_surface(&window, wgc::id::TypedId::zip(0, 1, wgt::Backend::Empty));
+
+ let device = match actions.pop() {
+ Some(trace::Action::Init { desc, backend }) => {
+ log::info!("Initializing the device for backend: {:?}", backend);
+ let adapter = global
+ .request_adapter(
+ &wgc::instance::RequestAdapterOptions {
+ power_preference: wgt::PowerPreference::LowPower,
+ #[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");
+
+ let info = gfx_select!(adapter => global.adapter_get_info(adapter)).unwrap();
+ log::info!("Picked '{}'", info.name);
+ let id = wgc::id::TypedId::zip(1, 0, backend);
+ let (_, error) = gfx_select!(adapter => global.adapter_request_device(
+ adapter,
+ &desc,
+ None,
+ id
+ ));
+ if let Some(e) = error {
+ panic!("{:?}", e);
+ }
+ id
+ }
+ _ => panic!("Expected Action::Init"),
+ };
+
+ log::info!("Executing actions");
+ #[cfg(not(feature = "winit"))]
+ {
+ #[cfg(feature = "renderdoc")]
+ rd.start_frame_capture(std::ptr::null(), std::ptr::null());
+
+ while let Some(action) = actions.pop() {
+ gfx_select!(device => global.process(device, action, &dir, &mut command_buffer_id_manager));
+ }
+
+ #[cfg(feature = "renderdoc")]
+ rd.end_frame_capture(std::ptr::null(), std::ptr::null());
+ gfx_select!(device => global.device_poll(device, true)).unwrap();
+ }
+ #[cfg(feature = "winit")]
+ {
+ use winit::{
+ event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
+ event_loop::ControlFlow,
+ };
+
+ 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));
+ }
+ _ => {}
+ }
+ });
+ }
+}
diff --git a/gfx/wgpu/player/src/lib.rs b/gfx/wgpu/player/src/lib.rs
new file mode 100644
index 0000000000..140ff70503
--- /dev/null
+++ b/gfx/wgpu/player/src/lib.rs
@@ -0,0 +1,313 @@
+/* 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/. */
+
+/*! This is a player library for WebGPU traces.
+ *
+ * # Notes
+ * - we call device_maintain_ids() before creating any refcounted resource,
+ * which is basically everything except for BGL and shader modules,
+ * so that we don't accidentally try to use the same ID.
+!*/
+
+use wgc::device::trace;
+
+use std::{borrow::Cow, fmt::Debug, fs, marker::PhantomData, path::Path};
+
+#[derive(Debug)]
+pub 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) {}
+}
+
+pub 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 {}
+
+pub trait GlobalPlay {
+ fn encode_commands<B: wgc::hub::GfxBackend>(
+ &self,
+ encoder: wgc::id::CommandEncoderId,
+ commands: Vec<trace::Command>,
+ ) -> wgc::id::CommandBufferId;
+ fn process<B: wgc::hub::GfxBackend>(
+ &self,
+ device: wgc::id::DeviceId,
+ action: trace::Action,
+ dir: &Path,
+ comb_manager: &mut wgc::hub::IdentityManager,
+ );
+}
+
+impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
+ fn encode_commands<B: wgc::hub::GfxBackend>(
+ &self,
+ encoder: wgc::id::CommandEncoderId,
+ commands: Vec<trace::Command>,
+ ) -> wgc::id::CommandBufferId {
+ for command in commands {
+ match command {
+ trace::Command::CopyBufferToBuffer {
+ src,
+ src_offset,
+ dst,
+ dst_offset,
+ size,
+ } => self
+ .command_encoder_copy_buffer_to_buffer::<B>(
+ encoder, src, src_offset, dst, dst_offset, size,
+ )
+ .unwrap(),
+ trace::Command::CopyBufferToTexture { src, dst, size } => self
+ .command_encoder_copy_buffer_to_texture::<B>(encoder, &src, &dst, &size)
+ .unwrap(),
+ trace::Command::CopyTextureToBuffer { src, dst, size } => self
+ .command_encoder_copy_texture_to_buffer::<B>(encoder, &src, &dst, &size)
+ .unwrap(),
+ trace::Command::CopyTextureToTexture { src, dst, size } => self
+ .command_encoder_copy_texture_to_texture::<B>(encoder, &src, &dst, &size)
+ .unwrap(),
+ trace::Command::RunComputePass { base } => {
+ self.command_encoder_run_compute_pass_impl::<B>(encoder, base.as_ref())
+ .unwrap();
+ }
+ trace::Command::RunRenderPass {
+ base,
+ target_colors,
+ target_depth_stencil,
+ } => {
+ self.command_encoder_run_render_pass_impl::<B>(
+ encoder,
+ base.as_ref(),
+ &target_colors,
+ target_depth_stencil.as_ref(),
+ )
+ .unwrap();
+ }
+ }
+ }
+ let (cmd_buf, error) = self
+ .command_encoder_finish::<B>(encoder, &wgt::CommandBufferDescriptor { label: None });
+ if let Some(e) = error {
+ panic!("{:?}", e);
+ }
+ cmd_buf
+ }
+
+ fn process<B: wgc::hub::GfxBackend>(
+ &self,
+ device: wgc::id::DeviceId,
+ action: trace::Action,
+ dir: &Path,
+ comb_manager: &mut wgc::hub::IdentityManager,
+ ) {
+ use wgc::device::trace::Action as A;
+ log::info!("action {:?}", action);
+ match action {
+ A::Init { .. } => panic!("Unexpected Action::Init: has to be the first action only"),
+ A::CreateSwapChain { .. } | A::PresentSwapChain(_) => {
+ panic!("Unexpected SwapChain action: winit feature is not enabled")
+ }
+ A::CreateBuffer(id, desc) => {
+ self.device_maintain_ids::<B>(device).unwrap();
+ let (_, error) = self.device_create_buffer::<B>(device, &desc, id);
+ if let Some(e) = error {
+ panic!("{:?}", e);
+ }
+ }
+ A::FreeBuffer(id) => {
+ self.buffer_destroy::<B>(id).unwrap();
+ }
+ A::DestroyBuffer(id) => {
+ self.buffer_drop::<B>(id, true);
+ }
+ A::CreateTexture(id, desc) => {
+ self.device_maintain_ids::<B>(device).unwrap();
+ let (_, error) = self.device_create_texture::<B>(device, &desc, id);
+ if let Some(e) = error {
+ panic!("{:?}", e);
+ }
+ }
+ A::FreeTexture(id) => {
+ self.texture_destroy::<B>(id).unwrap();
+ }
+ A::DestroyTexture(id) => {
+ self.texture_drop::<B>(id, true);
+ }
+ A::CreateTextureView {
+ id,
+ parent_id,
+ desc,
+ } => {
+ self.device_maintain_ids::<B>(device).unwrap();
+ let (_, error) = self.texture_create_view::<B>(parent_id, &desc, id);
+ if let Some(e) = error {
+ panic!("{:?}", e);
+ }
+ }
+ A::DestroyTextureView(id) => {
+ self.texture_view_drop::<B>(id).unwrap();
+ }
+ A::CreateSampler(id, desc) => {
+ self.device_maintain_ids::<B>(device).unwrap();
+ let (_, error) = self.device_create_sampler::<B>(device, &desc, id);
+ if let Some(e) = error {
+ panic!("{:?}", e);
+ }
+ }
+ A::DestroySampler(id) => {
+ self.sampler_drop::<B>(id);
+ }
+ A::GetSwapChainTexture { id, parent_id } => {
+ if let Some(id) = id {
+ self.swap_chain_get_current_texture_view::<B>(parent_id, id)
+ .unwrap()
+ .view_id
+ .unwrap();
+ }
+ }
+ A::CreateBindGroupLayout(id, desc) => {
+ let (_, error) = self.device_create_bind_group_layout::<B>(device, &desc, id);
+ if let Some(e) = error {
+ panic!("{:?}", e);
+ }
+ }
+ A::DestroyBindGroupLayout(id) => {
+ self.bind_group_layout_drop::<B>(id);
+ }
+ A::CreatePipelineLayout(id, desc) => {
+ self.device_maintain_ids::<B>(device).unwrap();
+ let (_, error) = self.device_create_pipeline_layout::<B>(device, &desc, id);
+ if let Some(e) = error {
+ panic!("{:?}", e);
+ }
+ }
+ A::DestroyPipelineLayout(id) => {
+ self.pipeline_layout_drop::<B>(id);
+ }
+ A::CreateBindGroup(id, desc) => {
+ self.device_maintain_ids::<B>(device).unwrap();
+ let (_, error) = self.device_create_bind_group::<B>(device, &desc, id);
+ if let Some(e) = error {
+ panic!("{:?}", e);
+ }
+ }
+ A::DestroyBindGroup(id) => {
+ self.bind_group_drop::<B>(id);
+ }
+ A::CreateShaderModule { id, data, label } => {
+ let desc = wgc::pipeline::ShaderModuleDescriptor {
+ source: if data.ends_with(".wgsl") {
+ let code = fs::read_to_string(dir.join(data)).unwrap();
+ wgc::pipeline::ShaderModuleSource::Wgsl(Cow::Owned(code))
+ } else {
+ let byte_vec = fs::read(dir.join(data)).unwrap();
+ let spv = byte_vec
+ .chunks(4)
+ .map(|c| u32::from_le_bytes([c[0], c[1], c[2], c[3]]))
+ .collect::<Vec<_>>();
+ wgc::pipeline::ShaderModuleSource::SpirV(Cow::Owned(spv))
+ },
+ label,
+ };
+ let (_, error) = self.device_create_shader_module::<B>(device, &desc, id);
+ if let Some(e) = error {
+ panic!("{:?}", e);
+ }
+ }
+ A::DestroyShaderModule(id) => {
+ self.shader_module_drop::<B>(id);
+ }
+ A::CreateComputePipeline(id, desc) => {
+ self.device_maintain_ids::<B>(device).unwrap();
+ let (_, _, error) =
+ self.device_create_compute_pipeline::<B>(device, &desc, id, None);
+ if let Some(e) = error {
+ panic!("{:?}", e);
+ }
+ }
+ A::DestroyComputePipeline(id) => {
+ self.compute_pipeline_drop::<B>(id);
+ }
+ A::CreateRenderPipeline(id, desc) => {
+ self.device_maintain_ids::<B>(device).unwrap();
+ let (_, _, error) =
+ self.device_create_render_pipeline::<B>(device, &desc, id, None);
+ if let Some(e) = error {
+ panic!("{:?}", e);
+ }
+ }
+ A::DestroyRenderPipeline(id) => {
+ self.render_pipeline_drop::<B>(id);
+ }
+ A::CreateRenderBundle { id, desc, base } => {
+ let bundle =
+ wgc::command::RenderBundleEncoder::new(&desc, device, Some(base)).unwrap();
+ let (_, error) = self.render_bundle_encoder_finish::<B>(
+ bundle,
+ &wgt::RenderBundleDescriptor { label: desc.label },
+ id,
+ );
+ if let Some(e) = error {
+ panic!("{:?}", e);
+ }
+ }
+ A::DestroyRenderBundle(id) => {
+ self.render_bundle_drop::<B>(id);
+ }
+ A::WriteBuffer {
+ id,
+ data,
+ range,
+ queued,
+ } => {
+ let bin = std::fs::read(dir.join(data)).unwrap();
+ let size = (range.end - range.start) as usize;
+ if queued {
+ self.queue_write_buffer::<B>(device, id, range.start, &bin)
+ .unwrap();
+ } else {
+ self.device_wait_for_buffer::<B>(device, id).unwrap();
+ self.device_set_buffer_sub_data::<B>(device, id, range.start, &bin[..size])
+ .unwrap();
+ }
+ }
+ A::WriteTexture {
+ to,
+ data,
+ layout,
+ size,
+ } => {
+ let bin = std::fs::read(dir.join(data)).unwrap();
+ self.queue_write_texture::<B>(device, &to, &bin, &layout, &size)
+ .unwrap();
+ }
+ A::Submit(_index, commands) => {
+ let (encoder, error) = self.device_create_command_encoder::<B>(
+ device,
+ &wgt::CommandEncoderDescriptor { label: None },
+ comb_manager.alloc(device.backend()),
+ );
+ if let Some(e) = error {
+ panic!("{:?}", e);
+ }
+ let cmdbuf = self.encode_commands::<B>(encoder, commands);
+ self.queue_submit::<B>(device, &[cmdbuf]).unwrap();
+ }
+ }
+ }
+}