Add validation for binding dedicated allocations (#2056)

This commit is contained in:
marc0246 2022-10-30 08:01:36 +01:00 committed by GitHub
parent 9c18a0cb23
commit bb495cceb1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 107 additions and 102 deletions

View File

@ -21,7 +21,7 @@ use crate::{
device::{Device, DeviceOwned}, device::{Device, DeviceOwned},
memory::{ memory::{
allocator::{AllocationCreationError, MemoryAlloc}, allocator::{AllocationCreationError, MemoryAlloc},
ExternalMemoryHandleType, ExternalMemoryHandleTypes, MemoryRequirements, DedicatedTo, ExternalMemoryHandleType, ExternalMemoryHandleTypes, MemoryRequirements,
}, },
range_map::RangeMap, range_map::RangeMap,
sync::{AccessError, CurrentAccess, Sharing}, sync::{AccessError, CurrentAccess, Sharing},
@ -400,12 +400,12 @@ impl RawBuffer {
} }
} }
pub(crate) fn id(&self) -> NonZeroU64 {
self.id
}
/// Binds device memory to this buffer. /// Binds device memory to this buffer.
/// pub fn bind_memory(
/// # Safety
///
/// - If `allocation` is a dedicated allocation, it must be dedicated to `self` specifically.
pub unsafe fn bind_memory(
self, self,
allocation: MemoryAlloc, allocation: MemoryAlloc,
) -> Result<Buffer, (BufferError, RawBuffer, MemoryAlloc)> { ) -> Result<Buffer, (BufferError, RawBuffer, MemoryAlloc)> {
@ -413,7 +413,7 @@ impl RawBuffer {
return Err((err, self, allocation)); return Err((err, self, allocation));
} }
self.bind_memory_unchecked(allocation) unsafe { self.bind_memory_unchecked(allocation) }
.map_err(|(err, buffer, allocation)| (err.into(), buffer, allocation)) .map_err(|(err, buffer, allocation)| (err.into(), buffer, allocation))
} }
@ -464,9 +464,12 @@ impl RawBuffer {
}); });
} }
if allocation.is_dedicated() { if let Some(dedicated_to) = memory.dedicated_to() {
// VUID-VkBindBufferMemoryInfo-memory-01508 // VUID-VkBindBufferMemoryInfo-memory-01508
// TODO: Check that the allocation is dedicated *to this buffer specifically*. match dedicated_to {
DedicatedTo::Buffer(id) if id == self.id => {}
_ => return Err(BufferError::DedicatedAllocationMismatch),
}
debug_assert!(memory_offset == 0); // This should be ensured by the allocator debug_assert!(memory_offset == 0); // This should be ensured by the allocator
} else { } else {
// VUID-VkBindBufferMemoryInfo-buffer-01444 // VUID-VkBindBufferMemoryInfo-buffer-01444
@ -1164,7 +1167,7 @@ impl<'a> DerefMut for BufferWriteGuard<'a> {
} }
} }
/// Error that can happen when creating a buffer. /// Error that can happen in buffer functions.
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum BufferError { pub enum BufferError {
VulkanError(VulkanError), VulkanError(VulkanError),
@ -1177,6 +1180,9 @@ pub enum BufferError {
requires_one_of: RequiresOneOf, requires_one_of: RequiresOneOf,
}, },
/// 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. /// A dedicated allocation is required for this buffer, but one was not provided.
DedicatedAllocationRequired, DedicatedAllocationRequired,
@ -1271,6 +1277,10 @@ impl Display for BufferError {
"a requirement was not met for: {}; requires one of: {}", "a requirement was not met for: {}; requires one of: {}",
required_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!( Self::DedicatedAllocationRequired => write!(
f, f,
"a dedicated allocation is required for this buffer, but one was not provided" "a dedicated allocation is required for this buffer, but one was not provided"

View File

@ -29,7 +29,7 @@ use crate::{
}, },
memory::{ memory::{
allocator::{AllocationCreationError, MemoryAlloc}, allocator::{AllocationCreationError, MemoryAlloc},
ExternalMemoryHandleType, ExternalMemoryHandleTypes, MemoryRequirements, DedicatedTo, ExternalMemoryHandleType, ExternalMemoryHandleTypes, MemoryRequirements,
}, },
range_map::RangeMap, range_map::RangeMap,
swapchain::Swapchain, swapchain::Swapchain,
@ -1301,19 +1301,18 @@ impl RawImage {
} }
} }
pub(crate) fn id(&self) -> NonZeroU64 {
self.id
}
/// Binds device memory to this image. /// Binds device memory to this image.
/// ///
/// - If `self.flags().disjoint` is not set, then `allocations` must contain exactly one /// - If `self.flags().disjoint` is not set, then `allocations` must contain exactly one
/// element. This element may be a dedicated allocation. /// element. This element may be a dedicated allocation.
/// - If `self.flags().disjoint` is set, then `allocations` must contain exactly /// - If `self.flags().disjoint` is set, then `allocations` must contain exactly
/// `self.format().unwrap().planes().len()` elements. These elements must not be dedicated /// `self.format().unwrap().planes().len()` elements. These elements must not be dedicated
/// allocations. /// allocations.
/// pub fn bind_memory(
/// # Safety
///
/// - If the element of `allocations` is a dedicated allocation, it must be dedicated to `self`
/// specifically.
pub unsafe fn bind_memory(
self, self,
allocations: impl IntoIterator<Item = MemoryAlloc>, allocations: impl IntoIterator<Item = MemoryAlloc>,
) -> Result< ) -> Result<
@ -1330,17 +1329,16 @@ impl RawImage {
return Err((err, self, allocations.into_iter())); return Err((err, self, allocations.into_iter()));
} }
self.bind_memory_unchecked(allocations) unsafe { self.bind_memory_unchecked(allocations) }.map_err(|(err, image, allocations)| {
.map_err(|(err, image, allocations)| { (
( err.into(),
err.into(), image,
image, allocations
allocations .into_iter()
.into_iter() .collect::<SmallVec<[_; 3]>>()
.collect::<SmallVec<[_; 3]>>() .into_iter(),
.into_iter(), )
) })
})
} }
fn validate_bind_memory(&self, allocations: &[MemoryAlloc]) -> Result<(), ImageError> { fn validate_bind_memory(&self, allocations: &[MemoryAlloc]) -> Result<(), ImageError> {
@ -1379,15 +1377,18 @@ impl RawImage {
// Ensured by taking ownership of `RawImage`. // Ensured by taking ownership of `RawImage`.
// VUID-VkBindImageMemoryInfo-image-01045 // VUID-VkBindImageMemoryInfo-image-01045
// Currently ensured by not having sparse binding flags, but this needs to be checked once // Currently ensured by not having sparse binding flags, but this needs to be checked
// those are enabled. // once those are enabled.
// VUID-VkBindImageMemoryInfo-memoryOffset-01046 // VUID-VkBindImageMemoryInfo-memoryOffset-01046
// Assume that `allocation` was created correctly. // Assume that `allocation` was created correctly.
if allocation.is_dedicated() { if let Some(dedicated_to) = memory.dedicated_to() {
// VUID-VkBindImageMemoryInfo-memory-02628 // VUID-VkBindImageMemoryInfo-memory-02628
// TODO: Check that the allocation is dedicated *to this buffer specifically*. match dedicated_to {
DedicatedTo::Image(id) if id == self.id => {}
_ => return Err(ImageError::DedicatedAllocationMismatch),
}
debug_assert!(memory_offset == 0); // This should be ensured by the allocator debug_assert!(memory_offset == 0); // This should be ensured by the allocator
} else { } else {
// VUID-VkBindImageMemoryInfo-image-01445 // VUID-VkBindImageMemoryInfo-image-01445
@ -1469,7 +1470,7 @@ impl RawImage {
/// ///
/// - If `self.flags().disjoint` is not set, then `allocations` must contain exactly one /// - If `self.flags().disjoint` is not set, then `allocations` must contain exactly one
/// element. /// element.
/// - If `self.flags().disjoint` is set, then `allocations` must contain exactly /// - If `self.flags().disjoint` is set, then `allocations` must contain exactly
/// `self.format().unwrap().planes().len()` elements. /// `self.format().unwrap().planes().len()` elements.
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn bind_memory_unchecked( pub unsafe fn bind_memory_unchecked(
@ -2770,6 +2771,9 @@ pub enum ImageError {
/// The `cube_compatible` flag was enabled together with multisampling. /// The `cube_compatible` flag was enabled together with multisampling.
CubeCompatibleMultisampling, CubeCompatibleMultisampling,
/// The memory was created dedicated to a resource, but not to this image.
DedicatedAllocationMismatch,
/// A dedicated allocation is required for this image, but one was not provided. /// A dedicated allocation is required for this image, but one was not provided.
DedicatedAllocationRequired, DedicatedAllocationRequired,
@ -2997,6 +3001,10 @@ impl Display for ImageError {
f, f,
"the `cube_compatible` flag was enabled together with multisampling", "the `cube_compatible` flag was enabled together with multisampling",
), ),
Self::DedicatedAllocationMismatch => write!(
f,
"the memory was created dedicated to a resource, but not to this image",
),
Self::DedicatedAllocationRequired => write!( Self::DedicatedAllocationRequired => write!(
f, f,
"a dedicated allocation is required for this image, but one was not provided" "a dedicated allocation is required for this image, but one was not provided"

View File

@ -1149,28 +1149,13 @@ unsafe impl<S: Suballocator> MemoryAllocator for GenericMemoryAllocator<S> {
}; };
match DeviceMemory::allocate_unchecked(self.device.clone(), allocate_info, None) { match DeviceMemory::allocate_unchecked(self.device.clone(), allocate_info, None) {
Ok(device_memory) => { Ok(device_memory) => {
break S::new(MemoryAlloc::new_root(device_memory)?); break S::new(MemoryAlloc::new(device_memory)?);
} }
// Retry up to 3 times, halving the allocation size each time. // Retry up to 3 times, halving the allocation size each time.
Err(VulkanError::OutOfHostMemory | VulkanError::OutOfDeviceMemory) if i < 3 => { Err(VulkanError::OutOfHostMemory | VulkanError::OutOfDeviceMemory) if i < 3 => {
i += 1; i += 1;
} }
Err(VulkanError::OutOfHostMemory) => { Err(err) => return Err(err.into()),
return Err(AllocationCreationError::VulkanError(
VulkanError::OutOfHostMemory,
));
}
Err(VulkanError::OutOfDeviceMemory) => {
return Err(AllocationCreationError::VulkanError(
VulkanError::OutOfDeviceMemory,
));
}
Err(VulkanError::TooManyObjects) => {
return Err(AllocationCreationError::VulkanError(
VulkanError::TooManyObjects,
));
}
Err(_) => unreachable!(),
} }
} }
}; };
@ -1381,7 +1366,6 @@ unsafe impl<S: Suballocator> MemoryAllocator for GenericMemoryAllocator<S> {
dedicated_allocation = None; dedicated_allocation = None;
} }
let is_dedicated = dedicated_allocation.is_some();
let allocate_info = MemoryAllocateInfo { let allocate_info = MemoryAllocateInfo {
allocation_size, allocation_size,
memory_type_index, memory_type_index,
@ -1390,26 +1374,13 @@ unsafe impl<S: Suballocator> MemoryAllocator for GenericMemoryAllocator<S> {
flags: self.flags, flags: self.flags,
..Default::default() ..Default::default()
}; };
let device_memory = let mut alloc = MemoryAlloc::new(
DeviceMemory::allocate_unchecked(self.device.clone(), allocate_info, None).map_err( DeviceMemory::allocate_unchecked(self.device.clone(), allocate_info, None)
|err| match err { .map_err(AllocationCreationError::from)?,
VulkanError::OutOfHostMemory => { )?;
AllocationCreationError::VulkanError(VulkanError::OutOfHostMemory) alloc.set_allocation_type(self.allocation_type);
}
VulkanError::OutOfDeviceMemory => {
AllocationCreationError::VulkanError(VulkanError::OutOfDeviceMemory)
}
VulkanError::TooManyObjects => {
AllocationCreationError::VulkanError(VulkanError::TooManyObjects)
}
_ => unreachable!(),
},
)?;
MemoryAlloc::new_inner(device_memory, is_dedicated).map(|mut alloc| { Ok(alloc)
alloc.set_allocation_type(self.allocation_type);
alloc
})
} }
} }
@ -1449,7 +1420,7 @@ pub struct GenericMemoryAllocatorCreateInfo<'b, 'e> {
/// all suballocations that the pool allocator makes inherit their allocation type from the /// all suballocations that the pool allocator makes inherit their allocation type from the
/// parent allocation. For the [`FreeListAllocator`] and the [`BuddyAllocator`] this must be /// parent allocation. For the [`FreeListAllocator`] and the [`BuddyAllocator`] this must be
/// [`AllocationType::Unknown`] otherwise you will get panics. It does not matter what this is /// [`AllocationType::Unknown`] otherwise you will get panics. It does not matter what this is
/// for when using the [`BumpAllocator`]. /// when using the [`BumpAllocator`].
/// ///
/// The default value is [`AllocationType::Unknown`]. /// The default value is [`AllocationType::Unknown`].
pub allocation_type: AllocationType, pub allocation_type: AllocationType,

View File

@ -43,10 +43,11 @@ use std::{
/// ///
/// There's a few ways you can obtain a `MemoryAlloc` in Vulkano. Most commonly you will probably /// There's a few ways you can obtain a `MemoryAlloc` in Vulkano. Most commonly you will probably
/// want to use a [memory allocator]. If you already have a [`DeviceMemory`] block on hand that you /// want to use a [memory allocator]. If you already have a [`DeviceMemory`] block on hand that you
/// would like to turn into an allocation, you can use one of the constructors. Lastly, you can use /// would like to turn into an allocation, you can use [the constructor]. Lastly, you can use a
/// a [suballocator] if you want to create multiple smaller allocations out of a bigger one. /// [suballocator] if you want to create multiple smaller allocations out of a bigger one.
/// ///
/// [memory allocator]: super::MemoryAllocator /// [memory allocator]: super::MemoryAllocator
/// [the constructor]: Self::new
/// [suballocator]: Suballocator /// [suballocator]: Suballocator
#[derive(Debug)] #[derive(Debug)]
pub struct MemoryAlloc { pub struct MemoryAlloc {
@ -89,26 +90,11 @@ unsafe impl Send for MemoryAlloc {}
unsafe impl Sync for MemoryAlloc {} unsafe impl Sync for MemoryAlloc {}
impl MemoryAlloc { impl MemoryAlloc {
/// Creates a new root allocation. /// Creates a new `MemoryAlloc`.
/// ///
/// The memory is mapped automatically if it's host-visible. /// The memory is mapped automatically if it's host-visible.
#[inline] #[inline]
pub fn new_root(device_memory: DeviceMemory) -> Result<Self, AllocationCreationError> { pub fn new(device_memory: DeviceMemory) -> Result<Self, AllocationCreationError> {
Self::new_inner(device_memory, false)
}
/// Creates a new dedicated allocation.
///
/// The memory is mapped automatically if it's host-visible.
#[inline]
pub fn new_dedicated(device_memory: DeviceMemory) -> Result<Self, AllocationCreationError> {
Self::new_inner(device_memory, true)
}
pub(super) fn new_inner(
device_memory: DeviceMemory,
dedicated: bool,
) -> Result<Self, AllocationCreationError> {
let device = device_memory.device(); let device = device_memory.device();
let physical_device = device.physical_device(); let physical_device = device.physical_device();
let memory_type_index = device_memory.memory_type_index(); let memory_type_index = device_memory.memory_type_index();
@ -159,7 +145,7 @@ impl MemoryAlloc {
allocation_type: AllocationType::Unknown, allocation_type: AllocationType::Unknown,
mapped_ptr, mapped_ptr,
atom_size, atom_size,
parent: if dedicated { parent: if device_memory.is_dedicated() {
AllocParent::Dedicated(device_memory) AllocParent::Dedicated(device_memory)
} else { } else {
AllocParent::Root(Arc::new(device_memory)) AllocParent::Root(Arc::new(device_memory))
@ -649,7 +635,7 @@ unsafe impl DeviceOwned for MemoryAlloc {
/// }) /// })
/// .unwrap() as u32; /// .unwrap() as u32;
/// ///
/// let region = MemoryAlloc::new_root( /// let region = MemoryAlloc::new(
/// DeviceMemory::allocate( /// DeviceMemory::allocate(
/// device.clone(), /// device.clone(),
/// MemoryAllocateInfo { /// MemoryAllocateInfo {
@ -659,7 +645,8 @@ unsafe impl DeviceOwned for MemoryAlloc {
/// }, /// },
/// ) /// )
/// .unwrap(), /// .unwrap(),
/// ); /// )
/// .unwrap();
/// ///
/// // You can now feed `region` into any suballocator. /// // You can now feed `region` into any suballocator.
/// ``` /// ```
@ -712,7 +699,6 @@ pub unsafe trait Suballocator: DeviceOwned {
/// - `create_info.alignment` must be a power of two. /// - `create_info.alignment` must be a power of two.
/// ///
/// [region]: Self#regions /// [region]: Self#regions
/// [`allocate`]: Self::allocate
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
unsafe fn allocate_unchecked( unsafe fn allocate_unchecked(
&self, &self,
@ -2746,7 +2732,7 @@ mod tests {
) )
.unwrap(); .unwrap();
PoolAllocator::new(MemoryAlloc::new_root(device_memory).unwrap(), 1) PoolAllocator::new(MemoryAlloc::new(device_memory).unwrap(), 1)
} }
let (device, _) = gfx_dev_and_queue!(); let (device, _) = gfx_dev_and_queue!();
@ -2804,7 +2790,7 @@ mod tests {
) )
.unwrap(); .unwrap();
PoolAllocator::<BLOCK_SIZE>::new(MemoryAlloc::new_root(device_memory).unwrap(), 1) PoolAllocator::<BLOCK_SIZE>::new(MemoryAlloc::new(device_memory).unwrap(), 1)
}; };
// This uses the fact that block indices are inserted into the free-list in order, so // This uses the fact that block indices are inserted into the free-list in order, so
@ -2837,7 +2823,7 @@ mod tests {
}, },
) )
.unwrap(); .unwrap();
let mut region = MemoryAlloc::new_root(device_memory).unwrap(); let mut region = MemoryAlloc::new(device_memory).unwrap();
unsafe { region.set_allocation_type(allocation_type) }; unsafe { region.set_allocation_type(allocation_type) };
PoolAllocator::new(region, 256) PoolAllocator::new(region, 256)
@ -3096,7 +3082,7 @@ mod tests {
}, },
) )
.unwrap(); .unwrap();
let mut allocator = <$type>::new(MemoryAlloc::new_root(device_memory).unwrap()); let mut allocator = <$type>::new(MemoryAlloc::new(device_memory).unwrap());
Arc::get_mut(&mut allocator) Arc::get_mut(&mut allocator)
.unwrap() .unwrap()
.buffer_image_granularity = $granularity; .buffer_image_granularity = $granularity;

View File

@ -7,7 +7,7 @@
// notice may not be copied, modified, or distributed except // notice may not be copied, modified, or distributed except
// according to those terms. // according to those terms.
use super::DedicatedAllocation; use super::{DedicatedAllocation, DedicatedTo};
use crate::{ use crate::{
device::{Device, DeviceOwned}, device::{Device, DeviceOwned},
macros::{vulkan_bitflags, vulkan_enum}, macros::{vulkan_bitflags, vulkan_enum},
@ -56,6 +56,7 @@ pub struct DeviceMemory {
allocation_size: DeviceSize, allocation_size: DeviceSize,
memory_type_index: u32, memory_type_index: u32,
dedicated_to: Option<DedicatedTo>,
export_handle_types: ExternalMemoryHandleTypes, export_handle_types: ExternalMemoryHandleTypes,
imported_handle_type: Option<ExternalMemoryHandleType>, imported_handle_type: Option<ExternalMemoryHandleType>,
flags: MemoryAllocateFlags, flags: MemoryAllocateFlags,
@ -97,7 +98,7 @@ impl DeviceMemory {
let MemoryAllocateInfo { let MemoryAllocateInfo {
allocation_size, allocation_size,
memory_type_index, memory_type_index,
dedicated_allocation: _, dedicated_allocation,
export_handle_types, export_handle_types,
flags, flags,
_ne: _, _ne: _,
@ -109,6 +110,7 @@ impl DeviceMemory {
id: Self::next_id(), id: Self::next_id(),
allocation_size, allocation_size,
memory_type_index, memory_type_index,
dedicated_to: dedicated_allocation.map(Into::into),
export_handle_types, export_handle_types,
imported_handle_type: None, imported_handle_type: None,
flags, flags,
@ -552,6 +554,7 @@ impl DeviceMemory {
id: Self::next_id(), id: Self::next_id(),
allocation_size, allocation_size,
memory_type_index, memory_type_index,
dedicated_to: dedicated_allocation.map(Into::into),
export_handle_types, export_handle_types,
imported_handle_type, imported_handle_type,
flags, flags,
@ -570,6 +573,18 @@ impl DeviceMemory {
self.allocation_size self.allocation_size
} }
/// Returns `true` if the memory is a [dedicated] to a resource.
///
/// [dedicated]: MemoryAllocateInfo#structfield.dedicated_allocation
#[inline]
pub fn is_dedicated(&self) -> bool {
self.dedicated_to.is_some()
}
pub(crate) fn dedicated_to(&self) -> Option<DedicatedTo> {
self.dedicated_to
}
/// Returns the handle types that can be exported from the memory allocation. /// Returns the handle types that can be exported from the memory allocation.
#[inline] #[inline]
pub fn export_handle_types(&self) -> ExternalMemoryHandleTypes { pub fn export_handle_types(&self) -> ExternalMemoryHandleTypes {

View File

@ -103,7 +103,7 @@ use crate::{
sync::Semaphore, sync::Semaphore,
DeviceSize, DeviceSize,
}; };
use std::sync::Arc; use std::{num::NonZeroU64, sync::Arc};
pub mod allocator; pub mod allocator;
mod device_memory; mod device_memory;
@ -323,6 +323,21 @@ pub enum DedicatedAllocation<'a> {
Image(&'a RawImage), Image(&'a RawImage),
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum DedicatedTo {
Buffer(NonZeroU64),
Image(NonZeroU64),
}
impl From<DedicatedAllocation<'_>> for DedicatedTo {
fn from(dedicated_allocation: DedicatedAllocation<'_>) -> Self {
match dedicated_allocation {
DedicatedAllocation::Buffer(buffer) => Self::Buffer(buffer.id()),
DedicatedAllocation::Image(image) => Self::Image(image.id()),
}
}
}
/// The properties for exporting or importing external memory, when a buffer or image is created /// The properties for exporting or importing external memory, when a buffer or image is created
/// with a specific configuration. /// with a specific configuration.
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]