Improve DeviceLayout convenience (#2599)

This commit is contained in:
marc0246 2024-11-04 11:49:41 +01:00 committed by GitHub
parent 4f87b525e7
commit a52e327184
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 86 additions and 83 deletions

View File

@ -30,7 +30,6 @@
use glam::f32::Mat4; use glam::f32::Mat4;
use rand::Rng; use rand::Rng;
use std::{ use std::{
alloc::Layout,
error::Error, error::Error,
slice, slice,
sync::{ sync::{
@ -293,7 +292,7 @@ impl App {
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default() ..Default::default()
}, },
DeviceLayout::from_layout(Layout::for_value(&vertices)).unwrap(), DeviceLayout::for_value(vertices.as_slice()).unwrap(),
) )
.unwrap(); .unwrap();
@ -311,7 +310,7 @@ impl App {
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default() ..Default::default()
}, },
DeviceLayout::from_layout(Layout::new::<vs::Data>()).unwrap(), DeviceLayout::new_sized::<vs::Data>(),
) )
.unwrap() .unwrap()
}); });

View File

@ -1,5 +1,5 @@
use crate::{App, RenderContext}; use crate::{App, RenderContext};
use std::{alloc::Layout, slice, sync::Arc}; use std::{slice, sync::Arc};
use vulkano::{ use vulkano::{
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage}, buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage},
command_buffer::RenderPassBeginInfo, command_buffer::RenderPassBeginInfo,
@ -122,7 +122,7 @@ impl SceneTask {
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default() ..Default::default()
}, },
DeviceLayout::from_layout(Layout::for_value(&vertices)).unwrap(), DeviceLayout::for_value(vertices.as_slice()).unwrap(),
) )
.unwrap(); .unwrap();

View File

@ -13,7 +13,7 @@ use crate::{
}, },
DeviceAlignment, DeviceAlignment,
}, },
DeviceSize, NonZeroDeviceSize, Validated, DeviceSize, Validated,
}; };
use crossbeam_queue::ArrayQueue; use crossbeam_queue::ArrayQueue;
use std::{ use std::{
@ -241,7 +241,6 @@ where
where where
T: BufferContents + ?Sized, 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 layout = T::LAYOUT.layout_for_len(len).unwrap();
unsafe { &mut *self.state.get() } unsafe { &mut *self.state.get() }

View File

@ -83,8 +83,8 @@ use crate::{
}, },
range_map::RangeMap, range_map::RangeMap,
sync::{future::AccessError, AccessConflict, CurrentAccess, Sharing}, sync::{future::AccessError, AccessConflict, CurrentAccess, Sharing},
DeviceSize, NonNullDeviceAddress, NonZeroDeviceSize, Requires, RequiresAllOf, RequiresOneOf, DeviceSize, NonNullDeviceAddress, Requires, RequiresAllOf, RequiresOneOf, Validated,
Validated, ValidationError, Version, VulkanError, VulkanObject, ValidationError, Version, VulkanError, VulkanObject,
}; };
use parking_lot::{Mutex, MutexGuard}; use parking_lot::{Mutex, MutexGuard};
use smallvec::SmallVec; use smallvec::SmallVec;
@ -352,7 +352,6 @@ impl Buffer {
where where
T: BufferContents + ?Sized, 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 layout = T::LAYOUT.layout_for_len(len).unwrap();
let buffer = Subbuffer::new(Buffer::new( let buffer = Subbuffer::new(Buffer::new(
allocator, allocator,

View File

@ -944,10 +944,10 @@ impl BufferContentsLayout {
} }
} }
/// Returns the [`DeviceLayout`] for the data for the given `len`, or returns [`None`] on /// Returns the [`DeviceLayout`] for the data for the given `len`, or returns [`None`] if `len`
/// arithmetic overflow or if the total size would exceed [`DeviceLayout::MAX_SIZE`]. /// is zero or if the total size would exceed [`DeviceLayout::MAX_SIZE`].
#[inline] #[inline]
pub const fn layout_for_len(&self, len: NonZeroDeviceSize) -> Option<DeviceLayout> { pub const fn layout_for_len(&self, len: DeviceSize) -> Option<DeviceLayout> {
match &self.0 { match &self.0 {
BufferContentsLayoutInner::Sized(sized) => Some(*sized), BufferContentsLayoutInner::Sized(sized) => Some(*sized),
BufferContentsLayoutInner::Unsized { BufferContentsLayoutInner::Unsized {
@ -977,7 +977,7 @@ impl BufferContentsLayout {
"types with alignments above 64 are not valid buffer contents", "types with alignments above 64 are not valid buffer contents",
); );
if let Ok(sized) = DeviceLayout::from_layout(sized) { if let Some(sized) = DeviceLayout::from_layout(sized) {
Self(BufferContentsLayoutInner::Sized(sized)) Self(BufferContentsLayoutInner::Sized(sized))
} else { } else {
unreachable!() unreachable!()
@ -994,7 +994,7 @@ impl BufferContentsLayout {
"types with alignments above 64 are not valid buffer contents", "types with alignments above 64 are not valid buffer contents",
); );
if let Ok(element_layout) = DeviceLayout::from_layout(element_layout) { if let Some(element_layout) = DeviceLayout::from_layout(element_layout) {
Self(BufferContentsLayoutInner::Unsized { Self(BufferContentsLayoutInner::Unsized {
head_layout: None, head_layout: None,
element_layout, element_layout,
@ -1021,11 +1021,11 @@ impl BufferContentsLayout {
while i < field_layouts.len() { while i < field_layouts.len() {
head_layout = match DeviceLayout::from_layout(field_layouts[i]) { head_layout = match DeviceLayout::from_layout(field_layouts[i]) {
Ok(field_layout) => Some(match head_layout { Some(field_layout) => Some(match head_layout {
Some(layout) => extend(layout, field_layout), Some(layout) => extend(layout, field_layout),
None => field_layout, None => field_layout,
}), }),
Err(_) => unreachable!(), None => unreachable!(),
}; };
i += 1; i += 1;
@ -1133,7 +1133,7 @@ impl BufferContentsLayout {
) )
} }
pub(super) const fn unwrap_sized(self) -> DeviceLayout { pub(crate) const fn unwrap_sized(self) -> DeviceLayout {
match self.0 { match self.0 {
BufferContentsLayoutInner::Sized(sized) => sized, BufferContentsLayoutInner::Sized(sized) => sized,
BufferContentsLayoutInner::Unsized { .. } => { BufferContentsLayoutInner::Unsized { .. } => {

View File

@ -4,7 +4,7 @@ use std::{
error::Error, error::Error,
fmt::{Debug, Display, Formatter, Result as FmtResult}, fmt::{Debug, Display, Formatter, Result as FmtResult},
hash::{Hash, Hasher}, hash::{Hash, Hasher},
mem::{self, align_of, size_of}, mem,
}; };
/// Vulkan analog of std's [`Alignment`], stored as a [`DeviceSize`] that is guaranteed to be a /// Vulkan analog of std's [`Alignment`], stored as a [`DeviceSize`] that is guaranteed to be a
@ -15,8 +15,10 @@ use std::{
#[repr(transparent)] #[repr(transparent)]
pub struct DeviceAlignment(AlignmentEnum); pub struct DeviceAlignment(AlignmentEnum);
const _: () = assert!(size_of::<DeviceAlignment>() == size_of::<DeviceSize>()); const _: () = assert!(mem::size_of::<DeviceAlignment>() == mem::size_of::<DeviceSize>());
const _: () = assert!(align_of::<DeviceAlignment>() == align_of::<DeviceSize>()); const _: () = assert!(mem::align_of::<DeviceAlignment>() == mem::align_of::<DeviceSize>());
const _: () = assert!(mem::size_of::<DeviceSize>() >= mem::size_of::<usize>());
impl DeviceAlignment { impl DeviceAlignment {
/// The smallest possible alignment, 1. /// The smallest possible alignment, 1.
@ -28,17 +30,15 @@ impl DeviceAlignment {
/// Returns the alignment for a type. /// Returns the alignment for a type.
#[inline] #[inline]
pub const fn of<T>() -> Self { 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. // SAFETY: rustc guarantees that the alignment of types is a power of two.
unsafe { DeviceAlignment::new_unchecked(align_of::<T>() as DeviceSize) } unsafe { DeviceAlignment::new_unchecked(mem::align_of::<T>() as DeviceSize) }
} }
/// Returns the alignment for a value.
#[inline]
pub fn of_val<T: ?Sized>(value: &T) -> Self {
// SAFETY: rustc guarantees that the alignment of types is a power of two.
unsafe { DeviceAlignment::new_unchecked(mem::align_of_val(value) as DeviceSize) }
} }
/// Tries to create a `DeviceAlignment` from a [`DeviceSize`], returning [`None`] if it's not a /// Tries to create a `DeviceAlignment` from a [`DeviceSize`], returning [`None`] if it's not a

View File

@ -1,11 +1,13 @@
use super::align_up; use super::align_up;
use crate::{macros::try_opt, memory::DeviceAlignment, DeviceSize, NonZeroDeviceSize}; use crate::{
buffer::BufferContents, macros::try_opt, memory::DeviceAlignment, DeviceSize, NonZeroDeviceSize,
};
use std::{ use std::{
alloc::Layout, alloc::Layout,
error::Error, error::Error,
fmt::{Debug, Display, Formatter, Result as FmtResult}, fmt::{Debug, Display, Formatter, Result as FmtResult},
hash::Hash, hash::Hash,
mem::size_of, mem,
}; };
/// Vulkan analog of std's [`Layout`], represented using [`DeviceSize`]s. /// Vulkan analog of std's [`Layout`], represented using [`DeviceSize`]s.
@ -17,6 +19,9 @@ pub struct DeviceLayout {
alignment: DeviceAlignment, alignment: DeviceAlignment,
} }
const _: () = assert!(mem::size_of::<DeviceSize>() >= mem::size_of::<usize>());
const _: () = assert!(DeviceLayout::MAX_SIZE >= isize::MAX as DeviceSize);
impl DeviceLayout { impl DeviceLayout {
/// The maximum size of a memory block after its layout's size has been rounded up to the /// The maximum size of a memory block after its layout's size has been rounded up to the
/// nearest multiple of its layout's alignment. /// nearest multiple of its layout's alignment.
@ -26,59 +31,43 @@ impl DeviceLayout {
/// undefined behavior*. /// undefined behavior*.
pub const MAX_SIZE: DeviceSize = DeviceAlignment::MAX.as_devicesize() - 1; pub const MAX_SIZE: DeviceSize = DeviceAlignment::MAX.as_devicesize() - 1;
/// Creates a new `DeviceLayout` from a [`Layout`], or returns an error if the `Layout` has /// Creates a new `DeviceLayout` from a [`Layout`], or returns [`None`] if the `Layout` has
/// zero size. /// zero size.
#[inline] #[inline]
pub const fn from_layout(layout: Layout) -> Result<Self, TryFromLayoutError> { pub const fn from_layout(layout: Layout) -> Option<DeviceLayout> {
let (size, alignment) = Self::size_alignment_from_layout(&layout); let (size, alignment) = Self::size_alignment_from_layout(&layout);
#[cfg(any(
target_pointer_width = "64",
target_pointer_width = "32",
target_pointer_width = "16",
))]
{
const _: () = assert!(size_of::<DeviceSize>() >= size_of::<usize>());
const _: () = assert!(DeviceLayout::MAX_SIZE >= isize::MAX as DeviceSize);
if let Some(size) = NonZeroDeviceSize::new(size) { if let Some(size) = NonZeroDeviceSize::new(size) {
// SAFETY: Under the precondition that `usize` can't overflow `DeviceSize`, which // SAFETY: Under the precondition that `usize` can't overflow `DeviceSize`, which we
// we checked above, `Layout`'s overflow-invariant is the same if not stricter than // checked above, `Layout`'s overflow-invariant is the same if not stricter than that
// that of `DeviceLayout`. // of `DeviceLayout`.
Ok(unsafe { DeviceLayout::new_unchecked(size, alignment) }) Some(unsafe { DeviceLayout::new_unchecked(size, alignment) })
} else { } else {
Err(TryFromLayoutError) None
}
} }
} }
/// Converts the `DeviceLayout` into a [`Layout`], or returns an error if the `DeviceLayout` /// Converts the `DeviceLayout` into a [`Layout`], or returns [`None`] if the `DeviceLayout`
/// doesn't meet the invariants of `Layout`. /// doesn't meet the invariants of `Layout`.
#[inline] #[inline]
pub const fn into_layout(self) -> Result<Layout, TryFromDeviceLayoutError> { pub const fn into_layout(self) -> Option<Layout> {
let (size, alignment) = (self.size(), self.alignment().as_devicesize()); let (size, alignment) = (self.size(), self.alignment().as_devicesize());
#[cfg(target_pointer_width = "64")] #[cfg(target_pointer_width = "64")]
{ {
const _: () = assert!(size_of::<DeviceSize>() <= size_of::<usize>());
const _: () = assert!(DeviceLayout::MAX_SIZE as usize <= isize::MAX as usize);
// SAFETY: Under the precondition that `DeviceSize` can't overflow `usize`, which we // SAFETY: Under the precondition that `DeviceSize` can't overflow `usize`, which we
// checked above, `DeviceLayout`'s overflow-invariant is the same if not stricter that // checked above, `DeviceLayout`'s overflow-invariant is the same if not stricter that
// of `Layout`. // of `Layout`.
Ok(unsafe { Layout::from_size_align_unchecked(size as usize, alignment as usize) }) Some(unsafe { Layout::from_size_align_unchecked(size as usize, alignment as usize) })
} }
#[cfg(any(target_pointer_width = "32", target_pointer_width = "16"))] #[cfg(any(target_pointer_width = "32", target_pointer_width = "16"))]
{ {
const _: () = assert!(size_of::<DeviceSize>() > size_of::<usize>());
const _: () = assert!(DeviceLayout::MAX_SIZE > isize::MAX as DeviceSize);
if size > usize::MAX as DeviceSize || alignment > usize::MAX as DeviceSize { if size > usize::MAX as DeviceSize || alignment > usize::MAX as DeviceSize {
Err(TryFromDeviceLayoutError) None
} else if let Ok(layout) = Layout::from_size_align(size as usize, alignment as usize) { } else if let Ok(layout) = Layout::from_size_align(size as usize, alignment as usize) {
Ok(layout) Some(layout)
} else { } else {
Err(TryFromDeviceLayoutError) None
} }
} }
} }
@ -116,6 +105,24 @@ impl DeviceLayout {
) )
} }
/// Creates a new `DeviceLayout` for a sized `T`.
#[inline]
pub const fn new_sized<T: BufferContents>() -> DeviceLayout {
T::LAYOUT.unwrap_sized()
}
/// Creates a new `DeviceLayout` for an unsized `T` with an unsized tail of `len` elements.
///
/// Returns [`None`] if `len` is zero or if the total size would exceed
/// [`DeviceLayout::MAX_SIZE`], unless the `T` is actually sized, in which case this behaves
/// identically to [`new_sized`] and `len` is ignored.
///
/// [`new_sized`]: Self::new_sized
#[inline]
pub const fn new_unsized<T: BufferContents + ?Sized>(len: DeviceSize) -> Option<DeviceLayout> {
T::LAYOUT.layout_for_len(len)
}
/// Creates a new `DeviceLayout` from the given `size` and `alignment`. /// Creates a new `DeviceLayout` from the given `size` and `alignment`.
/// ///
/// Returns [`None`] if `size` would exceed [`DeviceLayout::MAX_SIZE`] when rounded up to the /// Returns [`None`] if `size` would exceed [`DeviceLayout::MAX_SIZE`] when rounded up to the
@ -151,6 +158,14 @@ impl DeviceLayout {
DeviceLayout { size, alignment } DeviceLayout { size, alignment }
} }
/// Creates a new `DeviceLayout` for the given `value`.
///
/// Returns [`None`] if the value is zero-sized.
#[inline]
pub fn for_value<T: BufferContents + ?Sized>(value: &T) -> Option<DeviceLayout> {
DeviceLayout::from_layout(Layout::for_value(value))
}
/// Returns the minimum size in bytes for a memory block of this layout. /// Returns the minimum size in bytes for a memory block of this layout.
#[inline] #[inline]
pub const fn size(&self) -> DeviceSize { pub const fn size(&self) -> DeviceSize {
@ -202,10 +217,11 @@ impl DeviceLayout {
/// with padding at the end of each to ensure correct alignment of all instances. /// with padding at the end of each to ensure correct alignment of all instances.
/// ///
/// Returns a tuple consisting of the new layout and the stride, in bytes, of `self`, or /// Returns a tuple consisting of the new layout and the stride, in bytes, of `self`, or
/// returns [`None`] on arithmetic overflow or when the total size would exceed /// returns [`None`] if `n` is zero or when the total size would exceed
/// [`DeviceLayout::MAX_SIZE`]. /// [`DeviceLayout::MAX_SIZE`].
#[inline] #[inline]
pub const fn repeat(&self, n: NonZeroDeviceSize) -> Option<(Self, DeviceSize)> { pub const fn repeat(&self, n: DeviceSize) -> Option<(Self, DeviceSize)> {
let n = try_opt!(NonZeroDeviceSize::new(n));
let stride = self.padded_size(); let stride = self.padded_size();
let size = try_opt!(stride.checked_mul(n)); let size = try_opt!(stride.checked_mul(n));
let layout = try_opt!(DeviceLayout::new(size, self.alignment)); let layout = try_opt!(DeviceLayout::new(size, self.alignment));
@ -275,16 +291,7 @@ impl DeviceLayout {
#[inline(always)] #[inline(always)]
const fn size_alignment_from_layout(layout: &Layout) -> (DeviceSize, DeviceAlignment) { const fn size_alignment_from_layout(layout: &Layout) -> (DeviceSize, DeviceAlignment) {
#[cfg(any( // We checked that `usize` can't overflow `DeviceSize` above, so this can't truncate.
target_pointer_width = "64",
target_pointer_width = "32",
target_pointer_width = "16",
))]
{
const _: () = assert!(size_of::<DeviceSize>() >= size_of::<usize>());
const _: () = assert!(DeviceLayout::MAX_SIZE >= isize::MAX as DeviceSize);
// We checked that `usize` can't overflow `DeviceSize`, so this can't truncate.
let (size, alignment) = (layout.size() as DeviceSize, layout.align() as DeviceSize); let (size, alignment) = (layout.size() as DeviceSize, layout.align() as DeviceSize);
// SAFETY: `Layout`'s alignment-invariant guarantees that it is a power of two. // SAFETY: `Layout`'s alignment-invariant guarantees that it is a power of two.
@ -292,7 +299,6 @@ impl DeviceLayout {
(size, alignment) (size, alignment)
} }
}
} }
impl TryFrom<Layout> for DeviceLayout { impl TryFrom<Layout> for DeviceLayout {
@ -300,7 +306,7 @@ impl TryFrom<Layout> for DeviceLayout {
#[inline] #[inline]
fn try_from(layout: Layout) -> Result<Self, Self::Error> { fn try_from(layout: Layout) -> Result<Self, Self::Error> {
DeviceLayout::from_layout(layout) DeviceLayout::from_layout(layout).ok_or(TryFromLayoutError)
} }
} }
@ -309,7 +315,7 @@ impl TryFrom<DeviceLayout> for Layout {
#[inline] #[inline]
fn try_from(device_layout: DeviceLayout) -> Result<Self, Self::Error> { fn try_from(device_layout: DeviceLayout) -> Result<Self, Self::Error> {
DeviceLayout::into_layout(device_layout) DeviceLayout::into_layout(device_layout).ok_or(TryFromDeviceLayoutError)
} }
} }