use crate::buffer::{BufferAccess, BufferAccessObject, BufferInner};
use crate::device::Device;
use crate::device::DeviceOwned;
use crate::format::Format;
use crate::format::FormatFeatures;
use crate::DeviceSize;
use crate::Error;
use crate::OomError;
use crate::VulkanObject;
use crate::{check_errors, Version};
use std::error;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::mem::MaybeUninit;
use std::ptr;
use std::sync::Arc;
#[derive(Debug)]
pub struct BufferView<B>
where
B: BufferAccess + ?Sized,
{
handle: ash::vk::BufferView,
buffer: Arc<B>,
format: Option<Format>,
format_features: FormatFeatures,
}
impl<B> BufferView<B>
where
B: BufferAccess + ?Sized,
{
pub fn new(
buffer: Arc<B>,
create_info: BufferViewCreateInfo,
) -> Result<Arc<BufferView<B>>, BufferViewCreationError> {
let BufferViewCreateInfo { format, _ne: _ } = create_info;
let device = buffer.device();
let properties = device.physical_device().properties();
let range = buffer.size();
let BufferInner {
buffer: inner_buffer,
offset,
} = buffer.inner();
let format = format.unwrap();
if !(inner_buffer.usage().uniform_texel_buffer || inner_buffer.usage().storage_texel_buffer)
{
return Err(BufferViewCreationError::BufferMissingUsage);
}
let format_features = device
.physical_device()
.format_properties(format)
.buffer_features;
if inner_buffer.usage().uniform_texel_buffer && !format_features.uniform_texel_buffer {
return Err(BufferViewCreationError::UnsupportedFormat);
}
if inner_buffer.usage().storage_texel_buffer && !format_features.storage_texel_buffer {
return Err(BufferViewCreationError::UnsupportedFormat);
}
let block_size = format.block_size().unwrap();
let texels_per_block = format.texels_per_block();
if range % block_size != 0 {
return Err(BufferViewCreationError::RangeNotAligned {
range,
required_alignment: block_size,
});
}
if ((range / block_size) * texels_per_block as DeviceSize) as u32
> properties.max_texel_buffer_elements
{
return Err(BufferViewCreationError::MaxTexelBufferElementsExceeded);
}
if device.api_version() >= Version::V1_3 || device.enabled_features().texel_buffer_alignment
{
let element_size = if block_size % 3 == 0 {
block_size / 3
} else {
block_size
};
if inner_buffer.usage().storage_texel_buffer {
let mut required_alignment = properties
.storage_texel_buffer_offset_alignment_bytes
.unwrap();
if properties
.storage_texel_buffer_offset_single_texel_alignment
.unwrap()
{
required_alignment = required_alignment.min(element_size);
}
if offset % required_alignment != 0 {
return Err(BufferViewCreationError::OffsetNotAligned {
offset,
required_alignment,
});
}
}
if inner_buffer.usage().uniform_texel_buffer {
let mut required_alignment = properties
.uniform_texel_buffer_offset_alignment_bytes
.unwrap();
if properties
.uniform_texel_buffer_offset_single_texel_alignment
.unwrap()
{
required_alignment = required_alignment.min(element_size);
}
if offset % required_alignment != 0 {
return Err(BufferViewCreationError::OffsetNotAligned {
offset,
required_alignment,
});
}
}
} else {
let required_alignment = properties.min_texel_buffer_offset_alignment;
if offset % required_alignment != 0 {
return Err(BufferViewCreationError::OffsetNotAligned {
offset,
required_alignment,
});
}
}
let create_info = ash::vk::BufferViewCreateInfo {
flags: ash::vk::BufferViewCreateFlags::empty(),
buffer: inner_buffer.internal_object(),
format: format.into(),
offset,
range,
..Default::default()
};
let handle = unsafe {
let fns = device.fns();
let mut output = MaybeUninit::uninit();
check_errors(fns.v1_0.create_buffer_view(
device.internal_object(),
&create_info,
ptr::null(),
output.as_mut_ptr(),
))?;
output.assume_init()
};
Ok(Arc::new(BufferView {
handle,
buffer,
format: Some(format),
format_features,
}))
}
#[inline]
pub fn buffer(&self) -> &Arc<B> {
&self.buffer
}
}
impl<B> Drop for BufferView<B>
where
B: BufferAccess + ?Sized,
{
#[inline]
fn drop(&mut self) {
unsafe {
let fns = self.buffer.inner().buffer.device().fns();
fns.v1_0.destroy_buffer_view(
self.buffer.inner().buffer.device().internal_object(),
self.handle,
ptr::null(),
);
}
}
}
unsafe impl<B> VulkanObject for BufferView<B>
where
B: BufferAccess + ?Sized,
{
type Object = ash::vk::BufferView;
#[inline]
fn internal_object(&self) -> ash::vk::BufferView {
self.handle
}
}
unsafe impl<B> DeviceOwned for BufferView<B>
where
B: BufferAccess + ?Sized,
{
#[inline]
fn device(&self) -> &Arc<Device> {
self.buffer.device()
}
}
impl<B> PartialEq for BufferView<B>
where
B: BufferAccess + ?Sized,
{
#[inline]
fn eq(&self, other: &Self) -> bool {
self.handle == other.handle && self.device() == other.device()
}
}
impl<B> Eq for BufferView<B> where B: BufferAccess + ?Sized {}
impl<B> Hash for BufferView<B>
where
B: BufferAccess + ?Sized,
{
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.handle.hash(state);
self.device().hash(state);
}
}
#[derive(Clone, Debug)]
pub struct BufferViewCreateInfo {
pub format: Option<Format>,
pub _ne: crate::NonExhaustive,
}
impl Default for BufferViewCreateInfo {
#[inline]
fn default() -> Self {
Self {
format: None,
_ne: crate::NonExhaustive(()),
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum BufferViewCreationError {
OomError(OomError),
BufferMissingUsage,
OffsetNotAligned {
offset: DeviceSize,
required_alignment: DeviceSize,
},
RangeNotAligned {
range: DeviceSize,
required_alignment: DeviceSize,
},
UnsupportedFormat,
MaxTexelBufferElementsExceeded,
}
impl error::Error for BufferViewCreationError {
#[inline]
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
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> {
match *self {
BufferViewCreationError::OomError(_) => write!(
fmt,
"out of memory when creating buffer view",
),
BufferViewCreationError::BufferMissingUsage => write!(
fmt,
"the buffer was not created with one of the `storage_texel_buffer` or `uniform_texel_buffer` usages",
),
BufferViewCreationError::OffsetNotAligned { .. } => write!(
fmt,
"the offset within the buffer is not a multiple of the required alignment",
),
BufferViewCreationError::RangeNotAligned { .. } => write!(
fmt,
"the range within the buffer is not a multiple of the required alignment",
),
BufferViewCreationError::UnsupportedFormat => write!(
fmt,
"the requested format is not supported for this usage",
),
BufferViewCreationError::MaxTexelBufferElementsExceeded => write!(
fmt,
"the `max_texel_buffer_elements` limit has been exceeded",
),
}
}
}
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()
}
}
pub unsafe trait BufferViewAbstract:
VulkanObject<Object = ash::vk::BufferView> + DeviceOwned + Send + Sync
{
fn buffer(&self) -> Arc<dyn BufferAccess>;
fn format(&self) -> Option<Format>;
fn format_features(&self) -> &FormatFeatures;
}
unsafe impl<B> BufferViewAbstract for BufferView<B>
where
B: BufferAccess + ?Sized + 'static,
Arc<B>: BufferAccessObject,
{
#[inline]
fn buffer(&self) -> Arc<dyn BufferAccess> {
self.buffer.as_buffer_access_object()
}
#[inline]
fn format(&self) -> Option<Format> {
self.format
}
#[inline]
fn format_features(&self) -> &FormatFeatures {
&self.format_features
}
}
impl PartialEq for dyn BufferViewAbstract {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.internal_object() == other.internal_object() && self.device() == other.device()
}
}
impl Eq for dyn BufferViewAbstract {}
impl Hash for dyn BufferViewAbstract {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.internal_object().hash(state);
self.device().hash(state);
}
}
#[cfg(test)]
mod tests {
use crate::buffer::immutable::ImmutableBuffer;
use crate::buffer::view::{BufferView, BufferViewCreateInfo, BufferViewCreationError};
use crate::buffer::BufferUsage;
use crate::format::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,
BufferViewCreateInfo {
format: Some(Format::R8G8B8A8_UNORM),
..Default::default()
},
)
.unwrap();
}
#[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();
BufferView::new(
buffer,
BufferViewCreateInfo {
format: Some(Format::R8G8B8A8_UNORM),
..Default::default()
},
)
.unwrap();
}
#[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();
BufferView::new(
buffer,
BufferViewCreateInfo {
format: Some(Format::R32_UINT),
..Default::default()
},
)
.unwrap();
}
#[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,
BufferViewCreateInfo {
format: Some(Format::R8G8B8A8_UNORM),
..Default::default()
},
) {
Err(BufferViewCreationError::BufferMissingUsage) => (),
_ => 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,
BufferViewCreateInfo {
format: Some(Format::R64G64B64A64_SFLOAT),
..Default::default()
},
) {
Err(BufferViewCreationError::UnsupportedFormat) => (),
_ => panic!(),
}
}
}