Skip to content

Commit

Permalink
Improve DeviceLayout convenience (#2599)
Browse files Browse the repository at this point in the history
  • Loading branch information
marc0246 authored Nov 4, 2024
1 parent 4f87b52 commit a52e327
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 84 deletions.
5 changes: 2 additions & 3 deletions examples/async-update/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
use glam::f32::Mat4;
use rand::Rng;
use std::{
alloc::Layout,
error::Error,
slice,
sync::{
Expand Down Expand Up @@ -293,7 +292,7 @@ impl App {
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default()
},
DeviceLayout::from_layout(Layout::for_value(&vertices)).unwrap(),
DeviceLayout::for_value(vertices.as_slice()).unwrap(),
)
.unwrap();

Expand All @@ -311,7 +310,7 @@ impl App {
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default()
},
DeviceLayout::from_layout(Layout::new::<vs::Data>()).unwrap(),
DeviceLayout::new_sized::<vs::Data>(),
)
.unwrap()
});
Expand Down
4 changes: 2 additions & 2 deletions examples/bloom/scene.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{App, RenderContext};
use std::{alloc::Layout, slice, sync::Arc};
use std::{slice, sync::Arc};
use vulkano::{
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage},
command_buffer::RenderPassBeginInfo,
Expand Down Expand Up @@ -122,7 +122,7 @@ impl SceneTask {
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default()
},
DeviceLayout::from_layout(Layout::for_value(&vertices)).unwrap(),
DeviceLayout::for_value(vertices.as_slice()).unwrap(),
)
.unwrap();

Expand Down
3 changes: 1 addition & 2 deletions vulkano/src/buffer/allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::{
},
DeviceAlignment,
},
DeviceSize, NonZeroDeviceSize, Validated,
DeviceSize, Validated,
};
use crossbeam_queue::ArrayQueue;
use std::{
Expand Down Expand Up @@ -241,7 +241,6 @@ where
where
T: BufferContents + ?Sized,
{
let len = NonZeroDeviceSize::new(len).expect("empty slices are not valid buffer contents");
let layout = T::LAYOUT.layout_for_len(len).unwrap();

unsafe { &mut *self.state.get() }
Expand Down
5 changes: 2 additions & 3 deletions vulkano/src/buffer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ use crate::{
},
range_map::RangeMap,
sync::{future::AccessError, AccessConflict, CurrentAccess, Sharing},
DeviceSize, NonNullDeviceAddress, NonZeroDeviceSize, Requires, RequiresAllOf, RequiresOneOf,
Validated, ValidationError, Version, VulkanError, VulkanObject,
DeviceSize, NonNullDeviceAddress, Requires, RequiresAllOf, RequiresOneOf, Validated,
ValidationError, Version, VulkanError, VulkanObject,
};
use parking_lot::{Mutex, MutexGuard};
use smallvec::SmallVec;
Expand Down Expand Up @@ -352,7 +352,6 @@ impl Buffer {
where
T: BufferContents + ?Sized,
{
let len = NonZeroDeviceSize::new(len).expect("empty slices are not valid buffer contents");
let layout = T::LAYOUT.layout_for_len(len).unwrap();
let buffer = Subbuffer::new(Buffer::new(
allocator,
Expand Down
16 changes: 8 additions & 8 deletions vulkano/src/buffer/subbuffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -944,10 +944,10 @@ impl BufferContentsLayout {
}
}

/// Returns the [`DeviceLayout`] for the data for the given `len`, or returns [`None`] on
/// arithmetic overflow or if the total size would exceed [`DeviceLayout::MAX_SIZE`].
/// Returns the [`DeviceLayout`] for the data for the given `len`, or returns [`None`] if `len`
/// is zero or if the total size would exceed [`DeviceLayout::MAX_SIZE`].
#[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 {
BufferContentsLayoutInner::Sized(sized) => Some(*sized),
BufferContentsLayoutInner::Unsized {
Expand Down Expand Up @@ -977,7 +977,7 @@ impl BufferContentsLayout {
"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))
} else {
unreachable!()
Expand All @@ -994,7 +994,7 @@ impl BufferContentsLayout {
"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 {
head_layout: None,
element_layout,
Expand All @@ -1021,11 +1021,11 @@ impl BufferContentsLayout {

while i < field_layouts.len() {
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),
None => field_layout,
}),
Err(_) => unreachable!(),
None => unreachable!(),
};

i += 1;
Expand Down Expand Up @@ -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 {
BufferContentsLayoutInner::Sized(sized) => sized,
BufferContentsLayoutInner::Unsized { .. } => {
Expand Down
28 changes: 14 additions & 14 deletions vulkano/src/memory/alignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::{
error::Error,
fmt::{Debug, Display, Formatter, Result as FmtResult},
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
Expand All @@ -15,8 +15,10 @@ use std::{
#[repr(transparent)]
pub struct DeviceAlignment(AlignmentEnum);

const _: () = assert!(size_of::<DeviceAlignment>() == size_of::<DeviceSize>());
const _: () = assert!(align_of::<DeviceAlignment>() == align_of::<DeviceSize>());
const _: () = assert!(mem::size_of::<DeviceAlignment>() == mem::size_of::<DeviceSize>());
const _: () = assert!(mem::align_of::<DeviceAlignment>() == mem::align_of::<DeviceSize>());

const _: () = assert!(mem::size_of::<DeviceSize>() >= mem::size_of::<usize>());

impl DeviceAlignment {
/// The smallest possible alignment, 1.
Expand All @@ -28,17 +30,15 @@ impl DeviceAlignment {
/// 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) }
}
// SAFETY: rustc guarantees that the alignment of types is a power of two.
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
Expand Down
110 changes: 58 additions & 52 deletions vulkano/src/memory/allocator/layout.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
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::{
alloc::Layout,
error::Error,
fmt::{Debug, Display, Formatter, Result as FmtResult},
hash::Hash,
mem::size_of,
mem,
};

/// Vulkan analog of std's [`Layout`], represented using [`DeviceSize`]s.
Expand All @@ -17,6 +19,9 @@ pub struct DeviceLayout {
alignment: DeviceAlignment,
}

const _: () = assert!(mem::size_of::<DeviceSize>() >= mem::size_of::<usize>());
const _: () = assert!(DeviceLayout::MAX_SIZE >= isize::MAX as DeviceSize);

impl DeviceLayout {
/// 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.
Expand All @@ -26,59 +31,43 @@ impl DeviceLayout {
/// undefined behavior*.
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.
#[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);

#[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) {
// SAFETY: Under the precondition that `usize` can't overflow `DeviceSize`, which
// we checked above, `Layout`'s overflow-invariant is the same if not stricter than
// that of `DeviceLayout`.
Ok(unsafe { DeviceLayout::new_unchecked(size, alignment) })
} else {
Err(TryFromLayoutError)
}
if let Some(size) = NonZeroDeviceSize::new(size) {
// SAFETY: Under the precondition that `usize` can't overflow `DeviceSize`, which we
// checked above, `Layout`'s overflow-invariant is the same if not stricter than that
// of `DeviceLayout`.
Some(unsafe { DeviceLayout::new_unchecked(size, alignment) })
} else {
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`.
#[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());

#[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
// checked above, `DeviceLayout`'s overflow-invariant is the same if not stricter that
// 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"))]
{
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 {
Err(TryFromDeviceLayoutError)
None
} else if let Ok(layout) = Layout::from_size_align(size as usize, alignment as usize) {
Ok(layout)
Some(layout)
} else {
Err(TryFromDeviceLayoutError)
None
}
}
}
Expand Down Expand Up @@ -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`.
///
/// Returns [`None`] if `size` would exceed [`DeviceLayout::MAX_SIZE`] when rounded up to the
Expand Down Expand Up @@ -151,6 +158,14 @@ impl DeviceLayout {
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.
#[inline]
pub const fn size(&self) -> DeviceSize {
Expand Down Expand Up @@ -202,10 +217,11 @@ impl DeviceLayout {
/// 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 [`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`].
#[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 size = try_opt!(stride.checked_mul(n));
let layout = try_opt!(DeviceLayout::new(size, self.alignment));
Expand Down Expand Up @@ -275,23 +291,13 @@ impl DeviceLayout {

#[inline(always)]
const fn size_alignment_from_layout(layout: &Layout) -> (DeviceSize, DeviceAlignment) {
#[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);

// We checked that `usize` can't overflow `DeviceSize`, so this can't truncate.
let (size, alignment) = (layout.size() as DeviceSize, layout.align() as DeviceSize);
// We checked that `usize` can't overflow `DeviceSize` above, so this can't truncate.
let (size, alignment) = (layout.size() as DeviceSize, layout.align() as DeviceSize);

// SAFETY: `Layout`'s alignment-invariant guarantees that it is a power of two.
let alignment = unsafe { DeviceAlignment::new_unchecked(alignment) };
// SAFETY: `Layout`'s alignment-invariant guarantees that it is a power of two.
let alignment = unsafe { DeviceAlignment::new_unchecked(alignment) };

(size, alignment)
}
(size, alignment)
}
}

Expand All @@ -300,7 +306,7 @@ impl TryFrom<Layout> for DeviceLayout {

#[inline]
fn try_from(layout: Layout) -> Result<Self, Self::Error> {
DeviceLayout::from_layout(layout)
DeviceLayout::from_layout(layout).ok_or(TryFromLayoutError)
}
}

Expand All @@ -309,7 +315,7 @@ impl TryFrom<DeviceLayout> for Layout {

#[inline]
fn try_from(device_layout: DeviceLayout) -> Result<Self, Self::Error> {
DeviceLayout::into_layout(device_layout)
DeviceLayout::into_layout(device_layout).ok_or(TryFromDeviceLayoutError)
}
}

Expand Down

0 comments on commit a52e327

Please sign in to comment.