1use super::*;
8use crate::mem::BudgetSnapshot;
9
10#[derive(Debug)]
12pub struct Pool {
13 pub(super) owner: Identity,
14 pub(super) serial: u64,
15}
16
17impl Pool {
18 pub fn is_destroyed(&self) -> bool {
19 self.serial == 0
20 }
21}
22
23#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
28pub struct PoolCreateInfo {
29 pub priority: MemoryPriority,
30 pub memory_type_index: u32,
31 pub algorithm: AllocationAlgorithm,
34 pub block_size: u64,
36 pub min_block_count: usize,
38 pub max_block_count: usize,
41 pub min_allocation_alignment: u64,
43}
44
45impl PoolCreateInfo {
46 #[inline]
48 #[must_use]
49 pub fn new() -> Self {
50 Self::default()
51 }
52
53 #[inline]
55 #[must_use]
56 pub fn set_priority(mut self, value: MemoryPriority) -> Self {
57 self.priority = value;
58 self
59 }
60
61 #[inline]
63 #[must_use]
64 pub fn set_memory_type_index(mut self, value: u32) -> Self {
65 self.memory_type_index = value;
66 self
67 }
68
69 #[inline]
71 #[must_use]
72 pub fn set_algorithm(mut self, value: AllocationAlgorithm) -> Self {
73 self.algorithm = value;
74 self
75 }
76
77 #[inline]
79 #[must_use]
80 pub fn set_block_size(mut self, value: u64) -> Self {
81 self.block_size = value;
82 self
83 }
84
85 #[inline]
87 #[must_use]
88 pub fn set_min_block_count(mut self, value: usize) -> Self {
89 self.min_block_count = value;
90 self
91 }
92
93 #[inline]
95 #[must_use]
96 pub fn set_max_block_count(mut self, value: usize) -> Self {
97 self.max_block_count = value;
98 self
99 }
100
101 #[inline]
103 #[must_use]
104 pub fn set_min_allocation_alignment(mut self, value: u64) -> Self {
105 self.min_allocation_alignment = value;
106 self
107 }
108}
109
110#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
115pub struct PoolStats {
116 pub allocation_count: usize,
117 pub allocation_bytes: u64,
118 pub reserved_bytes: u64,
119 pub block_count: usize,
120 pub block_bytes: u64,
121 pub free_bytes: u64,
122 pub largest_free_region: u64,
123}
124
125impl PoolStats {
126 #[inline]
128 #[must_use]
129 pub fn new() -> Self {
130 Self::default()
131 }
132
133 #[inline]
135 #[must_use]
136 pub fn set_allocation_count(mut self, value: usize) -> Self {
137 self.allocation_count = value;
138 self
139 }
140
141 #[inline]
143 #[must_use]
144 pub fn set_allocation_bytes(mut self, value: u64) -> Self {
145 self.allocation_bytes = value;
146 self
147 }
148
149 #[inline]
151 #[must_use]
152 pub fn set_reserved_bytes(mut self, value: u64) -> Self {
153 self.reserved_bytes = value;
154 self
155 }
156
157 #[inline]
159 #[must_use]
160 pub fn set_block_count(mut self, value: usize) -> Self {
161 self.block_count = value;
162 self
163 }
164
165 #[inline]
167 #[must_use]
168 pub fn set_block_bytes(mut self, value: u64) -> Self {
169 self.block_bytes = value;
170 self
171 }
172
173 #[inline]
175 #[must_use]
176 pub fn set_free_bytes(mut self, value: u64) -> Self {
177 self.free_bytes = value;
178 self
179 }
180
181 #[inline]
183 #[must_use]
184 pub fn set_largest_free_region(mut self, value: u64) -> Self {
185 self.largest_free_region = value;
186 self
187 }
188}
189
190pub(super) struct PoolState<'callbacks> {
191 pub allocation_chain: super::super::chain::ChainData,
192 pub name: Option<HostString<'callbacks>>,
193 pub info: PoolCreateInfo,
194 pub preferred_block_size: u64,
195}
196
197pub(super) unsafe fn allocate(
201 allocator: &mut Allocator<'_>,
202 request: &AllocRequest,
203 storage: StorageOptions<'_>,
204 dedication: Dedication,
205 options: &AllocationCreateInfo,
206 class: ResourceClass,
207 avoided: vk::MemoryPropertyFlags,
208) -> Result<u64, AllocError> {
209 let pool = options.pool.expect("selected custom pool");
210 let state = validate(allocator, pool)?;
211 let info = state.info;
212 let preferred_block_size = state.preferred_block_size;
213 if !request.alignment.is_power_of_two() {
214 return Err(AllocError::InvalidRequest("invalid native alignment"));
215 }
216 let request = AllocRequest {
217 memory_type_bits: request.memory_type_bits & (1 << info.memory_type_index),
218 alignment: request.alignment.max(info.min_allocation_alignment),
219 ..*request
220 };
221 let selected_type = allocator
223 .selector
224 .find_memory_type_with_preferences(&request, avoided)?;
225 let blocks = BlockOptions {
226 selected_type: Some(selected_type),
227 algorithm: info.algorithm,
228 allow_ring: info.max_block_count == 1,
229 automatic: info.block_size == 0,
230 block_size: preferred_block_size,
231 strategy: options.strategy,
232 class,
233 grow: !options.never_allocate,
234 pool: Some(pool.serial),
235 upper_address: options.upper_address,
236 priority: info.priority,
237 within_budget: options.within_budget,
238 prefer_mapped: super::super::policy::prefers_mapped(options),
239 };
240 if info.block_size != 0 {
241 if dedication == Dedication::Required {
242 return Err(AllocError::UnsupportedCapability(
243 "fixed-size pools cannot dedicate resources",
244 ));
245 }
246 return unsafe { allocate_pooled(allocator, &request, avoided, blocks) };
249 }
250 let serial =
252 unsafe { allocate_automatic(allocator, &request, storage, dedication, avoided, blocks) }?;
253 allocator
254 .live
255 .get_mut(&serial)
256 .expect("new pool allocation")
257 .pool = Some(pool.serial);
258 Ok(serial)
259}
260
261pub(super) unsafe fn allocate_automatic(
266 allocator: &mut Allocator<'_>,
267 request: &AllocRequest,
268 storage: StorageOptions<'_>,
269 dedication: Dedication,
270 avoided: vk::MemoryPropertyFlags,
271 blocks: BlockOptions,
272) -> Result<u64, AllocError> {
273 if blocks.upper_address && dedication == Dedication::Required {
274 return Err(AllocError::UnsupportedCapability(
275 "upper address cannot use required dedication",
276 ));
277 }
278 if dedication == Dedication::Required {
279 if !blocks.grow {
280 return Err(AllocError::OutOfSpace);
281 }
282 return unsafe { allocate_storage(allocator, request, storage, avoided) };
284 }
285 let high_count = allocator.allocation_limit < u32::MAX / 4
286 && allocator.total_native_count() > allocator.allocation_limit as usize * 3 / 4;
287 let prefer_dedicated = blocks.grow
288 && !blocks.upper_address
289 && !high_count
290 && (dedication == Dedication::Preferred || request.size > blocks.block_size / 2);
291 if prefer_dedicated {
292 match unsafe { allocate_storage(allocator, request, storage, avoided) } {
294 Ok(serial) => return Ok(serial),
295 Err(error) if retryable(&error) => {}
296 Err(error) => return Err(error),
297 }
298 }
299 let mut last = match unsafe {
301 allocate_pooled(
302 allocator,
303 request,
304 avoided,
305 BlockOptions {
306 grow: false,
307 ..blocks
308 },
309 )
310 } {
311 Ok(serial) => return Ok(serial),
312 Err(error) if retryable(&error) => error,
313 Err(error) => return Err(error),
314 };
315 if !blocks.grow {
316 return Err(last);
317 }
318 let index = request.memory_type_bits.trailing_zeros() as usize;
319 let flags = allocator.selector.properties.memory_types[index].property_flags;
320 let atom = if flags.as_raw()
321 & (vk::MemoryPropertyFlagBits::HOST_VISIBLE_BIT | vk::MemoryPropertyFlagBits::HOST_COHERENT_BIT)
322 .as_raw()
323 == vk::MemoryPropertyFlagBits::HOST_VISIBLE_BIT.as_raw()
324 {
325 allocator.selector.atom
326 } else {
327 1
328 };
329 let reserved = request
330 .size
331 .checked_add(atom - 1)
332 .map(|value| value & !(atom - 1))
333 .ok_or(AllocError::InvalidRange)?;
334 let max_existing = allocator
335 .blocks
336 .iter()
337 .filter(|block| {
338 block.pool == blocks.pool
339 && block.memory_type_index as usize == index
340 && block.priority == blocks.priority
341 && block.automatic == blocks.automatic
342 })
343 .map(|block| block.size)
344 .max()
345 .unwrap_or(0);
346 let mut size = blocks.block_size;
347 let mut shift = 0;
348 while shift < 3 && size / 2 > max_existing && size / 4 >= reserved {
349 size /= 2;
350 shift += 1;
351 }
352 let snapshot = super::budget::heap_budgets(allocator)?;
355 let heap = allocator.selector.properties.memory_types[index].heap_index as usize;
356 let available = snapshot.heaps[heap]
357 .budget
358 .saturating_sub(snapshot.heaps[heap].usage)
359 .min(
360 snapshot.heaps[heap]
361 .limit
362 .saturating_sub(snapshot.heaps[heap].native_bytes),
363 );
364 while size >= reserved {
365 if !blocks.upper_address && size > available {
366 last = AllocError::OutOfSpace;
367 } else {
368 match unsafe {
370 allocate_pooled(
371 allocator,
372 request,
373 avoided,
374 BlockOptions {
375 block_size: size,
376 ..blocks
377 },
378 )
379 } {
380 Ok(serial) => return Ok(serial),
381 Err(error) if retryable(&error) => last = error,
382 Err(error) => return Err(error),
383 }
384 }
385 if shift == 3 {
386 break;
387 }
388 size /= 2;
389 shift += 1;
390 }
391 if !prefer_dedicated && !blocks.upper_address {
392 return unsafe { allocate_storage(allocator, request, storage, avoided) };
394 }
395 Err(last)
396}
397
398fn retryable(error: &AllocError) -> bool {
399 matches!(
400 error,
401 AllocError::OutOfSpace | AllocError::AllocationLimit | AllocError::AllocationTooLarge { .. }
402 ) || matches!(error, AllocError::Call(error) if recoverable(error))
403}
404
405impl Allocator<'_> {
406 pub unsafe fn create_pool(&mut self, info: &PoolCreateInfo) -> Result<Pool, AllocError> {
412 unsafe { create_pool(self, info) }
414 }
415
416 pub fn destroy_pool(&mut self, pool: &mut Pool) -> Result<(), AllocError> {
418 destroy_pool(self, pool)
419 }
420
421 pub fn pool_statistics(&self, pool: &Pool) -> Result<PoolStats, AllocError> {
423 pool_statistics(self, pool)
424 }
425}
426
427pub(super) fn validate_options<'a, 'device>(
428 allocator: &'a Allocator<'device>,
429 pool: &Pool,
430 options: &AllocationCreateInfo,
431) -> Result<&'a PoolState<'device>, AllocError> {
432 let state = validate(allocator, pool)?;
433 if options.upper_address
434 && (state.info.algorithm != AllocationAlgorithm::Linear || state.info.max_block_count != 1)
435 {
436 return Err(AllocError::UnsupportedCapability(
437 "upper address requires a single-block linear pool",
438 ));
439 }
440 Ok(state)
441}
442
443pub(super) fn validate<'a, 'device>(
444 allocator: &'a Allocator<'device>,
445 pool: &Pool,
446) -> Result<&'a PoolState<'device>, AllocError> {
447 allocator.ensure_open()?;
448 if allocator.owner != pool.owner {
449 return Err(AllocError::WrongAllocator);
450 }
451 allocator
452 .pools
453 .get(&pool.serial)
454 .ok_or(AllocError::UnknownPool)
455}
456
457pub unsafe fn create_pool(
470 allocator: &mut Allocator<'_>,
471 info: &PoolCreateInfo,
472) -> Result<Pool, AllocError> {
473 unsafe { create_pool_inner(allocator, info, Default::default()) }
475}
476
477pub unsafe fn create_pool_with_chain<'device>(
486 allocator: &mut Allocator<'device>,
487 info: &PoolCreateInfo,
488 chain: super::super::AllocationChain<'device>,
489) -> Result<Pool, AllocError> {
490 if chain.data.imports {
491 return Err(AllocError::InvalidRequest(
492 "pool chains cannot import memory",
493 ));
494 }
495 unsafe { create_pool_inner(allocator, info, chain.data) }
497}
498
499unsafe fn create_pool_inner(
500 allocator: &mut Allocator<'_>,
501 info: &PoolCreateInfo,
502 chain: super::super::chain::ChainData,
503) -> Result<Pool, AllocError> {
504 allocator.ensure_open()?;
505 allocator.ensure_global()?;
506 if info.priority != MemoryPriority::default() && !allocator.memory_priority {
507 return Err(AllocError::UnsupportedCapability(
508 "pool priority requires enabled EXT feature metadata",
509 ));
510 }
511 if (info.max_block_count != 0 && info.min_block_count > info.max_block_count)
512 || (info.min_allocation_alignment != 0 && !info.min_allocation_alignment.is_power_of_two())
513 {
514 return Err(AllocError::InvalidRequest("invalid pool policy"));
515 }
516 if info.memory_type_index >= allocator.selector.properties.memory_type_count {
517 return Err(AllocError::InvalidRequest("absent pool memory type"));
518 }
519 if info.block_size != 0 {
520 allocator.validate_size(info.block_size)?;
521 }
522 let flags =
523 allocator.selector.properties.memory_types[info.memory_type_index as usize].property_flags;
524 let protected = flags.as_raw() & vk::MemoryPropertyFlagBits::PROTECTED_BIT.as_raw() != 0;
525 if protected && !allocator.protected_memory {
526 return Err(AllocError::UnsupportedCapability(
527 "protected pool requires enabled feature metadata",
528 ));
529 }
530 super::external::allocation_exports(allocator, info.memory_type_index, chain)?;
531 allocator.selector.find_memory_type(&AllocRequest {
533 size: info.block_size.max(1),
534 alignment: 1,
535 memory_type_bits: 1 << info.memory_type_index,
536 required_flags: if protected {
537 vk::MemoryPropertyFlagBits::PROTECTED_BIT
538 } else {
539 vk::MemoryPropertyFlags::empty()
540 },
541 preferred_flags: vk::MemoryPropertyFlags::empty(),
542 })?;
543 let preferred_block_size = if info.block_size != 0 {
544 info.block_size
545 } else {
546 preferred_block_size(allocator, info.memory_type_index)?
547 };
548 allocator
549 .next_serial
550 .checked_add(1)
551 .ok_or(AllocError::SerialExhausted)?;
552 allocator
553 .pools
554 .try_reserve(1)
555 .map_err(|_| AllocError::HostAllocationFailed)?;
556 let serial = allocator.next_serial;
557 for _ in 0..info.min_block_count {
558 if let Err(error) = unsafe {
560 create_block(
561 allocator,
562 serial,
563 info.memory_type_index,
564 preferred_block_size,
565 info,
566 chain,
567 )
568 } {
569 remove_blocks(allocator, serial);
570 return Err(error);
571 }
572 }
573 allocator.next_serial += 1;
574 allocator.pools.insert_reserved(
575 serial,
576 PoolState {
577 allocation_chain: chain,
578 name: None,
579 info: *info,
580 preferred_block_size,
581 },
582 );
583 Ok(Pool {
584 owner: allocator.owner,
585 serial,
586 })
587}
588
589unsafe fn create_block(
590 allocator: &mut Allocator<'_>,
591 serial: u64,
592 memory_type_index: u32,
593 block_size: u64,
594 info: &PoolCreateInfo,
595 chain: super::super::chain::ChainData,
596) -> Result<(), AllocError> {
597 allocator.check_native(block_size, memory_type_index)?;
598 allocator
599 .blocks
600 .try_reserve(1)
601 .map_err(|_| AllocError::HostAllocationFailed)?;
602 let metadata = Metadata::new_native(
603 block_size,
604 info.algorithm,
605 info.max_block_count == 1,
606 allocator.granularity,
607 allocator.host,
608 )?;
609 let export_types = super::external::allocation_exports(allocator, memory_type_index, chain)?;
610 let handle = unsafe {
612 allocate_native(
613 allocator,
614 block_size,
615 memory_type_index,
616 StorageOptions {
617 chain: &chain,
618 dedicated: None,
619 device_address: allocator.buffer_device_address,
620 priority: info.priority,
621 within_budget: false,
622 },
623 )
624 }?;
625 if handle.is_null() || allocator.contains_memory(handle) {
626 return Err(
627 CallError::InvalidOutput("allocator returned a null or already-live memory handle").into(),
628 );
629 }
630 allocator.blocks.push_reserved(Block {
631 defragmentation_hold: false,
632 automatic: info.block_size == 0,
633 export_types,
634 priority: info.priority,
635 pool: Some(serial),
636 handle,
637 size: block_size,
638 memory_type_index,
639 metadata,
640 mapping: MappingOwner::new_with_hysteresis(allocator.mapping_hysteresis),
641 });
642 allocator.add_native(block_size, memory_type_index, handle);
643 Ok(())
644}
645
646fn remove_block(allocator: &mut Allocator<'_>, index: usize) {
647 let block = &mut allocator.blocks[index];
648 debug_assert!(block.metadata.is_empty());
649 debug_assert_eq!(block.mapping.read().references, 0);
650 unsafe {
652 block
653 .mapping
654 .write()
655 .clear_cache(allocator.device.as_device(), block.handle)
656 }
657 .expect("preflighted cached mapping cleanup");
658 unsafe {
660 allocator
661 .device
662 .as_device()
663 .free_memory(block.handle, allocator.callbacks)
664 }
665 .expect("preflighted pool cleanup");
666 let block = allocator.blocks.remove(index);
667 allocator.remove_native(block.size, block.memory_type_index, block.handle);
668}
669
670fn remove_blocks(allocator: &mut Allocator<'_>, serial: u64) {
671 for index in (0..allocator.blocks.len()).rev() {
672 if allocator.blocks[index].pool == Some(serial) {
673 remove_block(allocator, index);
674 }
675 }
676}
677
678pub fn destroy_pool(allocator: &mut Allocator<'_>, pool: &mut Pool) -> Result<(), AllocError> {
682 allocator.ensure_allocation()?;
683 validate(allocator, pool)?;
684 if allocator
685 .live
686 .values()
687 .any(|live| live.pool == Some(pool.serial))
688 {
689 return Err(AllocError::LiveAllocations);
690 }
691 remove_blocks(allocator, pool.serial);
692 allocator.pools.remove(&pool.serial);
693 pool.serial = 0;
694 Ok(())
695}
696
697pub fn pool_statistics(allocator: &Allocator<'_>, pool: &Pool) -> Result<PoolStats, AllocError> {
701 validate(allocator, pool)?;
702 let mut result = PoolStats::new();
703 for block in allocator
704 .blocks
705 .iter()
706 .filter(|block| block.pool == Some(pool.serial))
707 {
708 let stats = block.metadata.statistics();
709 result.block_count += 1;
710 result.block_bytes += block.size;
711 result.reserved_bytes += stats.allocated_bytes;
712 result.free_bytes += stats.free_bytes;
713 result.largest_free_region = result.largest_free_region.max(stats.largest_free_region);
714 }
715 for live in allocator
716 .live
717 .values()
718 .filter(|live| live.pool == Some(pool.serial))
719 {
720 result.allocation_count += 1;
721 result.allocation_bytes += live.size;
722 if live.placement.is_none() {
723 result.block_count += 1;
724 result.block_bytes += live.size;
725 result.reserved_bytes += live.reserved_size;
726 }
727 }
728 Ok(result)
729}
730
731pub(super) fn release_block(allocator: &mut Allocator<'_>, index: usize) {
735 let selected = &allocator.blocks[index];
736 if selected.defragmentation_hold {
737 return;
738 }
739 let domain = BlockDomain::new_from_block(selected);
740 let min = domain
741 .pool
742 .map_or(0, |serial| allocator.pools[&serial].info.min_block_count);
743 let same_domain = |block: &Block| domain.contains(block);
744 let count = allocator
745 .blocks
746 .iter()
747 .filter(|block| same_domain(block))
748 .count();
749 if count <= min {
750 return;
751 }
752 let empty = allocator.blocks[index].metadata.is_empty();
753 let other_empty = allocator
754 .blocks
755 .iter()
756 .enumerate()
757 .rfind(|(other, block)| {
758 *other != index
759 && !block.defragmentation_hold
760 && same_domain(block)
761 && block.metadata.is_empty()
762 && block.mapping.read().references == 0
763 })
764 .map(|(index, _)| index);
765 if empty {
766 let heap = allocator.selector.properties.memory_types
767 [allocator.blocks[index].memory_type_index as usize]
768 .heap_index as usize;
769 let ceiling =
770 allocator.heap_limits[heap].unwrap_or(allocator.selector.properties.memory_heaps[heap].size);
771 if other_empty.is_some()
772 || allocator.heap_bytes[heap] >= ceiling
773 || super::budget::cached_pressure(allocator, heap)
774 {
775 remove_block(allocator, index);
776 }
777 } else if other_empty.is_some() {
778 let last = allocator
781 .blocks
782 .iter()
783 .rposition(same_domain)
784 .expect("live block vector");
785 let block = &allocator.blocks[last];
786 if !block.defragmentation_hold
787 && block.metadata.is_empty()
788 && block.mapping.read().references == 0
789 {
790 remove_block(allocator, last);
791 }
792 }
793}
794
795pub(super) fn trim_blocks(
798 allocator: &mut Allocator<'_>,
799 snapshot: &mut BudgetSnapshot,
800 all: bool,
801) -> TrimStats {
802 let mut result = TrimStats::new();
803 for index in (0..allocator.blocks.len()).rev() {
804 let block = &allocator.blocks[index];
805 let serial = block.pool;
806 if block.defragmentation_hold
807 || block.mapping.read().references != 0
808 || !block.metadata.is_empty()
809 {
810 continue;
811 }
812 let min = serial.map_or(0, |serial| allocator.pools[&serial].info.min_block_count);
813 if allocator
814 .blocks
815 .iter()
816 .filter(|other| {
817 other.pool == serial
818 && other.memory_type_index == block.memory_type_index
819 && other.priority == block.priority
820 && other.automatic == block.automatic
821 })
822 .count()
823 <= min
824 {
825 continue;
826 }
827 let heap = allocator.selector.properties.memory_types[block.memory_type_index as usize]
828 .heap_index as usize;
829 let budget = &mut snapshot.heaps[heap];
830 if !all && budget.usage < budget.budget && budget.native_bytes < budget.limit {
831 continue;
832 }
833 result.block_count += 1;
835 result.block_bytes += block.size;
836 budget.usage = budget.usage.saturating_sub(block.size);
837 budget.native_bytes -= block.size;
838 remove_block(allocator, index);
839 }
840 result
841}
842
843pub(super) fn finish_defragmentation_blocks(allocator: &mut Allocator<'_>) -> TrimStats {
847 let mut result = TrimStats::new();
848 for index in (0..allocator.blocks.len()).rev() {
849 let block = &mut allocator.blocks[index];
850 if !block.defragmentation_hold {
851 continue;
852 }
853 block.defragmentation_hold = false;
854 if !block.metadata.is_empty() || block.mapping.read().references != 0 {
855 continue;
856 }
857 let pool = block.pool;
858 let minimum = pool.map_or(0, |serial| allocator.pools[&serial].info.min_block_count);
859 if pool.is_some()
860 && allocator
861 .blocks
862 .iter()
863 .filter(|block| block.pool == pool)
864 .count()
865 <= minimum
866 {
867 continue;
868 }
869 result.block_count += 1;
870 result.block_bytes += allocator.blocks[index].size;
871 remove_block(allocator, index);
872 }
873 result
874}
875
876fn preferred_block_size(allocator: &Allocator<'_>, index: u32) -> Result<u64, AllocError> {
878 let heap = allocator.selector.properties.memory_types[index as usize].heap_index as usize;
879 let heap_size = allocator.selector.properties.memory_heaps[heap].size;
880 let size = if heap_size <= 1024 * 1024 * 1024 {
881 heap_size / 8
882 } else {
883 allocator.preferred_large_heap_block_size
884 };
885 size
886 .checked_add(31)
887 .map(|size| size & !31)
888 .filter(|&size| size != 0)
889 .ok_or(AllocError::InvalidProperties("zero automatic block size"))
890}
891
892pub(super) unsafe fn allocate_default(
893 allocator: &mut Allocator<'_>,
894 request: &AllocRequest,
895 storage: StorageOptions<'_>,
896 dedication: Dedication,
897 options: &AllocationCreateInfo,
898 class: ResourceClass,
899 avoided: vk::MemoryPropertyFlags,
900) -> Result<u64, AllocError> {
901 let mut bits = request.memory_type_bits;
902 let mut last = None;
903 loop {
904 let candidate = AllocRequest {
905 memory_type_bits: bits,
906 ..*request
907 };
908 let index = match allocator
909 .selector
910 .find_memory_type_with_preferences(&candidate, avoided)
911 {
912 Ok(index) => index,
913 Err(error) => return Err(last.unwrap_or(error)),
914 };
915 bits &= !(1 << index);
916 let request = AllocRequest {
917 memory_type_bits: 1 << index,
918 ..*request
919 };
920 let blocks = BlockOptions {
921 selected_type: Some(index),
922 algorithm: AllocationAlgorithm::Tlsf,
923 allow_ring: false,
924 automatic: true,
925 block_size: preferred_block_size(allocator, index)?,
926 strategy: options.strategy,
927 class,
928 grow: !options.never_allocate,
929 pool: None,
930 upper_address: false,
931 priority: options.priority,
932 within_budget: options.within_budget,
933 prefer_mapped: super::super::policy::prefers_mapped(options),
934 };
935 match unsafe { allocate_automatic(allocator, &request, storage, dedication, avoided, blocks) } {
937 Ok(serial) => return Ok(serial),
938 Err(error) if retryable(&error) => last = Some(error),
939 Err(error) => return Err(error),
940 }
941 }
942}
943
944pub fn set_preferred_block_size(
950 allocator: &mut Allocator<'_>,
951 size: u64,
952) -> Result<(), AllocError> {
953 allocator.ensure_global()?;
954 allocator.ensure_open()?;
955 if allocator.native_count != 0 || !allocator.live.is_empty() {
956 return Err(AllocError::LiveAllocations);
957 }
958 if !allocator.pools.is_empty() {
959 return Err(AllocError::LivePools);
960 }
961 let size = size
962 .checked_add(31)
963 .map(|size| size & !31)
964 .filter(|&size| size != 0)
965 .ok_or(AllocError::InvalidRequest(
966 "preferred block size must be nonzero and roundable",
967 ))?;
968 allocator.preferred_large_heap_block_size = size;
969 Ok(())
970}
971
972impl Allocator<'_> {
973 pub fn set_preferred_block_size(&mut self, size: u64) -> Result<(), AllocError> {
975 set_preferred_block_size(self, size)
976 }
977}
978
979pub fn set_mapping_hysteresis(
985 allocator: &mut Allocator<'_>,
986 enabled: bool,
987) -> Result<(), AllocError> {
988 allocator.ensure_global()?;
989 allocator.ensure_open()?;
990 if allocator.native_count != 0 || !allocator.live.is_empty() {
991 return Err(AllocError::LiveAllocations);
992 }
993 if !allocator.pools.is_empty() {
994 return Err(AllocError::LivePools);
995 }
996 allocator.mapping_hysteresis = enabled;
997 Ok(())
998}
999
1000impl Allocator<'_> {
1001 pub fn set_mapping_hysteresis(&mut self, enabled: bool) -> Result<(), AllocError> {
1003 set_mapping_hysteresis(self, enabled)
1004 }
1005}