Fixes to the CpuBufferPool

This commit is contained in:
Pierre Krieger 2017-07-19 19:19:40 +02:00
parent bb5cfddccc
commit 3fe108336c
3 changed files with 95 additions and 46 deletions

View File

@ -88,14 +88,8 @@ fn main() {
let view = cgmath::Matrix4::look_at(cgmath::Point3::new(0.3, 0.3, 1.0), cgmath::Point3::new(0.0, 0.0, 0.0), cgmath::Vector3::new(0.0, -1.0, 0.0));
let scale = cgmath::Matrix4::from_scale(0.01);
let uniform_buffer = vulkano::buffer::cpu_access::CpuAccessibleBuffer::<vs::ty::Data>
::from_data(device.clone(), vulkano::buffer::BufferUsage::all(), Some(queue.family()),
vs::ty::Data {
world : <cgmath::Matrix4<f32> as cgmath::SquareMatrix>::identity().into(),
view : (view * scale).into(),
proj : proj.into(),
})
.expect("failed to create buffer");
let uniform_buffer = vulkano::buffer::cpu_pool::CpuBufferPool::<vs::ty::Data>
::new(device.clone(), vulkano::buffer::BufferUsage::all(), Some(queue.family()));
let vs = vs::Shader::load(device.clone()).expect("failed to create shader module");
let fs = fs::Shader::load(device.clone()).expect("failed to create shader module");
@ -138,11 +132,6 @@ fn main() {
.build(device.clone())
.unwrap());
let set = Arc::new(vulkano::descriptor::descriptor_set::PersistentDescriptorSet::start(pipeline.clone(), 0)
.add_buffer(uniform_buffer.clone()).unwrap()
.build().unwrap()
);
let framebuffers = images.iter().map(|image| {
Arc::new(vulkano::framebuffer::Framebuffer::start(renderpass.clone())
.add(image.clone()).unwrap()
@ -157,18 +146,24 @@ fn main() {
loop {
previous_frame.cleanup_finished();
{
// aquiring write lock for the uniform buffer
let mut buffer_content = uniform_buffer.write().unwrap();
let uniform_buffer_subbuffer = {
let elapsed = rotation_start.elapsed();
let rotation = elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1_000_000_000.0;
let rotation = cgmath::Matrix3::from_angle_y(cgmath::Rad(rotation as f32));
// since write lock implementd Deref and DerefMut traits,
// we can update content directly
buffer_content.world = cgmath::Matrix4::from(rotation).into();
}
let uniform_data = vs::ty::Data {
world : cgmath::Matrix4::from(rotation).into(),
view : (view * scale).into(),
proj : proj.into(),
};
uniform_buffer.next(uniform_data)
};
let set = Arc::new(vulkano::descriptor::descriptor_set::PersistentDescriptorSet::start(pipeline.clone(), 0)
.add_buffer(uniform_buffer_subbuffer).unwrap()
.build().unwrap()
);
let (image_num, acquire_future) = vulkano::swapchain::acquire_next_image(swapchain.clone(), None).unwrap();

View File

@ -11,6 +11,7 @@
#![allow(deprecated)]
use smallvec::SmallVec;
use std::cmp;
use std::iter;
use std::marker::PhantomData;
use std::mem;
@ -134,8 +135,12 @@ pub struct CpuBufferPoolChunk<T, A>
// Index of the subbuffer within `buffer`. In number of elements.
index: usize,
// Size of the subbuffer in number of elements.
len: usize,
// Number of bytes to add to `index * mem::size_of::<T>()` to obtain the start of the data in
// the buffer. Necessary for alignment purposes.
align_offset: usize,
// Size of the subbuffer in number of elements, as requested by the user.
requested_len: usize,
// Necessary to make it compile.
marker: PhantomData<Box<T>>,
@ -386,32 +391,55 @@ impl<T, A> CpuBufferPool<T, A>
};
let mut chunks_in_use = current_buffer.chunks_in_use.lock().unwrap();
let data_len = data.len();
// Find a suitable offset, or return if none available.
let index = {
let next_index = {
// Number of elements requested by the user.
let requested_len = data.len();
// Find a suitable offset and len, or returns if none available.
let (index, occupied_len, align_offset) = {
let (tentative_index, tentative_len, tentative_align_offset) = {
// Since the only place that touches `next_index` is this code, and since we
// own a mutex lock to the buffer, it means that `next_index` can't be accessed
// concurrently.
// TODO: ^ eventually should be put inside the mutex
current_buffer
let idx = current_buffer
.next_index
.load(Ordering::SeqCst)
.load(Ordering::SeqCst);
// Find the required alignment in bytes.
let align_bytes = cmp::max(
if self.usage.uniform_buffer {
self.device().physical_device().limits()
.min_uniform_buffer_offset_alignment() as usize
} else { 1 },
if self.usage.storage_buffer {
self.device().physical_device().limits()
.min_storage_buffer_offset_alignment() as usize
} else { 1 },
);
let tentative_align_offset = (align_bytes - ((idx * mem::size_of::<T>()) % align_bytes)) % align_bytes;
let additional_len = if tentative_align_offset == 0 {
0
} else {
1 + (tentative_align_offset - 1) / mem::size_of::<T>()
};
(idx, requested_len + additional_len, tentative_align_offset)
};
// Find out whether any chunk in use overlaps this range.
if next_index + data_len <= current_buffer.capacity &&
!chunks_in_use.iter().any(|c| (c.index >= next_index && c.index < next_index + data_len) ||
(c.index <= next_index && c.index + c.len > next_index))
if tentative_index + tentative_len <= current_buffer.capacity &&
!chunks_in_use.iter().any(|c| (c.index >= tentative_index && c.index < tentative_index + tentative_len) ||
(c.index <= tentative_index && c.index + c.len > tentative_index))
{
next_index
(tentative_index, tentative_len, tentative_align_offset)
} else {
// Impossible to allocate at `next_index`. Let's try 0 instead.
if data_len <= current_buffer.capacity &&
!chunks_in_use.iter().any(|c| c.index < data_len)
// Impossible to allocate at `tentative_index`. Let's try 0 instead.
if requested_len <= current_buffer.capacity &&
!chunks_in_use.iter().any(|c| c.index < requested_len)
{
0
(0, requested_len, 0)
} else {
// Buffer is full. Return.
return Err(data);
@ -421,26 +449,30 @@ impl<T, A> CpuBufferPool<T, A>
// Write `data` in the memory.
unsafe {
let range = (index * mem::size_of::<T>()) .. ((index + data_len) * mem::size_of::<T>());
let mem_off = current_buffer.memory.offset();
let range_start = index * mem::size_of::<T>() + align_offset + mem_off;
let range_end = (index + requested_len) * mem::size_of::<T>() + align_offset + mem_off;
let mut mapping = current_buffer
.memory
.mapped_memory()
.unwrap()
.read_write::<[T]>(range);
.read_write::<[T]>(range_start .. range_end);
let mut written = 0;
for (o, i) in mapping.iter_mut().zip(data) {
ptr::write(o, i);
written += 1;
}
assert_eq!(written, data_len);
assert_eq!(written, requested_len,
"Iterator passed to CpuBufferPool::chunk has a mismatch between reported \
length and actual number of elements");
}
// Mark the chunk as in use.
current_buffer.next_index.store(index + data_len, Ordering::SeqCst);
current_buffer.next_index.store(index + occupied_len, Ordering::SeqCst);
chunks_in_use.push(ActualBufferChunk {
index,
len: data_len,
len: occupied_len,
num_cpu_accesses: 1,
num_gpu_accesses: 0,
});
@ -449,7 +481,8 @@ impl<T, A> CpuBufferPool<T, A>
// TODO: remove .clone() once non-lexical borrows land
buffer: current_buffer.clone(),
index: index,
len: data_len,
align_offset,
requested_len,
marker: PhantomData,
})
}
@ -496,7 +529,8 @@ impl<T, A> Clone for CpuBufferPoolChunk<T, A>
CpuBufferPoolChunk {
buffer: self.buffer.clone(),
index: self.index,
len: self.len,
align_offset: self.align_offset,
requested_len: self.requested_len,
marker: PhantomData,
}
}
@ -509,13 +543,13 @@ unsafe impl<T, A> BufferAccess for CpuBufferPoolChunk<T, A>
fn inner(&self) -> BufferInner {
BufferInner {
buffer: &self.buffer.inner,
offset: self.index * mem::size_of::<T>(),
offset: self.index * mem::size_of::<T>() + self.align_offset,
}
}
#[inline]
fn size(&self) -> usize {
self.len * mem::size_of::<T>()
self.requested_len * mem::size_of::<T>()
}
#[inline]

View File

@ -865,6 +865,11 @@ impl DescriptorWrite {
let size = buffer.size();
let BufferInner { buffer, offset } = buffer.inner();
debug_assert_eq!(offset % buffer.device().physical_device().limits()
.min_uniform_buffer_offset_alignment() as usize, 0);
debug_assert!(size <= buffer.device().physical_device().limits()
.max_uniform_buffer_range() as usize);
DescriptorWrite {
binding: binding,
first_array_element: array_element,
@ -883,6 +888,11 @@ impl DescriptorWrite {
let size = buffer.size();
let BufferInner { buffer, offset } = buffer.inner();
debug_assert_eq!(offset % buffer.device().physical_device().limits()
.min_storage_buffer_offset_alignment() as usize, 0);
debug_assert!(size <= buffer.device().physical_device().limits()
.max_storage_buffer_range() as usize);
DescriptorWrite {
binding: binding,
first_array_element: array_element,
@ -902,6 +912,11 @@ impl DescriptorWrite {
let size = buffer.size();
let BufferInner { buffer, offset } = buffer.inner();
debug_assert_eq!(offset % buffer.device().physical_device().limits()
.min_uniform_buffer_offset_alignment() as usize, 0);
debug_assert!(size <= buffer.device().physical_device().limits()
.max_uniform_buffer_range() as usize);
DescriptorWrite {
binding: binding,
first_array_element: array_element,
@ -919,6 +934,11 @@ impl DescriptorWrite {
let size = buffer.size();
let BufferInner { buffer, offset } = buffer.inner();
debug_assert_eq!(offset % buffer.device().physical_device().limits()
.min_storage_buffer_offset_alignment() as usize, 0);
debug_assert!(size <= buffer.device().physical_device().limits()
.max_storage_buffer_range() as usize);
DescriptorWrite {
binding: binding,
first_array_element: array_element,