mirror of
https://github.com/gfx-rs/wgpu.git
synced 2026-04-22 03:02:01 -04:00
376 lines
15 KiB
Rust
376 lines
15 KiB
Rust
/*! 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.
|
|
!*/
|
|
#![cfg(not(target_arch = "wasm32"))]
|
|
#![warn(unsafe_op_in_unsafe_fn)]
|
|
|
|
use wgc::device::trace;
|
|
|
|
use std::{borrow::Cow, fs, path::Path};
|
|
|
|
pub trait GlobalPlay {
|
|
fn encode_commands<A: wgc::hal_api::HalApi>(
|
|
&self,
|
|
encoder: wgc::id::CommandEncoderId,
|
|
commands: Vec<trace::Command>,
|
|
) -> wgc::id::CommandBufferId;
|
|
fn process<A: wgc::hal_api::HalApi>(
|
|
&self,
|
|
device: wgc::id::DeviceId,
|
|
action: trace::Action,
|
|
dir: &Path,
|
|
comb_manager: &mut wgc::identity::IdentityManager<wgc::id::markers::CommandBuffer>,
|
|
);
|
|
}
|
|
|
|
impl GlobalPlay for wgc::global::Global {
|
|
fn encode_commands<A: wgc::hal_api::HalApi>(
|
|
&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::<A>(
|
|
encoder, src, src_offset, dst, dst_offset, size,
|
|
)
|
|
.unwrap(),
|
|
trace::Command::CopyBufferToTexture { src, dst, size } => self
|
|
.command_encoder_copy_buffer_to_texture::<A>(encoder, &src, &dst, &size)
|
|
.unwrap(),
|
|
trace::Command::CopyTextureToBuffer { src, dst, size } => self
|
|
.command_encoder_copy_texture_to_buffer::<A>(encoder, &src, &dst, &size)
|
|
.unwrap(),
|
|
trace::Command::CopyTextureToTexture { src, dst, size } => self
|
|
.command_encoder_copy_texture_to_texture::<A>(encoder, &src, &dst, &size)
|
|
.unwrap(),
|
|
trace::Command::ClearBuffer { dst, offset, size } => self
|
|
.command_encoder_clear_buffer::<A>(encoder, dst, offset, size)
|
|
.unwrap(),
|
|
trace::Command::ClearTexture {
|
|
dst,
|
|
subresource_range,
|
|
} => self
|
|
.command_encoder_clear_texture::<A>(encoder, dst, &subresource_range)
|
|
.unwrap(),
|
|
trace::Command::WriteTimestamp {
|
|
query_set_id,
|
|
query_index,
|
|
} => self
|
|
.command_encoder_write_timestamp::<A>(encoder, query_set_id, query_index)
|
|
.unwrap(),
|
|
trace::Command::ResolveQuerySet {
|
|
query_set_id,
|
|
start_query,
|
|
query_count,
|
|
destination,
|
|
destination_offset,
|
|
} => self
|
|
.command_encoder_resolve_query_set::<A>(
|
|
encoder,
|
|
query_set_id,
|
|
start_query,
|
|
query_count,
|
|
destination,
|
|
destination_offset,
|
|
)
|
|
.unwrap(),
|
|
trace::Command::PushDebugGroup(marker) => self
|
|
.command_encoder_push_debug_group::<A>(encoder, &marker)
|
|
.unwrap(),
|
|
trace::Command::PopDebugGroup => {
|
|
self.command_encoder_pop_debug_group::<A>(encoder).unwrap()
|
|
}
|
|
trace::Command::InsertDebugMarker(marker) => self
|
|
.command_encoder_insert_debug_marker::<A>(encoder, &marker)
|
|
.unwrap(),
|
|
trace::Command::RunComputePass {
|
|
base,
|
|
timestamp_writes,
|
|
} => {
|
|
self.command_encoder_run_compute_pass_impl::<A>(
|
|
encoder,
|
|
base.as_ref(),
|
|
timestamp_writes.as_ref(),
|
|
)
|
|
.unwrap();
|
|
}
|
|
trace::Command::RunRenderPass {
|
|
base,
|
|
target_colors,
|
|
target_depth_stencil,
|
|
timestamp_writes,
|
|
occlusion_query_set_id,
|
|
} => {
|
|
self.command_encoder_run_render_pass_impl::<A>(
|
|
encoder,
|
|
base.as_ref(),
|
|
&target_colors,
|
|
target_depth_stencil.as_ref(),
|
|
timestamp_writes.as_ref(),
|
|
occlusion_query_set_id,
|
|
)
|
|
.unwrap();
|
|
}
|
|
}
|
|
}
|
|
let (cmd_buf, error) = self
|
|
.command_encoder_finish::<A>(encoder, &wgt::CommandBufferDescriptor { label: None });
|
|
if let Some(e) = error {
|
|
panic!("{e}");
|
|
}
|
|
cmd_buf
|
|
}
|
|
|
|
fn process<A: wgc::hal_api::HalApi>(
|
|
&self,
|
|
device: wgc::id::DeviceId,
|
|
action: trace::Action,
|
|
dir: &Path,
|
|
comb_manager: &mut wgc::identity::IdentityManager<wgc::id::markers::CommandBuffer>,
|
|
) {
|
|
use wgc::device::trace::Action;
|
|
log::debug!("action {:?}", action);
|
|
//TODO: find a way to force ID perishing without excessive `maintain()` calls.
|
|
match action {
|
|
Action::Init { .. } => {
|
|
panic!("Unexpected Action::Init: has to be the first action only")
|
|
}
|
|
Action::ConfigureSurface { .. }
|
|
| Action::Present(_)
|
|
| Action::DiscardSurfaceTexture(_) => {
|
|
panic!("Unexpected Surface action: winit feature is not enabled")
|
|
}
|
|
Action::CreateBuffer(id, desc) => {
|
|
self.device_maintain_ids::<A>(device).unwrap();
|
|
let (_, error) = self.device_create_buffer::<A>(device, &desc, Some(id));
|
|
if let Some(e) = error {
|
|
panic!("{e}");
|
|
}
|
|
}
|
|
Action::FreeBuffer(id) => {
|
|
self.buffer_destroy::<A>(id).unwrap();
|
|
}
|
|
Action::DestroyBuffer(id) => {
|
|
self.buffer_drop::<A>(id, true);
|
|
}
|
|
Action::CreateTexture(id, desc) => {
|
|
self.device_maintain_ids::<A>(device).unwrap();
|
|
let (_, error) = self.device_create_texture::<A>(device, &desc, Some(id));
|
|
if let Some(e) = error {
|
|
panic!("{e}");
|
|
}
|
|
}
|
|
Action::FreeTexture(id) => {
|
|
self.texture_destroy::<A>(id).unwrap();
|
|
}
|
|
Action::DestroyTexture(id) => {
|
|
self.texture_drop::<A>(id, true);
|
|
}
|
|
Action::CreateTextureView {
|
|
id,
|
|
parent_id,
|
|
desc,
|
|
} => {
|
|
self.device_maintain_ids::<A>(device).unwrap();
|
|
let (_, error) = self.texture_create_view::<A>(parent_id, &desc, Some(id));
|
|
if let Some(e) = error {
|
|
panic!("{e}");
|
|
}
|
|
}
|
|
Action::DestroyTextureView(id) => {
|
|
self.texture_view_drop::<A>(id, true).unwrap();
|
|
}
|
|
Action::CreateSampler(id, desc) => {
|
|
self.device_maintain_ids::<A>(device).unwrap();
|
|
let (_, error) = self.device_create_sampler::<A>(device, &desc, Some(id));
|
|
if let Some(e) = error {
|
|
panic!("{e}");
|
|
}
|
|
}
|
|
Action::DestroySampler(id) => {
|
|
self.sampler_drop::<A>(id);
|
|
}
|
|
Action::GetSurfaceTexture { id, parent_id } => {
|
|
self.device_maintain_ids::<A>(device).unwrap();
|
|
self.surface_get_current_texture::<A>(parent_id, Some(id))
|
|
.unwrap()
|
|
.texture_id
|
|
.unwrap();
|
|
}
|
|
Action::CreateBindGroupLayout(id, desc) => {
|
|
let (_, error) = self.device_create_bind_group_layout::<A>(device, &desc, Some(id));
|
|
if let Some(e) = error {
|
|
panic!("{e}");
|
|
}
|
|
}
|
|
Action::DestroyBindGroupLayout(id) => {
|
|
self.bind_group_layout_drop::<A>(id);
|
|
}
|
|
Action::CreatePipelineLayout(id, desc) => {
|
|
self.device_maintain_ids::<A>(device).unwrap();
|
|
let (_, error) = self.device_create_pipeline_layout::<A>(device, &desc, Some(id));
|
|
if let Some(e) = error {
|
|
panic!("{e}");
|
|
}
|
|
}
|
|
Action::DestroyPipelineLayout(id) => {
|
|
self.pipeline_layout_drop::<A>(id);
|
|
}
|
|
Action::CreateBindGroup(id, desc) => {
|
|
self.device_maintain_ids::<A>(device).unwrap();
|
|
let (_, error) = self.device_create_bind_group::<A>(device, &desc, Some(id));
|
|
if let Some(e) = error {
|
|
panic!("{e}");
|
|
}
|
|
}
|
|
Action::DestroyBindGroup(id) => {
|
|
self.bind_group_drop::<A>(id);
|
|
}
|
|
Action::CreateShaderModule { id, desc, data } => {
|
|
log::debug!("Creating shader from {}", data);
|
|
let code = fs::read_to_string(dir.join(&data)).unwrap();
|
|
let source = if data.ends_with(".wgsl") {
|
|
wgc::pipeline::ShaderModuleSource::Wgsl(Cow::Owned(code.clone()))
|
|
} else if data.ends_with(".ron") {
|
|
let module = ron::de::from_str(&code).unwrap();
|
|
wgc::pipeline::ShaderModuleSource::Naga(module)
|
|
} else {
|
|
panic!("Unknown shader {}", data);
|
|
};
|
|
let (_, error) =
|
|
self.device_create_shader_module::<A>(device, &desc, source, Some(id));
|
|
if let Some(e) = error {
|
|
println!("shader compilation error:\n---{code}\n---\n{e}");
|
|
}
|
|
}
|
|
Action::DestroyShaderModule(id) => {
|
|
self.shader_module_drop::<A>(id);
|
|
}
|
|
Action::CreateComputePipeline {
|
|
id,
|
|
desc,
|
|
implicit_context,
|
|
} => {
|
|
self.device_maintain_ids::<A>(device).unwrap();
|
|
let implicit_ids =
|
|
implicit_context
|
|
.as_ref()
|
|
.map(|ic| wgc::device::ImplicitPipelineIds {
|
|
root_id: Some(ic.root_id),
|
|
group_ids: wgc::id::as_option_slice(&ic.group_ids),
|
|
});
|
|
let (_, error) =
|
|
self.device_create_compute_pipeline::<A>(device, &desc, Some(id), implicit_ids);
|
|
if let Some(e) = error {
|
|
panic!("{e}");
|
|
}
|
|
}
|
|
Action::DestroyComputePipeline(id) => {
|
|
self.compute_pipeline_drop::<A>(id);
|
|
}
|
|
Action::CreateRenderPipeline {
|
|
id,
|
|
desc,
|
|
implicit_context,
|
|
} => {
|
|
self.device_maintain_ids::<A>(device).unwrap();
|
|
let implicit_ids =
|
|
implicit_context
|
|
.as_ref()
|
|
.map(|ic| wgc::device::ImplicitPipelineIds {
|
|
root_id: Some(ic.root_id),
|
|
group_ids: wgc::id::as_option_slice(&ic.group_ids),
|
|
});
|
|
let (_, error) =
|
|
self.device_create_render_pipeline::<A>(device, &desc, Some(id), implicit_ids);
|
|
if let Some(e) = error {
|
|
panic!("{e}");
|
|
}
|
|
}
|
|
Action::DestroyRenderPipeline(id) => {
|
|
self.render_pipeline_drop::<A>(id);
|
|
}
|
|
Action::CreateRenderBundle { id, desc, base } => {
|
|
let bundle =
|
|
wgc::command::RenderBundleEncoder::new(&desc, device, Some(base)).unwrap();
|
|
let (_, error) = self.render_bundle_encoder_finish::<A>(
|
|
bundle,
|
|
&wgt::RenderBundleDescriptor { label: desc.label },
|
|
Some(id),
|
|
);
|
|
if let Some(e) = error {
|
|
panic!("{e}");
|
|
}
|
|
}
|
|
Action::DestroyRenderBundle(id) => {
|
|
self.render_bundle_drop::<A>(id);
|
|
}
|
|
Action::CreateQuerySet { id, desc } => {
|
|
self.device_maintain_ids::<A>(device).unwrap();
|
|
let (_, error) = self.device_create_query_set::<A>(device, &desc, Some(id));
|
|
if let Some(e) = error {
|
|
panic!("{e}");
|
|
}
|
|
}
|
|
Action::DestroyQuerySet(id) => {
|
|
self.query_set_drop::<A>(id);
|
|
}
|
|
Action::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::<A>(device.transmute(), id, range.start, &bin)
|
|
.unwrap();
|
|
} else {
|
|
self.device_wait_for_buffer::<A>(device, id).unwrap();
|
|
self.device_set_buffer_sub_data::<A>(device, id, range.start, &bin[..size])
|
|
.unwrap();
|
|
}
|
|
}
|
|
Action::WriteTexture {
|
|
to,
|
|
data,
|
|
layout,
|
|
size,
|
|
} => {
|
|
let bin = std::fs::read(dir.join(data)).unwrap();
|
|
self.queue_write_texture::<A>(device.transmute(), &to, &bin, &layout, &size)
|
|
.unwrap();
|
|
}
|
|
Action::Submit(_index, ref commands) if commands.is_empty() => {
|
|
self.queue_submit::<A>(device.transmute(), &[]).unwrap();
|
|
}
|
|
Action::Submit(_index, commands) => {
|
|
let (encoder, error) = self.device_create_command_encoder::<A>(
|
|
device,
|
|
&wgt::CommandEncoderDescriptor { label: None },
|
|
Some(comb_manager.process(device.backend()).transmute()),
|
|
);
|
|
if let Some(e) = error {
|
|
panic!("{e}");
|
|
}
|
|
let cmdbuf = self.encode_commands::<A>(encoder, commands);
|
|
self.queue_submit::<A>(device.transmute(), &[cmdbuf])
|
|
.unwrap();
|
|
}
|
|
}
|
|
}
|
|
}
|