Skip to main content

vk/memory/
selector.rs

1//! Checked, driver-independent memory-type selection.
2
3use super::{AllocError, AllocRequest};
4use crate::core 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}
11
12impl MemoryTypeSelector {
13	pub fn new(
14		properties: vk::PhysicalDeviceMemoryProperties,
15		atom: u64,
16	) -> Result<Self, AllocError> {
17		if properties.memory_type_count == 0
18			|| properties.memory_type_count > 32
19			|| properties.memory_heap_count == 0
20			|| properties.memory_heap_count > 16
21		{
22			return Err(AllocError::InvalidProperties(
23				"invalid memory type or heap count",
24			));
25		}
26		if !atom.is_power_of_two() {
27			return Err(AllocError::InvalidProperties(
28				"atom size must be a nonzero power of two",
29			));
30		}
31		for memory in &properties.memory_types[..properties.memory_type_count as usize] {
32			if memory.heap_index >= properties.memory_heap_count {
33				return Err(AllocError::InvalidProperties(
34					"memory type references an absent heap",
35				));
36			}
37		}
38		Ok(Self { properties, atom })
39	}
40
41	/// Select a compatible type; preferred-bit ties retain the lower index.
42	///
43	/// Donor: VMA `VmaAllocator_T::FindMemoryTypeIndex`, required-bit test and
44	/// missing-preferred-bit cost. Adaptation: checked metadata, no hidden usage policy.
45	pub fn find_memory_type(&self, request: &AllocRequest) -> Result<u32, AllocError> {
46		if request.size == 0 || !request.alignment.is_power_of_two() {
47			return Err(AllocError::InvalidRequest(
48				"size must be nonzero and alignment a power of two",
49			));
50		}
51		let mut best = None;
52		let mut cost = u32::MAX;
53		for i in 0..self.properties.memory_type_count {
54			if request.memory_type_bits & (1u32 << i) == 0 {
55				continue;
56			}
57			let memory = self.properties.memory_types[i as usize];
58			let flags = memory.property_flags.as_raw();
59			let required = request.required_flags.as_raw();
60			if flags & required != required {
61				continue;
62			}
63			// Tile heaps have a separate binding/size contract; ordinary storage
64			// must not acquire them merely because their property flags match.
65			if self.properties.memory_heaps[memory.heap_index as usize]
66				.flags
67				.as_raw()
68				& vk::MemoryHeapFlagBits::TILE_MEMORY_BIT_QCOM.as_raw()
69				!= 0
70			{
71				continue;
72			}
73			// AMD device-coherent types require enabled device features this baseline
74			// allocator does not negotiate. Protected types must be explicitly requested.
75			if flags & vk::MemoryPropertyFlagBits::DEVICE_COHERENT_BIT_AMD.as_raw() != 0 {
76				continue;
77			}
78			if flags & vk::MemoryPropertyFlagBits::PROTECTED_BIT.as_raw() != 0
79				&& required & vk::MemoryPropertyFlagBits::PROTECTED_BIT.as_raw() == 0
80			{
81				continue;
82			}
83			if request.size > self.properties.memory_heaps[memory.heap_index as usize].size {
84				continue;
85			}
86			let candidate = (request.preferred_flags.as_raw() & !flags).count_ones();
87			if candidate < cost {
88				best = Some(i);
89				cost = candidate;
90			}
91		}
92		best.ok_or(AllocError::NoCompatibleMemoryType)
93	}
94}