use std::error;
use std::fmt;
use std::marker::PhantomData;
use std::mem;
use std::ptr;
use std::sync::Arc;
use buffer::BufferAccess;
use buffer::BufferInner;
use buffer::TypedBufferAccess;
use device::Device;
use device::DeviceOwned;
use format::FormatDesc;
use format::StrongStorage;
use Error;
use OomError;
use SafeDeref;
use VulkanObject;
use check_errors;
use vk;
pub struct BufferView<F, B>
where B: BufferAccess
{
view: vk::BufferView,
buffer: B,
marker: PhantomData<F>,
atomic_accesses: bool,
}
impl<F, B> BufferView<F, B>
where B: BufferAccess
{
#[inline]
pub fn new(buffer: B, format: F) -> Result<BufferView<F, B>, BufferViewCreationError>
where B: TypedBufferAccess<Content = [F::Pixel]>,
F: StrongStorage + 'static
{
unsafe { BufferView::unchecked(buffer, format) }
}
pub unsafe fn unchecked(org_buffer: B, format: F)
-> Result<BufferView<F, B>, BufferViewCreationError>
where B: BufferAccess,
F: FormatDesc + 'static
{
let (view, format_props) = {
let size = org_buffer.size();
let BufferInner { buffer, offset } = org_buffer.inner();
let device = buffer.device();
let format = format.format();
if (offset %
device
.physical_device()
.limits()
.min_texel_buffer_offset_alignment() as usize) != 0
{
return Err(BufferViewCreationError::WrongBufferAlignment);
}
if !buffer.usage_uniform_texel_buffer() && !buffer.usage_storage_texel_buffer() {
return Err(BufferViewCreationError::WrongBufferUsage);
}
{
let nb = size /
format
.size()
.expect("Can't use a compressed format for buffer views");
let l = device
.physical_device()
.limits()
.max_texel_buffer_elements();
if nb > l as usize {
return Err(BufferViewCreationError::MaxTexelBufferElementsExceeded);
}
}
let format_props = {
let vk_i = device.instance().pointers();
let mut output = mem::uninitialized();
vk_i.GetPhysicalDeviceFormatProperties(device.physical_device().internal_object(),
format as u32,
&mut output);
output.bufferFeatures
};
if buffer.usage_uniform_texel_buffer() {
if (format_props & vk::FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT) == 0 {
return Err(BufferViewCreationError::UnsupportedFormat);
}
}
if buffer.usage_storage_texel_buffer() {
if (format_props & vk::FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT) == 0 {
return Err(BufferViewCreationError::UnsupportedFormat);
}
}
let infos = vk::BufferViewCreateInfo {
sType: vk::STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
buffer: buffer.internal_object(),
format: format as u32,
offset: offset as u64,
range: size as u64,
};
let vk = device.pointers();
let mut output = mem::uninitialized();
check_errors(vk.CreateBufferView(device.internal_object(),
&infos,
ptr::null(),
&mut output))?;
(output, format_props)
};
Ok(BufferView {
view: view,
buffer: org_buffer,
marker: PhantomData,
atomic_accesses: (format_props &
vk::FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT) !=
0,
})
}
#[inline]
pub fn buffer(&self) -> &B {
&self.buffer
}
#[inline]
pub fn uniform_texel_buffer(&self) -> bool {
self.buffer.inner().buffer.usage_uniform_texel_buffer()
}
#[inline]
pub fn storage_texel_buffer(&self) -> bool {
self.buffer.inner().buffer.usage_storage_texel_buffer()
}
#[inline]
pub fn storage_texel_buffer_atomic(&self) -> bool {
self.atomic_accesses && self.storage_texel_buffer()
}
}
unsafe impl<F, B> VulkanObject for BufferView<F, B>
where B: BufferAccess
{
type Object = vk::BufferView;
const TYPE: vk::DebugReportObjectTypeEXT = vk::DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT;
#[inline]
fn internal_object(&self) -> vk::BufferView {
self.view
}
}
unsafe impl<F, B> DeviceOwned for BufferView<F, B>
where B: BufferAccess
{
#[inline]
fn device(&self) -> &Arc<Device> {
self.buffer.device()
}
}
impl<F, B> fmt::Debug for BufferView<F, B>
where B: BufferAccess + fmt::Debug
{
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fmt.debug_struct("BufferView")
.field("raw", &self.view)
.field("buffer", &self.buffer)
.finish()
}
}
impl<F, B> Drop for BufferView<F, B>
where B: BufferAccess
{
#[inline]
fn drop(&mut self) {
unsafe {
let vk = self.buffer.inner().buffer.device().pointers();
vk.DestroyBufferView(self.buffer.inner().buffer.device().internal_object(),
self.view,
ptr::null());
}
}
}
pub unsafe trait BufferViewRef {
type BufferAccess: BufferAccess;
type Format;
fn view(&self) -> &BufferView<Self::Format, Self::BufferAccess>;
}
unsafe impl<F, B> BufferViewRef for BufferView<F, B>
where B: BufferAccess
{
type BufferAccess = B;
type Format = F;
#[inline]
fn view(&self) -> &BufferView<F, B> {
self
}
}
unsafe impl<T, F, B> BufferViewRef for T
where T: SafeDeref<Target = BufferView<F, B>>,
B: BufferAccess
{
type BufferAccess = B;
type Format = F;
#[inline]
fn view(&self) -> &BufferView<F, B> {
&**self
}
}
#[derive(Debug, Copy, Clone)]
pub enum BufferViewCreationError {
OomError(OomError),
WrongBufferUsage,
WrongBufferAlignment,
UnsupportedFormat,
MaxTexelBufferElementsExceeded,
}
impl error::Error for BufferViewCreationError {
#[inline]
fn description(&self) -> &str {
match *self {
BufferViewCreationError::OomError(_) => "out of memory when creating buffer view",
BufferViewCreationError::WrongBufferUsage =>
"the buffer is missing correct usage flags",
BufferViewCreationError::WrongBufferAlignment => {
"the offset within the buffer is not a multiple of the
`min_texel_buffer_offset_alignment` limit"
},
BufferViewCreationError::UnsupportedFormat =>
"the requested format is not supported for this usage",
BufferViewCreationError::MaxTexelBufferElementsExceeded => {
"the maximum number of texel elements is exceeded"
},
}
}
#[inline]
fn cause(&self) -> Option<&dyn error::Error> {
match *self {
BufferViewCreationError::OomError(ref err) => Some(err),
_ => None,
}
}
}
impl fmt::Display for BufferViewCreationError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "{}", error::Error::description(self))
}
}
impl From<OomError> for BufferViewCreationError {
#[inline]
fn from(err: OomError) -> BufferViewCreationError {
BufferViewCreationError::OomError(err)
}
}
impl From<Error> for BufferViewCreationError {
#[inline]
fn from(err: Error) -> BufferViewCreationError {
OomError::from(err).into()
}
}
#[cfg(test)]
mod tests {
use buffer::BufferUsage;
use buffer::BufferView;
use buffer::immutable::ImmutableBuffer;
use buffer::view::BufferViewCreationError;
use format;
#[test]
fn create_uniform() {
let (device, queue) = gfx_dev_and_queue!();
let usage = BufferUsage {
uniform_texel_buffer: true,
..BufferUsage::none()
};
let (buffer, _) = ImmutableBuffer::<[[u8; 4]]>::from_iter((0 .. 128).map(|_| [0; 4]),
usage,
queue.clone())
.unwrap();
let view = BufferView::new(buffer, format::R8G8B8A8Unorm).unwrap();
assert!(view.uniform_texel_buffer());
}
#[test]
fn create_storage() {
let (device, queue) = gfx_dev_and_queue!();
let usage = BufferUsage {
storage_texel_buffer: true,
..BufferUsage::none()
};
let (buffer, _) = ImmutableBuffer::<[[u8; 4]]>::from_iter((0 .. 128).map(|_| [0; 4]),
usage,
queue.clone())
.unwrap();
let view = BufferView::new(buffer, format::R8G8B8A8Unorm).unwrap();
assert!(view.storage_texel_buffer());
}
#[test]
fn create_storage_atomic() {
let (device, queue) = gfx_dev_and_queue!();
let usage = BufferUsage {
storage_texel_buffer: true,
..BufferUsage::none()
};
let (buffer, _) =
ImmutableBuffer::<[u32]>::from_iter((0 .. 128).map(|_| 0), usage, queue.clone())
.unwrap();
let view = BufferView::new(buffer, format::R32Uint).unwrap();
assert!(view.storage_texel_buffer());
assert!(view.storage_texel_buffer_atomic());
}
#[test]
fn wrong_usage() {
let (device, queue) = gfx_dev_and_queue!();
let (buffer, _) = ImmutableBuffer::<[[u8; 4]]>::from_iter((0 .. 128).map(|_| [0; 4]),
BufferUsage::none(),
queue.clone())
.unwrap();
match BufferView::new(buffer, format::R8G8B8A8Unorm) {
Err(BufferViewCreationError::WrongBufferUsage) => (),
_ => panic!(),
}
}
#[test]
fn unsupported_format() {
let (device, queue) = gfx_dev_and_queue!();
let usage = BufferUsage {
uniform_texel_buffer: true,
storage_texel_buffer: true,
..BufferUsage::none()
};
let (buffer, _) = ImmutableBuffer::<[[f64; 4]]>::from_iter((0 .. 128).map(|_| [0.0; 4]),
usage,
queue.clone())
.unwrap();
match BufferView::new(buffer, format::R64G64B64A64Sfloat) {
Err(BufferViewCreationError::UnsupportedFormat) => (),
_ => panic!(),
}
}
}