// Copyright (c) 2016 The vulkano developers // Licensed under the Apache License, Version 2.0 // or the MIT // license , // 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 = return; //! # let memory_allocator: vulkano::memory::allocator::StandardMemoryAllocator = return; //! let buffer = Buffer::new_slice::( //! &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_features: FormatFeatures, range: Range, } impl BufferView { /// Creates a new `BufferView`. #[inline] pub fn new( subbuffer: Subbuffer, create_info: BufferViewCreateInfo, ) -> Result, BufferViewCreationError> { Self::new_inner(subbuffer.into_bytes(), create_info) } fn new_inner( subbuffer: Subbuffer<[u8]>, create_info: BufferViewCreateInfo, ) -> Result, 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 { 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 { 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 { 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, 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 for BufferViewCreationError { fn from(err: OomError) -> Self { Self::OomError(err) } } impl From for BufferViewCreationError { fn from(err: VulkanError) -> Self { OomError::from(err).into() } } impl From 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::( &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!(), } } }