Allow sparse flags when creating an UnsafeBuffer

This commit is contained in:
Pierre Krieger 2016-04-28 10:58:01 +02:00
parent 2d0d3526e5
commit 8ac0cc8a7d
6 changed files with 221 additions and 7 deletions

View File

@ -30,6 +30,8 @@ use std::sync::Weak;
use std::time::Duration;
use smallvec::SmallVec;
use buffer::sys::BufferCreationError;
use buffer::sys::SparseLevel;
use buffer::sys::UnsafeBuffer;
use buffer::sys::Usage;
use buffer::traits::AccessRange;
@ -122,7 +124,12 @@ impl<T: ?Sized> CpuAccessibleBuffer<T> {
Sharing::Exclusive
};
try!(UnsafeBuffer::new(device, size, &usage, sharing))
match UnsafeBuffer::new(device, size, &usage, sharing, SparseLevel::none()) {
Ok(b) => b,
Err(BufferCreationError::OomError(err)) => return Err(err),
Err(_) => unreachable!() // We don't use sparse binding, therefore the other
// errors can't happen
}
};
let mem_ty = device.physical_device().memory_types()

View File

@ -21,6 +21,8 @@ use std::sync::Mutex;
use std::sync::Weak;
use smallvec::SmallVec;
use buffer::sys::BufferCreationError;
use buffer::sys::SparseLevel;
use buffer::sys::UnsafeBuffer;
use buffer::sys::Usage;
use buffer::traits::AccessRange;
@ -110,7 +112,12 @@ impl<T: ?Sized> DeviceLocalBuffer<T> {
Sharing::Exclusive
};
try!(UnsafeBuffer::new(device, size, &usage, sharing))
match UnsafeBuffer::new(device, size, &usage, sharing, SparseLevel::none()) {
Ok(b) => b,
Err(BufferCreationError::OomError(err)) => return Err(err),
Err(_) => unreachable!() // We don't use sparse binding, therefore the other
// errors can't happen
}
};
let mem_ty = {

View File

@ -28,6 +28,8 @@ use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use smallvec::SmallVec;
use buffer::sys::BufferCreationError;
use buffer::sys::SparseLevel;
use buffer::sys::UnsafeBuffer;
use buffer::sys::Usage;
use buffer::traits::AccessRange;
@ -108,7 +110,12 @@ impl<T: ?Sized> ImmutableBuffer<T> {
Sharing::Exclusive
};
try!(UnsafeBuffer::new(device, size, &usage, sharing))
match UnsafeBuffer::new(device, size, &usage, sharing, SparseLevel::none()) {
Ok(b) => b,
Err(BufferCreationError::OomError(err)) => return Err(err),
Err(_) => unreachable!() // We don't use sparse binding, therefore the other
// errors can't happen
}
};
let mem_ty = {

View File

@ -63,6 +63,7 @@ use std::ops::Range;
use std::sync::Arc;
pub use self::device_local::DeviceLocalBuffer;
pub use self::sys::BufferCreationError;
pub use self::sys::Usage;
pub use self::traits::Buffer;
pub use self::traits::TypedBuffer;

View File

@ -7,6 +7,8 @@
// notice may not be copied, modified, or distributed except
// according to those terms.
use std::error;
use std::fmt;
use std::mem;
use std::ptr;
use std::sync::Arc;
@ -18,6 +20,7 @@ use memory::MemoryRequirements;
use sync::Sharing;
use check_errors;
use Error;
use OomError;
use VulkanObject;
use VulkanPointers;
@ -27,6 +30,10 @@ use vk;
///
/// # Safety
///
/// - Doesn't handle synchronization.
/// - Doesn't handle memory aliasing problems.
/// - Doesn't check that memory was correctly bound and only once.
/// - Doesn't enforce type safety.
///
#[derive(Debug)]
pub struct UnsafeBuffer {
@ -40,14 +47,36 @@ impl UnsafeBuffer {
/// Creates a new buffer of the given size.
///
/// See the module's documentation for information about safety.
pub unsafe fn new<'a, I>(device: &Arc<Device>, size: usize, usage: &Usage, sharing: Sharing<I>)
-> Result<(UnsafeBuffer, MemoryRequirements), OomError>
///
/// # Panic
///
/// Panicks if `sparse.sparse` is false and `sparse.sparse_residency` or
/// `sparse.sparse_aliased` is true.
///
pub unsafe fn new<'a, I>(device: &Arc<Device>, size: usize, usage: &Usage, sharing: Sharing<I>,
sparse: SparseLevel)
-> Result<(UnsafeBuffer, MemoryRequirements), BufferCreationError>
where I: Iterator<Item = u32>
{
let vk = device.pointers();
let usage = usage.to_usage_bits();
// Checking sparse features.
assert!(sparse.sparse || !sparse.sparse_residency, "Can't enable sparse residency without \
enabling sparse binding as well");
assert!(sparse.sparse || !sparse.sparse_aliased, "Can't enable sparse aliasing without \
enabling sparse binding as well");
if sparse.sparse && !device.enabled_features().sparse_binding {
return Err(BufferCreationError::SparseBindingFeatureNotEnabled);
}
if sparse.sparse_residency && !device.enabled_features().sparse_residency_buffer {
return Err(BufferCreationError::SparseResidencyBufferFeatureNotEnabled);
}
if sparse.sparse_aliased && !device.enabled_features().sparse_residency_aliased {
return Err(BufferCreationError::SparseResidencyAliasedFeatureNotEnabled);
}
let buffer = {
let (sh_mode, sh_indices) = match sharing {
Sharing::Exclusive => (vk::SHARING_MODE_EXCLUSIVE, SmallVec::<[u32; 8]>::new()),
@ -57,7 +86,7 @@ impl UnsafeBuffer {
let infos = vk::BufferCreateInfo {
sType: vk::STRUCTURE_TYPE_BUFFER_CREATE_INFO,
pNext: ptr::null(),
flags: 0, // TODO: sparse resources binding
flags: sparse.to_flags(),
size: size as u64,
usage: usage,
sharingMode: sh_mode,
@ -186,6 +215,33 @@ impl Drop for UnsafeBuffer {
}
}
#[derive(Debug, Copy, Clone)]
pub struct SparseLevel {
pub sparse: bool,
pub sparse_residency: bool,
pub sparse_aliased: bool,
}
impl SparseLevel {
#[inline]
pub fn none() -> SparseLevel {
SparseLevel {
sparse: false,
sparse_residency: false,
sparse_aliased: false,
}
}
#[inline]
fn to_flags(&self) -> vk::BufferCreateFlagBits {
let mut result = 0;
if self.sparse { result |= vk::BUFFER_CREATE_SPARSE_BINDING_BIT; }
if self.sparse_residency { result |= vk::BUFFER_CREATE_SPARSE_RESIDENCY_BIT; }
if self.sparse_aliased { result |= vk::BUFFER_CREATE_SPARSE_ALIASED_BIT; }
result
}
}
/// Describes how a buffer is going to be used. This is **not** an optimization.
///
/// If you try to use a buffer in a way that you didn't declare, a panic will happen.
@ -341,10 +397,76 @@ impl Usage {
}
}
/// Error that can happen when creating a buffer.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum BufferCreationError {
/// Not enough memory.
OomError(OomError),
/// Sparse binding was requested but the corresponding feature wasn't enabled.
SparseBindingFeatureNotEnabled,
/// Sparse residency was requested but the corresponding feature wasn't enabled.
SparseResidencyBufferFeatureNotEnabled,
/// Sparse aliasing was requested but the corresponding feature wasn't enabled.
SparseResidencyAliasedFeatureNotEnabled,
}
impl error::Error for BufferCreationError {
#[inline]
fn description(&self) -> &str {
match *self {
BufferCreationError::OomError(_) => "not enough memory available",
BufferCreationError::SparseBindingFeatureNotEnabled => {
"sparse binding was requested but the corresponding feature wasn't enabled"
},
BufferCreationError::SparseResidencyBufferFeatureNotEnabled => {
"sparse residency was requested but the corresponding feature wasn't enabled"
},
BufferCreationError::SparseResidencyAliasedFeatureNotEnabled => {
"sparse aliasing was requested but the corresponding feature wasn't enabled"
},
}
}
#[inline]
fn cause(&self) -> Option<&error::Error> {
match *self {
BufferCreationError::OomError(ref err) => Some(err),
_ => None
}
}
}
impl fmt::Display for BufferCreationError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "{}", error::Error::description(self))
}
}
impl From<OomError> for BufferCreationError {
#[inline]
fn from(err: OomError) -> BufferCreationError {
BufferCreationError::OomError(err)
}
}
impl From<Error> for BufferCreationError {
#[inline]
fn from(err: Error) -> BufferCreationError {
match err {
err @ Error::OutOfHostMemory => BufferCreationError::OomError(OomError::from(err)),
err @ Error::OutOfDeviceMemory => BufferCreationError::OomError(OomError::from(err)),
_ => panic!("unexpected error: {:?}", err)
}
}
}
#[cfg(test)]
mod tests {
use std::iter::Empty;
use super::BufferCreationError;
use super::SparseLevel;
use super::UnsafeBuffer;
use super::Usage;
@ -355,11 +477,76 @@ mod tests {
fn create() {
let (device, _) = gfx_dev_and_queue!();
let (buf, reqs) = unsafe {
UnsafeBuffer::new(&device, 128, &Usage::all(), Sharing::Exclusive::<Empty<_>>)
UnsafeBuffer::new(&device, 128, &Usage::all(), Sharing::Exclusive::<Empty<_>>,
SparseLevel::none())
}.unwrap();
assert!(reqs.size >= 128);
assert_eq!(buf.size(), 128);
assert_eq!(&**buf.device() as *const Device, &*device as *const Device);
}
#[test]
#[should_panic = "Can't enable sparse residency without enabling sparse binding as well"]
fn panic_wrong_sparse_residency() {
let (device, _) = gfx_dev_and_queue!();
let sparse = SparseLevel { sparse: false, sparse_residency: true, sparse_aliased: false };
let _ = unsafe {
UnsafeBuffer::new(&device, 128, &Usage::all(), Sharing::Exclusive::<Empty<_>>,
sparse)
};
}
#[test]
#[should_panic = "Can't enable sparse aliasing without enabling sparse binding as well"]
fn panic_wrong_sparse_aliased() {
let (device, _) = gfx_dev_and_queue!();
let sparse = SparseLevel { sparse: false, sparse_residency: false, sparse_aliased: true };
let _ = unsafe {
UnsafeBuffer::new(&device, 128, &Usage::all(), Sharing::Exclusive::<Empty<_>>,
sparse)
};
}
#[test]
fn missing_feature_sparse_binding() {
let (device, _) = gfx_dev_and_queue!();
let sparse = SparseLevel { sparse: true, sparse_residency: false, sparse_aliased: false };
unsafe {
match UnsafeBuffer::new(&device, 128, &Usage::all(), Sharing::Exclusive::<Empty<_>>,
sparse)
{
Err(BufferCreationError::SparseBindingFeatureNotEnabled) => (),
_ => panic!()
}
};
}
#[test]
fn missing_feature_sparse_residency() {
let (device, _) = gfx_dev_and_queue!(sparse_binding);
let sparse = SparseLevel { sparse: true, sparse_residency: true, sparse_aliased: false };
unsafe {
match UnsafeBuffer::new(&device, 128, &Usage::all(), Sharing::Exclusive::<Empty<_>>,
sparse)
{
Err(BufferCreationError::SparseResidencyBufferFeatureNotEnabled) => (),
_ => panic!()
}
};
}
#[test]
fn missing_feature_sparse_aliased() {
let (device, _) = gfx_dev_and_queue!(sparse_binding);
let sparse = SparseLevel { sparse: true, sparse_residency: false, sparse_aliased: true };
unsafe {
match UnsafeBuffer::new(&device, 128, &Usage::all(), Sharing::Exclusive::<Empty<_>>,
sparse)
{
Err(BufferCreationError::SparseResidencyAliasedFeatureNotEnabled) => (),
_ => panic!()
}
};
}
}

View File

@ -55,6 +55,11 @@ macro_rules! gfx_dev_and_queue {
.. Features::none()
};
// If the physical device doesn't support the requested features, just return.
if !physical.supported_features().superset_of(&features) {
return;
}
let (device, queues) = match Device::new(&physical, &features,
&extensions, None, [(queue, 0.5)].iter().cloned())
{