1use super::{
9 AllocError, AllocRequest, Allocation, AllocationCreateInfo, AllocationInfo, AllocationPolicy,
10 AllocatorStats, MemoryPriority, MemoryTypeSelector,
11 block::{Block, BlockDomain, MappingOwner, MappingRead, MappingWrite},
12 granularity::{Granularity, ResourceClass},
13 host::{HostMap, HostMemory, HostString, HostVec, Identity},
14 metadata::{AllocationAlgorithm, Metadata, Placement},
15};
16use crate::{self as vk, Device, load::CallError};
17
18#[derive(Clone, Copy, PartialEq, Eq)]
19pub(in crate::mem) enum Resource {
20 Buffer(vk::Buffer),
21 Image(vk::Image),
22}
23
24impl Resource {
25 fn is_null(self) -> bool {
26 match self {
27 Self::Buffer(buffer) => buffer.is_null(),
28 Self::Image(image) => image.is_null(),
29 }
30 }
31}
32
33impl From<vk::Buffer> for Resource {
34 fn from(buffer: vk::Buffer) -> Self {
35 Self::Buffer(buffer)
36 }
37}
38
39impl From<vk::Image> for Resource {
40 fn from(image: vk::Image) -> Self {
41 Self::Image(image)
42 }
43}
44
45#[path = "synchronized/native.rs"]
46pub(in crate::mem) mod native;
47
48fn import_resource(resource: Option<super::ImportResource>) -> Option<Resource> {
49 resource.map(|resource| match resource {
50 super::ImportResource::Buffer(buffer) => Resource::Buffer(buffer),
51 super::ImportResource::Image(image) => Resource::Image(image),
52 })
53}
54
55struct Live<'callbacks> {
56 serial: u64,
57 pool: Option<u64>,
58 handle: vk::DeviceMemory,
59 size: u64,
60 reserved_size: u64,
61 block_size: u64,
63 memory_type_index: u32,
64 offset: u64,
65 alignment: u64,
66 map_count: u32,
67 persistent: bool,
68 resource: Option<Resource>,
69 bound: bool,
70 name: Option<HostString<'callbacks>>,
71 user_data: usize,
72 pinned: bool,
73 can_alias: bool,
74 imported: bool,
75 plane: Option<vk::ImageAspectFlagBits>,
76 export_types: vk::ExternalMemoryHandleTypeFlags,
77 guard_size: u64,
78 guard_offset: u64,
79 debug_ready: bool,
80 class: ResourceClass,
81 placement: Option<Placement>,
82 mapping: MappingOwner<'callbacks>,
83 native_users: u32,
84 pending_binding: Option<(Resource, Option<vk::ImageAspectFlagBits>)>,
85}
86
87#[derive(Clone, Copy)]
88enum RequirementsRoute {
89 Legacy,
90 Core,
91 Khr,
92}
93
94#[derive(Clone)]
99pub enum DeviceRetention<'device> {
100 Borrowed(&'device Device),
102 Owned(std::sync::Arc<Device>),
104}
105
106impl DeviceRetention<'_> {
107 pub fn as_device(&self) -> &Device {
109 match self {
110 Self::Borrowed(device) => device,
111 Self::Owned(device) => device,
112 }
113 }
114}
115
116impl std::ops::Deref for DeviceRetention<'_> {
117 type Target = Device;
118
119 fn deref(&self) -> &Device {
120 self.as_device()
121 }
122}
123
124pub struct Allocator<'device> {
129 host: HostMemory<'device>,
130 device: DeviceRetention<'device>,
131 callbacks: Option<&'device vk::AllocationCallbacks>,
132 device_memory_callbacks: Option<&'device super::DeviceMemoryCallbacks>,
133 physical_device: Option<vk::PhysicalDevice>,
134 instance_handle: Option<vk::InstanceHandle>,
135 physical_properties: Option<vk::PhysicalDeviceProperties>,
136 selector: MemoryTypeSelector,
137 owner: Identity,
138 next_serial: u64,
139 live: HostMap<'device, Live<'device>>,
140 native_handles: HostMap<'device, ()>,
142 associated: binding::Associations<'device>,
143 pools: HostMap<'device, pool::PoolState<'device>>,
144 blocks: HostVec<'device, Block<'device>>,
145 native_count: usize,
146 native_bytes: u64,
147 heap_bytes: [u64; 16],
148 heap_limits: [Option<u64>; 16],
149 allocation_limit: u32,
150 allocation_size_limit: Option<u64>,
151 integrated: Option<bool>,
152 buffer_device_address: bool,
153 memory_priority: bool,
154 protected_memory: bool,
155 budget_query: Option<budget::BudgetQuery>,
156 budget_cache: Option<super::BudgetSnapshot>,
157 budget_operations: u32,
158 current_frame_index: u32,
159 debug_settings: super::DebugMemorySettings,
160 external_types: [vk::ExternalMemoryHandleTypeFlags; 32],
161 preferred_large_heap_block_size: u64,
162 mapping_hysteresis: bool,
163 #[cfg(all(windows, feature = "platform-win32"))]
164 external_handles: HostVec<'device, external::CachedHandle>,
165 granularity: Granularity,
166 closed: bool,
167 parallel: Option<std::ptr::NonNull<std::ffi::c_void>>,
168 partition: parallel::Domain,
169 partition_id: u64,
170}
171
172#[path = "synchronized/scope.rs"]
173mod parallel;
174pub(super) use parallel::{Selection, with_domains};
175
176#[path = "external.rs"]
177mod external;
178#[cfg(unix)]
179pub use external::get_memory_fd;
180#[cfg(all(windows, feature = "platform-win32"))]
181pub use external::get_memory_win32_handle;
182pub use external::{external_memory_types, set_external_memory_types};
183
184#[path = "guard.rs"]
185mod guard;
186pub use guard::{check_corruption, check_pool_corruption, set_debug_memory_settings};
187
188#[path = "defragmentation.rs"]
189mod defragmentation;
190pub use defragmentation::{
191 DefragmentationAlgorithm, DefragmentationContext, DefragmentationInfo, DefragmentationMove,
192 DefragmentationPass, DefragmentationResource, DefragmentationStats, begin_defragmentation,
193 begin_defragmentation_with_break, set_allocation_pinned,
194};
195
196#[path = "services.rs"]
197mod services;
198pub use services::{
199 CacheRange, DetailedStatistics, MemoryStatistics, allocate_many, allocation_name, allocator_info,
200 copy_from_allocation, copy_to_allocation, create_aliasing_buffer, create_aliasing_image,
201 detailed_pool_statistics, detailed_statistics, flush_many, free_many, invalidate_many,
202 memory_properties, memory_type_properties, physical_device_properties, pool_name,
203 set_allocation_name, set_allocation_user_data, set_pool_name, write_statistics_json,
204};
205
206#[path = "request.rs"]
207mod request;
208pub use request::{
209 allocate_dedicated, allocate_with_options, create_dedicated_buffer, create_dedicated_image,
210 find_memory_type, find_memory_type_for_buffer_info, find_memory_type_for_image_info,
211};
212
213#[path = "binding.rs"]
214mod binding;
215pub use binding::{
216 allocate_for_buffer, allocate_for_image, bind_buffer_memory, bind_buffer_memory2,
217 bind_image_memory, bind_image_memory2,
218};
219
220#[path = "budget.rs"]
221mod budget;
222pub use budget::{
223 TrimStats, budget_snapshot, heap_budgets, query_budget, refresh_budget,
224 refresh_budget_with_context, set_current_frame_index, set_heap_limit, trim, trim_with_budget,
225};
226
227#[path = "pool.rs"]
228mod pool;
229pub use pool::{
230 Pool, PoolCreateInfo, PoolStats, create_pool, create_pool_with_chain, destroy_pool,
231 pool_statistics, set_mapping_hysteresis, set_preferred_block_size,
232};
233
234impl<'device> Allocator<'device> {
235 pub unsafe fn new(
248 device: &'device Device,
249 properties: vk::PhysicalDeviceMemoryProperties,
250 atom: u64,
251 ) -> Result<Self, AllocError> {
252 Self::build(DeviceRetention::Borrowed(device), properties, atom)
253 }
254
255 pub unsafe fn new_retained(
265 device: std::sync::Arc<Device>,
266 properties: vk::PhysicalDeviceMemoryProperties,
267 atom: u64,
268 ) -> Result<Allocator<'static>, AllocError> {
269 Self::build(DeviceRetention::Owned(device), properties, atom)
270 }
271
272 fn build<'a>(
273 device: DeviceRetention<'a>,
274 properties: vk::PhysicalDeviceMemoryProperties,
275 atom: u64,
276 ) -> Result<Allocator<'a>, AllocError> {
277 let mut selector = MemoryTypeSelector::new(properties, atom)?;
278 selector.device_coherent = super::resource::device_coherent_memory(device.as_device());
279 require(device.fp().allocate_memory.is_some(), "vkAllocateMemory")?;
280 require(device.fp().free_memory.is_some(), "vkFreeMemory")?;
281 let buffer_device_address = super::resource::buffer_device_address(device.as_device());
282 let memory_priority = super::resource::memory_priority(device.as_device());
283 let protected_memory = super::resource::protected_memory(device.as_device());
284 Ok(Allocator {
285 host: HostMemory::default(),
286 device,
287 callbacks: None,
288 device_memory_callbacks: None,
289 physical_device: None,
290 instance_handle: None,
291 physical_properties: None,
292 selector,
293 owner: Identity::new()?,
294 next_serial: 1,
295 live: HostMap::new(HostMemory::default()),
296 native_handles: HostMap::new(HostMemory::default()),
297 associated: binding::Associations::new(HostMemory::default()),
298 pools: HostMap::new(HostMemory::default()),
299 blocks: HostVec::new(HostMemory::default()),
300 native_count: 0,
301 native_bytes: 0,
302 heap_bytes: [0; 16],
303 heap_limits: [None; 16],
304 allocation_limit: u32::MAX,
305 allocation_size_limit: None,
306 integrated: None,
307 buffer_device_address,
308 memory_priority,
309 protected_memory,
310 budget_query: None,
311 budget_cache: None,
312 budget_operations: 0,
313 current_frame_index: 0,
314 debug_settings: super::DebugMemorySettings::new(),
315 external_types: [vk::ExternalMemoryHandleTypeFlags::empty(); 32],
316 preferred_large_heap_block_size: 256 * 1024 * 1024,
317 mapping_hysteresis: true,
318 #[cfg(all(windows, feature = "platform-win32"))]
319 external_handles: HostVec::new(HostMemory::default()),
320 granularity: Granularity(None),
321 closed: false,
322 parallel: None,
323 partition: parallel::Domain::Global,
324 partition_id: 0,
325 })
326 }
327
328 pub unsafe fn new_with_granularity(
339 device: &'device Device,
340 properties: vk::PhysicalDeviceMemoryProperties,
341 atom: u64,
342 granularity: u64,
343 ) -> Result<Self, AllocError> {
344 if !granularity.is_power_of_two() {
345 return Err(AllocError::InvalidProperties(
346 "buffer/image granularity must be a nonzero power of two",
347 ));
348 }
349 let mut allocator = unsafe { Self::new(device, properties, atom) }?;
351 allocator.granularity = Granularity(Some(granularity));
352 Ok(allocator)
353 }
354
355 pub unsafe fn new_retained_with_granularity(
359 device: std::sync::Arc<Device>,
360 properties: vk::PhysicalDeviceMemoryProperties,
361 atom: u64,
362 granularity: u64,
363 ) -> Result<Allocator<'static>, AllocError> {
364 if !granularity.is_power_of_two() {
365 return Err(AllocError::InvalidProperties(
366 "buffer/image granularity must be a nonzero power of two",
367 ));
368 }
369 let mut allocator = unsafe { Self::new_retained(device, properties, atom) }?;
371 allocator.granularity = Granularity(Some(granularity));
372 Ok(allocator)
373 }
374
375 pub unsafe fn new_from_physical_device(
389 instance: &vk::Instance,
390 physical_device: vk::PhysicalDevice,
391 device: &'device Device,
392 ) -> Result<Self, AllocError> {
393 let parent = instance
394 .capabilities()
395 .ok_or(AllocError::UnsupportedCapability(
396 "instance creation metadata is required",
397 ))?;
398 let capabilities = device
399 .capabilities()
400 .ok_or(AllocError::UnsupportedCapability(
401 "device creation metadata is required",
402 ))?;
403 if !capabilities.matches_instance(parent) {
404 return Err(AllocError::InvalidProperties(
405 "device/instance metadata disagree",
406 ));
407 }
408 if physical_device.is_null() {
409 return Err(AllocError::InvalidProperties("null physical device"));
410 }
411 let properties = unsafe { instance.get_physical_device_properties(physical_device) }?;
413 let maintenance3 = capabilities
416 .device_extensions()
417 .is_some_and(|mut extensions| extensions.any(|name| name == c"VK_KHR_maintenance3"));
418 let allocation_size_limit = if capabilities.api_version() >= vk::API_VERSION_1_1 || maintenance3
419 {
420 let mut limits = vk::PhysicalDeviceMaintenance3Properties::new();
421 let mut output = vk::PhysicalDeviceProperties2 {
422 p_next: std::ptr::from_mut(&mut limits).cast(),
423 ..Default::default()
424 };
425 if parent.instance_api_version() >= vk::API_VERSION_1_1 {
426 unsafe { instance.get_physical_device_properties2(physical_device, &mut output) }?;
428 } else if parent
429 .instance_extensions()
430 .any(|name| name == c"VK_KHR_get_physical_device_properties2")
431 {
432 unsafe { instance.get_physical_device_properties2_khr(physical_device, &mut output) }?;
435 } else {
436 return Err(AllocError::UnsupportedCapability(
437 "maintenance3 requires properties2",
438 ));
439 }
440 if limits.max_memory_allocation_size == 0 {
441 return Err(AllocError::InvalidProperties(
442 "zero native allocation-size limit",
443 ));
444 }
445 Some(limits.max_memory_allocation_size)
446 } else {
447 None
448 };
449 let memory = unsafe { instance.get_physical_device_memory_properties(physical_device) }?;
451 if properties.limits.max_memory_allocation_count == 0 {
452 return Err(AllocError::InvalidProperties(
453 "zero native allocation limit",
454 ));
455 }
456 let mut allocator = unsafe {
458 Self::new_with_granularity(
459 device,
460 memory,
461 properties.limits.non_coherent_atom_size,
462 properties.limits.buffer_image_granularity,
463 )
464 }?;
465 allocator.integrated = Some(properties.device_type == vk::PhysicalDeviceType::INTEGRATED_GPU);
466 allocator.physical_device = Some(physical_device);
467 allocator.budget_query = budget::capture_query(instance, physical_device);
468 allocator.instance_handle = Some(instance.handle());
469 allocator.physical_properties = Some(properties);
470 allocator.allocation_limit = properties.limits.max_memory_allocation_count;
471 allocator.allocation_size_limit = allocation_size_limit;
472 allocator.buffer_route()?;
474 Ok(allocator)
475 }
476
477 pub unsafe fn new_retained_from_physical_device(
484 instance: &vk::Instance,
485 physical_device: vk::PhysicalDevice,
486 device: std::sync::Arc<Device>,
487 ) -> Result<Allocator<'static>, AllocError> {
488 let parent = instance
489 .capabilities()
490 .ok_or(AllocError::UnsupportedCapability(
491 "instance creation metadata is required",
492 ))?;
493 let capabilities = device
494 .capabilities()
495 .ok_or(AllocError::UnsupportedCapability(
496 "device creation metadata is required",
497 ))?;
498 if !capabilities.matches_instance(parent) {
499 return Err(AllocError::InvalidProperties(
500 "device/instance metadata disagree",
501 ));
502 }
503 if physical_device.is_null() {
504 return Err(AllocError::InvalidProperties("null physical device"));
505 }
506 let properties = unsafe { instance.get_physical_device_properties(physical_device) }?;
508 let maintenance3 = capabilities
509 .device_extensions()
510 .is_some_and(|mut extensions| extensions.any(|name| name == c"VK_KHR_maintenance3"));
511 let allocation_size_limit = if capabilities.api_version() >= vk::API_VERSION_1_1 || maintenance3
512 {
513 let mut limits = vk::PhysicalDeviceMaintenance3Properties::new();
514 let mut output = vk::PhysicalDeviceProperties2 {
515 p_next: std::ptr::from_mut(&mut limits).cast(),
516 ..Default::default()
517 };
518 if parent.instance_api_version() >= vk::API_VERSION_1_1 {
519 unsafe { instance.get_physical_device_properties2(physical_device, &mut output) }?;
521 } else if parent
522 .instance_extensions()
523 .any(|name| name == c"VK_KHR_get_physical_device_properties2")
524 {
525 unsafe { instance.get_physical_device_properties2_khr(physical_device, &mut output) }?;
528 } else {
529 return Err(AllocError::UnsupportedCapability(
530 "maintenance3 requires properties2",
531 ));
532 }
533 if limits.max_memory_allocation_size == 0 {
534 return Err(AllocError::InvalidProperties(
535 "zero native allocation-size limit",
536 ));
537 }
538 Some(limits.max_memory_allocation_size)
539 } else {
540 None
541 };
542 let memory = unsafe { instance.get_physical_device_memory_properties(physical_device) }?;
544 if properties.limits.max_memory_allocation_count == 0 {
545 return Err(AllocError::InvalidProperties(
546 "zero native allocation limit",
547 ));
548 }
549 let mut allocator = unsafe {
551 Self::new_retained_with_granularity(
552 device,
553 memory,
554 properties.limits.non_coherent_atom_size,
555 properties.limits.buffer_image_granularity,
556 )
557 }?;
558 allocator.integrated = Some(properties.device_type == vk::PhysicalDeviceType::INTEGRATED_GPU);
559 allocator.physical_device = Some(physical_device);
560 allocator.budget_query = budget::capture_query(instance, physical_device);
561 allocator.instance_handle = Some(instance.handle());
562 allocator.physical_properties = Some(properties);
563 allocator.allocation_limit = properties.limits.max_memory_allocation_count;
564 allocator.allocation_size_limit = allocation_size_limit;
565 allocator.buffer_route()?;
567 Ok(allocator)
568 }
569
570 pub unsafe fn new_with_callbacks(
584 device: &'device Device,
585 properties: vk::PhysicalDeviceMemoryProperties,
586 atom: u64,
587 callbacks: &'device vk::AllocationCallbacks,
588 ) -> Result<Self, AllocError> {
589 let mut allocator = unsafe { Self::new(device, properties, atom) }?;
591 unsafe { allocator.initialize_callbacks(callbacks) }?;
593 Ok(allocator)
594 }
595
596 pub unsafe fn new_from_physical_device_with_callbacks(
603 instance: &vk::Instance,
604 physical_device: vk::PhysicalDevice,
605 device: &'device Device,
606 callbacks: &'device vk::AllocationCallbacks,
607 ) -> Result<Self, AllocError> {
608 let mut allocator =
610 unsafe { Self::new_from_physical_device(instance, physical_device, device) }?;
611 unsafe { allocator.initialize_callbacks(callbacks) }?;
613 Ok(allocator)
614 }
615
616 unsafe fn initialize_callbacks(
617 &mut self,
618 callbacks: &'device vk::AllocationCallbacks,
619 ) -> Result<(), AllocError> {
620 debug_assert!(self.live.is_empty() && self.pools.is_empty() && self.blocks.is_empty());
621 let host = unsafe { HostMemory::new_from_callbacks(callbacks) }?;
624 self.host = host;
625 self.live = HostMap::new(host);
626 self.native_handles = HostMap::new(host);
627 self.associated = binding::Associations::new(host);
628 self.pools = HostMap::new(host);
629 self.blocks = HostVec::new(host);
630 #[cfg(all(windows, feature = "platform-win32"))]
631 {
632 self.external_handles = HostVec::new(host);
633 }
634 self.callbacks = Some(callbacks);
635 Ok(())
636 }
637
638 pub fn live_count(&self) -> usize {
639 self.live.len()
640 }
641
642 pub fn is_closed(&self) -> bool {
643 self.closed
644 }
645
646 pub fn max_allocation_size(&self) -> Option<u64> {
650 self.allocation_size_limit
651 }
652
653 pub fn close(&mut self) -> Result<(), AllocError> {
655 close(self)
656 }
657
658 pub unsafe fn allocate(&mut self, request: &AllocRequest) -> Result<Allocation, AllocError> {
665 unsafe { allocate(self, request) }
667 }
668
669 pub unsafe fn free(&mut self, allocation: &mut Allocation) -> Result<(), AllocError> {
672 unsafe { free(self, allocation) }
674 }
675
676 pub unsafe fn map(
680 &mut self,
681 allocation: &Allocation,
682 ) -> Result<*mut std::ffi::c_void, AllocError> {
683 unsafe { map(self, allocation) }
685 }
686
687 pub unsafe fn unmap(&mut self, allocation: &Allocation) -> Result<(), AllocError> {
690 unsafe { unmap(self, allocation) }
692 }
693
694 pub unsafe fn flush(
697 &self,
698 allocation: &Allocation,
699 offset: u64,
700 size: u64,
701 ) -> Result<(), AllocError> {
702 unsafe { flush(self, allocation, offset, size) }
704 }
705
706 pub unsafe fn invalidate(
709 &self,
710 allocation: &Allocation,
711 offset: u64,
712 size: u64,
713 ) -> Result<(), AllocError> {
714 unsafe { invalidate(self, allocation, offset, size) }
716 }
717
718 pub unsafe fn create_buffer(
725 &mut self,
726 info: &vk::BufferCreateInfo,
727 required: vk::MemoryPropertyFlags,
728 preferred: vk::MemoryPropertyFlags,
729 ) -> Result<(vk::Buffer, Allocation), AllocError> {
730 unsafe { create_buffer(self, info, required, preferred) }
732 }
733
734 pub unsafe fn destroy_buffer(
739 &mut self,
740 buffer: vk::Buffer,
741 allocation: &mut Allocation,
742 ) -> Result<(), AllocError> {
743 unsafe { destroy_buffer(self, buffer, allocation) }
745 }
746
747 pub unsafe fn create_buffer_with_options(
751 &mut self,
752 info: &vk::BufferCreateInfo,
753 options: &AllocationCreateInfo,
754 ) -> Result<(vk::Buffer, Allocation), AllocError> {
755 unsafe { create_buffer_with_options(self, info, options) }
757 }
758
759 pub unsafe fn create_image(
763 &mut self,
764 info: &vk::ImageCreateInfo,
765 required: vk::MemoryPropertyFlags,
766 preferred: vk::MemoryPropertyFlags,
767 ) -> Result<(vk::Image, Allocation), AllocError> {
768 unsafe { create_image(self, info, required, preferred) }
770 }
771
772 pub unsafe fn create_image_with_options(
776 &mut self,
777 info: &vk::ImageCreateInfo,
778 options: &AllocationCreateInfo,
779 ) -> Result<(vk::Image, Allocation), AllocError> {
780 unsafe { create_image_with_options(self, info, options) }
782 }
783
784 pub unsafe fn destroy_image(
789 &mut self,
790 image: vk::Image,
791 allocation: &mut Allocation,
792 ) -> Result<(), AllocError> {
793 unsafe { destroy_image(self, image, allocation) }
795 }
796
797 pub fn mapped_pointer(
799 &self,
800 allocation: &Allocation,
801 ) -> Result<Option<*mut std::ffi::c_void>, AllocError> {
802 mapped_pointer(self, allocation)
803 }
804
805 pub fn allocation_info(&self, allocation: &Allocation) -> Result<AllocationInfo, AllocError> {
809 allocation_info(self, allocation)
810 }
811
812 pub fn statistics(&self) -> AllocatorStats {
813 statistics(self)
814 }
815
816 fn ensure_open(&self) -> Result<(), AllocError> {
817 if self.closed {
818 Err(AllocError::Closed)
819 } else {
820 Ok(())
821 }
822 }
823
824 fn parallel_state(&self) -> Option<¶llel::Shared<'device>> {
825 self
829 .parallel
830 .map(|pointer| unsafe { pointer.cast::<parallel::Shared<'device>>().as_ref() })
831 }
832
833 fn total_native_count(&self) -> usize {
834 self
835 .parallel_state()
836 .map_or(self.native_count, parallel::Shared::native_count)
837 }
838
839 fn release_resource_claim(&self, allocation: &Allocation) {
840 if let Some(shared) = self.parallel_state() {
841 let live = &self.live[&allocation.serial];
842 shared.release_resource(live.resource, live.plane, allocation.serial);
843 }
844 }
845
846 fn ensure_allocation(&self) -> Result<(), AllocError> {
847 if matches!(self.partition, parallel::Domain::Block(_)) {
848 Err(AllocError::InvalidRequest(
849 "block scope cannot allocate, free or trim storage",
850 ))
851 } else {
852 Ok(())
853 }
854 }
855
856 fn ensure_default(&self) -> Result<(), AllocError> {
857 self.ensure_allocation()?;
858 if matches!(self.partition, parallel::Domain::Pool(_)) {
859 Err(AllocError::InvalidRequest(
860 "pool scope requires its selected pool",
861 ))
862 } else {
863 Ok(())
864 }
865 }
866
867 fn ensure_global(&self) -> Result<(), AllocError> {
868 if self.parallel.is_some() {
869 Err(AllocError::InvalidRequest(
870 "allocator-wide operation inside pool scope",
871 ))
872 } else {
873 Ok(())
874 }
875 }
876
877 fn token(&self, serial: u64) -> Result<Allocation, AllocError> {
878 let live = self
879 .live
880 .get(&serial)
881 .ok_or(AllocError::UnknownAllocation)?;
882 let memory = self.selector.properties.memory_types[live.memory_type_index as usize];
883 let block_size = if live.placement.is_some() {
884 self
885 .blocks
886 .iter()
887 .find(|block| block.handle == live.handle)
888 .ok_or(AllocError::UnknownAllocation)?
889 .size
890 } else {
891 live.size
892 };
893 Ok(Allocation {
894 handle: live.handle,
895 size: live.size,
896 reserved_size: live.reserved_size,
897 offset: live.offset,
898 block_size,
899 memory_type_index: live.memory_type_index,
900 heap_index: memory.heap_index,
901 flags: memory.property_flags,
902 atom: self.selector.atom,
903 owner: self.owner,
904 serial,
905 })
906 }
907 #[inline]
909 fn published(&self, serial: u64) -> Allocation {
910 self.allocation_from_live(self.live.get(&serial).expect("published allocation"))
911 }
912 #[inline]
913 fn allocation_from_live(&self, live: &Live<'_>) -> Allocation {
914 let memory = self.selector.properties.memory_types[live.memory_type_index as usize];
915 Allocation {
916 handle: live.handle,
917 size: live.size,
918 reserved_size: live.reserved_size,
919 offset: live.offset,
920 block_size: live.block_size,
921 memory_type_index: live.memory_type_index,
922 heap_index: memory.heap_index,
923 flags: memory.property_flags,
924 atom: self.selector.atom,
925 owner: self.owner,
926 serial: live.serial,
927 }
928 }
929
930 fn validate(&self, allocation: &Allocation) -> Result<&Live<'device>, AllocError> {
931 self.ensure_open()?;
932 if self.owner != allocation.owner {
933 return Err(AllocError::WrongAllocator);
934 }
935 self
936 .live
937 .get(&allocation.serial)
938 .filter(|live| live.serial == allocation.serial && live.handle == allocation.handle)
939 .ok_or(AllocError::UnknownAllocation)
940 }
941
942 fn reserve(&mut self) -> Result<(), AllocError> {
943 self.ensure_open()?;
944 self.ensure_allocation()?;
945 if let Some(shared) = self.parallel_state() {
946 self.next_serial = shared.reserve_publication()?;
947 }
948 self
949 .next_serial
950 .checked_add(1)
951 .ok_or(AllocError::SerialExhausted)?;
952 self
953 .live
954 .try_reserve(1)
955 .map_err(|_| AllocError::HostAllocationFailed)
956 }
957
958 fn validate_size(&self, size: u64) -> Result<(), AllocError> {
959 if let Some(limit) = self.allocation_size_limit
960 && size > limit
961 {
962 return Err(AllocError::AllocationTooLarge {
963 requested: size,
964 limit,
965 });
966 }
967 Ok(())
968 }
969
970 fn check_native(&self, size: u64, index: u32) -> Result<(), AllocError> {
971 self.validate_size(size)?;
972 if self.native_count >= self.allocation_limit as usize {
973 return Err(AllocError::AllocationLimit);
974 }
975 let heap = self.selector.properties.memory_types[index as usize].heap_index as usize;
976 let available = self.heap_limits[heap]
977 .unwrap_or(self.selector.properties.memory_heaps[heap].size)
978 - self.heap_bytes[heap];
979 if size > available {
980 return Err(AllocError::OutOfSpace);
981 }
982 self
983 .native_bytes
984 .checked_add(size)
985 .ok_or(AllocError::InvalidRange)?;
986 Ok(())
987 }
988
989 fn add_native(&mut self, size: u64, index: u32, memory: vk::DeviceMemory) {
990 self.native_handles.insert_reserved(memory.as_raw(), ());
991 let heap = self.selector.properties.memory_types[index as usize].heap_index as usize;
992 self.native_count += 1;
993 self.native_bytes += size;
994 self.heap_bytes[heap] += size;
995 if let Some(callbacks) = self.device_memory_callbacks
996 && let Some(callback) = callbacks.allocated
997 {
998 unsafe {
1000 callback(
1001 self.device.handle(),
1002 memory,
1003 size,
1004 index,
1005 callbacks.p_user_data,
1006 )
1007 };
1008 }
1009 }
1010
1011 fn remove_native(&mut self, size: u64, index: u32, memory: vk::DeviceMemory) {
1012 self.native_handles.remove(&memory.as_raw());
1013 #[cfg(all(windows, feature = "platform-win32"))]
1014 {
1015 let before = self.external_handles.len();
1016 self.external_handles.retain(|entry| entry.memory != memory);
1017 if let Some(shared) = self.parallel_state() {
1018 shared.external_removed(before - self.external_handles.len());
1019 }
1020 }
1021 let heap = self.selector.properties.memory_types[index as usize].heap_index as usize;
1022 if let Some(shared) = self.parallel_state() {
1023 shared.release_native(size, heap);
1024 }
1025 self.native_count -= 1;
1026 self.native_bytes -= size;
1027 self.heap_bytes[heap] -= size;
1028 if let Some(callbacks) = self.device_memory_callbacks
1029 && let Some(callback) = callbacks.freed
1030 {
1031 unsafe {
1033 callback(
1034 self.device.handle(),
1035 memory,
1036 size,
1037 index,
1038 callbacks.p_user_data,
1039 )
1040 };
1041 }
1042 }
1043
1044 fn mapping_owner(&self, allocation: &Allocation) -> &MappingOwner<'device> {
1045 if self.live[&allocation.serial].placement.is_some() {
1046 &self
1047 .blocks
1048 .iter()
1049 .find(|block| block.handle == allocation.handle)
1050 .expect("live block")
1051 .mapping
1052 } else {
1053 &self.live[&allocation.serial].mapping
1054 }
1055 }
1056
1057 fn mapping_owner_mut(&mut self, allocation: &Allocation) -> &mut MappingOwner<'device> {
1058 if self.live[&allocation.serial].placement.is_some() {
1059 &mut self
1060 .blocks
1061 .iter_mut()
1062 .find(|block| block.handle == allocation.handle)
1063 .expect("live block")
1064 .mapping
1065 } else {
1066 &mut self
1067 .live
1068 .get_mut(&allocation.serial)
1069 .expect("validated live region")
1070 .mapping
1071 }
1072 }
1073
1074 fn mapping(&self, allocation: &Allocation) -> MappingRead<'_> {
1075 self.mapping_owner(allocation).read()
1076 }
1077
1078 fn mapping_mut(&mut self, allocation: &Allocation) -> MappingWrite<'_> {
1079 self.mapping_owner_mut(allocation).write()
1080 }
1081
1082 fn contains_memory(&self, memory: vk::DeviceMemory) -> bool {
1083 self.native_handles.get(&memory.as_raw()).is_some()
1084 }
1085
1086 fn buffer_route(&self) -> Result<RequirementsRoute, AllocError> {
1087 self.requirements_route(false)
1088 }
1089
1090 #[inline]
1091 fn requirements_route(&self, image: bool) -> Result<RequirementsRoute, AllocError> {
1092 let capabilities =
1093 self
1094 .device
1095 .as_device()
1096 .capabilities()
1097 .ok_or(AllocError::UnsupportedCapability(
1098 "managed resources require device creation metadata",
1099 ))?;
1100 if capabilities.api_version() < vk::API_VERSION_1_1 {
1101 return self.extension_requirements_route(image);
1102 }
1103 let fp = self.device.fp();
1104 if image {
1105 require(
1106 fp.get_image_memory_requirements2.is_some(),
1107 "vkGetImageMemoryRequirements2",
1108 )?;
1109 } else {
1110 require(
1111 fp.get_buffer_memory_requirements2.is_some(),
1112 "vkGetBufferMemoryRequirements2",
1113 )?;
1114 }
1115 Ok(RequirementsRoute::Core)
1116 }
1117 #[cold]
1119 fn extension_requirements_route(&self, image: bool) -> Result<RequirementsRoute, AllocError> {
1120 let fp = self.device.fp();
1121 let (khr, legacy) = if image {
1122 (
1123 (
1124 fp.get_image_memory_requirements2_khr.is_some(),
1125 "vkGetImageMemoryRequirements2KHR",
1126 ),
1127 (
1128 fp.get_image_memory_requirements.is_some(),
1129 "vkGetImageMemoryRequirements",
1130 ),
1131 )
1132 } else {
1133 (
1134 (
1135 fp.get_buffer_memory_requirements2_khr.is_some(),
1136 "vkGetBufferMemoryRequirements2KHR",
1137 ),
1138 (
1139 fp.get_buffer_memory_requirements.is_some(),
1140 "vkGetBufferMemoryRequirements",
1141 ),
1142 )
1143 };
1144 if super::resource::extension(self.device.as_device(), c"VK_KHR_dedicated_allocation") {
1145 if !super::resource::extension(self.device.as_device(), c"VK_KHR_get_memory_requirements2") {
1146 return Err(AllocError::UnsupportedCapability(
1147 "dedication requires VK_KHR_get_memory_requirements2",
1148 ));
1149 }
1150 require(khr.0, khr.1)?;
1151 Ok(RequirementsRoute::Khr)
1152 } else {
1153 require(legacy.0, legacy.1)?;
1154 Ok(RequirementsRoute::Legacy)
1155 }
1156 }
1157}
1158
1159fn require(present: bool, name: &'static str) -> Result<(), AllocError> {
1160 if present {
1161 Ok(())
1162 } else {
1163 Err(CallError::MissingCommand(name).into())
1164 }
1165}
1166
1167pub fn close(allocator: &mut Allocator<'_>) -> Result<(), AllocError> {
1169 allocator.ensure_global()?;
1170 if !allocator.live.is_empty() {
1171 return Err(AllocError::LiveAllocations);
1172 }
1173 if !allocator.pools.is_empty() {
1174 return Err(AllocError::LivePools);
1175 }
1176 let mut snapshot = budget::budget_snapshot(allocator);
1177 pool::trim_blocks(allocator, &mut snapshot, true);
1178 allocator.closed = true;
1179 Ok(())
1180}
1181
1182pub unsafe fn allocate(
1190 allocator: &mut Allocator<'_>,
1191 request: &AllocRequest,
1192) -> Result<Allocation, AllocError> {
1193 allocator.ensure_default()?;
1194 let serial = unsafe {
1196 allocate_storage(
1197 allocator,
1198 request,
1199 StorageOptions {
1200 chain: &super::chain::ChainData::EMPTY,
1201 dedicated: None,
1202 device_address: allocator.buffer_device_address,
1203 priority: MemoryPriority::default(),
1204 within_budget: false,
1205 },
1206 vk::MemoryPropertyFlags::empty(),
1207 )
1208 }?;
1209 let mut allocation = allocator.published(serial);
1210 if let Err(error) = unsafe { guard::initialize(allocator, &allocation) } {
1212 unsafe { rollback_allocation(allocator, &mut allocation) }
1214 .expect("unpublished debug initialization rollback");
1215 return Err(error);
1216 }
1217 Ok(allocation)
1218}
1219
1220#[derive(Clone, Copy)]
1221struct StorageOptions<'a> {
1222 chain: &'a super::chain::ChainData,
1223 dedicated: Option<&'a vk::MemoryDedicatedAllocateInfo<'a>>,
1224 device_address: bool,
1225 priority: MemoryPriority,
1226 within_budget: bool,
1227}
1228
1229unsafe fn allocate_native(
1230 allocator: &mut Allocator<'_>,
1231 size: u64,
1232 index: u32,
1233 storage: StorageOptions<'_>,
1234) -> Result<vk::DeviceMemory, AllocError> {
1235 if storage.within_budget {
1236 budget::admit_native(allocator, index, size)?;
1237 }
1238 external::allocation_exports(allocator, index, *storage.chain)?;
1239 let export = vk::ExportMemoryAllocateInfo {
1240 handle_types: allocator.external_types[index as usize],
1241 p_next: storage.chain.next,
1242 ..Default::default()
1243 };
1244 let tail = if export.handle_types.as_raw() != 0 {
1245 std::ptr::from_ref(&export).cast()
1246 } else {
1247 storage.chain.next
1248 };
1249 let mut dedicated = vk::MemoryDedicatedAllocateInfo {
1250 buffer: storage
1251 .dedicated
1252 .map_or(vk::Buffer::NULL, |info| info.buffer),
1253 image: storage.dedicated.map_or(vk::Image::NULL, |info| info.image),
1254 ..Default::default()
1255 };
1256 dedicated.p_next = tail;
1257 let tail = if storage.dedicated.is_some() {
1258 std::ptr::from_ref(&dedicated).cast()
1259 } else {
1260 tail
1261 };
1262 if let Some(flags) = storage.chain.flags
1263 && storage.device_address
1264 && flags.as_raw() & vk::MemoryAllocateFlagBits::DEVICE_ADDRESS_BIT.as_raw() == 0
1265 {
1266 return Err(AllocError::InvalidRequest(
1267 "explicit allocation flags omit required device address",
1268 ));
1269 }
1270 let flags = vk::MemoryAllocateFlagsInfo {
1271 flags: vk::MemoryAllocateFlagBits::DEVICE_ADDRESS_BIT,
1272 p_next: tail,
1273 ..Default::default()
1274 };
1275 let next = if storage.device_address && storage.chain.flags.is_none() {
1276 std::ptr::from_ref(&flags).cast()
1277 } else {
1278 tail
1279 };
1280 let priority = vk::MemoryPriorityAllocateInfoEXT {
1281 priority: storage.priority.get(),
1282 p_next: next,
1283 ..Default::default()
1284 };
1285 let info = vk::MemoryAllocateInfo {
1286 allocation_size: if storage.chain.import_kind == super::chain::ImportKind::MetalTexture {
1287 0
1288 } else {
1289 size
1290 },
1291 memory_type_index: index,
1292 p_next: if allocator.memory_priority {
1293 std::ptr::from_ref(&priority).cast()
1294 } else {
1295 next
1296 },
1297 ..Default::default()
1298 };
1299 allocator.native_handles.try_reserve(1)?;
1302 let reservation = allocator
1303 .parallel_state()
1304 .map(|shared| {
1305 let heap = allocator.selector.properties.memory_types[index as usize].heap_index as usize;
1306 shared.reserve_native(size, heap, storage.within_budget)
1307 })
1308 .transpose()?;
1309 let handle = unsafe { allocator.device.allocate_memory(&info, allocator.callbacks) }?;
1311 if let Some(reservation) = reservation
1312 && !handle.is_null()
1313 {
1314 reservation.commit();
1315 }
1316 Ok(handle)
1317}
1318
1319unsafe fn allocate_storage(
1320 allocator: &mut Allocator<'_>,
1321 request: &AllocRequest,
1322 storage: StorageOptions<'_>,
1323 avoided: vk::MemoryPropertyFlags,
1324) -> Result<u64, AllocError> {
1325 allocator.ensure_open()?;
1326 let imported = storage.chain.import_info;
1327 if let Some(info) = imported {
1328 if request.size > info.allocation_size {
1329 return Err(AllocError::InvalidRequest(
1330 "resource exceeds imported payload",
1331 ));
1332 }
1333 if storage.chain.import_kind == super::chain::ImportKind::Collection
1334 && request.size != info.allocation_size
1335 {
1336 return Err(AllocError::InvalidRequest(
1337 "collection requires exact resource size",
1338 ));
1339 }
1340 if let Some(resource) = import_resource(info.resource)
1341 && binding::associated(allocator, resource)
1342 {
1343 return Err(AllocError::InvalidRequest(
1344 "imported resource already has an allocation",
1345 ));
1346 }
1347 }
1348 let claim = imported
1349 .and_then(|info| import_resource(info.resource))
1350 .map(|resource| parallel::claim(allocator, resource, None, None))
1351 .transpose()?
1352 .flatten();
1353 let request = &AllocRequest {
1354 size: imported.map_or(request.size, |info| info.allocation_size),
1355 memory_type_bits: request.memory_type_bits
1356 & imported.map_or(u32::MAX, |info| info.memory_type_bits),
1357 ..*request
1358 };
1359 allocator.validate_size(request.size)?;
1360 if !allocator.protected_memory
1361 && request.required_flags.as_raw() & vk::MemoryPropertyFlagBits::PROTECTED_BIT.as_raw() != 0
1362 {
1363 return Err(AllocError::UnsupportedCapability("protected allocation"));
1364 }
1365 if allocator.native_count >= allocator.allocation_limit as usize {
1366 return Err(AllocError::AllocationLimit);
1367 }
1368 allocator.reserve()?;
1369 if let Some(resource) = imported.and_then(|info| import_resource(info.resource))
1370 && allocator.parallel.is_none()
1371 {
1372 allocator.associated.try_reserve_for(resource)?;
1373 }
1374 let mut bits = request.memory_type_bits;
1375 let mut last = None;
1376 loop {
1377 let candidate = AllocRequest {
1378 memory_type_bits: bits,
1379 ..*request
1380 };
1381 let index = match allocator
1382 .selector
1383 .find_memory_type_with_preferences(&candidate, avoided)
1384 {
1385 Ok(index) => index,
1386 Err(error) => return Err(last.unwrap_or(error)),
1387 };
1388 bits &= !(1 << index);
1389 if let Err(error) = allocator.check_native(request.size, index) {
1390 if error == AllocError::OutOfSpace {
1391 last = Some(error);
1392 continue;
1393 }
1394 return Err(error);
1395 }
1396 let exports = external::allocation_exports(allocator, index, *storage.chain)?;
1397 let handle = match unsafe { allocate_native(allocator, request.size, index, storage) } {
1399 Ok(handle) => handle,
1400 Err(error)
1401 if !storage.chain.imports
1402 && (matches!(&error, AllocError::Call(native) if recoverable(native))
1403 || error == AllocError::OutOfSpace) =>
1404 {
1405 last = Some(error);
1406 continue;
1407 }
1408 Err(error) => return Err(error),
1409 };
1410 if handle.is_null() || allocator.contains_memory(handle) {
1411 return Err(
1412 CallError::InvalidOutput("allocator returned a null or already-live memory handle").into(),
1413 );
1414 }
1415 allocator.add_native(request.size, index, handle);
1416 let serial = publish(
1417 allocator,
1418 Region {
1419 handle,
1420 index,
1421 size: request.size,
1422 reserved_size: request.size,
1423 offset: 0,
1424 alignment: request.alignment,
1425 block_size: request.size,
1426 placement: None,
1427 pool: None,
1428 class: ResourceClass::Unknown,
1429 },
1430 );
1431 let live = allocator
1432 .live
1433 .get_mut(&serial)
1434 .expect("new separate allocation");
1435 live.imported = storage.chain.imports;
1436 live.resource = imported.and_then(|info| import_resource(info.resource));
1437 live.export_types = exports;
1438 if let Some(claim) = claim {
1439 claim.commit(serial);
1440 } else if let Some(resource) = live.resource {
1441 allocator.associated.record(resource, None, serial);
1442 }
1443 return Ok(serial);
1444 }
1445}
1446
1447fn recoverable(error: &CallError) -> bool {
1448 matches!(error, CallError::Vulkan(result) if *result == vk::Result::ERROR_OUT_OF_DEVICE_MEMORY || *result == vk::Result::ERROR_OUT_OF_HOST_MEMORY)
1449}
1450
1451struct Region {
1453 handle: vk::DeviceMemory,
1454 index: u32,
1455 size: u64,
1456 reserved_size: u64,
1457 offset: u64,
1458 alignment: u64,
1459 block_size: u64,
1460 placement: Option<Placement>,
1461 pool: Option<u64>,
1462 class: ResourceClass,
1463}
1464
1465fn memory_flags(allocator: &Allocator<'_>, index: u32) -> vk::MemoryPropertyFlags {
1466 allocator.selector.properties.memory_types[index as usize].property_flags
1467}
1468
1469#[inline]
1470fn publish(allocator: &mut Allocator<'_>, region: Region) -> u64 {
1472 let Region {
1473 handle,
1474 index,
1475 size,
1476 reserved_size,
1477 offset,
1478 alignment,
1479 block_size,
1480 placement,
1481 pool,
1482 class,
1483 } = region;
1484 let serial = allocator.next_serial;
1485 allocator.next_serial += 1;
1486 if let Some(shared) = allocator.parallel_state() {
1487 shared.publish();
1488 }
1489 allocator.budget_operations = allocator.budget_operations.saturating_add(1);
1490 let guard_size = if allocator.debug_settings.margin != 0
1491 && placement.is_some()
1492 && guard::eligible(memory_flags(allocator, index))
1493 {
1494 allocator.debug_settings.margin
1495 } else {
1496 0
1497 };
1498 let guard_offset = if allocator.debug_settings.margin != 0
1499 && placement.is_some()
1500 && guard::eligible(memory_flags(allocator, index))
1501 {
1502 size.checked_add(3).expect("preflighted guard extent") & !3
1503 } else {
1504 0
1505 };
1506 allocator.live.insert_new_reserved_with(serial, || Live {
1507 serial,
1508 pool,
1509 handle,
1510 size,
1511 reserved_size,
1512 block_size,
1513 memory_type_index: index,
1514 offset,
1515 alignment,
1516 map_count: 0,
1517 persistent: false,
1518 resource: None,
1519 bound: false,
1520 name: None,
1521 user_data: 0,
1522 pinned: false,
1523 can_alias: false,
1524 imported: false,
1525 plane: None,
1526 export_types: vk::ExternalMemoryHandleTypeFlags::empty(),
1527 guard_size,
1528 guard_offset,
1529 debug_ready: false,
1530 class,
1531 placement,
1532 mapping: MappingOwner::default(),
1533 native_users: 0,
1534 pending_binding: None,
1535 });
1536 serial
1537}
1538
1539unsafe fn rollback_allocation(
1540 allocator: &mut Allocator<'_>,
1541 allocation: &mut Allocation,
1542) -> Result<(), AllocError> {
1543 allocator.validate(allocation)?;
1544 allocator
1547 .live
1548 .get_mut(&allocation.serial)
1549 .expect("unpublished allocation")
1550 .debug_ready = false;
1551 unsafe { free_region(allocator, allocation, true) }
1553}
1554
1555pub unsafe fn free(
1560 allocator: &mut Allocator<'_>,
1561 allocation: &mut Allocation,
1562) -> Result<(), AllocError> {
1563 unsafe { free_region(allocator, allocation, false) }
1565}
1566
1567unsafe fn free_region(
1571 allocator: &mut Allocator<'_>,
1572 allocation: &mut Allocation,
1573 release_explicit: bool,
1574) -> Result<(), AllocError> {
1575 allocator.ensure_allocation()?;
1576 let live = allocator.validate(allocation)?;
1577 if live.native_users != 0 {
1578 return Err(AllocError::InvalidRequest("native operation in progress"));
1579 }
1580 if live.map_count != 0 && !release_explicit {
1581 return Err(AllocError::AlreadyMapped);
1582 }
1583 let resource = live.resource;
1584 let plane = live.plane;
1585 let pending = live.pending_binding;
1586 let placement = live.placement;
1587 let (map_count, persistent) = (live.map_count, live.persistent);
1588 unsafe { guard::before_free(allocator, allocation) }?;
1592 let holds = map_count
1593 .checked_add(u32::from(persistent))
1594 .ok_or(AllocError::InvalidRange)?;
1595 if holds != 0 {
1596 let device = allocator.device.clone();
1597 unsafe {
1600 allocator
1601 .mapping_mut(allocation)
1602 .release_many(device.as_device(), allocation.handle, holds)
1603 }?;
1604 }
1605 if let Some(placement) = placement {
1606 let block_index = allocator
1607 .blocks
1608 .iter()
1609 .position(|b| b.handle == allocation.handle)
1610 .expect("live block");
1611 let domain = BlockDomain::new_from_block(&allocator.blocks[block_index]);
1612 let block = &mut allocator.blocks[block_index];
1613 unsafe {
1615 block
1616 .mapping
1617 .write()
1618 .post_free(allocator.device.as_device(), block.handle)
1619 }?;
1620 block
1621 .metadata
1622 .free(placement)
1623 .expect("validated private placement");
1624 if block.defragmentation_hold {
1625 } else if block.automatic || block.pool.is_some() {
1627 pool::release_block(allocator, block_index);
1628 } else if block.metadata.is_empty() {
1629 debug_assert_eq!(block.mapping.read().references, 0);
1630 unsafe {
1632 allocator.blocks[block_index]
1633 .mapping
1634 .write()
1635 .clear_cache(allocator.device.as_device(), allocation.handle)
1636 }?;
1637 unsafe {
1639 allocator
1640 .device
1641 .as_device()
1642 .free_memory(allocation.handle, allocator.callbacks)
1643 }?;
1644 let block = allocator.blocks.remove(block_index);
1645 allocator.remove_native(block.size, block.memory_type_index, block.handle);
1646 }
1647 domain.incrementally_sort(&mut allocator.blocks);
1648 } else {
1649 unsafe {
1651 allocator
1652 .device
1653 .as_device()
1654 .free_memory(allocation.handle, allocator.callbacks)
1655 }?;
1656 allocator.remove_native(
1657 allocation.block_size,
1658 allocation.memory_type_index,
1659 allocation.handle,
1660 );
1661 }
1662 allocator.live.discard(&allocation.serial);
1663 if let Some(resource) = resource {
1664 allocator
1665 .associated
1666 .release(resource, plane, allocation.serial);
1667 }
1668 if let Some((resource, plane)) = pending {
1669 allocator
1670 .associated
1671 .release(resource, plane, allocation.serial);
1672 }
1673 if let Some(shared) = allocator.parallel_state() {
1674 shared.retire(resource, plane, allocation.serial);
1675 }
1676 allocator.budget_operations = allocator.budget_operations.saturating_add(1);
1677 allocation.handle = vk::DeviceMemory::NULL;
1678 Ok(())
1679}
1680
1681pub unsafe fn map(
1687 allocator: &mut Allocator<'_>,
1688 allocation: &Allocation,
1689) -> Result<*mut std::ffi::c_void, AllocError> {
1690 let operation = native::MapOperation::prepare(allocator, allocation, true)?;
1691 let result = unsafe { operation.execute(&mut allocator.mapping_mut(allocation)) };
1693 operation.commit(allocator, result)
1694}
1695
1696pub unsafe fn unmap(
1700 allocator: &mut Allocator<'_>,
1701 allocation: &Allocation,
1702) -> Result<(), AllocError> {
1703 let operation = native::MapOperation::prepare(allocator, allocation, false)?;
1704 let result = unsafe { operation.execute(&mut allocator.mapping_mut(allocation)) };
1706 operation.commit(allocator, result).map(|_| ())
1707}
1708
1709pub fn mapped_pointer(
1712 allocator: &Allocator<'_>,
1713 allocation: &Allocation,
1714) -> Result<Option<*mut std::ffi::c_void>, AllocError> {
1715 let live = allocator.validate(allocation)?;
1716 if live.map_count == 0 && !live.persistent {
1717 return Ok(None);
1718 }
1719 Ok(Some(
1720 allocator
1721 .mapping(allocation)
1722 .pointer
1723 .cast::<u8>()
1724 .wrapping_add(allocation.offset as usize)
1725 .cast(),
1726 ))
1727}
1728
1729pub fn allocation_info(
1738 allocator: &Allocator<'_>,
1739 allocation: &Allocation,
1740) -> Result<AllocationInfo, AllocError> {
1741 let mapped_pointer = mapped_pointer(allocator, allocation)?;
1742 let live = allocator.validate(allocation)?;
1743 Ok(AllocationInfo {
1744 device_memory: allocation.handle,
1745 offset: allocation.offset,
1746 size: allocation.size,
1747 reserved_size: allocation.reserved_size,
1748 memory_type_index: allocation.memory_type_index,
1749 heap_index: allocation.heap_index,
1750 memory_properties: allocation.flags,
1751 mapped_pointer,
1752 map_count: live.map_count,
1753 native_size: allocation.block_size,
1754 is_separate: live.placement.is_none(),
1755 user_data: live.user_data,
1756 })
1757}
1758
1759pub fn statistics(allocator: &Allocator<'_>) -> AllocatorStats {
1761 AllocatorStats {
1762 allocation_count: allocator.live.len(),
1763 native_allocation_count: allocator.native_count,
1764 pooled_block_count: allocator.blocks.len(),
1765 allocation_bytes: allocator.live.values().map(|v| v.size).sum(),
1766 reserved_bytes: allocator.live.values().map(|v| v.reserved_size).sum(),
1767 native_bytes: allocator.native_bytes,
1768 mapped_native_count: allocator
1769 .blocks
1770 .iter()
1771 .filter(|b| !b.mapping.read().pointer.is_null())
1772 .count()
1773 + allocator
1774 .live
1775 .values()
1776 .filter(|v| v.placement.is_none() && v.mapping.read().references != 0)
1777 .count(),
1778 }
1779}
1780
1781pub unsafe fn flush(
1785 allocator: &Allocator<'_>,
1786 allocation: &Allocation,
1787 offset: u64,
1788 size: u64,
1789) -> Result<(), AllocError> {
1790 let live = allocator.validate(allocation)?;
1791 if live.map_count == 0 && !live.persistent {
1792 return Err(AllocError::NotMapped);
1793 }
1794 if let Some(range) = allocation.mapped_range(offset, size)? {
1795 unsafe { allocator.device.flush_mapped_memory_ranges(&[range]) }?;
1797 }
1798 Ok(())
1799}
1800
1801pub unsafe fn invalidate(
1805 allocator: &Allocator<'_>,
1806 allocation: &Allocation,
1807 offset: u64,
1808 size: u64,
1809) -> Result<(), AllocError> {
1810 let live = allocator.validate(allocation)?;
1811 if live.map_count == 0 && !live.persistent {
1812 return Err(AllocError::NotMapped);
1813 }
1814 if let Some(range) = allocation.mapped_range(offset, size)? {
1815 unsafe { allocator.device.invalidate_mapped_memory_ranges(&[range]) }?;
1817 }
1818 Ok(())
1819}
1820
1821pub unsafe fn create_buffer(
1834 allocator: &mut Allocator<'_>,
1835 info: &vk::BufferCreateInfo,
1836 required: vk::MemoryPropertyFlags,
1837 preferred: vk::MemoryPropertyFlags,
1838) -> Result<(vk::Buffer, Allocation), AllocError> {
1839 let options = AllocationCreateInfo {
1840 required_flags: required,
1841 preferred_flags: preferred,
1842 ..Default::default()
1843 };
1844 unsafe { create_buffer_with_options(allocator, info, &options) }
1846}
1847
1848pub unsafe fn create_buffer_with_options(
1861 allocator: &mut Allocator<'_>,
1862 info: &vk::BufferCreateInfo,
1863 options: &AllocationCreateInfo,
1864) -> Result<(vk::Buffer, Allocation), AllocError> {
1865 let device_address = unsafe { request::validate_buffer(allocator, info) }?;
1867 allocator.validate_size(info.size)?;
1868 let (mut required, preferred, avoided) =
1869 super::policy::preferences(options, info.usage.as_raw() & !3 != 0, allocator.integrated)?;
1870 required = request::resource_properties(
1871 allocator,
1872 info.flags.as_raw() & vk::BufferCreateFlagBits::PROTECTED_BIT.as_raw() != 0,
1873 required,
1874 )?;
1875 request::validate_options(allocator, options, required)?;
1876 let filtered;
1879 let options = if info.p_next.is_null() {
1880 options
1881 } else {
1882 filtered = unsafe { external::resource_options(allocator, options, info.p_next) }?;
1884 filtered.as_ref()
1885 };
1886 if options.pool.is_none()
1887 && options.policy == AllocationPolicy::Separate
1888 && allocator.native_count >= allocator.allocation_limit as usize
1889 {
1890 return Err(AllocError::AllocationLimit);
1891 }
1892 if options.persistent_map {
1893 require(allocator.device.fp().map_memory.is_some(), "vkMapMemory")?;
1894 require(
1895 allocator.device.fp().unmap_memory.is_some(),
1896 "vkUnmapMemory",
1897 )?;
1898 }
1899 allocator.buffer_route()?;
1900 let fp = allocator.device.fp();
1901 require(fp.create_buffer.is_some(), "vkCreateBuffer")?;
1902 require(fp.destroy_buffer.is_some(), "vkDestroyBuffer")?;
1903 require(fp.bind_buffer_memory.is_some(), "vkBindBufferMemory")?;
1904 allocator.reserve()?;
1905 let buffer = unsafe { allocator.device.create_buffer(info, allocator.callbacks) }?;
1907 if buffer.is_null() {
1908 return Err(CallError::InvalidOutput("vkCreateBuffer returned a null handle").into());
1909 }
1910 if binding::associated(allocator, Resource::Buffer(buffer)) {
1911 return Err(CallError::InvalidOutput("vkCreateBuffer returned an already-live handle").into());
1912 }
1913 let result = unsafe {
1915 binding::allocate_prepared_resource(
1916 allocator,
1917 Resource::Buffer(buffer),
1918 options,
1919 (required, preferred, avoided),
1920 ResourceClass::Buffer,
1921 device_address,
1922 )
1923 };
1924
1925 let mut allocation = match result {
1926 Ok(serial) => allocator.published(serial),
1927 Err(error) => {
1928 unsafe { allocator.device.destroy_buffer(buffer, allocator.callbacks) }
1930 .expect("preflighted cleanup");
1931 return Err(error);
1932 }
1933 };
1934 let result =
1937 unsafe { binding::persistent(allocator, &allocation, options) }.and_then(|()| unsafe {
1938 binding::bind_prepared(allocator, Resource::Buffer(buffer), &allocation)
1939 });
1940 if let Err(error) = result {
1941 unsafe {
1943 allocator.release_resource_claim(&allocation);
1944 allocator
1945 .device
1946 .as_device()
1947 .destroy_buffer(buffer, allocator.callbacks)
1948 .expect("preflighted cleanup");
1949 rollback_allocation(allocator, &mut allocation).expect("preflighted allocation rollback");
1950 }
1951 return Err(error);
1952 }
1953
1954 Ok((buffer, allocation))
1955}
1956
1957fn validate_policy(policy: AllocationPolicy) -> Result<(), AllocError> {
1958 match policy {
1959 AllocationPolicy::Pooled { block_size: 0 } => Err(AllocError::InvalidRequest(
1960 "pooled block size must be nonzero",
1961 )),
1962 AllocationPolicy::Adaptive {
1963 block_size,
1964 min_block_size,
1965 } if !block_size.is_power_of_two()
1966 || !min_block_size.is_power_of_two()
1967 || min_block_size > block_size =>
1968 {
1969 Err(AllocError::InvalidRequest(
1970 "adaptive block bounds must be ordered powers of two",
1971 ))
1972 }
1973 _ => Ok(()),
1974 }
1975}
1976
1977#[derive(Clone, Copy, PartialEq, Eq)]
1978enum Dedication {
1979 None,
1980 Preferred,
1981 Required,
1982}
1983
1984impl Dedication {
1985 fn new(requirements: &vk::MemoryDedicatedRequirements) -> Self {
1986 if requirements.requires_dedicated_allocation != 0 {
1987 Self::Required
1988 } else if requirements.prefers_dedicated_allocation != 0 {
1989 Self::Preferred
1990 } else {
1991 Self::None
1992 }
1993 }
1994}
1995
1996#[derive(Clone, Copy)]
1997struct BlockOptions {
1998 selected_type: Option<u32>,
2001 algorithm: AllocationAlgorithm,
2003 allow_ring: bool,
2004 automatic: bool,
2005 block_size: u64,
2006 strategy: super::PlacementStrategy,
2007 class: ResourceClass,
2008 grow: bool,
2009 pool: Option<u64>,
2010 upper_address: bool,
2011 priority: MemoryPriority,
2012 within_budget: bool,
2013 prefer_mapped: bool,
2014}
2015
2016unsafe fn allocate_managed(
2019 allocator: &mut Allocator<'_>,
2020 request: &AllocRequest,
2021 storage: StorageOptions<'_>,
2022 dedication_policy: Dedication,
2023 options: &AllocationCreateInfo,
2024 class: ResourceClass,
2025 avoided: vk::MemoryPropertyFlags,
2026) -> Result<u64, AllocError> {
2027 let (pool_chain, priority) =
2030 options
2031 .pool
2032 .map_or((super::chain::ChainData::EMPTY, storage.priority), |pool| {
2033 let state = &allocator.pools[&pool.serial];
2034 (state.allocation_chain, state.info.priority)
2035 });
2036 let chain = if let Some(chain) = &options.memory_allocate_next {
2037 if !pool_chain.next.is_null() {
2038 return Err(AllocError::InvalidRequest(
2039 "allocation and pool chains cannot be combined",
2040 ));
2041 }
2042 &chain.data
2043 } else {
2044 &pool_chain
2045 };
2046 let mut storage = StorageOptions {
2047 chain,
2048 priority,
2049 ..storage
2050 };
2051 let mut imported_dedicated = vk::MemoryDedicatedAllocateInfo::new();
2052 if let Some(resource) = storage
2053 .chain
2054 .import_info
2055 .and_then(|info| import_resource(info.resource))
2056 {
2057 if options.can_alias {
2058 return Err(AllocError::InvalidRequest(
2059 "resource-specific imports cannot alias",
2060 ));
2061 }
2062 if !allocator
2063 .device
2064 .as_device()
2065 .capabilities()
2066 .is_some_and(|caps| caps.api_version() >= vk::API_VERSION_1_1)
2067 && !super::resource::extension(allocator.device.as_device(), c"VK_KHR_dedicated_allocation")
2068 {
2069 return Err(AllocError::UnsupportedCapability(
2070 "import dedication requires core 1.1 or KHR_dedicated_allocation",
2071 ));
2072 }
2073 if let Some(existing) = storage.dedicated {
2074 let matches = match resource {
2075 Resource::Buffer(buffer) => existing.buffer == buffer && existing.image.is_null(),
2076 Resource::Image(image) => existing.image == image && existing.buffer.is_null(),
2077 };
2078 if !matches {
2079 return Err(AllocError::WrongResource);
2080 }
2081 }
2082 match resource {
2083 Resource::Buffer(buffer) => imported_dedicated.buffer = buffer,
2084 Resource::Image(image) => imported_dedicated.image = image,
2085 }
2086 storage.dedicated = Some(&imported_dedicated);
2087 }
2088 if storage.chain.import_kind == super::chain::ImportKind::Host {
2089 if dedication_policy == Dedication::Required {
2090 return Err(AllocError::InvalidRequest(
2091 "host imports forbid required resource dedication",
2092 ));
2093 }
2094 storage.dedicated = None;
2095 }
2096 let class = if options.can_alias {
2097 ResourceClass::Unknown
2098 } else {
2099 class
2100 };
2101 let dedication_policy = if options.can_alias {
2102 if dedication_policy == Dedication::Required {
2103 return Err(AllocError::UnsupportedCapability(
2104 "required resource dedication cannot alias",
2105 ));
2106 }
2107 storage.dedicated = None;
2108 storage.device_address = allocator.buffer_device_address;
2109 Dedication::None
2110 } else {
2111 dedication_policy
2112 };
2113 let dedication_policy = if options.never_allocate && dedication_policy == Dedication::Preferred {
2114 Dedication::None
2115 } else {
2116 dedication_policy
2117 };
2118 if options.never_allocate && dedication_policy == Dedication::Required {
2119 return Err(AllocError::OutOfSpace);
2120 }
2121
2122 let forced_separate = options.dedicated_memory || options.memory_allocate_next.is_some();
2123 if forced_separate {
2124 if options.never_allocate || options.upper_address {
2125 return Err(AllocError::InvalidRequest(
2126 "separate allocation conflicts with reuse/upper placement",
2127 ));
2128 }
2129 if let Some(pool) = options.pool {
2130 if allocator.pools[&pool.serial].info.block_size != 0 {
2131 return Err(AllocError::UnsupportedCapability(
2132 "fixed pool cannot allocate separate storage",
2133 ));
2134 }
2135 let index = allocator.pools[&pool.serial].info.memory_type_index;
2136 let request = AllocRequest {
2137 memory_type_bits: request.memory_type_bits & (1 << index),
2138 ..*request
2139 };
2140 let serial = unsafe { allocate_storage(allocator, &request, storage, avoided) }?;
2142 allocator
2143 .live
2144 .get_mut(&serial)
2145 .expect("new separate allocation")
2146 .pool = Some(pool.serial);
2147 return Ok(serial);
2148 }
2149 return unsafe { allocate_storage(allocator, request, storage, avoided) };
2151 }
2152
2153 if options.pool.is_some() {
2154 return unsafe {
2156 pool::allocate(
2157 allocator,
2158 request,
2159 storage,
2160 dedication_policy,
2161 options,
2162 class,
2163 avoided,
2164 )
2165 };
2166 }
2167 if options.policy == AllocationPolicy::Automatic {
2168 return unsafe {
2170 pool::allocate_default(
2171 allocator,
2172 request,
2173 storage,
2174 dedication_policy,
2175 options,
2176 class,
2177 avoided,
2178 )
2179 };
2180 }
2181
2182 if dedication_policy == Dedication::None {
2183 match options.policy {
2184 AllocationPolicy::Pooled { block_size } if request.size <= block_size => {
2185 return unsafe {
2187 allocate_pooled(
2188 allocator,
2189 request,
2190 avoided,
2191 BlockOptions {
2192 selected_type: None,
2193 algorithm: AllocationAlgorithm::Tlsf,
2194 allow_ring: false,
2195 automatic: false,
2196 block_size,
2197 strategy: options.strategy,
2198 class,
2199 grow: !options.never_allocate,
2200 pool: None,
2201 upper_address: false,
2202 priority: options.priority,
2203 within_budget: options.within_budget,
2204 prefer_mapped: super::policy::prefers_mapped(options),
2205 },
2206 )
2207 };
2208 }
2209 AllocationPolicy::Adaptive {
2210 block_size,
2211 min_block_size,
2212 } => {
2213 for grow in [false, true] {
2214 if grow && options.never_allocate {
2215 break;
2216 }
2217 let mut size = block_size;
2218 while size >= request.size && size >= min_block_size {
2219 match unsafe {
2221 allocate_pooled(
2222 allocator,
2223 request,
2224 avoided,
2225 BlockOptions {
2226 selected_type: None,
2227 algorithm: AllocationAlgorithm::Tlsf,
2228 allow_ring: false,
2229 automatic: false,
2230 block_size: size,
2231 strategy: options.strategy,
2232 class,
2233 grow,
2234 pool: None,
2235 upper_address: false,
2236 priority: options.priority,
2237 within_budget: options.within_budget,
2238 prefer_mapped: super::policy::prefers_mapped(options),
2239 },
2240 )
2241 } {
2242 Ok(serial) => return Ok(serial),
2243 Err(
2244 AllocError::OutOfSpace
2245 | AllocError::AllocationLimit
2246 | AllocError::AllocationTooLarge { .. },
2247 ) => {}
2248 Err(AllocError::Call(error)) if recoverable(&error) => {}
2249 Err(error) => return Err(error),
2250 }
2251 if size == min_block_size {
2252 break;
2253 }
2254 size /= 2;
2255 }
2256 }
2257 }
2258 _ => {}
2259 }
2260 }
2261 if options.never_allocate {
2263 return Err(AllocError::OutOfSpace);
2264 }
2265 unsafe { allocate_storage(allocator, request, storage, avoided) }
2266}
2267
2268unsafe fn allocate_pooled(
2274 allocator: &mut Allocator<'_>,
2275 request: &AllocRequest,
2276 avoided: vk::MemoryPropertyFlags,
2277 options: BlockOptions,
2278) -> Result<u64, AllocError> {
2279 let BlockOptions {
2280 mut selected_type,
2281 algorithm,
2282 allow_ring,
2283 automatic,
2284 block_size,
2285 strategy,
2286 class,
2287 grow,
2288 pool,
2289 upper_address,
2290 priority,
2291 within_budget,
2292 prefer_mapped,
2293 } = options;
2294 let exact_size = !automatic;
2295 if exact_size {
2296 allocator.validate_size(block_size)?;
2297 }
2298 let mut bits = request.memory_type_bits;
2299 let mut last = None;
2300 loop {
2301 let candidate = AllocRequest {
2302 memory_type_bits: bits,
2303 ..*request
2304 };
2305 let index = if let Some(index) = selected_type.take() {
2306 index
2307 } else {
2308 match allocator
2309 .selector
2310 .find_memory_type_with_preferences(&candidate, avoided)
2311 {
2312 Ok(index) => index,
2313 Err(error) => return Err(last.unwrap_or(error)),
2314 }
2315 };
2316 bits &= !(1 << index);
2317 let flags = allocator.selector.properties.memory_types[index as usize].property_flags;
2318 let noncoherent = flags.as_raw()
2319 & (vk::MemoryPropertyFlagBits::HOST_VISIBLE_BIT.as_raw()
2320 | vk::MemoryPropertyFlagBits::HOST_COHERENT_BIT.as_raw())
2321 == vk::MemoryPropertyFlagBits::HOST_VISIBLE_BIT.as_raw();
2322 let atom = if noncoherent {
2323 allocator.selector.atom
2324 } else {
2325 1
2326 };
2327 let guard_size = if guard::eligible(flags) {
2328 allocator.debug_settings.margin
2329 } else {
2330 0
2331 };
2332 let data_extent = if guard_size != 0 {
2333 request
2334 .size
2335 .checked_add(3)
2336 .map(|size| size & !3)
2337 .ok_or(AllocError::InvalidRange)?
2338 } else {
2339 request.size
2340 };
2341 let alignment = request
2342 .alignment
2343 .max(atom)
2344 .max(if guard_size != 0 { 4 } else { 1 });
2345 if !alignment.is_power_of_two() || !request.alignment.is_power_of_two() || request.size == 0 {
2346 return Err(AllocError::InvalidRequest(
2347 "invalid native buffer requirements",
2348 ));
2349 }
2350 let reserved = data_extent
2351 .checked_add(guard_size)
2352 .and_then(|size| size.checked_add(atom - 1))
2353 .map(|v| v & !(atom - 1))
2354 .ok_or(AllocError::InvalidRange)?;
2355 if reserved > block_size {
2356 last = Some(AllocError::OutOfSpace);
2358 continue;
2359 }
2360 let domain = BlockDomain {
2361 memory_type_index: index,
2362 pool,
2363 automatic,
2364 priority,
2365 exact_size: exact_size.then_some(block_size),
2366 };
2367 let last_block = if algorithm == AllocationAlgorithm::Linear {
2371 allocator
2372 .blocks
2373 .iter()
2374 .rposition(|block| domain.contains(block))
2375 } else {
2376 None
2377 };
2378 let reverse = strategy == super::PlacementStrategy::MinTime;
2379 let mapping_groups = allocator.blocks.len() > 1
2380 && algorithm == AllocationAlgorithm::Tlsf
2381 && !reverse
2382 && flags.as_raw() & vk::MemoryPropertyFlagBits::HOST_VISIBLE_BIT.as_raw() != 0;
2383 for group in 0..if mapping_groups { 2 } else { 1 } {
2384 for step in 0..allocator.blocks.len() {
2385 let block_index = if reverse {
2386 allocator.blocks.len() - 1 - step
2387 } else {
2388 step
2389 };
2390 let block = &mut allocator.blocks[block_index];
2391 let is_mapped = block.mapping.is_mapped();
2392 if !domain.contains(block)
2393 || (algorithm == AllocationAlgorithm::Linear && Some(block_index) != last_block)
2394 || (mapping_groups && ((group == 0) != (prefer_mapped == is_mapped)))
2395 {
2396 continue;
2397 }
2398 let (placement, offset) = match block.metadata.allocate_resource(
2399 reserved,
2400 alignment,
2401 strategy,
2402 upper_address,
2403 class,
2404 ) {
2405 Ok(placement) => placement,
2406 Err(AllocError::OutOfSpace) => continue,
2407 Err(error) => return Err(error),
2408 };
2409 let handle = block.handle;
2410 let actual_size = block.size;
2411 block.mapping.write().post_alloc();
2414 domain.incrementally_sort(&mut allocator.blocks);
2415 return Ok(publish(
2416 allocator,
2417 Region {
2418 handle,
2419 index,
2420 size: request.size,
2421 reserved_size: reserved,
2422 offset,
2423 alignment,
2424 block_size: actual_size,
2425 placement: Some(placement),
2426 pool,
2427 class,
2428 },
2429 ));
2430 }
2431 }
2432 if let Some(pool) = pool {
2433 let max = allocator.pools[&pool].info.max_block_count;
2434 if max != 0
2435 && allocator
2436 .blocks
2437 .iter()
2438 .filter(|block| block.pool == Some(pool))
2439 .count()
2440 >= max
2441 {
2442 return Err(AllocError::OutOfSpace);
2443 }
2444 }
2445 if !grow {
2446 last = Some(AllocError::OutOfSpace);
2447 continue;
2448 }
2449 if let Err(error) = allocator.check_native(block_size, index) {
2450 if error == AllocError::OutOfSpace || error == AllocError::AllocationLimit {
2452 last = Some(error);
2453 continue;
2454 }
2455 return Err(error);
2456 }
2457 allocator
2458 .blocks
2459 .try_reserve(1)
2460 .map_err(|_| AllocError::HostAllocationFailed)?;
2461 let mut metadata = Metadata::new_native(
2462 block_size,
2463 algorithm,
2464 allow_ring,
2465 allocator.granularity,
2466 allocator.host,
2467 )?;
2468 let (placement, offset) =
2469 metadata.allocate_resource(reserved, alignment, strategy, upper_address, class)?;
2470 let chain = pool.map_or(Default::default(), |serial| {
2471 allocator.pools[&serial].allocation_chain
2472 });
2473 let export_types = external::allocation_exports(allocator, index, chain)?;
2474 let handle = match unsafe {
2476 allocate_native(
2477 allocator,
2478 block_size,
2479 index,
2480 StorageOptions {
2481 chain: &chain,
2482 dedicated: None,
2483 device_address: allocator.buffer_device_address,
2484 priority,
2485 within_budget,
2486 },
2487 )
2488 } {
2489 Ok(handle) => handle,
2490 Err(error)
2491 if matches!(&error, AllocError::Call(native) if recoverable(native))
2492 || error == AllocError::OutOfSpace =>
2493 {
2494 last = Some(error);
2495 continue;
2496 }
2497 Err(error) => return Err(error),
2498 };
2499 if handle.is_null() || allocator.contains_memory(handle) {
2500 return Err(
2501 CallError::InvalidOutput("allocator returned a null or already-live memory handle").into(),
2502 );
2503 }
2504 allocator.blocks.push_reserved(Block {
2505 defragmentation_hold: false,
2506 automatic,
2507 export_types,
2508 priority,
2509 pool,
2510 handle,
2511 size: block_size,
2512 memory_type_index: index,
2513 metadata,
2514 mapping: MappingOwner::new_with_hysteresis(allocator.mapping_hysteresis),
2515 });
2516 allocator.add_native(block_size, index, handle);
2517 allocator
2518 .blocks
2519 .last_mut()
2520 .expect("new pooled block")
2521 .mapping
2522 .write()
2523 .post_alloc();
2524 domain.incrementally_sort(&mut allocator.blocks);
2525 return Ok(publish(
2526 allocator,
2527 Region {
2528 handle,
2529 index,
2530 size: request.size,
2531 reserved_size: reserved,
2532 offset,
2533 alignment,
2534 block_size,
2535 placement: Some(placement),
2536 pool,
2537 class,
2538 },
2539 ));
2540 }
2541}
2542
2543pub unsafe fn destroy_buffer(
2548 allocator: &mut Allocator<'_>,
2549 buffer: vk::Buffer,
2550 allocation: &mut Allocation,
2551) -> Result<(), AllocError> {
2552 allocator.ensure_allocation()?;
2553 let live = allocator.validate(allocation)?;
2554 if live.native_users != 0 {
2555 return Err(AllocError::InvalidRequest("native operation in progress"));
2556 }
2557 if live.resource != Some(Resource::Buffer(buffer)) {
2558 return Err(AllocError::WrongResource);
2559 }
2560 if live.map_count != 0 {
2561 return Err(AllocError::AlreadyMapped);
2562 }
2563 let debug_ready = live.debug_ready;
2564 require(
2566 allocator.device.fp().destroy_buffer.is_some(),
2567 "vkDestroyBuffer",
2568 )?;
2569 require(allocator.device.fp().free_memory.is_some(), "vkFreeMemory")?;
2570 unsafe {
2572 guard::before_free(allocator, allocation)?;
2573 allocator.release_resource_claim(allocation);
2574 allocator
2575 .device
2576 .as_device()
2577 .destroy_buffer(buffer, allocator.callbacks)?;
2578 if debug_ready {
2580 allocator
2581 .live
2582 .get_mut(&allocation.serial)
2583 .expect("validated resource allocation")
2584 .debug_ready = false;
2585 }
2586 free(allocator, allocation)
2587 }
2588}
2589
2590#[cfg(test)]
2591#[path = "../../../test/rs/mem/test_lifecycle.rs"]
2592mod tests;
2593
2594pub unsafe fn create_image(
2598 allocator: &mut Allocator<'_>,
2599 info: &vk::ImageCreateInfo,
2600 required: vk::MemoryPropertyFlags,
2601 preferred: vk::MemoryPropertyFlags,
2602) -> Result<(vk::Image, Allocation), AllocError> {
2603 let options = AllocationCreateInfo {
2604 required_flags: required,
2605 preferred_flags: preferred,
2606 ..Default::default()
2607 };
2608 unsafe { create_image_with_options(allocator, info, &options) }
2610}
2611
2612pub unsafe fn create_image_with_options(
2624 allocator: &mut Allocator<'_>,
2625 info: &vk::ImageCreateInfo,
2626 options: &AllocationCreateInfo,
2627) -> Result<(vk::Image, Allocation), AllocError> {
2628 let class = unsafe { request::validate_image(allocator, info) }?;
2630 let (mut required, preferred, avoided) =
2631 super::policy::preferences(options, info.usage.as_raw() & !3 != 0, allocator.integrated)?;
2632 required = request::resource_properties(
2633 allocator,
2634 info.flags.as_raw() & vk::ImageCreateFlagBits::PROTECTED_BIT.as_raw() != 0,
2635 required,
2636 )?;
2637 request::validate_options(allocator, options, required)?;
2638 let filtered;
2641 let options = if info.p_next.is_null() {
2642 options
2643 } else {
2644 filtered = unsafe { external::resource_options(allocator, options, info.p_next) }?;
2646 filtered.as_ref()
2647 };
2648 allocator.requirements_route(true)?;
2649 let fp = allocator.device.fp();
2650 require(fp.create_image.is_some(), "vkCreateImage")?;
2651 require(fp.destroy_image.is_some(), "vkDestroyImage")?;
2652 require(fp.bind_image_memory.is_some(), "vkBindImageMemory")?;
2653 if options.persistent_map {
2654 require(fp.map_memory.is_some(), "vkMapMemory")?;
2655 require(fp.unmap_memory.is_some(), "vkUnmapMemory")?;
2656 }
2657 allocator.reserve()?;
2658 let image = unsafe { allocator.device.create_image(info, allocator.callbacks) }?;
2660 if image.is_null() {
2661 return Err(CallError::InvalidOutput("vkCreateImage returned a null handle").into());
2662 }
2663 if binding::associated(allocator, Resource::Image(image)) {
2664 return Err(CallError::InvalidOutput("vkCreateImage returned an already-live handle").into());
2665 }
2666 let result = unsafe {
2668 binding::allocate_prepared_resource(
2669 allocator,
2670 Resource::Image(image),
2671 options,
2672 (required, preferred, avoided),
2673 class,
2674 false,
2675 )
2676 };
2677
2678 let mut allocation = match result {
2679 Ok(serial) => allocator.published(serial),
2680 Err(error) => {
2681 unsafe { allocator.device.destroy_image(image, allocator.callbacks) }
2683 .expect("preflighted cleanup");
2684 return Err(error);
2685 }
2686 };
2687 let result =
2690 unsafe { binding::persistent(allocator, &allocation, options) }.and_then(|()| unsafe {
2691 binding::bind_prepared(allocator, Resource::Image(image), &allocation)
2692 });
2693
2694 if let Err(error) = result {
2695 unsafe {
2697 allocator.release_resource_claim(&allocation);
2698 allocator
2699 .device
2700 .as_device()
2701 .destroy_image(image, allocator.callbacks)
2702 .expect("preflighted cleanup");
2703 rollback_allocation(allocator, &mut allocation).expect("preflighted allocation rollback");
2704 }
2705 return Err(error);
2706 }
2707 Ok((image, allocation))
2708}
2709
2710pub unsafe fn destroy_image(
2715 allocator: &mut Allocator<'_>,
2716 image: vk::Image,
2717 allocation: &mut Allocation,
2718) -> Result<(), AllocError> {
2719 allocator.ensure_allocation()?;
2720 let live = allocator.validate(allocation)?;
2721 if live.native_users != 0 {
2722 return Err(AllocError::InvalidRequest("native operation in progress"));
2723 }
2724 if live.plane.is_some() {
2725 return Err(AllocError::InvalidRequest(
2726 "destroy a disjoint image once, then release every plane allocation",
2727 ));
2728 }
2729 if live.resource != Some(Resource::Image(image)) {
2730 return Err(AllocError::WrongResource);
2731 }
2732 if live.map_count != 0 {
2733 return Err(AllocError::AlreadyMapped);
2734 }
2735 let debug_ready = live.debug_ready;
2736 require(
2737 allocator.device.fp().destroy_image.is_some(),
2738 "vkDestroyImage",
2739 )?;
2740 require(allocator.device.fp().free_memory.is_some(), "vkFreeMemory")?;
2741 unsafe {
2743 guard::before_free(allocator, allocation)?;
2744 allocator.release_resource_claim(allocation);
2745 allocator.device.destroy_image(image, allocator.callbacks)?;
2746 if debug_ready {
2748 allocator
2749 .live
2750 .get_mut(&allocation.serial)
2751 .expect("validated resource allocation")
2752 .debug_ready = false;
2753 }
2754 free(allocator, allocation)
2755 }
2756}