// 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. //! Location in memory that contains data. //! //! A Vulkan buffer is very similar to a buffer that you would use in programming languages in //! general, in the sense that it is a location in memory that contains data. The difference //! between a Vulkan buffer and a regular buffer is that the content of a Vulkan buffer is //! accessible from the GPU. //! //! Vulkano does not perform any specific marshalling of buffer data. The representation of the //! buffer in memory is identical between the CPU and GPU. Because the Rust compiler is allowed to //! reorder struct fields at will by default when using `#[repr(Rust)]`, it is advised to mark each //! struct requiring imput assembly as `#[repr(C)]`. This forces Rust to follow the standard C //! procedure. Each element is laid out in memory in the order of declaration and aligned to a //! multiple of their alignment. //! //! # Multiple levels of abstraction //! //! - The low-level implementation of a buffer is [`RawBuffer`], which corresponds directly to a //! `VkBuffer`, and as such doesn't hold onto any memory. //! - [`Buffer`] is a `RawBuffer` with memory bound to it, and with state tracking. //! - [`Subbuffer`] is what you will use most of the time, as it is what all the APIs expect. It is //! a reference to a portion of a `Buffer`. `Subbuffer` also has a type parameter, which is a //! hint for how the data in the portion of the buffer is going to be interpreted. //! //! # `Subbuffer` allocation //! //! There are two ways to get a `Subbuffer`: //! //! - By using the functions on `Buffer`, which create a new buffer and memory allocation each //! time, and give you a `Subbuffer` that has an entire `Buffer` dedicated to it. //! - By using the [`SubbufferAllocator`], which creates `Subbuffer`s by suballocating existing //! `Buffer`s such that the `Buffer`s can keep being reused. //! //! Which of these you should choose depends on the use case. For example, if you need to upload //! data to the device each frame, then you should use `SubbufferAllocator`. Same goes for if you //! need to download data very frequently, or if you need to allocate a lot of intermediary buffers //! that are only accessed by the device. On the other hand, if you need to upload some data just //! once, or you can keep reusing the same buffer (because its size is unchanging) it's best to //! use a dedicated `Buffer` for that. //! //! # Memory usage //! //! When allocating memory for a buffer, you have to specify a *memory usage*. This tells the //! memory allocator what memory type it should pick for the allocation. //! //! - [`MemoryUsage::DeviceOnly`] will allocate a buffer that's usually located in device-local //! memory and whose content can't be directly accessed by your application. Accessing this //! buffer from the device is generally faster compared to accessing a buffer that's located in //! host-visible memory. //! - [`MemoryUsage::Upload`] and [`MemoryUsage::Download`] both allocate from a host-visible //! memory type, which means the buffer can be accessed directly from the host. Buffers allocated //! with these memory usages are needed to get data to and from the device. //! //! Take for example a buffer that is under constant access by the device but you need to read its //! content on the host from time to time, it may be a good idea to use a device-local buffer as //! the main buffer and a host-visible buffer for when you need to read it. Then whenever you need //! to read the main buffer, ask the device to copy from the device-local buffer to the //! host-visible buffer, and read the host-visible buffer instead. //! //! # Buffer usage //! //! When you create a buffer, you have to specify its *usage*. In other words, you have to //! specify the way it is going to be used. Trying to use a buffer in a way that wasn't specified //! when you created it will result in a runtime error. //! //! You can use buffers for the following purposes: //! //! - Can contain arbitrary data that can be transferred from/to other buffers and images. //! - Can be read and modified from a shader. //! - Can be used as a source of vertices and indices. //! - Can be used as a source of list of models for draw indirect commands. //! //! Accessing a buffer from a shader can be done in the following ways: //! //! - As a uniform buffer. Uniform buffers are read-only. //! - As a storage buffer. Storage buffers can be read and written. //! - As a uniform texel buffer. Contrary to a uniform buffer, the data is interpreted by the GPU //! and can be for example normalized. //! - As a storage texel buffer. Additionally, some data formats can be modified with atomic //! operations. //! //! Using uniform/storage texel buffers requires creating a *buffer view*. See [the `view` module] //! for how to create a buffer view. //! //! See also [the `shader` module documentation] for information about how buffer contents need to //! be laid out in accordance with the shader interface. //! //! [`RawBuffer`]: self::sys::RawBuffer //! [`SubbufferAllocator`]: self::allocator::SubbufferAllocator //! [`MemoryUsage::DeviceOnly`]: crate::memory::allocator::MemoryUsage::DeviceOnly //! [`MemoryUsage::Upload`]: crate::memory::allocator::MemoryUsage::Upload //! [`MemoryUsage::Download`]: crate::memory::allocator::MemoryUsage::Download //! [the `view` module]: self::view //! [the `shader` module documentation]: crate::shader pub use self::{ subbuffer::{BufferContents, BufferContentsLayout, Subbuffer}, sys::BufferCreateInfo, usage::BufferUsage, }; use self::{ subbuffer::{ReadLockError, WriteLockError}, sys::RawBuffer, }; use crate::{ device::{Device, DeviceOwned}, macros::vulkan_bitflags, memory::{ allocator::{ AllocationCreateInfo, AllocationCreationError, AllocationType, DeviceLayout, MemoryAlloc, MemoryAllocator, }, is_aligned, DedicatedAllocation, DeviceAlignment, ExternalMemoryHandleType, ExternalMemoryHandleTypes, ExternalMemoryProperties, MemoryRequirements, }, range_map::RangeMap, sync::{future::AccessError, CurrentAccess, Sharing}, DeviceSize, NonZeroDeviceSize, RequirementNotMet, RequiresOneOf, Version, VulkanError, VulkanObject, }; use parking_lot::{Mutex, MutexGuard}; use smallvec::SmallVec; use std::{ error::Error, fmt::{Display, Error as FmtError, Formatter}, hash::{Hash, Hasher}, mem::size_of_val, ops::Range, ptr, sync::Arc, }; pub mod allocator; pub mod subbuffer; pub mod sys; mod usage; pub mod view; /// A storage for raw bytes. /// /// Unlike [`RawBuffer`], a `Buffer` has memory backing it, and can be used normally. /// /// See [the module-level documentation] for more information about buffers. /// /// # Examples /// /// Sometimes, you need a buffer that is rarely accessed by the host. To get the best performance /// in this case, one should use a buffer in device-local memory, that is inaccessible from the /// host. As such, to initialize or otherwise access such a buffer, we need a *staging buffer*. /// /// The following example outlines the general strategy one may take when initializing a /// device-local buffer. /// /// ``` /// use vulkano::{ /// buffer::{BufferUsage, Buffer, BufferCreateInfo}, /// command_buffer::{ /// AutoCommandBufferBuilder, CommandBufferUsage, CopyBufferInfo, /// PrimaryCommandBufferAbstract, /// }, /// memory::allocator::{AllocationCreateInfo, MemoryUsage}, /// sync::GpuFuture, /// DeviceSize, /// }; /// /// # let device: std::sync::Arc = return; /// # let queue: std::sync::Arc = return; /// # let memory_allocator: vulkano::memory::allocator::StandardMemoryAllocator = return; /// # let command_buffer_allocator: vulkano::command_buffer::allocator::StandardCommandBufferAllocator = return; /// // Simple iterator to construct test data. /// let data = (0..10_000).map(|i| i as f32); /// /// // Create a host-accessible buffer initialized with the data. /// let temporary_accessible_buffer = Buffer::from_iter( /// &memory_allocator, /// BufferCreateInfo { /// // Specify that this buffer will be used as a transfer source. /// usage: BufferUsage::TRANSFER_SRC, /// ..Default::default() /// }, /// AllocationCreateInfo { /// // Specify use for upload to the device. /// usage: MemoryUsage::Upload, /// ..Default::default() /// }, /// data, /// ) /// .unwrap(); /// /// // Create a buffer in device-local with enough space for a slice of `10_000` floats. /// let device_local_buffer = Buffer::new_slice::( /// &memory_allocator, /// BufferCreateInfo { /// // Specify use as a storage buffer and transfer destination. /// usage: BufferUsage::STORAGE_BUFFER | BufferUsage::TRANSFER_DST, /// ..Default::default() /// }, /// AllocationCreateInfo { /// // Specify use by the device only. /// usage: MemoryUsage::DeviceOnly, /// ..Default::default() /// }, /// 10_000 as DeviceSize, /// ) /// .unwrap(); /// /// // Create a one-time command to copy between the buffers. /// let mut cbb = AutoCommandBufferBuilder::primary( /// &command_buffer_allocator, /// queue.queue_family_index(), /// CommandBufferUsage::OneTimeSubmit, /// ) /// .unwrap(); /// cbb.copy_buffer(CopyBufferInfo::buffers( /// temporary_accessible_buffer, /// device_local_buffer.clone(), /// )) /// .unwrap(); /// let cb = cbb.build().unwrap(); /// /// // Execute the copy command and wait for completion before proceeding. /// cb.execute(queue.clone()) /// .unwrap() /// .then_signal_fence_and_flush() /// .unwrap() /// .wait(None /* timeout */) /// .unwrap() /// ``` /// /// [the module-level documentation]: self #[derive(Debug)] pub struct Buffer { inner: RawBuffer, memory: BufferMemory, state: Mutex, } /// The type of backing memory that a buffer can have. #[derive(Debug)] pub enum BufferMemory { /// The buffer is backed by normal memory, bound with [`bind_memory`]. /// /// [`bind_memory`]: RawBuffer::bind_memory Normal(MemoryAlloc), /// The buffer is backed by sparse memory, bound with [`bind_sparse`]. /// /// [`bind_sparse`]: crate::device::QueueGuard::bind_sparse Sparse, } impl Buffer { /// Creates a new `Buffer` and writes `data` in it. Returns a [`Subbuffer`] spanning the whole /// buffer. /// /// This only works with memory types that are host-visible. If you want to upload data to a /// buffer allocated in device-local memory, you will need to create a staging buffer and copy /// the contents over. /// /// > **Note**: You should **not** set the `buffer_info.size` field. The function does that /// > itself. /// /// # Panics /// /// - Panics if `T` has zero size. /// - Panics if `T` has an alignment greater than `64`. pub fn from_data( allocator: &(impl MemoryAllocator + ?Sized), buffer_info: BufferCreateInfo, allocation_info: AllocationCreateInfo, data: T, ) -> Result, BufferError> where T: BufferContents, { let buffer = Buffer::new_sized(allocator, buffer_info, allocation_info)?; unsafe { ptr::write(&mut *buffer.write()?, data) }; Ok(buffer) } /// Creates a new `Buffer` and writes all elements of `iter` in it. Returns a [`Subbuffer`] /// spanning the whole buffer. /// /// This only works with memory types that are host-visible. If you want to upload data to a /// buffer allocated in device-local memory, you will need to create a staging buffer and copy /// the contents over. /// /// > **Note**: You should **not** set the `buffer_info.size` field. The function does that /// > itself. /// /// # Panics /// /// - Panics if `iter` is empty. pub fn from_iter( allocator: &(impl MemoryAllocator + ?Sized), buffer_info: BufferCreateInfo, allocation_info: AllocationCreateInfo, iter: I, ) -> Result, BufferError> where T: BufferContents, I: IntoIterator, I::IntoIter: ExactSizeIterator, { let iter = iter.into_iter(); let buffer = Buffer::new_slice( allocator, buffer_info, allocation_info, iter.len().try_into().unwrap(), )?; for (o, i) in buffer.write()?.iter_mut().zip(iter) { unsafe { ptr::write(o, i) }; } Ok(buffer) } /// Creates a new uninitialized `Buffer` for sized data. Returns a [`Subbuffer`] spanning the /// whole buffer. /// /// > **Note**: You should **not** set the `buffer_info.size` field. The function does that /// > itself. pub fn new_sized( allocator: &(impl MemoryAllocator + ?Sized), buffer_info: BufferCreateInfo, allocation_info: AllocationCreateInfo, ) -> Result, BufferError> where T: BufferContents, { let layout = T::LAYOUT.unwrap_sized(); let buffer = Subbuffer::new(Buffer::new( allocator, buffer_info, allocation_info, layout, )?); Ok(unsafe { buffer.reinterpret_unchecked() }) } /// Creates a new uninitialized `Buffer` for a slice. Returns a [`Subbuffer`] spanning the /// whole buffer. /// /// > **Note**: You should **not** set the `buffer_info.size` field. The function does that /// > itself. /// /// # Panics /// /// - Panics if `len` is zero. pub fn new_slice( allocator: &(impl MemoryAllocator + ?Sized), buffer_info: BufferCreateInfo, allocation_info: AllocationCreateInfo, len: DeviceSize, ) -> Result, BufferError> where T: BufferContents, { Buffer::new_unsized(allocator, buffer_info, allocation_info, len) } /// Creates a new uninitialized `Buffer` for unsized data. Returns a [`Subbuffer`] spanning the /// whole buffer. /// /// > **Note**: You should **not** set the `buffer_info.size` field. The function does that /// > itself. /// /// # Panics /// /// - Panics if `len` is zero. pub fn new_unsized( allocator: &(impl MemoryAllocator + ?Sized), buffer_info: BufferCreateInfo, allocation_info: AllocationCreateInfo, len: DeviceSize, ) -> Result, BufferError> where T: BufferContents + ?Sized, { let len = NonZeroDeviceSize::new(len).expect("empty slices are not valid buffer contents"); let layout = T::LAYOUT.layout_for_len(len).unwrap(); let buffer = Subbuffer::new(Buffer::new( allocator, buffer_info, allocation_info, layout, )?); Ok(unsafe { buffer.reinterpret_unchecked() }) } /// Creates a new uninitialized `Buffer` with the given `layout`. /// /// > **Note**: You should **not** set the `buffer_info.size` field. The function does that /// > itself. /// /// # Panics /// /// - Panics if `layout.alignment()` is greater than 64. pub fn new( allocator: &(impl MemoryAllocator + ?Sized), mut buffer_info: BufferCreateInfo, allocation_info: AllocationCreateInfo, layout: DeviceLayout, ) -> Result, BufferError> { assert!(layout.alignment().as_devicesize() <= 64); // TODO: Enable once sparse binding materializes // assert!(!allocate_info.flags.contains(BufferCreateFlags::SPARSE_BINDING)); assert!( buffer_info.size == 0, "`Buffer::new*` functions set the `buffer_info.size` field themselves, you should not \ set it yourself", ); buffer_info.size = layout.size(); let raw_buffer = RawBuffer::new(allocator.device().clone(), buffer_info)?; let mut requirements = *raw_buffer.memory_requirements(); requirements.layout = requirements.layout.align_to(layout.alignment()).unwrap(); let mut allocation = unsafe { allocator.allocate_unchecked( requirements, AllocationType::Linear, allocation_info, Some(DedicatedAllocation::Buffer(&raw_buffer)), ) }?; debug_assert!(is_aligned( allocation.offset(), requirements.layout.alignment(), )); debug_assert!(allocation.size() == requirements.layout.size()); // The implementation might require a larger size than we wanted. With this it is easier to // invalidate and flush the whole buffer. It does not affect the allocation in any way. allocation.shrink(layout.size()); unsafe { raw_buffer.bind_memory_unchecked(allocation) } .map(Arc::new) .map_err(|(err, _, _)| err.into()) } fn from_raw(inner: RawBuffer, memory: BufferMemory) -> Self { let state = Mutex::new(BufferState::new(inner.size())); Buffer { inner, memory, state, } } /// Returns the type of memory that is backing this buffer. #[inline] pub fn memory(&self) -> &BufferMemory { &self.memory } /// Returns the memory requirements for this buffer. #[inline] pub fn memory_requirements(&self) -> &MemoryRequirements { self.inner.memory_requirements() } /// Returns the flags the buffer was created with. #[inline] pub fn flags(&self) -> BufferCreateFlags { self.inner.flags() } /// Returns the size of the buffer in bytes. #[inline] pub fn size(&self) -> DeviceSize { self.inner.size() } /// Returns the usage the buffer was created with. #[inline] pub fn usage(&self) -> BufferUsage { self.inner.usage() } /// Returns the sharing the buffer was created with. #[inline] pub fn sharing(&self) -> &Sharing> { self.inner.sharing() } /// Returns the external memory handle types that are supported with this buffer. #[inline] pub fn external_memory_handle_types(&self) -> ExternalMemoryHandleTypes { self.inner.external_memory_handle_types() } /// Returns the device address for this buffer. // TODO: Caching? pub fn device_address(&self) -> Result { let device = self.device(); // VUID-vkGetBufferDeviceAddress-bufferDeviceAddress-03324 if !device.enabled_features().buffer_device_address { return Err(BufferError::RequirementNotMet { required_for: "`Buffer::device_address`", requires_one_of: RequiresOneOf { features: &["buffer_device_address"], ..Default::default() }, }); } // VUID-VkBufferDeviceAddressInfo-buffer-02601 if !self.usage().intersects(BufferUsage::SHADER_DEVICE_ADDRESS) { return Err(BufferError::BufferMissingUsage); } let info = ash::vk::BufferDeviceAddressInfo { buffer: self.handle(), ..Default::default() }; let fns = device.fns(); let f = if device.api_version() >= Version::V1_2 { fns.v1_2.get_buffer_device_address } else if device.enabled_extensions().khr_buffer_device_address { fns.khr_buffer_device_address.get_buffer_device_address_khr } else { fns.ext_buffer_device_address.get_buffer_device_address_ext }; let ptr = unsafe { f(device.handle(), &info) }; Ok(NonZeroDeviceSize::new(ptr).unwrap()) } pub(crate) fn state(&self) -> MutexGuard<'_, BufferState> { self.state.lock() } } unsafe impl VulkanObject for Buffer { type Handle = ash::vk::Buffer; #[inline] fn handle(&self) -> Self::Handle { self.inner.handle() } } unsafe impl DeviceOwned for Buffer { #[inline] fn device(&self) -> &Arc { self.inner.device() } } impl PartialEq for Buffer { #[inline] fn eq(&self, other: &Self) -> bool { self.inner == other.inner } } impl Eq for Buffer {} impl Hash for Buffer { fn hash(&self, state: &mut H) { self.inner.hash(state); } } /// The current state of a buffer. #[derive(Debug)] pub(crate) struct BufferState { ranges: RangeMap, } impl BufferState { fn new(size: DeviceSize) -> Self { BufferState { ranges: [( 0..size, BufferRangeState { current_access: CurrentAccess::Shared { cpu_reads: 0, gpu_reads: 0, }, }, )] .into_iter() .collect(), } } pub(crate) fn check_cpu_read(&self, range: Range) -> Result<(), ReadLockError> { for (_range, state) in self.ranges.range(&range) { match &state.current_access { CurrentAccess::CpuExclusive { .. } => return Err(ReadLockError::CpuWriteLocked), CurrentAccess::GpuExclusive { .. } => return Err(ReadLockError::GpuWriteLocked), CurrentAccess::Shared { .. } => (), } } Ok(()) } pub(crate) unsafe fn cpu_read_lock(&mut self, range: Range) { self.ranges.split_at(&range.start); self.ranges.split_at(&range.end); for (_range, state) in self.ranges.range_mut(&range) { match &mut state.current_access { CurrentAccess::Shared { cpu_reads, .. } => { *cpu_reads += 1; } _ => unreachable!("Buffer is being written by the CPU or GPU"), } } } pub(crate) unsafe fn cpu_read_unlock(&mut self, range: Range) { self.ranges.split_at(&range.start); self.ranges.split_at(&range.end); for (_range, state) in self.ranges.range_mut(&range) { match &mut state.current_access { CurrentAccess::Shared { cpu_reads, .. } => *cpu_reads -= 1, _ => unreachable!("Buffer was not locked for CPU read"), } } } pub(crate) fn check_cpu_write(&self, range: Range) -> Result<(), WriteLockError> { for (_range, state) in self.ranges.range(&range) { match &state.current_access { CurrentAccess::CpuExclusive => return Err(WriteLockError::CpuLocked), CurrentAccess::GpuExclusive { .. } => return Err(WriteLockError::GpuLocked), CurrentAccess::Shared { cpu_reads: 0, gpu_reads: 0, } => (), CurrentAccess::Shared { cpu_reads, .. } if *cpu_reads > 0 => { return Err(WriteLockError::CpuLocked) } CurrentAccess::Shared { .. } => return Err(WriteLockError::GpuLocked), } } Ok(()) } pub(crate) unsafe fn cpu_write_lock(&mut self, range: Range) { self.ranges.split_at(&range.start); self.ranges.split_at(&range.end); for (_range, state) in self.ranges.range_mut(&range) { state.current_access = CurrentAccess::CpuExclusive; } } pub(crate) unsafe fn cpu_write_unlock(&mut self, range: Range) { self.ranges.split_at(&range.start); self.ranges.split_at(&range.end); for (_range, state) in self.ranges.range_mut(&range) { match &mut state.current_access { CurrentAccess::CpuExclusive => { state.current_access = CurrentAccess::Shared { cpu_reads: 0, gpu_reads: 0, } } _ => unreachable!("Buffer was not locked for CPU write"), } } } pub(crate) fn check_gpu_read(&self, range: Range) -> Result<(), AccessError> { for (_range, state) in self.ranges.range(&range) { match &state.current_access { CurrentAccess::Shared { .. } => (), _ => return Err(AccessError::AlreadyInUse), } } Ok(()) } pub(crate) unsafe fn gpu_read_lock(&mut self, range: Range) { self.ranges.split_at(&range.start); self.ranges.split_at(&range.end); for (_range, state) in self.ranges.range_mut(&range) { match &mut state.current_access { CurrentAccess::GpuExclusive { gpu_reads, .. } | CurrentAccess::Shared { gpu_reads, .. } => *gpu_reads += 1, _ => unreachable!("Buffer is being written by the CPU"), } } } pub(crate) unsafe fn gpu_read_unlock(&mut self, range: Range) { self.ranges.split_at(&range.start); self.ranges.split_at(&range.end); for (_range, state) in self.ranges.range_mut(&range) { match &mut state.current_access { CurrentAccess::GpuExclusive { gpu_reads, .. } => *gpu_reads -= 1, CurrentAccess::Shared { gpu_reads, .. } => *gpu_reads -= 1, _ => unreachable!("Buffer was not locked for GPU read"), } } } pub(crate) fn check_gpu_write(&self, range: Range) -> Result<(), AccessError> { for (_range, state) in self.ranges.range(&range) { match &state.current_access { CurrentAccess::Shared { cpu_reads: 0, gpu_reads: 0, } => (), _ => return Err(AccessError::AlreadyInUse), } } Ok(()) } pub(crate) unsafe fn gpu_write_lock(&mut self, range: Range) { self.ranges.split_at(&range.start); self.ranges.split_at(&range.end); for (_range, state) in self.ranges.range_mut(&range) { match &mut state.current_access { CurrentAccess::GpuExclusive { gpu_writes, .. } => *gpu_writes += 1, &mut CurrentAccess::Shared { cpu_reads: 0, gpu_reads, } => { state.current_access = CurrentAccess::GpuExclusive { gpu_reads, gpu_writes: 1, } } _ => unreachable!("Buffer is being accessed by the CPU"), } } } pub(crate) unsafe fn gpu_write_unlock(&mut self, range: Range) { self.ranges.split_at(&range.start); self.ranges.split_at(&range.end); for (_range, state) in self.ranges.range_mut(&range) { match &mut state.current_access { &mut CurrentAccess::GpuExclusive { gpu_reads, gpu_writes: 1, } => { state.current_access = CurrentAccess::Shared { cpu_reads: 0, gpu_reads, } } CurrentAccess::GpuExclusive { gpu_writes, .. } => *gpu_writes -= 1, _ => unreachable!("Buffer was not locked for GPU write"), } } } } /// The current state of a specific range of bytes in a buffer. #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct BufferRangeState { current_access: CurrentAccess, } /// Error that can happen in buffer functions. #[derive(Clone, Debug, PartialEq, Eq)] pub enum BufferError { VulkanError(VulkanError), /// Allocating memory failed. AllocError(AllocationCreationError), RequirementNotMet { required_for: &'static str, requires_one_of: RequiresOneOf, }, /// The buffer is missing the `SHADER_DEVICE_ADDRESS` usage. BufferMissingUsage, /// The memory was created dedicated to a resource, but not to this buffer. DedicatedAllocationMismatch, /// A dedicated allocation is required for this buffer, but one was not provided. DedicatedAllocationRequired, /// The host is already using this buffer in a way that is incompatible with the /// requested access. InUseByHost, /// The device is already using this buffer in a way that is incompatible with the /// requested access. InUseByDevice, /// The specified size exceeded the value of the `max_buffer_size` limit. MaxBufferSizeExceeded { size: DeviceSize, max: DeviceSize, }, /// The offset of the allocation does not have the required alignment. MemoryAllocationNotAligned { allocation_offset: DeviceSize, required_alignment: DeviceAlignment, }, /// The size of the allocation is smaller than what is required. MemoryAllocationTooSmall { allocation_size: DeviceSize, required_size: DeviceSize, }, /// The buffer was created with the `SHADER_DEVICE_ADDRESS` usage, but the memory does not /// support this usage. MemoryBufferDeviceAddressNotSupported, /// The memory was created with export handle types, but none of these handle types were /// enabled on the buffer. MemoryExternalHandleTypesDisjoint { buffer_handle_types: ExternalMemoryHandleTypes, memory_export_handle_types: ExternalMemoryHandleTypes, }, /// The memory was created with an import, but the import's handle type was not enabled on /// the buffer. MemoryImportedHandleTypeNotEnabled { buffer_handle_types: ExternalMemoryHandleTypes, memory_imported_handle_type: ExternalMemoryHandleType, }, /// The memory backing this buffer is not visible to the host. MemoryNotHostVisible, /// The protection of buffer and memory are not equal. MemoryProtectedMismatch { buffer_protected: bool, memory_protected: bool, }, /// The provided memory type is not one of the allowed memory types that can be bound to this /// buffer. MemoryTypeNotAllowed { provided_memory_type_index: u32, allowed_memory_type_bits: u32, }, /// The sharing mode was set to `Concurrent`, but one of the specified queue family indices was /// out of range. SharingQueueFamilyIndexOutOfRange { queue_family_index: u32, queue_family_count: u32, }, } impl Error for BufferError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { Self::VulkanError(err) => Some(err), Self::AllocError(err) => Some(err), _ => None, } } } impl Display for BufferError { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { match self { Self::VulkanError(_) => write!(f, "a runtime error occurred"), Self::AllocError(_) => write!(f, "allocating memory failed"), 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 is missing the `SHADER_DEVICE_ADDRESS` usage") } Self::DedicatedAllocationMismatch => write!( f, "the memory was created dedicated to a resource, but not to this buffer", ), Self::DedicatedAllocationRequired => write!( f, "a dedicated allocation is required for this buffer, but one was not provided" ), Self::InUseByHost => write!( f, "the host is already using this buffer in a way that is incompatible with the \ requested access", ), Self::InUseByDevice => write!( f, "the device is already using this buffer in a way that is incompatible with the \ requested access" ), Self::MaxBufferSizeExceeded { .. } => write!( f, "the specified size exceeded the value of the `max_buffer_size` limit", ), Self::MemoryAllocationNotAligned { allocation_offset, required_alignment, } => write!( f, "the offset of the allocation ({}) does not have the required alignment ({:?})", allocation_offset, required_alignment, ), Self::MemoryAllocationTooSmall { allocation_size, required_size, } => write!( f, "the size of the allocation ({}) is smaller than what is required ({})", allocation_size, required_size, ), Self::MemoryBufferDeviceAddressNotSupported => write!( f, "the buffer was created with the `SHADER_DEVICE_ADDRESS` usage, but the memory \ does not support this usage", ), Self::MemoryExternalHandleTypesDisjoint { .. } => write!( f, "the memory was created with export handle types, but none of these handle types \ were enabled on the buffer", ), Self::MemoryImportedHandleTypeNotEnabled { .. } => write!( f, "the memory was created with an import, but the import's handle type was not \ enabled on the buffer", ), Self::MemoryNotHostVisible => write!( f, "the memory backing this buffer is not visible to the host", ), Self::MemoryProtectedMismatch { buffer_protected, memory_protected, } => write!( f, "the protection of buffer ({}) and memory ({}) are not equal", buffer_protected, memory_protected, ), Self::MemoryTypeNotAllowed { provided_memory_type_index, allowed_memory_type_bits, } => write!( f, "the provided memory type ({}) is not one of the allowed memory types (", provided_memory_type_index, ) .and_then(|_| { let mut first = true; for i in (0..size_of_val(allowed_memory_type_bits)) .filter(|i| allowed_memory_type_bits & (1 << i) != 0) { if first { write!(f, "{}", i)?; first = false; } else { write!(f, ", {}", i)?; } } Ok(()) }) .and_then(|_| write!(f, ") that can be bound to this buffer")), Self::SharingQueueFamilyIndexOutOfRange { .. } => write!( f, "the sharing mode was set to `Concurrent`, but one of the specified queue family \ indices was out of range", ), } } } impl From for BufferError { fn from(err: VulkanError) -> Self { Self::VulkanError(err) } } impl From for BufferError { fn from(err: AllocationCreationError) -> Self { Self::AllocError(err) } } impl From for BufferError { fn from(err: RequirementNotMet) -> Self { Self::RequirementNotMet { required_for: err.required_for, requires_one_of: err.requires_one_of, } } } impl From for BufferError { fn from(err: ReadLockError) -> Self { match err { ReadLockError::CpuWriteLocked => Self::InUseByHost, ReadLockError::GpuWriteLocked => Self::InUseByDevice, } } } impl From for BufferError { fn from(err: WriteLockError) -> Self { match err { WriteLockError::CpuLocked => Self::InUseByHost, WriteLockError::GpuLocked => Self::InUseByDevice, } } } vulkan_bitflags! { #[non_exhaustive] /// Flags to be set when creating a buffer. BufferCreateFlags = BufferCreateFlags(u32); /* TODO: enable /// The buffer will be backed by sparse memory binding (through queue commands) instead of /// regular binding (through [`bind_memory`]). /// /// The [`sparse_binding`] feature must be enabled on the device. /// /// [`bind_memory`]: sys::RawBuffer::bind_memory /// [`sparse_binding`]: crate::device::Features::sparse_binding SPARSE_BINDING = SPARSE_BINDING,*/ /* TODO: enable /// The buffer can be used without being fully resident in memory at the time of use. /// /// This requires the `sparse_binding` flag as well. /// /// The [`sparse_residency_buffer`] feature must be enabled on the device. /// /// [`sparse_residency_buffer`]: crate::device::Features::sparse_residency_buffer SPARSE_RESIDENCY = SPARSE_RESIDENCY,*/ /* TODO: enable /// The buffer's memory can alias with another buffer or a different part of the same buffer. /// /// This requires the `sparse_binding` flag as well. /// /// The [`sparse_residency_aliased`] feature must be enabled on the device. /// /// [`sparse_residency_aliased`]: crate::device::Features::sparse_residency_aliased SPARSE_ALIASED = SPARSE_ALIASED,*/ /* TODO: enable /// The buffer is protected, and can only be used in combination with protected memory and other /// protected objects. /// /// The device API version must be at least 1.1. PROTECTED = PROTECTED { api_version: V1_1, },*/ /* TODO: enable /// The buffer's device address can be saved and reused on a subsequent run. /// /// The device API version must be at least 1.2, or either the [`khr_buffer_device_address`] or /// [`ext_buffer_device_address`] extension must be enabled on the device. DEVICE_ADDRESS_CAPTURE_REPLAY = DEVICE_ADDRESS_CAPTURE_REPLAY { api_version: V1_2, device_extensions: [khr_buffer_device_address, ext_buffer_device_address], },*/ } /// The buffer configuration to query in /// [`PhysicalDevice::external_buffer_properties`](crate::device::physical::PhysicalDevice::external_buffer_properties). #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct ExternalBufferInfo { /// The external handle type that will be used with the buffer. pub handle_type: ExternalMemoryHandleType, /// The usage that the buffer will have. pub usage: BufferUsage, /// The sparse binding parameters that will be used. pub sparse: Option, pub _ne: crate::NonExhaustive, } impl ExternalBufferInfo { /// Returns an `ExternalBufferInfo` with the specified `handle_type`. #[inline] pub fn handle_type(handle_type: ExternalMemoryHandleType) -> Self { Self { handle_type, usage: BufferUsage::empty(), sparse: None, _ne: crate::NonExhaustive(()), } } } /// The external memory properties supported for buffers with a given configuration. #[derive(Clone, Debug)] #[non_exhaustive] pub struct ExternalBufferProperties { /// The properties for external memory. pub external_memory_properties: ExternalMemoryProperties, }