blob: 3b1f4f4e6b39863a65999ac68a9b6bb073606376 [file] [log] [blame]
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
//! View of a buffer, in order to use it as a uniform texel buffer or storage texel buffer.
//!
//! In order to use a buffer as a uniform texel buffer or a storage texel buffer, you have to
//! create a `BufferView`, which indicates which format the data is in.
//!
//! In order to create a view from a buffer, the buffer must have been created with either the
//! `uniform_texel_buffer` or the `storage_texel_buffer` usage.
//!
//! # Examples
//!
//! ```
//! # use std::sync::Arc;
//! use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage};
//! use vulkano::buffer::view::{BufferView, BufferViewCreateInfo};
//! use vulkano::format::Format;
//! use vulkano::memory::allocator::AllocationCreateInfo;
//!
//! # let queue: Arc<vulkano::device::Queue> = return;
//! # let memory_allocator: vulkano::memory::allocator::StandardMemoryAllocator = return;
//! let buffer = Buffer::new_slice::<u32>(
//! &memory_allocator,
//! BufferCreateInfo {
//! usage: BufferUsage::STORAGE_TEXEL_BUFFER,
//! ..Default::default()
//! },
//! AllocationCreateInfo::default(),
//! 128,
//! )
//! .unwrap();
//!
//! let view = BufferView::new(
//! buffer,
//! BufferViewCreateInfo {
//! format: Some(Format::R32_UINT),
//! ..Default::default()
//! },
//! )
//! .unwrap();
//! ```
use super::{BufferUsage, Subbuffer};
use crate::{
device::{Device, DeviceOwned},
format::{Format, FormatFeatures},
macros::impl_id_counter,
memory::{is_aligned, DeviceAlignment},
DeviceSize, OomError, RequirementNotMet, RequiresOneOf, Version, VulkanError, VulkanObject,
};
use std::{
error::Error,
fmt::{Display, Error as FmtError, Formatter},
mem::MaybeUninit,
num::NonZeroU64,
ops::Range,
ptr,
sync::Arc,
};
/// Represents a way for the GPU to interpret buffer data. See the documentation of the
/// `view` module.
#[derive(Debug)]
pub struct BufferView {
handle: ash::vk::BufferView,
subbuffer: Subbuffer<[u8]>,
id: NonZeroU64,
format: Option<Format>,
format_features: FormatFeatures,
range: Range<DeviceSize>,
}
impl BufferView {
/// Creates a new `BufferView`.
#[inline]
pub fn new(
subbuffer: Subbuffer<impl ?Sized>,
create_info: BufferViewCreateInfo,
) -> Result<Arc<BufferView>, BufferViewCreationError> {
Self::new_inner(subbuffer.into_bytes(), create_info)
}
fn new_inner(
subbuffer: Subbuffer<[u8]>,
create_info: BufferViewCreateInfo,
) -> Result<Arc<BufferView>, BufferViewCreationError> {
let BufferViewCreateInfo { format, _ne: _ } = create_info;
let buffer = subbuffer.buffer();
let device = buffer.device();
let properties = device.physical_device().properties();
let size = subbuffer.size();
let offset = subbuffer.offset();
// No VUID, but seems sensible?
let format = format.unwrap();
// VUID-VkBufferViewCreateInfo-format-parameter
format.validate_device(device)?;
// VUID-VkBufferViewCreateInfo-buffer-00932
if !buffer
.usage()
.intersects(BufferUsage::UNIFORM_TEXEL_BUFFER | BufferUsage::STORAGE_TEXEL_BUFFER)
{
return Err(BufferViewCreationError::BufferMissingUsage);
}
// Use unchecked, because all validation has been done above.
let format_features = unsafe {
device
.physical_device()
.format_properties_unchecked(format)
.buffer_features
};
// VUID-VkBufferViewCreateInfo-buffer-00933
if buffer.usage().intersects(BufferUsage::UNIFORM_TEXEL_BUFFER)
&& !format_features.intersects(FormatFeatures::UNIFORM_TEXEL_BUFFER)
{
return Err(BufferViewCreationError::UnsupportedFormat);
}
// VUID-VkBufferViewCreateInfo-buffer-00934
if buffer.usage().intersects(BufferUsage::STORAGE_TEXEL_BUFFER)
&& !format_features.intersects(FormatFeatures::STORAGE_TEXEL_BUFFER)
{
return Err(BufferViewCreationError::UnsupportedFormat);
}
let block_size = format.block_size().unwrap();
let texels_per_block = format.texels_per_block();
// VUID-VkBufferViewCreateInfo-range-00929
if size % block_size != 0 {
return Err(BufferViewCreationError::RangeNotAligned {
range: size,
required_alignment: block_size,
});
}
// VUID-VkBufferViewCreateInfo-range-00930
if ((size / 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 = DeviceAlignment::new(if block_size % 3 == 0 {
block_size / 3
} else {
block_size
})
.unwrap();
if buffer.usage().intersects(BufferUsage::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);
}
// VUID-VkBufferViewCreateInfo-buffer-02750
if !is_aligned(offset, required_alignment) {
return Err(BufferViewCreationError::OffsetNotAligned {
offset,
required_alignment,
});
}
}
if buffer.usage().intersects(BufferUsage::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);
}
// VUID-VkBufferViewCreateInfo-buffer-02751
if !is_aligned(offset, required_alignment) {
return Err(BufferViewCreationError::OffsetNotAligned {
offset,
required_alignment,
});
}
}
} else {
let required_alignment = properties.min_texel_buffer_offset_alignment;
// VUID-VkBufferViewCreateInfo-offset-02749
if !is_aligned(offset, required_alignment) {
return Err(BufferViewCreationError::OffsetNotAligned {
offset,
required_alignment,
});
}
}
let create_info = ash::vk::BufferViewCreateInfo {
flags: ash::vk::BufferViewCreateFlags::empty(),
buffer: buffer.handle(),
format: format.into(),
offset,
range: size,
..Default::default()
};
let handle = unsafe {
let fns = device.fns();
let mut output = MaybeUninit::uninit();
(fns.v1_0.create_buffer_view)(
device.handle(),
&create_info,
ptr::null(),
output.as_mut_ptr(),
)
.result()
.map_err(VulkanError::from)?;
output.assume_init()
};
Ok(Arc::new(BufferView {
handle,
subbuffer,
id: Self::next_id(),
format: Some(format),
format_features,
range: 0..size,
}))
}
/// Returns the buffer associated to this view.
#[inline]
pub fn buffer(&self) -> &Subbuffer<[u8]> {
&self.subbuffer
}
/// Returns the format of this view.
#[inline]
pub fn format(&self) -> Option<Format> {
self.format
}
/// Returns the features supported by this view’s format.
#[inline]
pub fn format_features(&self) -> FormatFeatures {
self.format_features
}
/// Returns the byte range of the wrapped buffer that this view exposes.
#[inline]
pub fn range(&self) -> Range<DeviceSize> {
self.range.clone()
}
}
impl Drop for BufferView {
#[inline]
fn drop(&mut self) {
unsafe {
let fns = self.subbuffer.device().fns();
(fns.v1_0.destroy_buffer_view)(
self.subbuffer.device().handle(),
self.handle,
ptr::null(),
);
}
}
}
unsafe impl VulkanObject for BufferView {
type Handle = ash::vk::BufferView;
#[inline]
fn handle(&self) -> Self::Handle {
self.handle
}
}
unsafe impl DeviceOwned for BufferView {
#[inline]
fn device(&self) -> &Arc<Device> {
self.subbuffer.device()
}
}
impl_id_counter!(BufferView);
/// Parameters to create a new `BufferView`.
#[derive(Clone, Debug)]
pub struct BufferViewCreateInfo {
/// The format of the buffer view.
///
/// The default value is `None`, which must be overridden.
pub format: Option<Format>,
pub _ne: crate::NonExhaustive,
}
impl Default for BufferViewCreateInfo {
#[inline]
fn default() -> Self {
Self {
format: None,
_ne: crate::NonExhaustive(()),
}
}
}
/// Error that can happen when creating a buffer view.
#[derive(Debug, Copy, Clone)]
pub enum BufferViewCreationError {
/// Out of memory.
OomError(OomError),
RequirementNotMet {
required_for: &'static str,
requires_one_of: RequiresOneOf,
},
/// The buffer was not created with one of the `storage_texel_buffer` or
/// `uniform_texel_buffer` usages.
BufferMissingUsage,
/// The offset within the buffer is not a multiple of the required alignment.
OffsetNotAligned {
offset: DeviceSize,
required_alignment: DeviceAlignment,
},
/// The range within the buffer is not a multiple of the required alignment.
RangeNotAligned {
range: DeviceSize,
required_alignment: DeviceSize,
},
/// The requested format is not supported for this usage.
UnsupportedFormat,
/// The `max_texel_buffer_elements` limit has been exceeded.
MaxTexelBufferElementsExceeded,
}
impl Error for BufferViewCreationError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
BufferViewCreationError::OomError(err) => Some(err),
_ => None,
}
}
}
impl Display for BufferViewCreationError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
Self::OomError(_) => write!(f, "out of memory when creating buffer view"),
Self::RequirementNotMet {
required_for,
requires_one_of,
} => write!(
f,
"a requirement was not met for: {}; requires one of: {}",
required_for, requires_one_of,
),
Self::BufferMissingUsage => write!(
f,
"the buffer was not created with one of the `storage_texel_buffer` or \
`uniform_texel_buffer` usages",
),
Self::OffsetNotAligned { .. } => write!(
f,
"the offset within the buffer is not a multiple of the required alignment",
),
Self::RangeNotAligned { .. } => write!(
f,
"the range within the buffer is not a multiple of the required alignment",
),
Self::UnsupportedFormat => {
write!(f, "the requested format is not supported for this usage")
}
Self::MaxTexelBufferElementsExceeded => {
write!(f, "the `max_texel_buffer_elements` limit has been exceeded")
}
}
}
}
impl From<OomError> for BufferViewCreationError {
fn from(err: OomError) -> Self {
Self::OomError(err)
}
}
impl From<VulkanError> for BufferViewCreationError {
fn from(err: VulkanError) -> Self {
OomError::from(err).into()
}
}
impl From<RequirementNotMet> for BufferViewCreationError {
fn from(err: RequirementNotMet) -> Self {
Self::RequirementNotMet {
required_for: err.required_for,
requires_one_of: err.requires_one_of,
}
}
}
#[cfg(test)]
mod tests {
use super::{BufferView, BufferViewCreateInfo, BufferViewCreationError};
use crate::{
buffer::{Buffer, BufferCreateInfo, BufferUsage},
format::Format,
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
};
#[test]
fn create_uniform() {
// `VK_FORMAT_R8G8B8A8_UNORM` guaranteed to be a supported format
let (device, _) = gfx_dev_and_queue!();
let memory_allocator = StandardMemoryAllocator::new_default(device);
let buffer = Buffer::new_slice::<[u8; 4]>(
&memory_allocator,
BufferCreateInfo {
usage: BufferUsage::UNIFORM_TEXEL_BUFFER,
..Default::default()
},
AllocationCreateInfo {
usage: MemoryUsage::Upload,
..Default::default()
},
128,
)
.unwrap();
BufferView::new(
buffer,
BufferViewCreateInfo {
format: Some(Format::R8G8B8A8_UNORM),
..Default::default()
},
)
.unwrap();
}
#[test]
fn create_storage() {
// `VK_FORMAT_R8G8B8A8_UNORM` guaranteed to be a supported format
let (device, _) = gfx_dev_and_queue!();
let memory_allocator = StandardMemoryAllocator::new_default(device);
let buffer = Buffer::new_slice::<[u8; 4]>(
&memory_allocator,
BufferCreateInfo {
usage: BufferUsage::STORAGE_TEXEL_BUFFER,
..Default::default()
},
AllocationCreateInfo::default(),
128,
)
.unwrap();
BufferView::new(
buffer,
BufferViewCreateInfo {
format: Some(Format::R8G8B8A8_UNORM),
..Default::default()
},
)
.unwrap();
}
#[test]
fn create_storage_atomic() {
// `VK_FORMAT_R32_UINT` guaranteed to be a supported format for atomics
let (device, _) = gfx_dev_and_queue!();
let memory_allocator = StandardMemoryAllocator::new_default(device);
let buffer = Buffer::new_slice::<u32>(
&memory_allocator,
BufferCreateInfo {
usage: BufferUsage::STORAGE_TEXEL_BUFFER,
..Default::default()
},
AllocationCreateInfo::default(),
128,
)
.unwrap();
BufferView::new(
buffer,
BufferViewCreateInfo {
format: Some(Format::R32_UINT),
..Default::default()
},
)
.unwrap();
}
#[test]
fn wrong_usage() {
// `VK_FORMAT_R8G8B8A8_UNORM` guaranteed to be a supported format
let (device, _) = gfx_dev_and_queue!();
let memory_allocator = StandardMemoryAllocator::new_default(device);
let buffer = Buffer::new_slice::<[u8; 4]>(
&memory_allocator,
BufferCreateInfo {
usage: BufferUsage::TRANSFER_DST, // Dummy value
..Default::default()
},
AllocationCreateInfo::default(),
128,
)
.unwrap();
match BufferView::new(
buffer,
BufferViewCreateInfo {
format: Some(Format::R8G8B8A8_UNORM),
..Default::default()
},
) {
Err(BufferViewCreationError::BufferMissingUsage) => (),
_ => panic!(),
}
}
#[test]
fn unsupported_format() {
let (device, _) = gfx_dev_and_queue!();
let memory_allocator = StandardMemoryAllocator::new_default(device);
let buffer = Buffer::new_slice::<[f64; 4]>(
&memory_allocator,
BufferCreateInfo {
usage: BufferUsage::UNIFORM_TEXEL_BUFFER | BufferUsage::STORAGE_TEXEL_BUFFER,
..Default::default()
},
AllocationCreateInfo::default(),
128,
)
.unwrap();
// TODO: what if R64G64B64A64_SFLOAT is supported?
match BufferView::new(
buffer,
BufferViewCreateInfo {
format: Some(Format::R64G64B64A64_SFLOAT),
..Default::default()
},
) {
Err(BufferViewCreationError::UnsupportedFormat) => (),
_ => panic!(),
}
}
}