Merge pull request #449 from tomaka/state-cache-vbs

Cache the current vertex buffers in StateCacheLayer
This commit is contained in:
tomaka 2017-05-10 19:39:27 +02:00 committed by GitHub
commit b81d204df9
3 changed files with 119 additions and 11 deletions

View File

@ -45,6 +45,8 @@ pub struct StateCacheLayer<I> {
compute_pipeline: vk::Pipeline,
// The graphics pipeline currently bound. 0 if nothing bound.
graphics_pipeline: vk::Pipeline,
// The latest bind vertex buffers command.
vertex_buffers: Option<commands_raw::CmdBindVertexBuffersHash>,
}
impl<I> StateCacheLayer<I> {
@ -58,6 +60,7 @@ impl<I> StateCacheLayer<I> {
dynamic_state: DynamicState::none(),
compute_pipeline: 0,
graphics_pipeline: 0,
vertex_buffers: None,
}
}
@ -118,6 +121,7 @@ unsafe impl<Pl, I, O> AddCommand<commands_raw::CmdBindPipeline<Pl>> for StateCac
dynamic_state: DynamicState::none(),
graphics_pipeline: self.graphics_pipeline,
compute_pipeline: self.compute_pipeline,
vertex_buffers: self.vertex_buffers,
})
}
}
@ -137,6 +141,7 @@ unsafe impl<Cb, I, O> AddCommand<commands_raw::CmdExecuteCommands<Cb>> for State
dynamic_state: DynamicState::none(),
compute_pipeline: 0,
graphics_pipeline: 0,
vertex_buffers: None,
})
}
}
@ -170,6 +175,39 @@ unsafe impl<I, O> AddCommand<commands_raw::CmdSetState> for StateCacheLayer<I>
dynamic_state: self.dynamic_state,
graphics_pipeline: self.graphics_pipeline,
compute_pipeline: self.compute_pipeline,
vertex_buffers: self.vertex_buffers,
})
}
}
unsafe impl<I, O, B> AddCommand<commands_raw::CmdBindVertexBuffers<B>> for StateCacheLayer<I>
where I: AddCommand<commands_raw::CmdBindVertexBuffers<B>, Out = O>
{
type Out = StateCacheLayer<O>;
#[inline]
fn add(mut self, mut command: commands_raw::CmdBindVertexBuffers<B>)
-> Result<Self::Out, CommandAddError>
{
match &mut self.vertex_buffers {
&mut Some(ref mut curr) => {
if *curr != *command.hash() {
let new_hash = command.hash().clone();
command.diff(curr);
*curr = new_hash;
}
},
curr @ &mut None => {
*curr = Some(command.hash().clone());
}
};
Ok(StateCacheLayer {
inner: self.inner.add(command)?,
dynamic_state: self.dynamic_state,
graphics_pipeline: self.graphics_pipeline,
compute_pipeline: self.compute_pipeline,
vertex_buffers: self.vertex_buffers,
})
}
}
@ -200,6 +238,7 @@ macro_rules! pass_through {
dynamic_state: self.dynamic_state,
graphics_pipeline: self.graphics_pipeline,
compute_pipeline: self.compute_pipeline,
vertex_buffers: self.vertex_buffers,
})
}
}
@ -209,7 +248,6 @@ macro_rules! pass_through {
pass_through!((Rp, F), commands_raw::CmdBeginRenderPass<Rp, F>);
pass_through!((S, Pl), commands_raw::CmdBindDescriptorSets<S, Pl>);
pass_through!((B), commands_raw::CmdBindIndexBuffer<B>);
pass_through!((V), commands_raw::CmdBindVertexBuffers<V>);
pass_through!((S, D), commands_raw::CmdBlitImage<S, D>);
pass_through!((), commands_raw::CmdClearAttachments);
pass_through!((S, D), commands_raw::CmdCopyBuffer<S, D>);

View File

@ -23,16 +23,31 @@ use vk;
/// Command that binds vertex buffers to a command buffer.
pub struct CmdBindVertexBuffers<B> {
// Raw handles of the buffers to bind.
raw_buffers: SmallVec<[vk::Buffer; 4]>,
// Raw offsets of the buffers to bind.
offsets: SmallVec<[vk::DeviceSize; 4]>,
// Actual raw state of the command.
state: CmdBindVertexBuffersHash,
// Offset within `state` to start binding.
first_binding: u32,
// Number of bindings to pass to the command.
num_bindings: u32,
// The device of the buffer, so that we can compare it with the command buffer's device.
device: Arc<Device>,
// The buffers to bind. Unused, but we need to keep it alive.
buffers: B,
}
/// A "hash" of the bind vertex buffers command. Can be compared with a previous hash to determine
/// if two commands are identical.
///
/// > **Note**: This is not *actually* a hash, because there's no collision. If two objects are
/// > equal, then the commands are always identical.
#[derive(Clone, PartialEq, Eq)]
pub struct CmdBindVertexBuffersHash {
// Raw handles of the buffers to bind.
raw_buffers: SmallVec<[vk::Buffer; 4]>,
// Raw offsets of the buffers to bind.
offsets: SmallVec<[vk::DeviceSize; 4]>,
}
impl<B> CmdBindVertexBuffers<B> {
/// Builds the command.
#[inline]
@ -43,19 +58,65 @@ impl<B> CmdBindVertexBuffers<B> {
let (buffers, _, _) = source_def.decode(&buffers);
let device = buffers.first().unwrap().buffer.device().clone();
let raw_buffers = buffers.iter().map(|b| b.buffer.internal_object()).collect();
let raw_buffers: SmallVec<_> = buffers.iter().map(|b| b.buffer.internal_object()).collect();
let offsets = buffers.iter().map(|b| b.offset as vk::DeviceSize).collect();
(device, raw_buffers, offsets)
};
let num_bindings = raw_buffers.len() as u32;
CmdBindVertexBuffers {
raw_buffers: raw_buffers,
offsets: offsets,
state: CmdBindVertexBuffersHash {
raw_buffers: raw_buffers,
offsets: offsets,
},
first_binding: 0,
num_bindings: num_bindings,
device: device,
buffers: buffers,
}
}
/// Returns a hash that represents the command.
#[inline]
pub fn hash(&self) -> &CmdBindVertexBuffersHash {
&self.state
}
/// Modifies the command so that it doesn't bind vertex buffers that were already bound by a
/// previous command with the given hash.
///
/// Note that this doesn't modify the hash of the command.
pub fn diff(&mut self, previous_hash: &CmdBindVertexBuffersHash) {
// We don't want to split the command into multiple ones, so we just trim the list of
// vertex buffers at the start and at the end.
let left_trim = self.state.raw_buffers
.iter()
.zip(self.state.offsets.iter())
.zip(previous_hash.raw_buffers.iter())
.zip(previous_hash.offsets.iter())
.position(|(((&cur_buf, &cur_off), &prev_buf), &prev_off)| {
cur_buf != prev_buf || cur_off != prev_off
})
.map(|p| p as u32)
.unwrap_or(self.num_bindings);
let right_trim = self.state.raw_buffers
.iter()
.zip(self.state.offsets.iter().rev())
.zip(previous_hash.raw_buffers.iter().rev())
.zip(previous_hash.offsets.iter().rev())
.position(|(((&cur_buf, &cur_off), &prev_buf), &prev_off)| {
cur_buf != prev_buf || cur_off != prev_off
})
.map(|p| p as u32)
.unwrap_or(self.num_bindings);
self.first_binding = left_trim;
debug_assert!(left_trim <= self.state.raw_buffers.len() as u32);
self.num_bindings = (self.state.raw_buffers.len() as u32 - left_trim).saturating_sub(right_trim);
}
}
unsafe impl<B> DeviceOwned for CmdBindVertexBuffers<B> {
@ -73,10 +134,19 @@ unsafe impl<'a, P, B> AddCommand<&'a CmdBindVertexBuffers<B>> for UnsafeCommandB
#[inline]
fn add(self, command: &'a CmdBindVertexBuffers<B>) -> Result<Self::Out, CommandAddError> {
unsafe {
debug_assert_eq!(command.state.offsets.len(), command.state.raw_buffers.len());
debug_assert!(command.num_bindings <= command.state.raw_buffers.len() as u32);
if command.num_bindings == 0 {
return Ok(self);
}
let vk = self.device().pointers();
let cmd = self.internal_object();
vk.CmdBindVertexBuffers(cmd, 0, command.raw_buffers.len() as u32,
command.raw_buffers.as_ptr(), command.offsets.as_ptr());
vk.CmdBindVertexBuffers(cmd, command.first_binding,
command.num_bindings,
command.state.raw_buffers[command.first_binding as usize..].as_ptr(),
command.state.offsets[command.first_binding as usize..].as_ptr());
}
Ok(self)

View File

@ -15,7 +15,7 @@ pub use self::begin_render_pass::CmdBeginRenderPass;
pub use self::bind_index_buffer::CmdBindIndexBuffer;
pub use self::bind_descriptor_sets::{CmdBindDescriptorSets, CmdBindDescriptorSetsError};
pub use self::bind_pipeline::{CmdBindPipeline, CmdBindPipelineSys};
pub use self::bind_vertex_buffers::CmdBindVertexBuffers;
pub use self::bind_vertex_buffers::{CmdBindVertexBuffers, CmdBindVertexBuffersHash};
pub use self::blit_image::{CmdBlitImage, CmdBlitImageError};
pub use self::clear_attachments::CmdClearAttachments;
pub use self::copy_buffer::{CmdCopyBuffer, CmdCopyBufferError};