Avoid allocating a DynamicState every frame (#1008)

* Avoid allocating a DynamicState every frame

* Don't mutate the DynamicState

* Undo DynamicState::dynamic_state doc change
This commit is contained in:
Andrew Hickman 2018-08-04 13:38:33 +01:00 committed by Pierre Krieger
parent 175763a953
commit b0832072fc
7 changed files with 72 additions and 45 deletions

View File

@ -5,6 +5,7 @@
- Allow custom implementations of `RenderPassDesc` to specify `VK_SUBPASS_EXTERNAL` as a dependency source or destination - Allow custom implementations of `RenderPassDesc` to specify `VK_SUBPASS_EXTERNAL` as a dependency source or destination
- Added `vulkano_win::create_vk_surface` which allows creating a surface safely without taking ownership of - Added `vulkano_win::create_vk_surface` which allows creating a surface safely without taking ownership of
the window. the window.
- `AutoCommandBufferBuilder::draw` and friends no longer consume the `DynamicState` argument, allowing reuse between calls.
# Version 0.9.0 (2018-03-13) # Version 0.9.0 (2018-03-13)

View File

@ -145,6 +145,16 @@ fn main() {
let mut previous_frame_end = Box::new(tex_future) as Box<GpuFuture>; let mut previous_frame_end = Box::new(tex_future) as Box<GpuFuture>;
let mut dynamic_state = vulkano::command_buffer::DynamicState {
line_width: None,
viewports: Some(vec![vulkano::pipeline::viewport::Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0 .. 1.0,
}]),
scissors: None,
};
loop { loop {
previous_frame_end.cleanup_finished(); previous_frame_end.cleanup_finished();
if recreate_swapchain { if recreate_swapchain {
@ -166,6 +176,12 @@ fn main() {
framebuffers = None; framebuffers = None;
dynamic_state.viewports = Some(vec![vulkano::pipeline::viewport::Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0 .. 1.0,
}]);
recreate_swapchain = false; recreate_swapchain = false;
} }
@ -194,15 +210,7 @@ fn main() {
framebuffers.as_ref().unwrap()[image_num].clone(), false, framebuffers.as_ref().unwrap()[image_num].clone(), false,
vec![[0.0, 0.0, 1.0, 1.0].into()]).unwrap() vec![[0.0, 0.0, 1.0, 1.0].into()]).unwrap()
.draw(pipeline.clone(), .draw(pipeline.clone(),
vulkano::command_buffer::DynamicState { &dynamic_state,
line_width: None,
viewports: Some(vec![vulkano::pipeline::viewport::Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0 .. 1.0,
}]),
scissors: None,
},
vertex_buffer.clone(), vertex_buffer.clone(),
set.clone(), ()).unwrap() set.clone(), ()).unwrap()
.end_render_pass().unwrap() .end_render_pass().unwrap()

View File

@ -448,7 +448,7 @@ fn main() {
).unwrap() ).unwrap()
.draw( .draw(
graphics_pipeline.clone(), graphics_pipeline.clone(),
DynamicState::none(), &DynamicState::none(),
vertex_buffer.clone(), vertex_buffer.clone(),
(), (),
(), (),

View File

@ -138,6 +138,16 @@ fn main() {
let mut previous_frame = Box::new(vulkano::sync::now(device.clone())) as Box<GpuFuture>; let mut previous_frame = Box::new(vulkano::sync::now(device.clone())) as Box<GpuFuture>;
let rotation_start = std::time::Instant::now(); let rotation_start = std::time::Instant::now();
let mut dynamic_state = vulkano::command_buffer::DynamicState {
line_width: None,
viewports: Some(vec![vulkano::pipeline::viewport::Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0 .. 1.0,
}]),
scissors: None,
};
loop { loop {
previous_frame.cleanup_finished(); previous_frame.cleanup_finished();
@ -165,6 +175,12 @@ fn main() {
proj = cgmath::perspective(cgmath::Rad(std::f32::consts::FRAC_PI_2), { dimensions[0] as f32 / dimensions[1] as f32 }, 0.01, 100.0); proj = cgmath::perspective(cgmath::Rad(std::f32::consts::FRAC_PI_2), { dimensions[0] as f32 / dimensions[1] as f32 }, 0.01, 100.0);
dynamic_state.viewports = Some(vec![vulkano::pipeline::viewport::Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0 .. 1.0,
}]);
recreate_swapchain = false; recreate_swapchain = false;
} }
@ -216,15 +232,7 @@ fn main() {
]).unwrap() ]).unwrap()
.draw_indexed( .draw_indexed(
pipeline.clone(), pipeline.clone(),
vulkano::command_buffer::DynamicState { &dynamic_state,
line_width: None,
viewports: Some(vec![vulkano::pipeline::viewport::Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0 .. 1.0,
}]),
scissors: None,
},
(vertex_buffer.clone(), normals_buffer.clone()), (vertex_buffer.clone(), normals_buffer.clone()),
index_buffer.clone(), set.clone(), ()).unwrap() index_buffer.clone(), set.clone(), ()).unwrap()
.end_render_pass().unwrap() .end_render_pass().unwrap()

View File

@ -330,6 +330,16 @@ void main() {
// that, we store the submission of the previous frame here. // that, we store the submission of the previous frame here.
let mut previous_frame_end = Box::new(now(device.clone())) as Box<GpuFuture>; let mut previous_frame_end = Box::new(now(device.clone())) as Box<GpuFuture>;
let mut dynamic_state = DynamicState {
line_width: None,
viewports: Some(vec![Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0 .. 1.0,
}]),
scissors: None,
};
loop { loop {
// It is important to call this function from time to time, otherwise resources will keep // It is important to call this function from time to time, otherwise resources will keep
// accumulating and you will eventually reach an out of memory error. // accumulating and you will eventually reach an out of memory error.
@ -359,6 +369,12 @@ void main() {
framebuffers = None; framebuffers = None;
dynamic_state.viewports = Some(vec![Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0 .. 1.0,
}]);
recreate_swapchain = false; recreate_swapchain = false;
} }
@ -416,16 +432,7 @@ void main() {
// The last two parameters contain the list of resources to pass to the shaders. // The last two parameters contain the list of resources to pass to the shaders.
// Since we used an `EmptyPipeline` object, the objects have to be `()`. // Since we used an `EmptyPipeline` object, the objects have to be `()`.
.draw(pipeline.clone(), .draw(pipeline.clone(),
DynamicState { &dynamic_state,
line_width: None,
// TODO: Find a way to do this without having to dynamically allocate a Vec every frame.
viewports: Some(vec![Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
depth_range: 0.0 .. 1.0,
}]),
scissors: None,
},
vertex_buffer.clone(), (), ()) vertex_buffer.clone(), (), ())
.unwrap() .unwrap()

View File

@ -981,7 +981,7 @@ impl<P> AutoCommandBufferBuilder<P> {
} }
#[inline] #[inline]
pub fn draw<V, Gp, S, Pc>(mut self, pipeline: Gp, dynamic: DynamicState, vertices: V, sets: S, pub fn draw<V, Gp, S, Pc>(mut self, pipeline: Gp, dynamic: &DynamicState, vertices: V, sets: S,
constants: Pc) constants: Pc)
-> Result<Self, DrawError> -> Result<Self, DrawError>
where Gp: GraphicsPipelineAbstract + VertexSource<V> + Send + Sync + 'static + Clone, // TODO: meh for Clone where Gp: GraphicsPipelineAbstract + VertexSource<V> + Send + Sync + 'static + Clone, // TODO: meh for Clone
@ -991,7 +991,7 @@ impl<P> AutoCommandBufferBuilder<P> {
// TODO: must check that pipeline is compatible with render pass // TODO: must check that pipeline is compatible with render pass
self.ensure_inside_render_pass_inline(&pipeline)?; self.ensure_inside_render_pass_inline(&pipeline)?;
check_dynamic_state_validity(&pipeline, &dynamic)?; check_dynamic_state_validity(&pipeline, dynamic)?;
check_push_constants_validity(&pipeline, &constants)?; check_push_constants_validity(&pipeline, &constants)?;
check_descriptor_sets_validity(&pipeline, &sets)?; check_descriptor_sets_validity(&pipeline, &sets)?;
let vb_infos = check_vertex_buffers(&pipeline, vertices)?; let vb_infos = check_vertex_buffers(&pipeline, vertices)?;
@ -1005,7 +1005,7 @@ impl<P> AutoCommandBufferBuilder<P> {
let dynamic = self.state_cacher.dynamic_state(dynamic); let dynamic = self.state_cacher.dynamic_state(dynamic);
push_constants(&mut self.inner, pipeline.clone(), constants); push_constants(&mut self.inner, pipeline.clone(), constants);
set_state(&mut self.inner, dynamic); set_state(&mut self.inner, &dynamic);
descriptor_sets(&mut self.inner, descriptor_sets(&mut self.inner,
&mut self.state_cacher, &mut self.state_cacher,
true, true,
@ -1026,7 +1026,7 @@ impl<P> AutoCommandBufferBuilder<P> {
} }
#[inline] #[inline]
pub fn draw_indexed<V, Gp, S, Pc, Ib, I>(mut self, pipeline: Gp, dynamic: DynamicState, pub fn draw_indexed<V, Gp, S, Pc, Ib, I>(mut self, pipeline: Gp, dynamic: &DynamicState,
vertices: V, index_buffer: Ib, sets: S, constants: Pc) vertices: V, index_buffer: Ib, sets: S, constants: Pc)
-> Result<Self, DrawIndexedError> -> Result<Self, DrawIndexedError>
where Gp: GraphicsPipelineAbstract + VertexSource<V> + Send + Sync + 'static + Clone, // TODO: meh for Clone where Gp: GraphicsPipelineAbstract + VertexSource<V> + Send + Sync + 'static + Clone, // TODO: meh for Clone
@ -1039,7 +1039,7 @@ impl<P> AutoCommandBufferBuilder<P> {
self.ensure_inside_render_pass_inline(&pipeline)?; self.ensure_inside_render_pass_inline(&pipeline)?;
let ib_infos = check_index_buffer(self.device(), &index_buffer)?; let ib_infos = check_index_buffer(self.device(), &index_buffer)?;
check_dynamic_state_validity(&pipeline, &dynamic)?; check_dynamic_state_validity(&pipeline, dynamic)?;
check_push_constants_validity(&pipeline, &constants)?; check_push_constants_validity(&pipeline, &constants)?;
check_descriptor_sets_validity(&pipeline, &sets)?; check_descriptor_sets_validity(&pipeline, &sets)?;
let vb_infos = check_vertex_buffers(&pipeline, vertices)?; let vb_infos = check_vertex_buffers(&pipeline, vertices)?;
@ -1059,7 +1059,7 @@ impl<P> AutoCommandBufferBuilder<P> {
let dynamic = self.state_cacher.dynamic_state(dynamic); let dynamic = self.state_cacher.dynamic_state(dynamic);
push_constants(&mut self.inner, pipeline.clone(), constants); push_constants(&mut self.inner, pipeline.clone(), constants);
set_state(&mut self.inner, dynamic); set_state(&mut self.inner, &dynamic);
descriptor_sets(&mut self.inner, descriptor_sets(&mut self.inner,
&mut self.state_cacher, &mut self.state_cacher,
true, true,
@ -1079,7 +1079,7 @@ impl<P> AutoCommandBufferBuilder<P> {
} }
#[inline] #[inline]
pub fn draw_indirect<V, Gp, S, Pc, Ib>(mut self, pipeline: Gp, dynamic: DynamicState, pub fn draw_indirect<V, Gp, S, Pc, Ib>(mut self, pipeline: Gp, dynamic: &DynamicState,
vertices: V, indirect_buffer: Ib, sets: S, constants: Pc) vertices: V, indirect_buffer: Ib, sets: S, constants: Pc)
-> Result<Self, DrawIndirectError> -> Result<Self, DrawIndirectError>
where Gp: GraphicsPipelineAbstract + VertexSource<V> + Send + Sync + 'static + Clone, // TODO: meh for Clone where Gp: GraphicsPipelineAbstract + VertexSource<V> + Send + Sync + 'static + Clone, // TODO: meh for Clone
@ -1094,7 +1094,7 @@ impl<P> AutoCommandBufferBuilder<P> {
// TODO: must check that pipeline is compatible with render pass // TODO: must check that pipeline is compatible with render pass
self.ensure_inside_render_pass_inline(&pipeline)?; self.ensure_inside_render_pass_inline(&pipeline)?;
check_dynamic_state_validity(&pipeline, &dynamic)?; check_dynamic_state_validity(&pipeline, dynamic)?;
check_push_constants_validity(&pipeline, &constants)?; check_push_constants_validity(&pipeline, &constants)?;
check_descriptor_sets_validity(&pipeline, &sets)?; check_descriptor_sets_validity(&pipeline, &sets)?;
let vb_infos = check_vertex_buffers(&pipeline, vertices)?; let vb_infos = check_vertex_buffers(&pipeline, vertices)?;
@ -1110,7 +1110,7 @@ impl<P> AutoCommandBufferBuilder<P> {
let dynamic = self.state_cacher.dynamic_state(dynamic); let dynamic = self.state_cacher.dynamic_state(dynamic);
push_constants(&mut self.inner, pipeline.clone(), constants); push_constants(&mut self.inner, pipeline.clone(), constants);
set_state(&mut self.inner, dynamic); set_state(&mut self.inner, &dynamic);
descriptor_sets(&mut self.inner, descriptor_sets(&mut self.inner,
&mut self.state_cacher, &mut self.state_cacher,
true, true,
@ -1304,7 +1304,7 @@ unsafe fn push_constants<P, Pl, Pc>(destination: &mut SyncCommandBufferBuilder<P
} }
// Shortcut function to change the state of the pipeline. // Shortcut function to change the state of the pipeline.
unsafe fn set_state<P>(destination: &mut SyncCommandBufferBuilder<P>, dynamic: DynamicState) { unsafe fn set_state<P>(destination: &mut SyncCommandBufferBuilder<P>, dynamic: &DynamicState) {
if let Some(line_width) = dynamic.line_width { if let Some(line_width) = dynamic.line_width {
destination.set_line_width(line_width); destination.set_line_width(line_width);
} }

View File

@ -91,14 +91,17 @@ impl StateCacher {
/// ///
/// This function also updates the state cacher. The state cacher assumes that the state /// This function also updates the state cacher. The state cacher assumes that the state
/// changes are going to be performed after this function returns. /// changes are going to be performed after this function returns.
pub fn dynamic_state(&mut self, mut incoming: DynamicState) -> DynamicState { pub fn dynamic_state(&mut self, incoming: &DynamicState) -> DynamicState {
let mut changed = DynamicState::none();
macro_rules! cmp { macro_rules! cmp {
($field:ident) => ( ($field:ident) => (
if self.dynamic_state.$field == incoming.$field { if self.dynamic_state.$field != incoming.$field {
incoming.$field = None; changed.$field = incoming.$field.clone();
} else if incoming.$field.is_some() { if incoming.$field.is_some() {
self.dynamic_state.$field = incoming.$field.clone(); self.dynamic_state.$field = incoming.$field.clone();
} }
}
); );
} }
@ -106,7 +109,7 @@ impl StateCacher {
cmp!(viewports); cmp!(viewports);
cmp!(scissors); cmp!(scissors);
incoming changed
} }
/// Starts the process of comparing a list of descriptor sets to the descriptor sets currently /// Starts the process of comparing a list of descriptor sets to the descriptor sets currently