Skip to main content

vk/mem/
policy.rs

1//! Resource-aware memory preferences, ported from pinned VMA 3.4.0
2//! `FindMemoryPreferences` and `VmaBufferImageUsage::ContainsDeviceAccess`.
3//! See license/vma/MIT.txt.
4//! Adaptation: typed exclusive host intent, explicit flags by default, no implicit staging.
5use super::{AllocError, AllocationCreateInfo};
6use crate as vk;
7
8/// Policy for managed resources. Raw allocation always uses explicit flags.
9#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
10pub enum MemoryUsage {
11	#[default]
12	Explicit,
13	Auto,
14	PreferDevice,
15	PreferHost,
16}
17
18/// CPU access intent used by automatic memory selection; never submits a transfer.
19#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
20pub enum HostAccess {
21	#[default]
22	None,
23	SequentialWrite,
24	Random,
25}
26
27// Donor: CalcAllocationParams grants mapping intent to non-auto usage.
28// This is a block-search preference, not permission or proof of visibility.
29pub(super) fn prefers_mapped(info: &AllocationCreateInfo) -> bool {
30	info.usage == MemoryUsage::Explicit || info.host_access != HostAccess::None || info.persistent_map
31}
32
33pub(super) fn preferences(
34	info: &AllocationCreateInfo,
35	device_access: bool,
36	integrated: Option<bool>,
37) -> Result<
38	(
39		vk::MemoryPropertyFlags,
40		vk::MemoryPropertyFlags,
41		vk::MemoryPropertyFlags,
42	),
43	AllocError,
44> {
45	use vk::MemoryPropertyFlagBits as Flag;
46	let mut required = info.required_flags;
47	let mut preferred = info.preferred_flags;
48	let mut avoided = info.avoided_flags;
49	if info.allow_transfer_instead
50		&& (info.usage == MemoryUsage::Explicit || info.host_access == HostAccess::None)
51	{
52		return Err(AllocError::InvalidRequest(
53			"transfer fallback needs automatic host intent",
54		));
55	}
56	if info.usage != MemoryUsage::Explicit {
57		let prefer_host = info.usage == MemoryUsage::PreferHost;
58		let prefer_device = info.usage == MemoryUsage::PreferDevice;
59		let discrete_transfer = info.allow_transfer_instead
60			&& !integrated.ok_or(AllocError::UnsupportedCapability(
61				"transfer fallback requires queried physical-device type",
62			))? && device_access
63			&& !prefer_host;
64		match info.host_access {
65			HostAccess::Random => {
66				preferred = preferred | Flag::HOST_CACHED_BIT;
67				if discrete_transfer {
68					preferred = preferred | Flag::DEVICE_LOCAL_BIT;
69				} else if info.allow_transfer_instead {
70					preferred = preferred | Flag::HOST_VISIBLE_BIT;
71				} else {
72					required = required | Flag::HOST_VISIBLE_BIT;
73				}
74			}
75			HostAccess::SequentialWrite => {
76				avoided = avoided | Flag::HOST_CACHED_BIT;
77				if discrete_transfer {
78					preferred = preferred | Flag::DEVICE_LOCAL_BIT | Flag::HOST_VISIBLE_BIT;
79				} else {
80					if info.allow_transfer_instead {
81						preferred = preferred | Flag::HOST_VISIBLE_BIT;
82					} else {
83						required = required | Flag::HOST_VISIBLE_BIT;
84					}
85					if (device_access && !prefer_host) || (!device_access && prefer_device) {
86						preferred = preferred | Flag::DEVICE_LOCAL_BIT;
87					} else {
88						avoided = avoided | Flag::DEVICE_LOCAL_BIT;
89					}
90				}
91			}
92			HostAccess::None => {
93				if prefer_host {
94					avoided = avoided | Flag::DEVICE_LOCAL_BIT;
95				} else {
96					preferred = preferred | Flag::DEVICE_LOCAL_BIT;
97				}
98			}
99		}
100	} else if info.host_access != HostAccess::None {
101		return Err(AllocError::InvalidRequest(
102			"host intent requires automatic memory usage",
103		));
104	}
105	if (required | preferred).as_raw()
106		& (Flag::DEVICE_COHERENT_BIT_AMD | Flag::DEVICE_UNCACHED_BIT_AMD).as_raw()
107		== 0
108	{
109		avoided = avoided | Flag::DEVICE_UNCACHED_BIT_AMD;
110	}
111	if info.persistent_map {
112		required = required | Flag::HOST_VISIBLE_BIT;
113	}
114	Ok((required, preferred, avoided))
115}
116
117#[cfg(test)]
118#[path = "../../../test/rs/mem/test_policy.rs"]
119mod tests;