Generate device properties that are alignment as DeviceAlignment (#2155)

* Move `DeviceAlignment`

* Generate alignment-properties with the type `DeviceAlignment`

* Small fix

* Another small fix

* Switch to using `is_aligned` where possible

* Oopsie
This commit is contained in:
marc0246 2023-03-15 22:21:44 +01:00 committed by GitHub
parent 19acc71be3
commit 127b3eae61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 384 additions and 363 deletions

View File

@ -143,7 +143,8 @@ fn main() {
let min_dynamic_align = device
.physical_device()
.properties()
.min_uniform_buffer_offset_alignment as usize;
.min_uniform_buffer_offset_alignment
.as_devicesize() as usize;
println!("Minimum uniform buffer offset alignment: {min_dynamic_align}");
println!("Input: {data:?}");

View File

@ -196,6 +196,19 @@ fn properties_members(types: &HashMap<&str, (&Type, Vec<&str>)>) -> Vec<Properti
let vulkano_member = name.to_snake_case();
let vulkano_ty = match name {
"apiVersion" => quote! { Version },
"bufferImageGranularity"
| "minStorageBufferOffsetAlignment"
| "minTexelBufferOffsetAlignment"
| "minUniformBufferOffsetAlignment"
| "nonCoherentAtomSize"
| "optimalBufferCopyOffsetAlignment"
| "optimalBufferCopyRowPitchAlignment"
| "robustStorageBufferAccessSizeAlignment"
| "robustUniformBufferAccessSizeAlignment"
| "storageTexelBufferOffsetAlignmentBytes"
| "uniformTexelBufferOffsetAlignmentBytes" => {
quote! { DeviceAlignment }
}
_ => vulkano_type(ty, len),
};
match properties.entry(vulkano_member.clone()) {

View File

@ -13,9 +13,12 @@ use super::{Buffer, BufferContents, BufferError, BufferMemory, BufferUsage, Subb
use crate::{
buffer::BufferAllocateInfo,
device::{Device, DeviceOwned},
memory::allocator::{
align_up, AllocationCreationError, DeviceAlignment, DeviceLayout, MemoryAllocator,
MemoryUsage, StandardMemoryAllocator,
memory::{
allocator::{
align_up, AllocationCreationError, DeviceLayout, MemoryAllocator, MemoryUsage,
StandardMemoryAllocator,
},
DeviceAlignment,
},
DeviceSize, NonZeroDeviceSize,
};
@ -145,7 +148,6 @@ where
.into_iter()
.flatten()
.max()
.map(|alignment| DeviceAlignment::new(alignment).unwrap())
.unwrap_or(DeviceAlignment::MIN);
SubbufferAllocator {

View File

@ -111,11 +111,11 @@ use crate::{
macros::vulkan_bitflags,
memory::{
allocator::{
AllocationCreateInfo, AllocationCreationError, AllocationType, DeviceAlignment,
DeviceLayout, MemoryAlloc, MemoryAllocatePreference, MemoryAllocator, MemoryUsage,
AllocationCreateInfo, AllocationCreationError, AllocationType, DeviceLayout,
MemoryAlloc, MemoryAllocatePreference, MemoryAllocator, MemoryUsage,
},
DedicatedAllocation, ExternalMemoryHandleType, ExternalMemoryHandleTypes,
ExternalMemoryProperties, MemoryRequirements,
is_aligned, DedicatedAllocation, DeviceAlignment, ExternalMemoryHandleType,
ExternalMemoryHandleTypes, ExternalMemoryProperties, MemoryRequirements,
},
range_map::RangeMap,
sync::{future::AccessError, CurrentAccess, Sharing},
@ -394,7 +394,10 @@ impl Buffer {
};
let mut allocation = unsafe { allocator.allocate_unchecked(create_info) }?;
debug_assert!(allocation.offset() % requirements.layout.alignment().as_nonzero() == 0);
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

View File

@ -15,8 +15,8 @@ use crate::{
macros::try_opt,
memory::{
self,
allocator::{align_down, align_up, DeviceAlignment, DeviceLayout},
is_aligned,
allocator::{align_down, align_up, DeviceLayout},
is_aligned, DeviceAlignment,
},
DeviceSize, NonZeroDeviceSize,
};

View File

@ -17,9 +17,9 @@ use super::{Buffer, BufferCreateFlags, BufferError, BufferMemory, BufferUsage};
use crate::{
device::{Device, DeviceOwned},
memory::{
allocator::{AllocationType, DeviceAlignment, DeviceLayout, MemoryAlloc},
DedicatedTo, ExternalMemoryHandleTypes, MemoryAllocateFlags, MemoryPropertyFlags,
MemoryRequirements,
allocator::{AllocationType, DeviceLayout, MemoryAlloc},
is_aligned, DedicatedTo, ExternalMemoryHandleTypes, MemoryAllocateFlags,
MemoryPropertyFlags, MemoryRequirements,
},
sync::Sharing,
DeviceSize, RequiresOneOf, Version, VulkanError, VulkanObject,
@ -290,27 +290,21 @@ impl RawBuffer {
if usage.intersects(BufferUsage::UNIFORM_TEXEL_BUFFER | BufferUsage::STORAGE_TEXEL_BUFFER) {
memory_requirements.layout = memory_requirements
.layout
.align_to(
DeviceAlignment::new(properties.min_texel_buffer_offset_alignment).unwrap(),
)
.align_to(properties.min_texel_buffer_offset_alignment)
.unwrap();
}
if usage.intersects(BufferUsage::STORAGE_BUFFER) {
memory_requirements.layout = memory_requirements
.layout
.align_to(
DeviceAlignment::new(properties.min_storage_buffer_offset_alignment).unwrap(),
)
.align_to(properties.min_storage_buffer_offset_alignment)
.unwrap();
}
if usage.intersects(BufferUsage::UNIFORM_BUFFER) {
memory_requirements.layout = memory_requirements
.layout
.align_to(
DeviceAlignment::new(properties.min_uniform_buffer_offset_alignment).unwrap(),
)
.align_to(properties.min_uniform_buffer_offset_alignment)
.unwrap();
}
@ -445,7 +439,7 @@ impl RawBuffer {
}
// VUID-VkBindBufferMemoryInfo-memoryOffset-01036
if memory_offset % memory_requirements.layout.alignment().as_nonzero() != 0 {
if !is_aligned(memory_offset, memory_requirements.layout.alignment()) {
return Err(BufferError::MemoryAllocationNotAligned {
allocation_offset: memory_offset,
required_alignment: memory_requirements.layout.alignment(),

View File

@ -49,6 +49,7 @@ use super::{BufferUsage, Subbuffer};
use crate::{
device::{Device, DeviceOwned},
format::{Format, FormatFeatures},
memory::{is_aligned, DeviceAlignment},
DeviceSize, OomError, RequirementNotMet, RequiresOneOf, Version, VulkanError, VulkanObject,
};
use std::{
@ -152,11 +153,12 @@ impl BufferView {
if device.api_version() >= Version::V1_3 || device.enabled_features().texel_buffer_alignment
{
let element_size = if block_size % 3 == 0 {
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
@ -171,7 +173,7 @@ impl BufferView {
}
// VUID-VkBufferViewCreateInfo-buffer-02750
if offset % required_alignment != 0 {
if !is_aligned(offset, required_alignment) {
return Err(BufferViewCreationError::OffsetNotAligned {
offset,
required_alignment,
@ -192,7 +194,7 @@ impl BufferView {
}
// VUID-VkBufferViewCreateInfo-buffer-02751
if offset % required_alignment != 0 {
if !is_aligned(offset, required_alignment) {
return Err(BufferViewCreationError::OffsetNotAligned {
offset,
required_alignment,
@ -203,7 +205,7 @@ impl BufferView {
let required_alignment = properties.min_texel_buffer_offset_alignment;
// VUID-VkBufferViewCreateInfo-offset-02749
if offset % required_alignment != 0 {
if !is_aligned(offset, required_alignment) {
return Err(BufferViewCreationError::OffsetNotAligned {
offset,
required_alignment,
@ -340,7 +342,7 @@ pub enum BufferViewCreationError {
/// The offset within the buffer is not a multiple of the required alignment.
OffsetNotAligned {
offset: DeviceSize,
required_alignment: DeviceSize,
required_alignment: DeviceAlignment,
},
/// The range within the buffer is not a multiple of the required alignment.

View File

@ -23,6 +23,7 @@ use crate::{
WriteDescriptorSet,
},
device::{DeviceOwned, QueueFlags},
memory::{is_aligned, DeviceAlignment},
pipeline::{
graphics::{
input_assembly::{Index, IndexType},
@ -132,8 +133,8 @@ where
}
let properties = self.device().physical_device().properties();
let uniform_alignment = properties.min_uniform_buffer_offset_alignment as u32;
let storage_alignment = properties.min_storage_buffer_offset_alignment as u32;
let uniform_alignment = properties.min_uniform_buffer_offset_alignment;
let storage_alignment = properties.min_storage_buffer_offset_alignment;
for (i, set) in descriptor_sets.iter().enumerate() {
let set_num = first_set + i as u32;
@ -183,7 +184,7 @@ where
{
// VUID-vkCmdBindDescriptorSets-pDynamicOffsets-01971
// VUID-vkCmdBindDescriptorSets-pDynamicOffsets-01972
if offset % required_alignment != 0 {
if !is_aligned(offset as DeviceSize, required_alignment) {
return Err(BindPushError::DynamicOffsetNotAligned {
set_num,
binding_num,
@ -1341,7 +1342,7 @@ pub(in super::super) enum BindPushError {
binding_num: u32,
index: u32,
offset: u32,
required_alignment: u32,
required_alignment: DeviceAlignment,
},
/// In an element of `descriptor_sets`, a provided dynamic offset, when added to the end of the
@ -1459,7 +1460,7 @@ impl Display for BindPushError {
"in the element of `descriptor_sets` being bound to slot {}, the dynamic offset \
provided for binding {} index {} ({}) is not a multiple of the value of the \
`min_uniform_buffer_offset_alignment` or `min_storage_buffer_offset_alignment` \
property ({})",
property ({:?})",
set_num, binding_num, index, offset, required_alignment,
),
Self::DynamicOffsetOutOfBufferBounds {

View File

@ -17,6 +17,7 @@ use crate::{
DescriptorWriteInfo, WriteDescriptorSet,
},
device::{DeviceOwned, QueueFlags},
memory::is_aligned,
pipeline::{
graphics::{
input_assembly::{Index, IndexType},
@ -110,8 +111,8 @@ where
}
let properties = self.device().physical_device().properties();
let uniform_alignment = properties.min_uniform_buffer_offset_alignment as u32;
let storage_alignment = properties.min_storage_buffer_offset_alignment as u32;
let uniform_alignment = properties.min_uniform_buffer_offset_alignment;
let storage_alignment = properties.min_storage_buffer_offset_alignment;
for (i, set) in descriptor_sets.iter().enumerate() {
let set_num = first_set + i as u32;
@ -161,7 +162,7 @@ where
{
// VUID-vkCmdBindDescriptorSets-pDynamicOffsets-01971
// VUID-vkCmdBindDescriptorSets-pDynamicOffsets-01972
if offset % required_alignment != 0 {
if !is_aligned(offset as DeviceSize, required_alignment) {
return Err(BindPushError::DynamicOffsetNotAligned {
set_num,
binding_num,

View File

@ -1,17 +1,14 @@
use super::physical::{
MemoryDecompressionMethods, OpticalFlowGridSizes, PipelineRobustnessBufferBehavior,
PipelineRobustnessImageBehavior, RayTracingInvocationReorderMode,
ConformanceVersion, DriverId, MemoryDecompressionMethods, OpticalFlowGridSizes,
PhysicalDeviceType, PipelineRobustnessBufferBehavior, PipelineRobustnessImageBehavior,
PointClippingBehavior, RayTracingInvocationReorderMode, ShaderCoreProperties,
ShaderFloatControlsIndependence, SubgroupFeatures,
};
use crate::{
device::{
physical::{
ConformanceVersion, DriverId, PhysicalDeviceType, PointClippingBehavior,
ShaderCoreProperties, ShaderFloatControlsIndependence, SubgroupFeatures,
},
DeviceExtensions, QueueFlags,
},
device::{DeviceExtensions, QueueFlags},
image::{SampleCount, SampleCounts},
instance::InstanceExtensions,
memory::DeviceAlignment,
render_pass::ResolveModes,
shader::ShaderStages,
DeviceSize, Version,
@ -65,6 +62,13 @@ impl FromVulkan<u64> for u64 {
}
}
impl FromVulkan<u64> for DeviceAlignment {
#[inline]
fn from_vulkan(val: u64) -> Option<Self> {
DeviceAlignment::new(val)
}
}
impl FromVulkan<usize> for usize {
#[inline]
fn from_vulkan(val: usize) -> Option<Self> {

View File

@ -22,7 +22,7 @@ use crate::{
AllocationCreateInfo, AllocationType, MemoryAllocatePreference, MemoryAllocator,
MemoryUsage,
},
DedicatedAllocation, DeviceMemoryError, ExternalMemoryHandleType,
is_aligned, DedicatedAllocation, DeviceMemoryError, ExternalMemoryHandleType,
ExternalMemoryHandleTypes,
},
DeviceSize,
@ -427,7 +427,7 @@ impl AttachmentImage {
match unsafe { allocator.allocate_unchecked(create_info) } {
Ok(alloc) => {
debug_assert!(alloc.offset() % requirements.layout.alignment().as_nonzero() == 0);
debug_assert!(is_aligned(alloc.offset(), requirements.layout.alignment()));
debug_assert!(alloc.size() == requirements.layout.size());
let inner = Arc::new(unsafe {
@ -532,7 +532,7 @@ impl AttachmentImage {
)
} {
Ok(alloc) => {
debug_assert!(alloc.offset() % requirements.layout.alignment().as_nonzero() == 0);
debug_assert!(is_aligned(alloc.offset(), requirements.layout.alignment()));
debug_assert!(alloc.size() == requirements.layout.size());
let inner = Arc::new(unsafe {

View File

@ -27,7 +27,7 @@ use crate::{
AllocationCreateInfo, AllocationCreationError, AllocationType,
MemoryAllocatePreference, MemoryAllocator, MemoryUsage,
},
DedicatedAllocation,
is_aligned, DedicatedAllocation,
},
sampler::Filter,
sync::Sharing,
@ -149,7 +149,7 @@ impl ImmutableImage {
match unsafe { allocator.allocate_unchecked(create_info) } {
Ok(alloc) => {
debug_assert!(alloc.offset() % requirements.layout.alignment().as_nonzero() == 0);
debug_assert!(is_aligned(alloc.offset(), requirements.layout.alignment()));
debug_assert!(alloc.size() == requirements.layout.size());
let inner = Arc::new(unsafe {

View File

@ -22,7 +22,7 @@ use crate::{
AllocationCreateInfo, AllocationType, MemoryAllocatePreference, MemoryAllocator,
MemoryUsage,
},
DedicatedAllocation, DeviceMemoryError, ExternalMemoryHandleType,
is_aligned, DedicatedAllocation, DeviceMemoryError, ExternalMemoryHandleType,
ExternalMemoryHandleTypes,
},
sync::Sharing,
@ -125,7 +125,7 @@ impl StorageImage {
match unsafe { allocator.allocate_unchecked(create_info) } {
Ok(alloc) => {
debug_assert!(alloc.offset() % requirements.layout.alignment().as_nonzero() == 0);
debug_assert!(is_aligned(alloc.offset(), requirements.layout.alignment()));
debug_assert!(alloc.size() == requirements.layout.size());
let inner = Arc::new(unsafe {
@ -205,7 +205,7 @@ impl StorageImage {
)
} {
Ok(alloc) => {
debug_assert!(alloc.offset() % requirements.layout.alignment().as_nonzero() == 0);
debug_assert!(is_aligned(alloc.offset(), requirements.layout.alignment()));
debug_assert!(alloc.size() == requirements.layout.size());
let inner = Arc::new(unsafe {

View File

@ -28,11 +28,9 @@ use crate::{
SparseImageFormatProperties,
},
memory::{
allocator::{
AllocationCreationError, AllocationType, DeviceAlignment, DeviceLayout, MemoryAlloc,
},
DedicatedTo, ExternalMemoryHandleType, ExternalMemoryHandleTypes, MemoryPropertyFlags,
MemoryRequirements,
allocator::{AllocationCreationError, AllocationType, DeviceLayout, MemoryAlloc},
is_aligned, DedicatedTo, DeviceAlignment, ExternalMemoryHandleType,
ExternalMemoryHandleTypes, MemoryPropertyFlags, MemoryRequirements,
},
range_map::RangeMap,
swapchain::Swapchain,
@ -1479,7 +1477,7 @@ impl RawImage {
// VUID-VkBindImageMemoryInfo-pNext-01616
// VUID-VkBindImageMemoryInfo-pNext-01620
if memory_offset % memory_requirements.layout.alignment().as_nonzero() != 0 {
if !is_aligned(memory_offset, memory_requirements.layout.alignment()) {
return Err(ImageError::MemoryAllocationNotAligned {
allocations_index,
allocation_offset: memory_offset,

View File

@ -0,0 +1,249 @@
use crate::{DeviceSize, NonZeroDeviceSize};
use std::{
cmp::Ordering,
error::Error,
fmt::{Debug, Display, Formatter, Result as FmtResult},
hash::{Hash, Hasher},
mem::{self, align_of, size_of},
};
/// Vulkan analog of std's [`Alignment`], stored as a [`DeviceSize`] that is guaranteed to be a
/// valid Vulkan alignment.
///
/// [`Alignment`]: std::ptr::Alignment
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct DeviceAlignment(AlignmentEnum);
const _: () = assert!(size_of::<DeviceAlignment>() == size_of::<DeviceSize>());
const _: () = assert!(align_of::<DeviceAlignment>() == align_of::<DeviceSize>());
impl DeviceAlignment {
/// The smallest possible alignment, 1.
pub const MIN: Self = Self(AlignmentEnum::_Align1Shl0);
/// The largest possible alignment, 2<sup>63</sup>.
pub const MAX: Self = Self(AlignmentEnum::_Align1Shl63);
/// Returns the alignment for a type.
#[inline]
pub const fn of<T>() -> Self {
#[cfg(any(
target_pointer_width = "64",
target_pointer_width = "32",
target_pointer_width = "16",
))]
{
const _: () = assert!(size_of::<DeviceSize>() >= size_of::<usize>());
// SAFETY: rustc guarantees that the alignment of types is a power of two.
unsafe { DeviceAlignment::new_unchecked(align_of::<T>() as DeviceSize) }
}
}
/// Tries to create a `DeviceAlignment` from a [`DeviceSize`], returning [`None`] if it's not a
/// power of two.
#[inline]
pub const fn new(alignment: DeviceSize) -> Option<Self> {
if alignment.is_power_of_two() {
Some(unsafe { DeviceAlignment::new_unchecked(alignment) })
} else {
None
}
}
/// Creates a `DeviceAlignment` from a [`DeviceSize`] without checking if it's a power of two.
///
/// # Safety
///
/// - `alignment` must be a power of two, which also means it must be non-zero.
#[inline]
pub const unsafe fn new_unchecked(alignment: DeviceSize) -> Self {
debug_assert!(alignment.is_power_of_two());
unsafe { mem::transmute::<DeviceSize, DeviceAlignment>(alignment) }
}
/// Returns the alignment as a [`DeviceSize`].
#[inline]
pub const fn as_devicesize(self) -> DeviceSize {
self.0 as DeviceSize
}
/// Returns the alignment as a [`NonZeroDeviceSize`].
#[inline]
pub const fn as_nonzero(self) -> NonZeroDeviceSize {
// SAFETY: All the discriminants are non-zero.
unsafe { NonZeroDeviceSize::new_unchecked(self.as_devicesize()) }
}
/// Returns the base-2 logarithm of the alignment.
#[inline]
pub const fn log2(self) -> u32 {
self.as_nonzero().trailing_zeros()
}
// TODO: Replace with `Ord::max` once its constness is stabilized.
#[inline(always)]
pub(crate) const fn max(self, other: Self) -> Self {
if self.as_devicesize() >= other.as_devicesize() {
self
} else {
other
}
}
}
impl Debug for DeviceAlignment {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "{:?} (1 << {:?})", self.as_nonzero(), self.log2())
}
}
impl Default for DeviceAlignment {
#[inline]
fn default() -> Self {
DeviceAlignment::MIN
}
}
impl TryFrom<NonZeroDeviceSize> for DeviceAlignment {
type Error = TryFromIntError;
#[inline]
fn try_from(alignment: NonZeroDeviceSize) -> Result<Self, Self::Error> {
if alignment.is_power_of_two() {
Ok(unsafe { DeviceAlignment::new_unchecked(alignment.get()) })
} else {
Err(TryFromIntError)
}
}
}
impl TryFrom<DeviceSize> for DeviceAlignment {
type Error = TryFromIntError;
#[inline]
fn try_from(alignment: DeviceSize) -> Result<Self, Self::Error> {
DeviceAlignment::new(alignment).ok_or(TryFromIntError)
}
}
impl From<DeviceAlignment> for NonZeroDeviceSize {
#[inline]
fn from(alignment: DeviceAlignment) -> Self {
alignment.as_nonzero()
}
}
impl From<DeviceAlignment> for DeviceSize {
#[inline]
fn from(alignment: DeviceAlignment) -> Self {
alignment.as_devicesize()
}
}
// This is a false-positive, the underlying values that this impl and the derived `PartialEq` work
// with are the same.
#[allow(clippy::derive_hash_xor_eq)]
impl Hash for DeviceAlignment {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_nonzero().hash(state);
}
}
impl PartialOrd for DeviceAlignment {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.as_nonzero().partial_cmp(&other.as_nonzero())
}
}
impl Ord for DeviceAlignment {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.as_nonzero().cmp(&other.as_nonzero())
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(u64)]
enum AlignmentEnum {
_Align1Shl0 = 1 << 0,
_Align1Shl1 = 1 << 1,
_Align1Shl2 = 1 << 2,
_Align1Shl3 = 1 << 3,
_Align1Shl4 = 1 << 4,
_Align1Shl5 = 1 << 5,
_Align1Shl6 = 1 << 6,
_Align1Shl7 = 1 << 7,
_Align1Shl8 = 1 << 8,
_Align1Shl9 = 1 << 9,
_Align1Shl10 = 1 << 10,
_Align1Shl11 = 1 << 11,
_Align1Shl12 = 1 << 12,
_Align1Shl13 = 1 << 13,
_Align1Shl14 = 1 << 14,
_Align1Shl15 = 1 << 15,
_Align1Shl16 = 1 << 16,
_Align1Shl17 = 1 << 17,
_Align1Shl18 = 1 << 18,
_Align1Shl19 = 1 << 19,
_Align1Shl20 = 1 << 20,
_Align1Shl21 = 1 << 21,
_Align1Shl22 = 1 << 22,
_Align1Shl23 = 1 << 23,
_Align1Shl24 = 1 << 24,
_Align1Shl25 = 1 << 25,
_Align1Shl26 = 1 << 26,
_Align1Shl27 = 1 << 27,
_Align1Shl28 = 1 << 28,
_Align1Shl29 = 1 << 29,
_Align1Shl30 = 1 << 30,
_Align1Shl31 = 1 << 31,
_Align1Shl32 = 1 << 32,
_Align1Shl33 = 1 << 33,
_Align1Shl34 = 1 << 34,
_Align1Shl35 = 1 << 35,
_Align1Shl36 = 1 << 36,
_Align1Shl37 = 1 << 37,
_Align1Shl38 = 1 << 38,
_Align1Shl39 = 1 << 39,
_Align1Shl40 = 1 << 40,
_Align1Shl41 = 1 << 41,
_Align1Shl42 = 1 << 42,
_Align1Shl43 = 1 << 43,
_Align1Shl44 = 1 << 44,
_Align1Shl45 = 1 << 45,
_Align1Shl46 = 1 << 46,
_Align1Shl47 = 1 << 47,
_Align1Shl48 = 1 << 48,
_Align1Shl49 = 1 << 49,
_Align1Shl50 = 1 << 50,
_Align1Shl51 = 1 << 51,
_Align1Shl52 = 1 << 52,
_Align1Shl53 = 1 << 53,
_Align1Shl54 = 1 << 54,
_Align1Shl55 = 1 << 55,
_Align1Shl56 = 1 << 56,
_Align1Shl57 = 1 << 57,
_Align1Shl58 = 1 << 58,
_Align1Shl59 = 1 << 59,
_Align1Shl60 = 1 << 60,
_Align1Shl61 = 1 << 61,
_Align1Shl62 = 1 << 62,
_Align1Shl63 = 1 << 63,
}
/// Error that can happen when trying to convert an integer to a `DeviceAlignment`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TryFromIntError;
impl Error for TryFromIntError {}
impl Display for TryFromIntError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.write_str("attempted to convert a non-power-of-two integer to a `DeviceAlignment`")
}
}

View File

@ -8,14 +8,13 @@
// according to those terms.
use super::align_up;
use crate::{macros::try_opt, DeviceSize, NonZeroDeviceSize};
use crate::{macros::try_opt, memory::DeviceAlignment, DeviceSize, NonZeroDeviceSize};
use std::{
alloc::Layout,
cmp::Ordering,
error::Error,
fmt::{Debug, Display, Formatter, Result as FmtResult},
hash::{Hash, Hasher},
mem::{self, align_of, size_of},
hash::Hash,
mem::size_of,
};
/// Vulkan analog of std's [`Layout`], represented using [`DeviceSize`]s.
@ -351,237 +350,3 @@ impl Display for TryFromDeviceLayoutError {
)
}
}
/// Vulkan analog of std's [`Alignment`], stored as a [`DeviceSize`] that is guaranteed to be a
/// valid Vulkan alignment.
///
/// [`Alignment`]: std::ptr::Alignment
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct DeviceAlignment(AlignmentEnum);
const _: () = assert!(size_of::<DeviceAlignment>() == size_of::<DeviceSize>());
const _: () = assert!(align_of::<DeviceAlignment>() == align_of::<DeviceSize>());
impl DeviceAlignment {
/// The smallest possible alignment, 1.
pub const MIN: Self = Self(AlignmentEnum::_Align1Shl0);
/// The largest possible alignment, 2<sup>63</sup>.
pub const MAX: Self = Self(AlignmentEnum::_Align1Shl63);
/// Returns the alignment for a type.
#[inline]
pub const fn of<T>() -> Self {
#[cfg(any(
target_pointer_width = "64",
target_pointer_width = "32",
target_pointer_width = "16",
))]
{
const _: () = assert!(size_of::<DeviceSize>() >= size_of::<usize>());
// SAFETY: rustc guarantees that the alignment of types is a power of two.
unsafe { DeviceAlignment::new_unchecked(align_of::<T>() as DeviceSize) }
}
}
/// Tries to create a `DeviceAlignment` from a [`DeviceSize`], returning [`None`] if it's not a
/// power of two.
#[inline]
pub const fn new(alignment: DeviceSize) -> Option<Self> {
if alignment.is_power_of_two() {
Some(unsafe { DeviceAlignment::new_unchecked(alignment) })
} else {
None
}
}
/// Creates a `DeviceAlignment` from a [`DeviceSize`] without checking if it's a power of two.
///
/// # Safety
///
/// - `alignment` must be a power of two, which also means it must be non-zero.
#[inline]
pub const unsafe fn new_unchecked(alignment: DeviceSize) -> Self {
debug_assert!(alignment.is_power_of_two());
unsafe { mem::transmute::<DeviceSize, DeviceAlignment>(alignment) }
}
/// Returns the alignment as a [`DeviceSize`].
#[inline]
pub const fn as_devicesize(self) -> DeviceSize {
self.0 as DeviceSize
}
/// Returns the alignment as a [`NonZeroDeviceSize`].
#[inline]
pub const fn as_nonzero(self) -> NonZeroDeviceSize {
// SAFETY: All the discriminants are non-zero.
unsafe { NonZeroDeviceSize::new_unchecked(self.as_devicesize()) }
}
/// Returns the base-2 logarithm of the alignment.
#[inline]
pub const fn log2(self) -> u32 {
self.as_nonzero().trailing_zeros()
}
// TODO: Replace with `Ord::max` once its constness is stabilized.
#[inline(always)]
pub(crate) const fn max(self, other: Self) -> Self {
if self.as_devicesize() >= other.as_devicesize() {
self
} else {
other
}
}
}
impl Debug for DeviceAlignment {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "{:?} (1 << {:?})", self.as_nonzero(), self.log2())
}
}
impl TryFrom<NonZeroDeviceSize> for DeviceAlignment {
type Error = TryFromIntError;
#[inline]
fn try_from(alignment: NonZeroDeviceSize) -> Result<Self, Self::Error> {
if alignment.is_power_of_two() {
Ok(unsafe { DeviceAlignment::new_unchecked(alignment.get()) })
} else {
Err(TryFromIntError)
}
}
}
impl TryFrom<DeviceSize> for DeviceAlignment {
type Error = TryFromIntError;
#[inline]
fn try_from(alignment: DeviceSize) -> Result<Self, Self::Error> {
DeviceAlignment::new(alignment).ok_or(TryFromIntError)
}
}
impl From<DeviceAlignment> for NonZeroDeviceSize {
#[inline]
fn from(alignment: DeviceAlignment) -> Self {
alignment.as_nonzero()
}
}
impl From<DeviceAlignment> for DeviceSize {
#[inline]
fn from(alignment: DeviceAlignment) -> Self {
alignment.as_devicesize()
}
}
// This is a false-positive, the underlying values that this impl and the derived `PartialEq` work
// with are the same.
#[allow(clippy::derive_hash_xor_eq)]
impl Hash for DeviceAlignment {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_nonzero().hash(state);
}
}
impl PartialOrd for DeviceAlignment {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.as_nonzero().partial_cmp(&other.as_nonzero())
}
}
impl Ord for DeviceAlignment {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.as_nonzero().cmp(&other.as_nonzero())
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(u64)]
enum AlignmentEnum {
_Align1Shl0 = 1 << 0,
_Align1Shl1 = 1 << 1,
_Align1Shl2 = 1 << 2,
_Align1Shl3 = 1 << 3,
_Align1Shl4 = 1 << 4,
_Align1Shl5 = 1 << 5,
_Align1Shl6 = 1 << 6,
_Align1Shl7 = 1 << 7,
_Align1Shl8 = 1 << 8,
_Align1Shl9 = 1 << 9,
_Align1Shl10 = 1 << 10,
_Align1Shl11 = 1 << 11,
_Align1Shl12 = 1 << 12,
_Align1Shl13 = 1 << 13,
_Align1Shl14 = 1 << 14,
_Align1Shl15 = 1 << 15,
_Align1Shl16 = 1 << 16,
_Align1Shl17 = 1 << 17,
_Align1Shl18 = 1 << 18,
_Align1Shl19 = 1 << 19,
_Align1Shl20 = 1 << 20,
_Align1Shl21 = 1 << 21,
_Align1Shl22 = 1 << 22,
_Align1Shl23 = 1 << 23,
_Align1Shl24 = 1 << 24,
_Align1Shl25 = 1 << 25,
_Align1Shl26 = 1 << 26,
_Align1Shl27 = 1 << 27,
_Align1Shl28 = 1 << 28,
_Align1Shl29 = 1 << 29,
_Align1Shl30 = 1 << 30,
_Align1Shl31 = 1 << 31,
_Align1Shl32 = 1 << 32,
_Align1Shl33 = 1 << 33,
_Align1Shl34 = 1 << 34,
_Align1Shl35 = 1 << 35,
_Align1Shl36 = 1 << 36,
_Align1Shl37 = 1 << 37,
_Align1Shl38 = 1 << 38,
_Align1Shl39 = 1 << 39,
_Align1Shl40 = 1 << 40,
_Align1Shl41 = 1 << 41,
_Align1Shl42 = 1 << 42,
_Align1Shl43 = 1 << 43,
_Align1Shl44 = 1 << 44,
_Align1Shl45 = 1 << 45,
_Align1Shl46 = 1 << 46,
_Align1Shl47 = 1 << 47,
_Align1Shl48 = 1 << 48,
_Align1Shl49 = 1 << 49,
_Align1Shl50 = 1 << 50,
_Align1Shl51 = 1 << 51,
_Align1Shl52 = 1 << 52,
_Align1Shl53 = 1 << 53,
_Align1Shl54 = 1 << 54,
_Align1Shl55 = 1 << 55,
_Align1Shl56 = 1 << 56,
_Align1Shl57 = 1 << 57,
_Align1Shl58 = 1 << 58,
_Align1Shl59 = 1 << 59,
_Align1Shl60 = 1 << 60,
_Align1Shl61 = 1 << 61,
_Align1Shl62 = 1 << 62,
_Align1Shl63 = 1 << 63,
}
/// Error that can happen when trying to convert an integer to a `DeviceAlignment`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TryFromIntError;
impl Error for TryFromIntError {}
impl Display for TryFromIntError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.write_str("attempted to convert a non-power-of-two integer to a `DeviceAlignment`")
}
}

View File

@ -221,15 +221,16 @@ pub mod suballocator;
use self::array_vec::ArrayVec;
pub use self::{
layout::{DeviceAlignment, DeviceLayout},
layout::DeviceLayout,
suballocator::{
AllocationType, BuddyAllocator, BumpAllocator, FreeListAllocator, MemoryAlloc,
PoolAllocator, SuballocationCreateInfo, SuballocationCreationError, Suballocator,
},
};
use super::{
DedicatedAllocation, DeviceMemory, ExternalMemoryHandleTypes, MemoryAllocateFlags,
MemoryAllocateInfo, MemoryProperties, MemoryPropertyFlags, MemoryRequirements, MemoryType,
DedicatedAllocation, DeviceAlignment, DeviceMemory, ExternalMemoryHandleTypes,
MemoryAllocateFlags, MemoryAllocateInfo, MemoryProperties, MemoryPropertyFlags,
MemoryRequirements, MemoryType,
};
use crate::{
device::{Device, DeviceOwned},

View File

@ -21,7 +21,7 @@ use super::{
use crate::{
device::{Device, DeviceOwned},
image::ImageTiling,
memory::{DeviceMemory, MemoryPropertyFlags},
memory::{is_aligned, DeviceMemory, MemoryPropertyFlags},
DeviceSize, NonZeroDeviceSize, VulkanError, VulkanObject,
};
use crossbeam_queue::ArrayQueue;
@ -135,9 +135,7 @@ impl MemoryAlloc {
let atom_size = (property_flags.intersects(MemoryPropertyFlags::HOST_VISIBLE)
&& !property_flags.intersects(MemoryPropertyFlags::HOST_COHERENT))
.then_some(
DeviceAlignment::new(physical_device.properties().non_coherent_atom_size).unwrap(),
);
.then_some(physical_device.properties().non_coherent_atom_size);
Ok(MemoryAlloc {
offset: 0,
@ -301,8 +299,8 @@ impl MemoryAlloc {
// VUID-VkMappedMemoryRange-offset-00687
// VUID-VkMappedMemoryRange-size-01390
assert!(
range.start % atom_size.as_nonzero() == 0
&& (range.end % atom_size.as_nonzero() == 0 || range.end == self.size)
is_aligned(range.start, atom_size)
&& (is_aligned(range.end, atom_size) || range.end == self.size)
);
// VUID-VkMappedMemoryRange-offset-00687
@ -334,17 +332,15 @@ impl MemoryAlloc {
/// to be host-coherent.
fn debug_validate_memory_range(&self, range: &Range<DeviceSize>) {
debug_assert!(!range.is_empty() && range.end <= self.size);
debug_assert!(
{
let atom_size = self
.device()
.physical_device()
.properties()
.non_coherent_atom_size;
range.start % atom_size == 0
&& (range.end % atom_size == 0 || range.end == self.size)
},
let atom_size = self
.device()
.physical_device()
.properties()
.non_coherent_atom_size;
debug_assert!(
is_aligned(range.start, atom_size)
&& (is_aligned(range.end, atom_size) || range.end == self.size),
"attempted to invalidate or flush a memory range that is not aligned to the \
non-coherent atom size",
);
@ -1002,14 +998,12 @@ impl FreeListAllocator {
.root()
.expect("dedicated allocations can't be suballocated")
.clone();
let buffer_image_granularity = DeviceAlignment::new(
device_memory
.device()
.physical_device()
.properties()
.buffer_image_granularity,
)
.unwrap();
let buffer_image_granularity = device_memory
.device()
.physical_device()
.properties()
.buffer_image_granularity;
let atom_size = region.atom_size.unwrap_or(DeviceAlignment::MIN);
let free_size = AtomicU64::new(region.size);
@ -1600,14 +1594,11 @@ impl BuddyAllocator {
.root()
.expect("dedicated allocations can't be suballocated")
.clone();
let buffer_image_granularity = DeviceAlignment::new(
device_memory
.device()
.physical_device()
.properties()
.buffer_image_granularity,
)
.unwrap();
let buffer_image_granularity = device_memory
.device()
.physical_device()
.properties()
.buffer_image_granularity;
let atom_size = region.atom_size.unwrap_or(DeviceAlignment::MIN);
let free_size = AtomicU64::new(region.size);
@ -1742,7 +1733,7 @@ unsafe impl Suballocator for Arc<BuddyAllocator> {
// Start searching at the lowest possible order going up.
for (order, free_list) in state.free_list.iter_mut().enumerate().skip(min_order) {
for (index, &offset) in free_list.iter().enumerate() {
if offset % alignment.as_nonzero() == 0 {
if is_aligned(offset, alignment) {
free_list.remove(index);
// Go in the opposite direction, splitting nodes from higher orders. The lowest
@ -2117,14 +2108,11 @@ impl PoolAllocatorInner {
.expect("dedicated allocations can't be suballocated")
.clone();
#[cfg(not(test))]
let buffer_image_granularity = DeviceAlignment::new(
device_memory
.device()
.physical_device()
.properties()
.buffer_image_granularity,
)
.unwrap();
let buffer_image_granularity = device_memory
.device()
.physical_device()
.properties()
.buffer_image_granularity;
let atom_size = region.atom_size.unwrap_or(DeviceAlignment::MIN);
if region.allocation_type == AllocationType::Unknown {
block_size = align_up(block_size, buffer_image_granularity);
@ -2294,14 +2282,11 @@ impl BumpAllocator {
.root()
.expect("dedicated allocations can't be suballocated")
.clone();
let buffer_image_granularity = DeviceAlignment::new(
device_memory
.device()
.physical_device()
.properties()
.buffer_image_granularity,
)
.unwrap();
let buffer_image_granularity = device_memory
.device()
.physical_device()
.properties()
.buffer_image_granularity;
let atom_size = region.atom_size.unwrap_or(DeviceAlignment::MIN);
let state = AtomicU64::new(region.allocation_type as DeviceSize);

View File

@ -7,11 +7,11 @@
// notice may not be copied, modified, or distributed except
// according to those terms.
use super::{DedicatedAllocation, DedicatedTo};
use super::{DedicatedAllocation, DedicatedTo, DeviceAlignment};
use crate::{
device::{Device, DeviceOwned},
macros::{vulkan_bitflags, vulkan_bitflags_enum},
memory::MemoryPropertyFlags,
memory::{is_aligned, MemoryPropertyFlags},
DeviceSize, OomError, RequirementNotMet, RequiresOneOf, Version, VulkanError, VulkanObject,
};
use std::{
@ -1188,7 +1188,7 @@ pub struct MappedDeviceMemory {
pointer: *mut c_void, // points to `range.start`
range: Range<DeviceSize>,
atom_size: DeviceSize,
atom_size: DeviceAlignment,
coherent: bool,
}
@ -1249,8 +1249,8 @@ impl MappedDeviceMemory {
// parts of the mapped memory at the start and end that they're not able to
// invalidate/flush, which is probably unintended.
if !coherent
&& (range.start % atom_size != 0
|| (range.end % atom_size != 0 && range.end != memory.allocation_size))
&& (!is_aligned(range.start, atom_size)
|| (!is_aligned(range.end, atom_size) && range.end != memory.allocation_size))
{
return Err(MemoryMapError::RangeNotAlignedToAtomSize { range, atom_size });
}
@ -1275,7 +1275,6 @@ impl MappedDeviceMemory {
memory,
pointer,
range,
atom_size,
coherent,
})
@ -1460,8 +1459,9 @@ impl MappedDeviceMemory {
if !self.coherent {
// VUID-VkMappedMemoryRange-offset-00687
// VUID-VkMappedMemoryRange-size-01390
if range.start % self.atom_size != 0
|| (range.end % self.atom_size != 0 && range.end != self.memory.allocation_size)
if !is_aligned(range.start, self.atom_size)
|| (!is_aligned(range.end, self.atom_size)
&& range.end != self.memory.allocation_size)
{
return Err(MemoryMapError::RangeNotAlignedToAtomSize {
range,
@ -1521,7 +1521,7 @@ pub enum MemoryMapError {
/// property.
RangeNotAlignedToAtomSize {
range: Range<DeviceSize>,
atom_size: DeviceSize,
atom_size: DeviceAlignment,
},
}
@ -1554,7 +1554,7 @@ impl Display for MemoryMapError {
Self::RangeNotAlignedToAtomSize { range, atom_size } => write!(
f,
"the memory is not host-coherent, and the specified `range` bounds ({:?}) are not \
a multiple of the `non_coherent_atom_size` device property ({})",
a multiple of the `non_coherent_atom_size` device property ({:?})",
range, atom_size,
),
}

View File

@ -91,7 +91,8 @@
//! get memory from that pool. By default if you don't specify any pool when creating a buffer or
//! an image, an instance of `StandardMemoryPool` that is shared by the `Device` object is used.
use self::allocator::{DeviceAlignment, DeviceLayout};
pub use self::alignment::DeviceAlignment;
use self::allocator::DeviceLayout;
pub use self::device_memory::{
DeviceMemory, DeviceMemoryError, ExternalMemoryHandleType, ExternalMemoryHandleTypes,
MappedDeviceMemory, MemoryAllocateFlags, MemoryAllocateInfo, MemoryImportInfo, MemoryMapError,
@ -109,6 +110,7 @@ use std::{
sync::Arc,
};
mod alignment;
pub mod allocator;
mod device_memory;