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 Allocation
s 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
impl Allocation
sourcepub fn try_as_mapped_slab<'a>(&'a mut self) -> Option<MappedAllocationSlab<'a>>
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
pub fn chunk_id(&self) -> Option<NonZeroU64>
sourcepub fn memory_properties(&self) -> MemoryPropertyFlags
pub fn memory_properties(&self) -> MemoryPropertyFlags
Returns the vk::MemoryPropertyFlags
of this allocation.
sourcepub unsafe fn memory(&self) -> DeviceMemory
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.
sourcepub fn is_dedicated(&self) -> bool
pub fn is_dedicated(&self) -> bool
Returns true
if this allocation is using a dedicated underlying allocation.
sourcepub fn offset(&self) -> u64
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.
sourcepub fn mapped_ptr(&self) -> Option<NonNull<c_void>>
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.
sourcepub fn mapped_slice(&self) -> Option<&[u8]>
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.
sourcepub fn mapped_slice_mut(&mut self) -> Option<&mut [u8]>
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.
pub fn is_null(&self) -> bool
Trait Implementations§
source§impl Debug for Allocation
impl Debug for Allocation
source§impl Default for Allocation
impl Default for Allocation
source§impl Slab for Allocation
impl Slab for Allocation
source§fn base_ptr(&self) -> *const u8
fn base_ptr(&self) -> *const u8
self
.source§fn base_ptr_mut(&mut self) -> *mut u8
fn base_ptr_mut(&mut self) -> *mut u8
self
.source§fn as_maybe_uninit_bytes(&self) -> &[MaybeUninit<u8>]
fn as_maybe_uninit_bytes(&self) -> &[MaybeUninit<u8>]
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>]
fn as_maybe_uninit_bytes_mut(&mut self) -> &mut [MaybeUninit<u8>]
self
as a mutable slice of MaybeUninit<u8>
.source§unsafe fn assume_initialized_as_bytes(&self) -> &[u8] ⓘ
unsafe fn assume_initialized_as_bytes(&self) -> &[u8] ⓘ
source§unsafe fn assume_initialized_as_bytes_mut(&mut self) -> &mut [u8] ⓘ
unsafe fn assume_initialized_as_bytes_mut(&mut self) -> &mut [u8] ⓘ
self
as a mutable byte slice. This assumes that all bytes
in self
are initialized. Read moresource§unsafe fn assume_range_initialized_as_bytes<R>(&self, range: R) -> &[u8] ⓘ
unsafe fn assume_range_initialized_as_bytes<R>(&self, range: R) -> &[u8] ⓘ
self
as a byte slice. This assumes that all bytes
within range
are initialized. Read more