diff --git a/examples/async-update/main.rs b/examples/async-update/main.rs index bf69bca4..0df794fa 100644 --- a/examples/async-update/main.rs +++ b/examples/async-update/main.rs @@ -30,7 +30,6 @@ use glam::f32::Mat4; use rand::Rng; use std::{ - alloc::Layout, error::Error, slice, sync::{ @@ -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(); @@ -311,7 +310,7 @@ impl App { | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, ..Default::default() }, - DeviceLayout::from_layout(Layout::new::()).unwrap(), + DeviceLayout::new_sized::(), ) .unwrap() }); diff --git a/examples/bloom/scene.rs b/examples/bloom/scene.rs index d9b13815..929ac32a 100644 --- a/examples/bloom/scene.rs +++ b/examples/bloom/scene.rs @@ -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, @@ -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(); diff --git a/vulkano/src/buffer/allocator.rs b/vulkano/src/buffer/allocator.rs index 549c6009..18cc3921 100644 --- a/vulkano/src/buffer/allocator.rs +++ b/vulkano/src/buffer/allocator.rs @@ -13,7 +13,7 @@ use crate::{ }, DeviceAlignment, }, - DeviceSize, NonZeroDeviceSize, Validated, + DeviceSize, Validated, }; use crossbeam_queue::ArrayQueue; use std::{ @@ -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() } diff --git a/vulkano/src/buffer/mod.rs b/vulkano/src/buffer/mod.rs index fa4d40a8..086a051c 100644 --- a/vulkano/src/buffer/mod.rs +++ b/vulkano/src/buffer/mod.rs @@ -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; @@ -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, diff --git a/vulkano/src/buffer/subbuffer.rs b/vulkano/src/buffer/subbuffer.rs index f3eb12f5..6c2c0462 100644 --- a/vulkano/src/buffer/subbuffer.rs +++ b/vulkano/src/buffer/subbuffer.rs @@ -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 { + pub const fn layout_for_len(&self, len: DeviceSize) -> Option { match &self.0 { BufferContentsLayoutInner::Sized(sized) => Some(*sized), BufferContentsLayoutInner::Unsized { @@ -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!() @@ -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, @@ -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; @@ -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 { .. } => { diff --git a/vulkano/src/memory/alignment.rs b/vulkano/src/memory/alignment.rs index 6e39dcd4..c32149a2 100644 --- a/vulkano/src/memory/alignment.rs +++ b/vulkano/src/memory/alignment.rs @@ -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 @@ -15,8 +15,10 @@ use std::{ #[repr(transparent)] pub struct DeviceAlignment(AlignmentEnum); -const _: () = assert!(size_of::() == size_of::()); -const _: () = assert!(align_of::() == align_of::()); +const _: () = assert!(mem::size_of::() == mem::size_of::()); +const _: () = assert!(mem::align_of::() == mem::align_of::()); + +const _: () = assert!(mem::size_of::() >= mem::size_of::()); impl DeviceAlignment { /// The smallest possible alignment, 1. @@ -28,17 +30,15 @@ impl DeviceAlignment { /// Returns the alignment for a type. #[inline] pub const fn of() -> Self { - #[cfg(any( - target_pointer_width = "64", - target_pointer_width = "32", - target_pointer_width = "16", - ))] - { - const _: () = assert!(size_of::() >= size_of::()); + // SAFETY: rustc guarantees that the alignment of types is a power of two. + unsafe { DeviceAlignment::new_unchecked(mem::align_of::() as DeviceSize) } + } - // SAFETY: rustc guarantees that the alignment of types is a power of two. - unsafe { DeviceAlignment::new_unchecked(align_of::() as DeviceSize) } - } + /// Returns the alignment for a value. + #[inline] + pub fn of_val(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 diff --git a/vulkano/src/memory/allocator/layout.rs b/vulkano/src/memory/allocator/layout.rs index 722a0873..2db717a1 100644 --- a/vulkano/src/memory/allocator/layout.rs +++ b/vulkano/src/memory/allocator/layout.rs @@ -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. @@ -17,6 +19,9 @@ pub struct DeviceLayout { alignment: DeviceAlignment, } +const _: () = assert!(mem::size_of::() >= mem::size_of::()); +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. @@ -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 { + pub const fn from_layout(layout: Layout) -> Option { 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::() >= size_of::()); - 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 { + pub const fn into_layout(self) -> Option { let (size, alignment) = (self.size(), self.alignment().as_devicesize()); #[cfg(target_pointer_width = "64")] { - const _: () = assert!(size_of::() <= size_of::()); - 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::() > size_of::()); - 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 } } } @@ -116,6 +105,24 @@ impl DeviceLayout { ) } + /// Creates a new `DeviceLayout` for a sized `T`. + #[inline] + pub const fn new_sized() -> 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(len: DeviceSize) -> Option { + 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 @@ -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(value: &T) -> Option { + 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 { @@ -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)); @@ -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::() >= size_of::()); - const _: () = assert!(DeviceLayout::MAX_SIZE >= isize::MAX 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); - // We checked that `usize` can't overflow `DeviceSize`, 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) } } @@ -300,7 +306,7 @@ impl TryFrom for DeviceLayout { #[inline] fn try_from(layout: Layout) -> Result { - DeviceLayout::from_layout(layout) + DeviceLayout::from_layout(layout).ok_or(TryFromLayoutError) } } @@ -309,7 +315,7 @@ impl TryFrom for Layout { #[inline] fn try_from(device_layout: DeviceLayout) -> Result { - DeviceLayout::into_layout(device_layout) + DeviceLayout::into_layout(device_layout).ok_or(TryFromDeviceLayoutError) } }