Skip to main content

vk/mem/
selector.rs

1//! Checked, driver-independent memory-type selection.
2
3use super::{AllocError, AllocRequest};
4use crate as vk;
5
6/// Validated device memory properties and non-coherent atom size.
7pub struct MemoryTypeSelector {
8	pub(crate) properties: vk::PhysicalDeviceMemoryProperties,
9	pub(crate) atom: u64,
10	pub(super) device_coherent: bool,
11	pub(super) allowed_bits: u32,
12}
13
14impl MemoryTypeSelector {
15	pub fn new(
16		properties: vk::PhysicalDeviceMemoryProperties,
17		atom: u64,
18	) -> Result<Self, AllocError> {
19		if properties.memory_type_count == 0
20			|| properties.memory_type_count > 32
21			|| properties.memory_heap_count == 0
22			|| properties.memory_heap_count > 16
23		{
24			return Err(AllocError::InvalidProperties(
25				"invalid memory type or heap count",
26			));
27		}
28		if !atom.is_power_of_two() {
29			return Err(AllocError::InvalidProperties(
30				"atom size must be a nonzero power of two",
31			));
32		}
33		for memory in &properties.memory_types[..properties.memory_type_count as usize] {
34			if memory.heap_index >= properties.memory_heap_count {
35				return Err(AllocError::InvalidProperties(
36					"memory type references an absent heap",
37				));
38			}
39		}
40		Ok(Self {
41			properties,
42			atom,
43			device_coherent: false,
44			allowed_bits: u32::MAX,
45		})
46	}
47
48	/// Select a compatible type; preferred-bit ties retain the lower index.
49	///
50	/// Donor: VMA `VmaAllocator_T::FindMemoryTypeIndex`, required-bit test and
51	/// missing-preferred-bit cost. Adaptation: checked metadata, no hidden usage policy.
52	pub fn find_memory_type(&self, request: &AllocRequest) -> Result<u32, AllocError> {
53		self.find_memory_type_with_preferences(request, vk::MemoryPropertyFlags::empty())
54	}
55
56	/// Required flags are mandatory; cost counts missing preferred and present avoided bits.
57	pub fn find_memory_type_with_preferences(
58		&self,
59		request: &AllocRequest,
60		avoided: vk::MemoryPropertyFlags,
61	) -> Result<u32, AllocError> {
62		if request.size == 0 || !request.alignment.is_power_of_two() {
63			return Err(AllocError::InvalidRequest(
64				"size must be nonzero and alignment a power of two",
65			));
66		}
67		let mut best = None;
68		let mut cost = u32::MAX;
69		// Visit only admitted bits, still in increasing index order for ties.
70		let mut bits = request.memory_type_bits
71			& self.allowed_bits
72			& (u32::MAX >> (32 - self.properties.memory_type_count));
73		while bits != 0 {
74			let i = bits.trailing_zeros();
75			bits &= bits - 1;
76			let memory = self.properties.memory_types[i as usize];
77			let flags = memory.property_flags.as_raw();
78			let required = request.required_flags.as_raw();
79			if flags & required != required {
80				continue;
81			}
82			// Tile heaps have a separate binding/size contract; ordinary storage
83			// must not acquire them merely because their property flags match.
84			if self.properties.memory_heaps[memory.heap_index as usize]
85				.flags
86				.as_raw()
87				& vk::MemoryHeapFlagBits::TILE_MEMORY_BIT_QCOM.as_raw()
88				!= 0
89			{
90				continue;
91			}
92			// AMD device-coherent types require captured enabled feature admission.
93			// A standalone selector is conservative. Protected types require explicit flags.
94			if !self.device_coherent
95				&& flags & vk::MemoryPropertyFlagBits::DEVICE_COHERENT_BIT_AMD.as_raw() != 0
96			{
97				continue;
98			}
99			if flags & vk::MemoryPropertyFlagBits::PROTECTED_BIT.as_raw() != 0
100				&& required & vk::MemoryPropertyFlagBits::PROTECTED_BIT.as_raw() == 0
101			{
102				continue;
103			}
104			if request.size > self.properties.memory_heaps[memory.heap_index as usize].size {
105				continue;
106			}
107			let candidate = (request.preferred_flags.as_raw() & !flags).count_ones()
108				+ (avoided.as_raw() & flags).count_ones();
109			if candidate < cost {
110				best = Some(i);
111				cost = candidate;
112				// Costs are nonnegative; the first perfect match also wins every tie.
113				if cost == 0 {
114					break;
115				}
116			}
117		}
118		best.ok_or(AllocError::NoCompatibleMemoryType)
119	}
120}