vulkano/buffer/subbuffer.rs
1// Copyright (c) 2016 The vulkano developers
2// Licensed under the Apache License, Version 2.0
3// <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
5// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
6// at your option. All files in the project carrying such
7// notice may not be copied, modified, or distributed except
8// according to those terms.
9
10//! A subpart of a buffer.
11
12use super::{allocator::Arena, Buffer, BufferError, BufferMemory};
13use crate::{
14 device::{Device, DeviceOwned},
15 macros::try_opt,
16 memory::{
17 self,
18 allocator::{align_down, align_up, DeviceLayout},
19 is_aligned, DeviceAlignment,
20 },
21 DeviceSize, NonZeroDeviceSize,
22};
23use bytemuck::{AnyBitPattern, PodCastError};
24use std::{
25 alloc::Layout,
26 cmp,
27 error::Error,
28 ffi::c_void,
29 fmt::{Display, Error as FmtError, Formatter},
30 hash::{Hash, Hasher},
31 marker::PhantomData,
32 mem::{self, align_of, size_of},
33 ops::{Deref, DerefMut, Range, RangeBounds},
34 ptr::{self, NonNull},
35 sync::Arc,
36 thread,
37};
38
39#[cfg(feature = "macros")]
40pub use vulkano_macros::BufferContents;
41
42/// A subpart of a buffer.
43///
44/// This type doesn't correspond to any Vulkan object, it exists for API convenience. Most Vulkan
45/// functions that work with buffers take the buffer as argument as well as an offset and size
46/// within the buffer, which we can represent with a single subbuffer instead.
47///
48/// `Subbuffer` also has a type parameter, which is a hint for how the data is going to be
49/// interpreted by the host or device (or both). This is useful so that we can allocate
50/// (sub)buffers that are correctly aligned and have the correct size for their content, and for
51/// type-safety. For example, when reading/writing a subbuffer from the host, you can use
52/// [`Subbuffer::read`]/[`Subbuffer::write`] without worrying about the alignment and size being
53/// correct and about converting your data from/to raw bytes.
54///
55/// There are two ways to get a `Subbuffer`:
56///
57/// - By using the functions on [`Buffer`], which create a new buffer and memory allocation each
58/// time, and give you a `Subbuffer` that has an entire `Buffer` dedicated to it.
59/// - By using the [`SubbufferAllocator`], which creates `Subbuffer`s by suballocating existing
60/// `Buffer`s such that the `Buffer`s can keep being reused.
61///
62/// Alternatively, you can also create a `Buffer` manually and convert it to a `Subbuffer<[u8]>`.
63///
64/// [`SubbufferAllocator`]: super::allocator::SubbufferAllocator
65#[derive(Debug)]
66#[repr(C)]
67pub struct Subbuffer<T: ?Sized> {
68 offset: DeviceSize,
69 size: DeviceSize,
70 parent: SubbufferParent,
71 marker: PhantomData<Arc<T>>,
72}
73
74#[derive(Clone, Debug, PartialEq, Eq, Hash)]
75enum SubbufferParent {
76 Arena(Arc<Arena>),
77 Buffer(Arc<Buffer>),
78}
79
80impl<T: ?Sized> Subbuffer<T> {
81 pub(super) fn from_arena(arena: Arc<Arena>, offset: DeviceSize, size: DeviceSize) -> Self {
82 Subbuffer {
83 offset,
84 size,
85 parent: SubbufferParent::Arena(arena),
86 marker: PhantomData,
87 }
88 }
89
90 /// Returns the offset of the subbuffer, in bytes, relative to the buffer.
91 pub fn offset(&self) -> DeviceSize {
92 self.offset
93 }
94
95 /// Returns the offset of the subbuffer, in bytes, relative to the [`DeviceMemory`] block.
96 fn memory_offset(&self) -> DeviceSize {
97 let allocation = match self.buffer().memory() {
98 BufferMemory::Normal(a) => a,
99 BufferMemory::Sparse => unreachable!(),
100 };
101
102 allocation.offset() + self.offset
103 }
104
105 /// Returns the size of the subbuffer in bytes.
106 pub fn size(&self) -> DeviceSize {
107 self.size
108 }
109
110 /// Returns the range the subbuffer occupies, in bytes, relative to the buffer.
111 pub(crate) fn range(&self) -> Range<DeviceSize> {
112 self.offset..self.offset + self.size
113 }
114
115 /// Returns the buffer that this subbuffer is a part of.
116 pub fn buffer(&self) -> &Arc<Buffer> {
117 match &self.parent {
118 SubbufferParent::Arena(arena) => arena.buffer(),
119 SubbufferParent::Buffer(buffer) => buffer,
120 }
121 }
122
123 /// Returns the mapped pointer to the start of the subbuffer if the memory is host-visible,
124 /// otherwise returns [`None`].
125 pub fn mapped_ptr(&self) -> Option<NonNull<c_void>> {
126 match self.buffer().memory() {
127 BufferMemory::Normal(a) => a.mapped_ptr().map(|ptr| {
128 // SAFETY: The original address came from the Vulkan implementation, and allocation
129 // sizes are guaranteed to not exceed `isize::MAX` when there's a mapped pointer,
130 // so the offset better be in range.
131 unsafe { NonNull::new_unchecked(ptr.as_ptr().add(self.offset as usize)) }
132 }),
133 BufferMemory::Sparse => unreachable!(),
134 }
135 }
136
137 /// Returns the device address for this subbuffer.
138 pub fn device_address(&self) -> Result<NonZeroDeviceSize, BufferError> {
139 self.buffer().device_address().map(|ptr| {
140 // SAFETY: The original address came from the Vulkan implementation, and allocation
141 // sizes are guaranteed to not exceed `DeviceLayout::MAX_SIZE`, so the offset better be
142 // in range.
143 unsafe { NonZeroDeviceSize::new_unchecked(ptr.get() + self.offset) }
144 })
145 }
146
147 /// Casts the subbuffer to a slice of raw bytes.
148 pub fn into_bytes(self) -> Subbuffer<[u8]> {
149 unsafe { self.reinterpret_unchecked_inner() }
150 }
151
152 /// Same as [`into_bytes`], except it works with a reference to the subbuffer.
153 ///
154 /// [`into_bytes`]: Self::into_bytes
155 pub fn as_bytes(&self) -> &Subbuffer<[u8]> {
156 unsafe { self.reinterpret_ref_unchecked_inner() }
157 }
158
159 #[inline(always)]
160 unsafe fn reinterpret_unchecked_inner<U: ?Sized>(self) -> Subbuffer<U> {
161 // SAFETY: All `Subbuffer`s share the same layout.
162 mem::transmute::<Subbuffer<T>, Subbuffer<U>>(self)
163 }
164
165 #[inline(always)]
166 unsafe fn reinterpret_ref_unchecked_inner<U: ?Sized>(&self) -> &Subbuffer<U> {
167 assert!(size_of::<Subbuffer<T>>() == size_of::<Subbuffer<U>>());
168 assert!(align_of::<Subbuffer<T>>() == align_of::<Subbuffer<U>>());
169
170 // SAFETY: All `Subbuffer`s share the same layout.
171 mem::transmute::<&Subbuffer<T>, &Subbuffer<U>>(self)
172 }
173}
174
175impl<T> Subbuffer<T>
176where
177 T: BufferContents + ?Sized,
178{
179 /// Changes the `T` generic parameter of the subbffer to the desired type without checking if
180 /// the contents are correctly aligned and sized.
181 ///
182 /// **NEVER use this function** unless you absolutely have to, and even then, open an issue on
183 /// GitHub instead. **An unaligned / incorrectly sized subbuffer is undefined behavior _both on
184 /// the Rust and the Vulkan side!_**
185 ///
186 /// # Safety
187 ///
188 /// - `self.memory_offset()` must be properly aligned for `U`.
189 /// - `self.size()` must be valid for `U`, which means:
190 /// - If `U` is sized, the size must match exactly.
191 /// - If `U` is unsized, then the subbuffer size minus the size of the head (sized part) of
192 /// the DST must be evenly divisible by the size of the element type.
193 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
194 pub unsafe fn reinterpret_unchecked<U>(self) -> Subbuffer<U>
195 where
196 U: BufferContents + ?Sized,
197 {
198 let element_size = U::LAYOUT.element_size().unwrap_or(1);
199 debug_assert!(is_aligned(self.memory_offset(), U::LAYOUT.alignment()));
200 debug_assert!(self.size >= U::LAYOUT.head_size());
201 debug_assert!((self.size - U::LAYOUT.head_size()) % element_size == 0);
202
203 self.reinterpret_unchecked_inner()
204 }
205
206 /// Same as [`reinterpret_unchecked`], except it works with a reference to the subbuffer.
207 ///
208 /// # Safety
209 ///
210 /// Please read the safety docs on [`reinterpret_unchecked`] carefully.
211 ///
212 /// [`reinterpret_unchecked`]: Self::reinterpret_unchecked
213 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
214 pub unsafe fn reinterpret_ref_unchecked<U>(&self) -> &Subbuffer<U>
215 where
216 U: BufferContents + ?Sized,
217 {
218 let element_size = U::LAYOUT.element_size().unwrap_or(1);
219 debug_assert!(is_aligned(self.memory_offset(), U::LAYOUT.alignment()));
220 debug_assert!(self.size >= U::LAYOUT.head_size());
221 debug_assert!((self.size - U::LAYOUT.head_size()) % element_size == 0);
222
223 self.reinterpret_ref_unchecked_inner()
224 }
225
226 /// Locks the subbuffer in order to read its content from the host.
227 ///
228 /// If the subbuffer is currently used in exclusive mode by the device, this function will
229 /// return an error. Similarly if you called [`write`] on the buffer and haven't dropped the
230 /// lock, this function will return an error as well.
231 ///
232 /// After this function successfully locks the subbuffer, any attempt to submit a command buffer
233 /// that uses it in exclusive mode will fail. You can still submit this subbuffer for
234 /// non-exclusive accesses (ie. reads).
235 ///
236 /// If the memory backing the buffer is not [host-coherent], then this function will lock a
237 /// range that is potentially larger than the subbuffer, because the range given to
238 /// [`invalidate_range`] must be aligned to the [`non_coherent_atom_size`]. This means that for
239 /// example if your Vulkan implementation reports an atom size of 64, and you tried to put 2
240 /// subbuffers of size 32 in the same buffer, one at offset 0 and one at offset 32, while the
241 /// buffer is backed by non-coherent memory, then invalidating one subbuffer would also
242 /// invalidate the other subbuffer. This can lead to data races and is therefore not allowed.
243 /// What you should do in that case is ensure that each subbuffer is aligned to the
244 /// non-coherent atom size, so in this case one would be at offset 0 and the other at offset
245 /// 64. [`SubbufferAllocator`] does this automatically.
246 ///
247 /// [host-coherent]: crate::memory::MemoryPropertyFlags::HOST_COHERENT
248 /// [`invalidate_range`]: crate::memory::allocator::MemoryAlloc::invalidate_range
249 /// [`non_coherent_atom_size`]: crate::device::Properties::non_coherent_atom_size
250 /// [`write`]: Self::write
251 /// [`SubbufferAllocator`]: super::allocator::SubbufferAllocator
252 pub fn read(&self) -> Result<BufferReadGuard<'_, T>, BufferError> {
253 let allocation = match self.buffer().memory() {
254 BufferMemory::Normal(a) => a,
255 BufferMemory::Sparse => todo!("`Subbuffer::read` doesn't support sparse binding yet"),
256 };
257
258 let range = if let Some(atom_size) = allocation.atom_size() {
259 // This works because the suballocators align allocations to the non-coherent atom size
260 // when the memory is host-visible but not host-coherent.
261 let start = align_down(self.offset, atom_size);
262 let end = cmp::min(
263 align_up(self.offset + self.size, atom_size),
264 allocation.size(),
265 );
266
267 Range { start, end }
268 } else {
269 self.range()
270 };
271
272 let mut state = self.buffer().state();
273 state.check_cpu_read(range.clone())?;
274 unsafe { state.cpu_read_lock(range.clone()) };
275
276 if allocation.atom_size().is_some() {
277 // If there are other read locks being held at this point, they also called
278 // `invalidate_range` when locking. The GPU can't write data while the CPU holds a read
279 // lock, so there will be no new data and this call will do nothing.
280 // TODO: probably still more efficient to call it only if we're the first to acquire a
281 // read lock, but the number of CPU locks isn't currently tracked anywhere.
282 unsafe { allocation.invalidate_range(range.clone()) }?;
283 }
284
285 let mapped_ptr = self.mapped_ptr().ok_or(BufferError::MemoryNotHostVisible)?;
286 // SAFETY: `Subbuffer` guarantees that its contents are laid out correctly for `T`.
287 let data = unsafe { &*T::from_ffi(mapped_ptr.as_ptr(), self.size as usize) };
288
289 Ok(BufferReadGuard {
290 subbuffer: self,
291 data,
292 range,
293 })
294 }
295
296 /// Locks the subbuffer in order to write its content from the host.
297 ///
298 /// If the subbuffer is currently in use by the device, this function will return an error.
299 /// Similarly if you called [`read`] on the subbuffer and haven't dropped the lock, this
300 /// function will return an error as well.
301 ///
302 /// After this function successfully locks the buffer, any attempt to submit a command buffer
303 /// that uses it and any attempt to call `read` will return an error.
304 ///
305 /// If the memory backing the buffer is not [host-coherent], then this function will lock a
306 /// range that is potentially larger than the subbuffer, because the range given to
307 /// [`flush_range`] must be aligned to the [`non_coherent_atom_size`]. This means that for
308 /// example if your Vulkan implementation reports an atom size of 64, and you tried to put 2
309 /// subbuffers of size 32 in the same buffer, one at offset 0 and one at offset 32, while the
310 /// buffer is backed by non-coherent memory, then flushing one subbuffer would also flush the
311 /// other subbuffer. This can lead to data races and is therefore not allowed. What you should
312 /// do in that case is ensure that each subbuffer is aligned to the non-coherent atom size, so
313 /// in this case one would be at offset 0 and the other at offset 64. [`SubbufferAllocator`]
314 /// does this automatically.
315 ///
316 /// [host-coherent]: crate::memory::MemoryPropertyFlags::HOST_COHERENT
317 /// [`flush_range`]: crate::memory::allocator::MemoryAlloc::flush_range
318 /// [`non_coherent_atom_size`]: crate::device::Properties::non_coherent_atom_size
319 /// [`read`]: Self::read
320 /// [`SubbufferAllocator`]: super::allocator::SubbufferAllocator
321 pub fn write(&self) -> Result<BufferWriteGuard<'_, T>, BufferError> {
322 let allocation = match self.buffer().memory() {
323 BufferMemory::Normal(a) => a,
324 BufferMemory::Sparse => todo!("`Subbuffer::write` doesn't support sparse binding yet"),
325 };
326
327 let range = if let Some(atom_size) = allocation.atom_size() {
328 // This works because the suballocators align allocations to the non-coherent atom size
329 // when the memory is host-visible but not host-coherent.
330 let start = align_down(self.offset, atom_size);
331 let end = cmp::min(
332 align_up(self.offset + self.size, atom_size),
333 allocation.size(),
334 );
335
336 Range { start, end }
337 } else {
338 self.range()
339 };
340
341 let mut state = self.buffer().state();
342 state.check_cpu_write(range.clone())?;
343 unsafe { state.cpu_write_lock(range.clone()) };
344
345 if allocation.atom_size().is_some() {
346 unsafe { allocation.invalidate_range(range.clone()) }?;
347 }
348
349 let mapped_ptr = self.mapped_ptr().ok_or(BufferError::MemoryNotHostVisible)?;
350 // SAFETY: `Subbuffer` guarantees that its contents are laid out correctly for `T`.
351 let data = unsafe { &mut *T::from_ffi(mapped_ptr.as_ptr(), self.size as usize) };
352
353 Ok(BufferWriteGuard {
354 subbuffer: self,
355 data,
356 range,
357 })
358 }
359}
360
361impl<T> Subbuffer<T> {
362 /// Converts the subbuffer to a slice of one element.
363 pub fn into_slice(self) -> Subbuffer<[T]> {
364 unsafe { self.reinterpret_unchecked_inner() }
365 }
366
367 /// Same as [`into_slice`], except it works with a reference to the subbuffer.
368 ///
369 /// [`into_slice`]: Self::into_slice
370 pub fn as_slice(&self) -> &Subbuffer<[T]> {
371 unsafe { self.reinterpret_ref_unchecked_inner() }
372 }
373}
374
375impl<T> Subbuffer<T>
376where
377 T: BufferContents,
378{
379 /// Tries to cast a subbuffer of raw bytes to a `Subbuffer<T>`.
380 pub fn try_from_bytes(subbuffer: Subbuffer<[u8]>) -> Result<Self, PodCastError> {
381 if subbuffer.size() != size_of::<T>() as DeviceSize {
382 Err(PodCastError::SizeMismatch)
383 } else if !is_aligned(subbuffer.memory_offset(), DeviceAlignment::of::<T>()) {
384 Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned)
385 } else {
386 Ok(unsafe { subbuffer.reinterpret_unchecked() })
387 }
388 }
389
390 /// Tries to cast the subbuffer to a different type.
391 pub fn try_cast<U>(self) -> Result<Subbuffer<U>, PodCastError>
392 where
393 U: BufferContents,
394 {
395 if size_of::<U>() != size_of::<T>() {
396 Err(PodCastError::SizeMismatch)
397 } else if align_of::<U>() > align_of::<T>()
398 && !is_aligned(self.memory_offset(), DeviceAlignment::of::<U>())
399 {
400 Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned)
401 } else {
402 Ok(unsafe { self.reinterpret_unchecked() })
403 }
404 }
405}
406
407impl<T> Subbuffer<[T]> {
408 /// Returns the number of elements in the slice.
409 pub fn len(&self) -> DeviceSize {
410 debug_assert!(self.size % size_of::<T>() as DeviceSize == 0);
411
412 self.size / size_of::<T>() as DeviceSize
413 }
414
415 /// Reduces the subbuffer to just one element of the slice.
416 ///
417 /// # Panics
418 ///
419 /// - Panics if `index` is out of bounds.
420 pub fn index(self, index: DeviceSize) -> Subbuffer<T> {
421 assert!(index <= self.len());
422
423 unsafe { self.index_unchecked(index) }
424 }
425
426 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
427 pub unsafe fn index_unchecked(self, index: DeviceSize) -> Subbuffer<T> {
428 Subbuffer {
429 offset: self.offset + index * size_of::<T>() as DeviceSize,
430 size: size_of::<T>() as DeviceSize,
431 parent: self.parent,
432 marker: PhantomData,
433 }
434 }
435
436 /// Reduces the subbuffer to just a range of the slice.
437 ///
438 /// # Panics
439 ///
440 /// - Panics if `range` is out of bounds.
441 /// - Panics if `range` is empty.
442 pub fn slice(mut self, range: impl RangeBounds<DeviceSize>) -> Subbuffer<[T]> {
443 let Range { start, end } = memory::range(range, ..self.len()).unwrap();
444
445 self.offset += start * size_of::<T>() as DeviceSize;
446 self.size = (end - start) * size_of::<T>() as DeviceSize;
447 assert!(self.size != 0);
448
449 self
450 }
451
452 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
453 pub unsafe fn slice_unchecked(mut self, range: impl RangeBounds<DeviceSize>) -> Subbuffer<[T]> {
454 let Range { start, end } = memory::range(range, ..self.len()).unwrap_unchecked();
455
456 self.offset += start * size_of::<T>() as DeviceSize;
457 self.size = (end - start) * size_of::<T>() as DeviceSize;
458 debug_assert!(self.size != 0);
459
460 self
461 }
462
463 /// Splits the subbuffer into two at an index.
464 ///
465 /// # Panics
466 ///
467 /// - Panics if `mid` is not greater than `0`.
468 /// - Panics if `mid` is not less than `self.len()`.
469 pub fn split_at(self, mid: DeviceSize) -> (Subbuffer<[T]>, Subbuffer<[T]>) {
470 assert!(0 < mid && mid < self.len());
471
472 unsafe { self.split_at_unchecked(mid) }
473 }
474
475 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
476 pub unsafe fn split_at_unchecked(self, mid: DeviceSize) -> (Subbuffer<[T]>, Subbuffer<[T]>) {
477 (
478 self.clone().slice_unchecked(..mid),
479 self.slice_unchecked(mid..),
480 )
481 }
482}
483
484impl Subbuffer<[u8]> {
485 /// Creates a new `Subbuffer<[u8]>` spanning the whole buffer.
486 #[inline]
487 pub fn new(buffer: Arc<Buffer>) -> Self {
488 Subbuffer {
489 offset: 0,
490 size: buffer.size(),
491 parent: SubbufferParent::Buffer(buffer),
492 marker: PhantomData,
493 }
494 }
495
496 /// Casts the slice to a different element type while ensuring correct alignment for the type.
497 ///
498 /// The offset of the subbuffer is rounded up to the alignment of `T` and the size abjusted for
499 /// the padding, then the size is rounded down to the nearest multiple of `T`'s size.
500 ///
501 /// # Panics
502 ///
503 /// - Panics if the aligned offset would be out of bounds.
504 pub fn cast_aligned<T>(self) -> Subbuffer<[T]>
505 where
506 T: BufferContents,
507 {
508 let layout = DeviceLayout::from_layout(Layout::new::<T>()).unwrap();
509 let aligned = self.align_to(layout);
510
511 unsafe { aligned.reinterpret_unchecked() }
512 }
513
514 /// Aligns the subbuffer to the given `layout` by rounding the offset up to
515 /// `layout.alignment()` and adjusting the size for the padding, and then rounding the size
516 /// down to the nearest multiple of `layout.size()`.
517 ///
518 /// # Panics
519 ///
520 /// - Panics if the aligned offset would be out of bounds.
521 /// - Panics if `layout.alignment()` exceeds `64`.
522 #[inline]
523 pub fn align_to(mut self, layout: DeviceLayout) -> Subbuffer<[u8]> {
524 assert!(layout.alignment().as_devicesize() <= 64);
525
526 let offset = self.memory_offset();
527 let padding_front = align_up(offset, layout.alignment()) - offset;
528
529 self.offset += padding_front;
530 self.size = self.size.checked_sub(padding_front).unwrap();
531 self.size -= self.size % layout.size();
532
533 self
534 }
535}
536
537impl<T> Subbuffer<[T]>
538where
539 T: BufferContents,
540{
541 /// Tries to cast the slice to a different element type.
542 pub fn try_cast_slice<U>(self) -> Result<Subbuffer<[U]>, PodCastError>
543 where
544 U: BufferContents,
545 {
546 if size_of::<U>() != size_of::<T>() && self.size() % size_of::<U>() as DeviceSize != 0 {
547 Err(PodCastError::OutputSliceWouldHaveSlop)
548 } else if align_of::<U>() > align_of::<T>()
549 && !is_aligned(self.memory_offset(), DeviceAlignment::of::<U>())
550 {
551 Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned)
552 } else {
553 Ok(unsafe { self.reinterpret_unchecked() })
554 }
555 }
556}
557
558impl From<Arc<Buffer>> for Subbuffer<[u8]> {
559 #[inline]
560 fn from(buffer: Arc<Buffer>) -> Self {
561 Self::new(buffer)
562 }
563}
564
565impl<T: ?Sized> Clone for Subbuffer<T> {
566 fn clone(&self) -> Self {
567 Subbuffer {
568 parent: self.parent.clone(),
569 ..*self
570 }
571 }
572}
573
574unsafe impl<T: ?Sized> DeviceOwned for Subbuffer<T> {
575 fn device(&self) -> &Arc<Device> {
576 self.buffer().device()
577 }
578}
579
580impl<T: ?Sized> PartialEq for Subbuffer<T> {
581 fn eq(&self, other: &Self) -> bool {
582 self.parent == other.parent && self.offset == other.offset && self.size == other.size
583 }
584}
585
586impl<T: ?Sized> Eq for Subbuffer<T> {}
587
588impl<T: ?Sized> Hash for Subbuffer<T> {
589 fn hash<H: Hasher>(&self, state: &mut H) {
590 self.parent.hash(state);
591 self.offset.hash(state);
592 self.size.hash(state);
593 }
594}
595
596/// RAII structure used to release the CPU access of a subbuffer when dropped.
597///
598/// This structure is created by the [`read`] method on [`Subbuffer`].
599///
600/// [`read`]: Subbuffer::read
601#[derive(Debug)]
602pub struct BufferReadGuard<'a, T: ?Sized> {
603 subbuffer: &'a Subbuffer<T>,
604 data: &'a T,
605 range: Range<DeviceSize>,
606}
607
608impl<T: ?Sized> Drop for BufferReadGuard<'_, T> {
609 fn drop(&mut self) {
610 let mut state = self.subbuffer.buffer().state();
611 unsafe { state.cpu_read_unlock(self.range.clone()) };
612 }
613}
614
615impl<T: ?Sized> Deref for BufferReadGuard<'_, T> {
616 type Target = T;
617
618 fn deref(&self) -> &Self::Target {
619 self.data
620 }
621}
622
623/// RAII structure used to release the CPU write access of a subbuffer when dropped.
624///
625/// This structure is created by the [`write`] method on [`Subbuffer`].
626///
627/// [`write`]: Subbuffer::write
628#[derive(Debug)]
629pub struct BufferWriteGuard<'a, T: ?Sized> {
630 subbuffer: &'a Subbuffer<T>,
631 data: &'a mut T,
632 range: Range<DeviceSize>,
633}
634
635impl<T: ?Sized> Drop for BufferWriteGuard<'_, T> {
636 fn drop(&mut self) {
637 let allocation = match self.subbuffer.buffer().memory() {
638 BufferMemory::Normal(a) => a,
639 BufferMemory::Sparse => unreachable!(),
640 };
641
642 if allocation.atom_size().is_some() && !thread::panicking() {
643 unsafe { allocation.flush_range(self.range.clone()).unwrap() };
644 }
645
646 let mut state = self.subbuffer.buffer().state();
647 unsafe { state.cpu_write_unlock(self.range.clone()) };
648 }
649}
650
651impl<T: ?Sized> Deref for BufferWriteGuard<'_, T> {
652 type Target = T;
653
654 fn deref(&self) -> &Self::Target {
655 self.data
656 }
657}
658
659impl<T: ?Sized> DerefMut for BufferWriteGuard<'_, T> {
660 fn deref_mut(&mut self) -> &mut Self::Target {
661 self.data
662 }
663}
664
665/// Error when attempting to CPU-read a buffer.
666#[derive(Clone, Debug, PartialEq, Eq)]
667pub enum ReadLockError {
668 /// The buffer is already locked for write mode by the CPU.
669 CpuWriteLocked,
670 /// The buffer is already locked for write mode by the GPU.
671 GpuWriteLocked,
672}
673
674impl Error for ReadLockError {}
675
676impl Display for ReadLockError {
677 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
678 write!(
679 f,
680 "{}",
681 match self {
682 ReadLockError::CpuWriteLocked => {
683 "the buffer is already locked for write mode by the CPU"
684 }
685 ReadLockError::GpuWriteLocked => {
686 "the buffer is already locked for write mode by the GPU"
687 }
688 }
689 )
690 }
691}
692
693/// Error when attempting to CPU-write a buffer.
694#[derive(Clone, Debug, PartialEq, Eq)]
695pub enum WriteLockError {
696 /// The buffer is already locked by the CPU.
697 CpuLocked,
698 /// The buffer is already locked by the GPU.
699 GpuLocked,
700}
701
702impl Error for WriteLockError {}
703
704impl Display for WriteLockError {
705 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
706 write!(
707 f,
708 "{}",
709 match self {
710 WriteLockError::CpuLocked => "the buffer is already locked by the CPU",
711 WriteLockError::GpuLocked => "the buffer is already locked by the GPU",
712 }
713 )
714 }
715}
716
717/// Trait for types of data that can be put in a buffer.
718///
719/// This trait is not intended to be implemented manually (ever) and attempting so will make you
720/// one sad individual very quickly. Rather you should use [the derive macro]. Note also that there
721/// are blanket implementations of this trait: you don't need to implement it if the type in
722/// question already implements bytemuck's [`AnyBitPattern`]. Most if not all linear algebra crates
723/// have a feature flag that you can enable for bytemuck support. The trait is also already
724/// implemented for all slices where the element type implements `BufferContents`.
725///
726/// # Examples
727///
728/// Deriving the trait for sized types:
729///
730/// ```
731/// # use vulkano::buffer::BufferContents;
732/// #[derive(BufferContents)]
733/// #[repr(C)]
734/// struct MyData {
735/// x: f32,
736/// y: f32,
737/// array: [i32; 12],
738/// }
739/// ```
740///
741/// Deriving the trait for unsized types works the same:
742///
743/// ```
744/// # use vulkano::buffer::BufferContents;
745/// #[derive(BufferContents)]
746/// #[repr(C)]
747/// struct MyData {
748/// x: f32,
749/// y: f32,
750/// slice: [i32],
751/// }
752/// ```
753///
754/// This even works if the last field is a user-defined DST too:
755///
756/// ```
757/// # use vulkano::buffer::BufferContents;
758/// #[derive(BufferContents)]
759/// #[repr(C)]
760/// struct MyData {
761/// x: f32,
762/// y: f32,
763/// other: OtherData,
764/// }
765///
766/// #[derive(BufferContents)]
767/// #[repr(C)]
768/// struct OtherData {
769/// slice: [i32],
770/// }
771/// ```
772///
773/// You can also use generics if you please:
774///
775/// ```
776/// # use vulkano::buffer::BufferContents;
777/// #[derive(BufferContents)]
778/// #[repr(C)]
779/// struct MyData<T, U> {
780/// x: T,
781/// y: T,
782/// slice: [U],
783/// }
784/// ```
785///
786/// This even works with dependently-sized types:
787///
788/// ```
789/// # use vulkano::buffer::BufferContents;
790/// #[derive(BufferContents)]
791/// #[repr(C)]
792/// struct MyData<T>
793/// where
794/// T: ?Sized,
795/// {
796/// x: f32,
797/// y: f32,
798/// z: T,
799/// }
800/// ```
801///
802/// [the derive macro]: vulkano_macros::BufferContents
803//
804// If you absolutely *must* implement this trait by hand, here are the safety requirements (but
805// please open an issue on GitHub instead):
806//
807// - The type must be a struct and all fields must implement `BufferContents`.
808// - `LAYOUT` must be the correct layout for the type, which also means the type must either be
809// sized or if it's unsized then its metadata must be the same as that of a slice. Implementing
810// `BufferContents` for any other kind of DST is instantaneous horrifically undefined behavior.
811// - `from_ffi` must create a pointer with the same address as the `data` parameter that is passed
812// in. The pointer is expected to be aligned properly already.
813// - `from_ffi` must create a pointer that is expected to be valid for reads (and potentially
814// writes) for exactly `range` bytes. The `data` and `range` are expected to be valid for the
815// `LAYOUT`.
816pub unsafe trait BufferContents: Send + Sync + 'static {
817 /// The layout of the contents.
818 const LAYOUT: BufferContentsLayout;
819
820 /// Creates a pointer to `Self` from a pointer to the start of the data and a range in bytes.
821 ///
822 /// # Safety
823 ///
824 /// - If `Self` is sized, then `range` must match the size exactly.
825 /// - If `Self` is unsized, then the `range` minus the size of the head (sized part) of the DST
826 /// must be evenly divisible by the size of the element type.
827 #[doc(hidden)]
828 unsafe fn from_ffi(data: *mut c_void, range: usize) -> *mut Self;
829}
830
831unsafe impl<T> BufferContents for T
832where
833 T: AnyBitPattern + Send + Sync,
834{
835 const LAYOUT: BufferContentsLayout =
836 if let Some(layout) = BufferContentsLayout::from_sized(Layout::new::<T>()) {
837 layout
838 } else {
839 panic!("zero-sized types are not valid buffer contents");
840 };
841
842 #[inline(always)]
843 unsafe fn from_ffi(data: *mut c_void, range: usize) -> *mut Self {
844 debug_assert!(range == size_of::<T>());
845 debug_assert!(data as usize % align_of::<T>() == 0);
846
847 data.cast()
848 }
849}
850
851unsafe impl<T> BufferContents for [T]
852where
853 T: BufferContents,
854{
855 const LAYOUT: BufferContentsLayout = BufferContentsLayout(BufferContentsLayoutInner::Unsized {
856 head_layout: None,
857 element_layout: T::LAYOUT.unwrap_sized(),
858 });
859
860 #[inline(always)]
861 unsafe fn from_ffi(data: *mut c_void, range: usize) -> *mut Self {
862 debug_assert!(range % size_of::<T>() == 0);
863 debug_assert!(data as usize % align_of::<T>() == 0);
864 let len = range / size_of::<T>();
865
866 ptr::slice_from_raw_parts_mut(data.cast(), len)
867 }
868}
869
870/// Describes the layout required for a type so that it can be read from/written to a buffer. This
871/// is used to allocate (sub)buffers generically.
872///
873/// This is similar to [`DeviceLayout`] except that this exists for the sole purpose of describing
874/// the layout of buffer contents specifically. Which means for example that the sizedness of the
875/// type is captured, as well as the layout of the head and tail if the layout is for unsized data,
876/// in order to be able to represent everything that Vulkan can stuff in a buffer.
877///
878/// `BufferContentsLayout` also has an additional invariant compared to `DeviceLayout`: the
879/// alignment of the data must not exceed `64`. This is because that's the guaranteed alignment
880/// that all `DeviceMemory` blocks must be aligned to at minimum, and hence any greater alignment
881/// can't be guaranteed. Other than that, the invariant that sizes must be non-zero applies here as
882/// well, for both sized data and the element type of unsized data.
883#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
884pub struct BufferContentsLayout(BufferContentsLayoutInner);
885
886#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
887enum BufferContentsLayoutInner {
888 Sized(DeviceLayout),
889 Unsized {
890 head_layout: Option<DeviceLayout>,
891 element_layout: DeviceLayout,
892 },
893}
894
895impl BufferContentsLayout {
896 /// Returns the size of the head (sized part). If the data has no sized part, then this will
897 /// return 0.
898 #[inline]
899 pub const fn head_size(&self) -> DeviceSize {
900 match &self.0 {
901 BufferContentsLayoutInner::Sized(sized) => sized.size(),
902 BufferContentsLayoutInner::Unsized {
903 head_layout: None, ..
904 } => 0,
905 BufferContentsLayoutInner::Unsized {
906 head_layout: Some(head_layout),
907 ..
908 } => head_layout.size(),
909 }
910 }
911
912 /// Returns the size of the element type if the data is unsized, or returns [`None`].
913 /// Guaranteed to be non-zero.
914 #[inline]
915 pub const fn element_size(&self) -> Option<DeviceSize> {
916 match &self.0 {
917 BufferContentsLayoutInner::Sized(_) => None,
918 BufferContentsLayoutInner::Unsized { element_layout, .. } => {
919 Some(element_layout.size())
920 }
921 }
922 }
923
924 /// Returns the alignment required for the data. Guaranteed to not exceed `64`.
925 #[inline]
926 pub const fn alignment(&self) -> DeviceAlignment {
927 match &self.0 {
928 BufferContentsLayoutInner::Sized(sized) => sized.alignment(),
929 BufferContentsLayoutInner::Unsized {
930 head_layout: None,
931 element_layout,
932 } => element_layout.alignment(),
933 BufferContentsLayoutInner::Unsized {
934 head_layout: Some(head_layout),
935 ..
936 } => head_layout.alignment(),
937 }
938 }
939
940 /// Returns the [`DeviceLayout`] for the data for the given `len`, or returns [`None`] on
941 /// arithmetic overflow or if the total size would exceed [`DeviceLayout::MAX_SIZE`].
942 #[inline]
943 pub const fn layout_for_len(&self, len: NonZeroDeviceSize) -> Option<DeviceLayout> {
944 match &self.0 {
945 BufferContentsLayoutInner::Sized(sized) => Some(*sized),
946 BufferContentsLayoutInner::Unsized {
947 head_layout,
948 element_layout,
949 } => {
950 let (tail_layout, _) = try_opt!(element_layout.repeat(len));
951
952 if let Some(head_layout) = head_layout {
953 let (layout, _) = try_opt!(head_layout.extend(tail_layout));
954
955 Some(layout.pad_to_alignment())
956 } else {
957 Some(tail_layout)
958 }
959 }
960 }
961 }
962
963 /// Creates a new `BufferContentsLayout` from a sized layout. This is inteded for use by the
964 /// derive macro only.
965 #[doc(hidden)]
966 #[inline]
967 pub const fn from_sized(sized: Layout) -> Option<Self> {
968 assert!(
969 sized.align() <= 64,
970 "types with alignments above 64 are not valid buffer contents",
971 );
972
973 if let Ok(sized) = DeviceLayout::from_layout(sized) {
974 Some(Self(BufferContentsLayoutInner::Sized(sized)))
975 } else {
976 None
977 }
978 }
979
980 /// Creates a new `BufferContentsLayout` from a head and element layout. This is inteded for
981 /// use by the derive macro only.
982 #[doc(hidden)]
983 #[inline]
984 pub const fn from_head_element_layout(
985 head_layout: Layout,
986 element_layout: Layout,
987 ) -> Option<Self> {
988 if head_layout.align() > 64 || element_layout.align() > 64 {
989 panic!("types with alignments above 64 are not valid buffer contents");
990 }
991
992 // The head of a `BufferContentsLayout` can be zero-sized.
993 // TODO: Replace with `Result::ok` once its constness is stabilized.
994 let head_layout = if let Ok(head_layout) = DeviceLayout::from_layout(head_layout) {
995 Some(head_layout)
996 } else {
997 None
998 };
999
1000 if let Ok(element_layout) = DeviceLayout::from_layout(element_layout) {
1001 Some(Self(BufferContentsLayoutInner::Unsized {
1002 head_layout,
1003 element_layout,
1004 }))
1005 } else {
1006 None
1007 }
1008 }
1009
1010 /// Extends the given `previous` [`Layout`] by `self`. This is intended for use by the derive
1011 /// macro only.
1012 #[doc(hidden)]
1013 #[inline]
1014 pub const fn extend_from_layout(self, previous: &Layout) -> Option<Self> {
1015 assert!(
1016 previous.align() <= 64,
1017 "types with alignments above 64 are not valid buffer contents",
1018 );
1019
1020 match self.0 {
1021 BufferContentsLayoutInner::Sized(sized) => {
1022 let (sized, _) = try_opt!(sized.extend_from_layout(previous));
1023
1024 Some(Self(BufferContentsLayoutInner::Sized(sized)))
1025 }
1026 BufferContentsLayoutInner::Unsized {
1027 head_layout: None,
1028 element_layout,
1029 } => {
1030 // The head of a `BufferContentsLayout` can be zero-sized.
1031 // TODO: Replace with `Result::ok` once its constness is stabilized.
1032 let head_layout = if let Ok(head_layout) = DeviceLayout::from_layout(*previous) {
1033 Some(head_layout)
1034 } else {
1035 None
1036 };
1037
1038 Some(Self(BufferContentsLayoutInner::Unsized {
1039 head_layout,
1040 element_layout,
1041 }))
1042 }
1043 BufferContentsLayoutInner::Unsized {
1044 head_layout: Some(head_layout),
1045 element_layout,
1046 } => {
1047 let (head_layout, _) = try_opt!(head_layout.extend_from_layout(previous));
1048
1049 Some(Self(BufferContentsLayoutInner::Unsized {
1050 head_layout: Some(head_layout),
1051 element_layout,
1052 }))
1053 }
1054 }
1055 }
1056
1057 /// Creates a new `BufferContentsLayout` by rounding up the size of the head to the nearest
1058 /// multiple of its alignment if the layout is sized, or by rounding up the size of the head to
1059 /// the nearest multiple of the alignment of the element type and aligning the head to the
1060 /// alignment of the element type if there is a sized part. Doesn't do anything if there is no
1061 /// sized part. Returns [`None`] if the new head size would exceed [`DeviceLayout::MAX_SIZE`].
1062 /// This is inteded for use by the derive macro only.
1063 #[doc(hidden)]
1064 #[inline]
1065 pub const fn pad_to_alignment(&self) -> Option<Self> {
1066 match &self.0 {
1067 BufferContentsLayoutInner::Sized(sized) => Some(Self(
1068 BufferContentsLayoutInner::Sized(sized.pad_to_alignment()),
1069 )),
1070 BufferContentsLayoutInner::Unsized {
1071 head_layout: None,
1072 element_layout,
1073 } => Some(Self(BufferContentsLayoutInner::Unsized {
1074 head_layout: None,
1075 element_layout: *element_layout,
1076 })),
1077 BufferContentsLayoutInner::Unsized {
1078 head_layout: Some(head_layout),
1079 element_layout,
1080 } => {
1081 // We must pad the head to the alignment of the element type, *not* the alignment
1082 // of the head.
1083 //
1084 // Consider a head layout of `(u8, u8, u8)` and an element layout of `u32`. If we
1085 // padded the head to its own alignment, like is the case for sized layouts, it
1086 // wouldn't change the size. Yet there is padding between the head and the first
1087 // element of the slice.
1088 //
1089 // The reverse is true: consider a head layout of `(u16, u8)` and an element layout
1090 // of `u8`. If we padded the head to its own alignment, it would be too large.
1091 let padded_head_size =
1092 head_layout.size() + head_layout.padding_needed_for(element_layout.alignment());
1093
1094 // SAFETY: `BufferContentsLayout`'s invariant guarantees that the alignment of the
1095 // element type doesn't exceed 64, which together with the overflow invariant of
1096 // `DeviceLayout` means that this can't overflow.
1097 let padded_head_size =
1098 unsafe { NonZeroDeviceSize::new_unchecked(padded_head_size) };
1099
1100 // We have to align the head to the alignment of the element type, so that the
1101 // struct as a whole is aligned correctly when a different struct is extended with
1102 // this one.
1103 //
1104 // Note that this is *not* the same as aligning the head to the alignment of the
1105 // element type and then padding the layout to its alignment. Consider the same
1106 // layout from above, with a head layout of `(u16, u8)` and an element layout of
1107 // `u8`. If we aligned the head to the element type and then padded it to its own
1108 // alignment, we would get the same wrong result as above. This instead ensures the
1109 // head is padded to the element and aligned to it, without the alignment of the
1110 // head interfering.
1111 let alignment =
1112 DeviceAlignment::max(head_layout.alignment(), element_layout.alignment());
1113
1114 if let Some(head_layout) = DeviceLayout::new(padded_head_size, alignment) {
1115 Some(Self(BufferContentsLayoutInner::Unsized {
1116 head_layout: Some(head_layout),
1117 element_layout: *element_layout,
1118 }))
1119 } else {
1120 None
1121 }
1122 }
1123 }
1124 }
1125
1126 pub(super) const fn unwrap_sized(self) -> DeviceLayout {
1127 match self.0 {
1128 BufferContentsLayoutInner::Sized(sized) => sized,
1129 BufferContentsLayoutInner::Unsized { .. } => {
1130 panic!("called `BufferContentsLayout::unwrap_sized` on an unsized layout");
1131 }
1132 }
1133 }
1134}
1135
1136#[cfg(test)]
1137mod tests {
1138 use super::*;
1139 use crate::{
1140 buffer::{
1141 sys::{BufferCreateInfo, RawBuffer},
1142 BufferUsage,
1143 },
1144 memory::{
1145 allocator::{
1146 AllocationCreateInfo, AllocationType, DeviceLayout, MemoryAllocator, MemoryUsage,
1147 StandardMemoryAllocator,
1148 },
1149 MemoryRequirements,
1150 },
1151 };
1152
1153 #[test]
1154 fn derive_buffer_contents() {
1155 #[derive(BufferContents)]
1156 #[repr(C)]
1157 struct Test1(u32, u64, u8);
1158
1159 assert_eq!(Test1::LAYOUT.head_size() as usize, size_of::<Test1>());
1160 assert_eq!(Test1::LAYOUT.element_size(), None);
1161 assert_eq!(
1162 Test1::LAYOUT.alignment().as_devicesize() as usize,
1163 align_of::<Test1>(),
1164 );
1165
1166 #[derive(BufferContents)]
1167 #[repr(C)]
1168 struct Composite1(Test1, [f32; 10], Test1);
1169
1170 assert_eq!(
1171 Composite1::LAYOUT.head_size() as usize,
1172 size_of::<Composite1>(),
1173 );
1174 assert_eq!(Composite1::LAYOUT.element_size(), None);
1175 assert_eq!(
1176 Composite1::LAYOUT.alignment().as_devicesize() as usize,
1177 align_of::<Composite1>(),
1178 );
1179
1180 #[derive(BufferContents)]
1181 #[repr(C)]
1182 struct Test2(u64, u8, [u32]);
1183
1184 assert_eq!(
1185 Test2::LAYOUT.head_size() as usize,
1186 size_of::<u64>() + size_of::<u32>(),
1187 );
1188 assert_eq!(
1189 Test2::LAYOUT.element_size().unwrap() as usize,
1190 size_of::<u32>(),
1191 );
1192 assert_eq!(
1193 Test2::LAYOUT.alignment().as_devicesize() as usize,
1194 align_of::<u64>(),
1195 );
1196
1197 #[derive(BufferContents)]
1198 #[repr(C)]
1199 struct Composite2(Test1, [f32; 10], Test2);
1200
1201 assert_eq!(
1202 Composite2::LAYOUT.head_size() as usize,
1203 size_of::<Test1>() + size_of::<[f32; 10]>() + size_of::<u64>() + size_of::<u32>(),
1204 );
1205 assert_eq!(
1206 Composite2::LAYOUT.element_size().unwrap() as usize,
1207 size_of::<u32>(),
1208 );
1209 assert_eq!(
1210 Composite2::LAYOUT.alignment().as_devicesize() as usize,
1211 align_of::<u64>(),
1212 );
1213 }
1214
1215 #[test]
1216 fn split_at() {
1217 let (device, _) = gfx_dev_and_queue!();
1218 let allocator = StandardMemoryAllocator::new_default(device);
1219
1220 let buffer = Buffer::new_slice::<u32>(
1221 &allocator,
1222 BufferCreateInfo {
1223 usage: BufferUsage::TRANSFER_SRC,
1224 ..Default::default()
1225 },
1226 AllocationCreateInfo {
1227 usage: MemoryUsage::Upload,
1228 ..Default::default()
1229 },
1230 6,
1231 )
1232 .unwrap();
1233
1234 {
1235 let (left, right) = buffer.clone().split_at(2);
1236 assert!(left.len() == 2);
1237 assert!(right.len() == 4);
1238 }
1239
1240 {
1241 let (left, right) = buffer.clone().split_at(5);
1242 assert!(left.len() == 5);
1243 assert!(right.len() == 1);
1244 }
1245
1246 {
1247 assert_should_panic!({ buffer.clone().split_at(0) });
1248 }
1249
1250 {
1251 assert_should_panic!({ buffer.split_at(6) });
1252 }
1253 }
1254
1255 #[test]
1256 fn cast_aligned() {
1257 let (device, _) = gfx_dev_and_queue!();
1258 let allocator = StandardMemoryAllocator::new_default(device.clone());
1259
1260 let raw_buffer = RawBuffer::new(
1261 device,
1262 BufferCreateInfo {
1263 size: 32,
1264 usage: BufferUsage::TRANSFER_SRC,
1265 ..Default::default()
1266 },
1267 )
1268 .unwrap();
1269
1270 let requirements = MemoryRequirements {
1271 layout: DeviceLayout::from_size_alignment(32, 1).unwrap(),
1272 memory_type_bits: 1,
1273 prefers_dedicated_allocation: false,
1274 requires_dedicated_allocation: false,
1275 };
1276
1277 // Allocate some junk in the same block as the buffer.
1278 let _junk = allocator
1279 .allocate(
1280 MemoryRequirements {
1281 layout: DeviceLayout::from_size_alignment(17, 1).unwrap(),
1282 ..requirements
1283 },
1284 AllocationType::Linear,
1285 AllocationCreateInfo::default(),
1286 None,
1287 )
1288 .unwrap();
1289
1290 let allocation = allocator
1291 .allocate(
1292 requirements,
1293 AllocationType::Linear,
1294 AllocationCreateInfo::default(),
1295 None,
1296 )
1297 .unwrap();
1298
1299 let buffer = Buffer::from_raw(raw_buffer, BufferMemory::Normal(allocation));
1300 let buffer = Subbuffer::from(Arc::new(buffer));
1301
1302 assert!(buffer.memory_offset() >= 17);
1303
1304 {
1305 #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
1306 #[repr(C, align(16))]
1307 struct Test([u8; 16]);
1308
1309 let aligned = buffer.clone().cast_aligned::<Test>();
1310 assert_eq!(aligned.memory_offset() % 16, 0);
1311 assert_eq!(aligned.size(), 16);
1312 }
1313
1314 {
1315 let aligned = buffer.clone().cast_aligned::<[u8; 16]>();
1316 assert_eq!(aligned.size() % 16, 0);
1317 }
1318
1319 {
1320 let layout = DeviceLayout::from_size_alignment(32, 16).unwrap();
1321 let aligned = buffer.clone().align_to(layout);
1322 assert!(is_aligned(aligned.memory_offset(), layout.alignment()));
1323 assert_eq!(aligned.size(), 0);
1324 }
1325
1326 {
1327 let layout = DeviceLayout::from_size_alignment(1, 64).unwrap();
1328 assert_should_panic!({ buffer.align_to(layout) });
1329 }
1330 }
1331}