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

source

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.

source

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.

source

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.

source

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.

source

pub const fn empty() -> Self

Returns a MemoryTypeFilter with none of the flags set.

source

pub const fn union(self, other: Self) -> Self

Returns the union of self and other.

Trait Implementations§

source§

impl BitOr for MemoryTypeFilter

§

type Output = MemoryTypeFilter

The resulting type after applying the | operator.
source§

fn bitor(self, rhs: Self) -> Self::Output

Performs the | operation. Read more
source§

impl Clone for MemoryTypeFilter

source§

fn clone(&self) -> MemoryTypeFilter

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for MemoryTypeFilter

source§

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

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

impl Default for MemoryTypeFilter

source§

fn default() -> MemoryTypeFilter

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

impl PartialEq for MemoryTypeFilter

source§

fn eq(&self, other: &MemoryTypeFilter) -> bool

This method tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
source§

impl Copy for MemoryTypeFilter

source§

impl Eq for MemoryTypeFilter

source§

impl StructuralEq for MemoryTypeFilter

source§

impl StructuralPartialEq for MemoryTypeFilter

Auto Trait Implementations§

Blanket Implementations§

source§

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

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

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

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere 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 Twhere 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> ToOwned for Twhere T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere 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 Twhere 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.