Struct gpu_allocator::vulkan::Allocation

source ·
pub struct Allocation { /* private fields */ }
Expand description

A piece of allocated memory.

Could be contained in its own individual underlying memory object or as a sub-region of a larger allocation.

§Copying data into a CPU-mapped Allocation

You’ll very likely want to copy data into CPU-mapped Allocations in order to send that data to the GPU. Doing this data transfer correctly without invoking undefined behavior can be quite fraught and non-obvious[1].

To help you do this correctly, Allocation implements presser::Slab, which means you can directly pass it in to many of presser’s helper functions (for example, copy_from_slice_to_offset).

In most cases, this will work perfectly. However, note that if you try to use an Allocation as a Slab and it is not valid to do so (if it is not CPU-mapped or if its size > isize::MAX), you will cause a panic. If you aren’t sure about these conditions, you may use Allocation::try_as_mapped_slab.

§Example

Say we’ve created an Allocation called my_allocation, which is CPU-mapped.

let mut my_allocation: Allocation = my_allocator.allocate(...)?;

And we want to fill it with some data in the form of a my_gpu_data: Vec<MyGpuVector>, defined as such:

// note that this is size(12) but align(16), thus we have 4 padding bytes.
// this would mean a `&[MyGpuVector]` is invalid to cast as a `&[u8]`, but
// we can still use `presser` to copy it directly in a valid manner.
#[repr(C, align(16))]
#[derive(Clone, Copy)]
struct MyGpuVertex {
    x: f32,
    y: f32,
    z: f32,
}

let my_gpu_data: Vec<MyGpuData> = make_vertex_data();

Depending on how the data we’re copying will be used, the Vulkan device may have a minimum alignment requirement for that data:

let min_gpu_align = my_vulkan_device_specifications.min_alignment_thing;

Finally, we can use presser::copy_from_slice_to_offset_with_align to perform the copy, simply passing &mut my_allocation since Allocation implements Slab.

let copy_record = presser::copy_from_slice_to_offset_with_align(
    &my_gpu_data[..], // a slice containing all elements of my_gpu_data
    &mut my_allocation, // our Allocation
    0, // start as close to the beginning of the allocation as possible
    min_gpu_align, // the minimum alignment we queried previously
)?;

It’s important to note that the data may not have actually been copied starting at the requested start_offset (0 in the example above) depending on the alignment of the underlying allocation as well as the alignment requirements of MyGpuVector and the min_gpu_align we passed in. Thus, we can query the copy_record for the actual starting offset:

let actual_data_start_offset = copy_record.copy_start_offset;

§Safety

It is technically not fully safe to use an Allocation as a presser::Slab because we can’t validate that the GPU is not using the data in the buffer while self is borrowed. However, trying to validate this statically is really hard and the community has basically decided that requiring unsafe for functions like this creates too much “unsafe-noise”, ultimately making it harder to debug more insidious unsafety that is unrelated to GPU-CPU sync issues.

So, as would always be the case, you must ensure the GPU is not using the data in self for the duration that you hold the returned MappedAllocationSlab.

Implementations§

source§

impl Allocation

source

pub fn try_as_mapped_slab<'a>(&'a mut self) -> Option<MappedAllocationSlab<'a>>

Tries to borrow the CPU-mapped memory that backs this allocation as a presser::Slab, which you can then use to safely copy data into the raw, potentially-uninitialized buffer. See the documentation of Allocation for an example of this.

Returns None if self.mapped_ptr() is None, or if self.size() is greater than isize::MAX because this could lead to undefined behavior.

Note that Allocation implements Slab natively, so you can actually pass this allocation as a Slab directly. However, if self is not actually a valid Slab (this function would return None as described above), then trying to use it as a Slab will panic.

§Safety

See the note about safety in the documentation of Allocation

source

pub fn chunk_id(&self) -> Option<NonZeroU64>

source

pub fn memory_properties(&self) -> MemoryPropertyFlags

Returns the vk::MemoryPropertyFlags of this allocation.

source

pub unsafe fn memory(&self) -> DeviceMemory

Returns the vk::DeviceMemory object that is backing this allocation. This memory object can be shared with multiple other allocations and shouldn’t be freed (or allocated from) without this library, because that will lead to undefined behavior.

§Safety

The result of this function can safely be used to pass into ash::Device::bind_buffer_memory(), ash::Device::bind_image_memory() etc. It is exposed for this reason. Keep in mind to also pass Self::offset() along to those.

source

pub fn is_dedicated(&self) -> bool

Returns true if this allocation is using a dedicated underlying allocation.

source

pub fn offset(&self) -> u64

Returns the offset of the allocation on the vk::DeviceMemory. When binding the memory to a buffer or image, this offset needs to be supplied as well.

source

pub fn size(&self) -> u64

Returns the size of the allocation

source

pub fn mapped_ptr(&self) -> Option<NonNull<c_void>>

Returns a valid mapped pointer if the memory is host visible, otherwise it will return None. The pointer already points to the exact memory region of the suballocation, so no offset needs to be applied.

source

pub fn mapped_slice(&self) -> Option<&[u8]>

Returns a valid mapped slice if the memory is host visible, otherwise it will return None. The slice already references the exact memory region of the allocation, so no offset needs to be applied.

source

pub fn mapped_slice_mut(&mut self) -> Option<&mut [u8]>

Returns a valid mapped mutable slice if the memory is host visible, otherwise it will return None. The slice already references the exact memory region of the allocation, so no offset needs to be applied.

source

pub fn is_null(&self) -> bool

Trait Implementations§

source§

impl Debug for Allocation

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for Allocation

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl Slab for Allocation

source§

fn base_ptr(&self) -> *const u8

Get a pointer to the beginning of the allocation represented by self.
source§

fn base_ptr_mut(&mut self) -> *mut u8

Get a pointer to the beginning of the allocation represented by self.
source§

fn size(&self) -> usize

Get the size of the allocation represented by self.
source§

fn as_maybe_uninit_bytes(&self) -> &[MaybeUninit<u8>]

Interpret a portion of self as a slice of MaybeUninit<u8>. This is likely not incredibly useful, you probably want to use Slab::as_maybe_uninit_bytes_mut
source§

fn as_maybe_uninit_bytes_mut(&mut self) -> &mut [MaybeUninit<u8>]

Interpret a portion of self as a mutable slice of MaybeUninit<u8>.
source§

unsafe fn assume_initialized_as_bytes(&self) -> &[u8]

Interpret self as a byte slice. This assumes that all bytes in self are initialized. Read more
source§

unsafe fn assume_initialized_as_bytes_mut(&mut self) -> &mut [u8]

Interpret self as a mutable byte slice. This assumes that all bytes in self are initialized. Read more
source§

unsafe fn assume_range_initialized_as_bytes<R>(&self, range: R) -> &[u8]
where R: SliceIndex<[MaybeUninit<u8>], Output = [MaybeUninit<u8>]>,

Interpret a range of self as a byte slice. This assumes that all bytes within range are initialized. Read more
source§

unsafe fn assume_range_initialized_as_bytes_mut<R>( &mut self, range: R, ) -> &mut [u8]
where R: SliceIndex<[MaybeUninit<u8>], Output = [MaybeUninit<u8>]>,

Interpret a range of self as a mutable byte slice. This assumes that all bytes within range are initialized. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.