1use super::{
17 AllocError, AllocationAlgorithm,
18 host::{HostMemory, HostVec, Identity},
19 metadata::{Metadata, Placement},
20};
21use crate as vk;
22
23#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
25pub enum PlacementStrategy {
26 #[default]
27 Default,
28 MinMemory,
29 MinTime,
30 MinOffset,
31}
32
33pub struct VirtualAllocation {
35 owner: Identity,
36 placement: Option<Placement>,
37}
38
39#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub struct VirtualAllocationInfo {
42 pub user_data: usize,
44 pub offset: u64,
45 pub size: u64,
46}
47
48impl VirtualAllocationInfo {
49 #[inline]
51 #[must_use]
52 pub fn set_user_data(mut self, value: usize) -> Self {
53 self.user_data = value;
54 self
55 }
56
57 #[inline]
59 #[must_use]
60 pub fn set_offset(mut self, value: u64) -> Self {
61 self.offset = value;
62 self
63 }
64
65 #[inline]
67 #[must_use]
68 pub fn set_size(mut self, value: u64) -> Self {
69 self.size = value;
70 self
71 }
72}
73
74#[derive(Clone, Copy, Debug, PartialEq, Eq)]
77pub struct VirtualBlockStats {
78 pub size: u64,
79 pub allocation_count: usize,
80 pub allocated_bytes: u64,
81 pub free_bytes: u64,
82 pub free_region_count: usize,
83 pub largest_free_region: u64,
84}
85
86impl VirtualBlockStats {
87 #[inline]
89 #[must_use]
90 pub fn set_size(mut self, value: u64) -> Self {
91 self.size = value;
92 self
93 }
94
95 #[inline]
97 #[must_use]
98 pub fn set_allocation_count(mut self, value: usize) -> Self {
99 self.allocation_count = value;
100 self
101 }
102
103 #[inline]
105 #[must_use]
106 pub fn set_allocated_bytes(mut self, value: u64) -> Self {
107 self.allocated_bytes = value;
108 self
109 }
110
111 #[inline]
113 #[must_use]
114 pub fn set_free_bytes(mut self, value: u64) -> Self {
115 self.free_bytes = value;
116 self
117 }
118
119 #[inline]
121 #[must_use]
122 pub fn set_free_region_count(mut self, value: usize) -> Self {
123 self.free_region_count = value;
124 self
125 }
126
127 #[inline]
129 #[must_use]
130 pub fn set_largest_free_region(mut self, value: u64) -> Self {
131 self.largest_free_region = value;
132 self
133 }
134}
135
136pub struct VirtualBlock<'callbacks> {
143 owner: Identity,
144 metadata: Metadata<'callbacks>,
145}
146
147#[derive(Clone, Copy, Debug, PartialEq, Eq)]
149pub struct VirtualBlockDetails {
150 pub statistics: VirtualBlockStats,
151 pub allocation_size_min: Option<u64>,
152 pub allocation_size_max: Option<u64>,
153 pub free_range_size_min: Option<u64>,
154 pub free_range_size_max: Option<u64>,
155}
156
157impl VirtualBlockDetails {
158 #[inline]
160 #[must_use]
161 pub fn set_statistics(mut self, value: VirtualBlockStats) -> Self {
162 self.statistics = value;
163 self
164 }
165
166 #[inline]
168 #[must_use]
169 pub fn set_allocation_size_min(mut self, value: Option<u64>) -> Self {
170 self.allocation_size_min = value;
171 self
172 }
173
174 #[inline]
176 #[must_use]
177 pub fn set_allocation_size_max(mut self, value: Option<u64>) -> Self {
178 self.allocation_size_max = value;
179 self
180 }
181
182 #[inline]
184 #[must_use]
185 pub fn set_free_range_size_min(mut self, value: Option<u64>) -> Self {
186 self.free_range_size_min = value;
187 self
188 }
189
190 #[inline]
192 #[must_use]
193 pub fn set_free_range_size_max(mut self, value: Option<u64>) -> Self {
194 self.free_range_size_max = value;
195 self
196 }
197}
198
199pub fn set_user_data(
201 block: &mut VirtualBlock,
202 allocation: &VirtualAllocation,
203 data: usize,
204) -> Result<(), AllocError> {
205 allocation_info(block, allocation)?;
206 block.metadata.set_user_data(
207 allocation.placement.ok_or(AllocError::UnknownAllocation)?,
208 data,
209 )
210}
211
212pub fn is_empty(block: &VirtualBlock) -> bool {
214 block.metadata.is_empty()
215}
216
217pub fn detailed_statistics(block: &VirtualBlock) -> VirtualBlockDetails {
219 let mut result = VirtualBlockDetails {
220 statistics: statistics(block),
221 allocation_size_min: None,
222 allocation_size_max: None,
223 free_range_size_min: None,
224 free_range_size_max: None,
225 };
226 block.metadata.for_each_allocation(|_, size, _| {
227 result.allocation_size_min = Some(result.allocation_size_min.map_or(size, |old| old.min(size)));
228 result.allocation_size_max = Some(result.allocation_size_max.map_or(size, |old| old.max(size)));
229 });
230 block.metadata.for_each_free(|_, size| {
231 result.free_range_size_min = Some(result.free_range_size_min.map_or(size, |old| old.min(size)));
232 result.free_range_size_max = Some(result.free_range_size_max.map_or(size, |old| old.max(size)));
233 });
234 result
235}
236
237pub fn write_statistics_json(
241 block: &VirtualBlock,
242 output: &mut impl std::io::Write,
243 detailed: bool,
244) -> Result<(), AllocError> {
245 let stats = statistics(block);
246 let mut allocations = HostVec::new(block.metadata.host_memory());
247 if detailed {
248 allocations
249 .try_reserve_exact(stats.allocation_count)
250 .map_err(|_| AllocError::HostAllocationFailed)?;
251 block
252 .metadata
253 .for_each_allocation(|offset, size, user_data| {
254 allocations.push_reserved((offset, size, user_data))
255 });
256 allocations.sort_unstable_by_key(|value| value.0);
257 }
258 let result = (|| -> std::io::Result<()> {
259 write!(
260 output,
261 "{{\"schema\":\"vk.memory.virtual.v1\",\"size\":{},\"allocation_count\":{},\"allocation_bytes\":{},\"free_bytes\":{},\"free_range_count\":{},\"largest_free_range\":{}",
262 stats.size,
263 stats.allocation_count,
264 stats.allocated_bytes,
265 stats.free_bytes,
266 stats.free_region_count,
267 stats.largest_free_region
268 )?;
269 if detailed {
270 output.write_all(b",\"allocations\":[")?;
271 for (index, (offset, size, data)) in allocations.iter().enumerate() {
272 if index != 0 {
273 output.write_all(b",")?;
274 }
275 write!(
276 output,
277 "{{\"offset\":{offset},\"size\":{size},\"user_data\":{data}}}"
278 )?;
279 }
280 output.write_all(b"],\"free_ranges\":[")?;
281 let mut first = true;
282 let mut error = None;
283 block.metadata.for_each_free(|offset, size| {
284 if error.is_some() {
285 return;
286 }
287 let result = (|| {
288 if !first {
289 output.write_all(b",")?;
290 }
291 first = false;
292 write!(output, "[{offset},{size}]")
293 })();
294 if let Err(failure) = result {
295 error = Some(failure);
296 }
297 });
298 if let Some(error) = error {
299 return Err(error);
300 }
301 output.write_all(b"]")?;
302 }
303 output.write_all(b"}\n")
304 })();
305 result.map_err(|error| AllocError::Output {
306 kind: error.kind(),
307 code: error.raw_os_error(),
308 })
309}
310
311impl<'callbacks> VirtualBlock<'callbacks> {
312 pub fn is_empty(&self) -> bool {
314 is_empty(self)
315 }
316
317 pub fn set_user_data(
319 &mut self,
320 allocation: &VirtualAllocation,
321 data: usize,
322 ) -> Result<(), AllocError> {
323 set_user_data(self, allocation, data)
324 }
325
326 pub fn detailed_statistics(&self) -> VirtualBlockDetails {
328 detailed_statistics(self)
329 }
330
331 pub fn write_statistics_json(
333 &self,
334 output: &mut impl std::io::Write,
335 detailed: bool,
336 ) -> Result<(), AllocError> {
337 write_statistics_json(self, output, detailed)
338 }
339
340 pub fn new(size: u64) -> Result<Self, AllocError> {
341 Self::new_with_algorithm(size, AllocationAlgorithm::Tlsf)
342 }
343
344 pub fn new_with_algorithm(size: u64, algorithm: AllocationAlgorithm) -> Result<Self, AllocError> {
360 Ok(Self {
361 owner: Identity::new()?,
362 metadata: Metadata::new(size, algorithm, true)?,
363 })
364 }
365
366 pub unsafe fn new_with_callbacks(
395 size: u64,
396 algorithm: AllocationAlgorithm,
397 callbacks: &'callbacks vk::AllocationCallbacks,
398 ) -> Result<Self, AllocError> {
399 let host = unsafe { HostMemory::new_from_callbacks(callbacks) }?;
402 Ok(Self {
403 owner: Identity::new()?,
404 metadata: Metadata::new_with_host(size, algorithm, true, host)?,
405 })
406 }
407
408 #[inline]
410 pub fn allocate(
411 &mut self,
412 size: u64,
413 alignment: u64,
414 strategy: PlacementStrategy,
415 ) -> Result<VirtualAllocation, AllocError> {
416 allocate(self, size, alignment, strategy)
417 }
418
419 pub fn allocate_upper(
423 &mut self,
424 size: u64,
425 alignment: u64,
426 ) -> Result<VirtualAllocation, AllocError> {
427 allocate_upper(self, size, alignment)
428 }
429
430 #[inline]
432 pub fn free(&mut self, allocation: &mut VirtualAllocation) -> Result<(), AllocError> {
433 free(self, allocation)
434 }
435
436 pub fn allocation_info(
438 &self,
439 allocation: &VirtualAllocation,
440 ) -> Result<VirtualAllocationInfo, AllocError> {
441 allocation_info(self, allocation)
442 }
443
444 pub fn clear(&mut self) {
447 clear(self)
448 }
449
450 pub fn statistics(&self) -> VirtualBlockStats {
452 statistics(self)
453 }
454}
455
456#[inline]
461pub fn allocate(
462 block: &mut VirtualBlock,
463 size: u64,
464 alignment: u64,
465 strategy: PlacementStrategy,
466) -> Result<VirtualAllocation, AllocError> {
467 let placement = block.metadata.allocate(size, alignment, strategy, false)?;
468 Ok(VirtualAllocation {
469 owner: block.owner,
470 placement: Some(placement),
471 })
472}
473
474pub fn allocate_upper(
478 block: &mut VirtualBlock,
479 size: u64,
480 alignment: u64,
481) -> Result<VirtualAllocation, AllocError> {
482 let placement = block
483 .metadata
484 .allocate(size, alignment, PlacementStrategy::Default, true)?;
485 Ok(VirtualAllocation {
486 owner: block.owner,
487 placement: Some(placement),
488 })
489}
490
491pub fn allocation_info(
493 block: &VirtualBlock,
494 allocation: &VirtualAllocation,
495) -> Result<VirtualAllocationInfo, AllocError> {
496 if block.owner != allocation.owner {
497 return Err(AllocError::WrongAllocator);
498 }
499 block
500 .metadata
501 .info(allocation.placement.ok_or(AllocError::UnknownAllocation)?)
502}
503
504#[inline]
506pub fn free(
507 block: &mut VirtualBlock,
508 allocation: &mut VirtualAllocation,
509) -> Result<(), AllocError> {
510 if block.owner != allocation.owner {
511 return Err(AllocError::WrongAllocator);
512 }
513 block
515 .metadata
516 .free(allocation.placement.ok_or(AllocError::UnknownAllocation)?)?;
517 allocation.placement = None;
518 Ok(())
519}
520
521pub fn clear(block: &mut VirtualBlock) {
523 block.metadata.clear()
524}
525
526pub fn statistics(block: &VirtualBlock) -> VirtualBlockStats {
528 block.metadata.statistics()
529}