trace: support pipelines, refactor destruction sequence

This commit is contained in:
Dzmitry Malyshau 2020-04-29 15:36:50 -04:00 committed by Dzmitry Malyshau
parent 018417f174
commit 854c1be035
7 changed files with 278 additions and 72 deletions

1
Cargo.lock generated
View File

@ -840,6 +840,7 @@ version = "0.1.0"
dependencies = [
"env_logger",
"log",
"raw-window-handle",
"ron",
"wgpu-core",
"wgpu-types",

View File

@ -17,6 +17,7 @@ publish = false
[dependencies]
env_logger = "0.7"
log = "0.4"
raw-window-handle = "0.3"
ron = "0.5"
winit = { version = "0.22", optional = true }

View File

@ -10,6 +10,7 @@ use std::{
fs::File,
marker::PhantomData,
path::{Path, PathBuf},
ptr,
};
macro_rules! gfx_select {
@ -41,7 +42,26 @@ impl Label {
fn as_ptr(&self) -> *const std::os::raw::c_char {
match self.0 {
Some(ref c_string) => c_string.as_ptr(),
None => std::ptr::null(),
None => ptr::null(),
}
}
}
struct OwnedProgrammableStage {
desc: wgc::pipeline::ProgrammableStageDescriptor,
#[allow(dead_code)]
entry_point: CString,
}
impl From<trace::ProgrammableStageDescriptor> for OwnedProgrammableStage {
fn from(stage: trace::ProgrammableStageDescriptor) -> Self {
let entry_point = CString::new(stage.entry_point.as_str()).unwrap();
OwnedProgrammableStage {
desc: wgc::pipeline::ProgrammableStageDescriptor {
module: stage.module,
entry_point: entry_point.as_ptr(),
},
entry_point,
}
}
}
@ -178,7 +198,7 @@ fn main() {
log::info!("Found {} actions", actions.len());
#[cfg(feature = "winit")]
let event_loop = {
let mut event_loop = {
log::info!("Creating a window");
EventLoop::new()
};
@ -194,12 +214,10 @@ fn main() {
let mut command_buffer_id_manager = wgc::hub::IdentityManager::default();
#[cfg(feature = "winit")]
let (_size, surface) = {
let size = window.inner_size();
let id = wgc::id::TypedId::zip(1, 0, wgt::Backend::Empty);
let surface = global.instance_create_surface(window.raw_window_handle(), id);
(size, surface)
};
let surface = global.instance_create_surface(
raw_window_handle::HasRawWindowHandle::raw_window_handle(&window),
wgc::id::TypedId::zip(0, 1, wgt::Backend::Empty),
);
let adapter = global
.pick_adapter(
@ -272,15 +290,19 @@ fn main() {
A::DestroySampler(id) => {
gfx_select!(device => global.sampler_destroy(id));
}
A::CreateSwapChain { id: _, desc } => {
A::CreateSwapChain { id, desc } => {
#[cfg(feature = "winit")]
{
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));
}
#[cfg(not(feature = "winit"))]
let _ = desc;
{
let _ = (id, desc);
panic!("Enable `winit` feature to work with swapchains");
}
}
A::GetSwapChainTexture { id, parent_id } => {
gfx_select!(device => global.swap_chain_get_next_texture(parent_id, id)).unwrap();
@ -374,6 +396,58 @@ fn main() {
A::DestroyShaderModule(id) => {
gfx_select!(device => global.shader_module_destroy(id));
}
A::CreateComputePipeline { id, desc } => {
let cs_stage = OwnedProgrammableStage::from(desc.compute_stage);
gfx_select!(device => global.device_create_compute_pipeline(
device,
&wgc::pipeline::ComputePipelineDescriptor {
layout: desc.layout,
compute_stage: cs_stage.desc,
},
id));
}
A::DestroyComputePipeline(id) => {
gfx_select!(device => global.compute_pipeline_destroy(id));
}
A::CreateRenderPipeline { id, desc } => {
let vs_stage = OwnedProgrammableStage::from(desc.vertex_stage);
let fs_stage = desc.fragment_stage.map(OwnedProgrammableStage::from);
let vertex_buffers = desc
.vertex_state
.vertex_buffers
.iter()
.map(|vb| wgc::pipeline::VertexBufferLayoutDescriptor {
array_stride: vb.array_stride,
step_mode: vb.step_mode,
attributes: vb.attributes.as_ptr(),
attributes_length: vb.attributes.len(),
})
.collect::<Vec<_>>();
gfx_select!(device => global.device_create_render_pipeline(
device,
&wgc::pipeline::RenderPipelineDescriptor {
layout: desc.layout,
vertex_stage: vs_stage.desc,
fragment_stage: fs_stage.as_ref().map_or(ptr::null(), |s| &s.desc),
primitive_topology: desc.primitive_topology,
rasterization_state: desc.rasterization_state.as_ref().map_or(ptr::null(), |rs| rs),
color_states: desc.color_states.as_ptr(),
color_states_length: desc.color_states.len(),
depth_stencil_state: desc.depth_stencil_state.as_ref().map_or(ptr::null(), |ds| ds),
vertex_state: wgc::pipeline::VertexStateDescriptor {
index_format: desc.vertex_state.index_format,
vertex_buffers: vertex_buffers.as_ptr(),
vertex_buffers_length: vertex_buffers.len(),
},
sample_count: desc.sample_count,
sample_mask: desc.sample_mask,
alpha_to_coverage_enabled: desc.alpha_to_coverage_enabled,
},
id));
}
A::DestroyRenderPipeline(id) => {
gfx_select!(device => global.render_pipeline_destroy(id));
}
A::WriteBuffer { id, data, range } => {
let bin = std::fs::read(dir.join(data)).unwrap();
let size = (range.end - range.start) as usize;
@ -383,7 +457,7 @@ fn main() {
let encoder = gfx_select!(device => global.device_create_command_encoder(
device,
&wgt::CommandEncoderDescriptor {
label: std::ptr::null(),
label: ptr::null(),
},
command_buffer_id_manager.alloc(device.backend())
));
@ -392,4 +466,33 @@ fn main() {
}
}
}
log::info!("Done replay");
#[cfg(feature = "winit")]
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 = match event {
Event::WindowEvent { event, .. } => match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(VirtualKeyCode::Escape),
state: ElementState::Pressed,
..
},
..
}
| WindowEvent::CloseRequested => ControlFlow::Exit,
_ => ControlFlow::Poll,
},
_ => ControlFlow::Poll,
}
},
);
}

View File

@ -8,6 +8,8 @@ use crate::{
track::TrackerSet,
FastHashMap, RefCount, Stored, SubmissionIndex,
};
#[cfg(feature = "trace")]
use crate::device::trace;
use copyless::VecHelper as _;
use gfx_descriptor::{DescriptorAllocator, DescriptorSet};
@ -288,6 +290,8 @@ impl<B: GfxBackend> LifetimeTracker<B> {
&mut self,
global: &Global<G>,
trackers: &Mutex<TrackerSet>,
#[cfg(feature = "trace")]
trace: Option<&Mutex<trace::Trace>>,
token: &mut Token<super::Device<B>>,
) {
let hub = B::hub(global);
@ -298,6 +302,8 @@ impl<B: GfxBackend> LifetimeTracker<B> {
for id in self.suspected_resources.bind_groups.drain(..) {
if trackers.bind_groups.remove_abandoned(id) {
#[cfg(feature = "trace")]
trace.map(|t| t.lock().add(trace::Action::DestroyBindGroup(id)));
hub.bind_groups.free_id(id);
let res = guard.remove(id).unwrap();
@ -332,6 +338,8 @@ impl<B: GfxBackend> LifetimeTracker<B> {
for id in self.suspected_resources.texture_views.drain(..) {
if trackers.views.remove_abandoned(id) {
#[cfg(feature = "trace")]
trace.map(|t| t.lock().add(trace::Action::DestroyTextureView(id)));
hub.texture_views.free_id(id);
let res = guard.remove(id).unwrap();
@ -360,6 +368,8 @@ impl<B: GfxBackend> LifetimeTracker<B> {
for id in self.suspected_resources.textures.drain(..) {
if trackers.textures.remove_abandoned(id) {
#[cfg(feature = "trace")]
trace.map(|t| t.lock().add(trace::Action::DestroyTexture(id)));
hub.textures.free_id(id);
let res = guard.remove(id).unwrap();
@ -380,6 +390,8 @@ impl<B: GfxBackend> LifetimeTracker<B> {
for id in self.suspected_resources.samplers.drain(..) {
if trackers.samplers.remove_abandoned(id) {
#[cfg(feature = "trace")]
trace.map(|t| t.lock().add(trace::Action::DestroySampler(id)));
hub.samplers.free_id(id);
let res = guard.remove(id).unwrap();
@ -400,6 +412,8 @@ impl<B: GfxBackend> LifetimeTracker<B> {
for id in self.suspected_resources.buffers.drain(..) {
if trackers.buffers.remove_abandoned(id) {
#[cfg(feature = "trace")]
trace.map(|t| t.lock().add(trace::Action::DestroyBuffer(id)));
hub.buffers.free_id(id);
let res = guard.remove(id).unwrap();
log::debug!("Buffer {:?} is detached", id);
@ -421,6 +435,8 @@ impl<B: GfxBackend> LifetimeTracker<B> {
for id in self.suspected_resources.compute_pipelines.drain(..) {
if trackers.compute_pipes.remove_abandoned(id) {
#[cfg(feature = "trace")]
trace.map(|t| t.lock().add(trace::Action::DestroyComputePipeline(id)));
hub.compute_pipelines.free_id(id);
let res = guard.remove(id).unwrap();
@ -441,6 +457,8 @@ impl<B: GfxBackend> LifetimeTracker<B> {
for id in self.suspected_resources.render_pipelines.drain(..) {
if trackers.render_pipes.remove_abandoned(id) {
#[cfg(feature = "trace")]
trace.map(|t| t.lock().add(trace::Action::DestroyRenderPipeline(id)));
hub.render_pipelines.free_id(id);
let res = guard.remove(id).unwrap();
@ -465,6 +483,8 @@ impl<B: GfxBackend> LifetimeTracker<B> {
{
//Note: this has to happen after all the suspected pipelines are destroyed
if ref_count.load() == 1 {
#[cfg(feature = "trace")]
trace.map(|t| t.lock().add(trace::Action::DestroyPipelineLayout(id)));
hub.pipeline_layouts.free_id(id);
let layout = guard.remove(id).unwrap();
self.free_resources.pipeline_layouts.push(layout.raw);

View File

@ -281,7 +281,13 @@ impl<B: GfxBackend> Device<B> {
) -> Vec<BufferMapPendingCallback> {
let mut life_tracker = self.lock_life(token);
life_tracker.triage_suspected(global, &self.trackers, token);
life_tracker.triage_suspected(
global,
&self.trackers,
#[cfg(feature = "trace")]
self.trace.as_ref(),
token,
);
life_tracker.triage_mapped(global, token);
life_tracker.triage_framebuffers(global, &mut *self.framebuffers.lock(), token);
let _last_done = life_tracker.triage_submissions(&self.raw, force_wait);
@ -575,6 +581,15 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let id = hub.buffers.register_identity(id_in, buffer, &mut token);
log::info!("Created mapped buffer {:?} with {:?}", id, desc);
#[cfg(feature = "trace")]
match device.trace {
Some(ref trace) => trace.lock().add(trace::Action::CreateBuffer {
id,
desc: desc.map_label(own_label),
}),
None => (),
};
device
.trackers
.lock()
@ -692,14 +707,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
};
let (device_guard, mut token) = hub.devices.read(&mut token);
let device = &device_guard[device_id];
#[cfg(feature = "trace")]
match device.trace {
Some(ref trace) => trace.lock().add(trace::Action::DestroyBuffer(buffer_id)),
None => (),
};
device
device_guard[device_id]
.lock_life(&mut token)
.suspected_resources
.buffers
@ -752,14 +760,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
};
let (device_guard, mut token) = hub.devices.read(&mut token);
let device = &device_guard[device_id];
#[cfg(feature = "trace")]
match device.trace {
Some(ref trace) => trace.lock().add(trace::Action::DestroyTexture(texture_id)),
None => (),
};
device
device_guard[device_id]
.lock_life(&mut token)
.suspected_resources
.textures
@ -882,16 +883,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
};
let (device_guard, mut token) = hub.devices.read(&mut token);
let device = &device_guard[device_id];
#[cfg(feature = "trace")]
match device.trace {
Some(ref trace) => trace
.lock()
.add(trace::Action::DestroyTextureView(texture_view_id)),
None => (),
};
device
device_guard[device_id]
.lock_life(&mut token)
.suspected_resources
.texture_views
@ -967,14 +959,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
};
let (device_guard, mut token) = hub.devices.read(&mut token);
let device = &device_guard[device_id];
#[cfg(feature = "trace")]
match device.trace {
Some(ref trace) => trace.lock().add(trace::Action::DestroySampler(sampler_id)),
None => (),
};
device
device_guard[device_id]
.lock_life(&mut token)
.suspected_resources
.samplers
@ -1153,16 +1138,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
};
let (device_guard, mut token) = hub.devices.read(&mut token);
let device = &device_guard[device_id];
#[cfg(feature = "trace")]
match device.trace {
Some(ref trace) => trace
.lock()
.add(trace::Action::DestroyPipelineLayout(pipeline_layout_id)),
None => (),
};
device
device_guard[device_id]
.lock_life(&mut token)
.suspected_resources
.pipeline_layouts
@ -1437,16 +1413,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
};
let (device_guard, mut token) = hub.devices.read(&mut token);
let device = &device_guard[device_id];
#[cfg(feature = "trace")]
match device.trace {
Some(ref trace) => trace
.lock()
.add(trace::Action::DestroyBindGroup(bind_group_id)),
None => (),
};
device
device_guard[device_id]
.lock_life(&mut token)
.suspected_resources
.bind_groups
@ -1505,7 +1472,6 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.add(trace::Action::DestroyShaderModule(shader_module_id)),
None => (),
};
unsafe {
device.raw.destroy_shader_module(module.raw);
}
@ -2065,8 +2031,38 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
life_guard: LifeGuard::new(),
};
hub.render_pipelines
.register_identity(id_in, pipeline, &mut token)
let id = hub.render_pipelines
.register_identity(id_in, pipeline, &mut token);
#[cfg(feature = "trace")]
match device.trace {
Some(ref trace) => trace.lock().add(trace::Action::CreateRenderPipeline {
id,
desc: trace::RenderPipelineDescriptor {
layout: desc.layout,
vertex_stage: trace::ProgrammableStageDescriptor::new(&desc.vertex_stage),
fragment_stage: unsafe { desc.fragment_stage.as_ref() }.map(trace::ProgrammableStageDescriptor::new),
primitive_topology: desc.primitive_topology,
rasterization_state: unsafe { desc.rasterization_state.as_ref() }.cloned(),
color_states: color_states.to_vec(),
depth_stencil_state: depth_stencil_state.cloned(),
vertex_state: trace::VertexStateDescriptor {
index_format: desc.vertex_state.index_format,
vertex_buffers: desc_vbs.iter().map(|vbl| trace::VertexBufferLayoutDescriptor {
array_stride: vbl.array_stride,
step_mode: vbl.step_mode,
attributes: unsafe { slice::from_raw_parts(vbl.attributes, vbl.attributes_length) }
.iter().cloned().collect(),
}).collect(),
},
sample_count: desc.sample_count,
sample_mask: desc.sample_mask,
alpha_to_coverage_enabled: desc.alpha_to_coverage_enabled,
},
}),
None => (),
};
id
}
pub fn render_pipeline_destroy<B: GfxBackend>(&self, render_pipeline_id: id::RenderPipelineId) {
@ -2151,8 +2147,21 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
},
life_guard: LifeGuard::new(),
};
hub.compute_pipelines
.register_identity(id_in, pipeline, &mut token)
let id = hub.compute_pipelines
.register_identity(id_in, pipeline, &mut token);
#[cfg(feature = "trace")]
match device.trace {
Some(ref trace) => trace.lock().add(trace::Action::CreateComputePipeline {
id,
desc: trace::ComputePipelineDescriptor {
layout: desc.layout,
compute_stage: trace::ProgrammableStageDescriptor::new(&desc.compute_stage),
},
}),
None => (),
};
id
}
pub fn compute_pipeline_destroy<B: GfxBackend>(

View File

@ -29,6 +29,68 @@ pub enum BindingResource {
TextureView(id::TextureViewId),
}
#[derive(Debug)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
pub struct ProgrammableStageDescriptor {
pub module: id::ShaderModuleId,
pub entry_point: String,
}
#[cfg(feature = "trace")]
impl ProgrammableStageDescriptor {
pub fn new(desc: &crate::pipeline::ProgrammableStageDescriptor) -> Self {
ProgrammableStageDescriptor {
module: desc.module,
entry_point: unsafe { std::ffi::CStr::from_ptr(desc.entry_point) }
.to_string_lossy()
.to_string(),
}
}
}
#[derive(Debug)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
pub struct ComputePipelineDescriptor {
pub layout: id::PipelineLayoutId,
pub compute_stage: ProgrammableStageDescriptor,
}
#[derive(Debug)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
pub struct VertexBufferLayoutDescriptor {
pub array_stride: wgt::BufferAddress,
pub step_mode: wgt::InputStepMode,
pub attributes: Vec<wgt::VertexAttributeDescriptor>,
}
#[derive(Debug)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
pub struct VertexStateDescriptor {
pub index_format: wgt::IndexFormat,
pub vertex_buffers: Vec<VertexBufferLayoutDescriptor>,
}
#[derive(Debug)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
pub struct RenderPipelineDescriptor {
pub layout: id::PipelineLayoutId,
pub vertex_stage: ProgrammableStageDescriptor,
pub fragment_stage: Option<ProgrammableStageDescriptor>,
pub primitive_topology: wgt::PrimitiveTopology,
pub rasterization_state: Option<wgt::RasterizationStateDescriptor>,
pub color_states: Vec<wgt::ColorStateDescriptor>,
pub depth_stencil_state: Option<wgt::DepthStencilStateDescriptor>,
pub vertex_state: VertexStateDescriptor,
pub sample_count: u32,
pub sample_mask: u32,
pub alpha_to_coverage_enabled: bool,
}
#[derive(Debug)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
@ -89,6 +151,16 @@ pub enum Action {
data: FileName,
},
DestroyShaderModule(id::ShaderModuleId),
CreateComputePipeline {
id: id::ComputePipelineId,
desc: ComputePipelineDescriptor,
},
DestroyComputePipeline(id::ComputePipelineId),
CreateRenderPipeline {
id: id::RenderPipelineId,
desc: RenderPipelineDescriptor,
},
DestroyRenderPipeline(id::RenderPipelineId),
WriteBuffer {
id: id::BufferId,
data: FileName,

View File

@ -151,7 +151,7 @@ impl SurfaceId {
}
}
impl SwapChainId {
pub(crate) fn to_surface_id(self) -> SurfaceId {
pub fn to_surface_id(self) -> SurfaceId {
let (index, epoch, _) = self.unzip();
Id::zip(index, epoch, Backend::Empty)
}