use super::{
cpu_access::{ReadLockError, WriteLockError},
BufferContents, BufferCreateFlags, BufferUsage,
};
use crate::{
device::{Device, DeviceOwned},
memory::{
allocator::{AllocationCreationError, MemoryAlloc},
DedicatedTo, ExternalMemoryHandleType, ExternalMemoryHandleTypes, MemoryRequirements,
},
range_map::RangeMap,
sync::{AccessError, CurrentAccess, Sharing},
DeviceSize, 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, MaybeUninit},
num::NonZeroU64,
ops::{Deref, DerefMut, Range},
ptr,
sync::Arc,
};
#[derive(Debug)]
pub struct RawBuffer {
handle: ash::vk::Buffer,
device: Arc<Device>,
id: NonZeroU64,
flags: BufferCreateFlags,
size: DeviceSize,
usage: BufferUsage,
sharing: Sharing<SmallVec<[u32; 4]>>,
external_memory_handle_types: ExternalMemoryHandleTypes,
memory_requirements: MemoryRequirements,
}
impl RawBuffer {
#[inline]
pub fn new(
device: Arc<Device>,
mut create_info: BufferCreateInfo,
) -> Result<Self, BufferError> {
match &mut create_info.sharing {
Sharing::Exclusive => (),
Sharing::Concurrent(queue_family_indices) => {
queue_family_indices.sort_unstable();
queue_family_indices.dedup();
}
}
Self::validate_new(&device, &create_info)?;
unsafe { Ok(Self::new_unchecked(device, create_info)?) }
}
fn validate_new(device: &Device, create_info: &BufferCreateInfo) -> Result<(), BufferError> {
let &BufferCreateInfo {
flags,
ref sharing,
size,
usage,
external_memory_handle_types,
_ne: _,
} = create_info;
flags.validate_device(device)?;
usage.validate_device(device)?;
assert!(!usage.is_empty());
assert!(size != 0);
match sharing {
Sharing::Exclusive => (),
Sharing::Concurrent(queue_family_indices) => {
assert!(queue_family_indices.len() >= 2);
for &queue_family_index in queue_family_indices.iter() {
if queue_family_index
>= device.physical_device().queue_family_properties().len() as u32
{
return Err(BufferError::SharingQueueFamilyIndexOutOfRange {
queue_family_index,
queue_family_count: device
.physical_device()
.queue_family_properties()
.len() as u32,
});
}
}
}
}
if let Some(max_buffer_size) = device.physical_device().properties().max_buffer_size {
if size > max_buffer_size {
return Err(BufferError::MaxBufferSizeExceeded {
size,
max: max_buffer_size,
});
}
}
if !external_memory_handle_types.is_empty() {
if !(device.api_version() >= Version::V1_1
|| device.enabled_extensions().khr_external_memory)
{
return Err(BufferError::RequirementNotMet {
required_for: "`create_info.external_memory_handle_types` is not empty",
requires_one_of: RequiresOneOf {
api_version: Some(Version::V1_1),
device_extensions: &["khr_external_memory"],
..Default::default()
},
});
}
external_memory_handle_types.validate_device(device)?;
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn new_unchecked(
device: Arc<Device>,
create_info: BufferCreateInfo,
) -> Result<Self, VulkanError> {
let &BufferCreateInfo {
flags,
ref sharing,
size,
usage,
external_memory_handle_types,
_ne: _,
} = &create_info;
let (sharing_mode, queue_family_index_count, p_queue_family_indices) = match sharing {
Sharing::Exclusive => (ash::vk::SharingMode::EXCLUSIVE, 0, &[] as _),
Sharing::Concurrent(queue_family_indices) => (
ash::vk::SharingMode::CONCURRENT,
queue_family_indices.len() as u32,
queue_family_indices.as_ptr(),
),
};
let mut create_info_vk = ash::vk::BufferCreateInfo {
flags: flags.into(),
size,
usage: usage.into(),
sharing_mode,
queue_family_index_count,
p_queue_family_indices,
..Default::default()
};
let mut external_memory_info_vk = None;
if !external_memory_handle_types.is_empty() {
let _ = external_memory_info_vk.insert(ash::vk::ExternalMemoryBufferCreateInfo {
handle_types: external_memory_handle_types.into(),
..Default::default()
});
}
if let Some(next) = external_memory_info_vk.as_mut() {
next.p_next = create_info_vk.p_next;
create_info_vk.p_next = next as *const _ as *const _;
}
let handle = {
let fns = device.fns();
let mut output = MaybeUninit::uninit();
(fns.v1_0.create_buffer)(
device.handle(),
&create_info_vk,
ptr::null(),
output.as_mut_ptr(),
)
.result()
.map_err(VulkanError::from)?;
output.assume_init()
};
Ok(Self::from_handle(device, handle, create_info))
}
#[inline]
pub unsafe fn from_handle(
device: Arc<Device>,
handle: ash::vk::Buffer,
create_info: BufferCreateInfo,
) -> Self {
fn align(val: DeviceSize, al: DeviceSize) -> DeviceSize {
al * (1 + (val - 1) / al)
}
let BufferCreateInfo {
flags,
size,
usage,
sharing,
external_memory_handle_types,
_ne: _,
} = create_info;
let mut memory_requirements = Self::get_memory_requirements(&device, handle);
debug_assert!(memory_requirements.size >= size);
debug_assert!(memory_requirements.memory_type_bits != 0);
let properties = device.physical_device().properties();
if usage.uniform_texel_buffer || usage.storage_texel_buffer {
memory_requirements.alignment = align(
memory_requirements.alignment,
properties.min_texel_buffer_offset_alignment,
);
}
if usage.storage_buffer {
memory_requirements.alignment = align(
memory_requirements.alignment,
properties.min_storage_buffer_offset_alignment,
);
}
if usage.uniform_buffer {
memory_requirements.alignment = align(
memory_requirements.alignment,
properties.min_uniform_buffer_offset_alignment,
);
}
RawBuffer {
handle,
device,
id: Self::next_id(),
flags,
size,
usage,
sharing,
external_memory_handle_types,
memory_requirements,
}
}
fn get_memory_requirements(device: &Device, handle: ash::vk::Buffer) -> MemoryRequirements {
let info_vk = ash::vk::BufferMemoryRequirementsInfo2 {
buffer: handle,
..Default::default()
};
let mut memory_requirements2_vk = ash::vk::MemoryRequirements2::default();
let mut memory_dedicated_requirements_vk = None;
if device.api_version() >= Version::V1_1
|| device.enabled_extensions().khr_dedicated_allocation
{
debug_assert!(
device.api_version() >= Version::V1_1
|| device.enabled_extensions().khr_get_memory_requirements2
);
let next = memory_dedicated_requirements_vk
.insert(ash::vk::MemoryDedicatedRequirements::default());
next.p_next = memory_requirements2_vk.p_next;
memory_requirements2_vk.p_next = next as *mut _ as *mut _;
}
unsafe {
let fns = device.fns();
if device.api_version() >= Version::V1_1
|| device.enabled_extensions().khr_get_memory_requirements2
{
if device.api_version() >= Version::V1_1 {
(fns.v1_1.get_buffer_memory_requirements2)(
device.handle(),
&info_vk,
&mut memory_requirements2_vk,
);
} else {
(fns.khr_get_memory_requirements2
.get_buffer_memory_requirements2_khr)(
device.handle(),
&info_vk,
&mut memory_requirements2_vk,
);
}
} else {
(fns.v1_0.get_buffer_memory_requirements)(
device.handle(),
handle,
&mut memory_requirements2_vk.memory_requirements,
);
}
}
MemoryRequirements {
size: memory_requirements2_vk.memory_requirements.size,
alignment: memory_requirements2_vk.memory_requirements.alignment,
memory_type_bits: memory_requirements2_vk.memory_requirements.memory_type_bits,
prefers_dedicated_allocation: memory_dedicated_requirements_vk
.map_or(false, |dreqs| dreqs.prefers_dedicated_allocation != 0),
requires_dedicated_allocation: memory_dedicated_requirements_vk
.map_or(false, |dreqs| dreqs.requires_dedicated_allocation != 0),
}
}
pub(crate) fn id(&self) -> NonZeroU64 {
self.id
}
pub fn bind_memory(
self,
allocation: MemoryAlloc,
) -> Result<Buffer, (BufferError, RawBuffer, MemoryAlloc)> {
if let Err(err) = self.validate_bind_memory(&allocation) {
return Err((err, self, allocation));
}
unsafe { self.bind_memory_unchecked(allocation) }
.map_err(|(err, buffer, allocation)| (err.into(), buffer, allocation))
}
fn validate_bind_memory(&self, allocation: &MemoryAlloc) -> Result<(), BufferError> {
let memory_requirements = &self.memory_requirements;
let memory = allocation.device_memory();
let memory_offset = allocation.offset();
let memory_type = &self
.device
.physical_device()
.memory_properties()
.memory_types[memory.memory_type_index() as usize];
assert_eq!(self.device(), memory.device());
if memory_requirements.memory_type_bits & (1 << memory.memory_type_index()) == 0 {
return Err(BufferError::MemoryTypeNotAllowed {
provided_memory_type_index: memory.memory_type_index(),
allowed_memory_type_bits: memory_requirements.memory_type_bits,
});
}
if memory_offset % memory_requirements.alignment != 0 {
return Err(BufferError::MemoryAllocationNotAligned {
allocation_offset: memory_offset,
required_alignment: memory_requirements.alignment,
});
}
if allocation.size() < memory_requirements.size {
return Err(BufferError::MemoryAllocationTooSmall {
allocation_size: allocation.size(),
required_size: memory_requirements.size,
});
}
if let Some(dedicated_to) = memory.dedicated_to() {
match dedicated_to {
DedicatedTo::Buffer(id) if id == self.id => {}
_ => return Err(BufferError::DedicatedAllocationMismatch),
}
debug_assert!(memory_offset == 0); } else {
if memory_requirements.requires_dedicated_allocation {
return Err(BufferError::DedicatedAllocationRequired);
}
}
if memory_type.property_flags.protected {
return Err(BufferError::MemoryProtectedMismatch {
buffer_protected: false,
memory_protected: true,
});
}
if !memory.export_handle_types().is_empty()
&& !memory
.export_handle_types()
.intersects(&self.external_memory_handle_types)
{
return Err(BufferError::MemoryExternalHandleTypesDisjoint {
buffer_handle_types: self.external_memory_handle_types,
memory_export_handle_types: memory.export_handle_types(),
});
}
if let Some(handle_type) = memory.imported_handle_type() {
if !ExternalMemoryHandleTypes::from(handle_type)
.intersects(&self.external_memory_handle_types)
{
return Err(BufferError::MemoryImportedHandleTypeNotEnabled {
buffer_handle_types: self.external_memory_handle_types,
memory_imported_handle_type: handle_type,
});
}
}
if !self.device.enabled_extensions().ext_buffer_device_address
&& self.usage.shader_device_address
&& !memory.flags().device_address
{
return Err(BufferError::MemoryBufferDeviceAddressNotSupported);
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn bind_memory_unchecked(
self,
allocation: MemoryAlloc,
) -> Result<Buffer, (VulkanError, RawBuffer, MemoryAlloc)> {
let memory = allocation.device_memory();
let memory_offset = allocation.offset();
let fns = self.device.fns();
let result = if self.device.api_version() >= Version::V1_1
|| self.device.enabled_extensions().khr_bind_memory2
{
let bind_infos_vk = [ash::vk::BindBufferMemoryInfo {
buffer: self.handle,
memory: memory.handle(),
memory_offset,
..Default::default()
}];
if self.device.api_version() >= Version::V1_1 {
(fns.v1_1.bind_buffer_memory2)(
self.device.handle(),
bind_infos_vk.len() as u32,
bind_infos_vk.as_ptr(),
)
} else {
(fns.khr_bind_memory2.bind_buffer_memory2_khr)(
self.device.handle(),
bind_infos_vk.len() as u32,
bind_infos_vk.as_ptr(),
)
}
} else {
(fns.v1_0.bind_buffer_memory)(
self.device.handle(),
self.handle,
memory.handle(),
memory_offset,
)
}
.result();
if let Err(err) = result {
return Err((VulkanError::from(err), self, allocation));
}
Ok(Buffer::from_raw(self, BufferMemory::Normal(allocation)))
}
pub fn memory_requirements(&self) -> &MemoryRequirements {
&self.memory_requirements
}
#[inline]
pub fn flags(&self) -> BufferCreateFlags {
self.flags
}
#[inline]
pub fn size(&self) -> DeviceSize {
self.size
}
#[inline]
pub fn usage(&self) -> &BufferUsage {
&self.usage
}
#[inline]
pub fn sharing(&self) -> &Sharing<SmallVec<[u32; 4]>> {
&self.sharing
}
#[inline]
pub fn external_memory_handle_types(&self) -> ExternalMemoryHandleTypes {
self.external_memory_handle_types
}
}
impl Drop for RawBuffer {
#[inline]
fn drop(&mut self) {
unsafe {
let fns = self.device.fns();
(fns.v1_0.destroy_buffer)(self.device.handle(), self.handle, ptr::null());
}
}
}
unsafe impl VulkanObject for RawBuffer {
type Handle = ash::vk::Buffer;
#[inline]
fn handle(&self) -> Self::Handle {
self.handle
}
}
unsafe impl DeviceOwned for RawBuffer {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
crate::impl_id_counter!(RawBuffer);
#[derive(Clone, Debug)]
pub struct BufferCreateInfo {
pub flags: BufferCreateFlags,
pub sharing: Sharing<SmallVec<[u32; 4]>>,
pub size: DeviceSize,
pub usage: BufferUsage,
pub external_memory_handle_types: ExternalMemoryHandleTypes,
pub _ne: crate::NonExhaustive,
}
impl Default for BufferCreateInfo {
#[inline]
fn default() -> Self {
Self {
flags: BufferCreateFlags::empty(),
sharing: Sharing::Exclusive,
size: 0,
usage: BufferUsage::empty(),
external_memory_handle_types: ExternalMemoryHandleTypes::empty(),
_ne: crate::NonExhaustive(()),
}
}
}
#[derive(Debug)]
pub struct Buffer {
inner: RawBuffer,
memory: BufferMemory,
state: Mutex<BufferState>,
}
#[derive(Debug)]
pub enum BufferMemory {
Normal(MemoryAlloc),
Sparse,
}
impl Buffer {
fn from_raw(inner: RawBuffer, memory: BufferMemory) -> Self {
let state = Mutex::new(BufferState::new(inner.size));
Buffer {
inner,
memory,
state,
}
}
#[inline]
pub fn memory(&self) -> &BufferMemory {
&self.memory
}
#[inline]
pub fn memory_requirements(&self) -> &MemoryRequirements {
&self.inner.memory_requirements
}
#[inline]
pub fn flags(&self) -> BufferCreateFlags {
self.inner.flags
}
#[inline]
pub fn size(&self) -> DeviceSize {
self.inner.size
}
#[inline]
pub fn usage(&self) -> &BufferUsage {
&self.inner.usage
}
#[inline]
pub fn sharing(&self) -> &Sharing<SmallVec<[u32; 4]>> {
&self.inner.sharing
}
#[inline]
pub fn external_memory_handle_types(&self) -> ExternalMemoryHandleTypes {
self.inner.external_memory_handle_types
}
pub fn read(&self, range: Range<DeviceSize>) -> Result<BufferReadGuard<'_, [u8]>, BufferError> {
assert!(!range.is_empty() && range.end <= self.inner.size);
let allocation = match &self.memory {
BufferMemory::Normal(a) => a,
BufferMemory::Sparse => todo!("`Buffer::read` doesn't support sparse binding yet"),
};
if allocation.mapped_ptr().is_none() {
return Err(BufferError::MemoryNotHostVisible);
}
let mut state = self.state();
unsafe {
state.check_cpu_read(range.clone())?;
state.cpu_read_lock(range.clone());
}
let data = unsafe {
allocation.invalidate_range(0..self.size()).unwrap();
allocation.mapped_slice().unwrap()
};
Ok(BufferReadGuard {
buffer: self,
range,
data,
})
}
pub fn write(&self, range: Range<DeviceSize>) -> Result<BufferWriteGuard<'_>, BufferError> {
assert!(!range.is_empty() && range.end <= self.inner.size);
let allocation = match &self.memory {
BufferMemory::Normal(a) => a,
BufferMemory::Sparse => todo!("`Buffer::write` doesn't support sparse binding yet"),
};
if allocation.mapped_ptr().is_none() {
return Err(BufferError::MemoryNotHostVisible);
}
let mut state = self.state();
unsafe {
state.check_cpu_write(range.clone())?;
state.cpu_write_lock(range.clone());
}
let data = unsafe {
allocation.invalidate_range(0..self.size()).unwrap();
allocation.write(0..self.size()).unwrap()
};
Ok(BufferWriteGuard {
buffer: self,
range,
data,
})
}
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<Device> {
&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<H: Hasher>(&self, state: &mut H) {
self.inner.hash(state);
}
}
#[derive(Debug)]
pub(crate) struct BufferState {
ranges: RangeMap<DeviceSize, BufferRangeState>,
}
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<DeviceSize>) -> 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<DeviceSize>) {
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<DeviceSize>) {
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<DeviceSize>) -> 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<DeviceSize>) {
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<DeviceSize>) {
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<DeviceSize>) -> 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<DeviceSize>) {
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<DeviceSize>) {
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<DeviceSize>) -> 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<DeviceSize>) {
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<DeviceSize>) {
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"),
}
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct BufferRangeState {
current_access: CurrentAccess,
}
#[derive(Debug)]
pub struct BufferReadGuard<'a, T>
where
T: BufferContents + ?Sized,
{
buffer: &'a Buffer,
range: Range<DeviceSize>,
data: &'a T,
}
impl<'a, T> Drop for BufferReadGuard<'a, T>
where
T: BufferContents + ?Sized + 'a,
{
fn drop(&mut self) {
unsafe {
let mut state = self.buffer.state();
state.cpu_read_unlock(self.range.clone());
}
}
}
impl<'a, T> Deref for BufferReadGuard<'a, T>
where
T: BufferContents + ?Sized + 'a,
{
type Target = T;
fn deref(&self) -> &Self::Target {
self.data
}
}
#[derive(Debug)]
pub struct BufferWriteGuard<'a> {
buffer: &'a Buffer,
range: Range<DeviceSize>,
data: &'a mut [u8],
}
impl<'a> Drop for BufferWriteGuard<'a> {
fn drop(&mut self) {
let allocation = match &self.buffer.memory {
BufferMemory::Normal(a) => a,
BufferMemory::Sparse => unreachable!(),
};
unsafe {
allocation.flush_range(0..self.buffer.size()).unwrap();
let mut state = self.buffer.state();
state.cpu_write_unlock(self.range.clone());
}
}
}
impl<'a> Deref for BufferWriteGuard<'a> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.data
}
}
impl<'a> DerefMut for BufferWriteGuard<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.data
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum BufferError {
VulkanError(VulkanError),
AllocError(AllocationCreationError),
RequirementNotMet {
required_for: &'static str,
requires_one_of: RequiresOneOf,
},
DedicatedAllocationMismatch,
DedicatedAllocationRequired,
InUseByHost,
InUseByDevice,
MaxBufferSizeExceeded {
size: DeviceSize,
max: DeviceSize,
},
MemoryAllocationNotAligned {
allocation_offset: DeviceSize,
required_alignment: DeviceSize,
},
MemoryAllocationTooSmall {
allocation_size: DeviceSize,
required_size: DeviceSize,
},
MemoryBufferDeviceAddressNotSupported,
MemoryExternalHandleTypesDisjoint {
buffer_handle_types: ExternalMemoryHandleTypes,
memory_export_handle_types: ExternalMemoryHandleTypes,
},
MemoryImportedHandleTypeNotEnabled {
buffer_handle_types: ExternalMemoryHandleTypes,
memory_imported_handle_type: ExternalMemoryHandleType,
},
MemoryNotHostVisible,
MemoryProtectedMismatch {
buffer_protected: bool,
memory_protected: bool,
},
MemoryTypeNotAllowed {
provided_memory_type_index: u32,
allowed_memory_type_bits: u32,
},
SharingQueueFamilyIndexOutOfRange {
queue_family_index: u32,
queue_family_count: u32,
},
}
impl Error for BufferError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
BufferError::VulkanError(err) => Some(err),
BufferError::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::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<VulkanError> for BufferError {
fn from(err: VulkanError) -> BufferError {
match err {
VulkanError::OutOfHostMemory => BufferError::AllocError(
AllocationCreationError::VulkanError(VulkanError::OutOfHostMemory),
),
VulkanError::OutOfDeviceMemory => BufferError::AllocError(
AllocationCreationError::VulkanError(VulkanError::OutOfDeviceMemory),
),
_ => panic!("unexpected error: {:?}", err),
}
}
}
impl From<RequirementNotMet> for BufferError {
fn from(err: RequirementNotMet) -> Self {
Self::RequirementNotMet {
required_for: err.required_for,
requires_one_of: err.requires_one_of,
}
}
}
impl From<ReadLockError> for BufferError {
fn from(err: ReadLockError) -> Self {
match err {
ReadLockError::CpuWriteLocked => Self::InUseByHost,
ReadLockError::GpuWriteLocked => Self::InUseByDevice,
}
}
}
impl From<WriteLockError> for BufferError {
fn from(err: WriteLockError) -> Self {
match err {
WriteLockError::CpuLocked => Self::InUseByHost,
WriteLockError::GpuLocked => Self::InUseByDevice,
}
}
}
#[cfg(test)]
mod tests {
use super::{BufferCreateInfo, BufferUsage, RawBuffer};
use crate::device::{Device, DeviceOwned};
#[test]
fn create() {
let (device, _) = gfx_dev_and_queue!();
let buf = RawBuffer::new(
device.clone(),
BufferCreateInfo {
size: 128,
usage: BufferUsage {
transfer_dst: true,
..BufferUsage::empty()
},
..Default::default()
},
)
.unwrap();
let reqs = buf.memory_requirements();
assert!(reqs.size >= 128);
assert_eq!(buf.size(), 128);
assert_eq!(&**buf.device() as *const Device, &*device as *const Device);
}
#[test]
fn create_empty_buffer() {
let (device, _) = gfx_dev_and_queue!();
assert_should_panic!({
RawBuffer::new(
device,
BufferCreateInfo {
size: 0,
usage: BufferUsage {
transfer_dst: true,
..BufferUsage::empty()
},
..Default::default()
},
)
});
}
}