Skip to main content

vk/memory/
virtual_block.rs

1//! Host-only region placement over an integer byte range.
2//!
3//! No Vulkan memory, buffers, mapping, synchronization or implicit retirement is
4//! provided. Consumers own the validity/completion of any resource using these
5//! offsets. Tokens are explicit: dropping one does not release its region.
6//!
7//! ```
8//! use vk::memory::{PlacementStrategy, VirtualBlock};
9//! let mut block = VirtualBlock::new(4096)?;
10//! let mut region = block.allocate(64, 16, PlacementStrategy::Default)?;
11//! assert_eq!(block.allocation_info(&region)?.offset, 0);
12//! block.free(&mut region)?;
13//! # Ok::<(), vk::AllocError>(())
14//! ```
15
16use super::{AllocError, tlsf::Tlsf};
17use std::sync::Arc;
18
19/// VMA virtual-TLSF search order; these policies are not latency guarantees.
20#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
21pub enum PlacementStrategy {
22	#[default]
23	Default,
24	MinMemory,
25	MinTime,
26	MinOffset,
27}
28
29/// Generation-checked identity in exactly one virtual block; no implicit free.
30pub struct VirtualAllocation {
31	owner: Arc<()>,
32	index: usize,
33	serial: u64,
34}
35
36/// Placement snapshot; valid only while the matching allocation remains live.
37#[derive(Clone, Copy, Debug, PartialEq, Eq)]
38pub struct VirtualAllocationInfo {
39	pub offset: u64,
40	pub size: u64,
41}
42
43/// Host metadata statistics. Alignment holes remain free, not reserved to a token.
44#[derive(Clone, Copy, Debug, PartialEq, Eq)]
45pub struct VirtualBlockStats {
46	pub size: u64,
47	pub allocation_count: usize,
48	pub allocated_bytes: u64,
49	pub free_bytes: u64,
50	pub free_region_count: usize,
51	pub largest_free_region: u64,
52}
53
54/// Virtual TLSF allocator; owns host metadata and no native objects.
55///
56/// Mutations require exclusive access. Drop releases Rust metadata only.
57/// This is not a Vulkan memory block or a pool with GPU lifetime tracking.
58pub struct VirtualBlock {
59	owner: Arc<()>,
60	metadata: Tlsf,
61}
62
63impl VirtualBlock {
64	pub fn new(size: u64) -> Result<Self, AllocError> {
65		Ok(Self {
66			owner: Arc::new(()),
67			metadata: Tlsf::new(size)?,
68		})
69	}
70
71	/// Allocate an aligned region using the chosen donor search order.
72	pub fn allocate(
73		&mut self,
74		size: u64,
75		alignment: u64,
76		strategy: PlacementStrategy,
77	) -> Result<VirtualAllocation, AllocError> {
78		allocate(self, size, alignment, strategy)
79	}
80
81	/// Release a matching live identity. No native resource is destroyed or waited on.
82	pub fn free(&mut self, allocation: &mut VirtualAllocation) -> Result<(), AllocError> {
83		free(self, allocation)
84	}
85
86	/// Validate identity/generation and return the current offset/size snapshot.
87	pub fn allocation_info(
88		&self,
89		allocation: &VirtualAllocation,
90	) -> Result<VirtualAllocationInfo, AllocError> {
91		allocation_info(self, allocation)
92	}
93
94	/// Invalidate all tokens and restore the full range, retaining host capacity.
95	/// Users of previously returned offsets must stop using them before reuse.
96	pub fn clear(&mut self) {
97		clear(self)
98	}
99
100	/// Calculate detailed free-span statistics; this is not a constant-time hot path.
101	pub fn statistics(&self) -> VirtualBlockStats {
102		statistics(self)
103	}
104}
105
106/// Allocate a nonzero region with a nonzero power-of-two alignment.
107///
108/// Failure leaves placements and donor free-list ordering unchanged. Only host
109/// metadata is allocated; no Vulkan calls or memory mapping occur.
110pub fn allocate(
111	block: &mut VirtualBlock,
112	size: u64,
113	alignment: u64,
114	strategy: PlacementStrategy,
115) -> Result<VirtualAllocation, AllocError> {
116	let (index, serial) = block.metadata.allocate(size, alignment, strategy)?;
117	Ok(VirtualAllocation {
118		owner: block.owner.clone(),
119		index,
120		serial,
121	})
122}
123
124/// Reject foreign, freed or stale tokens before reading placement metadata.
125pub fn allocation_info(
126	block: &VirtualBlock,
127	allocation: &VirtualAllocation,
128) -> Result<VirtualAllocationInfo, AllocError> {
129	if !Arc::ptr_eq(&block.owner, &allocation.owner) {
130		return Err(AllocError::WrongAllocator);
131	}
132	block.metadata.info(allocation.index, allocation.serial)
133}
134
135/// Free a checked live region and invalidate its token; no native effects.
136pub fn free(
137	block: &mut VirtualBlock,
138	allocation: &mut VirtualAllocation,
139) -> Result<(), AllocError> {
140	allocation_info(block, allocation)?;
141	block.metadata.free(allocation.index, allocation.serial)?;
142	allocation.index = usize::MAX;
143	Ok(())
144}
145
146/// Clear all regions, invalidating every prior generation; never submits or waits.
147pub fn clear(block: &mut VirtualBlock) {
148	block.metadata.clear()
149}
150
151/// Calculate allocation bytes, free bytes, free-region count and largest span.
152pub fn statistics(block: &VirtualBlock) -> VirtualBlockStats {
153	block.metadata.statistics()
154}