Skip to main content

vk/mem/
allocation.rs

1//! Checked separate or pooled allocation identity and bounded cache ranges.
2
3use super::{AllocError, host::Identity};
4use crate as vk;
5
6/// One live native region. No implicit free, unmap, submission or wait in Drop.
7pub struct Allocation {
8	pub(crate) handle: vk::DeviceMemory,
9	pub(crate) size: u64,
10	pub(crate) offset: u64,
11	pub(crate) block_size: u64,
12	pub(crate) reserved_size: u64,
13	pub(crate) memory_type_index: u32,
14	pub(crate) heap_index: u32,
15	pub(crate) flags: vk::MemoryPropertyFlags,
16	pub(crate) atom: u64,
17	pub(super) owner: Identity,
18	pub(crate) serial: u64,
19}
20
21impl Allocation {
22	pub fn handle(&self) -> vk::DeviceMemory {
23		self.handle
24	}
25
26	pub fn offset(&self) -> u64 {
27		self.offset
28	}
29
30	pub fn size(&self) -> u64 {
31		self.size
32	}
33
34	/// Native extent reserved to this region, including non-coherent atom padding.
35	pub fn reserved_size(&self) -> u64 {
36		self.reserved_size
37	}
38
39	pub fn memory_type_index(&self) -> u32 {
40		self.memory_type_index
41	}
42
43	pub fn heap_index(&self) -> u32 {
44		self.heap_index
45	}
46
47	pub fn is_host_coherent(&self) -> bool {
48		self.flags.as_raw() & vk::MemoryPropertyFlagBits::HOST_COHERENT_BIT.as_raw() != 0
49	}
50
51	pub fn is_host_visible(&self) -> bool {
52		self.flags.as_raw() & vk::MemoryPropertyFlagBits::HOST_VISIBLE_BIT.as_raw() != 0
53	}
54
55	pub fn is_freed(&self) -> bool {
56		self.handle.is_null()
57	}
58
59	/// Translate an allocation-relative range to a native block range.
60	/// This does not validate allocator liveness or establish a mapping.
61	///
62	/// Donor: VMA `VmaAllocator_T::GetFlushOrInvalidateRange`.
63	/// Adaptation: checked arithmetic, no-op ranges, allocation-end exception.
64	pub fn mapped_range(
65		&self,
66		offset: u64,
67		size: u64,
68	) -> Result<Option<vk::MappedMemoryRange<'static>>, AllocError> {
69		if self.is_freed() {
70			return Err(AllocError::UnknownAllocation);
71		}
72		if !self.is_host_visible() {
73			return Err(AllocError::NotHostVisible);
74		}
75		let end = if size == vk::WHOLE_SIZE {
76			self.size
77		} else {
78			offset.checked_add(size).ok_or(AllocError::InvalidRange)?
79		};
80		if offset > self.size || end > self.size || end < offset {
81			return Err(AllocError::InvalidRange);
82		}
83		if offset == end || self.is_host_coherent() {
84			return Ok(None);
85		}
86		let absolute_end = self
87			.offset
88			.checked_add(end)
89			.ok_or(AllocError::InvalidRange)?;
90		let start = self
91			.offset
92			.checked_add(offset)
93			.ok_or(AllocError::InvalidRange)?
94			& !(self.atom - 1);
95		let rounded = absolute_end
96			.checked_add(self.atom - 1)
97			.map(|v| v & !(self.atom - 1))
98			.unwrap_or(self.block_size)
99			.min(self.block_size);
100		Ok(Some(vk::MappedMemoryRange {
101			memory: self.handle,
102			offset: start,
103			size: rounded - start,
104			..Default::default()
105		}))
106	}
107}
108
109#[cfg(test)]
110#[path = "../../../test/rs/mem/test_allocation.rs"]
111mod tests;