Improve clarity of MemoryTypeFilter docs (#2428)

* Make the combination disclaimers **impossible** to miss

* Add headings to the examples for improved visibility

* Update vulkano/src/memory/allocator/mod.rs

Co-authored-by: Rua <ruawhitepaw@gmail.com>

---------

Co-authored-by: Rua <ruawhitepaw@gmail.com>
This commit is contained in:
marc0246 2023-12-20 11:50:57 +01:00 committed by GitHub
parent 6afe4792df
commit 648f3ce715
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -367,6 +367,8 @@ impl Debug for dyn MemoryAllocator {
/// ///
/// # Examples /// # Examples
/// ///
/// #### Direct device access only
///
/// For resources that the device frequently accesses, e.g. textures, render targets, or /// For resources that the device frequently accesses, e.g. textures, render targets, or
/// intermediary buffers, you want device-local memory without any host access: /// intermediary buffers, you want device-local memory without any host access:
/// ///
@ -396,8 +398,10 @@ impl Debug for dyn MemoryAllocator {
/// .unwrap(); /// .unwrap();
/// ``` /// ```
/// ///
/// #### Sequential writes from host, indirect device access
///
/// For staging, the resource is only ever written to sequentially. Also, since the device will /// 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 /// only read the staging resource 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: /// memory, in fact it would be wasteful. Therefore, it's best to put it in host-local memory:
/// ///
/// ``` /// ```
@ -425,6 +429,8 @@ impl Debug for dyn MemoryAllocator {
/// # let staging_buffer: vulkano::buffer::Subbuffer<u32> = staging_buffer; /// # let staging_buffer: vulkano::buffer::Subbuffer<u32> = staging_buffer;
/// ``` /// ```
/// ///
/// #### Sequential writes from host, direct device access
///
/// For resources that the device accesses directly, aka a buffer/image used for anything other /// 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: /// than transfers, it's probably best to put it in device-local memory:
/// ///
@ -453,6 +459,8 @@ impl Debug for dyn MemoryAllocator {
/// # let uniform_buffer: vulkano::buffer::Subbuffer<u32> = uniform_buffer; /// # let uniform_buffer: vulkano::buffer::Subbuffer<u32> = uniform_buffer;
/// ``` /// ```
/// ///
/// #### Readback to host
///
/// For readback, e.g. getting the results of a compute shader back to the host: /// For readback, e.g. getting the results of a compute shader back to the host:
/// ///
/// ``` /// ```
@ -487,7 +495,9 @@ pub struct MemoryTypeFilter {
} }
impl MemoryTypeFilter { impl MemoryTypeFilter {
/// Prefers picking a memory type with the [`DEVICE_LOCAL`] flag. /// Prefers picking a memory type with the [`DEVICE_LOCAL`] flag. **It does not affect whether
/// the memory is host-visible**. You need to combine this with either the
/// [`HOST_SEQUENTIAL_WRITE`] or [`HOST_RANDOM_ACCESS`] filter for that.
/// ///
/// Memory being device-local means that it is fastest to access for the device. However, /// 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, /// for dedicated GPUs, getting memory in and out of VRAM requires to go through the PCIe bus,
@ -514,22 +524,22 @@ impl MemoryTypeFilter {
/// memory types that are not `HOST_VISIBLE`, which makes sense since it's all system RAM. In /// 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. /// 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, /// Don't use this together with [`PREFER_HOST`], that makes no sense.
/// make sure you combine this with either the [`HOST_SEQUENTIAL_WRITE`] or
/// [`HOST_RANDOM_ACCESS`] filter.
/// ///
/// [`DEVICE_LOCAL`]: MemoryPropertyFlags::DEVICE_LOCAL /// [`DEVICE_LOCAL`]: MemoryPropertyFlags::DEVICE_LOCAL
/// [`HOST_VISIBLE`]: MemoryPropertyFlags::HOST_VISIBLE
/// [`PREFER_HOST`]: Self::PREFER_HOST
/// [`HOST_SEQUENTIAL_WRITE`]: Self::HOST_SEQUENTIAL_WRITE /// [`HOST_SEQUENTIAL_WRITE`]: Self::HOST_SEQUENTIAL_WRITE
/// [`HOST_RANDOM_ACCESS`]: Self::HOST_RANDOM_ACCESS /// [`HOST_RANDOM_ACCESS`]: Self::HOST_RANDOM_ACCESS
/// [`HOST_VISIBLE`]: MemoryPropertyFlags::HOST_VISIBLE
/// [`PREFER_HOST`]: Self::PREFER_HOST
pub const PREFER_DEVICE: Self = Self { pub const PREFER_DEVICE: Self = Self {
required_flags: MemoryPropertyFlags::empty(), required_flags: MemoryPropertyFlags::empty(),
preferred_flags: MemoryPropertyFlags::DEVICE_LOCAL, preferred_flags: MemoryPropertyFlags::DEVICE_LOCAL,
not_preferred_flags: MemoryPropertyFlags::empty(), not_preferred_flags: MemoryPropertyFlags::empty(),
}; };
/// Prefers picking a memory type without the [`DEVICE_LOCAL`] flag. /// Prefers picking a memory type without the [`DEVICE_LOCAL`] flag. **It does not affect
/// whether the memory is host-visible**. You need to combine this with either the
/// [`HOST_SEQUENTIAL_WRITE`] or [`HOST_RANDOM_ACCESS`] filter for that.
/// ///
/// This option is best suited for resources that the host does access, but device doesn't /// 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. /// access directly, such as staging buffers and readback buffers.
@ -544,28 +554,29 @@ impl MemoryTypeFilter {
/// still prefer host-local memory if the memory is rarely used, such as for manually paging /// 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. /// 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 /// Don't use this together with [`PREFER_DEVICE`], that makes no sense.
/// access, make sure you combine this with either the [`HOST_SEQUENTIAL_WRITE`] or
/// [`HOST_RANDOM_ACCESS`] filter.
/// ///
/// [`DEVICE_LOCAL`]: MemoryPropertyFlags::DEVICE_LOCAL /// [`DEVICE_LOCAL`]: MemoryPropertyFlags::DEVICE_LOCAL
/// [`PREFER_DEVICE`]: Self::PREFER_DEVICE
/// [`HOST_SEQUENTIAL_WRITE`]: Self::HOST_SEQUENTIAL_WRITE /// [`HOST_SEQUENTIAL_WRITE`]: Self::HOST_SEQUENTIAL_WRITE
/// [`HOST_RANDOM_ACCESS`]: Self::HOST_RANDOM_ACCESS /// [`HOST_RANDOM_ACCESS`]: Self::HOST_RANDOM_ACCESS
/// [`PREFER_DEVICE`]: Self::PREFER_DEVICE
pub const PREFER_HOST: Self = Self { pub const PREFER_HOST: Self = Self {
required_flags: MemoryPropertyFlags::empty(), required_flags: MemoryPropertyFlags::empty(),
preferred_flags: MemoryPropertyFlags::empty(), preferred_flags: MemoryPropertyFlags::empty(),
not_preferred_flags: MemoryPropertyFlags::DEVICE_LOCAL, not_preferred_flags: MemoryPropertyFlags::DEVICE_LOCAL,
}; };
/// This guarantees picking a memory type that has the [`HOST_VISIBLE`] flag. Using this filter /// This guarantees picking a memory type that has the [`HOST_VISIBLE`] flag. **It does not
/// allows the allocator to pick a memory type that is uncached and write-combined, which is /// affect whether the memory is device-local or host-local**. You need to combine this with
/// ideal for sequential writes. However, this optimization might lead to poor performance for /// either the [`PREFER_DEVICE`] or [`PREFER_HOST`] filter for that.
/// 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 /// Using this filter allows the allocator to pick a memory type that is uncached and
/// equivalently using [`slice::copy_from_slice`]. Copying sized data also counts, as rustc /// write-combined, which is ideal for sequential writes. However, this optimization might lead
/// should write the memory locations in order. If you have a struct, make sure you write it /// to poor performance for anything else. What counts as a sequential write is any kind of
/// member-by-member. /// 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 /// 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. /// only write to from the host, like a uniform or vertex buffer.
@ -576,7 +587,8 @@ impl MemoryTypeFilter {
/// to get the most performance out, if that's possible. /// to get the most performance out, if that's possible.
/// ///
/// [`HOST_VISIBLE`]: MemoryPropertyFlags::HOST_VISIBLE /// [`HOST_VISIBLE`]: MemoryPropertyFlags::HOST_VISIBLE
/// [`HOST_COHERENT`]: MemoryPropertyFlags::HOST_COHERENT /// [`PREFER_DEVICE`]: Self::PREFER_DEVICE
/// [`PREFER_HOST`]: Self::PREFER_HOST
/// [`HOST_RANDOM_ACCESS`]: Self::HOST_RANDOM_ACCESS /// [`HOST_RANDOM_ACCESS`]: Self::HOST_RANDOM_ACCESS
pub const HOST_SEQUENTIAL_WRITE: Self = Self { pub const HOST_SEQUENTIAL_WRITE: Self = Self {
required_flags: MemoryPropertyFlags::HOST_VISIBLE, required_flags: MemoryPropertyFlags::HOST_VISIBLE,
@ -585,7 +597,9 @@ impl MemoryTypeFilter {
}; };
/// This guarantees picking a memory type that has the [`HOST_VISIBLE`] and [`HOST_CACHED`] /// 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. /// flags, which is best suited for readback and/or random access. **It does not affect whether
/// the memory is device-local or host-local**. You need to combine this with either the
/// [`PREFER_DEVICE`] or [`PREFER_HOST`] filter for that.
/// ///
/// Example use cases include using the device for things other than rendering and getting the /// 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 /// results back to the host. That might be compute shading, or image or video manipulation, or
@ -597,6 +611,8 @@ impl MemoryTypeFilter {
/// ///
/// [`HOST_VISIBLE`]: MemoryPropertyFlags::HOST_VISIBLE /// [`HOST_VISIBLE`]: MemoryPropertyFlags::HOST_VISIBLE
/// [`HOST_CACHED`]: MemoryPropertyFlags::HOST_CACHED /// [`HOST_CACHED`]: MemoryPropertyFlags::HOST_CACHED
/// [`PREFER_DEVICE`]: Self::PREFER_DEVICE
/// [`PREFER_HOST`]: Self::PREFER_HOST
/// [`HOST_SEQUENTIAL_WRITE`]: Self::HOST_SEQUENTIAL_WRITE /// [`HOST_SEQUENTIAL_WRITE`]: Self::HOST_SEQUENTIAL_WRITE
pub const HOST_RANDOM_ACCESS: Self = Self { pub const HOST_RANDOM_ACCESS: Self = Self {
required_flags: MemoryPropertyFlags::HOST_VISIBLE.union(MemoryPropertyFlags::HOST_CACHED), required_flags: MemoryPropertyFlags::HOST_VISIBLE.union(MemoryPropertyFlags::HOST_CACHED),