Struct vulkano::memory::allocator::MemoryTypeFilter
source · pub struct MemoryTypeFilter {
pub required_flags: MemoryPropertyFlags,
pub preferred_flags: MemoryPropertyFlags,
pub not_preferred_flags: MemoryPropertyFlags,
}
Expand description
Describes what memory property flags are required, preferred and not preferred when picking a memory type index.
Examples
For resources that the device frequently accesses, e.g. textures, render targets, or intermediary buffers, you want device-local memory without any host access:
let texture = Image::new(
memory_allocator.clone(),
ImageCreateInfo {
format,
extent,
usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED,
..Default::default()
},
AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE,
..Default::default()
},
)
.unwrap();
For staging, the resource is only ever written to sequentially. Also, since the device will only read the staging resourse once, it would yield no benefit to place it in device-local memory, in fact it would be wasteful. Therefore, it’s best to put it in host-local memory:
let staging_buffer = Buffer::new_sized(
memory_allocator.clone(),
BufferCreateInfo {
usage: BufferUsage::TRANSFER_SRC,
..Default::default()
},
AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter::PREFER_HOST |
MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default()
},
)
.unwrap();
For resources that the device accesses directly, aka a buffer/image used for anything other than transfers, it’s probably best to put it in device-local memory:
let uniform_buffer = Buffer::new_sized(
memory_allocator.clone(),
BufferCreateInfo {
usage: BufferUsage::UNIFORM_BUFFER,
..Default::default()
},
AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE |
MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default()
},
)
.unwrap();
For readback, e.g. getting the results of a compute shader back to the host:
let readback_buffer = Buffer::new_sized(
memory_allocator.clone(),
BufferCreateInfo {
usage: BufferUsage::TRANSFER_DST,
..Default::default()
},
AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter::PREFER_HOST |
MemoryTypeFilter::HOST_RANDOM_ACCESS,
..Default::default()
},
)
.unwrap();
Fields§
§required_flags: MemoryPropertyFlags
§preferred_flags: MemoryPropertyFlags
§not_preferred_flags: MemoryPropertyFlags
Implementations§
source§impl MemoryTypeFilter
impl MemoryTypeFilter
sourcepub const PREFER_DEVICE: Self = _
pub const PREFER_DEVICE: Self = _
Prefers picking a memory type with the DEVICE_LOCAL
flag.
Memory being device-local means that it is fastest to access for the device. However, for dedicated GPUs, getting memory in and out of VRAM requires to go through the PCIe bus, which is very slow and should therefore only be done when necessary.
This filter is best suited for anything that the host doesn’t access, but the device accesses frequently. For example textures, render targets, and intermediary buffers.
For memory that the host does access but less frequently than the device, such as updating
a uniform buffer each frame, device-local memory may also be preferred. In this case,
because the memory is only written once before being consumed by the device and becoming
outdated, it doesn’t matter that the data is potentially transferred over the PCIe bus
since it only happens once. Since this is only a preference, if you have some requirements
such as the memory being HOST_VISIBLE
, those requirements will take precendence.
For memory that the host doesn’t access, and the device doesn’t access directly, you may still prefer device-local memory if the memory is used regularly. For instance, an image that is swapped each frame.
Keep in mind that for implementations with unified memory, there’s only system RAM. That
means that even if the implementation reports a memory type that is not HOST_VISIBLE
and
is DEVICE_LOCAL
, its still the same unified memory as all other memory. However, it may
still be faster to access. On the other hand, some such implementations don’t report any
memory types that are not HOST_VISIBLE
, which makes sense since it’s all system RAM. In
that case you wouldn’t need to do staging.
Don’t use this together with PREFER_HOST
, that makes no sense. If you need host access,
make sure you combine this with either the HOST_SEQUENTIAL_WRITE
or
HOST_RANDOM_ACCESS
filter.
sourcepub const PREFER_HOST: Self = _
pub const PREFER_HOST: Self = _
Prefers picking a memory type without the DEVICE_LOCAL
flag.
This option is best suited for resources that the host does access, but device doesn’t access directly, such as staging buffers and readback buffers.
For memory that the host does access but less frequently than the device, such as updating a uniform buffer each frame, you may still get away with using host-local memory if the updates are small and local enough. In such cases, the memory should be able to be quickly cached by the device, such that the data potentially being transferred over the PCIe bus wouldn’t matter.
For memory that the host doesn’t access, and the device doesn’t access directly, you may still prefer host-local memory if the memory is rarely used, such as for manually paging parts of device-local memory out in order to free up space on the device.
Don’t use this together with PREFER_DEVICE
, that makes no sense. If you need host
access, make sure you combine this with either the HOST_SEQUENTIAL_WRITE
or
HOST_RANDOM_ACCESS
filter.
sourcepub const HOST_SEQUENTIAL_WRITE: Self = _
pub const HOST_SEQUENTIAL_WRITE: Self = _
This guarantees picking a memory type that has the HOST_VISIBLE
flag. Using this filter
allows the allocator to pick a memory type that is uncached and write-combined, which is
ideal for sequential writes. However, this optimization might lead to poor performance for
anything else. What counts as a sequential write is any kind of loop that writes memory
locations in order, such as iterating over a slice while writing each element in order, or
equivalently using slice::copy_from_slice
. Copying sized data also counts, as rustc
should write the memory locations in order. If you have a struct, make sure you write it
member-by-member.
Example use cases include staging buffers, as well as any other kind of buffer that you only write to from the host, like a uniform or vertex buffer.
Don’t use this together with HOST_RANDOM_ACCESS
, that makes no sense. If you do both a
sequential write and read or random access, then you should use HOST_RANDOM_ACCESS
instead. However, you could also consider using different allocations for the two purposes
to get the most performance out, if that’s possible.
sourcepub const HOST_RANDOM_ACCESS: Self = _
pub const HOST_RANDOM_ACCESS: Self = _
This guarantees picking a memory type that has the HOST_VISIBLE
and HOST_CACHED
flags, which is best suited for readback and/or random access.
Example use cases include using the device for things other than rendering and getting the results back to the host. That might be compute shading, or image or video manipulation, or screenshotting.
Don’t use this together with HOST_SEQUENTIAL_WRITE
, that makes no sense. If you are
sure you only ever need to sequentially write to the allocation, then using
HOST_SEQUENTIAL_WRITE
instead will yield better performance.
Trait Implementations§
source§impl BitOr for MemoryTypeFilter
impl BitOr for MemoryTypeFilter
source§impl Clone for MemoryTypeFilter
impl Clone for MemoryTypeFilter
source§fn clone(&self) -> MemoryTypeFilter
fn clone(&self) -> MemoryTypeFilter
1.0.0 · source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source
. Read moresource§impl Debug for MemoryTypeFilter
impl Debug for MemoryTypeFilter
source§impl Default for MemoryTypeFilter
impl Default for MemoryTypeFilter
source§fn default() -> MemoryTypeFilter
fn default() -> MemoryTypeFilter
source§impl PartialEq for MemoryTypeFilter
impl PartialEq for MemoryTypeFilter
source§fn eq(&self, other: &MemoryTypeFilter) -> bool
fn eq(&self, other: &MemoryTypeFilter) -> bool
self
and other
values to be equal, and is used
by ==
.