• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (c) 2017-2022 Advanced Micro Devices, Inc. All rights reserved.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22 
23 #ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H
24 #define AMD_VULKAN_MEMORY_ALLOCATOR_H
25 
26 /** \mainpage Vulkan Memory Allocator
27 
28 <b>Version 3.0.1 (2022-05-26)</b>
29 
30 Copyright (c) 2017-2022 Advanced Micro Devices, Inc. All rights reserved. \n
31 License: MIT
32 
33 <b>API documentation divided into groups:</b> [Modules](modules.html)
34 
35 \section main_table_of_contents Table of contents
36 
37 - <b>User guide</b>
38   - \subpage quick_start
39     - [Project setup](@ref quick_start_project_setup)
40     - [Initialization](@ref quick_start_initialization)
41     - [Resource allocation](@ref quick_start_resource_allocation)
42   - \subpage choosing_memory_type
43     - [Usage](@ref choosing_memory_type_usage)
44     - [Required and preferred flags](@ref choosing_memory_type_required_preferred_flags)
45     - [Explicit memory types](@ref choosing_memory_type_explicit_memory_types)
46     - [Custom memory pools](@ref choosing_memory_type_custom_memory_pools)
47     - [Dedicated allocations](@ref choosing_memory_type_dedicated_allocations)
48   - \subpage memory_mapping
49     - [Mapping functions](@ref memory_mapping_mapping_functions)
50     - [Persistently mapped memory](@ref memory_mapping_persistently_mapped_memory)
51     - [Cache flush and invalidate](@ref memory_mapping_cache_control)
52   - \subpage staying_within_budget
53     - [Querying for budget](@ref staying_within_budget_querying_for_budget)
54     - [Controlling memory usage](@ref staying_within_budget_controlling_memory_usage)
55   - \subpage resource_aliasing
56   - \subpage custom_memory_pools
57     - [Choosing memory type index](@ref custom_memory_pools_MemTypeIndex)
58     - [Linear allocation algorithm](@ref linear_algorithm)
59       - [Free-at-once](@ref linear_algorithm_free_at_once)
60       - [Stack](@ref linear_algorithm_stack)
61       - [Double stack](@ref linear_algorithm_double_stack)
62       - [Ring buffer](@ref linear_algorithm_ring_buffer)
63   - \subpage defragmentation
64   - \subpage statistics
65     - [Numeric statistics](@ref statistics_numeric_statistics)
66     - [JSON dump](@ref statistics_json_dump)
67   - \subpage allocation_annotation
68     - [Allocation user data](@ref allocation_user_data)
69     - [Allocation names](@ref allocation_names)
70   - \subpage virtual_allocator
71   - \subpage debugging_memory_usage
72     - [Memory initialization](@ref debugging_memory_usage_initialization)
73     - [Margins](@ref debugging_memory_usage_margins)
74     - [Corruption detection](@ref debugging_memory_usage_corruption_detection)
75   - \subpage opengl_interop
76 - \subpage usage_patterns
77     - [GPU-only resource](@ref usage_patterns_gpu_only)
78     - [Staging copy for upload](@ref usage_patterns_staging_copy_upload)
79     - [Readback](@ref usage_patterns_readback)
80     - [Advanced data uploading](@ref usage_patterns_advanced_data_uploading)
81     - [Other use cases](@ref usage_patterns_other_use_cases)
82 - \subpage configuration
83   - [Pointers to Vulkan functions](@ref config_Vulkan_functions)
84   - [Custom host memory allocator](@ref custom_memory_allocator)
85   - [Device memory allocation callbacks](@ref allocation_callbacks)
86   - [Device heap memory limit](@ref heap_memory_limit)
87 - <b>Extension support</b>
88     - \subpage vk_khr_dedicated_allocation
89     - \subpage enabling_buffer_device_address
90     - \subpage vk_ext_memory_priority
91     - \subpage vk_amd_device_coherent_memory
92 - \subpage general_considerations
93   - [Thread safety](@ref general_considerations_thread_safety)
94   - [Versioning and compatibility](@ref general_considerations_versioning_and_compatibility)
95   - [Validation layer warnings](@ref general_considerations_validation_layer_warnings)
96   - [Allocation algorithm](@ref general_considerations_allocation_algorithm)
97   - [Features not supported](@ref general_considerations_features_not_supported)
98 
99 \section main_see_also See also
100 
101 - [**Product page on GPUOpen**](https://gpuopen.com/gaming-product/vulkan-memory-allocator/)
102 - [**Source repository on GitHub**](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)
103 
104 \defgroup group_init Library initialization
105 
106 \brief API elements related to the initialization and management of the entire library, especially #VmaAllocator object.
107 
108 \defgroup group_alloc Memory allocation
109 
110 \brief API elements related to the allocation, deallocation, and management of Vulkan memory, buffers, images.
111 Most basic ones being: vmaCreateBuffer(), vmaCreateImage().
112 
113 \defgroup group_virtual Virtual allocator
114 
115 \brief API elements related to the mechanism of \ref virtual_allocator - using the core allocation algorithm
116 for user-defined purpose without allocating any real GPU memory.
117 
118 \defgroup group_stats Statistics
119 
120 \brief API elements that query current status of the allocator, from memory usage, budget, to full dump of the internal state in JSON format.
121 See documentation chapter: \ref statistics.
122 */
123 
124 
125 #ifdef __cplusplus
126 extern "C" {
127 #endif
128 
129 #ifndef VULKAN_H_
130     #include <vulkan/vulkan.h>
131 #endif
132 
133 // Define this macro to declare maximum supported Vulkan version in format AAABBBCCC,
134 // where AAA = major, BBB = minor, CCC = patch.
135 // If you want to use version > 1.0, it still needs to be enabled via VmaAllocatorCreateInfo::vulkanApiVersion.
136 #if !defined(VMA_VULKAN_VERSION)
137     #if defined(VK_VERSION_1_3)
138         #define VMA_VULKAN_VERSION 1003000
139     #elif defined(VK_VERSION_1_2)
140         #define VMA_VULKAN_VERSION 1002000
141     #elif defined(VK_VERSION_1_1)
142         #define VMA_VULKAN_VERSION 1001000
143     #else
144         #define VMA_VULKAN_VERSION 1000000
145     #endif
146 #endif
147 
148 #if defined(__ANDROID__) && defined(VK_NO_PROTOTYPES) && VMA_STATIC_VULKAN_FUNCTIONS
149     extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
150     extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
151     extern PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
152     extern PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
153     extern PFN_vkAllocateMemory vkAllocateMemory;
154     extern PFN_vkFreeMemory vkFreeMemory;
155     extern PFN_vkMapMemory vkMapMemory;
156     extern PFN_vkUnmapMemory vkUnmapMemory;
157     extern PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
158     extern PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
159     extern PFN_vkBindBufferMemory vkBindBufferMemory;
160     extern PFN_vkBindImageMemory vkBindImageMemory;
161     extern PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
162     extern PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
163     extern PFN_vkCreateBuffer vkCreateBuffer;
164     extern PFN_vkDestroyBuffer vkDestroyBuffer;
165     extern PFN_vkCreateImage vkCreateImage;
166     extern PFN_vkDestroyImage vkDestroyImage;
167     extern PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
168     #if VMA_VULKAN_VERSION >= 1001000
169         extern PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2;
170         extern PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2;
171         extern PFN_vkBindBufferMemory2 vkBindBufferMemory2;
172         extern PFN_vkBindImageMemory2 vkBindImageMemory2;
173         extern PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2;
174     #endif // #if VMA_VULKAN_VERSION >= 1001000
175 #endif // #if defined(__ANDROID__) && VMA_STATIC_VULKAN_FUNCTIONS && VK_NO_PROTOTYPES
176 
177 #if !defined(VMA_DEDICATED_ALLOCATION)
178     #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation
179         #define VMA_DEDICATED_ALLOCATION 1
180     #else
181         #define VMA_DEDICATED_ALLOCATION 0
182     #endif
183 #endif
184 
185 #if !defined(VMA_BIND_MEMORY2)
186     #if VK_KHR_bind_memory2
187         #define VMA_BIND_MEMORY2 1
188     #else
189         #define VMA_BIND_MEMORY2 0
190     #endif
191 #endif
192 
193 #if !defined(VMA_MEMORY_BUDGET)
194     #if VK_EXT_memory_budget && (VK_KHR_get_physical_device_properties2 || VMA_VULKAN_VERSION >= 1001000)
195         #define VMA_MEMORY_BUDGET 1
196     #else
197         #define VMA_MEMORY_BUDGET 0
198     #endif
199 #endif
200 
201 // Defined to 1 when VK_KHR_buffer_device_address device extension or equivalent core Vulkan 1.2 feature is defined in its headers.
202 #if !defined(VMA_BUFFER_DEVICE_ADDRESS)
203     #if VK_KHR_buffer_device_address || VMA_VULKAN_VERSION >= 1002000
204         #define VMA_BUFFER_DEVICE_ADDRESS 1
205     #else
206         #define VMA_BUFFER_DEVICE_ADDRESS 0
207     #endif
208 #endif
209 
210 // Defined to 1 when VK_EXT_memory_priority device extension is defined in Vulkan headers.
211 #if !defined(VMA_MEMORY_PRIORITY)
212     #if VK_EXT_memory_priority
213         #define VMA_MEMORY_PRIORITY 1
214     #else
215         #define VMA_MEMORY_PRIORITY 0
216     #endif
217 #endif
218 
219 // Defined to 1 when VK_KHR_external_memory device extension is defined in Vulkan headers.
220 #if !defined(VMA_EXTERNAL_MEMORY)
221     #if VK_KHR_external_memory
222         #define VMA_EXTERNAL_MEMORY 1
223     #else
224         #define VMA_EXTERNAL_MEMORY 0
225     #endif
226 #endif
227 
228 // Define these macros to decorate all public functions with additional code,
229 // before and after returned type, appropriately. This may be useful for
230 // exporting the functions when compiling VMA as a separate library. Example:
231 // #define VMA_CALL_PRE  __declspec(dllexport)
232 // #define VMA_CALL_POST __cdecl
233 #ifndef VMA_CALL_PRE
234     #define VMA_CALL_PRE
235 #endif
236 #ifndef VMA_CALL_POST
237     #define VMA_CALL_POST
238 #endif
239 
240 // Define this macro to decorate pointers with an attribute specifying the
241 // length of the array they point to if they are not null.
242 //
243 // The length may be one of
244 // - The name of another parameter in the argument list where the pointer is declared
245 // - The name of another member in the struct where the pointer is declared
246 // - The name of a member of a struct type, meaning the value of that member in
247 //   the context of the call. For example
248 //   VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount"),
249 //   this means the number of memory heaps available in the device associated
250 //   with the VmaAllocator being dealt with.
251 #ifndef VMA_LEN_IF_NOT_NULL
252     #define VMA_LEN_IF_NOT_NULL(len)
253 #endif
254 
255 // The VMA_NULLABLE macro is defined to be _Nullable when compiling with Clang.
256 // see: https://clang.llvm.org/docs/AttributeReference.html#nullable
257 #ifndef VMA_NULLABLE
258     #ifdef __clang__
259         #define VMA_NULLABLE _Nullable
260     #else
261         #define VMA_NULLABLE
262     #endif
263 #endif
264 
265 // The VMA_NOT_NULL macro is defined to be _Nonnull when compiling with Clang.
266 // see: https://clang.llvm.org/docs/AttributeReference.html#nonnull
267 #ifndef VMA_NOT_NULL
268     #ifdef __clang__
269         #define VMA_NOT_NULL _Nonnull
270     #else
271         #define VMA_NOT_NULL
272     #endif
273 #endif
274 
275 // If non-dispatchable handles are represented as pointers then we can give
276 // then nullability annotations
277 #ifndef VMA_NOT_NULL_NON_DISPATCHABLE
278     #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
279         #define VMA_NOT_NULL_NON_DISPATCHABLE VMA_NOT_NULL
280     #else
281         #define VMA_NOT_NULL_NON_DISPATCHABLE
282     #endif
283 #endif
284 
285 #ifndef VMA_NULLABLE_NON_DISPATCHABLE
286     #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
287         #define VMA_NULLABLE_NON_DISPATCHABLE VMA_NULLABLE
288     #else
289         #define VMA_NULLABLE_NON_DISPATCHABLE
290     #endif
291 #endif
292 
293 #ifndef VMA_STATS_STRING_ENABLED
294     #define VMA_STATS_STRING_ENABLED 1
295 #endif
296 
297 ////////////////////////////////////////////////////////////////////////////////
298 ////////////////////////////////////////////////////////////////////////////////
299 //
300 //    INTERFACE
301 //
302 ////////////////////////////////////////////////////////////////////////////////
303 ////////////////////////////////////////////////////////////////////////////////
304 
305 // Sections for managing code placement in file, only for development purposes e.g. for convenient folding inside an IDE.
306 #ifndef _VMA_ENUM_DECLARATIONS
307 
308 /**
309 \addtogroup group_init
310 @{
311 */
312 
313 /// Flags for created #VmaAllocator.
314 typedef enum VmaAllocatorCreateFlagBits
315 {
316     /** \brief Allocator and all objects created from it will not be synchronized internally, so you must guarantee they are used from only one thread at a time or synchronized externally by you.
317 
318     Using this flag may increase performance because internal mutexes are not used.
319     */
320     VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001,
321     /** \brief Enables usage of VK_KHR_dedicated_allocation extension.
322 
323     The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`.
324     When it is `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1.
325 
326     Using this extension will automatically allocate dedicated blocks of memory for
327     some buffers and images instead of suballocating place for them out of bigger
328     memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
329     flag) when it is recommended by the driver. It may improve performance on some
330     GPUs.
331 
332     You may set this flag only if you found out that following device extensions are
333     supported, you enabled them while creating Vulkan device passed as
334     VmaAllocatorCreateInfo::device, and you want them to be used internally by this
335     library:
336 
337     - VK_KHR_get_memory_requirements2 (device extension)
338     - VK_KHR_dedicated_allocation (device extension)
339 
340     When this flag is set, you can experience following warnings reported by Vulkan
341     validation layer. You can ignore them.
342 
343     > vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer.
344     */
345     VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002,
346     /**
347     Enables usage of VK_KHR_bind_memory2 extension.
348 
349     The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`.
350     When it is `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1.
351 
352     You may set this flag only if you found out that this device extension is supported,
353     you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,
354     and you want it to be used internally by this library.
355 
356     The extension provides functions `vkBindBufferMemory2KHR` and `vkBindImageMemory2KHR`,
357     which allow to pass a chain of `pNext` structures while binding.
358     This flag is required if you use `pNext` parameter in vmaBindBufferMemory2() or vmaBindImageMemory2().
359     */
360     VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT = 0x00000004,
361     /**
362     Enables usage of VK_EXT_memory_budget extension.
363 
364     You may set this flag only if you found out that this device extension is supported,
365     you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,
366     and you want it to be used internally by this library, along with another instance extension
367     VK_KHR_get_physical_device_properties2, which is required by it (or Vulkan 1.1, where this extension is promoted).
368 
369     The extension provides query for current memory usage and budget, which will probably
370     be more accurate than an estimation used by the library otherwise.
371     */
372     VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT = 0x00000008,
373     /**
374     Enables usage of VK_AMD_device_coherent_memory extension.
375 
376     You may set this flag only if you:
377 
378     - found out that this device extension is supported and enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,
379     - checked that `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true and set it while creating the Vulkan device,
380     - want it to be used internally by this library.
381 
382     The extension and accompanying device feature provide access to memory types with
383     `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flags.
384     They are useful mostly for writing breadcrumb markers - a common method for debugging GPU crash/hang/TDR.
385 
386     When the extension is not enabled, such memory types are still enumerated, but their usage is illegal.
387     To protect from this error, if you don't create the allocator with this flag, it will refuse to allocate any memory or create a custom pool in such memory type,
388     returning `VK_ERROR_FEATURE_NOT_PRESENT`.
389     */
390     VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT = 0x00000010,
391     /**
392     Enables usage of "buffer device address" feature, which allows you to use function
393     `vkGetBufferDeviceAddress*` to get raw GPU pointer to a buffer and pass it for usage inside a shader.
394 
395     You may set this flag only if you:
396 
397     1. (For Vulkan version < 1.2) Found as available and enabled device extension
398     VK_KHR_buffer_device_address.
399     This extension is promoted to core Vulkan 1.2.
400     2. Found as available and enabled device feature `VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress`.
401 
402     When this flag is set, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT` using VMA.
403     The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT` to
404     allocated memory blocks wherever it might be needed.
405 
406     For more information, see documentation chapter \ref enabling_buffer_device_address.
407     */
408     VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT = 0x00000020,
409     /**
410     Enables usage of VK_EXT_memory_priority extension in the library.
411 
412     You may set this flag only if you found available and enabled this device extension,
413     along with `VkPhysicalDeviceMemoryPriorityFeaturesEXT::memoryPriority == VK_TRUE`,
414     while creating Vulkan device passed as VmaAllocatorCreateInfo::device.
415 
416     When this flag is used, VmaAllocationCreateInfo::priority and VmaPoolCreateInfo::priority
417     are used to set priorities of allocated Vulkan memory. Without it, these variables are ignored.
418 
419     A priority must be a floating-point value between 0 and 1, indicating the priority of the allocation relative to other memory allocations.
420     Larger values are higher priority. The granularity of the priorities is implementation-dependent.
421     It is automatically passed to every call to `vkAllocateMemory` done by the library using structure `VkMemoryPriorityAllocateInfoEXT`.
422     The value to be used for default priority is 0.5.
423     For more details, see the documentation of the VK_EXT_memory_priority extension.
424     */
425     VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT = 0x00000040,
426 
427     VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
428 } VmaAllocatorCreateFlagBits;
429 /// See #VmaAllocatorCreateFlagBits.
430 typedef VkFlags VmaAllocatorCreateFlags;
431 
432 /** @} */
433 
434 /**
435 \addtogroup group_alloc
436 @{
437 */
438 
439 /// \brief Intended usage of the allocated memory.
440 typedef enum VmaMemoryUsage
441 {
442     /** No intended memory usage specified.
443     Use other members of VmaAllocationCreateInfo to specify your requirements.
444     */
445     VMA_MEMORY_USAGE_UNKNOWN = 0,
446     /**
447     \deprecated Obsolete, preserved for backward compatibility.
448     Prefers `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
449     */
450     VMA_MEMORY_USAGE_GPU_ONLY = 1,
451     /**
452     \deprecated Obsolete, preserved for backward compatibility.
453     Guarantees `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`.
454     */
455     VMA_MEMORY_USAGE_CPU_ONLY = 2,
456     /**
457     \deprecated Obsolete, preserved for backward compatibility.
458     Guarantees `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`, prefers `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
459     */
460     VMA_MEMORY_USAGE_CPU_TO_GPU = 3,
461     /**
462     \deprecated Obsolete, preserved for backward compatibility.
463     Guarantees `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`, prefers `VK_MEMORY_PROPERTY_HOST_CACHED_BIT`.
464     */
465     VMA_MEMORY_USAGE_GPU_TO_CPU = 4,
466     /**
467     \deprecated Obsolete, preserved for backward compatibility.
468     Prefers not `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
469     */
470     VMA_MEMORY_USAGE_CPU_COPY = 5,
471     /**
472     Lazily allocated GPU memory having `VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT`.
473     Exists mostly on mobile platforms. Using it on desktop PC or other GPUs with no such memory type present will fail the allocation.
474 
475     Usage: Memory for transient attachment images (color attachments, depth attachments etc.), created with `VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT`.
476 
477     Allocations with this usage are always created as dedicated - it implies #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
478     */
479     VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED = 6,
480     /**
481     Selects best memory type automatically.
482     This flag is recommended for most common use cases.
483 
484     When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT),
485     you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT
486     in VmaAllocationCreateInfo::flags.
487 
488     It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g.
489     vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo()
490     and not with generic memory allocation functions.
491     */
492     VMA_MEMORY_USAGE_AUTO = 7,
493     /**
494     Selects best memory type automatically with preference for GPU (device) memory.
495 
496     When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT),
497     you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT
498     in VmaAllocationCreateInfo::flags.
499 
500     It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g.
501     vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo()
502     and not with generic memory allocation functions.
503     */
504     VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE = 8,
505     /**
506     Selects best memory type automatically with preference for CPU (host) memory.
507 
508     When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT),
509     you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT
510     in VmaAllocationCreateInfo::flags.
511 
512     It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g.
513     vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo()
514     and not with generic memory allocation functions.
515     */
516     VMA_MEMORY_USAGE_AUTO_PREFER_HOST = 9,
517 
518     VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
519 } VmaMemoryUsage;
520 
521 /// Flags to be passed as VmaAllocationCreateInfo::flags.
522 typedef enum VmaAllocationCreateFlagBits
523 {
524     /** \brief Set this flag if the allocation should have its own memory block.
525 
526     Use it for special, big resources, like fullscreen images used as attachments.
527     */
528     VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001,
529 
530     /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block.
531 
532     If new allocation cannot be placed in any of the existing blocks, allocation
533     fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
534 
535     You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and
536     #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense.
537     */
538     VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002,
539     /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it.
540 
541     Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData.
542 
543     It is valid to use this flag for allocation made from memory type that is not
544     `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is
545     useful if you need an allocation that is efficient to use on GPU
546     (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that
547     support it (e.g. Intel GPU).
548     */
549     VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004,
550     /** \deprecated Preserved for backward compatibility. Consider using vmaSetAllocationName() instead.
551 
552     Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a
553     null-terminated string. Instead of copying pointer value, a local copy of the
554     string is made and stored in allocation's `pName`. The string is automatically
555     freed together with the allocation. It is also used in vmaBuildStatsString().
556     */
557     VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020,
558     /** Allocation will be created from upper stack in a double stack pool.
559 
560     This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag.
561     */
562     VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040,
563     /** Create both buffer/image and allocation, but don't bind them together.
564     It is useful when you want to bind yourself to do some more advanced binding, e.g. using some extensions.
565     The flag is meaningful only with functions that bind by default: vmaCreateBuffer(), vmaCreateImage().
566     Otherwise it is ignored.
567 
568     If you want to make sure the new buffer/image is not tied to the new memory allocation
569     through `VkMemoryDedicatedAllocateInfoKHR` structure in case the allocation ends up in its own memory block,
570     use also flag #VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT.
571     */
572     VMA_ALLOCATION_CREATE_DONT_BIND_BIT = 0x00000080,
573     /** Create allocation only if additional device memory required for it, if any, won't exceed
574     memory budget. Otherwise return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
575     */
576     VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT = 0x00000100,
577     /** \brief Set this flag if the allocated memory will have aliasing resources.
578 
579     Usage of this flag prevents supplying `VkMemoryDedicatedAllocateInfoKHR` when #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT is specified.
580     Otherwise created dedicated memory will not be suitable for aliasing resources, resulting in Vulkan Validation Layer errors.
581     */
582     VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT = 0x00000200,
583     /**
584     Requests possibility to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT).
585 
586     - If you use #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` value,
587       you must use this flag to be able to map the allocation. Otherwise, mapping is incorrect.
588     - If you use other value of #VmaMemoryUsage, this flag is ignored and mapping is always possible in memory types that are `HOST_VISIBLE`.
589       This includes allocations created in \ref custom_memory_pools.
590 
591     Declares that mapped memory will only be written sequentially, e.g. using `memcpy()` or a loop writing number-by-number,
592     never read or accessed randomly, so a memory type can be selected that is uncached and write-combined.
593 
594     \warning Violating this declaration may work correctly, but will likely be very slow.
595     Watch out for implicit reads introduced by doing e.g. `pMappedData[i] += x;`
596     Better prepare your data in a local variable and `memcpy()` it to the mapped pointer all at once.
597     */
598     VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT = 0x00000400,
599     /**
600     Requests possibility to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT).
601 
602     - If you use #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` value,
603       you must use this flag to be able to map the allocation. Otherwise, mapping is incorrect.
604     - If you use other value of #VmaMemoryUsage, this flag is ignored and mapping is always possible in memory types that are `HOST_VISIBLE`.
605       This includes allocations created in \ref custom_memory_pools.
606 
607     Declares that mapped memory can be read, written, and accessed in random order,
608     so a `HOST_CACHED` memory type is required.
609     */
610     VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT = 0x00000800,
611     /**
612     Together with #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT,
613     it says that despite request for host access, a not-`HOST_VISIBLE` memory type can be selected
614     if it may improve performance.
615 
616     By using this flag, you declare that you will check if the allocation ended up in a `HOST_VISIBLE` memory type
617     (e.g. using vmaGetAllocationMemoryProperties()) and if not, you will create some "staging" buffer and
618     issue an explicit transfer to write/read your data.
619     To prepare for this possibility, don't forget to add appropriate flags like
620     `VK_BUFFER_USAGE_TRANSFER_DST_BIT`, `VK_BUFFER_USAGE_TRANSFER_SRC_BIT` to the parameters of created buffer or image.
621     */
622     VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT = 0x00001000,
623     /** Allocation strategy that chooses smallest possible free range for the allocation
624     to minimize memory usage and fragmentation, possibly at the expense of allocation time.
625     */
626     VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = 0x00010000,
627     /** Allocation strategy that chooses first suitable free range for the allocation -
628     not necessarily in terms of the smallest offset but the one that is easiest and fastest to find
629     to minimize allocation time, possibly at the expense of allocation quality.
630     */
631     VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = 0x00020000,
632     /** Allocation strategy that chooses always the lowest offset in available space.
633     This is not the most efficient strategy but achieves highly packed data.
634     Used internally by defragmentation, not recomended in typical usage.
635     */
636     VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT  = 0x00040000,
637     /** Alias to #VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT.
638     */
639     VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT,
640     /** Alias to #VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT.
641     */
642     VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT,
643     /** A bit mask to extract only `STRATEGY` bits from entire set of flags.
644     */
645     VMA_ALLOCATION_CREATE_STRATEGY_MASK =
646         VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT |
647         VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT |
648         VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,
649 
650     VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
651 } VmaAllocationCreateFlagBits;
652 /// See #VmaAllocationCreateFlagBits.
653 typedef VkFlags VmaAllocationCreateFlags;
654 
655 /// Flags to be passed as VmaPoolCreateInfo::flags.
656 typedef enum VmaPoolCreateFlagBits
657 {
658     /** \brief Use this flag if you always allocate only buffers and linear images or only optimal images out of this pool and so Buffer-Image Granularity can be ignored.
659 
660     This is an optional optimization flag.
661 
662     If you always allocate using vmaCreateBuffer(), vmaCreateImage(),
663     vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator
664     knows exact type of your allocations so it can handle Buffer-Image Granularity
665     in the optimal way.
666 
667     If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(),
668     exact type of such allocations is not known, so allocator must be conservative
669     in handling Buffer-Image Granularity, which can lead to suboptimal allocation
670     (wasted memory). In that case, if you can make sure you always allocate only
671     buffers and linear images or only optimal images out of this pool, use this flag
672     to make allocator disregard Buffer-Image Granularity and so make allocations
673     faster and more optimal.
674     */
675     VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002,
676 
677     /** \brief Enables alternative, linear allocation algorithm in this pool.
678 
679     Specify this flag to enable linear allocation algorithm, which always creates
680     new allocations after last one and doesn't reuse space from allocations freed in
681     between. It trades memory consumption for simplified algorithm and data
682     structure, which has better performance and uses less memory for metadata.
683 
684     By using this flag, you can achieve behavior of free-at-once, stack,
685     ring buffer, and double stack.
686     For details, see documentation chapter \ref linear_algorithm.
687     */
688     VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004,
689 
690     /** Bit mask to extract only `ALGORITHM` bits from entire set of flags.
691     */
692     VMA_POOL_CREATE_ALGORITHM_MASK =
693         VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT,
694 
695     VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
696 } VmaPoolCreateFlagBits;
697 /// Flags to be passed as VmaPoolCreateInfo::flags. See #VmaPoolCreateFlagBits.
698 typedef VkFlags VmaPoolCreateFlags;
699 
700 /// Flags to be passed as VmaDefragmentationInfo::flags.
701 typedef enum VmaDefragmentationFlagBits
702 {
703     /* \brief Use simple but fast algorithm for defragmentation.
704     May not achieve best results but will require least time to compute and least allocations to copy.
705     */
706     VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT = 0x1,
707     /* \brief Default defragmentation algorithm, applied also when no `ALGORITHM` flag is specified.
708     Offers a balance between defragmentation quality and the amount of allocations and bytes that need to be moved.
709     */
710     VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT = 0x2,
711     /* \brief Perform full defragmentation of memory.
712     Can result in notably more time to compute and allocations to copy, but will achieve best memory packing.
713     */
714     VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT = 0x4,
715     /** \brief Use the most roboust algorithm at the cost of time to compute and number of copies to make.
716     Only available when bufferImageGranularity is greater than 1, since it aims to reduce
717     alignment issues between different types of resources.
718     Otherwise falls back to same behavior as #VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT.
719     */
720     VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT = 0x8,
721 
722     /// A bit mask to extract only `ALGORITHM` bits from entire set of flags.
723     VMA_DEFRAGMENTATION_FLAG_ALGORITHM_MASK =
724         VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT |
725         VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT |
726         VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT |
727         VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT,
728 
729     VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
730 } VmaDefragmentationFlagBits;
731 /// See #VmaDefragmentationFlagBits.
732 typedef VkFlags VmaDefragmentationFlags;
733 
734 /// Operation performed on single defragmentation move. See structure #VmaDefragmentationMove.
735 typedef enum VmaDefragmentationMoveOperation
736 {
737     /// Buffer/image has been recreated at `dstTmpAllocation`, data has been copied, old buffer/image has been destroyed. `srcAllocation` should be changed to point to the new place. This is the default value set by vmaBeginDefragmentationPass().
738     VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY = 0,
739     /// Set this value if you cannot move the allocation. New place reserved at `dstTmpAllocation` will be freed. `srcAllocation` will remain unchanged.
740     VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE = 1,
741     /// Set this value if you decide to abandon the allocation and you destroyed the buffer/image. New place reserved at `dstTmpAllocation` will be freed, along with `srcAllocation`, which will be destroyed.
742     VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY = 2,
743 } VmaDefragmentationMoveOperation;
744 
745 /** @} */
746 
747 /**
748 \addtogroup group_virtual
749 @{
750 */
751 
752 /// Flags to be passed as VmaVirtualBlockCreateInfo::flags.
753 typedef enum VmaVirtualBlockCreateFlagBits
754 {
755     /** \brief Enables alternative, linear allocation algorithm in this virtual block.
756 
757     Specify this flag to enable linear allocation algorithm, which always creates
758     new allocations after last one and doesn't reuse space from allocations freed in
759     between. It trades memory consumption for simplified algorithm and data
760     structure, which has better performance and uses less memory for metadata.
761 
762     By using this flag, you can achieve behavior of free-at-once, stack,
763     ring buffer, and double stack.
764     For details, see documentation chapter \ref linear_algorithm.
765     */
766     VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT = 0x00000001,
767 
768     /** \brief Bit mask to extract only `ALGORITHM` bits from entire set of flags.
769     */
770     VMA_VIRTUAL_BLOCK_CREATE_ALGORITHM_MASK =
771         VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT,
772 
773     VMA_VIRTUAL_BLOCK_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
774 } VmaVirtualBlockCreateFlagBits;
775 /// Flags to be passed as VmaVirtualBlockCreateInfo::flags. See #VmaVirtualBlockCreateFlagBits.
776 typedef VkFlags VmaVirtualBlockCreateFlags;
777 
778 /// Flags to be passed as VmaVirtualAllocationCreateInfo::flags.
779 typedef enum VmaVirtualAllocationCreateFlagBits
780 {
781     /** \brief Allocation will be created from upper stack in a double stack pool.
782 
783     This flag is only allowed for virtual blocks created with #VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT flag.
784     */
785     VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT,
786     /** \brief Allocation strategy that tries to minimize memory usage.
787     */
788     VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT,
789     /** \brief Allocation strategy that tries to minimize allocation time.
790     */
791     VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT,
792     /** Allocation strategy that chooses always the lowest offset in available space.
793     This is not the most efficient strategy but achieves highly packed data.
794     */
795     VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,
796     /** \brief A bit mask to extract only `STRATEGY` bits from entire set of flags.
797 
798     These strategy flags are binary compatible with equivalent flags in #VmaAllocationCreateFlagBits.
799     */
800     VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK = VMA_ALLOCATION_CREATE_STRATEGY_MASK,
801 
802     VMA_VIRTUAL_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
803 } VmaVirtualAllocationCreateFlagBits;
804 /// Flags to be passed as VmaVirtualAllocationCreateInfo::flags. See #VmaVirtualAllocationCreateFlagBits.
805 typedef VkFlags VmaVirtualAllocationCreateFlags;
806 
807 /** @} */
808 
809 #endif // _VMA_ENUM_DECLARATIONS
810 
811 #ifndef _VMA_DATA_TYPES_DECLARATIONS
812 
813 /**
814 \addtogroup group_init
815 @{ */
816 
817 /** \struct VmaAllocator
818 \brief Represents main object of this library initialized.
819 
820 Fill structure #VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it.
821 Call function vmaDestroyAllocator() to destroy it.
822 
823 It is recommended to create just one object of this type per `VkDevice` object,
824 right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed.
825 */
826 VK_DEFINE_HANDLE(VmaAllocator)
827 
828 /** @} */
829 
830 /**
831 \addtogroup group_alloc
832 @{
833 */
834 
835 /** \struct VmaPool
836 \brief Represents custom memory pool
837 
838 Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it.
839 Call function vmaDestroyPool() to destroy it.
840 
841 For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools).
842 */
843 VK_DEFINE_HANDLE(VmaPool)
844 
845 /** \struct VmaAllocation
846 \brief Represents single memory allocation.
847 
848 It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type
849 plus unique offset.
850 
851 There are multiple ways to create such object.
852 You need to fill structure VmaAllocationCreateInfo.
853 For more information see [Choosing memory type](@ref choosing_memory_type).
854 
855 Although the library provides convenience functions that create Vulkan buffer or image,
856 allocate memory for it and bind them together,
857 binding of the allocation to a buffer or an image is out of scope of the allocation itself.
858 Allocation object can exist without buffer/image bound,
859 binding can be done manually by the user, and destruction of it can be done
860 independently of destruction of the allocation.
861 
862 The object also remembers its size and some other information.
863 To retrieve this information, use function vmaGetAllocationInfo() and inspect
864 returned structure VmaAllocationInfo.
865 */
866 VK_DEFINE_HANDLE(VmaAllocation)
867 
868 /** \struct VmaDefragmentationContext
869 \brief An opaque object that represents started defragmentation process.
870 
871 Fill structure #VmaDefragmentationInfo and call function vmaBeginDefragmentation() to create it.
872 Call function vmaEndDefragmentation() to destroy it.
873 */
874 VK_DEFINE_HANDLE(VmaDefragmentationContext)
875 
876 /** @} */
877 
878 /**
879 \addtogroup group_virtual
880 @{
881 */
882 
883 /** \struct VmaVirtualAllocation
884 \brief Represents single memory allocation done inside VmaVirtualBlock.
885 
886 Use it as a unique identifier to virtual allocation within the single block.
887 
888 Use value `VK_NULL_HANDLE` to represent a null/invalid allocation.
889 */
890 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VmaVirtualAllocation);
891 
892 /** @} */
893 
894 /**
895 \addtogroup group_virtual
896 @{
897 */
898 
899 /** \struct VmaVirtualBlock
900 \brief Handle to a virtual block object that allows to use core allocation algorithm without allocating any real GPU memory.
901 
902 Fill in #VmaVirtualBlockCreateInfo structure and use vmaCreateVirtualBlock() to create it. Use vmaDestroyVirtualBlock() to destroy it.
903 For more information, see documentation chapter \ref virtual_allocator.
904 
905 This object is not thread-safe - should not be used from multiple threads simultaneously, must be synchronized externally.
906 */
907 VK_DEFINE_HANDLE(VmaVirtualBlock)
908 
909 /** @} */
910 
911 /**
912 \addtogroup group_init
913 @{
914 */
915 
916 /// Callback function called after successful vkAllocateMemory.
917 typedef void (VKAPI_PTR* PFN_vmaAllocateDeviceMemoryFunction)(
918     VmaAllocator VMA_NOT_NULL                    allocator,
919     uint32_t                                     memoryType,
920     VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,
921     VkDeviceSize                                 size,
922     void* VMA_NULLABLE                           pUserData);
923 
924 /// Callback function called before vkFreeMemory.
925 typedef void (VKAPI_PTR* PFN_vmaFreeDeviceMemoryFunction)(
926     VmaAllocator VMA_NOT_NULL                    allocator,
927     uint32_t                                     memoryType,
928     VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,
929     VkDeviceSize                                 size,
930     void* VMA_NULLABLE                           pUserData);
931 
932 /** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`.
933 
934 Provided for informative purpose, e.g. to gather statistics about number of
935 allocations or total amount of memory allocated in Vulkan.
936 
937 Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
938 */
939 typedef struct VmaDeviceMemoryCallbacks
940 {
941     /// Optional, can be null.
942     PFN_vmaAllocateDeviceMemoryFunction VMA_NULLABLE pfnAllocate;
943     /// Optional, can be null.
944     PFN_vmaFreeDeviceMemoryFunction VMA_NULLABLE pfnFree;
945     /// Optional, can be null.
946     void* VMA_NULLABLE pUserData;
947 } VmaDeviceMemoryCallbacks;
948 
949 /** \brief Pointers to some Vulkan functions - a subset used by the library.
950 
951 Used in VmaAllocatorCreateInfo::pVulkanFunctions.
952 */
953 typedef struct VmaVulkanFunctions
954 {
955     /// Required when using VMA_DYNAMIC_VULKAN_FUNCTIONS.
956     PFN_vkGetInstanceProcAddr VMA_NULLABLE vkGetInstanceProcAddr;
957     /// Required when using VMA_DYNAMIC_VULKAN_FUNCTIONS.
958     PFN_vkGetDeviceProcAddr VMA_NULLABLE vkGetDeviceProcAddr;
959     PFN_vkGetPhysicalDeviceProperties VMA_NULLABLE vkGetPhysicalDeviceProperties;
960     PFN_vkGetPhysicalDeviceMemoryProperties VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties;
961     PFN_vkAllocateMemory VMA_NULLABLE vkAllocateMemory;
962     PFN_vkFreeMemory VMA_NULLABLE vkFreeMemory;
963     PFN_vkMapMemory VMA_NULLABLE vkMapMemory;
964     PFN_vkUnmapMemory VMA_NULLABLE vkUnmapMemory;
965     PFN_vkFlushMappedMemoryRanges VMA_NULLABLE vkFlushMappedMemoryRanges;
966     PFN_vkInvalidateMappedMemoryRanges VMA_NULLABLE vkInvalidateMappedMemoryRanges;
967     PFN_vkBindBufferMemory VMA_NULLABLE vkBindBufferMemory;
968     PFN_vkBindImageMemory VMA_NULLABLE vkBindImageMemory;
969     PFN_vkGetBufferMemoryRequirements VMA_NULLABLE vkGetBufferMemoryRequirements;
970     PFN_vkGetImageMemoryRequirements VMA_NULLABLE vkGetImageMemoryRequirements;
971     PFN_vkCreateBuffer VMA_NULLABLE vkCreateBuffer;
972     PFN_vkDestroyBuffer VMA_NULLABLE vkDestroyBuffer;
973     PFN_vkCreateImage VMA_NULLABLE vkCreateImage;
974     PFN_vkDestroyImage VMA_NULLABLE vkDestroyImage;
975     PFN_vkCmdCopyBuffer VMA_NULLABLE vkCmdCopyBuffer;
976 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
977     /// Fetch "vkGetBufferMemoryRequirements2" on Vulkan >= 1.1, fetch "vkGetBufferMemoryRequirements2KHR" when using VK_KHR_dedicated_allocation extension.
978     PFN_vkGetBufferMemoryRequirements2KHR VMA_NULLABLE vkGetBufferMemoryRequirements2KHR;
979     /// Fetch "vkGetImageMemoryRequirements2" on Vulkan >= 1.1, fetch "vkGetImageMemoryRequirements2KHR" when using VK_KHR_dedicated_allocation extension.
980     PFN_vkGetImageMemoryRequirements2KHR VMA_NULLABLE vkGetImageMemoryRequirements2KHR;
981 #endif
982 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
983     /// Fetch "vkBindBufferMemory2" on Vulkan >= 1.1, fetch "vkBindBufferMemory2KHR" when using VK_KHR_bind_memory2 extension.
984     PFN_vkBindBufferMemory2KHR VMA_NULLABLE vkBindBufferMemory2KHR;
985     /// Fetch "vkBindImageMemory2" on Vulkan >= 1.1, fetch "vkBindImageMemory2KHR" when using VK_KHR_bind_memory2 extension.
986     PFN_vkBindImageMemory2KHR VMA_NULLABLE vkBindImageMemory2KHR;
987 #endif
988 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
989     PFN_vkGetPhysicalDeviceMemoryProperties2KHR VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties2KHR;
990 #endif
991 #if VMA_VULKAN_VERSION >= 1003000
992     /// Fetch from "vkGetDeviceBufferMemoryRequirements" on Vulkan >= 1.3, but you can also fetch it from "vkGetDeviceBufferMemoryRequirementsKHR" if you enabled extension VK_KHR_maintenance4.
993     PFN_vkGetDeviceBufferMemoryRequirements VMA_NULLABLE vkGetDeviceBufferMemoryRequirements;
994     /// Fetch from "vkGetDeviceImageMemoryRequirements" on Vulkan >= 1.3, but you can also fetch it from "vkGetDeviceImageMemoryRequirementsKHR" if you enabled extension VK_KHR_maintenance4.
995     PFN_vkGetDeviceImageMemoryRequirements VMA_NULLABLE vkGetDeviceImageMemoryRequirements;
996 #endif
997 } VmaVulkanFunctions;
998 
999 /// Description of a Allocator to be created.
1000 typedef struct VmaAllocatorCreateInfo
1001 {
1002     /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum.
1003     VmaAllocatorCreateFlags flags;
1004     /// Vulkan physical device.
1005     /** It must be valid throughout whole lifetime of created allocator. */
1006     VkPhysicalDevice VMA_NOT_NULL physicalDevice;
1007     /// Vulkan device.
1008     /** It must be valid throughout whole lifetime of created allocator. */
1009     VkDevice VMA_NOT_NULL device;
1010     /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional.
1011     /** Set to 0 to use default, which is currently 256 MiB. */
1012     VkDeviceSize preferredLargeHeapBlockSize;
1013     /// Custom CPU memory allocation callbacks. Optional.
1014     /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */
1015     const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks;
1016     /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional.
1017     /** Optional, can be null. */
1018     const VmaDeviceMemoryCallbacks* VMA_NULLABLE pDeviceMemoryCallbacks;
1019     /** \brief Either null or a pointer to an array of limits on maximum number of bytes that can be allocated out of particular Vulkan memory heap.
1020 
1021     If not NULL, it must be a pointer to an array of
1022     `VkPhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on
1023     maximum number of bytes that can be allocated out of particular Vulkan memory
1024     heap.
1025 
1026     Any of the elements may be equal to `VK_WHOLE_SIZE`, which means no limit on that
1027     heap. This is also the default in case of `pHeapSizeLimit` = NULL.
1028 
1029     If there is a limit defined for a heap:
1030 
1031     - If user tries to allocate more memory from that heap using this allocator,
1032       the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
1033     - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the
1034       value of this limit will be reported instead when using vmaGetMemoryProperties().
1035 
1036     Warning! Using this feature may not be equivalent to installing a GPU with
1037     smaller amount of memory, because graphics driver doesn't necessary fail new
1038     allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is
1039     exceeded. It may return success and just silently migrate some device memory
1040     blocks to system RAM. This driver behavior can also be controlled using
1041     VK_AMD_memory_overallocation_behavior extension.
1042     */
1043     const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pHeapSizeLimit;
1044 
1045     /** \brief Pointers to Vulkan functions. Can be null.
1046 
1047     For details see [Pointers to Vulkan functions](@ref config_Vulkan_functions).
1048     */
1049     const VmaVulkanFunctions* VMA_NULLABLE pVulkanFunctions;
1050     /** \brief Handle to Vulkan instance object.
1051 
1052     Starting from version 3.0.0 this member is no longer optional, it must be set!
1053     */
1054     VkInstance VMA_NOT_NULL instance;
1055     /** \brief Optional. The highest version of Vulkan that the application is designed to use.
1056 
1057     It must be a value in the format as created by macro `VK_MAKE_VERSION` or a constant like: `VK_API_VERSION_1_1`, `VK_API_VERSION_1_0`.
1058     The patch version number specified is ignored. Only the major and minor versions are considered.
1059     It must be less or equal (preferably equal) to value as passed to `vkCreateInstance` as `VkApplicationInfo::apiVersion`.
1060     Only versions 1.0, 1.1, 1.2, 1.3 are supported by the current implementation.
1061     Leaving it initialized to zero is equivalent to `VK_API_VERSION_1_0`.
1062     */
1063     uint32_t vulkanApiVersion;
1064     size_t maxBlockCount = SIZE_MAX;
1065 #if VMA_EXTERNAL_MEMORY
1066     /** \brief Either null or a pointer to an array of external memory handle types for each Vulkan memory type.
1067 
1068     If not NULL, it must be a pointer to an array of `VkPhysicalDeviceMemoryProperties::memoryTypeCount`
1069     elements, defining external memory handle types of particular Vulkan memory type,
1070     to be passed using `VkExportMemoryAllocateInfoKHR`.
1071 
1072     Any of the elements may be equal to 0, which means not to use `VkExportMemoryAllocateInfoKHR` on this memory type.
1073     This is also the default in case of `pTypeExternalMemoryHandleTypes` = NULL.
1074     */
1075     const VkExternalMemoryHandleTypeFlagsKHR* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryTypeCount") pTypeExternalMemoryHandleTypes;
1076 #endif // #if VMA_EXTERNAL_MEMORY
1077 } VmaAllocatorCreateInfo;
1078 
1079 /// Information about existing #VmaAllocator object.
1080 typedef struct VmaAllocatorInfo
1081 {
1082     /** \brief Handle to Vulkan instance object.
1083 
1084     This is the same value as has been passed through VmaAllocatorCreateInfo::instance.
1085     */
1086     VkInstance VMA_NOT_NULL instance;
1087     /** \brief Handle to Vulkan physical device object.
1088 
1089     This is the same value as has been passed through VmaAllocatorCreateInfo::physicalDevice.
1090     */
1091     VkPhysicalDevice VMA_NOT_NULL physicalDevice;
1092     /** \brief Handle to Vulkan device object.
1093 
1094     This is the same value as has been passed through VmaAllocatorCreateInfo::device.
1095     */
1096     VkDevice VMA_NOT_NULL device;
1097 } VmaAllocatorInfo;
1098 
1099 /** @} */
1100 
1101 /**
1102 \addtogroup group_stats
1103 @{
1104 */
1105 
1106 /** \brief Calculated statistics of memory usage e.g. in a specific memory type, heap, custom pool, or total.
1107 
1108 These are fast to calculate.
1109 See functions: vmaGetHeapBudgets(), vmaGetPoolStatistics().
1110 */
1111 typedef struct VmaStatistics
1112 {
1113     /** \brief Number of `VkDeviceMemory` objects - Vulkan memory blocks allocated.
1114     */
1115     uint32_t blockCount;
1116     /** \brief Number of #VmaAllocation objects allocated.
1117 
1118     Dedicated allocations have their own blocks, so each one adds 1 to `allocationCount` as well as `blockCount`.
1119     */
1120     uint32_t allocationCount;
1121     /** \brief Number of bytes allocated in `VkDeviceMemory` blocks.
1122 
1123     \note To avoid confusion, please be aware that what Vulkan calls an "allocation" - a whole `VkDeviceMemory` object
1124     (e.g. as in `VkPhysicalDeviceLimits::maxMemoryAllocationCount`) is called a "block" in VMA, while VMA calls
1125     "allocation" a #VmaAllocation object that represents a memory region sub-allocated from such block, usually for a single buffer or image.
1126     */
1127     VkDeviceSize blockBytes;
1128     /** \brief Total number of bytes occupied by all #VmaAllocation objects.
1129 
1130     Always less or equal than `blockBytes`.
1131     Difference `(blockBytes - allocationBytes)` is the amount of memory allocated from Vulkan
1132     but unused by any #VmaAllocation.
1133     */
1134     VkDeviceSize allocationBytes;
1135 } VmaStatistics;
1136 
1137 /** \brief More detailed statistics than #VmaStatistics.
1138 
1139 These are slower to calculate. Use for debugging purposes.
1140 See functions: vmaCalculateStatistics(), vmaCalculatePoolStatistics().
1141 
1142 Previous version of the statistics API provided averages, but they have been removed
1143 because they can be easily calculated as:
1144 
1145 \code
1146 VkDeviceSize allocationSizeAvg = detailedStats.statistics.allocationBytes / detailedStats.statistics.allocationCount;
1147 VkDeviceSize unusedBytes = detailedStats.statistics.blockBytes - detailedStats.statistics.allocationBytes;
1148 VkDeviceSize unusedRangeSizeAvg = unusedBytes / detailedStats.unusedRangeCount;
1149 \endcode
1150 */
1151 typedef struct VmaDetailedStatistics
1152 {
1153     /// Basic statistics.
1154     VmaStatistics statistics;
1155     /// Number of free ranges of memory between allocations.
1156     uint32_t unusedRangeCount;
1157     /// Smallest allocation size. `VK_WHOLE_SIZE` if there are 0 allocations.
1158     VkDeviceSize allocationSizeMin;
1159     /// Largest allocation size. 0 if there are 0 allocations.
1160     VkDeviceSize allocationSizeMax;
1161     /// Smallest empty range size. `VK_WHOLE_SIZE` if there are 0 empty ranges.
1162     VkDeviceSize unusedRangeSizeMin;
1163     /// Largest empty range size. 0 if there are 0 empty ranges.
1164     VkDeviceSize unusedRangeSizeMax;
1165 } VmaDetailedStatistics;
1166 
1167 /** \brief  General statistics from current state of the Allocator -
1168 total memory usage across all memory heaps and types.
1169 
1170 These are slower to calculate. Use for debugging purposes.
1171 See function vmaCalculateStatistics().
1172 */
1173 typedef struct VmaTotalStatistics
1174 {
1175     VmaDetailedStatistics memoryType[VK_MAX_MEMORY_TYPES];
1176     VmaDetailedStatistics memoryHeap[VK_MAX_MEMORY_HEAPS];
1177     VmaDetailedStatistics total;
1178 } VmaTotalStatistics;
1179 
1180 /** \brief Statistics of current memory usage and available budget for a specific memory heap.
1181 
1182 These are fast to calculate.
1183 See function vmaGetHeapBudgets().
1184 */
1185 typedef struct VmaBudget
1186 {
1187     /** \brief Statistics fetched from the library.
1188     */
1189     VmaStatistics statistics;
1190     /** \brief Estimated current memory usage of the program, in bytes.
1191 
1192     Fetched from system using VK_EXT_memory_budget extension if enabled.
1193 
1194     It might be different than `statistics.blockBytes` (usually higher) due to additional implicit objects
1195     also occupying the memory, like swapchain, pipelines, descriptor heaps, command buffers, or
1196     `VkDeviceMemory` blocks allocated outside of this library, if any.
1197     */
1198     VkDeviceSize usage;
1199     /** \brief Estimated amount of memory available to the program, in bytes.
1200 
1201     Fetched from system using VK_EXT_memory_budget extension if enabled.
1202 
1203     It might be different (most probably smaller) than `VkMemoryHeap::size[heapIndex]` due to factors
1204     external to the program, decided by the operating system.
1205     Difference `budget - usage` is the amount of additional memory that can probably
1206     be allocated without problems. Exceeding the budget may result in various problems.
1207     */
1208     VkDeviceSize budget;
1209 } VmaBudget;
1210 
1211 /** @} */
1212 
1213 /**
1214 \addtogroup group_alloc
1215 @{
1216 */
1217 
1218 /** \brief Parameters of new #VmaAllocation.
1219 
1220 To be used with functions like vmaCreateBuffer(), vmaCreateImage(), and many others.
1221 */
1222 typedef struct VmaAllocationCreateInfo
1223 {
1224     /// Use #VmaAllocationCreateFlagBits enum.
1225     VmaAllocationCreateFlags flags;
1226     /** \brief Intended usage of memory.
1227 
1228     You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n
1229     If `pool` is not null, this member is ignored.
1230     */
1231     VmaMemoryUsage usage;
1232     /** \brief Flags that must be set in a Memory Type chosen for an allocation.
1233 
1234     Leave 0 if you specify memory requirements in other way. \n
1235     If `pool` is not null, this member is ignored.*/
1236     VkMemoryPropertyFlags requiredFlags;
1237     /** \brief Flags that preferably should be set in a memory type chosen for an allocation.
1238 
1239     Set to 0 if no additional flags are preferred. \n
1240     If `pool` is not null, this member is ignored. */
1241     VkMemoryPropertyFlags preferredFlags;
1242     /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation.
1243 
1244     Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if
1245     it meets other requirements specified by this structure, with no further
1246     restrictions on memory type index. \n
1247     If `pool` is not null, this member is ignored.
1248     */
1249     uint32_t memoryTypeBits;
1250     /** \brief Pool that this allocation should be created in.
1251 
1252     Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members:
1253     `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.
1254     */
1255     VmaPool VMA_NULLABLE pool;
1256     /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().
1257 
1258     If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either
1259     null or pointer to a null-terminated string. The string will be then copied to
1260     internal buffer, so it doesn't need to be valid after allocation call.
1261     */
1262     void* VMA_NULLABLE pUserData;
1263     /** \brief A floating-point value between 0 and 1, indicating the priority of the allocation relative to other memory allocations.
1264 
1265     It is used only when #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT flag was used during creation of the #VmaAllocator object
1266     and this allocation ends up as dedicated or is explicitly forced as dedicated using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
1267     Otherwise, it has the priority of a memory block where it is placed and this variable is ignored.
1268     */
1269     float priority;
1270 } VmaAllocationCreateInfo;
1271 
1272 /// Describes parameter of created #VmaPool.
1273 typedef struct VmaPoolCreateInfo
1274 {
1275     /** \brief Vulkan memory type index to allocate this pool from.
1276     */
1277     uint32_t memoryTypeIndex;
1278     /** \brief Use combination of #VmaPoolCreateFlagBits.
1279     */
1280     VmaPoolCreateFlags flags;
1281     /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. Optional.
1282 
1283     Specify nonzero to set explicit, constant size of memory blocks used by this
1284     pool.
1285 
1286     Leave 0 to use default and let the library manage block sizes automatically.
1287     Sizes of particular blocks may vary.
1288     In this case, the pool will also support dedicated allocations.
1289     */
1290     VkDeviceSize blockSize;
1291     /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty.
1292 
1293     Set to 0 to have no preallocated blocks and allow the pool be completely empty.
1294     */
1295     size_t minBlockCount;
1296     /** \brief Maximum number of blocks that can be allocated in this pool. Optional.
1297 
1298     Set to 0 to use default, which is `SIZE_MAX`, which means no limit.
1299 
1300     Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated
1301     throughout whole lifetime of this pool.
1302     */
1303     size_t maxBlockCount;
1304     /** \brief A floating-point value between 0 and 1, indicating the priority of the allocations in this pool relative to other memory allocations.
1305 
1306     It is used only when #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT flag was used during creation of the #VmaAllocator object.
1307     Otherwise, this variable is ignored.
1308     */
1309     float priority;
1310     /** \brief Additional minimum alignment to be used for all allocations created from this pool. Can be 0.
1311 
1312     Leave 0 (default) not to impose any additional alignment. If not 0, it must be a power of two.
1313     It can be useful in cases where alignment returned by Vulkan by functions like `vkGetBufferMemoryRequirements` is not enough,
1314     e.g. when doing interop with OpenGL.
1315     */
1316     VkDeviceSize minAllocationAlignment;
1317     /** \brief Additional `pNext` chain to be attached to `VkMemoryAllocateInfo` used for every allocation made by this pool. Optional.
1318 
1319     Optional, can be null. If not null, it must point to a `pNext` chain of structures that can be attached to `VkMemoryAllocateInfo`.
1320     It can be useful for special needs such as adding `VkExportMemoryAllocateInfoKHR`.
1321     Structures pointed by this member must remain alive and unchanged for the whole lifetime of the custom pool.
1322 
1323     Please note that some structures, e.g. `VkMemoryPriorityAllocateInfoEXT`, `VkMemoryDedicatedAllocateInfoKHR`,
1324     can be attached automatically by this library when using other, more convenient of its features.
1325     */
1326     void* VMA_NULLABLE pMemoryAllocateNext;
1327 } VmaPoolCreateInfo;
1328 
1329 /** @} */
1330 
1331 /**
1332 \addtogroup group_alloc
1333 @{
1334 */
1335 
1336 /// Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
1337 typedef struct VmaAllocationInfo
1338 {
1339     /** \brief Memory type index that this allocation was allocated from.
1340 
1341     It never changes.
1342     */
1343     uint32_t memoryType;
1344     /** \brief Handle to Vulkan memory object.
1345 
1346     Same memory object can be shared by multiple allocations.
1347 
1348     It can change after the allocation is moved during \ref defragmentation.
1349     */
1350     VkDeviceMemory VMA_NULLABLE_NON_DISPATCHABLE deviceMemory;
1351     /** \brief Offset in `VkDeviceMemory` object to the beginning of this allocation, in bytes. `(deviceMemory, offset)` pair is unique to this allocation.
1352 
1353     You usually don't need to use this offset. If you create a buffer or an image together with the allocation using e.g. function
1354     vmaCreateBuffer(), vmaCreateImage(), functions that operate on these resources refer to the beginning of the buffer or image,
1355     not entire device memory block. Functions like vmaMapMemory(), vmaBindBufferMemory() also refer to the beginning of the allocation
1356     and apply this offset automatically.
1357 
1358     It can change after the allocation is moved during \ref defragmentation.
1359     */
1360     VkDeviceSize offset;
1361     /** \brief Size of this allocation, in bytes.
1362 
1363     It never changes.
1364 
1365     \note Allocation size returned in this variable may be greater than the size
1366     requested for the resource e.g. as `VkBufferCreateInfo::size`. Whole size of the
1367     allocation is accessible for operations on memory e.g. using a pointer after
1368     mapping with vmaMapMemory(), but operations on the resource e.g. using
1369     `vkCmdCopyBuffer` must be limited to the size of the resource.
1370     */
1371     VkDeviceSize size;
1372     /** \brief Pointer to the beginning of this allocation as mapped data.
1373 
1374     If the allocation hasn't been mapped using vmaMapMemory() and hasn't been
1375     created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value is null.
1376 
1377     It can change after call to vmaMapMemory(), vmaUnmapMemory().
1378     It can also change after the allocation is moved during \ref defragmentation.
1379     */
1380     void* VMA_NULLABLE pMappedData;
1381     /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData().
1382 
1383     It can change after call to vmaSetAllocationUserData() for this allocation.
1384     */
1385     void* VMA_NULLABLE pUserData;
1386     /** \brief Custom allocation name that was set with vmaSetAllocationName().
1387 
1388     It can change after call to vmaSetAllocationName() for this allocation.
1389 
1390     Another way to set custom name is to pass it in VmaAllocationCreateInfo::pUserData with
1391     additional flag #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT set [DEPRECATED].
1392     */
1393     const char* VMA_NULLABLE pName;
1394 } VmaAllocationInfo;
1395 
1396 /** \brief Parameters for defragmentation.
1397 
1398 To be used with function vmaBeginDefragmentation().
1399 */
1400 typedef struct VmaDefragmentationInfo
1401 {
1402     /// \brief Use combination of #VmaDefragmentationFlagBits.
1403     VmaDefragmentationFlags flags;
1404     /** \brief Custom pool to be defragmented.
1405 
1406     If null then default pools will undergo defragmentation process.
1407     */
1408     VmaPool VMA_NULLABLE pool;
1409     /** \brief Maximum numbers of bytes that can be copied during single pass, while moving allocations to different places.
1410 
1411     `0` means no limit.
1412     */
1413     VkDeviceSize maxBytesPerPass;
1414     /** \brief Maximum number of allocations that can be moved during single pass to a different place.
1415 
1416     `0` means no limit.
1417     */
1418     uint32_t maxAllocationsPerPass;
1419 } VmaDefragmentationInfo;
1420 
1421 /// Single move of an allocation to be done for defragmentation.
1422 typedef struct VmaDefragmentationMove
1423 {
1424     /// Operation to be performed on the allocation by vmaEndDefragmentationPass(). Default value is #VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY. You can modify it.
1425     VmaDefragmentationMoveOperation operation;
1426     /// Allocation that should be moved.
1427     VmaAllocation VMA_NOT_NULL srcAllocation;
1428     /** \brief Temporary allocation pointing to destination memory that will replace `srcAllocation`.
1429 
1430     \warning Do not store this allocation in your data structures! It exists only temporarily, for the duration of the defragmentation pass,
1431     to be used for binding new buffer/image to the destination memory using e.g. vmaBindBufferMemory().
1432     vmaEndDefragmentationPass() will destroy it and make `srcAllocation` point to this memory.
1433     */
1434     VmaAllocation VMA_NOT_NULL dstTmpAllocation;
1435 } VmaDefragmentationMove;
1436 
1437 /** \brief Parameters for incremental defragmentation steps.
1438 
1439 To be used with function vmaBeginDefragmentationPass().
1440 */
1441 typedef struct VmaDefragmentationPassMoveInfo
1442 {
1443     /// Number of elements in the `pMoves` array.
1444     uint32_t moveCount;
1445     /** \brief Array of moves to be performed by the user in the current defragmentation pass.
1446 
1447     Pointer to an array of `moveCount` elements, owned by VMA, created in vmaBeginDefragmentationPass(), destroyed in vmaEndDefragmentationPass().
1448 
1449     For each element, you should:
1450 
1451     1. Create a new buffer/image in the place pointed by VmaDefragmentationMove::dstMemory + VmaDefragmentationMove::dstOffset.
1452     2. Copy data from the VmaDefragmentationMove::srcAllocation e.g. using `vkCmdCopyBuffer`, `vkCmdCopyImage`.
1453     3. Make sure these commands finished executing on the GPU.
1454     4. Destroy the old buffer/image.
1455 
1456     Only then you can finish defragmentation pass by calling vmaEndDefragmentationPass().
1457     After this call, the allocation will point to the new place in memory.
1458 
1459     Alternatively, if you cannot move specific allocation, you can set VmaDefragmentationMove::operation to #VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE.
1460 
1461     Alternatively, if you decide you want to completely remove the allocation:
1462 
1463     1. Destroy its buffer/image.
1464     2. Set VmaDefragmentationMove::operation to #VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY.
1465 
1466     Then, after vmaEndDefragmentationPass() the allocation will be freed.
1467     */
1468     VmaDefragmentationMove* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(moveCount) pMoves;
1469 } VmaDefragmentationPassMoveInfo;
1470 
1471 /// Statistics returned for defragmentation process in function vmaEndDefragmentation().
1472 typedef struct VmaDefragmentationStats
1473 {
1474     /// Total number of bytes that have been copied while moving allocations to different places.
1475     VkDeviceSize bytesMoved;
1476     /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects.
1477     VkDeviceSize bytesFreed;
1478     /// Number of allocations that have been moved to different places.
1479     uint32_t allocationsMoved;
1480     /// Number of empty `VkDeviceMemory` objects that have been released to the system.
1481     uint32_t deviceMemoryBlocksFreed;
1482 } VmaDefragmentationStats;
1483 
1484 /** @} */
1485 
1486 /**
1487 \addtogroup group_virtual
1488 @{
1489 */
1490 
1491 /// Parameters of created #VmaVirtualBlock object to be passed to vmaCreateVirtualBlock().
1492 typedef struct VmaVirtualBlockCreateInfo
1493 {
1494     /** \brief Total size of the virtual block.
1495 
1496     Sizes can be expressed in bytes or any units you want as long as you are consistent in using them.
1497     For example, if you allocate from some array of structures, 1 can mean single instance of entire structure.
1498     */
1499     VkDeviceSize size;
1500 
1501     /** \brief Use combination of #VmaVirtualBlockCreateFlagBits.
1502     */
1503     VmaVirtualBlockCreateFlags flags;
1504 
1505     /** \brief Custom CPU memory allocation callbacks. Optional.
1506 
1507     Optional, can be null. When specified, they will be used for all CPU-side memory allocations.
1508     */
1509     const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks;
1510 } VmaVirtualBlockCreateInfo;
1511 
1512 /// Parameters of created virtual allocation to be passed to vmaVirtualAllocate().
1513 typedef struct VmaVirtualAllocationCreateInfo
1514 {
1515     /** \brief Size of the allocation.
1516 
1517     Cannot be zero.
1518     */
1519     VkDeviceSize size;
1520     /** \brief Required alignment of the allocation. Optional.
1521 
1522     Must be power of two. Special value 0 has the same meaning as 1 - means no special alignment is required, so allocation can start at any offset.
1523     */
1524     VkDeviceSize alignment;
1525     /** \brief Use combination of #VmaVirtualAllocationCreateFlagBits.
1526     */
1527     VmaVirtualAllocationCreateFlags flags;
1528     /** \brief Custom pointer to be associated with the allocation. Optional.
1529 
1530     It can be any value and can be used for user-defined purposes. It can be fetched or changed later.
1531     */
1532     void* VMA_NULLABLE pUserData;
1533 } VmaVirtualAllocationCreateInfo;
1534 
1535 /// Parameters of an existing virtual allocation, returned by vmaGetVirtualAllocationInfo().
1536 typedef struct VmaVirtualAllocationInfo
1537 {
1538     /** \brief Offset of the allocation.
1539 
1540     Offset at which the allocation was made.
1541     */
1542     VkDeviceSize offset;
1543     /** \brief Size of the allocation.
1544 
1545     Same value as passed in VmaVirtualAllocationCreateInfo::size.
1546     */
1547     VkDeviceSize size;
1548     /** \brief Custom pointer associated with the allocation.
1549 
1550     Same value as passed in VmaVirtualAllocationCreateInfo::pUserData or to vmaSetVirtualAllocationUserData().
1551     */
1552     void* VMA_NULLABLE pUserData;
1553 } VmaVirtualAllocationInfo;
1554 
1555 /** @} */
1556 
1557 #endif // _VMA_DATA_TYPES_DECLARATIONS
1558 
1559 #ifndef _VMA_FUNCTION_HEADERS
1560 
1561 /**
1562 \addtogroup group_init
1563 @{
1564 */
1565 
1566 /// Creates #VmaAllocator object.
1567 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
1568     const VmaAllocatorCreateInfo* VMA_NOT_NULL pCreateInfo,
1569     VmaAllocator VMA_NULLABLE* VMA_NOT_NULL pAllocator);
1570 
1571 /// Destroys allocator object.
1572 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
1573     VmaAllocator VMA_NULLABLE allocator);
1574 
1575 /** \brief Returns information about existing #VmaAllocator object - handle to Vulkan device etc.
1576 
1577 It might be useful if you want to keep just the #VmaAllocator handle and fetch other required handles to
1578 `VkPhysicalDevice`, `VkDevice` etc. every time using this function.
1579 */
1580 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(
1581     VmaAllocator VMA_NOT_NULL allocator,
1582     VmaAllocatorInfo* VMA_NOT_NULL pAllocatorInfo);
1583 
1584 /**
1585 PhysicalDeviceProperties are fetched from physicalDevice by the allocator.
1586 You can access it here, without fetching it again on your own.
1587 */
1588 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
1589     VmaAllocator VMA_NOT_NULL allocator,
1590     const VkPhysicalDeviceProperties* VMA_NULLABLE* VMA_NOT_NULL ppPhysicalDeviceProperties);
1591 
1592 /**
1593 PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator.
1594 You can access it here, without fetching it again on your own.
1595 */
1596 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
1597     VmaAllocator VMA_NOT_NULL allocator,
1598     const VkPhysicalDeviceMemoryProperties* VMA_NULLABLE* VMA_NOT_NULL ppPhysicalDeviceMemoryProperties);
1599 
1600 /**
1601 \brief Given Memory Type Index, returns Property Flags of this memory type.
1602 
1603 This is just a convenience function. Same information can be obtained using
1604 vmaGetMemoryProperties().
1605 */
1606 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
1607     VmaAllocator VMA_NOT_NULL allocator,
1608     uint32_t memoryTypeIndex,
1609     VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);
1610 
1611 /** \brief Sets index of the current frame.
1612 */
1613 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
1614     VmaAllocator VMA_NOT_NULL allocator,
1615     uint32_t frameIndex);
1616 
1617 /** @} */
1618 
1619 /**
1620 \addtogroup group_stats
1621 @{
1622 */
1623 
1624 /** \brief Retrieves statistics from current state of the Allocator.
1625 
1626 This function is called "calculate" not "get" because it has to traverse all
1627 internal data structures, so it may be quite slow. Use it for debugging purposes.
1628 For faster but more brief statistics suitable to be called every frame or every allocation,
1629 use vmaGetHeapBudgets().
1630 
1631 Note that when using allocator from multiple threads, returned information may immediately
1632 become outdated.
1633 */
1634 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStatistics(
1635     VmaAllocator VMA_NOT_NULL allocator,
1636     VmaTotalStatistics* VMA_NOT_NULL pStats);
1637 
1638 /** \brief Free empty block of the Allocator.
1639 */
1640 VMA_CALL_PRE void VMA_CALL_POST vmaFreeEmptyBlock(
1641     VmaAllocator VMA_NOT_NULL allocator);
1642 
1643 /** \brief Retrieves information about current memory usage and budget for all memory heaps.
1644 
1645 \param allocator
1646 \param[out] pBudgets Must point to array with number of elements at least equal to number of memory heaps in physical device used.
1647 
1648 This function is called "get" not "calculate" because it is very fast, suitable to be called
1649 every frame or every allocation. For more detailed statistics use vmaCalculateStatistics().
1650 
1651 Note that when using allocator from multiple threads, returned information may immediately
1652 become outdated.
1653 */
1654 VMA_CALL_PRE void VMA_CALL_POST vmaGetHeapBudgets(
1655     VmaAllocator VMA_NOT_NULL allocator,
1656     VmaBudget* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pBudgets);
1657 
1658 /** @} */
1659 
1660 /**
1661 \addtogroup group_alloc
1662 @{
1663 */
1664 
1665 /**
1666 \brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.
1667 
1668 This algorithm tries to find a memory type that:
1669 
1670 - Is allowed by memoryTypeBits.
1671 - Contains all the flags from pAllocationCreateInfo->requiredFlags.
1672 - Matches intended usage.
1673 - Has as many flags from pAllocationCreateInfo->preferredFlags as possible.
1674 
1675 \return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result
1676 from this function or any other allocating function probably means that your
1677 device doesn't support any memory type with requested features for the specific
1678 type of resource you want to use it for. Please check parameters of your
1679 resource, like image layout (OPTIMAL versus LINEAR) or mip level count.
1680 */
1681 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
1682     VmaAllocator VMA_NOT_NULL allocator,
1683     uint32_t memoryTypeBits,
1684     const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
1685     uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
1686 
1687 /**
1688 \brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.
1689 
1690 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
1691 It internally creates a temporary, dummy buffer that never has memory bound.
1692 */
1693 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
1694     VmaAllocator VMA_NOT_NULL allocator,
1695     const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
1696     const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
1697     uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
1698 
1699 /**
1700 \brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.
1701 
1702 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
1703 It internally creates a temporary, dummy image that never has memory bound.
1704 */
1705 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
1706     VmaAllocator VMA_NOT_NULL allocator,
1707     const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
1708     const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
1709     uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
1710 
1711 /** \brief Allocates Vulkan device memory and creates #VmaPool object.
1712 
1713 \param allocator Allocator object.
1714 \param pCreateInfo Parameters of pool to create.
1715 \param[out] pPool Handle to created pool.
1716 */
1717 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
1718     VmaAllocator VMA_NOT_NULL allocator,
1719     const VmaPoolCreateInfo* VMA_NOT_NULL pCreateInfo,
1720     VmaPool VMA_NULLABLE* VMA_NOT_NULL pPool);
1721 
1722 /** \brief Destroys #VmaPool object and frees Vulkan device memory.
1723 */
1724 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
1725     VmaAllocator VMA_NOT_NULL allocator,
1726     VmaPool VMA_NULLABLE pool);
1727 
1728 /** @} */
1729 
1730 /**
1731 \addtogroup group_stats
1732 @{
1733 */
1734 
1735 /** \brief Retrieves statistics of existing #VmaPool object.
1736 
1737 \param allocator Allocator object.
1738 \param pool Pool object.
1739 \param[out] pPoolStats Statistics of specified pool.
1740 */
1741 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStatistics(
1742     VmaAllocator VMA_NOT_NULL allocator,
1743     VmaPool VMA_NOT_NULL pool,
1744     VmaStatistics* VMA_NOT_NULL pPoolStats);
1745 
1746 /** \brief Retrieves detailed statistics of existing #VmaPool object.
1747 
1748 \param allocator Allocator object.
1749 \param pool Pool object.
1750 \param[out] pPoolStats Statistics of specified pool.
1751 */
1752 VMA_CALL_PRE void VMA_CALL_POST vmaCalculatePoolStatistics(
1753     VmaAllocator VMA_NOT_NULL allocator,
1754     VmaPool VMA_NOT_NULL pool,
1755     VmaDetailedStatistics* VMA_NOT_NULL pPoolStats);
1756 
1757 /** @} */
1758 
1759 /**
1760 \addtogroup group_alloc
1761 @{
1762 */
1763 
1764 /** \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions.
1765 
1766 Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
1767 `VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is
1768 `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
1769 
1770 Possible return values:
1771 
1772 - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool.
1773 - `VK_SUCCESS` - corruption detection has been performed and succeeded.
1774 - `VK_ERROR_UNKNOWN` - corruption detection has been performed and found memory corruptions around one of the allocations.
1775   `VMA_ASSERT` is also fired in that case.
1776 - Other value: Error returned by Vulkan, e.g. memory mapping failure.
1777 */
1778 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(
1779     VmaAllocator VMA_NOT_NULL allocator,
1780     VmaPool VMA_NOT_NULL pool);
1781 
1782 /** \brief Retrieves name of a custom pool.
1783 
1784 After the call `ppName` is either null or points to an internally-owned null-terminated string
1785 containing name of the pool that was previously set. The pointer becomes invalid when the pool is
1786 destroyed or its name is changed using vmaSetPoolName().
1787 */
1788 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
1789     VmaAllocator VMA_NOT_NULL allocator,
1790     VmaPool VMA_NOT_NULL pool,
1791     const char* VMA_NULLABLE* VMA_NOT_NULL ppName);
1792 
1793 /** \brief Sets name of a custom pool.
1794 
1795 `pName` can be either null or pointer to a null-terminated string with new name for the pool.
1796 Function makes internal copy of the string, so it can be changed or freed immediately after this call.
1797 */
1798 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
1799     VmaAllocator VMA_NOT_NULL allocator,
1800     VmaPool VMA_NOT_NULL pool,
1801     const char* VMA_NULLABLE pName);
1802 
1803 /** \brief General purpose memory allocation.
1804 
1805 \param allocator
1806 \param pVkMemoryRequirements
1807 \param pCreateInfo
1808 \param[out] pAllocation Handle to allocated memory.
1809 \param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
1810 
1811 You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
1812 
1813 It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(),
1814 vmaCreateBuffer(), vmaCreateImage() instead whenever possible.
1815 */
1816 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
1817     VmaAllocator VMA_NOT_NULL allocator,
1818     const VkMemoryRequirements* VMA_NOT_NULL pVkMemoryRequirements,
1819     const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
1820     VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,
1821     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
1822 
1823 /** \brief General purpose memory allocation for multiple allocation objects at once.
1824 
1825 \param allocator Allocator object.
1826 \param pVkMemoryRequirements Memory requirements for each allocation.
1827 \param pCreateInfo Creation parameters for each allocation.
1828 \param allocationCount Number of allocations to make.
1829 \param[out] pAllocations Pointer to array that will be filled with handles to created allocations.
1830 \param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations.
1831 
1832 You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
1833 
1834 Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding.
1835 It is just a general purpose allocation function able to make multiple allocations at once.
1836 It may be internally optimized to be more efficient than calling vmaAllocateMemory() `allocationCount` times.
1837 
1838 All allocations are made using same parameters. All of them are created out of the same memory pool and type.
1839 If any allocation fails, all allocations already made within this function call are also freed, so that when
1840 returned result is not `VK_SUCCESS`, `pAllocation` array is always entirely filled with `VK_NULL_HANDLE`.
1841 */
1842 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
1843     VmaAllocator VMA_NOT_NULL allocator,
1844     const VkMemoryRequirements* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pVkMemoryRequirements,
1845     const VmaAllocationCreateInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pCreateInfo,
1846     size_t allocationCount,
1847     VmaAllocation VMA_NULLABLE* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
1848     VmaAllocationInfo* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationInfo);
1849 
1850 /** \brief Allocates memory suitable for given `VkBuffer`.
1851 
1852 \param allocator
1853 \param buffer
1854 \param pCreateInfo
1855 \param[out] pAllocation Handle to allocated memory.
1856 \param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
1857 
1858 It only creates #VmaAllocation. To bind the memory to the buffer, use vmaBindBufferMemory().
1859 
1860 This is a special-purpose function. In most cases you should use vmaCreateBuffer().
1861 
1862 You must free the allocation using vmaFreeMemory() when no longer needed.
1863 */
1864 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
1865     VmaAllocator VMA_NOT_NULL allocator,
1866     VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
1867     const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
1868     VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,
1869     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
1870 
1871 /** \brief Allocates memory suitable for given `VkImage`.
1872 
1873 \param allocator
1874 \param image
1875 \param pCreateInfo
1876 \param[out] pAllocation Handle to allocated memory.
1877 \param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
1878 
1879 It only creates #VmaAllocation. To bind the memory to the buffer, use vmaBindImageMemory().
1880 
1881 This is a special-purpose function. In most cases you should use vmaCreateImage().
1882 
1883 You must free the allocation using vmaFreeMemory() when no longer needed.
1884 */
1885 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
1886     VmaAllocator VMA_NOT_NULL allocator,
1887     VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
1888     const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
1889     VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,
1890     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
1891 
1892 /** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().
1893 
1894 Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped.
1895 */
1896 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
1897     VmaAllocator VMA_NOT_NULL allocator,
1898     const VmaAllocation VMA_NULLABLE allocation);
1899 
1900 /** \brief Frees memory and destroys multiple allocations.
1901 
1902 Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding.
1903 It is just a general purpose function to free memory and destroy allocations made using e.g. vmaAllocateMemory(),
1904 vmaAllocateMemoryPages() and other functions.
1905 It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times.
1906 
1907 Allocations in `pAllocations` array can come from any memory pools and types.
1908 Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped.
1909 */
1910 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
1911     VmaAllocator VMA_NOT_NULL allocator,
1912     size_t allocationCount,
1913     const VmaAllocation VMA_NULLABLE* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations);
1914 
1915 /** \brief Returns current information about specified allocation.
1916 
1917 Current paramteres of given allocation are returned in `pAllocationInfo`.
1918 
1919 Although this function doesn't lock any mutex, so it should be quite efficient,
1920 you should avoid calling it too often.
1921 You can retrieve same VmaAllocationInfo structure while creating your resource, from function
1922 vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change
1923 (e.g. due to defragmentation).
1924 */
1925 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
1926     VmaAllocator VMA_NOT_NULL allocator,
1927     VmaAllocation VMA_NOT_NULL allocation,
1928     VmaAllocationInfo* VMA_NOT_NULL pAllocationInfo);
1929 
1930 /** \brief Sets pUserData in given allocation to new value.
1931 
1932 The value of pointer `pUserData` is copied to allocation's `pUserData`.
1933 It is opaque, so you can use it however you want - e.g.
1934 as a pointer, ordinal number or some handle to you own data.
1935 */
1936 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
1937     VmaAllocator VMA_NOT_NULL allocator,
1938     VmaAllocation VMA_NOT_NULL allocation,
1939     void* VMA_NULLABLE pUserData);
1940 
1941 /** \brief Sets pName in given allocation to new value.
1942 
1943 `pName` must be either null, or pointer to a null-terminated string. The function
1944 makes local copy of the string and sets it as allocation's `pName`. String
1945 passed as pName doesn't need to be valid for whole lifetime of the allocation -
1946 you can free it after this call. String previously pointed by allocation's
1947 `pName` is freed from memory.
1948 */
1949 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationName(
1950     VmaAllocator VMA_NOT_NULL allocator,
1951     VmaAllocation VMA_NOT_NULL allocation,
1952     const char* VMA_NULLABLE pName);
1953 
1954 /**
1955 \brief Given an allocation, returns Property Flags of its memory type.
1956 
1957 This is just a convenience function. Same information can be obtained using
1958 vmaGetAllocationInfo() + vmaGetMemoryProperties().
1959 */
1960 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationMemoryProperties(
1961     VmaAllocator VMA_NOT_NULL allocator,
1962     VmaAllocation VMA_NOT_NULL allocation,
1963     VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);
1964 
1965 /** \brief Maps memory represented by given allocation and returns pointer to it.
1966 
1967 Maps memory represented by given allocation to make it accessible to CPU code.
1968 When succeeded, `*ppData` contains pointer to first byte of this memory.
1969 
1970 \warning
1971 If the allocation is part of a bigger `VkDeviceMemory` block, returned pointer is
1972 correctly offsetted to the beginning of region assigned to this particular allocation.
1973 Unlike the result of `vkMapMemory`, it points to the allocation, not to the beginning of the whole block.
1974 You should not add VmaAllocationInfo::offset to it!
1975 
1976 Mapping is internally reference-counted and synchronized, so despite raw Vulkan
1977 function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory`
1978 multiple times simultaneously, it is safe to call this function on allocations
1979 assigned to the same memory block. Actual Vulkan memory will be mapped on first
1980 mapping and unmapped on last unmapping.
1981 
1982 If the function succeeded, you must call vmaUnmapMemory() to unmap the
1983 allocation when mapping is no longer needed or before freeing the allocation, at
1984 the latest.
1985 
1986 It also safe to call this function multiple times on the same allocation. You
1987 must call vmaUnmapMemory() same number of times as you called vmaMapMemory().
1988 
1989 It is also safe to call this function on allocation created with
1990 #VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time.
1991 You must still call vmaUnmapMemory() same number of times as you called
1992 vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the
1993 "0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag.
1994 
1995 This function fails when used on allocation made in memory type that is not
1996 `HOST_VISIBLE`.
1997 
1998 This function doesn't automatically flush or invalidate caches.
1999 If the allocation is made from a memory types that is not `HOST_COHERENT`,
2000 you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification.
2001 */
2002 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
2003     VmaAllocator VMA_NOT_NULL allocator,
2004     VmaAllocation VMA_NOT_NULL allocation,
2005     void* VMA_NULLABLE* VMA_NOT_NULL ppData);
2006 
2007 /** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
2008 
2009 For details, see description of vmaMapMemory().
2010 
2011 This function doesn't automatically flush or invalidate caches.
2012 If the allocation is made from a memory types that is not `HOST_COHERENT`,
2013 you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification.
2014 */
2015 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
2016     VmaAllocator VMA_NOT_NULL allocator,
2017     VmaAllocation VMA_NOT_NULL allocation);
2018 
2019 /** \brief Flushes memory of given allocation.
2020 
2021 Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation.
2022 It needs to be called after writing to a mapped memory for memory types that are not `HOST_COHERENT`.
2023 Unmap operation doesn't do that automatically.
2024 
2025 - `offset` must be relative to the beginning of allocation.
2026 - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
2027 - `offset` and `size` don't have to be aligned.
2028   They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
2029 - If `size` is 0, this call is ignored.
2030 - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
2031   this call is ignored.
2032 
2033 Warning! `offset` and `size` are relative to the contents of given `allocation`.
2034 If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively.
2035 Do not pass allocation's offset as `offset`!!!
2036 
2037 This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is
2038 called, otherwise `VK_SUCCESS`.
2039 */
2040 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(
2041     VmaAllocator VMA_NOT_NULL allocator,
2042     VmaAllocation VMA_NOT_NULL allocation,
2043     VkDeviceSize offset,
2044     VkDeviceSize size);
2045 
2046 /** \brief Invalidates memory of given allocation.
2047 
2048 Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation.
2049 It needs to be called before reading from a mapped memory for memory types that are not `HOST_COHERENT`.
2050 Map operation doesn't do that automatically.
2051 
2052 - `offset` must be relative to the beginning of allocation.
2053 - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
2054 - `offset` and `size` don't have to be aligned.
2055   They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
2056 - If `size` is 0, this call is ignored.
2057 - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
2058   this call is ignored.
2059 
2060 Warning! `offset` and `size` are relative to the contents of given `allocation`.
2061 If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively.
2062 Do not pass allocation's offset as `offset`!!!
2063 
2064 This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if
2065 it is called, otherwise `VK_SUCCESS`.
2066 */
2067 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(
2068     VmaAllocator VMA_NOT_NULL allocator,
2069     VmaAllocation VMA_NOT_NULL allocation,
2070     VkDeviceSize offset,
2071     VkDeviceSize size);
2072 
2073 /** \brief Flushes memory of given set of allocations.
2074 
2075 Calls `vkFlushMappedMemoryRanges()` for memory associated with given ranges of given allocations.
2076 For more information, see documentation of vmaFlushAllocation().
2077 
2078 \param allocator
2079 \param allocationCount
2080 \param allocations
2081 \param offsets If not null, it must point to an array of offsets of regions to flush, relative to the beginning of respective allocations. Null means all ofsets are zero.
2082 \param sizes If not null, it must point to an array of sizes of regions to flush in respective allocations. Null means `VK_WHOLE_SIZE` for all allocations.
2083 
2084 This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is
2085 called, otherwise `VK_SUCCESS`.
2086 */
2087 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
2088     VmaAllocator VMA_NOT_NULL allocator,
2089     uint32_t allocationCount,
2090     const VmaAllocation VMA_NOT_NULL* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
2091     const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
2092     const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
2093 
2094 /** \brief Invalidates memory of given set of allocations.
2095 
2096 Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given ranges of given allocations.
2097 For more information, see documentation of vmaInvalidateAllocation().
2098 
2099 \param allocator
2100 \param allocationCount
2101 \param allocations
2102 \param offsets If not null, it must point to an array of offsets of regions to flush, relative to the beginning of respective allocations. Null means all ofsets are zero.
2103 \param sizes If not null, it must point to an array of sizes of regions to flush in respective allocations. Null means `VK_WHOLE_SIZE` for all allocations.
2104 
2105 This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if it is
2106 called, otherwise `VK_SUCCESS`.
2107 */
2108 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
2109     VmaAllocator VMA_NOT_NULL allocator,
2110     uint32_t allocationCount,
2111     const VmaAllocation VMA_NOT_NULL* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
2112     const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
2113     const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
2114 
2115 /** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions.
2116 
2117 \param allocator
2118 \param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked.
2119 
2120 Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
2121 `VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are
2122 `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
2123 
2124 Possible return values:
2125 
2126 - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types.
2127 - `VK_SUCCESS` - corruption detection has been performed and succeeded.
2128 - `VK_ERROR_UNKNOWN` - corruption detection has been performed and found memory corruptions around one of the allocations.
2129   `VMA_ASSERT` is also fired in that case.
2130 - Other value: Error returned by Vulkan, e.g. memory mapping failure.
2131 */
2132 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(
2133     VmaAllocator VMA_NOT_NULL allocator,
2134     uint32_t memoryTypeBits);
2135 
2136 /** \brief Begins defragmentation process.
2137 
2138 \param allocator Allocator object.
2139 \param pInfo Structure filled with parameters of defragmentation.
2140 \param[out] pContext Context object that must be passed to vmaEndDefragmentation() to finish defragmentation.
2141 \returns
2142 - `VK_SUCCESS` if defragmentation can begin.
2143 - `VK_ERROR_FEATURE_NOT_PRESENT` if defragmentation is not supported.
2144 
2145 For more information about defragmentation, see documentation chapter:
2146 [Defragmentation](@ref defragmentation).
2147 */
2148 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentation(
2149     VmaAllocator VMA_NOT_NULL allocator,
2150     const VmaDefragmentationInfo* VMA_NOT_NULL pInfo,
2151     VmaDefragmentationContext VMA_NULLABLE* VMA_NOT_NULL pContext);
2152 
2153 /** \brief Ends defragmentation process.
2154 
2155 \param allocator Allocator object.
2156 \param context Context object that has been created by vmaBeginDefragmentation().
2157 \param[out] pStats Optional stats for the defragmentation. Can be null.
2158 
2159 Use this function to finish defragmentation started by vmaBeginDefragmentation().
2160 */
2161 VMA_CALL_PRE void VMA_CALL_POST vmaEndDefragmentation(
2162     VmaAllocator VMA_NOT_NULL allocator,
2163     VmaDefragmentationContext VMA_NOT_NULL context,
2164     VmaDefragmentationStats* VMA_NULLABLE pStats);
2165 
2166 /** \brief Starts single defragmentation pass.
2167 
2168 \param allocator Allocator object.
2169 \param context Context object that has been created by vmaBeginDefragmentation().
2170 \param[out] pPassInfo Computed informations for current pass.
2171 \returns
2172 - `VK_SUCCESS` if no more moves are possible. Then you can omit call to vmaEndDefragmentationPass() and simply end whole defragmentation.
2173 - `VK_INCOMPLETE` if there are pending moves returned in `pPassInfo`. You need to perform them, call vmaEndDefragmentationPass(),
2174   and then preferably try another pass with vmaBeginDefragmentationPass().
2175 */
2176 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
2177     VmaAllocator VMA_NOT_NULL allocator,
2178     VmaDefragmentationContext VMA_NOT_NULL context,
2179     VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo);
2180 
2181 /** \brief Ends single defragmentation pass.
2182 
2183 \param allocator Allocator object.
2184 \param context Context object that has been created by vmaBeginDefragmentation().
2185 \param pPassInfo Computed informations for current pass filled by vmaBeginDefragmentationPass() and possibly modified by you.
2186 
2187 Returns `VK_SUCCESS` if no more moves are possible or `VK_INCOMPLETE` if more defragmentations are possible.
2188 
2189 Ends incremental defragmentation pass and commits all defragmentation moves from `pPassInfo`.
2190 After this call:
2191 
2192 - Allocations at `pPassInfo[i].srcAllocation` that had `pPassInfo[i].operation ==` #VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY
2193   (which is the default) will be pointing to the new destination place.
2194 - Allocation at `pPassInfo[i].srcAllocation` that had `pPassInfo[i].operation ==` #VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY
2195   will be freed.
2196 
2197 If no more moves are possible you can end whole defragmentation.
2198 */
2199 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
2200     VmaAllocator VMA_NOT_NULL allocator,
2201     VmaDefragmentationContext VMA_NOT_NULL context,
2202     VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo);
2203 
2204 /** \brief Binds buffer to allocation.
2205 
2206 Binds specified buffer to region of memory represented by specified allocation.
2207 Gets `VkDeviceMemory` handle and offset from the allocation.
2208 If you want to create a buffer, allocate memory for it and bind them together separately,
2209 you should use this function for binding instead of standard `vkBindBufferMemory()`,
2210 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
2211 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
2212 (which is illegal in Vulkan).
2213 
2214 It is recommended to use function vmaCreateBuffer() instead of this one.
2215 */
2216 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
2217     VmaAllocator VMA_NOT_NULL allocator,
2218     VmaAllocation VMA_NOT_NULL allocation,
2219     VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer);
2220 
2221 /** \brief Binds buffer to allocation with additional parameters.
2222 
2223 \param allocator
2224 \param allocation
2225 \param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0.
2226 \param buffer
2227 \param pNext A chain of structures to be attached to `VkBindBufferMemoryInfoKHR` structure used internally. Normally it should be null.
2228 
2229 This function is similar to vmaBindBufferMemory(), but it provides additional parameters.
2230 
2231 If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag
2232 or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails.
2233 */
2234 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
2235     VmaAllocator VMA_NOT_NULL allocator,
2236     VmaAllocation VMA_NOT_NULL allocation,
2237     VkDeviceSize allocationLocalOffset,
2238     VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
2239     const void* VMA_NULLABLE pNext);
2240 
2241 /** \brief Binds image to allocation.
2242 
2243 Binds specified image to region of memory represented by specified allocation.
2244 Gets `VkDeviceMemory` handle and offset from the allocation.
2245 If you want to create an image, allocate memory for it and bind them together separately,
2246 you should use this function for binding instead of standard `vkBindImageMemory()`,
2247 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
2248 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
2249 (which is illegal in Vulkan).
2250 
2251 It is recommended to use function vmaCreateImage() instead of this one.
2252 */
2253 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
2254     VmaAllocator VMA_NOT_NULL allocator,
2255     VmaAllocation VMA_NOT_NULL allocation,
2256     VkImage VMA_NOT_NULL_NON_DISPATCHABLE image);
2257 
2258 /** \brief Binds image to allocation with additional parameters.
2259 
2260 \param allocator
2261 \param allocation
2262 \param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0.
2263 \param image
2264 \param pNext A chain of structures to be attached to `VkBindImageMemoryInfoKHR` structure used internally. Normally it should be null.
2265 
2266 This function is similar to vmaBindImageMemory(), but it provides additional parameters.
2267 
2268 If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag
2269 or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails.
2270 */
2271 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
2272     VmaAllocator VMA_NOT_NULL allocator,
2273     VmaAllocation VMA_NOT_NULL allocation,
2274     VkDeviceSize allocationLocalOffset,
2275     VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
2276     const void* VMA_NULLABLE pNext);
2277 
2278 /** \brief Creates a new `VkBuffer`, allocates and binds memory for it.
2279 
2280 \param allocator
2281 \param pBufferCreateInfo
2282 \param pAllocationCreateInfo
2283 \param[out] pBuffer Buffer that was created.
2284 \param[out] pAllocation Allocation that was created.
2285 \param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
2286 
2287 This function automatically:
2288 
2289 -# Creates buffer.
2290 -# Allocates appropriate memory for it.
2291 -# Binds the buffer with the memory.
2292 
2293 If any of these operations fail, buffer and allocation are not created,
2294 returned value is negative error code, `*pBuffer` and `*pAllocation` are null.
2295 
2296 If the function succeeded, you must destroy both buffer and allocation when you
2297 no longer need them using either convenience function vmaDestroyBuffer() or
2298 separately, using `vkDestroyBuffer()` and vmaFreeMemory().
2299 
2300 If #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used,
2301 VK_KHR_dedicated_allocation extension is used internally to query driver whether
2302 it requires or prefers the new buffer to have dedicated allocation. If yes,
2303 and if dedicated allocation is possible
2304 (#VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated
2305 allocation for this buffer, just like when using
2306 #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
2307 
2308 \note This function creates a new `VkBuffer`. Sub-allocation of parts of one large buffer,
2309 although recommended as a good practice, is out of scope of this library and could be implemented
2310 by the user as a higher-level logic on top of VMA.
2311 */
2312 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
2313     VmaAllocator VMA_NOT_NULL allocator,
2314     const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
2315     const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2316     VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer,
2317     VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,
2318     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
2319 
2320 /** \brief Creates a buffer with additional minimum alignment.
2321 
2322 Similar to vmaCreateBuffer() but provides additional parameter `minAlignment` which allows to specify custom,
2323 minimum alignment to be used when placing the buffer inside a larger memory block, which may be needed e.g.
2324 for interop with OpenGL.
2325 */
2326 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment(
2327     VmaAllocator VMA_NOT_NULL allocator,
2328     const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
2329     const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2330     VkDeviceSize minAlignment,
2331     VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer,
2332     VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,
2333     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
2334 
2335 /** \brief Creates a new `VkBuffer`, binds already created memory for it.
2336 
2337 \param allocator
2338 \param allocation Allocation that provides memory to be used for binding new buffer to it.
2339 \param pBufferCreateInfo
2340 \param[out] pBuffer Buffer that was created.
2341 
2342 This function automatically:
2343 
2344 -# Creates buffer.
2345 -# Binds the buffer with the supplied memory.
2346 
2347 If any of these operations fail, buffer is not created,
2348 returned value is negative error code and `*pBuffer` is null.
2349 
2350 If the function succeeded, you must destroy the buffer when you
2351 no longer need it using `vkDestroyBuffer()`. If you want to also destroy the corresponding
2352 allocation you can use convenience function vmaDestroyBuffer().
2353 */
2354 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer(
2355     VmaAllocator VMA_NOT_NULL allocator,
2356     VmaAllocation VMA_NOT_NULL allocation,
2357     const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
2358     VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer);
2359 
2360 /** \brief Destroys Vulkan buffer and frees allocated memory.
2361 
2362 This is just a convenience function equivalent to:
2363 
2364 \code
2365 vkDestroyBuffer(device, buffer, allocationCallbacks);
2366 vmaFreeMemory(allocator, allocation);
2367 \endcode
2368 
2369 It it safe to pass null as buffer and/or allocation.
2370 */
2371 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
2372     VmaAllocator VMA_NOT_NULL allocator,
2373     VkBuffer VMA_NULLABLE_NON_DISPATCHABLE buffer,
2374     VmaAllocation VMA_NULLABLE allocation);
2375 
2376 /// Function similar to vmaCreateBuffer().
2377 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
2378     VmaAllocator VMA_NOT_NULL allocator,
2379     const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
2380     const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2381     VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage,
2382     VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,
2383     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
2384 
2385 /// Function similar to vmaCreateAliasingBuffer().
2386 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage(
2387     VmaAllocator VMA_NOT_NULL allocator,
2388     VmaAllocation VMA_NOT_NULL allocation,
2389     const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
2390     VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage);
2391 
2392 /** \brief Destroys Vulkan image and frees allocated memory.
2393 
2394 This is just a convenience function equivalent to:
2395 
2396 \code
2397 vkDestroyImage(device, image, allocationCallbacks);
2398 vmaFreeMemory(allocator, allocation);
2399 \endcode
2400 
2401 It it safe to pass null as image and/or allocation.
2402 */
2403 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
2404     VmaAllocator VMA_NOT_NULL allocator,
2405     VkImage VMA_NULLABLE_NON_DISPATCHABLE image,
2406     VmaAllocation VMA_NULLABLE allocation);
2407 
2408 /** @} */
2409 
2410 /**
2411 \addtogroup group_virtual
2412 @{
2413 */
2414 
2415 /** \brief Creates new #VmaVirtualBlock object.
2416 
2417 \param pCreateInfo Parameters for creation.
2418 \param[out] pVirtualBlock Returned virtual block object or `VMA_NULL` if creation failed.
2419 */
2420 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock(
2421     const VmaVirtualBlockCreateInfo* VMA_NOT_NULL pCreateInfo,
2422     VmaVirtualBlock VMA_NULLABLE* VMA_NOT_NULL pVirtualBlock);
2423 
2424 /** \brief Destroys #VmaVirtualBlock object.
2425 
2426 Please note that you should consciously handle virtual allocations that could remain unfreed in the block.
2427 You should either free them individually using vmaVirtualFree() or call vmaClearVirtualBlock()
2428 if you are sure this is what you want. If you do neither, an assert is called.
2429 
2430 If you keep pointers to some additional metadata associated with your virtual allocations in their `pUserData`,
2431 don't forget to free them.
2432 */
2433 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyVirtualBlock(
2434     VmaVirtualBlock VMA_NULLABLE virtualBlock);
2435 
2436 /** \brief Returns true of the #VmaVirtualBlock is empty - contains 0 virtual allocations and has all its space available for new allocations.
2437 */
2438 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaIsVirtualBlockEmpty(
2439     VmaVirtualBlock VMA_NOT_NULL virtualBlock);
2440 
2441 /** \brief Returns information about a specific virtual allocation within a virtual block, like its size and `pUserData` pointer.
2442 */
2443 VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo(
2444     VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2445     VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo);
2446 
2447 /** \brief Allocates new virtual allocation inside given #VmaVirtualBlock.
2448 
2449 If the allocation fails due to not enough free space available, `VK_ERROR_OUT_OF_DEVICE_MEMORY` is returned
2450 (despite the function doesn't ever allocate actual GPU memory).
2451 `pAllocation` is then set to `VK_NULL_HANDLE` and `pOffset`, if not null, it set to `UINT64_MAX`.
2452 
2453 \param virtualBlock Virtual block
2454 \param pCreateInfo Parameters for the allocation
2455 \param[out] pAllocation Returned handle of the new allocation
2456 \param[out] pOffset Returned offset of the new allocation. Optional, can be null.
2457 */
2458 VMA_CALL_PRE VkResult VMA_CALL_POST vmaVirtualAllocate(
2459     VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2460     const VmaVirtualAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
2461     VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pAllocation,
2462     VkDeviceSize* VMA_NULLABLE pOffset);
2463 
2464 /** \brief Frees virtual allocation inside given #VmaVirtualBlock.
2465 
2466 It is correct to call this function with `allocation == VK_NULL_HANDLE` - it does nothing.
2467 */
2468 VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree(
2469     VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2470     VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE allocation);
2471 
2472 /** \brief Frees all virtual allocations inside given #VmaVirtualBlock.
2473 
2474 You must either call this function or free each virtual allocation individually with vmaVirtualFree()
2475 before destroying a virtual block. Otherwise, an assert is called.
2476 
2477 If you keep pointer to some additional metadata associated with your virtual allocation in its `pUserData`,
2478 don't forget to free it as well.
2479 */
2480 VMA_CALL_PRE void VMA_CALL_POST vmaClearVirtualBlock(
2481     VmaVirtualBlock VMA_NOT_NULL virtualBlock);
2482 
2483 /** \brief Changes custom pointer associated with given virtual allocation.
2484 */
2485 VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData(
2486     VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2487     VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation,
2488     void* VMA_NULLABLE pUserData);
2489 
2490 /** \brief Calculates and returns statistics about virtual allocations and memory usage in given #VmaVirtualBlock.
2491 
2492 This function is fast to call. For more detailed statistics, see vmaCalculateVirtualBlockStatistics().
2493 */
2494 VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualBlockStatistics(
2495     VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2496     VmaStatistics* VMA_NOT_NULL pStats);
2497 
2498 /** \brief Calculates and returns detailed statistics about virtual allocations and memory usage in given #VmaVirtualBlock.
2499 
2500 This function is slow to call. Use for debugging purposes.
2501 For less detailed statistics, see vmaGetVirtualBlockStatistics().
2502 */
2503 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateVirtualBlockStatistics(
2504     VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2505     VmaDetailedStatistics* VMA_NOT_NULL pStats);
2506 
2507 /** @} */
2508 
2509 #if VMA_STATS_STRING_ENABLED
2510 /**
2511 \addtogroup group_stats
2512 @{
2513 */
2514 
2515 /** \brief Builds and returns a null-terminated string in JSON format with information about given #VmaVirtualBlock.
2516 \param virtualBlock Virtual block.
2517 \param[out] ppStatsString Returned string.
2518 \param detailedMap Pass `VK_FALSE` to only obtain statistics as returned by vmaCalculateVirtualBlockStatistics(). Pass `VK_TRUE` to also obtain full list of allocations and free spaces.
2519 
2520 Returned string must be freed using vmaFreeVirtualBlockStatsString().
2521 */
2522 VMA_CALL_PRE void VMA_CALL_POST vmaBuildVirtualBlockStatsString(
2523     VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2524     char* VMA_NULLABLE* VMA_NOT_NULL ppStatsString,
2525     VkBool32 detailedMap);
2526 
2527 /// Frees a string returned by vmaBuildVirtualBlockStatsString().
2528 VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString(
2529     VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2530     char* VMA_NULLABLE pStatsString);
2531 
2532 /** \brief Builds and returns statistics as a null-terminated string in JSON format.
2533 \param allocator
2534 \param[out] ppStatsString Must be freed using vmaFreeStatsString() function.
2535 \param detailedMap
2536 */
2537 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
2538     VmaAllocator VMA_NOT_NULL allocator,
2539     char* VMA_NULLABLE* VMA_NOT_NULL ppStatsString,
2540     VkBool32 detailedMap);
2541 
2542 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
2543     VmaAllocator VMA_NOT_NULL allocator,
2544     char* VMA_NULLABLE pStatsString);
2545 
2546 /** @} */
2547 
2548 #endif // VMA_STATS_STRING_ENABLED
2549 
2550 #endif // _VMA_FUNCTION_HEADERS
2551 
2552 #ifdef __cplusplus
2553 }
2554 #endif
2555 
2556 #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
2557 
2558 ////////////////////////////////////////////////////////////////////////////////
2559 ////////////////////////////////////////////////////////////////////////////////
2560 //
2561 //    IMPLEMENTATION
2562 //
2563 ////////////////////////////////////////////////////////////////////////////////
2564 ////////////////////////////////////////////////////////////////////////////////
2565 
2566 // For Visual Studio IntelliSense.
2567 #if defined(__cplusplus) && defined(__INTELLISENSE__)
2568 #define VMA_IMPLEMENTATION
2569 #endif
2570 
2571 #ifdef VMA_IMPLEMENTATION
2572 #undef VMA_IMPLEMENTATION
2573 
2574 #include <cstdint>
2575 #include <cstdlib>
2576 #include <cstring>
2577 #include <utility>
2578 #include <type_traits>
2579 
2580 #ifdef _MSC_VER
2581     #include <intrin.h> // For functions like __popcnt, _BitScanForward etc.
2582 #endif
2583 #if __cplusplus >= 202002L || _MSVC_LANG >= 202002L // C++20
2584     #include <bit> // For std::popcount
2585 #endif
2586 
2587 /*******************************************************************************
2588 CONFIGURATION SECTION
2589 
2590 Define some of these macros before each #include of this header or change them
2591 here if you need other then default behavior depending on your environment.
2592 */
2593 #ifndef _VMA_CONFIGURATION
2594 
2595 /*
2596 Define this macro to 1 to make the library fetch pointers to Vulkan functions
2597 internally, like:
2598 
2599     vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
2600 */
2601 #if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
2602     #define VMA_STATIC_VULKAN_FUNCTIONS 1
2603 #endif
2604 
2605 /*
2606 Define this macro to 1 to make the library fetch pointers to Vulkan functions
2607 internally, like:
2608 
2609     vulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkGetDeviceProcAddr(device, "vkAllocateMemory");
2610 
2611 To use this feature in new versions of VMA you now have to pass
2612 VmaVulkanFunctions::vkGetInstanceProcAddr and vkGetDeviceProcAddr as
2613 VmaAllocatorCreateInfo::pVulkanFunctions. Other members can be null.
2614 */
2615 #if !defined(VMA_DYNAMIC_VULKAN_FUNCTIONS)
2616     #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
2617 #endif
2618 
2619 #ifndef VMA_USE_STL_SHARED_MUTEX
2620     // Compiler conforms to C++17.
2621     #if __cplusplus >= 201703L
2622         #define VMA_USE_STL_SHARED_MUTEX 1
2623     // Visual studio defines __cplusplus properly only when passed additional parameter: /Zc:__cplusplus
2624     // Otherwise it is always 199711L, despite shared_mutex works since Visual Studio 2015 Update 2.
2625     #elif defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && __cplusplus == 199711L && _MSVC_LANG >= 201703L
2626         #define VMA_USE_STL_SHARED_MUTEX 1
2627     #else
2628         #define VMA_USE_STL_SHARED_MUTEX 0
2629     #endif
2630 #endif
2631 
2632 /*
2633 Define this macro to include custom header files without having to edit this file directly, e.g.:
2634 
2635     // Inside of "my_vma_configuration_user_includes.h":
2636 
2637     #include "my_custom_assert.h" // for MY_CUSTOM_ASSERT
2638     #include "my_custom_min.h" // for my_custom_min
2639     #include <algorithm>
2640     #include <mutex>
2641 
2642     // Inside a different file, which includes "vk_mem_alloc.h":
2643 
2644     #define VMA_CONFIGURATION_USER_INCLUDES_H "my_vma_configuration_user_includes.h"
2645     #define VMA_ASSERT(expr) MY_CUSTOM_ASSERT(expr)
2646     #define VMA_MIN(v1, v2)  (my_custom_min(v1, v2))
2647     #include "vk_mem_alloc.h"
2648     ...
2649 
2650 The following headers are used in this CONFIGURATION section only, so feel free to
2651 remove them if not needed.
2652 */
2653 #if !defined(VMA_CONFIGURATION_USER_INCLUDES_H)
2654     #include <cassert> // for assert
2655     #include <algorithm> // for min, max
2656     #include <mutex>
2657 #else
2658     #include VMA_CONFIGURATION_USER_INCLUDES_H
2659 #endif
2660 
2661 #ifndef VMA_NULL
2662    // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
2663    #define VMA_NULL   nullptr
2664 #endif
2665 
2666 #if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
2667 #include <cstdlib>
vma_aligned_alloc(size_t alignment,size_t size)2668 static void* vma_aligned_alloc(size_t alignment, size_t size)
2669 {
2670     // alignment must be >= sizeof(void*)
2671     if(alignment < sizeof(void*))
2672     {
2673         alignment = sizeof(void*);
2674     }
2675 
2676     return memalign(alignment, size);
2677 }
2678 #elif defined(__APPLE__) || defined(__ANDROID__) || (defined(__linux__) && defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC))
2679 #include <cstdlib>
2680 
2681 #if defined(__APPLE__)
2682 #include <AvailabilityMacros.h>
2683 #endif
2684 
vma_aligned_alloc(size_t alignment,size_t size)2685 static void* vma_aligned_alloc(size_t alignment, size_t size)
2686 {
2687     // Unfortunately, aligned_alloc causes VMA to crash due to it returning null pointers. (At least under 11.4)
2688     // Therefore, for now disable this specific exception until a proper solution is found.
2689     //#if defined(__APPLE__) && (defined(MAC_OS_X_VERSION_10_16) || defined(__IPHONE_14_0))
2690     //#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_16 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
2691     //    // For C++14, usr/include/malloc/_malloc.h declares aligned_alloc()) only
2692     //    // with the MacOSX11.0 SDK in Xcode 12 (which is what adds
2693     //    // MAC_OS_X_VERSION_10_16), even though the function is marked
2694     //    // availabe for 10.15. That is why the preprocessor checks for 10.16 but
2695     //    // the __builtin_available checks for 10.15.
2696     //    // People who use C++17 could call aligned_alloc with the 10.15 SDK already.
2697     //    if (__builtin_available(macOS 10.15, iOS 13, *))
2698     //        return aligned_alloc(alignment, size);
2699     //#endif
2700     //#endif
2701 
2702     // alignment must be >= sizeof(void*)
2703     if(alignment < sizeof(void*))
2704     {
2705         alignment = sizeof(void*);
2706     }
2707 
2708     void *pointer;
2709     if(posix_memalign(&pointer, alignment, size) == 0)
2710         return pointer;
2711     return VMA_NULL;
2712 }
2713 #elif defined(_WIN32)
vma_aligned_alloc(size_t alignment,size_t size)2714 static void* vma_aligned_alloc(size_t alignment, size_t size)
2715 {
2716     return _aligned_malloc(size, alignment);
2717 }
2718 #else
vma_aligned_alloc(size_t alignment,size_t size)2719 static void* vma_aligned_alloc(size_t alignment, size_t size)
2720 {
2721     return aligned_alloc(alignment, size);
2722 }
2723 #endif
2724 
2725 #if defined(_WIN32)
vma_aligned_free(void * ptr)2726 static void vma_aligned_free(void* ptr)
2727 {
2728     _aligned_free(ptr);
2729 }
2730 #else
vma_aligned_free(void * VMA_NULLABLE ptr)2731 static void vma_aligned_free(void* VMA_NULLABLE ptr)
2732 {
2733     free(ptr);
2734 }
2735 #endif
2736 
2737 // If your compiler is not compatible with C++11 and definition of
2738 // aligned_alloc() function is missing, uncommeting following line may help:
2739 
2740 //#include <malloc.h>
2741 
2742 // Normal assert to check for programmer's errors, especially in Debug configuration.
2743 #ifndef VMA_ASSERT
2744    #ifdef NDEBUG
2745        #define VMA_ASSERT(expr)
2746    #else
2747        #define VMA_ASSERT(expr)         assert(expr)
2748    #endif
2749 #endif
2750 
2751 // Assert that will be called very often, like inside data structures e.g. operator[].
2752 // Making it non-empty can make program slow.
2753 #ifndef VMA_HEAVY_ASSERT
2754    #ifdef NDEBUG
2755        #define VMA_HEAVY_ASSERT(expr)
2756    #else
2757        #define VMA_HEAVY_ASSERT(expr)   //VMA_ASSERT(expr)
2758    #endif
2759 #endif
2760 
2761 #ifndef VMA_ALIGN_OF
2762    #define VMA_ALIGN_OF(type)       (__alignof(type))
2763 #endif
2764 
2765 #ifndef VMA_SYSTEM_ALIGNED_MALLOC
2766    #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) vma_aligned_alloc((alignment), (size))
2767 #endif
2768 
2769 #ifndef VMA_SYSTEM_ALIGNED_FREE
2770    // VMA_SYSTEM_FREE is the old name, but might have been defined by the user
2771    #if defined(VMA_SYSTEM_FREE)
2772       #define VMA_SYSTEM_ALIGNED_FREE(ptr)     VMA_SYSTEM_FREE(ptr)
2773    #else
2774       #define VMA_SYSTEM_ALIGNED_FREE(ptr)     vma_aligned_free(ptr)
2775     #endif
2776 #endif
2777 
2778 #ifndef VMA_COUNT_BITS_SET
2779     // Returns number of bits set to 1 in (v)
2780     #define VMA_COUNT_BITS_SET(v) VmaCountBitsSet(v)
2781 #endif
2782 
2783 #ifndef VMA_BITSCAN_LSB
2784     // Scans integer for index of first nonzero value from the Least Significant Bit (LSB). If mask is 0 then returns UINT8_MAX
2785     #define VMA_BITSCAN_LSB(mask) VmaBitScanLSB(mask)
2786 #endif
2787 
2788 #ifndef VMA_BITSCAN_MSB
2789     // Scans integer for index of first nonzero value from the Most Significant Bit (MSB). If mask is 0 then returns UINT8_MAX
2790     #define VMA_BITSCAN_MSB(mask) VmaBitScanMSB(mask)
2791 #endif
2792 
2793 #ifndef VMA_MIN
2794    #define VMA_MIN(v1, v2)    ((std::min)((v1), (v2)))
2795 #endif
2796 
2797 #ifndef VMA_MAX
2798    #define VMA_MAX(v1, v2)    ((std::max)((v1), (v2)))
2799 #endif
2800 
2801 #ifndef VMA_SWAP
2802    #define VMA_SWAP(v1, v2)   std::swap((v1), (v2))
2803 #endif
2804 
2805 #ifndef VMA_SORT
2806    #define VMA_SORT(beg, end, cmp)  std::sort(beg, end, cmp)
2807 #endif
2808 
2809 #ifndef VMA_DEBUG_LOG
2810    #define VMA_DEBUG_LOG(format, ...)
2811    /*
2812    #define VMA_DEBUG_LOG(format, ...) do { \
2813        printf(format, __VA_ARGS__); \
2814        printf("\n"); \
2815    } while(false)
2816    */
2817 #endif
2818 
2819 // Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
2820 #if VMA_STATS_STRING_ENABLED
VmaUint32ToStr(char * VMA_NOT_NULL outStr,size_t strLen,uint32_t num)2821     static inline void VmaUint32ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint32_t num)
2822     {
2823         snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
2824     }
VmaUint64ToStr(char * VMA_NOT_NULL outStr,size_t strLen,uint64_t num)2825     static inline void VmaUint64ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint64_t num)
2826     {
2827         snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
2828     }
VmaPtrToStr(char * VMA_NOT_NULL outStr,size_t strLen,const void * ptr)2829     static inline void VmaPtrToStr(char* VMA_NOT_NULL outStr, size_t strLen, const void* ptr)
2830     {
2831         snprintf(outStr, strLen, "%p", ptr);
2832     }
2833 #endif
2834 
2835 #ifndef VMA_MUTEX
2836     class VmaMutex
2837     {
2838     public:
Lock()2839         void Lock() { m_Mutex.lock(); }
Unlock()2840         void Unlock() { m_Mutex.unlock(); }
TryLock()2841         bool TryLock() { return m_Mutex.try_lock(); }
2842     private:
2843         std::mutex m_Mutex;
2844     };
2845     #define VMA_MUTEX VmaMutex
2846 #endif
2847 
2848 // Read-write mutex, where "read" is shared access, "write" is exclusive access.
2849 #ifndef VMA_RW_MUTEX
2850     #if VMA_USE_STL_SHARED_MUTEX
2851         // Use std::shared_mutex from C++17.
2852         #include <shared_mutex>
2853         class VmaRWMutex
2854         {
2855         public:
LockRead()2856             void LockRead() { m_Mutex.lock_shared(); }
UnlockRead()2857             void UnlockRead() { m_Mutex.unlock_shared(); }
TryLockRead()2858             bool TryLockRead() { return m_Mutex.try_lock_shared(); }
LockWrite()2859             void LockWrite() { m_Mutex.lock(); }
UnlockWrite()2860             void UnlockWrite() { m_Mutex.unlock(); }
TryLockWrite()2861             bool TryLockWrite() { return m_Mutex.try_lock(); }
2862         private:
2863             std::shared_mutex m_Mutex;
2864         };
2865         #define VMA_RW_MUTEX VmaRWMutex
2866     #elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600
2867         // Use SRWLOCK from WinAPI.
2868         // Minimum supported client = Windows Vista, server = Windows Server 2008.
2869         class VmaRWMutex
2870         {
2871         public:
VmaRWMutex()2872             VmaRWMutex() { InitializeSRWLock(&m_Lock); }
LockRead()2873             void LockRead() { AcquireSRWLockShared(&m_Lock); }
UnlockRead()2874             void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
TryLockRead()2875             bool TryLockRead() { return TryAcquireSRWLockShared(&m_Lock) != FALSE; }
LockWrite()2876             void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
UnlockWrite()2877             void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
TryLockWrite()2878             bool TryLockWrite() { return TryAcquireSRWLockExclusive(&m_Lock) != FALSE; }
2879         private:
2880             SRWLOCK m_Lock;
2881         };
2882         #define VMA_RW_MUTEX VmaRWMutex
2883     #else
2884         // Less efficient fallback: Use normal mutex.
2885         class VmaRWMutex
2886         {
2887         public:
LockRead()2888             void LockRead() { m_Mutex.Lock(); }
UnlockRead()2889             void UnlockRead() { m_Mutex.Unlock(); }
TryLockRead()2890             bool TryLockRead() { return m_Mutex.TryLock(); }
LockWrite()2891             void LockWrite() { m_Mutex.Lock(); }
UnlockWrite()2892             void UnlockWrite() { m_Mutex.Unlock(); }
TryLockWrite()2893             bool TryLockWrite() { return m_Mutex.TryLock(); }
2894         private:
2895             VMA_MUTEX m_Mutex;
2896         };
2897         #define VMA_RW_MUTEX VmaRWMutex
2898     #endif // #if VMA_USE_STL_SHARED_MUTEX
2899 #endif // #ifndef VMA_RW_MUTEX
2900 
2901 /*
2902 If providing your own implementation, you need to implement a subset of std::atomic.
2903 */
2904 #ifndef VMA_ATOMIC_UINT32
2905     #include <atomic>
2906     #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
2907 #endif
2908 
2909 #ifndef VMA_ATOMIC_UINT64
2910     #include <atomic>
2911     #define VMA_ATOMIC_UINT64 std::atomic<uint64_t>
2912 #endif
2913 
2914 #ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
2915     /**
2916     Every allocation will have its own memory block.
2917     Define to 1 for debugging purposes only.
2918     */
2919     #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
2920 #endif
2921 
2922 #ifndef VMA_MIN_ALIGNMENT
2923     /**
2924     Minimum alignment of all allocations, in bytes.
2925     Set to more than 1 for debugging purposes. Must be power of two.
2926     */
2927     #ifdef VMA_DEBUG_ALIGNMENT // Old name
2928         #define VMA_MIN_ALIGNMENT VMA_DEBUG_ALIGNMENT
2929     #else
2930         #define VMA_MIN_ALIGNMENT (1)
2931     #endif
2932 #endif
2933 
2934 #ifndef VMA_DEBUG_MARGIN
2935     /**
2936     Minimum margin after every allocation, in bytes.
2937     Set nonzero for debugging purposes only.
2938     */
2939     #define VMA_DEBUG_MARGIN (0)
2940 #endif
2941 
2942 #ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
2943     /**
2944     Define this macro to 1 to automatically fill new allocations and destroyed
2945     allocations with some bit pattern.
2946     */
2947     #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)
2948 #endif
2949 
2950 #ifndef VMA_DEBUG_DETECT_CORRUPTION
2951     /**
2952     Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to
2953     enable writing magic value to the margin after every allocation and
2954     validating it, so that memory corruptions (out-of-bounds writes) are detected.
2955     */
2956     #define VMA_DEBUG_DETECT_CORRUPTION (0)
2957 #endif
2958 
2959 #ifndef VMA_DEBUG_GLOBAL_MUTEX
2960     /**
2961     Set this to 1 for debugging purposes only, to enable single mutex protecting all
2962     entry calls to the library. Can be useful for debugging multithreading issues.
2963     */
2964     #define VMA_DEBUG_GLOBAL_MUTEX (0)
2965 #endif
2966 
2967 #ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
2968     /**
2969     Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.
2970     Set to more than 1 for debugging purposes only. Must be power of two.
2971     */
2972     #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
2973 #endif
2974 
2975 #ifndef VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT
2976     /*
2977     Set this to 1 to make VMA never exceed VkPhysicalDeviceLimits::maxMemoryAllocationCount
2978     and return error instead of leaving up to Vulkan implementation what to do in such cases.
2979     */
2980     #define VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT (0)
2981 #endif
2982 
2983 #ifndef VMA_SMALL_HEAP_MAX_SIZE
2984    /// Maximum size of a memory heap in Vulkan to consider it "small".
2985    #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
2986 #endif
2987 
2988 #ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
2989    /// Default size of a block allocated as single VkDeviceMemory from a "large" heap.
2990    #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
2991 #endif
2992 
2993 /*
2994 Mapping hysteresis is a logic that launches when vmaMapMemory/vmaUnmapMemory is called
2995 or a persistently mapped allocation is created and destroyed several times in a row.
2996 It keeps additional +1 mapping of a device memory block to prevent calling actual
2997 vkMapMemory/vkUnmapMemory too many times, which may improve performance and help
2998 tools like RenderDOc.
2999 */
3000 #ifndef VMA_MAPPING_HYSTERESIS_ENABLED
3001     #define VMA_MAPPING_HYSTERESIS_ENABLED 1
3002 #endif
3003 
3004 #ifndef VMA_CLASS_NO_COPY
3005     #define VMA_CLASS_NO_COPY(className) \
3006         private: \
3007             className(const className&) = delete; \
3008             className& operator=(const className&) = delete;
3009 #endif
3010 
3011 #define VMA_VALIDATE(cond) do { if(!(cond)) { \
3012         VMA_ASSERT(0 && "Validation failed: " #cond); \
3013         return false; \
3014     } } while(false)
3015 
3016 /*******************************************************************************
3017 END OF CONFIGURATION
3018 */
3019 #endif // _VMA_CONFIGURATION
3020 
3021 
3022 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC;
3023 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
3024 // Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.
3025 static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
3026 
3027 // Copy of some Vulkan definitions so we don't need to check their existence just to handle few constants.
3028 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY = 0x00000040;
3029 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY = 0x00000080;
3030 static const uint32_t VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY = 0x00020000;
3031 static const uint32_t VK_IMAGE_CREATE_DISJOINT_BIT_COPY = 0x00000200;
3032 static const int32_t VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT_COPY = 1000158000;
3033 static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
3034 static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
3035 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
3036 
3037 // This one is tricky. Vulkan specification defines this code as available since
3038 // Vulkan 1.0, but doesn't actually define it in Vulkan SDK earlier than 1.2.131.
3039 // See pull request #207.
3040 #define VK_ERROR_UNKNOWN_COPY ((VkResult)-13)
3041 
3042 
3043 #if VMA_STATS_STRING_ENABLED
3044 // Correspond to values of enum VmaSuballocationType.
3045 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] =
3046 {
3047     "FREE",
3048     "UNKNOWN",
3049     "BUFFER",
3050     "IMAGE_UNKNOWN",
3051     "IMAGE_LINEAR",
3052     "IMAGE_OPTIMAL",
3053 };
3054 #endif
3055 
3056 static VkAllocationCallbacks VmaEmptyAllocationCallbacks =
3057     { VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
3058 
3059 
3060 #ifndef _VMA_ENUM_DECLARATIONS
3061 
3062 enum VmaSuballocationType
3063 {
3064     VMA_SUBALLOCATION_TYPE_FREE = 0,
3065     VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
3066     VMA_SUBALLOCATION_TYPE_BUFFER = 2,
3067     VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
3068     VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
3069     VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
3070     VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
3071 };
3072 
3073 enum VMA_CACHE_OPERATION
3074 {
3075     VMA_CACHE_FLUSH,
3076     VMA_CACHE_INVALIDATE
3077 };
3078 
3079 enum class VmaAllocationRequestType
3080 {
3081     Normal,
3082     TLSF,
3083     // Used by "Linear" algorithm.
3084     UpperAddress,
3085     EndOf1st,
3086     EndOf2nd,
3087 };
3088 
3089 #endif // _VMA_ENUM_DECLARATIONS
3090 
3091 #ifndef _VMA_FORWARD_DECLARATIONS
3092 // Opaque handle used by allocation algorithms to identify single allocation in any conforming way.
3093 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VmaAllocHandle);
3094 
3095 struct VmaMutexLock;
3096 struct VmaMutexLockRead;
3097 struct VmaMutexLockWrite;
3098 
3099 template<typename T>
3100 struct AtomicTransactionalIncrement;
3101 
3102 template<typename T>
3103 struct VmaStlAllocator;
3104 
3105 template<typename T, typename AllocatorT>
3106 class VmaVector;
3107 
3108 template<typename T, typename AllocatorT, size_t N>
3109 class VmaSmallVector;
3110 
3111 template<typename T>
3112 class VmaPoolAllocator;
3113 
3114 template<typename T>
3115 struct VmaListItem;
3116 
3117 template<typename T>
3118 class VmaRawList;
3119 
3120 template<typename T, typename AllocatorT>
3121 class VmaList;
3122 
3123 template<typename ItemTypeTraits>
3124 class VmaIntrusiveLinkedList;
3125 
3126 // Unused in this version
3127 #if 0
3128 template<typename T1, typename T2>
3129 struct VmaPair;
3130 template<typename FirstT, typename SecondT>
3131 struct VmaPairFirstLess;
3132 
3133 template<typename KeyT, typename ValueT>
3134 class VmaMap;
3135 #endif
3136 
3137 #if VMA_STATS_STRING_ENABLED
3138 class VmaStringBuilder;
3139 class VmaJsonWriter;
3140 #endif
3141 
3142 class VmaDeviceMemoryBlock;
3143 
3144 struct VmaDedicatedAllocationListItemTraits;
3145 class VmaDedicatedAllocationList;
3146 
3147 struct VmaSuballocation;
3148 struct VmaSuballocationOffsetLess;
3149 struct VmaSuballocationOffsetGreater;
3150 struct VmaSuballocationItemSizeLess;
3151 
3152 typedef VmaList<VmaSuballocation, VmaStlAllocator<VmaSuballocation>> VmaSuballocationList;
3153 
3154 struct VmaAllocationRequest;
3155 
3156 class VmaBlockMetadata;
3157 class VmaBlockMetadata_Linear;
3158 class VmaBlockMetadata_TLSF;
3159 
3160 class VmaBlockVector;
3161 
3162 struct VmaPoolListItemTraits;
3163 
3164 struct VmaCurrentBudgetData;
3165 
3166 class VmaAllocationObjectAllocator;
3167 
3168 #endif // _VMA_FORWARD_DECLARATIONS
3169 
3170 
3171 #ifndef _VMA_FUNCTIONS
3172 
3173 /*
3174 Returns number of bits set to 1 in (v).
3175 
3176 On specific platforms and compilers you can use instrinsics like:
3177 
3178 Visual Studio:
3179     return __popcnt(v);
3180 GCC, Clang:
3181     return static_cast<uint32_t>(__builtin_popcount(v));
3182 
3183 Define macro VMA_COUNT_BITS_SET to provide your optimized implementation.
3184 But you need to check in runtime whether user's CPU supports these, as some old processors don't.
3185 */
VmaCountBitsSet(uint32_t v)3186 static inline uint32_t VmaCountBitsSet(uint32_t v)
3187 {
3188 #if __cplusplus >= 202002L || _MSVC_LANG >= 202002L // C++20
3189     return std::popcount(v);
3190 #else
3191     uint32_t c = v - ((v >> 1) & 0x55555555);
3192     c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
3193     c = ((c >> 4) + c) & 0x0F0F0F0F;
3194     c = ((c >> 8) + c) & 0x00FF00FF;
3195     c = ((c >> 16) + c) & 0x0000FFFF;
3196     return c;
3197 #endif
3198 }
3199 
VmaBitScanLSB(uint64_t mask)3200 static inline uint8_t VmaBitScanLSB(uint64_t mask)
3201 {
3202 #if defined(_MSC_VER) && defined(_WIN64)
3203     unsigned long pos;
3204     if (_BitScanForward64(&pos, mask))
3205         return static_cast<uint8_t>(pos);
3206     return UINT8_MAX;
3207 #elif defined __GNUC__ || defined __clang__
3208     return static_cast<uint8_t>(__builtin_ffsll(mask)) - 1U;
3209 #else
3210     uint8_t pos = 0;
3211     uint64_t bit = 1;
3212     do
3213     {
3214         if (mask & bit)
3215             return pos;
3216         bit <<= 1;
3217     } while (pos++ < 63);
3218     return UINT8_MAX;
3219 #endif
3220 }
3221 
VmaBitScanLSB(uint32_t mask)3222 static inline uint8_t VmaBitScanLSB(uint32_t mask)
3223 {
3224 #ifdef _MSC_VER
3225     unsigned long pos;
3226     if (_BitScanForward(&pos, mask))
3227         return static_cast<uint8_t>(pos);
3228     return UINT8_MAX;
3229 #elif defined __GNUC__ || defined __clang__
3230     return static_cast<uint8_t>(__builtin_ffs(mask)) - 1U;
3231 #else
3232     uint8_t pos = 0;
3233     uint32_t bit = 1;
3234     do
3235     {
3236         if (mask & bit)
3237             return pos;
3238         bit <<= 1;
3239     } while (pos++ < 31);
3240     return UINT8_MAX;
3241 #endif
3242 }
3243 
VmaBitScanMSB(uint64_t mask)3244 static inline uint8_t VmaBitScanMSB(uint64_t mask)
3245 {
3246 #if defined(_MSC_VER) && defined(_WIN64)
3247     unsigned long pos;
3248     if (_BitScanReverse64(&pos, mask))
3249         return static_cast<uint8_t>(pos);
3250 #elif defined __GNUC__ || defined __clang__
3251     if (mask)
3252         return 63 - static_cast<uint8_t>(__builtin_clzll(mask));
3253 #else
3254     uint8_t pos = 63;
3255     uint64_t bit = 1ULL << 63;
3256     do
3257     {
3258         if (mask & bit)
3259             return pos;
3260         bit >>= 1;
3261     } while (pos-- > 0);
3262 #endif
3263     return UINT8_MAX;
3264 }
3265 
VmaBitScanMSB(uint32_t mask)3266 static inline uint8_t VmaBitScanMSB(uint32_t mask)
3267 {
3268 #ifdef _MSC_VER
3269     unsigned long pos;
3270     if (_BitScanReverse(&pos, mask))
3271         return static_cast<uint8_t>(pos);
3272 #elif defined __GNUC__ || defined __clang__
3273     if (mask)
3274         return 31 - static_cast<uint8_t>(__builtin_clz(mask));
3275 #else
3276     uint8_t pos = 31;
3277     uint32_t bit = 1UL << 31;
3278     do
3279     {
3280         if (mask & bit)
3281             return pos;
3282         bit >>= 1;
3283     } while (pos-- > 0);
3284 #endif
3285     return UINT8_MAX;
3286 }
3287 
3288 /*
3289 Returns true if given number is a power of two.
3290 T must be unsigned integer number or signed integer but always nonnegative.
3291 For 0 returns true.
3292 */
3293 template <typename T>
VmaIsPow2(T x)3294 inline bool VmaIsPow2(T x)
3295 {
3296     return (x & (x - 1)) == 0;
3297 }
3298 
3299 // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
3300 // Use types like uint32_t, uint64_t as T.
3301 template <typename T>
VmaAlignUp(T val,T alignment)3302 static inline T VmaAlignUp(T val, T alignment)
3303 {
3304     VMA_HEAVY_ASSERT(VmaIsPow2(alignment));
3305     return (val + alignment - 1) & ~(alignment - 1);
3306 }
3307 
3308 // Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8.
3309 // Use types like uint32_t, uint64_t as T.
3310 template <typename T>
VmaAlignDown(T val,T alignment)3311 static inline T VmaAlignDown(T val, T alignment)
3312 {
3313     VMA_HEAVY_ASSERT(VmaIsPow2(alignment));
3314     return val & ~(alignment - 1);
3315 }
3316 
3317 // Division with mathematical rounding to nearest number.
3318 template <typename T>
VmaRoundDiv(T x,T y)3319 static inline T VmaRoundDiv(T x, T y)
3320 {
3321     return (x + (y / (T)2)) / y;
3322 }
3323 
3324 // Divide by 'y' and round up to nearest integer.
3325 template <typename T>
VmaDivideRoundingUp(T x,T y)3326 static inline T VmaDivideRoundingUp(T x, T y)
3327 {
3328     return (x + y - (T)1) / y;
3329 }
3330 
3331 // Returns smallest power of 2 greater or equal to v.
VmaNextPow2(uint32_t v)3332 static inline uint32_t VmaNextPow2(uint32_t v)
3333 {
3334     v--;
3335     v |= v >> 1;
3336     v |= v >> 2;
3337     v |= v >> 4;
3338     v |= v >> 8;
3339     v |= v >> 16;
3340     v++;
3341     return v;
3342 }
3343 
VmaNextPow2(uint64_t v)3344 static inline uint64_t VmaNextPow2(uint64_t v)
3345 {
3346     v--;
3347     v |= v >> 1;
3348     v |= v >> 2;
3349     v |= v >> 4;
3350     v |= v >> 8;
3351     v |= v >> 16;
3352     v |= v >> 32;
3353     v++;
3354     return v;
3355 }
3356 
3357 // Returns largest power of 2 less or equal to v.
VmaPrevPow2(uint32_t v)3358 static inline uint32_t VmaPrevPow2(uint32_t v)
3359 {
3360     v |= v >> 1;
3361     v |= v >> 2;
3362     v |= v >> 4;
3363     v |= v >> 8;
3364     v |= v >> 16;
3365     v = v ^ (v >> 1);
3366     return v;
3367 }
3368 
VmaPrevPow2(uint64_t v)3369 static inline uint64_t VmaPrevPow2(uint64_t v)
3370 {
3371     v |= v >> 1;
3372     v |= v >> 2;
3373     v |= v >> 4;
3374     v |= v >> 8;
3375     v |= v >> 16;
3376     v |= v >> 32;
3377     v = v ^ (v >> 1);
3378     return v;
3379 }
3380 
VmaStrIsEmpty(const char * pStr)3381 static inline bool VmaStrIsEmpty(const char* pStr)
3382 {
3383     return pStr == VMA_NULL || *pStr == '\0';
3384 }
3385 
3386 /*
3387 Returns true if two memory blocks occupy overlapping pages.
3388 ResourceA must be in less memory offset than ResourceB.
3389 
3390 Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
3391 chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
3392 */
VmaBlocksOnSamePage(VkDeviceSize resourceAOffset,VkDeviceSize resourceASize,VkDeviceSize resourceBOffset,VkDeviceSize pageSize)3393 static inline bool VmaBlocksOnSamePage(
3394     VkDeviceSize resourceAOffset,
3395     VkDeviceSize resourceASize,
3396     VkDeviceSize resourceBOffset,
3397     VkDeviceSize pageSize)
3398 {
3399     VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
3400     VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
3401     VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
3402     VkDeviceSize resourceBStart = resourceBOffset;
3403     VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
3404     return resourceAEndPage == resourceBStartPage;
3405 }
3406 
3407 /*
3408 Returns true if given suballocation types could conflict and must respect
3409 VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
3410 or linear image and another one is optimal image. If type is unknown, behave
3411 conservatively.
3412 */
VmaIsBufferImageGranularityConflict(VmaSuballocationType suballocType1,VmaSuballocationType suballocType2)3413 static inline bool VmaIsBufferImageGranularityConflict(
3414     VmaSuballocationType suballocType1,
3415     VmaSuballocationType suballocType2)
3416 {
3417     if (suballocType1 > suballocType2)
3418     {
3419         VMA_SWAP(suballocType1, suballocType2);
3420     }
3421 
3422     switch (suballocType1)
3423     {
3424     case VMA_SUBALLOCATION_TYPE_FREE:
3425         return false;
3426     case VMA_SUBALLOCATION_TYPE_UNKNOWN:
3427         return true;
3428     case VMA_SUBALLOCATION_TYPE_BUFFER:
3429         return
3430             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3431             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3432     case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
3433         return
3434             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3435             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
3436             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3437     case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
3438         return
3439             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3440     case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
3441         return false;
3442     default:
3443         VMA_ASSERT(0);
3444         return true;
3445     }
3446 }
3447 
VmaWriteMagicValue(void * pData,VkDeviceSize offset)3448 static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
3449 {
3450 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
3451     uint32_t* pDst = (uint32_t*)((char*)pData + offset);
3452     const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3453     for (size_t i = 0; i < numberCount; ++i, ++pDst)
3454     {
3455         *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
3456     }
3457 #else
3458     // no-op
3459 #endif
3460 }
3461 
VmaValidateMagicValue(const void * pData,VkDeviceSize offset)3462 static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
3463 {
3464 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
3465     const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
3466     const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3467     for (size_t i = 0; i < numberCount; ++i, ++pSrc)
3468     {
3469         if (*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
3470         {
3471             return false;
3472         }
3473     }
3474 #endif
3475     return true;
3476 }
3477 
3478 /*
3479 Fills structure with parameters of an example buffer to be used for transfers
3480 during GPU memory defragmentation.
3481 */
VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo & outBufCreateInfo)3482 static void VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo& outBufCreateInfo)
3483 {
3484     memset(&outBufCreateInfo, 0, sizeof(outBufCreateInfo));
3485     outBufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
3486     outBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
3487     outBufCreateInfo.size = (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE; // Example size.
3488 }
3489 
3490 
3491 /*
3492 Performs binary search and returns iterator to first element that is greater or
3493 equal to (key), according to comparison (cmp).
3494 
3495 Cmp should return true if first argument is less than second argument.
3496 
3497 Returned value is the found element, if present in the collection or place where
3498 new element with value (key) should be inserted.
3499 */
3500 template <typename CmpLess, typename IterT, typename KeyT>
VmaBinaryFindFirstNotLess(IterT beg,IterT end,const KeyT & key,const CmpLess & cmp)3501 static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT& key, const CmpLess& cmp)
3502 {
3503     size_t down = 0, up = (end - beg);
3504     while (down < up)
3505     {
3506         const size_t mid = down + (up - down) / 2;  // Overflow-safe midpoint calculation
3507         if (cmp(*(beg + mid), key))
3508         {
3509             down = mid + 1;
3510         }
3511         else
3512         {
3513             up = mid;
3514         }
3515     }
3516     return beg + down;
3517 }
3518 
3519 template<typename CmpLess, typename IterT, typename KeyT>
VmaBinaryFindSorted(const IterT & beg,const IterT & end,const KeyT & value,const CmpLess & cmp)3520 IterT VmaBinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp)
3521 {
3522     IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
3523         beg, end, value, cmp);
3524     if (it == end ||
3525         (!cmp(*it, value) && !cmp(value, *it)))
3526     {
3527         return it;
3528     }
3529     return end;
3530 }
3531 
3532 /*
3533 Returns true if all pointers in the array are not-null and unique.
3534 Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
3535 T must be pointer type, e.g. VmaAllocation, VmaPool.
3536 */
3537 template<typename T>
VmaValidatePointerArray(uint32_t count,const T * arr)3538 static bool VmaValidatePointerArray(uint32_t count, const T* arr)
3539 {
3540     for (uint32_t i = 0; i < count; ++i)
3541     {
3542         const T iPtr = arr[i];
3543         if (iPtr == VMA_NULL)
3544         {
3545             return false;
3546         }
3547         for (uint32_t j = i + 1; j < count; ++j)
3548         {
3549             if (iPtr == arr[j])
3550             {
3551                 return false;
3552             }
3553         }
3554     }
3555     return true;
3556 }
3557 
3558 template<typename MainT, typename NewT>
VmaPnextChainPushFront(MainT * mainStruct,NewT * newStruct)3559 static inline void VmaPnextChainPushFront(MainT* mainStruct, NewT* newStruct)
3560 {
3561     newStruct->pNext = mainStruct->pNext;
3562     mainStruct->pNext = newStruct;
3563 }
3564 
3565 // This is the main algorithm that guides the selection of a memory type best for an allocation -
3566 // converts usage to required/preferred/not preferred flags.
FindMemoryPreferences(bool isIntegratedGPU,const VmaAllocationCreateInfo & allocCreateInfo,VkFlags bufImgUsage,VkMemoryPropertyFlags & outRequiredFlags,VkMemoryPropertyFlags & outPreferredFlags,VkMemoryPropertyFlags & outNotPreferredFlags)3567 static bool FindMemoryPreferences(
3568     bool isIntegratedGPU,
3569     const VmaAllocationCreateInfo& allocCreateInfo,
3570     VkFlags bufImgUsage, // VkBufferCreateInfo::usage or VkImageCreateInfo::usage. UINT32_MAX if unknown.
3571     VkMemoryPropertyFlags& outRequiredFlags,
3572     VkMemoryPropertyFlags& outPreferredFlags,
3573     VkMemoryPropertyFlags& outNotPreferredFlags)
3574 {
3575     outRequiredFlags = allocCreateInfo.requiredFlags;
3576     outPreferredFlags = allocCreateInfo.preferredFlags;
3577     outNotPreferredFlags = 0;
3578 
3579     switch(allocCreateInfo.usage)
3580     {
3581     case VMA_MEMORY_USAGE_UNKNOWN:
3582         break;
3583     case VMA_MEMORY_USAGE_GPU_ONLY:
3584         if(!isIntegratedGPU || (outPreferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
3585         {
3586             outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3587         }
3588         break;
3589     case VMA_MEMORY_USAGE_CPU_ONLY:
3590         outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
3591         break;
3592     case VMA_MEMORY_USAGE_CPU_TO_GPU:
3593         outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
3594         if(!isIntegratedGPU || (outPreferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
3595         {
3596             outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3597         }
3598         break;
3599     case VMA_MEMORY_USAGE_GPU_TO_CPU:
3600         outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
3601         outPreferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
3602         break;
3603     case VMA_MEMORY_USAGE_CPU_COPY:
3604         outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3605         break;
3606     case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED:
3607         outRequiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
3608         break;
3609     case VMA_MEMORY_USAGE_AUTO:
3610     case VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE:
3611     case VMA_MEMORY_USAGE_AUTO_PREFER_HOST:
3612     {
3613         if(bufImgUsage == UINT32_MAX)
3614         {
3615             VMA_ASSERT(0 && "VMA_MEMORY_USAGE_AUTO* values can only be used with functions like vmaCreateBuffer, vmaCreateImage so that the details of the created resource are known.");
3616             return false;
3617         }
3618         // This relies on values of VK_IMAGE_USAGE_TRANSFER* being the same VK_BUFFER_IMAGE_TRANSFER*.
3619         const bool deviceAccess = (bufImgUsage & ~(VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT)) != 0;
3620         const bool hostAccessSequentialWrite = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT) != 0;
3621         const bool hostAccessRandom = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT) != 0;
3622         const bool hostAccessAllowTransferInstead = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT) != 0;
3623         const bool preferDevice = allocCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
3624         const bool preferHost = allocCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_HOST;
3625 
3626         // CPU random access - e.g. a buffer written to or transferred from GPU to read back on CPU.
3627         if(hostAccessRandom)
3628         {
3629             if(!isIntegratedGPU && deviceAccess && hostAccessAllowTransferInstead && !preferHost)
3630             {
3631                 // Nice if it will end up in HOST_VISIBLE, but more importantly prefer DEVICE_LOCAL.
3632                 // Omitting HOST_VISIBLE here is intentional.
3633                 // In case there is DEVICE_LOCAL | HOST_VISIBLE | HOST_CACHED, it will pick that one.
3634                 // Otherwise, this will give same weight to DEVICE_LOCAL as HOST_VISIBLE | HOST_CACHED and select the former if occurs first on the list.
3635                 outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
3636             }
3637             else
3638             {
3639                 // Always CPU memory, cached.
3640                 outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
3641             }
3642         }
3643         // CPU sequential write - may be CPU or host-visible GPU memory, uncached and write-combined.
3644         else if(hostAccessSequentialWrite)
3645         {
3646             // Want uncached and write-combined.
3647             outNotPreferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
3648 
3649             if(!isIntegratedGPU && deviceAccess && hostAccessAllowTransferInstead && !preferHost)
3650             {
3651                 outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
3652             }
3653             else
3654             {
3655                 outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
3656                 // Direct GPU access, CPU sequential write (e.g. a dynamic uniform buffer updated every frame)
3657                 if(deviceAccess)
3658                 {
3659                     // Could go to CPU memory or GPU BAR/unified. Up to the user to decide. If no preference, choose GPU memory.
3660                     if(preferHost)
3661                         outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3662                     else
3663                         outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3664                 }
3665                 // GPU no direct access, CPU sequential write (e.g. an upload buffer to be transferred to the GPU)
3666                 else
3667                 {
3668                     // Could go to CPU memory or GPU BAR/unified. Up to the user to decide. If no preference, choose CPU memory.
3669                     if(preferDevice)
3670                         outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3671                     else
3672                         outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3673                 }
3674             }
3675         }
3676         // No CPU access
3677         else
3678         {
3679             // GPU access, no CPU access (e.g. a color attachment image) - prefer GPU memory
3680             if(deviceAccess)
3681             {
3682                 // ...unless there is a clear preference from the user not to do so.
3683                 if(preferHost)
3684                     outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3685                 else
3686                     outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3687             }
3688             // No direct GPU access, no CPU access, just transfers.
3689             // It may be staging copy intended for e.g. preserving image for next frame (then better GPU memory) or
3690             // a "swap file" copy to free some GPU memory (then better CPU memory).
3691             // Up to the user to decide. If no preferece, assume the former and choose GPU memory.
3692             if(preferHost)
3693                 outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3694             else
3695                 outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3696         }
3697         break;
3698     }
3699     default:
3700         VMA_ASSERT(0);
3701     }
3702 
3703     // Avoid DEVICE_COHERENT unless explicitly requested.
3704     if(((allocCreateInfo.requiredFlags | allocCreateInfo.preferredFlags) &
3705         (VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0)
3706     {
3707         outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY;
3708     }
3709 
3710     return true;
3711 }
3712 
3713 ////////////////////////////////////////////////////////////////////////////////
3714 // Memory allocation
3715 
VmaMalloc(const VkAllocationCallbacks * pAllocationCallbacks,size_t size,size_t alignment)3716 static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
3717 {
3718     void* result = VMA_NULL;
3719     if ((pAllocationCallbacks != VMA_NULL) &&
3720         (pAllocationCallbacks->pfnAllocation != VMA_NULL))
3721     {
3722         result = (*pAllocationCallbacks->pfnAllocation)(
3723             pAllocationCallbacks->pUserData,
3724             size,
3725             alignment,
3726             VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
3727     }
3728     else
3729     {
3730         result = VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
3731     }
3732     VMA_ASSERT(result != VMA_NULL && "CPU memory allocation failed.");
3733     return result;
3734 }
3735 
VmaFree(const VkAllocationCallbacks * pAllocationCallbacks,void * ptr)3736 static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
3737 {
3738     if ((pAllocationCallbacks != VMA_NULL) &&
3739         (pAllocationCallbacks->pfnFree != VMA_NULL))
3740     {
3741         (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
3742     }
3743     else
3744     {
3745         VMA_SYSTEM_ALIGNED_FREE(ptr);
3746     }
3747 }
3748 
3749 template<typename T>
VmaAllocate(const VkAllocationCallbacks * pAllocationCallbacks)3750 static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
3751 {
3752     return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
3753 }
3754 
3755 template<typename T>
VmaAllocateArray(const VkAllocationCallbacks * pAllocationCallbacks,size_t count)3756 static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
3757 {
3758     return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
3759 }
3760 
3761 #define vma_new(allocator, type)   new(VmaAllocate<type>(allocator))(type)
3762 
3763 #define vma_new_array(allocator, type, count)   new(VmaAllocateArray<type>((allocator), (count)))(type)
3764 
3765 template<typename T>
vma_delete(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr)3766 static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
3767 {
3768     ptr->~T();
3769     VmaFree(pAllocationCallbacks, ptr);
3770 }
3771 
3772 template<typename T>
vma_delete_array(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr,size_t count)3773 static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
3774 {
3775     if (ptr != VMA_NULL)
3776     {
3777         for (size_t i = count; i--; )
3778         {
3779             ptr[i].~T();
3780         }
3781         VmaFree(pAllocationCallbacks, ptr);
3782     }
3783 }
3784 
VmaCreateStringCopy(const VkAllocationCallbacks * allocs,const char * srcStr)3785 static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr)
3786 {
3787     if (srcStr != VMA_NULL)
3788     {
3789         const size_t len = strlen(srcStr);
3790         char* const result = vma_new_array(allocs, char, len + 1);
3791         memcpy(result, srcStr, len + 1);
3792         return result;
3793     }
3794     return VMA_NULL;
3795 }
3796 
3797 #if VMA_STATS_STRING_ENABLED
VmaCreateStringCopy(const VkAllocationCallbacks * allocs,const char * srcStr,size_t strLen)3798 static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr, size_t strLen)
3799 {
3800     if (srcStr != VMA_NULL)
3801     {
3802         char* const result = vma_new_array(allocs, char, strLen + 1);
3803         memcpy(result, srcStr, strLen);
3804         result[strLen] = '\0';
3805         return result;
3806     }
3807     return VMA_NULL;
3808 }
3809 #endif // VMA_STATS_STRING_ENABLED
3810 
VmaFreeString(const VkAllocationCallbacks * allocs,char * str)3811 static void VmaFreeString(const VkAllocationCallbacks* allocs, char* str)
3812 {
3813     if (str != VMA_NULL)
3814     {
3815         const size_t len = strlen(str);
3816         vma_delete_array(allocs, str, len + 1);
3817     }
3818 }
3819 
3820 template<typename CmpLess, typename VectorT>
VmaVectorInsertSorted(VectorT & vector,const typename VectorT::value_type & value)3821 size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
3822 {
3823     const size_t indexToInsert = VmaBinaryFindFirstNotLess(
3824         vector.data(),
3825         vector.data() + vector.size(),
3826         value,
3827         CmpLess()) - vector.data();
3828     VmaVectorInsert(vector, indexToInsert, value);
3829     return indexToInsert;
3830 }
3831 
3832 template<typename CmpLess, typename VectorT>
VmaVectorRemoveSorted(VectorT & vector,const typename VectorT::value_type & value)3833 bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
3834 {
3835     CmpLess comparator;
3836     typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
3837         vector.begin(),
3838         vector.end(),
3839         value,
3840         comparator);
3841     if ((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
3842     {
3843         size_t indexToRemove = it - vector.begin();
3844         VmaVectorRemove(vector, indexToRemove);
3845         return true;
3846     }
3847     return false;
3848 }
3849 #endif // _VMA_FUNCTIONS
3850 
3851 #ifndef _VMA_STATISTICS_FUNCTIONS
3852 
VmaClearStatistics(VmaStatistics & outStats)3853 static void VmaClearStatistics(VmaStatistics& outStats)
3854 {
3855     outStats.blockCount = 0;
3856     outStats.allocationCount = 0;
3857     outStats.blockBytes = 0;
3858     outStats.allocationBytes = 0;
3859 }
3860 
VmaAddStatistics(VmaStatistics & inoutStats,const VmaStatistics & src)3861 static void VmaAddStatistics(VmaStatistics& inoutStats, const VmaStatistics& src)
3862 {
3863     inoutStats.blockCount += src.blockCount;
3864     inoutStats.allocationCount += src.allocationCount;
3865     inoutStats.blockBytes += src.blockBytes;
3866     inoutStats.allocationBytes += src.allocationBytes;
3867 }
3868 
VmaClearDetailedStatistics(VmaDetailedStatistics & outStats)3869 static void VmaClearDetailedStatistics(VmaDetailedStatistics& outStats)
3870 {
3871     VmaClearStatistics(outStats.statistics);
3872     outStats.unusedRangeCount = 0;
3873     outStats.allocationSizeMin = VK_WHOLE_SIZE;
3874     outStats.allocationSizeMax = 0;
3875     outStats.unusedRangeSizeMin = VK_WHOLE_SIZE;
3876     outStats.unusedRangeSizeMax = 0;
3877 }
3878 
VmaAddDetailedStatisticsAllocation(VmaDetailedStatistics & inoutStats,VkDeviceSize size)3879 static void VmaAddDetailedStatisticsAllocation(VmaDetailedStatistics& inoutStats, VkDeviceSize size)
3880 {
3881     inoutStats.statistics.allocationCount++;
3882     inoutStats.statistics.allocationBytes += size;
3883     inoutStats.allocationSizeMin = VMA_MIN(inoutStats.allocationSizeMin, size);
3884     inoutStats.allocationSizeMax = VMA_MAX(inoutStats.allocationSizeMax, size);
3885 }
3886 
VmaAddDetailedStatisticsUnusedRange(VmaDetailedStatistics & inoutStats,VkDeviceSize size)3887 static void VmaAddDetailedStatisticsUnusedRange(VmaDetailedStatistics& inoutStats, VkDeviceSize size)
3888 {
3889     inoutStats.unusedRangeCount++;
3890     inoutStats.unusedRangeSizeMin = VMA_MIN(inoutStats.unusedRangeSizeMin, size);
3891     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, size);
3892 }
3893 
VmaAddDetailedStatistics(VmaDetailedStatistics & inoutStats,const VmaDetailedStatistics & src)3894 static void VmaAddDetailedStatistics(VmaDetailedStatistics& inoutStats, const VmaDetailedStatistics& src)
3895 {
3896     VmaAddStatistics(inoutStats.statistics, src.statistics);
3897     inoutStats.unusedRangeCount += src.unusedRangeCount;
3898     inoutStats.allocationSizeMin = VMA_MIN(inoutStats.allocationSizeMin, src.allocationSizeMin);
3899     inoutStats.allocationSizeMax = VMA_MAX(inoutStats.allocationSizeMax, src.allocationSizeMax);
3900     inoutStats.unusedRangeSizeMin = VMA_MIN(inoutStats.unusedRangeSizeMin, src.unusedRangeSizeMin);
3901     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, src.unusedRangeSizeMax);
3902 }
3903 
3904 #endif // _VMA_STATISTICS_FUNCTIONS
3905 
3906 #ifndef _VMA_MUTEX_LOCK
3907 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
3908 struct VmaMutexLock
3909 {
VMA_CLASS_NO_COPYVmaMutexLock3910     VMA_CLASS_NO_COPY(VmaMutexLock)
3911 public:
3912     VmaMutexLock(VMA_MUTEX& mutex, bool useMutex = true) :
3913         m_pMutex(useMutex ? &mutex : VMA_NULL)
3914     {
3915         if (m_pMutex) { m_pMutex->Lock(); }
3916     }
~VmaMutexLockVmaMutexLock3917     ~VmaMutexLock() {  if (m_pMutex) { m_pMutex->Unlock(); } }
3918 
3919 private:
3920     VMA_MUTEX* m_pMutex;
3921 };
3922 
3923 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
3924 struct VmaMutexLockRead
3925 {
VMA_CLASS_NO_COPYVmaMutexLockRead3926     VMA_CLASS_NO_COPY(VmaMutexLockRead)
3927 public:
3928     VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
3929         m_pMutex(useMutex ? &mutex : VMA_NULL)
3930     {
3931         if (m_pMutex) { m_pMutex->LockRead(); }
3932     }
~VmaMutexLockReadVmaMutexLockRead3933     ~VmaMutexLockRead() { if (m_pMutex) { m_pMutex->UnlockRead(); } }
3934 
3935 private:
3936     VMA_RW_MUTEX* m_pMutex;
3937 };
3938 
3939 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
3940 struct VmaMutexLockWrite
3941 {
VMA_CLASS_NO_COPYVmaMutexLockWrite3942     VMA_CLASS_NO_COPY(VmaMutexLockWrite)
3943 public:
3944     VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex)
3945         : m_pMutex(useMutex ? &mutex : VMA_NULL)
3946     {
3947         if (m_pMutex) { m_pMutex->LockWrite(); }
3948     }
~VmaMutexLockWriteVmaMutexLockWrite3949     ~VmaMutexLockWrite() { if (m_pMutex) { m_pMutex->UnlockWrite(); } }
3950 
3951 private:
3952     VMA_RW_MUTEX* m_pMutex;
3953 };
3954 
3955 #if VMA_DEBUG_GLOBAL_MUTEX
3956     static VMA_MUTEX gDebugGlobalMutex;
3957     #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
3958 #else
3959     #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
3960 #endif
3961 #endif // _VMA_MUTEX_LOCK
3962 
3963 #ifndef _VMA_ATOMIC_TRANSACTIONAL_INCREMENT
3964 // An object that increments given atomic but decrements it back in the destructor unless Commit() is called.
3965 template<typename T>
3966 struct AtomicTransactionalIncrement
3967 {
3968 public:
3969     typedef std::atomic<T> AtomicT;
3970 
~AtomicTransactionalIncrementAtomicTransactionalIncrement3971     ~AtomicTransactionalIncrement()
3972     {
3973         if(m_Atomic)
3974             --(*m_Atomic);
3975     }
3976 
CommitAtomicTransactionalIncrement3977     void Commit() { m_Atomic = nullptr; }
IncrementAtomicTransactionalIncrement3978     T Increment(AtomicT* atomic)
3979     {
3980         m_Atomic = atomic;
3981         return m_Atomic->fetch_add(1);
3982     }
3983 
3984 private:
3985     AtomicT* m_Atomic = nullptr;
3986 };
3987 #endif // _VMA_ATOMIC_TRANSACTIONAL_INCREMENT
3988 
3989 #ifndef _VMA_STL_ALLOCATOR
3990 // STL-compatible allocator.
3991 template<typename T>
3992 struct VmaStlAllocator
3993 {
3994     const VkAllocationCallbacks* const m_pCallbacks;
3995     typedef T value_type;
3996 
VmaStlAllocatorVmaStlAllocator3997     VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) {}
3998     template<typename U>
VmaStlAllocatorVmaStlAllocator3999     VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) {}
4000     VmaStlAllocator(const VmaStlAllocator&) = default;
4001     VmaStlAllocator& operator=(const VmaStlAllocator&) = delete;
4002 
allocateVmaStlAllocator4003     T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
deallocateVmaStlAllocator4004     void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
4005 
4006     template<typename U>
4007     bool operator==(const VmaStlAllocator<U>& rhs) const
4008     {
4009         return m_pCallbacks == rhs.m_pCallbacks;
4010     }
4011     template<typename U>
4012     bool operator!=(const VmaStlAllocator<U>& rhs) const
4013     {
4014         return m_pCallbacks != rhs.m_pCallbacks;
4015     }
4016 };
4017 #endif // _VMA_STL_ALLOCATOR
4018 
4019 #ifndef _VMA_VECTOR
4020 /* Class with interface compatible with subset of std::vector.
4021 T must be POD because constructors and destructors are not called and memcpy is
4022 used for these objects. */
4023 template<typename T, typename AllocatorT>
4024 class VmaVector
4025 {
4026 public:
4027     typedef T value_type;
4028     typedef T* iterator;
4029     typedef const T* const_iterator;
4030 
4031     VmaVector(const AllocatorT& allocator);
4032     VmaVector(size_t count, const AllocatorT& allocator);
4033     // This version of the constructor is here for compatibility with pre-C++14 std::vector.
4034     // value is unused.
VmaVector(size_t count,const T & value,const AllocatorT & allocator)4035     VmaVector(size_t count, const T& value, const AllocatorT& allocator) : VmaVector(count, allocator) {}
4036     VmaVector(const VmaVector<T, AllocatorT>& src);
4037     VmaVector& operator=(const VmaVector& rhs);
~VmaVector()4038     ~VmaVector() { VmaFree(m_Allocator.m_pCallbacks, m_pArray); }
4039 
empty()4040     bool empty() const { return m_Count == 0; }
size()4041     size_t size() const { return m_Count; }
data()4042     T* data() { return m_pArray; }
front()4043     T& front() { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[0]; }
back()4044     T& back() { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[m_Count - 1]; }
data()4045     const T* data() const { return m_pArray; }
front()4046     const T& front() const { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[0]; }
back()4047     const T& back() const { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[m_Count - 1]; }
4048 
begin()4049     iterator begin() { return m_pArray; }
end()4050     iterator end() { return m_pArray + m_Count; }
cbegin()4051     const_iterator cbegin() const { return m_pArray; }
cend()4052     const_iterator cend() const { return m_pArray + m_Count; }
begin()4053     const_iterator begin() const { return cbegin(); }
end()4054     const_iterator end() const { return cend(); }
4055 
pop_front()4056     void pop_front() { VMA_HEAVY_ASSERT(m_Count > 0); remove(0); }
pop_back()4057     void pop_back() { VMA_HEAVY_ASSERT(m_Count > 0); resize(size() - 1); }
push_front(const T & src)4058     void push_front(const T& src) { insert(0, src); }
4059 
4060     void push_back(const T& src);
4061     void reserve(size_t newCapacity, bool freeMemory = false);
4062     void resize(size_t newCount);
clear()4063     void clear() { resize(0); }
4064     void shrink_to_fit();
4065     void insert(size_t index, const T& src);
4066     void remove(size_t index);
4067 
4068     T& operator[](size_t index) { VMA_HEAVY_ASSERT(index < m_Count); return m_pArray[index]; }
4069     const T& operator[](size_t index) const { VMA_HEAVY_ASSERT(index < m_Count); return m_pArray[index]; }
4070 
4071 private:
4072     AllocatorT m_Allocator;
4073     T* m_pArray;
4074     size_t m_Count;
4075     size_t m_Capacity;
4076 };
4077 
4078 #ifndef _VMA_VECTOR_FUNCTIONS
4079 template<typename T, typename AllocatorT>
VmaVector(const AllocatorT & allocator)4080 VmaVector<T, AllocatorT>::VmaVector(const AllocatorT& allocator)
4081     : m_Allocator(allocator),
4082     m_pArray(VMA_NULL),
4083     m_Count(0),
4084     m_Capacity(0) {}
4085 
4086 template<typename T, typename AllocatorT>
VmaVector(size_t count,const AllocatorT & allocator)4087 VmaVector<T, AllocatorT>::VmaVector(size_t count, const AllocatorT& allocator)
4088     : m_Allocator(allocator),
4089     m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
4090     m_Count(count),
4091     m_Capacity(count) {}
4092 
4093 template<typename T, typename AllocatorT>
VmaVector(const VmaVector & src)4094 VmaVector<T, AllocatorT>::VmaVector(const VmaVector& src)
4095     : m_Allocator(src.m_Allocator),
4096     m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
4097     m_Count(src.m_Count),
4098     m_Capacity(src.m_Count)
4099 {
4100     if (m_Count != 0)
4101     {
4102         memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
4103     }
4104 }
4105 
4106 template<typename T, typename AllocatorT>
4107 VmaVector<T, AllocatorT>& VmaVector<T, AllocatorT>::operator=(const VmaVector& rhs)
4108 {
4109     if (&rhs != this)
4110     {
4111         resize(rhs.m_Count);
4112         if (m_Count != 0)
4113         {
4114             memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
4115         }
4116     }
4117     return *this;
4118 }
4119 
4120 template<typename T, typename AllocatorT>
push_back(const T & src)4121 void VmaVector<T, AllocatorT>::push_back(const T& src)
4122 {
4123     const size_t newIndex = size();
4124     resize(newIndex + 1);
4125     m_pArray[newIndex] = src;
4126 }
4127 
4128 template<typename T, typename AllocatorT>
reserve(size_t newCapacity,bool freeMemory)4129 void VmaVector<T, AllocatorT>::reserve(size_t newCapacity, bool freeMemory)
4130 {
4131     newCapacity = VMA_MAX(newCapacity, m_Count);
4132 
4133     if ((newCapacity < m_Capacity) && !freeMemory)
4134     {
4135         newCapacity = m_Capacity;
4136     }
4137 
4138     if (newCapacity != m_Capacity)
4139     {
4140         T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
4141         if (m_Count != 0)
4142         {
4143             memcpy(newArray, m_pArray, m_Count * sizeof(T));
4144         }
4145         VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4146         m_Capacity = newCapacity;
4147         m_pArray = newArray;
4148     }
4149 }
4150 
4151 template<typename T, typename AllocatorT>
resize(size_t newCount)4152 void VmaVector<T, AllocatorT>::resize(size_t newCount)
4153 {
4154     size_t newCapacity = m_Capacity;
4155     if (newCount > m_Capacity)
4156     {
4157         newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
4158     }
4159 
4160     if (newCapacity != m_Capacity)
4161     {
4162         T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
4163         const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
4164         if (elementsToCopy != 0)
4165         {
4166             memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
4167         }
4168         VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4169         m_Capacity = newCapacity;
4170         m_pArray = newArray;
4171     }
4172 
4173     m_Count = newCount;
4174 }
4175 
4176 template<typename T, typename AllocatorT>
shrink_to_fit()4177 void VmaVector<T, AllocatorT>::shrink_to_fit()
4178 {
4179     if (m_Capacity > m_Count)
4180     {
4181         T* newArray = VMA_NULL;
4182         if (m_Count > 0)
4183         {
4184             newArray = VmaAllocateArray<T>(m_Allocator.m_pCallbacks, m_Count);
4185             memcpy(newArray, m_pArray, m_Count * sizeof(T));
4186         }
4187         VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4188         m_Capacity = m_Count;
4189         m_pArray = newArray;
4190     }
4191 }
4192 
4193 template<typename T, typename AllocatorT>
insert(size_t index,const T & src)4194 void VmaVector<T, AllocatorT>::insert(size_t index, const T& src)
4195 {
4196     VMA_HEAVY_ASSERT(index <= m_Count);
4197     const size_t oldCount = size();
4198     resize(oldCount + 1);
4199     if (index < oldCount)
4200     {
4201         memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
4202     }
4203     m_pArray[index] = src;
4204 }
4205 
4206 template<typename T, typename AllocatorT>
remove(size_t index)4207 void VmaVector<T, AllocatorT>::remove(size_t index)
4208 {
4209     VMA_HEAVY_ASSERT(index < m_Count);
4210     const size_t oldCount = size();
4211     if (index < oldCount - 1)
4212     {
4213         memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
4214     }
4215     resize(oldCount - 1);
4216 }
4217 #endif // _VMA_VECTOR_FUNCTIONS
4218 
4219 template<typename T, typename allocatorT>
VmaVectorInsert(VmaVector<T,allocatorT> & vec,size_t index,const T & item)4220 static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
4221 {
4222     vec.insert(index, item);
4223 }
4224 
4225 template<typename T, typename allocatorT>
VmaVectorRemove(VmaVector<T,allocatorT> & vec,size_t index)4226 static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
4227 {
4228     vec.remove(index);
4229 }
4230 #endif // _VMA_VECTOR
4231 
4232 #ifndef _VMA_SMALL_VECTOR
4233 /*
4234 This is a vector (a variable-sized array), optimized for the case when the array is small.
4235 
4236 It contains some number of elements in-place, which allows it to avoid heap allocation
4237 when the actual number of elements is below that threshold. This allows normal "small"
4238 cases to be fast without losing generality for large inputs.
4239 */
4240 template<typename T, typename AllocatorT, size_t N>
4241 class VmaSmallVector
4242 {
4243 public:
4244     typedef T value_type;
4245     typedef T* iterator;
4246 
4247     VmaSmallVector(const AllocatorT& allocator);
4248     VmaSmallVector(size_t count, const AllocatorT& allocator);
4249     template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
4250     VmaSmallVector(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>&) = delete;
4251     template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
4252     VmaSmallVector<T, AllocatorT, N>& operator=(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>&) = delete;
4253     ~VmaSmallVector() = default;
4254 
empty()4255     bool empty() const { return m_Count == 0; }
size()4256     size_t size() const { return m_Count; }
data()4257     T* data() { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
front()4258     T& front() { VMA_HEAVY_ASSERT(m_Count > 0); return data()[0]; }
back()4259     T& back() { VMA_HEAVY_ASSERT(m_Count > 0); return data()[m_Count - 1]; }
data()4260     const T* data() const { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
front()4261     const T& front() const { VMA_HEAVY_ASSERT(m_Count > 0); return data()[0]; }
back()4262     const T& back() const { VMA_HEAVY_ASSERT(m_Count > 0); return data()[m_Count - 1]; }
4263 
begin()4264     iterator begin() { return data(); }
end()4265     iterator end() { return data() + m_Count; }
4266 
pop_front()4267     void pop_front() { VMA_HEAVY_ASSERT(m_Count > 0); remove(0); }
pop_back()4268     void pop_back() { VMA_HEAVY_ASSERT(m_Count > 0); resize(size() - 1); }
push_front(const T & src)4269     void push_front(const T& src) { insert(0, src); }
4270 
4271     void push_back(const T& src);
4272     void resize(size_t newCount, bool freeMemory = false);
4273     void clear(bool freeMemory = false);
4274     void insert(size_t index, const T& src);
4275     void remove(size_t index);
4276 
4277     T& operator[](size_t index) { VMA_HEAVY_ASSERT(index < m_Count); return data()[index]; }
4278     const T& operator[](size_t index) const { VMA_HEAVY_ASSERT(index < m_Count); return data()[index]; }
4279 
4280 private:
4281     size_t m_Count;
4282     T m_StaticArray[N]; // Used when m_Size <= N
4283     VmaVector<T, AllocatorT> m_DynamicArray; // Used when m_Size > N
4284 };
4285 
4286 #ifndef _VMA_SMALL_VECTOR_FUNCTIONS
4287 template<typename T, typename AllocatorT, size_t N>
VmaSmallVector(const AllocatorT & allocator)4288 VmaSmallVector<T, AllocatorT, N>::VmaSmallVector(const AllocatorT& allocator)
4289     : m_Count(0),
4290     m_DynamicArray(allocator) {}
4291 
4292 template<typename T, typename AllocatorT, size_t N>
VmaSmallVector(size_t count,const AllocatorT & allocator)4293 VmaSmallVector<T, AllocatorT, N>::VmaSmallVector(size_t count, const AllocatorT& allocator)
4294     : m_Count(count),
4295     m_DynamicArray(count > N ? count : 0, allocator) {}
4296 
4297 template<typename T, typename AllocatorT, size_t N>
push_back(const T & src)4298 void VmaSmallVector<T, AllocatorT, N>::push_back(const T& src)
4299 {
4300     const size_t newIndex = size();
4301     resize(newIndex + 1);
4302     data()[newIndex] = src;
4303 }
4304 
4305 template<typename T, typename AllocatorT, size_t N>
resize(size_t newCount,bool freeMemory)4306 void VmaSmallVector<T, AllocatorT, N>::resize(size_t newCount, bool freeMemory)
4307 {
4308     if (newCount > N && m_Count > N)
4309     {
4310         // Any direction, staying in m_DynamicArray
4311         m_DynamicArray.resize(newCount);
4312         if (freeMemory)
4313         {
4314             m_DynamicArray.shrink_to_fit();
4315         }
4316     }
4317     else if (newCount > N && m_Count <= N)
4318     {
4319         // Growing, moving from m_StaticArray to m_DynamicArray
4320         m_DynamicArray.resize(newCount);
4321         if (m_Count > 0)
4322         {
4323             memcpy(m_DynamicArray.data(), m_StaticArray, m_Count * sizeof(T));
4324         }
4325     }
4326     else if (newCount <= N && m_Count > N)
4327     {
4328         // Shrinking, moving from m_DynamicArray to m_StaticArray
4329         if (newCount > 0)
4330         {
4331             memcpy(m_StaticArray, m_DynamicArray.data(), newCount * sizeof(T));
4332         }
4333         m_DynamicArray.resize(0);
4334         if (freeMemory)
4335         {
4336             m_DynamicArray.shrink_to_fit();
4337         }
4338     }
4339     else
4340     {
4341         // Any direction, staying in m_StaticArray - nothing to do here
4342     }
4343     m_Count = newCount;
4344 }
4345 
4346 template<typename T, typename AllocatorT, size_t N>
clear(bool freeMemory)4347 void VmaSmallVector<T, AllocatorT, N>::clear(bool freeMemory)
4348 {
4349     m_DynamicArray.clear();
4350     if (freeMemory)
4351     {
4352         m_DynamicArray.shrink_to_fit();
4353     }
4354     m_Count = 0;
4355 }
4356 
4357 template<typename T, typename AllocatorT, size_t N>
insert(size_t index,const T & src)4358 void VmaSmallVector<T, AllocatorT, N>::insert(size_t index, const T& src)
4359 {
4360     VMA_HEAVY_ASSERT(index <= m_Count);
4361     const size_t oldCount = size();
4362     resize(oldCount + 1);
4363     T* const dataPtr = data();
4364     if (index < oldCount)
4365     {
4366         //  I know, this could be more optimal for case where memmove can be memcpy directly from m_StaticArray to m_DynamicArray.
4367         memmove(dataPtr + (index + 1), dataPtr + index, (oldCount - index) * sizeof(T));
4368     }
4369     dataPtr[index] = src;
4370 }
4371 
4372 template<typename T, typename AllocatorT, size_t N>
remove(size_t index)4373 void VmaSmallVector<T, AllocatorT, N>::remove(size_t index)
4374 {
4375     VMA_HEAVY_ASSERT(index < m_Count);
4376     const size_t oldCount = size();
4377     if (index < oldCount - 1)
4378     {
4379         //  I know, this could be more optimal for case where memmove can be memcpy directly from m_DynamicArray to m_StaticArray.
4380         T* const dataPtr = data();
4381         memmove(dataPtr + index, dataPtr + (index + 1), (oldCount - index - 1) * sizeof(T));
4382     }
4383     resize(oldCount - 1);
4384 }
4385 #endif // _VMA_SMALL_VECTOR_FUNCTIONS
4386 #endif // _VMA_SMALL_VECTOR
4387 
4388 #ifndef _VMA_POOL_ALLOCATOR
4389 /*
4390 Allocator for objects of type T using a list of arrays (pools) to speed up
4391 allocation. Number of elements that can be allocated is not bounded because
4392 allocator can create multiple blocks.
4393 */
4394 template<typename T>
4395 class VmaPoolAllocator
4396 {
4397     VMA_CLASS_NO_COPY(VmaPoolAllocator)
4398 public:
4399     VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity);
4400     ~VmaPoolAllocator();
4401     template<typename... Types> T* Alloc(Types&&... args);
4402     void Free(T* ptr);
4403 
4404 private:
4405     union Item
4406     {
4407         uint32_t NextFreeIndex;
4408         alignas(T) char Value[sizeof(T)];
4409     };
4410     struct ItemBlock
4411     {
4412         Item* pItems;
4413         uint32_t Capacity;
4414         uint32_t FirstFreeIndex;
4415     };
4416 
4417     const VkAllocationCallbacks* m_pAllocationCallbacks;
4418     const uint32_t m_FirstBlockCapacity;
4419     VmaVector<ItemBlock, VmaStlAllocator<ItemBlock>> m_ItemBlocks;
4420 
4421     ItemBlock& CreateNewBlock();
4422 };
4423 
4424 #ifndef _VMA_POOL_ALLOCATOR_FUNCTIONS
4425 template<typename T>
VmaPoolAllocator(const VkAllocationCallbacks * pAllocationCallbacks,uint32_t firstBlockCapacity)4426 VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity)
4427     : m_pAllocationCallbacks(pAllocationCallbacks),
4428     m_FirstBlockCapacity(firstBlockCapacity),
4429     m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
4430 {
4431     VMA_ASSERT(m_FirstBlockCapacity > 1);
4432 }
4433 
4434 template<typename T>
~VmaPoolAllocator()4435 VmaPoolAllocator<T>::~VmaPoolAllocator()
4436 {
4437     for (size_t i = m_ItemBlocks.size(); i--;)
4438         vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity);
4439     m_ItemBlocks.clear();
4440 }
4441 
4442 template<typename T>
Alloc(Types &&...args)4443 template<typename... Types> T* VmaPoolAllocator<T>::Alloc(Types&&... args)
4444 {
4445     for (size_t i = m_ItemBlocks.size(); i--; )
4446     {
4447         ItemBlock& block = m_ItemBlocks[i];
4448         // This block has some free items: Use first one.
4449         if (block.FirstFreeIndex != UINT32_MAX)
4450         {
4451             Item* const pItem = &block.pItems[block.FirstFreeIndex];
4452             block.FirstFreeIndex = pItem->NextFreeIndex;
4453             T* result = (T*)&pItem->Value;
4454             new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
4455             return result;
4456         }
4457     }
4458 
4459     // No block has free item: Create new one and use it.
4460     ItemBlock& newBlock = CreateNewBlock();
4461     Item* const pItem = &newBlock.pItems[0];
4462     newBlock.FirstFreeIndex = pItem->NextFreeIndex;
4463     T* result = (T*)&pItem->Value;
4464     new(result) T(std::forward<Types>(args)...); // Explicit constructor call.
4465     return result;
4466 }
4467 
4468 template<typename T>
Free(T * ptr)4469 void VmaPoolAllocator<T>::Free(T* ptr)
4470 {
4471     // Search all memory blocks to find ptr.
4472     for (size_t i = m_ItemBlocks.size(); i--; )
4473     {
4474         ItemBlock& block = m_ItemBlocks[i];
4475 
4476         // Casting to union.
4477         Item* pItemPtr;
4478         memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
4479 
4480         // Check if pItemPtr is in address range of this block.
4481         if ((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity))
4482         {
4483             ptr->~T(); // Explicit destructor call.
4484             const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
4485             pItemPtr->NextFreeIndex = block.FirstFreeIndex;
4486             block.FirstFreeIndex = index;
4487             return;
4488         }
4489     }
4490     VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
4491 }
4492 
4493 template<typename T>
CreateNewBlock()4494 typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
4495 {
4496     const uint32_t newBlockCapacity = m_ItemBlocks.empty() ?
4497         m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2;
4498 
4499     const ItemBlock newBlock =
4500     {
4501         vma_new_array(m_pAllocationCallbacks, Item, newBlockCapacity),
4502         newBlockCapacity,
4503         0
4504     };
4505 
4506     m_ItemBlocks.push_back(newBlock);
4507 
4508     // Setup singly-linked list of all free items in this block.
4509     for (uint32_t i = 0; i < newBlockCapacity - 1; ++i)
4510         newBlock.pItems[i].NextFreeIndex = i + 1;
4511     newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX;
4512     return m_ItemBlocks.back();
4513 }
4514 #endif // _VMA_POOL_ALLOCATOR_FUNCTIONS
4515 #endif // _VMA_POOL_ALLOCATOR
4516 
4517 #ifndef _VMA_RAW_LIST
4518 template<typename T>
4519 struct VmaListItem
4520 {
4521     VmaListItem* pPrev;
4522     VmaListItem* pNext;
4523     T Value;
4524 };
4525 
4526 // Doubly linked list.
4527 template<typename T>
4528 class VmaRawList
4529 {
4530     VMA_CLASS_NO_COPY(VmaRawList)
4531 public:
4532     typedef VmaListItem<T> ItemType;
4533 
4534     VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
4535     // Intentionally not calling Clear, because that would be unnecessary
4536     // computations to return all items to m_ItemAllocator as free.
4537     ~VmaRawList() = default;
4538 
GetCount()4539     size_t GetCount() const { return m_Count; }
IsEmpty()4540     bool IsEmpty() const { return m_Count == 0; }
4541 
Front()4542     ItemType* Front() { return m_pFront; }
Back()4543     ItemType* Back() { return m_pBack; }
Front()4544     const ItemType* Front() const { return m_pFront; }
Back()4545     const ItemType* Back() const { return m_pBack; }
4546 
4547     ItemType* PushFront();
4548     ItemType* PushBack();
4549     ItemType* PushFront(const T& value);
4550     ItemType* PushBack(const T& value);
4551     void PopFront();
4552     void PopBack();
4553 
4554     // Item can be null - it means PushBack.
4555     ItemType* InsertBefore(ItemType* pItem);
4556     // Item can be null - it means PushFront.
4557     ItemType* InsertAfter(ItemType* pItem);
4558     ItemType* InsertBefore(ItemType* pItem, const T& value);
4559     ItemType* InsertAfter(ItemType* pItem, const T& value);
4560 
4561     void Clear();
4562     void Remove(ItemType* pItem);
4563 
4564 private:
4565     const VkAllocationCallbacks* const m_pAllocationCallbacks;
4566     VmaPoolAllocator<ItemType> m_ItemAllocator;
4567     ItemType* m_pFront;
4568     ItemType* m_pBack;
4569     size_t m_Count;
4570 };
4571 
4572 #ifndef _VMA_RAW_LIST_FUNCTIONS
4573 template<typename T>
VmaRawList(const VkAllocationCallbacks * pAllocationCallbacks)4574 VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks)
4575     : m_pAllocationCallbacks(pAllocationCallbacks),
4576     m_ItemAllocator(pAllocationCallbacks, 128),
4577     m_pFront(VMA_NULL),
4578     m_pBack(VMA_NULL),
4579     m_Count(0) {}
4580 
4581 template<typename T>
PushFront()4582 VmaListItem<T>* VmaRawList<T>::PushFront()
4583 {
4584     ItemType* const pNewItem = m_ItemAllocator.Alloc();
4585     pNewItem->pPrev = VMA_NULL;
4586     if (IsEmpty())
4587     {
4588         pNewItem->pNext = VMA_NULL;
4589         m_pFront = pNewItem;
4590         m_pBack = pNewItem;
4591         m_Count = 1;
4592     }
4593     else
4594     {
4595         pNewItem->pNext = m_pFront;
4596         m_pFront->pPrev = pNewItem;
4597         m_pFront = pNewItem;
4598         ++m_Count;
4599     }
4600     return pNewItem;
4601 }
4602 
4603 template<typename T>
PushBack()4604 VmaListItem<T>* VmaRawList<T>::PushBack()
4605 {
4606     ItemType* const pNewItem = m_ItemAllocator.Alloc();
4607     pNewItem->pNext = VMA_NULL;
4608     if(IsEmpty())
4609     {
4610         pNewItem->pPrev = VMA_NULL;
4611         m_pFront = pNewItem;
4612         m_pBack = pNewItem;
4613         m_Count = 1;
4614     }
4615     else
4616     {
4617         pNewItem->pPrev = m_pBack;
4618         m_pBack->pNext = pNewItem;
4619         m_pBack = pNewItem;
4620         ++m_Count;
4621     }
4622     return pNewItem;
4623 }
4624 
4625 template<typename T>
PushFront(const T & value)4626 VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
4627 {
4628     ItemType* const pNewItem = PushFront();
4629     pNewItem->Value = value;
4630     return pNewItem;
4631 }
4632 
4633 template<typename T>
PushBack(const T & value)4634 VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
4635 {
4636     ItemType* const pNewItem = PushBack();
4637     pNewItem->Value = value;
4638     return pNewItem;
4639 }
4640 
4641 template<typename T>
PopFront()4642 void VmaRawList<T>::PopFront()
4643 {
4644     VMA_HEAVY_ASSERT(m_Count > 0);
4645     ItemType* const pFrontItem = m_pFront;
4646     ItemType* const pNextItem = pFrontItem->pNext;
4647     if (pNextItem != VMA_NULL)
4648     {
4649         pNextItem->pPrev = VMA_NULL;
4650     }
4651     m_pFront = pNextItem;
4652     m_ItemAllocator.Free(pFrontItem);
4653     --m_Count;
4654 }
4655 
4656 template<typename T>
PopBack()4657 void VmaRawList<T>::PopBack()
4658 {
4659     VMA_HEAVY_ASSERT(m_Count > 0);
4660     ItemType* const pBackItem = m_pBack;
4661     ItemType* const pPrevItem = pBackItem->pPrev;
4662     if(pPrevItem != VMA_NULL)
4663     {
4664         pPrevItem->pNext = VMA_NULL;
4665     }
4666     m_pBack = pPrevItem;
4667     m_ItemAllocator.Free(pBackItem);
4668     --m_Count;
4669 }
4670 
4671 template<typename T>
Clear()4672 void VmaRawList<T>::Clear()
4673 {
4674     if (IsEmpty() == false)
4675     {
4676         ItemType* pItem = m_pBack;
4677         while (pItem != VMA_NULL)
4678         {
4679             ItemType* const pPrevItem = pItem->pPrev;
4680             m_ItemAllocator.Free(pItem);
4681             pItem = pPrevItem;
4682         }
4683         m_pFront = VMA_NULL;
4684         m_pBack = VMA_NULL;
4685         m_Count = 0;
4686     }
4687 }
4688 
4689 template<typename T>
Remove(ItemType * pItem)4690 void VmaRawList<T>::Remove(ItemType* pItem)
4691 {
4692     VMA_HEAVY_ASSERT(pItem != VMA_NULL);
4693     VMA_HEAVY_ASSERT(m_Count > 0);
4694 
4695     if(pItem->pPrev != VMA_NULL)
4696     {
4697         pItem->pPrev->pNext = pItem->pNext;
4698     }
4699     else
4700     {
4701         VMA_HEAVY_ASSERT(m_pFront == pItem);
4702         m_pFront = pItem->pNext;
4703     }
4704 
4705     if(pItem->pNext != VMA_NULL)
4706     {
4707         pItem->pNext->pPrev = pItem->pPrev;
4708     }
4709     else
4710     {
4711         VMA_HEAVY_ASSERT(m_pBack == pItem);
4712         m_pBack = pItem->pPrev;
4713     }
4714 
4715     m_ItemAllocator.Free(pItem);
4716     --m_Count;
4717 }
4718 
4719 template<typename T>
InsertBefore(ItemType * pItem)4720 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
4721 {
4722     if(pItem != VMA_NULL)
4723     {
4724         ItemType* const prevItem = pItem->pPrev;
4725         ItemType* const newItem = m_ItemAllocator.Alloc();
4726         newItem->pPrev = prevItem;
4727         newItem->pNext = pItem;
4728         pItem->pPrev = newItem;
4729         if(prevItem != VMA_NULL)
4730         {
4731             prevItem->pNext = newItem;
4732         }
4733         else
4734         {
4735             VMA_HEAVY_ASSERT(m_pFront == pItem);
4736             m_pFront = newItem;
4737         }
4738         ++m_Count;
4739         return newItem;
4740     }
4741     else
4742         return PushBack();
4743 }
4744 
4745 template<typename T>
InsertAfter(ItemType * pItem)4746 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
4747 {
4748     if(pItem != VMA_NULL)
4749     {
4750         ItemType* const nextItem = pItem->pNext;
4751         ItemType* const newItem = m_ItemAllocator.Alloc();
4752         newItem->pNext = nextItem;
4753         newItem->pPrev = pItem;
4754         pItem->pNext = newItem;
4755         if(nextItem != VMA_NULL)
4756         {
4757             nextItem->pPrev = newItem;
4758         }
4759         else
4760         {
4761             VMA_HEAVY_ASSERT(m_pBack == pItem);
4762             m_pBack = newItem;
4763         }
4764         ++m_Count;
4765         return newItem;
4766     }
4767     else
4768         return PushFront();
4769 }
4770 
4771 template<typename T>
InsertBefore(ItemType * pItem,const T & value)4772 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
4773 {
4774     ItemType* const newItem = InsertBefore(pItem);
4775     newItem->Value = value;
4776     return newItem;
4777 }
4778 
4779 template<typename T>
InsertAfter(ItemType * pItem,const T & value)4780 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
4781 {
4782     ItemType* const newItem = InsertAfter(pItem);
4783     newItem->Value = value;
4784     return newItem;
4785 }
4786 #endif // _VMA_RAW_LIST_FUNCTIONS
4787 #endif // _VMA_RAW_LIST
4788 
4789 #ifndef _VMA_LIST
4790 template<typename T, typename AllocatorT>
4791 class VmaList
4792 {
4793     VMA_CLASS_NO_COPY(VmaList)
4794 public:
4795     class reverse_iterator;
4796     class const_iterator;
4797     class const_reverse_iterator;
4798 
4799     class iterator
4800     {
4801         friend class const_iterator;
4802         friend class VmaList<T, AllocatorT>;
4803     public:
iterator()4804         iterator() :  m_pList(VMA_NULL), m_pItem(VMA_NULL) {}
iterator(const reverse_iterator & src)4805         iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
4806 
4807         T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; }
4808         T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; }
4809 
4810         bool operator==(const iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; }
4811         bool operator!=(const iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; }
4812 
4813         iterator operator++(int) { iterator result = *this; ++*this; return result; }
4814         iterator operator--(int) { iterator result = *this; --*this; return result; }
4815 
4816         iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pNext; return *this; }
4817         iterator& operator--();
4818 
4819     private:
4820         VmaRawList<T>* m_pList;
4821         VmaListItem<T>* m_pItem;
4822 
iterator(VmaRawList<T> * pList,VmaListItem<T> * pItem)4823         iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) : m_pList(pList),  m_pItem(pItem) {}
4824     };
4825     class reverse_iterator
4826     {
4827         friend class const_reverse_iterator;
4828         friend class VmaList<T, AllocatorT>;
4829     public:
reverse_iterator()4830         reverse_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {}
reverse_iterator(const iterator & src)4831         reverse_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
4832 
4833         T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; }
4834         T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; }
4835 
4836         bool operator==(const reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; }
4837         bool operator!=(const reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; }
4838 
4839         reverse_iterator operator++(int) { reverse_iterator result = *this; ++* this; return result; }
4840         reverse_iterator operator--(int) { reverse_iterator result = *this; --* this; return result; }
4841 
4842         reverse_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pPrev; return *this; }
4843         reverse_iterator& operator--();
4844 
4845     private:
4846         VmaRawList<T>* m_pList;
4847         VmaListItem<T>* m_pItem;
4848 
reverse_iterator(VmaRawList<T> * pList,VmaListItem<T> * pItem)4849         reverse_iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) : m_pList(pList),  m_pItem(pItem) {}
4850     };
4851     class const_iterator
4852     {
4853         friend class VmaList<T, AllocatorT>;
4854     public:
const_iterator()4855         const_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {}
const_iterator(const iterator & src)4856         const_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
const_iterator(const reverse_iterator & src)4857         const_iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
4858 
drop_const()4859         iterator drop_const() { return { const_cast<VmaRawList<T>*>(m_pList), const_cast<VmaListItem<T>*>(m_pItem) }; }
4860 
4861         const T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; }
4862         const T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; }
4863 
4864         bool operator==(const const_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; }
4865         bool operator!=(const const_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; }
4866 
4867         const_iterator operator++(int) { const_iterator result = *this; ++* this; return result; }
4868         const_iterator operator--(int) { const_iterator result = *this; --* this; return result; }
4869 
4870         const_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pNext; return *this; }
4871         const_iterator& operator--();
4872 
4873     private:
4874         const VmaRawList<T>* m_pList;
4875         const VmaListItem<T>* m_pItem;
4876 
const_iterator(const VmaRawList<T> * pList,const VmaListItem<T> * pItem)4877         const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) : m_pList(pList), m_pItem(pItem) {}
4878     };
4879     class const_reverse_iterator
4880     {
4881         friend class VmaList<T, AllocatorT>;
4882     public:
const_reverse_iterator()4883         const_reverse_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {}
const_reverse_iterator(const reverse_iterator & src)4884         const_reverse_iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
const_reverse_iterator(const iterator & src)4885         const_reverse_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
4886 
drop_const()4887         reverse_iterator drop_const() { return { const_cast<VmaRawList<T>*>(m_pList), const_cast<VmaListItem<T>*>(m_pItem) }; }
4888 
4889         const T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; }
4890         const T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; }
4891 
4892         bool operator==(const const_reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; }
4893         bool operator!=(const const_reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; }
4894 
4895         const_reverse_iterator operator++(int) { const_reverse_iterator result = *this; ++* this; return result; }
4896         const_reverse_iterator operator--(int) { const_reverse_iterator result = *this; --* this; return result; }
4897 
4898         const_reverse_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pPrev; return *this; }
4899         const_reverse_iterator& operator--();
4900 
4901     private:
4902         const VmaRawList<T>* m_pList;
4903         const VmaListItem<T>* m_pItem;
4904 
const_reverse_iterator(const VmaRawList<T> * pList,const VmaListItem<T> * pItem)4905         const_reverse_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) : m_pList(pList), m_pItem(pItem) {}
4906     };
4907 
VmaList(const AllocatorT & allocator)4908     VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) {}
4909 
empty()4910     bool empty() const { return m_RawList.IsEmpty(); }
size()4911     size_t size() const { return m_RawList.GetCount(); }
4912 
begin()4913     iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
end()4914     iterator end() { return iterator(&m_RawList, VMA_NULL); }
4915 
cbegin()4916     const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
cend()4917     const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
4918 
begin()4919     const_iterator begin() const { return cbegin(); }
end()4920     const_iterator end() const { return cend(); }
4921 
rbegin()4922     reverse_iterator rbegin() { return reverse_iterator(&m_RawList, m_RawList.Back()); }
rend()4923     reverse_iterator rend() { return reverse_iterator(&m_RawList, VMA_NULL); }
4924 
crbegin()4925     const_reverse_iterator crbegin() const { return const_reverse_iterator(&m_RawList, m_RawList.Back()); }
crend()4926     const_reverse_iterator crend() const { return const_reverse_iterator(&m_RawList, VMA_NULL); }
4927 
rbegin()4928     const_reverse_iterator rbegin() const { return crbegin(); }
rend()4929     const_reverse_iterator rend() const { return crend(); }
4930 
push_back(const T & value)4931     void push_back(const T& value) { m_RawList.PushBack(value); }
insert(iterator it,const T & value)4932     iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
4933 
clear()4934     void clear() { m_RawList.Clear(); }
erase(iterator it)4935     void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
4936 
4937 private:
4938     VmaRawList<T> m_RawList;
4939 };
4940 
4941 #ifndef _VMA_LIST_FUNCTIONS
4942 template<typename T, typename AllocatorT>
4943 typename VmaList<T, AllocatorT>::iterator& VmaList<T, AllocatorT>::iterator::operator--()
4944 {
4945     if (m_pItem != VMA_NULL)
4946     {
4947         m_pItem = m_pItem->pPrev;
4948     }
4949     else
4950     {
4951         VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4952         m_pItem = m_pList->Back();
4953     }
4954     return *this;
4955 }
4956 
4957 template<typename T, typename AllocatorT>
4958 typename VmaList<T, AllocatorT>::reverse_iterator& VmaList<T, AllocatorT>::reverse_iterator::operator--()
4959 {
4960     if (m_pItem != VMA_NULL)
4961     {
4962         m_pItem = m_pItem->pNext;
4963     }
4964     else
4965     {
4966         VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4967         m_pItem = m_pList->Front();
4968     }
4969     return *this;
4970 }
4971 
4972 template<typename T, typename AllocatorT>
4973 typename VmaList<T, AllocatorT>::const_iterator& VmaList<T, AllocatorT>::const_iterator::operator--()
4974 {
4975     if (m_pItem != VMA_NULL)
4976     {
4977         m_pItem = m_pItem->pPrev;
4978     }
4979     else
4980     {
4981         VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4982         m_pItem = m_pList->Back();
4983     }
4984     return *this;
4985 }
4986 
4987 template<typename T, typename AllocatorT>
4988 typename VmaList<T, AllocatorT>::const_reverse_iterator& VmaList<T, AllocatorT>::const_reverse_iterator::operator--()
4989 {
4990     if (m_pItem != VMA_NULL)
4991     {
4992         m_pItem = m_pItem->pNext;
4993     }
4994     else
4995     {
4996         VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4997         m_pItem = m_pList->Back();
4998     }
4999     return *this;
5000 }
5001 #endif // _VMA_LIST_FUNCTIONS
5002 #endif // _VMA_LIST
5003 
5004 #ifndef _VMA_INTRUSIVE_LINKED_LIST
5005 /*
5006 Expected interface of ItemTypeTraits:
5007 struct MyItemTypeTraits
5008 {
5009     typedef MyItem ItemType;
5010     static ItemType* GetPrev(const ItemType* item) { return item->myPrevPtr; }
5011     static ItemType* GetNext(const ItemType* item) { return item->myNextPtr; }
5012     static ItemType*& AccessPrev(ItemType* item) { return item->myPrevPtr; }
5013     static ItemType*& AccessNext(ItemType* item) { return item->myNextPtr; }
5014 };
5015 */
5016 template<typename ItemTypeTraits>
5017 class VmaIntrusiveLinkedList
5018 {
5019 public:
5020     typedef typename ItemTypeTraits::ItemType ItemType;
GetPrev(const ItemType * item)5021     static ItemType* GetPrev(const ItemType* item) { return ItemTypeTraits::GetPrev(item); }
GetNext(const ItemType * item)5022     static ItemType* GetNext(const ItemType* item) { return ItemTypeTraits::GetNext(item); }
5023 
5024     // Movable, not copyable.
5025     VmaIntrusiveLinkedList() = default;
5026     VmaIntrusiveLinkedList(VmaIntrusiveLinkedList && src);
5027     VmaIntrusiveLinkedList(const VmaIntrusiveLinkedList&) = delete;
5028     VmaIntrusiveLinkedList& operator=(VmaIntrusiveLinkedList&& src);
5029     VmaIntrusiveLinkedList& operator=(const VmaIntrusiveLinkedList&) = delete;
~VmaIntrusiveLinkedList()5030     ~VmaIntrusiveLinkedList() { VMA_HEAVY_ASSERT(IsEmpty()); }
5031 
GetCount()5032     size_t GetCount() const { return m_Count; }
IsEmpty()5033     bool IsEmpty() const { return m_Count == 0; }
Front()5034     ItemType* Front() { return m_Front; }
Back()5035     ItemType* Back() { return m_Back; }
Front()5036     const ItemType* Front() const { return m_Front; }
Back()5037     const ItemType* Back() const { return m_Back; }
5038 
5039     void PushBack(ItemType* item);
5040     void PushFront(ItemType* item);
5041     ItemType* PopBack();
5042     ItemType* PopFront();
5043 
5044     // MyItem can be null - it means PushBack.
5045     void InsertBefore(ItemType* existingItem, ItemType* newItem);
5046     // MyItem can be null - it means PushFront.
5047     void InsertAfter(ItemType* existingItem, ItemType* newItem);
5048     void Remove(ItemType* item);
5049     void RemoveAll();
5050 
5051 private:
5052     ItemType* m_Front = VMA_NULL;
5053     ItemType* m_Back = VMA_NULL;
5054     size_t m_Count = 0;
5055 };
5056 
5057 #ifndef _VMA_INTRUSIVE_LINKED_LIST_FUNCTIONS
5058 template<typename ItemTypeTraits>
VmaIntrusiveLinkedList(VmaIntrusiveLinkedList && src)5059 VmaIntrusiveLinkedList<ItemTypeTraits>::VmaIntrusiveLinkedList(VmaIntrusiveLinkedList&& src)
5060     : m_Front(src.m_Front), m_Back(src.m_Back), m_Count(src.m_Count)
5061 {
5062     src.m_Front = src.m_Back = VMA_NULL;
5063     src.m_Count = 0;
5064 }
5065 
5066 template<typename ItemTypeTraits>
5067 VmaIntrusiveLinkedList<ItemTypeTraits>& VmaIntrusiveLinkedList<ItemTypeTraits>::operator=(VmaIntrusiveLinkedList&& src)
5068 {
5069     if (&src != this)
5070     {
5071         VMA_HEAVY_ASSERT(IsEmpty());
5072         m_Front = src.m_Front;
5073         m_Back = src.m_Back;
5074         m_Count = src.m_Count;
5075         src.m_Front = src.m_Back = VMA_NULL;
5076         src.m_Count = 0;
5077     }
5078     return *this;
5079 }
5080 
5081 template<typename ItemTypeTraits>
PushBack(ItemType * item)5082 void VmaIntrusiveLinkedList<ItemTypeTraits>::PushBack(ItemType* item)
5083 {
5084     VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL);
5085     if (IsEmpty())
5086     {
5087         m_Front = item;
5088         m_Back = item;
5089         m_Count = 1;
5090     }
5091     else
5092     {
5093         ItemTypeTraits::AccessPrev(item) = m_Back;
5094         ItemTypeTraits::AccessNext(m_Back) = item;
5095         m_Back = item;
5096         ++m_Count;
5097     }
5098 }
5099 
5100 template<typename ItemTypeTraits>
PushFront(ItemType * item)5101 void VmaIntrusiveLinkedList<ItemTypeTraits>::PushFront(ItemType* item)
5102 {
5103     VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL);
5104     if (IsEmpty())
5105     {
5106         m_Front = item;
5107         m_Back = item;
5108         m_Count = 1;
5109     }
5110     else
5111     {
5112         ItemTypeTraits::AccessNext(item) = m_Front;
5113         ItemTypeTraits::AccessPrev(m_Front) = item;
5114         m_Front = item;
5115         ++m_Count;
5116     }
5117 }
5118 
5119 template<typename ItemTypeTraits>
PopBack()5120 typename VmaIntrusiveLinkedList<ItemTypeTraits>::ItemType* VmaIntrusiveLinkedList<ItemTypeTraits>::PopBack()
5121 {
5122     VMA_HEAVY_ASSERT(m_Count > 0);
5123     ItemType* const backItem = m_Back;
5124     ItemType* const prevItem = ItemTypeTraits::GetPrev(backItem);
5125     if (prevItem != VMA_NULL)
5126     {
5127         ItemTypeTraits::AccessNext(prevItem) = VMA_NULL;
5128     }
5129     m_Back = prevItem;
5130     --m_Count;
5131     ItemTypeTraits::AccessPrev(backItem) = VMA_NULL;
5132     ItemTypeTraits::AccessNext(backItem) = VMA_NULL;
5133     return backItem;
5134 }
5135 
5136 template<typename ItemTypeTraits>
PopFront()5137 typename VmaIntrusiveLinkedList<ItemTypeTraits>::ItemType* VmaIntrusiveLinkedList<ItemTypeTraits>::PopFront()
5138 {
5139     VMA_HEAVY_ASSERT(m_Count > 0);
5140     ItemType* const frontItem = m_Front;
5141     ItemType* const nextItem = ItemTypeTraits::GetNext(frontItem);
5142     if (nextItem != VMA_NULL)
5143     {
5144         ItemTypeTraits::AccessPrev(nextItem) = VMA_NULL;
5145     }
5146     m_Front = nextItem;
5147     --m_Count;
5148     ItemTypeTraits::AccessPrev(frontItem) = VMA_NULL;
5149     ItemTypeTraits::AccessNext(frontItem) = VMA_NULL;
5150     return frontItem;
5151 }
5152 
5153 template<typename ItemTypeTraits>
InsertBefore(ItemType * existingItem,ItemType * newItem)5154 void VmaIntrusiveLinkedList<ItemTypeTraits>::InsertBefore(ItemType* existingItem, ItemType* newItem)
5155 {
5156     VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL);
5157     if (existingItem != VMA_NULL)
5158     {
5159         ItemType* const prevItem = ItemTypeTraits::GetPrev(existingItem);
5160         ItemTypeTraits::AccessPrev(newItem) = prevItem;
5161         ItemTypeTraits::AccessNext(newItem) = existingItem;
5162         ItemTypeTraits::AccessPrev(existingItem) = newItem;
5163         if (prevItem != VMA_NULL)
5164         {
5165             ItemTypeTraits::AccessNext(prevItem) = newItem;
5166         }
5167         else
5168         {
5169             VMA_HEAVY_ASSERT(m_Front == existingItem);
5170             m_Front = newItem;
5171         }
5172         ++m_Count;
5173     }
5174     else
5175         PushBack(newItem);
5176 }
5177 
5178 template<typename ItemTypeTraits>
InsertAfter(ItemType * existingItem,ItemType * newItem)5179 void VmaIntrusiveLinkedList<ItemTypeTraits>::InsertAfter(ItemType* existingItem, ItemType* newItem)
5180 {
5181     VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL);
5182     if (existingItem != VMA_NULL)
5183     {
5184         ItemType* const nextItem = ItemTypeTraits::GetNext(existingItem);
5185         ItemTypeTraits::AccessNext(newItem) = nextItem;
5186         ItemTypeTraits::AccessPrev(newItem) = existingItem;
5187         ItemTypeTraits::AccessNext(existingItem) = newItem;
5188         if (nextItem != VMA_NULL)
5189         {
5190             ItemTypeTraits::AccessPrev(nextItem) = newItem;
5191         }
5192         else
5193         {
5194             VMA_HEAVY_ASSERT(m_Back == existingItem);
5195             m_Back = newItem;
5196         }
5197         ++m_Count;
5198     }
5199     else
5200         return PushFront(newItem);
5201 }
5202 
5203 template<typename ItemTypeTraits>
Remove(ItemType * item)5204 void VmaIntrusiveLinkedList<ItemTypeTraits>::Remove(ItemType* item)
5205 {
5206     VMA_HEAVY_ASSERT(item != VMA_NULL && m_Count > 0);
5207     if (ItemTypeTraits::GetPrev(item) != VMA_NULL)
5208     {
5209         ItemTypeTraits::AccessNext(ItemTypeTraits::AccessPrev(item)) = ItemTypeTraits::GetNext(item);
5210     }
5211     else
5212     {
5213         VMA_HEAVY_ASSERT(m_Front == item);
5214         m_Front = ItemTypeTraits::GetNext(item);
5215     }
5216 
5217     if (ItemTypeTraits::GetNext(item) != VMA_NULL)
5218     {
5219         ItemTypeTraits::AccessPrev(ItemTypeTraits::AccessNext(item)) = ItemTypeTraits::GetPrev(item);
5220     }
5221     else
5222     {
5223         VMA_HEAVY_ASSERT(m_Back == item);
5224         m_Back = ItemTypeTraits::GetPrev(item);
5225     }
5226     ItemTypeTraits::AccessPrev(item) = VMA_NULL;
5227     ItemTypeTraits::AccessNext(item) = VMA_NULL;
5228     --m_Count;
5229 }
5230 
5231 template<typename ItemTypeTraits>
RemoveAll()5232 void VmaIntrusiveLinkedList<ItemTypeTraits>::RemoveAll()
5233 {
5234     if (!IsEmpty())
5235     {
5236         ItemType* item = m_Back;
5237         while (item != VMA_NULL)
5238         {
5239             ItemType* const prevItem = ItemTypeTraits::AccessPrev(item);
5240             ItemTypeTraits::AccessPrev(item) = VMA_NULL;
5241             ItemTypeTraits::AccessNext(item) = VMA_NULL;
5242             item = prevItem;
5243         }
5244         m_Front = VMA_NULL;
5245         m_Back = VMA_NULL;
5246         m_Count = 0;
5247     }
5248 }
5249 #endif // _VMA_INTRUSIVE_LINKED_LIST_FUNCTIONS
5250 #endif // _VMA_INTRUSIVE_LINKED_LIST
5251 
5252 // Unused in this version.
5253 #if 0
5254 
5255 #ifndef _VMA_PAIR
5256 template<typename T1, typename T2>
5257 struct VmaPair
5258 {
5259     T1 first;
5260     T2 second;
5261 
5262     VmaPair() : first(), second() {}
5263     VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) {}
5264 };
5265 
5266 template<typename FirstT, typename SecondT>
5267 struct VmaPairFirstLess
5268 {
5269     bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
5270     {
5271         return lhs.first < rhs.first;
5272     }
5273     bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
5274     {
5275         return lhs.first < rhsFirst;
5276     }
5277 };
5278 #endif // _VMA_PAIR
5279 
5280 #ifndef _VMA_MAP
5281 /* Class compatible with subset of interface of std::unordered_map.
5282 KeyT, ValueT must be POD because they will be stored in VmaVector.
5283 */
5284 template<typename KeyT, typename ValueT>
5285 class VmaMap
5286 {
5287 public:
5288     typedef VmaPair<KeyT, ValueT> PairType;
5289     typedef PairType* iterator;
5290 
5291     VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) {}
5292 
5293     iterator begin() { return m_Vector.begin(); }
5294     iterator end() { return m_Vector.end(); }
5295     size_t size() { return m_Vector.size(); }
5296 
5297     void insert(const PairType& pair);
5298     iterator find(const KeyT& key);
5299     void erase(iterator it);
5300 
5301 private:
5302     VmaVector< PairType, VmaStlAllocator<PairType>> m_Vector;
5303 };
5304 
5305 #ifndef _VMA_MAP_FUNCTIONS
5306 template<typename KeyT, typename ValueT>
5307 void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
5308 {
5309     const size_t indexToInsert = VmaBinaryFindFirstNotLess(
5310         m_Vector.data(),
5311         m_Vector.data() + m_Vector.size(),
5312         pair,
5313         VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
5314     VmaVectorInsert(m_Vector, indexToInsert, pair);
5315 }
5316 
5317 template<typename KeyT, typename ValueT>
5318 VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
5319 {
5320     PairType* it = VmaBinaryFindFirstNotLess(
5321         m_Vector.data(),
5322         m_Vector.data() + m_Vector.size(),
5323         key,
5324         VmaPairFirstLess<KeyT, ValueT>());
5325     if ((it != m_Vector.end()) && (it->first == key))
5326     {
5327         return it;
5328     }
5329     else
5330     {
5331         return m_Vector.end();
5332     }
5333 }
5334 
5335 template<typename KeyT, typename ValueT>
5336 void VmaMap<KeyT, ValueT>::erase(iterator it)
5337 {
5338     VmaVectorRemove(m_Vector, it - m_Vector.begin());
5339 }
5340 #endif // _VMA_MAP_FUNCTIONS
5341 #endif // _VMA_MAP
5342 
5343 #endif // #if 0
5344 
5345 #if !defined(_VMA_STRING_BUILDER) && VMA_STATS_STRING_ENABLED
5346 class VmaStringBuilder
5347 {
5348 public:
VmaStringBuilder(const VkAllocationCallbacks * allocationCallbacks)5349     VmaStringBuilder(const VkAllocationCallbacks* allocationCallbacks) : m_Data(VmaStlAllocator<char>(allocationCallbacks)) {}
5350     ~VmaStringBuilder() = default;
5351 
GetLength()5352     size_t GetLength() const { return m_Data.size(); }
GetData()5353     const char* GetData() const { return m_Data.data(); }
AddNewLine()5354     void AddNewLine() { Add('\n'); }
Add(char ch)5355     void Add(char ch) { m_Data.push_back(ch); }
5356 
5357     void Add(const char* pStr);
5358     void AddNumber(uint32_t num);
5359     void AddNumber(uint64_t num);
5360     void AddPointer(const void* ptr);
5361 
5362 private:
5363     VmaVector<char, VmaStlAllocator<char>> m_Data;
5364 };
5365 
5366 #ifndef _VMA_STRING_BUILDER_FUNCTIONS
Add(const char * pStr)5367 void VmaStringBuilder::Add(const char* pStr)
5368 {
5369     const size_t strLen = strlen(pStr);
5370     if (strLen > 0)
5371     {
5372         const size_t oldCount = m_Data.size();
5373         m_Data.resize(oldCount + strLen);
5374         memcpy(m_Data.data() + oldCount, pStr, strLen);
5375     }
5376 }
5377 
AddNumber(uint32_t num)5378 void VmaStringBuilder::AddNumber(uint32_t num)
5379 {
5380     char buf[11];
5381     buf[10] = '\0';
5382     char* p = &buf[10];
5383     do
5384     {
5385         *--p = '0' + (num % 10);
5386         num /= 10;
5387     } while (num);
5388     Add(p);
5389 }
5390 
AddNumber(uint64_t num)5391 void VmaStringBuilder::AddNumber(uint64_t num)
5392 {
5393     char buf[21];
5394     buf[20] = '\0';
5395     char* p = &buf[20];
5396     do
5397     {
5398         *--p = '0' + (num % 10);
5399         num /= 10;
5400     } while (num);
5401     Add(p);
5402 }
5403 
AddPointer(const void * ptr)5404 void VmaStringBuilder::AddPointer(const void* ptr)
5405 {
5406     char buf[21];
5407     VmaPtrToStr(buf, sizeof(buf), ptr);
5408     Add(buf);
5409 }
5410 #endif //_VMA_STRING_BUILDER_FUNCTIONS
5411 #endif // _VMA_STRING_BUILDER
5412 
5413 #if !defined(_VMA_JSON_WRITER) && VMA_STATS_STRING_ENABLED
5414 /*
5415 Allows to conveniently build a correct JSON document to be written to the
5416 VmaStringBuilder passed to the constructor.
5417 */
5418 class VmaJsonWriter
5419 {
5420     VMA_CLASS_NO_COPY(VmaJsonWriter)
5421 public:
5422     // sb - string builder to write the document to. Must remain alive for the whole lifetime of this object.
5423     VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
5424     ~VmaJsonWriter();
5425 
5426     // Begins object by writing "{".
5427     // Inside an object, you must call pairs of WriteString and a value, e.g.:
5428     // j.BeginObject(true); j.WriteString("A"); j.WriteNumber(1); j.WriteString("B"); j.WriteNumber(2); j.EndObject();
5429     // Will write: { "A": 1, "B": 2 }
5430     void BeginObject(bool singleLine = false);
5431     // Ends object by writing "}".
5432     void EndObject();
5433 
5434     // Begins array by writing "[".
5435     // Inside an array, you can write a sequence of any values.
5436     void BeginArray(bool singleLine = false);
5437     // Ends array by writing "[".
5438     void EndArray();
5439 
5440     // Writes a string value inside "".
5441     // pStr can contain any ANSI characters, including '"', new line etc. - they will be properly escaped.
5442     void WriteString(const char* pStr);
5443 
5444     // Begins writing a string value.
5445     // Call BeginString, ContinueString, ContinueString, ..., EndString instead of
5446     // WriteString to conveniently build the string content incrementally, made of
5447     // parts including numbers.
5448     void BeginString(const char* pStr = VMA_NULL);
5449     // Posts next part of an open string.
5450     void ContinueString(const char* pStr);
5451     // Posts next part of an open string. The number is converted to decimal characters.
5452     void ContinueString(uint32_t n);
5453     void ContinueString(uint64_t n);
5454     void ContinueString_Size(size_t n);
5455     // Posts next part of an open string. Pointer value is converted to characters
5456     // using "%p" formatting - shown as hexadecimal number, e.g.: 000000081276Ad00
5457     void ContinueString_Pointer(const void* ptr);
5458     // Ends writing a string value by writing '"'.
5459     void EndString(const char* pStr = VMA_NULL);
5460 
5461     // Writes a number value.
5462     void WriteNumber(uint32_t n);
5463     void WriteNumber(uint64_t n);
5464     void WriteSize(size_t n);
5465     // Writes a boolean value - false or true.
5466     void WriteBool(bool b);
5467     // Writes a null value.
5468     void WriteNull();
5469 
5470 private:
5471     enum COLLECTION_TYPE
5472     {
5473         COLLECTION_TYPE_OBJECT,
5474         COLLECTION_TYPE_ARRAY,
5475     };
5476     struct StackItem
5477     {
5478         COLLECTION_TYPE type;
5479         uint32_t valueCount;
5480         bool singleLineMode;
5481     };
5482 
5483     static const char* const INDENT;
5484 
5485     VmaStringBuilder& m_SB;
5486     VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
5487     bool m_InsideString;
5488 
5489     // Write size_t for less than 64bits
WriteSize(size_t n,std::integral_constant<bool,false>)5490     void WriteSize(size_t n, std::integral_constant<bool, false>) { m_SB.AddNumber(static_cast<uint32_t>(n)); }
5491     // Write size_t for 64bits
WriteSize(size_t n,std::integral_constant<bool,true>)5492     void WriteSize(size_t n, std::integral_constant<bool, true>) { m_SB.AddNumber(static_cast<uint64_t>(n)); }
5493 
5494     void BeginValue(bool isString);
5495     void WriteIndent(bool oneLess = false);
5496 };
5497 const char* const VmaJsonWriter::INDENT = "  ";
5498 
5499 #ifndef _VMA_JSON_WRITER_FUNCTIONS
VmaJsonWriter(const VkAllocationCallbacks * pAllocationCallbacks,VmaStringBuilder & sb)5500 VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb)
5501     : m_SB(sb),
5502     m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
5503     m_InsideString(false) {}
5504 
~VmaJsonWriter()5505 VmaJsonWriter::~VmaJsonWriter()
5506 {
5507     VMA_ASSERT(!m_InsideString);
5508     VMA_ASSERT(m_Stack.empty());
5509 }
5510 
BeginObject(bool singleLine)5511 void VmaJsonWriter::BeginObject(bool singleLine)
5512 {
5513     VMA_ASSERT(!m_InsideString);
5514 
5515     BeginValue(false);
5516     m_SB.Add('{');
5517 
5518     StackItem item;
5519     item.type = COLLECTION_TYPE_OBJECT;
5520     item.valueCount = 0;
5521     item.singleLineMode = singleLine;
5522     m_Stack.push_back(item);
5523 }
5524 
EndObject()5525 void VmaJsonWriter::EndObject()
5526 {
5527     VMA_ASSERT(!m_InsideString);
5528 
5529     WriteIndent(true);
5530     m_SB.Add('}');
5531 
5532     VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
5533     m_Stack.pop_back();
5534 }
5535 
BeginArray(bool singleLine)5536 void VmaJsonWriter::BeginArray(bool singleLine)
5537 {
5538     VMA_ASSERT(!m_InsideString);
5539 
5540     BeginValue(false);
5541     m_SB.Add('[');
5542 
5543     StackItem item;
5544     item.type = COLLECTION_TYPE_ARRAY;
5545     item.valueCount = 0;
5546     item.singleLineMode = singleLine;
5547     m_Stack.push_back(item);
5548 }
5549 
EndArray()5550 void VmaJsonWriter::EndArray()
5551 {
5552     VMA_ASSERT(!m_InsideString);
5553 
5554     WriteIndent(true);
5555     m_SB.Add(']');
5556 
5557     VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
5558     m_Stack.pop_back();
5559 }
5560 
WriteString(const char * pStr)5561 void VmaJsonWriter::WriteString(const char* pStr)
5562 {
5563     BeginString(pStr);
5564     EndString();
5565 }
5566 
BeginString(const char * pStr)5567 void VmaJsonWriter::BeginString(const char* pStr)
5568 {
5569     VMA_ASSERT(!m_InsideString);
5570 
5571     BeginValue(true);
5572     m_SB.Add('"');
5573     m_InsideString = true;
5574     if (pStr != VMA_NULL && pStr[0] != '\0')
5575     {
5576         ContinueString(pStr);
5577     }
5578 }
5579 
ContinueString(const char * pStr)5580 void VmaJsonWriter::ContinueString(const char* pStr)
5581 {
5582     VMA_ASSERT(m_InsideString);
5583 
5584     const size_t strLen = strlen(pStr);
5585     for (size_t i = 0; i < strLen; ++i)
5586     {
5587         char ch = pStr[i];
5588         if (ch == '\\')
5589         {
5590             m_SB.Add("\\\\");
5591         }
5592         else if (ch == '"')
5593         {
5594             m_SB.Add("\\\"");
5595         }
5596         else if (ch >= 32)
5597         {
5598             m_SB.Add(ch);
5599         }
5600         else switch (ch)
5601         {
5602         case '\b':
5603             m_SB.Add("\\b");
5604             break;
5605         case '\f':
5606             m_SB.Add("\\f");
5607             break;
5608         case '\n':
5609             m_SB.Add("\\n");
5610             break;
5611         case '\r':
5612             m_SB.Add("\\r");
5613             break;
5614         case '\t':
5615             m_SB.Add("\\t");
5616             break;
5617         default:
5618             VMA_ASSERT(0 && "Character not currently supported.");
5619             break;
5620         }
5621     }
5622 }
5623 
ContinueString(uint32_t n)5624 void VmaJsonWriter::ContinueString(uint32_t n)
5625 {
5626     VMA_ASSERT(m_InsideString);
5627     m_SB.AddNumber(n);
5628 }
5629 
ContinueString(uint64_t n)5630 void VmaJsonWriter::ContinueString(uint64_t n)
5631 {
5632     VMA_ASSERT(m_InsideString);
5633     m_SB.AddNumber(n);
5634 }
5635 
ContinueString_Size(size_t n)5636 void VmaJsonWriter::ContinueString_Size(size_t n)
5637 {
5638     VMA_ASSERT(m_InsideString);
5639     // Fix for AppleClang incorrect type casting
5640     // TODO: Change to if constexpr when C++17 used as minimal standard
5641     WriteSize(n, std::is_same<size_t, uint64_t>{});
5642 }
5643 
ContinueString_Pointer(const void * ptr)5644 void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
5645 {
5646     VMA_ASSERT(m_InsideString);
5647     m_SB.AddPointer(ptr);
5648 }
5649 
EndString(const char * pStr)5650 void VmaJsonWriter::EndString(const char* pStr)
5651 {
5652     VMA_ASSERT(m_InsideString);
5653     if (pStr != VMA_NULL && pStr[0] != '\0')
5654     {
5655         ContinueString(pStr);
5656     }
5657     m_SB.Add('"');
5658     m_InsideString = false;
5659 }
5660 
WriteNumber(uint32_t n)5661 void VmaJsonWriter::WriteNumber(uint32_t n)
5662 {
5663     VMA_ASSERT(!m_InsideString);
5664     BeginValue(false);
5665     m_SB.AddNumber(n);
5666 }
5667 
WriteNumber(uint64_t n)5668 void VmaJsonWriter::WriteNumber(uint64_t n)
5669 {
5670     VMA_ASSERT(!m_InsideString);
5671     BeginValue(false);
5672     m_SB.AddNumber(n);
5673 }
5674 
WriteSize(size_t n)5675 void VmaJsonWriter::WriteSize(size_t n)
5676 {
5677     VMA_ASSERT(!m_InsideString);
5678     BeginValue(false);
5679     // Fix for AppleClang incorrect type casting
5680     // TODO: Change to if constexpr when C++17 used as minimal standard
5681     WriteSize(n, std::is_same<size_t, uint64_t>{});
5682 }
5683 
WriteBool(bool b)5684 void VmaJsonWriter::WriteBool(bool b)
5685 {
5686     VMA_ASSERT(!m_InsideString);
5687     BeginValue(false);
5688     m_SB.Add(b ? "true" : "false");
5689 }
5690 
WriteNull()5691 void VmaJsonWriter::WriteNull()
5692 {
5693     VMA_ASSERT(!m_InsideString);
5694     BeginValue(false);
5695     m_SB.Add("null");
5696 }
5697 
BeginValue(bool isString)5698 void VmaJsonWriter::BeginValue(bool isString)
5699 {
5700     if (!m_Stack.empty())
5701     {
5702         StackItem& currItem = m_Stack.back();
5703         if (currItem.type == COLLECTION_TYPE_OBJECT &&
5704             currItem.valueCount % 2 == 0)
5705         {
5706             VMA_ASSERT(isString);
5707         }
5708 
5709         if (currItem.type == COLLECTION_TYPE_OBJECT &&
5710             currItem.valueCount % 2 != 0)
5711         {
5712             m_SB.Add(": ");
5713         }
5714         else if (currItem.valueCount > 0)
5715         {
5716             m_SB.Add(", ");
5717             WriteIndent();
5718         }
5719         else
5720         {
5721             WriteIndent();
5722         }
5723         ++currItem.valueCount;
5724     }
5725 }
5726 
WriteIndent(bool oneLess)5727 void VmaJsonWriter::WriteIndent(bool oneLess)
5728 {
5729     if (!m_Stack.empty() && !m_Stack.back().singleLineMode)
5730     {
5731         m_SB.AddNewLine();
5732 
5733         size_t count = m_Stack.size();
5734         if (count > 0 && oneLess)
5735         {
5736             --count;
5737         }
5738         for (size_t i = 0; i < count; ++i)
5739         {
5740             m_SB.Add(INDENT);
5741         }
5742     }
5743 }
5744 #endif // _VMA_JSON_WRITER_FUNCTIONS
5745 
VmaPrintDetailedStatistics(VmaJsonWriter & json,const VmaDetailedStatistics & stat)5746 static void VmaPrintDetailedStatistics(VmaJsonWriter& json, const VmaDetailedStatistics& stat)
5747 {
5748     json.BeginObject();
5749 
5750     json.WriteString("BlockCount");
5751     json.WriteNumber(stat.statistics.blockCount);
5752     json.WriteString("BlockBytes");
5753     json.WriteNumber(stat.statistics.blockBytes);
5754     json.WriteString("AllocationCount");
5755     json.WriteNumber(stat.statistics.allocationCount);
5756     json.WriteString("AllocationBytes");
5757     json.WriteNumber(stat.statistics.allocationBytes);
5758     json.WriteString("UnusedRangeCount");
5759     json.WriteNumber(stat.unusedRangeCount);
5760 
5761     if (stat.statistics.allocationCount > 1)
5762     {
5763         json.WriteString("AllocationSizeMin");
5764         json.WriteNumber(stat.allocationSizeMin);
5765         json.WriteString("AllocationSizeMax");
5766         json.WriteNumber(stat.allocationSizeMax);
5767     }
5768     if (stat.unusedRangeCount > 1)
5769     {
5770         json.WriteString("UnusedRangeSizeMin");
5771         json.WriteNumber(stat.unusedRangeSizeMin);
5772         json.WriteString("UnusedRangeSizeMax");
5773         json.WriteNumber(stat.unusedRangeSizeMax);
5774     }
5775     json.EndObject();
5776 }
5777 #endif // _VMA_JSON_WRITER
5778 
5779 #ifndef _VMA_MAPPING_HYSTERESIS
5780 
5781 class VmaMappingHysteresis
5782 {
5783     VMA_CLASS_NO_COPY(VmaMappingHysteresis)
5784 public:
5785     VmaMappingHysteresis() = default;
5786 
GetExtraMapping()5787     uint32_t GetExtraMapping() const { return m_ExtraMapping; }
5788 
5789     // Call when Map was called.
5790     // Returns true if switched to extra +1 mapping reference count.
PostMap()5791     bool PostMap()
5792     {
5793 #if VMA_MAPPING_HYSTERESIS_ENABLED
5794         if(m_ExtraMapping == 0)
5795         {
5796             ++m_MajorCounter;
5797             if(m_MajorCounter >= COUNTER_MIN_EXTRA_MAPPING)
5798             {
5799                 m_ExtraMapping = 1;
5800                 m_MajorCounter = 0;
5801                 m_MinorCounter = 0;
5802                 return true;
5803             }
5804         }
5805         else // m_ExtraMapping == 1
5806             PostMinorCounter();
5807 #endif // #if VMA_MAPPING_HYSTERESIS_ENABLED
5808         return false;
5809     }
5810 
5811     // Call when Unmap was called.
PostUnmap()5812     void PostUnmap()
5813     {
5814 #if VMA_MAPPING_HYSTERESIS_ENABLED
5815         if(m_ExtraMapping == 0)
5816             ++m_MajorCounter;
5817         else // m_ExtraMapping == 1
5818             PostMinorCounter();
5819 #endif // #if VMA_MAPPING_HYSTERESIS_ENABLED
5820     }
5821 
5822     // Call when allocation was made from the memory block.
PostAlloc()5823     void PostAlloc()
5824     {
5825 #if VMA_MAPPING_HYSTERESIS_ENABLED
5826         if(m_ExtraMapping == 1)
5827             ++m_MajorCounter;
5828         else // m_ExtraMapping == 0
5829             PostMinorCounter();
5830 #endif // #if VMA_MAPPING_HYSTERESIS_ENABLED
5831     }
5832 
5833     // Call when allocation was freed from the memory block.
5834     // Returns true if switched to extra -1 mapping reference count.
PostFree()5835     bool PostFree()
5836     {
5837 #if VMA_MAPPING_HYSTERESIS_ENABLED
5838         if(m_ExtraMapping == 1)
5839         {
5840             ++m_MajorCounter;
5841             if(m_MajorCounter >= COUNTER_MIN_EXTRA_MAPPING &&
5842                 m_MajorCounter > m_MinorCounter + 1)
5843             {
5844                 m_ExtraMapping = 0;
5845                 m_MajorCounter = 0;
5846                 m_MinorCounter = 0;
5847                 return true;
5848             }
5849         }
5850         else // m_ExtraMapping == 0
5851             PostMinorCounter();
5852 #endif // #if VMA_MAPPING_HYSTERESIS_ENABLED
5853         return false;
5854     }
5855 
5856 private:
5857     static const int32_t COUNTER_MIN_EXTRA_MAPPING = 7;
5858 
5859     uint32_t m_MinorCounter = 0;
5860     uint32_t m_MajorCounter = 0;
5861     uint32_t m_ExtraMapping = 0; // 0 or 1.
5862 
PostMinorCounter()5863     void PostMinorCounter()
5864     {
5865         if(m_MinorCounter < m_MajorCounter)
5866         {
5867             ++m_MinorCounter;
5868         }
5869         else if(m_MajorCounter > 0)
5870         {
5871             --m_MajorCounter;
5872             --m_MinorCounter;
5873         }
5874     }
5875 };
5876 
5877 #endif // _VMA_MAPPING_HYSTERESIS
5878 
5879 #ifndef _VMA_DEVICE_MEMORY_BLOCK
5880 /*
5881 Represents a single block of device memory (`VkDeviceMemory`) with all the
5882 data about its regions (aka suballocations, #VmaAllocation), assigned and free.
5883 
5884 Thread-safety:
5885 - Access to m_pMetadata must be externally synchronized.
5886 - Map, Unmap, Bind* are synchronized internally.
5887 */
5888 class VmaDeviceMemoryBlock
5889 {
5890     VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
5891 public:
5892     VmaBlockMetadata* m_pMetadata;
5893 
5894     VmaDeviceMemoryBlock(VmaAllocator hAllocator);
5895     ~VmaDeviceMemoryBlock();
5896 
5897     // Always call after construction.
5898     void Init(
5899         VmaAllocator hAllocator,
5900         VmaPool hParentPool,
5901         uint32_t newMemoryTypeIndex,
5902         VkDeviceMemory newMemory,
5903         VkDeviceSize newSize,
5904         uint32_t id,
5905         uint32_t algorithm,
5906         VkDeviceSize bufferImageGranularity);
5907     // Always call before destruction.
5908     void Destroy(VmaAllocator allocator);
5909 
GetParentPool()5910     VmaPool GetParentPool() const { return m_hParentPool; }
GetDeviceMemory()5911     VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
GetMemoryTypeIndex()5912     uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetId()5913     uint32_t GetId() const { return m_Id; }
GetMappedData()5914     void* GetMappedData() const { return m_pMappedData; }
GetMapRefCount()5915     uint32_t GetMapRefCount() const { return m_MapCount; }
5916 
5917     // Call when allocation/free was made from m_pMetadata.
5918     // Used for m_MappingHysteresis.
PostAlloc()5919     void PostAlloc() { m_MappingHysteresis.PostAlloc(); }
5920     void PostFree(VmaAllocator hAllocator);
5921 
5922     // Validates all data structures inside this object. If not valid, returns false.
5923     bool Validate() const;
5924     VkResult CheckCorruption(VmaAllocator hAllocator);
5925 
5926     // ppData can be null.
5927     VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
5928     void Unmap(VmaAllocator hAllocator, uint32_t count);
5929 
5930     VkResult WriteMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5931     VkResult ValidateMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5932 
5933     VkResult BindBufferMemory(
5934         const VmaAllocator hAllocator,
5935         const VmaAllocation hAllocation,
5936         VkDeviceSize allocationLocalOffset,
5937         VkBuffer hBuffer,
5938         const void* pNext);
5939     VkResult BindImageMemory(
5940         const VmaAllocator hAllocator,
5941         const VmaAllocation hAllocation,
5942         VkDeviceSize allocationLocalOffset,
5943         VkImage hImage,
5944         const void* pNext);
5945 
5946 private:
5947     VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
5948     uint32_t m_MemoryTypeIndex;
5949     uint32_t m_Id;
5950     VkDeviceMemory m_hMemory;
5951 
5952     /*
5953     Protects access to m_hMemory so it is not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
5954     Also protects m_MapCount, m_pMappedData.
5955     Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
5956     */
5957     VMA_MUTEX m_MapAndBindMutex;
5958     VmaMappingHysteresis m_MappingHysteresis;
5959     uint32_t m_MapCount;
5960     void* m_pMappedData;
5961 };
5962 #endif // _VMA_DEVICE_MEMORY_BLOCK
5963 
5964 #ifndef _VMA_ALLOCATION_T
5965 struct VmaAllocation_T
5966 {
5967     friend struct VmaDedicatedAllocationListItemTraits;
5968 
5969     enum FLAGS
5970     {
5971         FLAG_PERSISTENT_MAP   = 0x01,
5972         FLAG_MAPPING_ALLOWED  = 0x02,
5973     };
5974 
5975 public:
5976     enum ALLOCATION_TYPE
5977     {
5978         ALLOCATION_TYPE_NONE,
5979         ALLOCATION_TYPE_BLOCK,
5980         ALLOCATION_TYPE_DEDICATED,
5981     };
5982 
5983     // This struct is allocated using VmaPoolAllocator.
5984     VmaAllocation_T(bool mappingAllowed);
5985     ~VmaAllocation_T();
5986 
5987     void InitBlockAllocation(
5988         VmaDeviceMemoryBlock* block,
5989         VmaAllocHandle allocHandle,
5990         VkDeviceSize alignment,
5991         VkDeviceSize size,
5992         uint32_t memoryTypeIndex,
5993         VmaSuballocationType suballocationType,
5994         bool mapped);
5995     // pMappedData not null means allocation is created with MAPPED flag.
5996     void InitDedicatedAllocation(
5997         VmaPool hParentPool,
5998         uint32_t memoryTypeIndex,
5999         VkDeviceMemory hMemory,
6000         VmaSuballocationType suballocationType,
6001         void* pMappedData,
6002         VkDeviceSize size);
6003 
GetTypeVmaAllocation_T6004     ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
GetAlignmentVmaAllocation_T6005     VkDeviceSize GetAlignment() const { return m_Alignment; }
GetSizeVmaAllocation_T6006     VkDeviceSize GetSize() const { return m_Size; }
GetUserDataVmaAllocation_T6007     void* GetUserData() const { return m_pUserData; }
GetNameVmaAllocation_T6008     const char* GetName() const { return m_pName; }
GetSuballocationTypeVmaAllocation_T6009     VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
6010 
GetBlockVmaAllocation_T6011     VmaDeviceMemoryBlock* GetBlock() const { VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); return m_BlockAllocation.m_Block; }
GetMemoryTypeIndexVmaAllocation_T6012     uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
IsPersistentMapVmaAllocation_T6013     bool IsPersistentMap() const { return (m_Flags & FLAG_PERSISTENT_MAP) != 0; }
IsMappingAllowedVmaAllocation_T6014     bool IsMappingAllowed() const { return (m_Flags & FLAG_MAPPING_ALLOWED) != 0; }
6015 
SetUserDataVmaAllocation_T6016     void SetUserData(VmaAllocator hAllocator, void* pUserData) { m_pUserData = pUserData; }
6017     void SetName(VmaAllocator hAllocator, const char* pName);
6018     void FreeName(VmaAllocator hAllocator);
6019     uint8_t SwapBlockAllocation(VmaAllocator hAllocator, VmaAllocation allocation);
6020     VmaAllocHandle GetAllocHandle() const;
6021     VkDeviceSize GetOffset() const;
6022     VmaPool GetParentPool() const;
6023     VkDeviceMemory GetMemory() const;
6024     void* GetMappedData() const;
6025 
6026     void BlockAllocMap();
6027     void BlockAllocUnmap();
6028     VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
6029     void DedicatedAllocUnmap(VmaAllocator hAllocator);
6030 
6031     void FreeEmptyBlock();
6032 
6033 #if VMA_STATS_STRING_ENABLED
GetBufferImageUsageVmaAllocation_T6034     uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
6035 
6036     void InitBufferImageUsage(uint32_t bufferImageUsage);
6037     void PrintParameters(class VmaJsonWriter& json) const;
6038 #endif
6039 
6040 private:
6041     // Allocation out of VmaDeviceMemoryBlock.
6042     struct BlockAllocation
6043     {
6044         VmaDeviceMemoryBlock* m_Block;
6045         VmaAllocHandle m_AllocHandle;
6046     };
6047     // Allocation for an object that has its own private VkDeviceMemory.
6048     struct DedicatedAllocation
6049     {
6050         VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
6051         VkDeviceMemory m_hMemory;
6052         void* m_pMappedData; // Not null means memory is mapped.
6053         VmaAllocation_T* m_Prev;
6054         VmaAllocation_T* m_Next;
6055     };
6056     union
6057     {
6058         // Allocation out of VmaDeviceMemoryBlock.
6059         BlockAllocation m_BlockAllocation;
6060         // Allocation for an object that has its own private VkDeviceMemory.
6061         DedicatedAllocation m_DedicatedAllocation;
6062     };
6063 
6064     VkDeviceSize m_Alignment;
6065     VkDeviceSize m_Size;
6066     void* m_pUserData;
6067     char* m_pName;
6068     uint32_t m_MemoryTypeIndex;
6069     uint8_t m_Type; // ALLOCATION_TYPE
6070     uint8_t m_SuballocationType; // VmaSuballocationType
6071     // Reference counter for vmaMapMemory()/vmaUnmapMemory().
6072     uint8_t m_MapCount;
6073     uint8_t m_Flags; // enum FLAGS
6074 #if VMA_STATS_STRING_ENABLED
6075     uint32_t m_BufferImageUsage; // 0 if unknown.
6076 #endif
6077 };
6078 #endif // _VMA_ALLOCATION_T
6079 
6080 #ifndef _VMA_DEDICATED_ALLOCATION_LIST_ITEM_TRAITS
6081 struct VmaDedicatedAllocationListItemTraits
6082 {
6083     typedef VmaAllocation_T ItemType;
6084 
GetPrevVmaDedicatedAllocationListItemTraits6085     static ItemType* GetPrev(const ItemType* item)
6086     {
6087         VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
6088         return item->m_DedicatedAllocation.m_Prev;
6089     }
GetNextVmaDedicatedAllocationListItemTraits6090     static ItemType* GetNext(const ItemType* item)
6091     {
6092         VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
6093         return item->m_DedicatedAllocation.m_Next;
6094     }
AccessPrevVmaDedicatedAllocationListItemTraits6095     static ItemType*& AccessPrev(ItemType* item)
6096     {
6097         VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
6098         return item->m_DedicatedAllocation.m_Prev;
6099     }
AccessNextVmaDedicatedAllocationListItemTraits6100     static ItemType*& AccessNext(ItemType* item)
6101     {
6102         VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
6103         return item->m_DedicatedAllocation.m_Next;
6104     }
6105 };
6106 #endif // _VMA_DEDICATED_ALLOCATION_LIST_ITEM_TRAITS
6107 
6108 #ifndef _VMA_DEDICATED_ALLOCATION_LIST
6109 /*
6110 Stores linked list of VmaAllocation_T objects.
6111 Thread-safe, synchronized internally.
6112 */
6113 class VmaDedicatedAllocationList
6114 {
6115 public:
VmaDedicatedAllocationList()6116     VmaDedicatedAllocationList() {}
6117     ~VmaDedicatedAllocationList();
6118 
Init(bool useMutex)6119     void Init(bool useMutex) { m_UseMutex = useMutex; }
6120     bool Validate();
6121 
6122     void AddDetailedStatistics(VmaDetailedStatistics& inoutStats);
6123     void AddStatistics(VmaStatistics& inoutStats);
6124 #if VMA_STATS_STRING_ENABLED
6125     // Writes JSON array with the list of allocations.
6126     void BuildStatsString(VmaJsonWriter& json);
6127 #endif
6128 
6129     bool IsEmpty();
6130     void Register(VmaAllocation alloc);
6131     void Unregister(VmaAllocation alloc);
6132 
6133 private:
6134     typedef VmaIntrusiveLinkedList<VmaDedicatedAllocationListItemTraits> DedicatedAllocationLinkedList;
6135 
6136     bool m_UseMutex = true;
6137     VMA_RW_MUTEX m_Mutex;
6138     DedicatedAllocationLinkedList m_AllocationList;
6139 };
6140 
6141 #ifndef _VMA_DEDICATED_ALLOCATION_LIST_FUNCTIONS
6142 
~VmaDedicatedAllocationList()6143 VmaDedicatedAllocationList::~VmaDedicatedAllocationList()
6144 {
6145     VMA_HEAVY_ASSERT(Validate());
6146 
6147     if (!m_AllocationList.IsEmpty())
6148     {
6149         VMA_ASSERT(false && "Unfreed dedicated allocations found!");
6150     }
6151 }
6152 
Validate()6153 bool VmaDedicatedAllocationList::Validate()
6154 {
6155     const size_t declaredCount = m_AllocationList.GetCount();
6156     size_t actualCount = 0;
6157     VmaMutexLockRead lock(m_Mutex, m_UseMutex);
6158     for (VmaAllocation alloc = m_AllocationList.Front();
6159         alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc))
6160     {
6161         ++actualCount;
6162     }
6163     VMA_VALIDATE(actualCount == declaredCount);
6164 
6165     return true;
6166 }
6167 
AddDetailedStatistics(VmaDetailedStatistics & inoutStats)6168 void VmaDedicatedAllocationList::AddDetailedStatistics(VmaDetailedStatistics& inoutStats)
6169 {
6170     for(auto* item = m_AllocationList.Front(); item != nullptr; item = DedicatedAllocationLinkedList::GetNext(item))
6171     {
6172         const VkDeviceSize size = item->GetSize();
6173         inoutStats.statistics.blockCount++;
6174         inoutStats.statistics.blockBytes += size;
6175         VmaAddDetailedStatisticsAllocation(inoutStats, item->GetSize());
6176     }
6177 }
6178 
AddStatistics(VmaStatistics & inoutStats)6179 void VmaDedicatedAllocationList::AddStatistics(VmaStatistics& inoutStats)
6180 {
6181     VmaMutexLockRead lock(m_Mutex, m_UseMutex);
6182 
6183     const uint32_t allocCount = (uint32_t)m_AllocationList.GetCount();
6184     inoutStats.blockCount += allocCount;
6185     inoutStats.allocationCount += allocCount;
6186 
6187     for(auto* item = m_AllocationList.Front(); item != nullptr; item = DedicatedAllocationLinkedList::GetNext(item))
6188     {
6189         const VkDeviceSize size = item->GetSize();
6190         inoutStats.blockBytes += size;
6191         inoutStats.allocationBytes += size;
6192     }
6193 }
6194 
6195 #if VMA_STATS_STRING_ENABLED
BuildStatsString(VmaJsonWriter & json)6196 void VmaDedicatedAllocationList::BuildStatsString(VmaJsonWriter& json)
6197 {
6198     VmaMutexLockRead lock(m_Mutex, m_UseMutex);
6199     json.BeginArray();
6200     for (VmaAllocation alloc = m_AllocationList.Front();
6201         alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc))
6202     {
6203         json.BeginObject(true);
6204         alloc->PrintParameters(json);
6205         json.EndObject();
6206     }
6207     json.EndArray();
6208 }
6209 #endif // VMA_STATS_STRING_ENABLED
6210 
IsEmpty()6211 bool VmaDedicatedAllocationList::IsEmpty()
6212 {
6213     VmaMutexLockRead lock(m_Mutex, m_UseMutex);
6214     return m_AllocationList.IsEmpty();
6215 }
6216 
Register(VmaAllocation alloc)6217 void VmaDedicatedAllocationList::Register(VmaAllocation alloc)
6218 {
6219     VmaMutexLockWrite lock(m_Mutex, m_UseMutex);
6220     m_AllocationList.PushBack(alloc);
6221 }
6222 
Unregister(VmaAllocation alloc)6223 void VmaDedicatedAllocationList::Unregister(VmaAllocation alloc)
6224 {
6225     VmaMutexLockWrite lock(m_Mutex, m_UseMutex);
6226     m_AllocationList.Remove(alloc);
6227 }
6228 #endif // _VMA_DEDICATED_ALLOCATION_LIST_FUNCTIONS
6229 #endif // _VMA_DEDICATED_ALLOCATION_LIST
6230 
6231 #ifndef _VMA_SUBALLOCATION
6232 /*
6233 Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
6234 allocated memory block or free.
6235 */
6236 struct VmaSuballocation
6237 {
6238     VkDeviceSize offset;
6239     VkDeviceSize size;
6240     void* userData;
6241     VmaSuballocationType type;
6242 };
6243 
6244 // Comparator for offsets.
6245 struct VmaSuballocationOffsetLess
6246 {
operatorVmaSuballocationOffsetLess6247     bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6248     {
6249         return lhs.offset < rhs.offset;
6250     }
6251 };
6252 
6253 struct VmaSuballocationOffsetGreater
6254 {
operatorVmaSuballocationOffsetGreater6255     bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6256     {
6257         return lhs.offset > rhs.offset;
6258     }
6259 };
6260 
6261 struct VmaSuballocationItemSizeLess
6262 {
operatorVmaSuballocationItemSizeLess6263     bool operator()(const VmaSuballocationList::iterator lhs,
6264         const VmaSuballocationList::iterator rhs) const
6265     {
6266         return lhs->size < rhs->size;
6267     }
6268 
operatorVmaSuballocationItemSizeLess6269     bool operator()(const VmaSuballocationList::iterator lhs,
6270         VkDeviceSize rhsSize) const
6271     {
6272         return lhs->size < rhsSize;
6273     }
6274 };
6275 #endif // _VMA_SUBALLOCATION
6276 
6277 #ifndef _VMA_ALLOCATION_REQUEST
6278 /*
6279 Parameters of planned allocation inside a VmaDeviceMemoryBlock.
6280 item points to a FREE suballocation.
6281 */
6282 struct VmaAllocationRequest
6283 {
6284     VmaAllocHandle allocHandle;
6285     VkDeviceSize size;
6286     VmaSuballocationList::iterator item;
6287     void* customData;
6288     uint64_t algorithmData;
6289     VmaAllocationRequestType type;
6290 };
6291 #endif // _VMA_ALLOCATION_REQUEST
6292 
6293 #ifndef _VMA_BLOCK_METADATA
6294 /*
6295 Data structure used for bookkeeping of allocations and unused ranges of memory
6296 in a single VkDeviceMemory block.
6297 */
6298 class VmaBlockMetadata
6299 {
6300 public:
6301     // pAllocationCallbacks, if not null, must be owned externally - alive and unchanged for the whole lifetime of this object.
6302     VmaBlockMetadata(const VkAllocationCallbacks* pAllocationCallbacks,
6303         VkDeviceSize bufferImageGranularity, bool isVirtual);
6304     virtual ~VmaBlockMetadata() = default;
6305 
Init(VkDeviceSize size)6306     virtual void Init(VkDeviceSize size) { m_Size = size; }
IsVirtual()6307     bool IsVirtual() const { return m_IsVirtual; }
GetSize()6308     VkDeviceSize GetSize() const { return m_Size; }
6309 
6310     // Validates all data structures inside this object. If not valid, returns false.
6311     virtual bool Validate() const = 0;
6312     virtual size_t GetAllocationCount() const = 0;
6313     virtual size_t GetFreeRegionsCount() const = 0;
6314     virtual VkDeviceSize GetSumFreeSize() const = 0;
6315     // Returns true if this block is empty - contains only single free suballocation.
6316     virtual bool IsEmpty() const = 0;
6317     virtual void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) = 0;
6318     virtual VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const = 0;
6319     virtual void* GetAllocationUserData(VmaAllocHandle allocHandle) const = 0;
6320 
6321     virtual VmaAllocHandle GetAllocationListBegin() const = 0;
6322     virtual VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const = 0;
6323     virtual VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const = 0;
6324 
6325     // Shouldn't modify blockCount.
6326     virtual void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const = 0;
6327     virtual void AddStatistics(VmaStatistics& inoutStats) const = 0;
6328 
6329 #if VMA_STATS_STRING_ENABLED
6330     virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
6331 #endif
6332 
6333     // Tries to find a place for suballocation with given parameters inside this block.
6334     // If succeeded, fills pAllocationRequest and returns true.
6335     // If failed, returns false.
6336     virtual bool CreateAllocationRequest(
6337         VkDeviceSize allocSize,
6338         VkDeviceSize allocAlignment,
6339         bool upperAddress,
6340         VmaSuballocationType allocType,
6341         // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
6342         uint32_t strategy,
6343         VmaAllocationRequest* pAllocationRequest) = 0;
6344 
6345     virtual VkResult CheckCorruption(const void* pBlockData) = 0;
6346 
6347     // Makes actual allocation based on request. Request must already be checked and valid.
6348     virtual void Alloc(
6349         const VmaAllocationRequest& request,
6350         VmaSuballocationType type,
6351         void* userData) = 0;
6352 
6353     // Frees suballocation assigned to given memory region.
6354     virtual void Free(VmaAllocHandle allocHandle) = 0;
6355 
6356     // Frees all allocations.
6357     // Careful! Don't call it if there are VmaAllocation objects owned by userData of cleared allocations!
6358     virtual void Clear() = 0;
6359 
6360     virtual void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) = 0;
6361     virtual void DebugLogAllAllocations() const = 0;
6362 
6363 protected:
GetAllocationCallbacks()6364     const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
GetBufferImageGranularity()6365     VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
GetDebugMargin()6366     VkDeviceSize GetDebugMargin() const { return IsVirtual() ? 0 : VMA_DEBUG_MARGIN; }
6367 
6368     void DebugLogAllocation(VkDeviceSize offset, VkDeviceSize size, void* userData) const;
6369 #if VMA_STATS_STRING_ENABLED
6370     // mapRefCount == UINT32_MAX means unspecified.
6371     void PrintDetailedMap_Begin(class VmaJsonWriter& json,
6372         VkDeviceSize unusedBytes,
6373         size_t allocationCount,
6374         size_t unusedRangeCount) const;
6375     void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
6376         VkDeviceSize offset, VkDeviceSize size, void* userData) const;
6377     void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
6378         VkDeviceSize offset,
6379         VkDeviceSize size) const;
6380     void PrintDetailedMap_End(class VmaJsonWriter& json) const;
6381 #endif
6382 
6383 private:
6384     VkDeviceSize m_Size;
6385     const VkAllocationCallbacks* m_pAllocationCallbacks;
6386     const VkDeviceSize m_BufferImageGranularity;
6387     const bool m_IsVirtual;
6388 };
6389 
6390 #ifndef _VMA_BLOCK_METADATA_FUNCTIONS
VmaBlockMetadata(const VkAllocationCallbacks * pAllocationCallbacks,VkDeviceSize bufferImageGranularity,bool isVirtual)6391 VmaBlockMetadata::VmaBlockMetadata(const VkAllocationCallbacks* pAllocationCallbacks,
6392     VkDeviceSize bufferImageGranularity, bool isVirtual)
6393     : m_Size(0),
6394     m_pAllocationCallbacks(pAllocationCallbacks),
6395     m_BufferImageGranularity(bufferImageGranularity),
6396     m_IsVirtual(isVirtual) {}
6397 
DebugLogAllocation(VkDeviceSize offset,VkDeviceSize size,void * userData)6398 void VmaBlockMetadata::DebugLogAllocation(VkDeviceSize offset, VkDeviceSize size, void* userData) const
6399 {
6400     if (IsVirtual())
6401     {
6402         VMA_DEBUG_LOG("UNFREED VIRTUAL ALLOCATION; Offset: %llu; Size: %llu; UserData: %p", offset, size, userData);
6403     }
6404     else
6405     {
6406         VMA_ASSERT(userData != VMA_NULL);
6407         VmaAllocation allocation = reinterpret_cast<VmaAllocation>(userData);
6408 
6409         userData = allocation->GetUserData();
6410         const char* name = allocation->GetName();
6411 
6412 #if VMA_STATS_STRING_ENABLED
6413         VMA_DEBUG_LOG("UNFREED ALLOCATION; Offset: %llu; Size: %llu; UserData: %p; Name: %s; Type: %s; Usage: %u",
6414             offset, size, userData, name ? name : "vma_empty",
6415             VMA_SUBALLOCATION_TYPE_NAMES[allocation->GetSuballocationType()],
6416             allocation->GetBufferImageUsage());
6417 #else
6418         VMA_DEBUG_LOG("UNFREED ALLOCATION; Offset: %llu; Size: %llu; UserData: %p; Name: %s; Type: %u",
6419             offset, size, userData, name ? name : "vma_empty",
6420             (uint32_t)allocation->GetSuballocationType());
6421 #endif // VMA_STATS_STRING_ENABLED
6422     }
6423 
6424 }
6425 
6426 #if VMA_STATS_STRING_ENABLED
PrintDetailedMap_Begin(class VmaJsonWriter & json,VkDeviceSize unusedBytes,size_t allocationCount,size_t unusedRangeCount)6427 void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
6428     VkDeviceSize unusedBytes, size_t allocationCount, size_t unusedRangeCount) const
6429 {
6430     json.WriteString("TotalBytes");
6431     json.WriteNumber(GetSize());
6432 
6433     json.WriteString("UnusedBytes");
6434     json.WriteSize(unusedBytes);
6435 
6436     json.WriteString("Allocations");
6437     json.WriteSize(allocationCount);
6438 
6439     json.WriteString("UnusedRanges");
6440     json.WriteSize(unusedRangeCount);
6441 
6442     json.WriteString("Suballocations");
6443     json.BeginArray();
6444 }
6445 
PrintDetailedMap_Allocation(class VmaJsonWriter & json,VkDeviceSize offset,VkDeviceSize size,void * userData)6446 void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
6447     VkDeviceSize offset, VkDeviceSize size, void* userData) const
6448 {
6449     json.BeginObject(true);
6450 
6451     json.WriteString("Offset");
6452     json.WriteNumber(offset);
6453 
6454     if (IsVirtual())
6455     {
6456         json.WriteString("Size");
6457         json.WriteNumber(size);
6458         if (userData)
6459         {
6460             json.WriteString("CustomData");
6461             json.BeginString();
6462             json.ContinueString_Pointer(userData);
6463             json.EndString();
6464         }
6465     }
6466     else
6467     {
6468         ((VmaAllocation)userData)->PrintParameters(json);
6469     }
6470 
6471     json.EndObject();
6472 }
6473 
PrintDetailedMap_UnusedRange(class VmaJsonWriter & json,VkDeviceSize offset,VkDeviceSize size)6474 void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
6475     VkDeviceSize offset, VkDeviceSize size) const
6476 {
6477     json.BeginObject(true);
6478 
6479     json.WriteString("Offset");
6480     json.WriteNumber(offset);
6481 
6482     json.WriteString("Type");
6483     json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
6484 
6485     json.WriteString("Size");
6486     json.WriteNumber(size);
6487 
6488     json.EndObject();
6489 }
6490 
PrintDetailedMap_End(class VmaJsonWriter & json)6491 void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
6492 {
6493     json.EndArray();
6494 }
6495 #endif // VMA_STATS_STRING_ENABLED
6496 #endif // _VMA_BLOCK_METADATA_FUNCTIONS
6497 #endif // _VMA_BLOCK_METADATA
6498 
6499 #ifndef _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY
6500 // Before deleting object of this class remember to call 'Destroy()'
6501 class VmaBlockBufferImageGranularity final
6502 {
6503 public:
6504     struct ValidationContext
6505     {
6506         const VkAllocationCallbacks* allocCallbacks;
6507         uint16_t* pageAllocs;
6508     };
6509 
6510     VmaBlockBufferImageGranularity(VkDeviceSize bufferImageGranularity);
6511     ~VmaBlockBufferImageGranularity();
6512 
IsEnabled()6513     bool IsEnabled() const { return m_BufferImageGranularity > MAX_LOW_BUFFER_IMAGE_GRANULARITY; }
6514 
6515     void Init(const VkAllocationCallbacks* pAllocationCallbacks, VkDeviceSize size);
6516     // Before destroying object you must call free it's memory
6517     void Destroy(const VkAllocationCallbacks* pAllocationCallbacks);
6518 
6519     void RoundupAllocRequest(VmaSuballocationType allocType,
6520         VkDeviceSize& inOutAllocSize,
6521         VkDeviceSize& inOutAllocAlignment) const;
6522 
6523     bool CheckConflictAndAlignUp(VkDeviceSize& inOutAllocOffset,
6524         VkDeviceSize allocSize,
6525         VkDeviceSize blockOffset,
6526         VkDeviceSize blockSize,
6527         VmaSuballocationType allocType) const;
6528 
6529     void AllocPages(uint8_t allocType, VkDeviceSize offset, VkDeviceSize size);
6530     void FreePages(VkDeviceSize offset, VkDeviceSize size);
6531     void Clear();
6532 
6533     ValidationContext StartValidation(const VkAllocationCallbacks* pAllocationCallbacks,
6534         bool isVirutal) const;
6535     bool Validate(ValidationContext& ctx, VkDeviceSize offset, VkDeviceSize size) const;
6536     bool FinishValidation(ValidationContext& ctx) const;
6537 
6538 private:
6539     static const uint16_t MAX_LOW_BUFFER_IMAGE_GRANULARITY = 256;
6540 
6541     struct RegionInfo
6542     {
6543         uint8_t allocType;
6544         uint16_t allocCount;
6545     };
6546 
6547     VkDeviceSize m_BufferImageGranularity;
6548     uint32_t m_RegionCount;
6549     RegionInfo* m_RegionInfo;
6550 
GetStartPage(VkDeviceSize offset)6551     uint32_t GetStartPage(VkDeviceSize offset) const { return OffsetToPageIndex(offset & ~(m_BufferImageGranularity - 1)); }
GetEndPage(VkDeviceSize offset,VkDeviceSize size)6552     uint32_t GetEndPage(VkDeviceSize offset, VkDeviceSize size) const { return OffsetToPageIndex((offset + size - 1) & ~(m_BufferImageGranularity - 1)); }
6553 
6554     uint32_t OffsetToPageIndex(VkDeviceSize offset) const;
6555     void AllocPage(RegionInfo& page, uint8_t allocType);
6556 };
6557 
6558 #ifndef _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY_FUNCTIONS
VmaBlockBufferImageGranularity(VkDeviceSize bufferImageGranularity)6559 VmaBlockBufferImageGranularity::VmaBlockBufferImageGranularity(VkDeviceSize bufferImageGranularity)
6560     : m_BufferImageGranularity(bufferImageGranularity),
6561     m_RegionCount(0),
6562     m_RegionInfo(VMA_NULL) {}
6563 
~VmaBlockBufferImageGranularity()6564 VmaBlockBufferImageGranularity::~VmaBlockBufferImageGranularity()
6565 {
6566     VMA_ASSERT(m_RegionInfo == VMA_NULL && "Free not called before destroying object!");
6567 }
6568 
Init(const VkAllocationCallbacks * pAllocationCallbacks,VkDeviceSize size)6569 void VmaBlockBufferImageGranularity::Init(const VkAllocationCallbacks* pAllocationCallbacks, VkDeviceSize size)
6570 {
6571     if (IsEnabled())
6572     {
6573         m_RegionCount = static_cast<uint32_t>(VmaDivideRoundingUp(size, m_BufferImageGranularity));
6574         m_RegionInfo = vma_new_array(pAllocationCallbacks, RegionInfo, m_RegionCount);
6575         memset(m_RegionInfo, 0, m_RegionCount * sizeof(RegionInfo));
6576     }
6577 }
6578 
Destroy(const VkAllocationCallbacks * pAllocationCallbacks)6579 void VmaBlockBufferImageGranularity::Destroy(const VkAllocationCallbacks* pAllocationCallbacks)
6580 {
6581     if (m_RegionInfo)
6582     {
6583         vma_delete_array(pAllocationCallbacks, m_RegionInfo, m_RegionCount);
6584         m_RegionInfo = VMA_NULL;
6585     }
6586 }
6587 
RoundupAllocRequest(VmaSuballocationType allocType,VkDeviceSize & inOutAllocSize,VkDeviceSize & inOutAllocAlignment)6588 void VmaBlockBufferImageGranularity::RoundupAllocRequest(VmaSuballocationType allocType,
6589     VkDeviceSize& inOutAllocSize,
6590     VkDeviceSize& inOutAllocAlignment) const
6591 {
6592     if (m_BufferImageGranularity > 1 &&
6593         m_BufferImageGranularity <= MAX_LOW_BUFFER_IMAGE_GRANULARITY)
6594     {
6595         if (allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
6596             allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
6597             allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
6598         {
6599             inOutAllocAlignment = VMA_MAX(inOutAllocAlignment, m_BufferImageGranularity);
6600             inOutAllocSize = VmaAlignUp(inOutAllocSize, m_BufferImageGranularity);
6601         }
6602     }
6603 }
6604 
CheckConflictAndAlignUp(VkDeviceSize & inOutAllocOffset,VkDeviceSize allocSize,VkDeviceSize blockOffset,VkDeviceSize blockSize,VmaSuballocationType allocType)6605 bool VmaBlockBufferImageGranularity::CheckConflictAndAlignUp(VkDeviceSize& inOutAllocOffset,
6606     VkDeviceSize allocSize,
6607     VkDeviceSize blockOffset,
6608     VkDeviceSize blockSize,
6609     VmaSuballocationType allocType) const
6610 {
6611     if (IsEnabled())
6612     {
6613         uint32_t startPage = GetStartPage(inOutAllocOffset);
6614         if (m_RegionInfo[startPage].allocCount > 0 &&
6615             VmaIsBufferImageGranularityConflict(static_cast<VmaSuballocationType>(m_RegionInfo[startPage].allocType), allocType))
6616         {
6617             inOutAllocOffset = VmaAlignUp(inOutAllocOffset, m_BufferImageGranularity);
6618             if (blockSize < allocSize + inOutAllocOffset - blockOffset)
6619                 return true;
6620             ++startPage;
6621         }
6622         uint32_t endPage = GetEndPage(inOutAllocOffset, allocSize);
6623         if (endPage != startPage &&
6624             m_RegionInfo[endPage].allocCount > 0 &&
6625             VmaIsBufferImageGranularityConflict(static_cast<VmaSuballocationType>(m_RegionInfo[endPage].allocType), allocType))
6626         {
6627             return true;
6628         }
6629     }
6630     return false;
6631 }
6632 
AllocPages(uint8_t allocType,VkDeviceSize offset,VkDeviceSize size)6633 void VmaBlockBufferImageGranularity::AllocPages(uint8_t allocType, VkDeviceSize offset, VkDeviceSize size)
6634 {
6635     if (IsEnabled())
6636     {
6637         uint32_t startPage = GetStartPage(offset);
6638         AllocPage(m_RegionInfo[startPage], allocType);
6639 
6640         uint32_t endPage = GetEndPage(offset, size);
6641         if (startPage != endPage)
6642             AllocPage(m_RegionInfo[endPage], allocType);
6643     }
6644 }
6645 
FreePages(VkDeviceSize offset,VkDeviceSize size)6646 void VmaBlockBufferImageGranularity::FreePages(VkDeviceSize offset, VkDeviceSize size)
6647 {
6648     if (IsEnabled())
6649     {
6650         uint32_t startPage = GetStartPage(offset);
6651         --m_RegionInfo[startPage].allocCount;
6652         if (m_RegionInfo[startPage].allocCount == 0)
6653             m_RegionInfo[startPage].allocType = VMA_SUBALLOCATION_TYPE_FREE;
6654         uint32_t endPage = GetEndPage(offset, size);
6655         if (startPage != endPage)
6656         {
6657             --m_RegionInfo[endPage].allocCount;
6658             if (m_RegionInfo[endPage].allocCount == 0)
6659                 m_RegionInfo[endPage].allocType = VMA_SUBALLOCATION_TYPE_FREE;
6660         }
6661     }
6662 }
6663 
Clear()6664 void VmaBlockBufferImageGranularity::Clear()
6665 {
6666     if (m_RegionInfo)
6667         memset(m_RegionInfo, 0, m_RegionCount * sizeof(RegionInfo));
6668 }
6669 
StartValidation(const VkAllocationCallbacks * pAllocationCallbacks,bool isVirutal)6670 VmaBlockBufferImageGranularity::ValidationContext VmaBlockBufferImageGranularity::StartValidation(
6671     const VkAllocationCallbacks* pAllocationCallbacks, bool isVirutal) const
6672 {
6673     ValidationContext ctx{ pAllocationCallbacks, VMA_NULL };
6674     if (!isVirutal && IsEnabled())
6675     {
6676         ctx.pageAllocs = vma_new_array(pAllocationCallbacks, uint16_t, m_RegionCount);
6677         memset(ctx.pageAllocs, 0, m_RegionCount * sizeof(uint16_t));
6678     }
6679     return ctx;
6680 }
6681 
Validate(ValidationContext & ctx,VkDeviceSize offset,VkDeviceSize size)6682 bool VmaBlockBufferImageGranularity::Validate(ValidationContext& ctx,
6683     VkDeviceSize offset, VkDeviceSize size) const
6684 {
6685     if (IsEnabled())
6686     {
6687         uint32_t start = GetStartPage(offset);
6688         ++ctx.pageAllocs[start];
6689         VMA_VALIDATE(m_RegionInfo[start].allocCount > 0);
6690 
6691         uint32_t end = GetEndPage(offset, size);
6692         if (start != end)
6693         {
6694             ++ctx.pageAllocs[end];
6695             VMA_VALIDATE(m_RegionInfo[end].allocCount > 0);
6696         }
6697     }
6698     return true;
6699 }
6700 
FinishValidation(ValidationContext & ctx)6701 bool VmaBlockBufferImageGranularity::FinishValidation(ValidationContext& ctx) const
6702 {
6703     // Check proper page structure
6704     if (IsEnabled())
6705     {
6706         VMA_ASSERT(ctx.pageAllocs != VMA_NULL && "Validation context not initialized!");
6707 
6708         for (uint32_t page = 0; page < m_RegionCount; ++page)
6709         {
6710             VMA_VALIDATE(ctx.pageAllocs[page] == m_RegionInfo[page].allocCount);
6711         }
6712         vma_delete_array(ctx.allocCallbacks, ctx.pageAllocs, m_RegionCount);
6713         ctx.pageAllocs = VMA_NULL;
6714     }
6715     return true;
6716 }
6717 
OffsetToPageIndex(VkDeviceSize offset)6718 uint32_t VmaBlockBufferImageGranularity::OffsetToPageIndex(VkDeviceSize offset) const
6719 {
6720     return static_cast<uint32_t>(offset >> VMA_BITSCAN_MSB(m_BufferImageGranularity));
6721 }
6722 
AllocPage(RegionInfo & page,uint8_t allocType)6723 void VmaBlockBufferImageGranularity::AllocPage(RegionInfo& page, uint8_t allocType)
6724 {
6725     // When current alloc type is free then it can be overriden by new type
6726     if (page.allocCount == 0 || (page.allocCount > 0 && page.allocType == VMA_SUBALLOCATION_TYPE_FREE))
6727         page.allocType = allocType;
6728 
6729     ++page.allocCount;
6730 }
6731 #endif // _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY_FUNCTIONS
6732 #endif // _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY
6733 
6734 #if 0
6735 #ifndef _VMA_BLOCK_METADATA_GENERIC
6736 class VmaBlockMetadata_Generic : public VmaBlockMetadata
6737 {
6738     friend class VmaDefragmentationAlgorithm_Generic;
6739     friend class VmaDefragmentationAlgorithm_Fast;
6740     VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
6741 public:
6742     VmaBlockMetadata_Generic(const VkAllocationCallbacks* pAllocationCallbacks,
6743         VkDeviceSize bufferImageGranularity, bool isVirtual);
6744     virtual ~VmaBlockMetadata_Generic() = default;
6745 
6746     size_t GetAllocationCount() const override { return m_Suballocations.size() - m_FreeCount; }
6747     VkDeviceSize GetSumFreeSize() const override { return m_SumFreeSize; }
6748     bool IsEmpty() const override { return (m_Suballocations.size() == 1) && (m_FreeCount == 1); }
6749     void Free(VmaAllocHandle allocHandle) override { FreeSuballocation(FindAtOffset((VkDeviceSize)allocHandle - 1)); }
6750     VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle - 1; };
6751 
6752     void Init(VkDeviceSize size) override;
6753     bool Validate() const override;
6754 
6755     void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override;
6756     void AddStatistics(VmaStatistics& inoutStats) const override;
6757 
6758     void FreeEmptyBlock();
6759 
6760 #if VMA_STATS_STRING_ENABLED
6761     void PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const override;
6762 #endif
6763 
6764     bool CreateAllocationRequest(
6765         VkDeviceSize allocSize,
6766         VkDeviceSize allocAlignment,
6767         bool upperAddress,
6768         VmaSuballocationType allocType,
6769         uint32_t strategy,
6770         VmaAllocationRequest* pAllocationRequest) override;
6771 
6772     VkResult CheckCorruption(const void* pBlockData) override;
6773 
6774     void Alloc(
6775         const VmaAllocationRequest& request,
6776         VmaSuballocationType type,
6777         void* userData) override;
6778 
6779     void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override;
6780     void* GetAllocationUserData(VmaAllocHandle allocHandle) const override;
6781     VmaAllocHandle GetAllocationListBegin() const override;
6782     VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override;
6783     void Clear() override;
6784     void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override;
6785     void DebugLogAllAllocations() const override;
6786 
6787 private:
6788     uint32_t m_FreeCount;
6789     VkDeviceSize m_SumFreeSize;
6790     VmaSuballocationList m_Suballocations;
6791     // Suballocations that are free. Sorted by size, ascending.
6792     VmaVector<VmaSuballocationList::iterator, VmaStlAllocator<VmaSuballocationList::iterator>> m_FreeSuballocationsBySize;
6793 
6794     VkDeviceSize AlignAllocationSize(VkDeviceSize size) const { return IsVirtual() ? size : VmaAlignUp(size, (VkDeviceSize)16); }
6795 
6796     VmaSuballocationList::iterator FindAtOffset(VkDeviceSize offset) const;
6797     bool ValidateFreeSuballocationList() const;
6798 
6799     // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
6800     // If yes, fills pOffset and returns true. If no, returns false.
6801     bool CheckAllocation(
6802         VkDeviceSize allocSize,
6803         VkDeviceSize allocAlignment,
6804         VmaSuballocationType allocType,
6805         VmaSuballocationList::const_iterator suballocItem,
6806         VmaAllocHandle* pAllocHandle) const;
6807 
6808     // Given free suballocation, it merges it with following one, which must also be free.
6809     void MergeFreeWithNext(VmaSuballocationList::iterator item);
6810     // Releases given suballocation, making it free.
6811     // Merges it with adjacent free suballocations if applicable.
6812     // Returns iterator to new free suballocation at this place.
6813     VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
6814     // Given free suballocation, it inserts it into sorted list of
6815     // m_FreeSuballocationsBySize if it is suitable.
6816     void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
6817     // Given free suballocation, it removes it from sorted list of
6818     // m_FreeSuballocationsBySize if it is suitable.
6819     void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
6820 };
6821 
6822 #ifndef _VMA_BLOCK_METADATA_GENERIC_FUNCTIONS
6823 VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(const VkAllocationCallbacks* pAllocationCallbacks,
6824     VkDeviceSize bufferImageGranularity, bool isVirtual)
6825     : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual),
6826     m_FreeCount(0),
6827     m_SumFreeSize(0),
6828     m_Suballocations(VmaStlAllocator<VmaSuballocation>(pAllocationCallbacks)),
6829     m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(pAllocationCallbacks)) {}
6830 
6831 void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
6832 {
6833     VmaBlockMetadata::Init(size);
6834 
6835     m_FreeCount = 1;
6836     m_SumFreeSize = size;
6837 
6838     VmaSuballocation suballoc = {};
6839     suballoc.offset = 0;
6840     suballoc.size = size;
6841     suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
6842 
6843     m_Suballocations.push_back(suballoc);
6844     m_FreeSuballocationsBySize.push_back(m_Suballocations.begin());
6845 }
6846 
6847 bool VmaBlockMetadata_Generic::Validate() const
6848 {
6849     VMA_VALIDATE(!m_Suballocations.empty());
6850 
6851     // Expected offset of new suballocation as calculated from previous ones.
6852     VkDeviceSize calculatedOffset = 0;
6853     // Expected number of free suballocations as calculated from traversing their list.
6854     uint32_t calculatedFreeCount = 0;
6855     // Expected sum size of free suballocations as calculated from traversing their list.
6856     VkDeviceSize calculatedSumFreeSize = 0;
6857     // Expected number of free suballocations that should be registered in
6858     // m_FreeSuballocationsBySize calculated from traversing their list.
6859     size_t freeSuballocationsToRegister = 0;
6860     // True if previous visited suballocation was free.
6861     bool prevFree = false;
6862 
6863     const VkDeviceSize debugMargin = GetDebugMargin();
6864 
6865     for (const auto& subAlloc : m_Suballocations)
6866     {
6867         // Actual offset of this suballocation doesn't match expected one.
6868         VMA_VALIDATE(subAlloc.offset == calculatedOffset);
6869 
6870         const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
6871         // Two adjacent free suballocations are invalid. They should be merged.
6872         VMA_VALIDATE(!prevFree || !currFree);
6873 
6874         VmaAllocation alloc = (VmaAllocation)subAlloc.userData;
6875         if (!IsVirtual())
6876         {
6877             VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));
6878         }
6879 
6880         if (currFree)
6881         {
6882             calculatedSumFreeSize += subAlloc.size;
6883             ++calculatedFreeCount;
6884             ++freeSuballocationsToRegister;
6885 
6886             // Margin required between allocations - every free space must be at least that large.
6887             VMA_VALIDATE(subAlloc.size >= debugMargin);
6888         }
6889         else
6890         {
6891             if (!IsVirtual())
6892             {
6893                 VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == subAlloc.offset + 1);
6894                 VMA_VALIDATE(alloc->GetSize() == subAlloc.size);
6895             }
6896 
6897             // Margin required between allocations - previous allocation must be free.
6898             VMA_VALIDATE(debugMargin == 0 || prevFree);
6899         }
6900 
6901         calculatedOffset += subAlloc.size;
6902         prevFree = currFree;
6903     }
6904 
6905     // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
6906     // match expected one.
6907     VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
6908 
6909     VkDeviceSize lastSize = 0;
6910     for (size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
6911     {
6912         VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
6913 
6914         // Only free suballocations can be registered in m_FreeSuballocationsBySize.
6915         VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
6916         // They must be sorted by size ascending.
6917         VMA_VALIDATE(suballocItem->size >= lastSize);
6918 
6919         lastSize = suballocItem->size;
6920     }
6921 
6922     // Check if totals match calculated values.
6923     VMA_VALIDATE(ValidateFreeSuballocationList());
6924     VMA_VALIDATE(calculatedOffset == GetSize());
6925     VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
6926     VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
6927 
6928     return true;
6929 }
6930 
6931 void VmaBlockMetadata_Generic::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const
6932 {
6933     const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
6934     inoutStats.statistics.blockCount++;
6935     inoutStats.statistics.blockBytes += GetSize();
6936 
6937     for (const auto& suballoc : m_Suballocations)
6938     {
6939         if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
6940             VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size);
6941         else
6942             VmaAddDetailedStatisticsUnusedRange(inoutStats, suballoc.size);
6943     }
6944 }
6945 
6946 void VmaBlockMetadata_Generic::AddStatistics(VmaStatistics& inoutStats) const
6947 {
6948     inoutStats.blockCount++;
6949     inoutStats.allocationCount += (uint32_t)m_Suballocations.size() - m_FreeCount;
6950     inoutStats.blockBytes += GetSize();
6951     inoutStats.allocationBytes += GetSize() - m_SumFreeSize;
6952 }
6953 
6954 #if VMA_STATS_STRING_ENABLED
6955 void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const
6956 {
6957     PrintDetailedMap_Begin(json,
6958         m_SumFreeSize, // unusedBytes
6959         m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
6960         m_FreeCount, // unusedRangeCount
6961         mapRefCount);
6962 
6963     for (const auto& suballoc : m_Suballocations)
6964     {
6965         if (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
6966         {
6967             PrintDetailedMap_UnusedRange(json, suballoc.offset, suballoc.size);
6968         }
6969         else
6970         {
6971             PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);
6972         }
6973     }
6974 
6975     PrintDetailedMap_End(json);
6976 }
6977 #endif // VMA_STATS_STRING_ENABLED
6978 
6979 bool VmaBlockMetadata_Generic::CreateAllocationRequest(
6980     VkDeviceSize allocSize,
6981     VkDeviceSize allocAlignment,
6982     bool upperAddress,
6983     VmaSuballocationType allocType,
6984     uint32_t strategy,
6985     VmaAllocationRequest* pAllocationRequest)
6986 {
6987     VMA_ASSERT(allocSize > 0);
6988     VMA_ASSERT(!upperAddress);
6989     VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
6990     VMA_ASSERT(pAllocationRequest != VMA_NULL);
6991     VMA_HEAVY_ASSERT(Validate());
6992 
6993     allocSize = AlignAllocationSize(allocSize);
6994 
6995     pAllocationRequest->type = VmaAllocationRequestType::Normal;
6996     pAllocationRequest->size = allocSize;
6997 
6998     const VkDeviceSize debugMargin = GetDebugMargin();
6999 
7000     // There is not enough total free space in this block to fulfill the request: Early return.
7001     if (m_SumFreeSize < allocSize + debugMargin)
7002     {
7003         return false;
7004     }
7005 
7006     // New algorithm, efficiently searching freeSuballocationsBySize.
7007     const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
7008     if (freeSuballocCount > 0)
7009     {
7010         if (strategy == 0 ||
7011             strategy == VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT)
7012         {
7013             // Find first free suballocation with size not less than allocSize + debugMargin.
7014             VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
7015                 m_FreeSuballocationsBySize.data(),
7016                 m_FreeSuballocationsBySize.data() + freeSuballocCount,
7017                 allocSize + debugMargin,
7018                 VmaSuballocationItemSizeLess());
7019             size_t index = it - m_FreeSuballocationsBySize.data();
7020             for (; index < freeSuballocCount; ++index)
7021             {
7022                 if (CheckAllocation(
7023                     allocSize,
7024                     allocAlignment,
7025                     allocType,
7026                     m_FreeSuballocationsBySize[index],
7027                     &pAllocationRequest->allocHandle))
7028                 {
7029                     pAllocationRequest->item = m_FreeSuballocationsBySize[index];
7030                     return true;
7031                 }
7032             }
7033         }
7034         else if (strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
7035         {
7036             for (VmaSuballocationList::iterator it = m_Suballocations.begin();
7037                 it != m_Suballocations.end();
7038                 ++it)
7039             {
7040                 if (it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
7041                     allocSize,
7042                     allocAlignment,
7043                     allocType,
7044                     it,
7045                     &pAllocationRequest->allocHandle))
7046                 {
7047                     pAllocationRequest->item = it;
7048                     return true;
7049                 }
7050             }
7051         }
7052         else
7053         {
7054             VMA_ASSERT(strategy & (VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT | VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT ));
7055             // Search staring from biggest suballocations.
7056             for (size_t index = freeSuballocCount; index--; )
7057             {
7058                 if (CheckAllocation(
7059                     allocSize,
7060                     allocAlignment,
7061                     allocType,
7062                     m_FreeSuballocationsBySize[index],
7063                     &pAllocationRequest->allocHandle))
7064                 {
7065                     pAllocationRequest->item = m_FreeSuballocationsBySize[index];
7066                     return true;
7067                 }
7068             }
7069         }
7070     }
7071 
7072     return false;
7073 }
7074 
7075 VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
7076 {
7077     for (auto& suballoc : m_Suballocations)
7078     {
7079         if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
7080         {
7081             if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
7082             {
7083                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
7084                 return VK_ERROR_UNKNOWN_COPY;
7085             }
7086         }
7087     }
7088 
7089     return VK_SUCCESS;
7090 }
7091 
7092 void VmaBlockMetadata_Generic::Alloc(
7093     const VmaAllocationRequest& request,
7094     VmaSuballocationType type,
7095     void* userData)
7096 {
7097     VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
7098     VMA_ASSERT(request.item != m_Suballocations.end());
7099     VmaSuballocation& suballoc = *request.item;
7100     // Given suballocation is a free block.
7101     VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
7102 
7103     // Given offset is inside this suballocation.
7104     VMA_ASSERT((VkDeviceSize)request.allocHandle - 1 >= suballoc.offset);
7105     const VkDeviceSize paddingBegin = (VkDeviceSize)request.allocHandle - suballoc.offset - 1;
7106     VMA_ASSERT(suballoc.size >= paddingBegin + request.size);
7107     const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - request.size;
7108 
7109     // Unregister this free suballocation from m_FreeSuballocationsBySize and update
7110     // it to become used.
7111     UnregisterFreeSuballocation(request.item);
7112 
7113     suballoc.offset = (VkDeviceSize)request.allocHandle - 1;
7114     suballoc.size = request.size;
7115     suballoc.type = type;
7116     suballoc.userData = userData;
7117 
7118     // If there are any free bytes remaining at the end, insert new free suballocation after current one.
7119     if (paddingEnd)
7120     {
7121         VmaSuballocation paddingSuballoc = {};
7122         paddingSuballoc.offset = suballoc.offset + suballoc.size;
7123         paddingSuballoc.size = paddingEnd;
7124         paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
7125         VmaSuballocationList::iterator next = request.item;
7126         ++next;
7127         const VmaSuballocationList::iterator paddingEndItem =
7128             m_Suballocations.insert(next, paddingSuballoc);
7129         RegisterFreeSuballocation(paddingEndItem);
7130     }
7131 
7132     // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
7133     if (paddingBegin)
7134     {
7135         VmaSuballocation paddingSuballoc = {};
7136         paddingSuballoc.offset = suballoc.offset - paddingBegin;
7137         paddingSuballoc.size = paddingBegin;
7138         paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
7139         const VmaSuballocationList::iterator paddingBeginItem =
7140             m_Suballocations.insert(request.item, paddingSuballoc);
7141         RegisterFreeSuballocation(paddingBeginItem);
7142     }
7143 
7144     // Update totals.
7145     m_FreeCount = m_FreeCount - 1;
7146     if (paddingBegin > 0)
7147     {
7148         ++m_FreeCount;
7149     }
7150     if (paddingEnd > 0)
7151     {
7152         ++m_FreeCount;
7153     }
7154     m_SumFreeSize -= request.size;
7155 }
7156 
7157 void VmaBlockMetadata_Generic::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo)
7158 {
7159     outInfo.offset = (VkDeviceSize)allocHandle - 1;
7160     const VmaSuballocation& suballoc = *FindAtOffset(outInfo.offset);
7161     outInfo.size = suballoc.size;
7162     outInfo.pUserData = suballoc.userData;
7163 }
7164 
7165 void* VmaBlockMetadata_Generic::GetAllocationUserData(VmaAllocHandle allocHandle) const
7166 {
7167     return FindAtOffset((VkDeviceSize)allocHandle - 1)->userData;
7168 }
7169 
7170 VmaAllocHandle VmaBlockMetadata_Generic::GetAllocationListBegin() const
7171 {
7172     if (IsEmpty())
7173         return VK_NULL_HANDLE;
7174 
7175     for (const auto& suballoc : m_Suballocations)
7176     {
7177         if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
7178             return (VmaAllocHandle)(suballoc.offset + 1);
7179     }
7180     VMA_ASSERT(false && "Should contain at least 1 allocation!");
7181     return VK_NULL_HANDLE;
7182 }
7183 
7184 VmaAllocHandle VmaBlockMetadata_Generic::GetNextAllocation(VmaAllocHandle prevAlloc) const
7185 {
7186     VmaSuballocationList::const_iterator prev = FindAtOffset((VkDeviceSize)prevAlloc - 1);
7187 
7188     for (VmaSuballocationList::const_iterator it = ++prev; it != m_Suballocations.end(); ++it)
7189     {
7190         if (it->type != VMA_SUBALLOCATION_TYPE_FREE)
7191             return (VmaAllocHandle)(it->offset + 1);
7192     }
7193     return VK_NULL_HANDLE;
7194 }
7195 
7196 void VmaBlockMetadata_Generic::Clear()
7197 {
7198     const VkDeviceSize size = GetSize();
7199 
7200     VMA_ASSERT(IsVirtual());
7201     m_FreeCount = 1;
7202     m_SumFreeSize = size;
7203     m_Suballocations.clear();
7204     m_FreeSuballocationsBySize.clear();
7205 
7206     VmaSuballocation suballoc = {};
7207     suballoc.offset = 0;
7208     suballoc.size = size;
7209     suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
7210     m_Suballocations.push_back(suballoc);
7211 
7212     m_FreeSuballocationsBySize.push_back(m_Suballocations.begin());
7213 }
7214 
7215 void VmaBlockMetadata_Generic::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData)
7216 {
7217     VmaSuballocation& suballoc = *FindAtOffset((VkDeviceSize)allocHandle - 1);
7218     suballoc.userData = userData;
7219 }
7220 
7221 void VmaBlockMetadata_Generic::DebugLogAllAllocations() const
7222 {
7223     for (const auto& suballoc : m_Suballocations)
7224     {
7225         if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
7226             DebugLogAllocation(suballoc.offset, suballoc.size, suballoc.userData);
7227     }
7228 }
7229 
7230 VmaSuballocationList::iterator VmaBlockMetadata_Generic::FindAtOffset(VkDeviceSize offset) const
7231 {
7232     VMA_HEAVY_ASSERT(!m_Suballocations.empty());
7233     const VkDeviceSize last = m_Suballocations.rbegin()->offset;
7234     if (last == offset)
7235         return m_Suballocations.rbegin().drop_const();
7236     const VkDeviceSize first = m_Suballocations.begin()->offset;
7237     if (first == offset)
7238         return m_Suballocations.begin().drop_const();
7239 
7240     const size_t suballocCount = m_Suballocations.size();
7241     const VkDeviceSize step = (last - first + m_Suballocations.begin()->size) / suballocCount;
7242     auto findSuballocation = [&](auto begin, auto end) -> VmaSuballocationList::iterator
7243     {
7244         for (auto suballocItem = begin;
7245             suballocItem != end;
7246             ++suballocItem)
7247         {
7248             if (suballocItem->offset == offset)
7249                 return suballocItem.drop_const();
7250         }
7251         VMA_ASSERT(false && "Not found!");
7252         return m_Suballocations.end().drop_const();
7253     };
7254     // If requested offset is closer to the end of range, search from the end
7255     if (offset - first > suballocCount * step / 2)
7256     {
7257         return findSuballocation(m_Suballocations.rbegin(), m_Suballocations.rend());
7258     }
7259     return findSuballocation(m_Suballocations.begin(), m_Suballocations.end());
7260 }
7261 
7262 bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
7263 {
7264     VkDeviceSize lastSize = 0;
7265     for (size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
7266     {
7267         const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
7268 
7269         VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
7270         VMA_VALIDATE(it->size >= lastSize);
7271         lastSize = it->size;
7272     }
7273     return true;
7274 }
7275 
7276 bool VmaBlockMetadata_Generic::CheckAllocation(
7277     VkDeviceSize allocSize,
7278     VkDeviceSize allocAlignment,
7279     VmaSuballocationType allocType,
7280     VmaSuballocationList::const_iterator suballocItem,
7281     VmaAllocHandle* pAllocHandle) const
7282 {
7283     VMA_ASSERT(allocSize > 0);
7284     VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
7285     VMA_ASSERT(suballocItem != m_Suballocations.cend());
7286     VMA_ASSERT(pAllocHandle != VMA_NULL);
7287 
7288     const VkDeviceSize debugMargin = GetDebugMargin();
7289     const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity();
7290 
7291     const VmaSuballocation& suballoc = *suballocItem;
7292     VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
7293 
7294     // Size of this suballocation is too small for this request: Early return.
7295     if (suballoc.size < allocSize)
7296     {
7297         return false;
7298     }
7299 
7300     // Start from offset equal to beginning of this suballocation.
7301     VkDeviceSize offset = suballoc.offset + (suballocItem == m_Suballocations.cbegin() ? 0 : GetDebugMargin());
7302 
7303     // Apply debugMargin from the end of previous alloc.
7304     if (debugMargin > 0)
7305     {
7306         offset += debugMargin;
7307     }
7308 
7309     // Apply alignment.
7310     offset = VmaAlignUp(offset, allocAlignment);
7311 
7312     // Check previous suballocations for BufferImageGranularity conflicts.
7313     // Make bigger alignment if necessary.
7314     if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment)
7315     {
7316         bool bufferImageGranularityConflict = false;
7317         VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
7318         while (prevSuballocItem != m_Suballocations.cbegin())
7319         {
7320             --prevSuballocItem;
7321             const VmaSuballocation& prevSuballoc = *prevSuballocItem;
7322             if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, offset, bufferImageGranularity))
7323             {
7324                 if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
7325                 {
7326                     bufferImageGranularityConflict = true;
7327                     break;
7328                 }
7329             }
7330             else
7331                 // Already on previous page.
7332                 break;
7333         }
7334         if (bufferImageGranularityConflict)
7335         {
7336             offset = VmaAlignUp(offset, bufferImageGranularity);
7337         }
7338     }
7339 
7340     // Calculate padding at the beginning based on current offset.
7341     const VkDeviceSize paddingBegin = offset - suballoc.offset;
7342 
7343     // Fail if requested size plus margin after is bigger than size of this suballocation.
7344     if (paddingBegin + allocSize + debugMargin > suballoc.size)
7345     {
7346         return false;
7347     }
7348 
7349     // Check next suballocations for BufferImageGranularity conflicts.
7350     // If conflict exists, allocation cannot be made here.
7351     if (allocSize % bufferImageGranularity || offset % bufferImageGranularity)
7352     {
7353         VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
7354         ++nextSuballocItem;
7355         while (nextSuballocItem != m_Suballocations.cend())
7356         {
7357             const VmaSuballocation& nextSuballoc = *nextSuballocItem;
7358             if (VmaBlocksOnSamePage(offset, allocSize, nextSuballoc.offset, bufferImageGranularity))
7359             {
7360                 if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
7361                 {
7362                     return false;
7363                 }
7364             }
7365             else
7366             {
7367                 // Already on next page.
7368                 break;
7369             }
7370             ++nextSuballocItem;
7371         }
7372     }
7373 
7374     *pAllocHandle = (VmaAllocHandle)(offset + 1);
7375     // All tests passed: Success. pAllocHandle is already filled.
7376     return true;
7377 }
7378 
7379 void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
7380 {
7381     VMA_ASSERT(item != m_Suballocations.end());
7382     VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
7383 
7384     VmaSuballocationList::iterator nextItem = item;
7385     ++nextItem;
7386     VMA_ASSERT(nextItem != m_Suballocations.end());
7387     VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
7388 
7389     item->size += nextItem->size;
7390     --m_FreeCount;
7391     m_Suballocations.erase(nextItem);
7392 }
7393 
7394 VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
7395 {
7396     // Change this suballocation to be marked as free.
7397     VmaSuballocation& suballoc = *suballocItem;
7398     suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
7399     suballoc.userData = VMA_NULL;
7400 
7401     // Update totals.
7402     ++m_FreeCount;
7403     m_SumFreeSize += suballoc.size;
7404 
7405     // Merge with previous and/or next suballocation if it's also free.
7406     bool mergeWithNext = false;
7407     bool mergeWithPrev = false;
7408 
7409     VmaSuballocationList::iterator nextItem = suballocItem;
7410     ++nextItem;
7411     if ((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
7412     {
7413         mergeWithNext = true;
7414     }
7415 
7416     VmaSuballocationList::iterator prevItem = suballocItem;
7417     if (suballocItem != m_Suballocations.begin())
7418     {
7419         --prevItem;
7420         if (prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
7421         {
7422             mergeWithPrev = true;
7423         }
7424     }
7425 
7426     if (mergeWithNext)
7427     {
7428         UnregisterFreeSuballocation(nextItem);
7429         MergeFreeWithNext(suballocItem);
7430     }
7431 
7432     if (mergeWithPrev)
7433     {
7434         UnregisterFreeSuballocation(prevItem);
7435         MergeFreeWithNext(prevItem);
7436         RegisterFreeSuballocation(prevItem);
7437         return prevItem;
7438     }
7439     else
7440     {
7441         RegisterFreeSuballocation(suballocItem);
7442         return suballocItem;
7443     }
7444 }
7445 
7446 void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
7447 {
7448     VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
7449     VMA_ASSERT(item->size > 0);
7450 
7451     // You may want to enable this validation at the beginning or at the end of
7452     // this function, depending on what do you want to check.
7453     VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
7454 
7455     if (m_FreeSuballocationsBySize.empty())
7456     {
7457         m_FreeSuballocationsBySize.push_back(item);
7458     }
7459     else
7460     {
7461         VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
7462     }
7463 
7464     //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
7465 }
7466 
7467 void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
7468 {
7469     VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
7470     VMA_ASSERT(item->size > 0);
7471 
7472     // You may want to enable this validation at the beginning or at the end of
7473     // this function, depending on what do you want to check.
7474     VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
7475 
7476     VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
7477         m_FreeSuballocationsBySize.data(),
7478         m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
7479         item,
7480         VmaSuballocationItemSizeLess());
7481     for (size_t index = it - m_FreeSuballocationsBySize.data();
7482         index < m_FreeSuballocationsBySize.size();
7483         ++index)
7484     {
7485         if (m_FreeSuballocationsBySize[index] == item)
7486         {
7487             VmaVectorRemove(m_FreeSuballocationsBySize, index);
7488             return;
7489         }
7490         VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
7491     }
7492     VMA_ASSERT(0 && "Not found.");
7493 
7494     //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
7495 }
7496 #endif // _VMA_BLOCK_METADATA_GENERIC_FUNCTIONS
7497 #endif // _VMA_BLOCK_METADATA_GENERIC
7498 #endif // #if 0
7499 
7500 #ifndef _VMA_BLOCK_METADATA_LINEAR
7501 /*
7502 Allocations and their references in internal data structure look like this:
7503 
7504 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
7505 
7506         0 +-------+
7507           |       |
7508           |       |
7509           |       |
7510           +-------+
7511           | Alloc |  1st[m_1stNullItemsBeginCount]
7512           +-------+
7513           | Alloc |  1st[m_1stNullItemsBeginCount + 1]
7514           +-------+
7515           |  ...  |
7516           +-------+
7517           | Alloc |  1st[1st.size() - 1]
7518           +-------+
7519           |       |
7520           |       |
7521           |       |
7522 GetSize() +-------+
7523 
7524 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
7525 
7526         0 +-------+
7527           | Alloc |  2nd[0]
7528           +-------+
7529           | Alloc |  2nd[1]
7530           +-------+
7531           |  ...  |
7532           +-------+
7533           | Alloc |  2nd[2nd.size() - 1]
7534           +-------+
7535           |       |
7536           |       |
7537           |       |
7538           +-------+
7539           | Alloc |  1st[m_1stNullItemsBeginCount]
7540           +-------+
7541           | Alloc |  1st[m_1stNullItemsBeginCount + 1]
7542           +-------+
7543           |  ...  |
7544           +-------+
7545           | Alloc |  1st[1st.size() - 1]
7546           +-------+
7547           |       |
7548 GetSize() +-------+
7549 
7550 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
7551 
7552         0 +-------+
7553           |       |
7554           |       |
7555           |       |
7556           +-------+
7557           | Alloc |  1st[m_1stNullItemsBeginCount]
7558           +-------+
7559           | Alloc |  1st[m_1stNullItemsBeginCount + 1]
7560           +-------+
7561           |  ...  |
7562           +-------+
7563           | Alloc |  1st[1st.size() - 1]
7564           +-------+
7565           |       |
7566           |       |
7567           |       |
7568           +-------+
7569           | Alloc |  2nd[2nd.size() - 1]
7570           +-------+
7571           |  ...  |
7572           +-------+
7573           | Alloc |  2nd[1]
7574           +-------+
7575           | Alloc |  2nd[0]
7576 GetSize() +-------+
7577 
7578 */
7579 class VmaBlockMetadata_Linear : public VmaBlockMetadata
7580 {
7581     VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
7582 public:
7583     VmaBlockMetadata_Linear(const VkAllocationCallbacks* pAllocationCallbacks,
7584         VkDeviceSize bufferImageGranularity, bool isVirtual);
7585     virtual ~VmaBlockMetadata_Linear() = default;
7586 
GetSumFreeSize()7587     VkDeviceSize GetSumFreeSize() const override { return m_SumFreeSize; }
IsEmpty()7588     bool IsEmpty() const override { return GetAllocationCount() == 0; }
GetAllocationOffset(VmaAllocHandle allocHandle)7589     VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle - 1; };
7590 
7591     void Init(VkDeviceSize size) override;
7592     bool Validate() const override;
7593     size_t GetAllocationCount() const override;
7594     size_t GetFreeRegionsCount() const override;
7595 
7596     void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override;
7597     void AddStatistics(VmaStatistics& inoutStats) const override;
7598 
7599 #if VMA_STATS_STRING_ENABLED
7600     void PrintDetailedMap(class VmaJsonWriter& json) const override;
7601 #endif
7602 
7603     bool CreateAllocationRequest(
7604         VkDeviceSize allocSize,
7605         VkDeviceSize allocAlignment,
7606         bool upperAddress,
7607         VmaSuballocationType allocType,
7608         uint32_t strategy,
7609         VmaAllocationRequest* pAllocationRequest) override;
7610 
7611     VkResult CheckCorruption(const void* pBlockData) override;
7612 
7613     void Alloc(
7614         const VmaAllocationRequest& request,
7615         VmaSuballocationType type,
7616         void* userData) override;
7617 
7618     void Free(VmaAllocHandle allocHandle) override;
7619     void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override;
7620     void* GetAllocationUserData(VmaAllocHandle allocHandle) const override;
7621     VmaAllocHandle GetAllocationListBegin() const override;
7622     VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override;
7623     VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const override;
7624     void Clear() override;
7625     void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override;
7626     void DebugLogAllAllocations() const override;
7627 
7628 private:
7629     /*
7630     There are two suballocation vectors, used in ping-pong way.
7631     The one with index m_1stVectorIndex is called 1st.
7632     The one with index (m_1stVectorIndex ^ 1) is called 2nd.
7633     2nd can be non-empty only when 1st is not empty.
7634     When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
7635     */
7636     typedef VmaVector<VmaSuballocation, VmaStlAllocator<VmaSuballocation>> SuballocationVectorType;
7637 
7638     enum SECOND_VECTOR_MODE
7639     {
7640         SECOND_VECTOR_EMPTY,
7641         /*
7642         Suballocations in 2nd vector are created later than the ones in 1st, but they
7643         all have smaller offset.
7644         */
7645         SECOND_VECTOR_RING_BUFFER,
7646         /*
7647         Suballocations in 2nd vector are upper side of double stack.
7648         They all have offsets higher than those in 1st vector.
7649         Top of this stack means smaller offsets, but higher indices in this vector.
7650         */
7651         SECOND_VECTOR_DOUBLE_STACK,
7652     };
7653 
7654     VkDeviceSize m_SumFreeSize;
7655     SuballocationVectorType m_Suballocations0, m_Suballocations1;
7656     uint32_t m_1stVectorIndex;
7657     SECOND_VECTOR_MODE m_2ndVectorMode;
7658     // Number of items in 1st vector with hAllocation = null at the beginning.
7659     size_t m_1stNullItemsBeginCount;
7660     // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
7661     size_t m_1stNullItemsMiddleCount;
7662     // Number of items in 2nd vector with hAllocation = null.
7663     size_t m_2ndNullItemsCount;
7664 
AccessSuballocations1st()7665     SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
AccessSuballocations2nd()7666     SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
AccessSuballocations1st()7667     const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
AccessSuballocations2nd()7668     const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
7669 
7670     VmaSuballocation& FindSuballocation(VkDeviceSize offset) const;
7671     bool ShouldCompact1st() const;
7672     void CleanupAfterFree();
7673 
7674     bool CreateAllocationRequest_LowerAddress(
7675         VkDeviceSize allocSize,
7676         VkDeviceSize allocAlignment,
7677         VmaSuballocationType allocType,
7678         uint32_t strategy,
7679         VmaAllocationRequest* pAllocationRequest);
7680     bool CreateAllocationRequest_UpperAddress(
7681         VkDeviceSize allocSize,
7682         VkDeviceSize allocAlignment,
7683         VmaSuballocationType allocType,
7684         uint32_t strategy,
7685         VmaAllocationRequest* pAllocationRequest);
7686 };
7687 
7688 #ifndef _VMA_BLOCK_METADATA_LINEAR_FUNCTIONS
VmaBlockMetadata_Linear(const VkAllocationCallbacks * pAllocationCallbacks,VkDeviceSize bufferImageGranularity,bool isVirtual)7689 VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(const VkAllocationCallbacks* pAllocationCallbacks,
7690     VkDeviceSize bufferImageGranularity, bool isVirtual)
7691     : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual),
7692     m_SumFreeSize(0),
7693     m_Suballocations0(VmaStlAllocator<VmaSuballocation>(pAllocationCallbacks)),
7694     m_Suballocations1(VmaStlAllocator<VmaSuballocation>(pAllocationCallbacks)),
7695     m_1stVectorIndex(0),
7696     m_2ndVectorMode(SECOND_VECTOR_EMPTY),
7697     m_1stNullItemsBeginCount(0),
7698     m_1stNullItemsMiddleCount(0),
7699     m_2ndNullItemsCount(0) {}
7700 
Init(VkDeviceSize size)7701 void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
7702 {
7703     VmaBlockMetadata::Init(size);
7704     m_SumFreeSize = size;
7705 }
7706 
Validate()7707 bool VmaBlockMetadata_Linear::Validate() const
7708 {
7709     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
7710     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
7711 
7712     VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
7713     VMA_VALIDATE(!suballocations1st.empty() ||
7714         suballocations2nd.empty() ||
7715         m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
7716 
7717     if (!suballocations1st.empty())
7718     {
7719         // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
7720         VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].type != VMA_SUBALLOCATION_TYPE_FREE);
7721         // Null item at the end should be just pop_back().
7722         VMA_VALIDATE(suballocations1st.back().type != VMA_SUBALLOCATION_TYPE_FREE);
7723     }
7724     if (!suballocations2nd.empty())
7725     {
7726         // Null item at the end should be just pop_back().
7727         VMA_VALIDATE(suballocations2nd.back().type != VMA_SUBALLOCATION_TYPE_FREE);
7728     }
7729 
7730     VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
7731     VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
7732 
7733     VkDeviceSize sumUsedSize = 0;
7734     const size_t suballoc1stCount = suballocations1st.size();
7735     const VkDeviceSize debugMargin = GetDebugMargin();
7736     VkDeviceSize offset = 0;
7737 
7738     if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
7739     {
7740         const size_t suballoc2ndCount = suballocations2nd.size();
7741         size_t nullItem2ndCount = 0;
7742         for (size_t i = 0; i < suballoc2ndCount; ++i)
7743         {
7744             const VmaSuballocation& suballoc = suballocations2nd[i];
7745             const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
7746 
7747             VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
7748             if (!IsVirtual())
7749             {
7750                 VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));
7751             }
7752             VMA_VALIDATE(suballoc.offset >= offset);
7753 
7754             if (!currFree)
7755             {
7756                 if (!IsVirtual())
7757                 {
7758                     VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1);
7759                     VMA_VALIDATE(alloc->GetSize() == suballoc.size);
7760                 }
7761                 sumUsedSize += suballoc.size;
7762             }
7763             else
7764             {
7765                 ++nullItem2ndCount;
7766             }
7767 
7768             offset = suballoc.offset + suballoc.size + debugMargin;
7769         }
7770 
7771         VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
7772     }
7773 
7774     for (size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
7775     {
7776         const VmaSuballocation& suballoc = suballocations1st[i];
7777         VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
7778             suballoc.userData == VMA_NULL);
7779     }
7780 
7781     size_t nullItem1stCount = m_1stNullItemsBeginCount;
7782 
7783     for (size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
7784     {
7785         const VmaSuballocation& suballoc = suballocations1st[i];
7786         const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
7787 
7788         VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
7789         if (!IsVirtual())
7790         {
7791             VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));
7792         }
7793         VMA_VALIDATE(suballoc.offset >= offset);
7794         VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
7795 
7796         if (!currFree)
7797         {
7798             if (!IsVirtual())
7799             {
7800                 VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1);
7801                 VMA_VALIDATE(alloc->GetSize() == suballoc.size);
7802             }
7803             sumUsedSize += suballoc.size;
7804         }
7805         else
7806         {
7807             ++nullItem1stCount;
7808         }
7809 
7810         offset = suballoc.offset + suballoc.size + debugMargin;
7811     }
7812     VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
7813 
7814     if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
7815     {
7816         const size_t suballoc2ndCount = suballocations2nd.size();
7817         size_t nullItem2ndCount = 0;
7818         for (size_t i = suballoc2ndCount; i--; )
7819         {
7820             const VmaSuballocation& suballoc = suballocations2nd[i];
7821             const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
7822 
7823             VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
7824             if (!IsVirtual())
7825             {
7826                 VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));
7827             }
7828             VMA_VALIDATE(suballoc.offset >= offset);
7829 
7830             if (!currFree)
7831             {
7832                 if (!IsVirtual())
7833                 {
7834                     VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1);
7835                     VMA_VALIDATE(alloc->GetSize() == suballoc.size);
7836                 }
7837                 sumUsedSize += suballoc.size;
7838             }
7839             else
7840             {
7841                 ++nullItem2ndCount;
7842             }
7843 
7844             offset = suballoc.offset + suballoc.size + debugMargin;
7845         }
7846 
7847         VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
7848     }
7849 
7850     VMA_VALIDATE(offset <= GetSize());
7851     VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
7852 
7853     return true;
7854 }
7855 
GetAllocationCount()7856 size_t VmaBlockMetadata_Linear::GetAllocationCount() const
7857 {
7858     return AccessSuballocations1st().size() - m_1stNullItemsBeginCount - m_1stNullItemsMiddleCount +
7859         AccessSuballocations2nd().size() - m_2ndNullItemsCount;
7860 }
7861 
GetFreeRegionsCount()7862 size_t VmaBlockMetadata_Linear::GetFreeRegionsCount() const
7863 {
7864     // Function only used for defragmentation, which is disabled for this algorithm
7865     VMA_ASSERT(0);
7866     return SIZE_MAX;
7867 }
7868 
AddDetailedStatistics(VmaDetailedStatistics & inoutStats)7869 void VmaBlockMetadata_Linear::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const
7870 {
7871     const VkDeviceSize size = GetSize();
7872     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
7873     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
7874     const size_t suballoc1stCount = suballocations1st.size();
7875     const size_t suballoc2ndCount = suballocations2nd.size();
7876 
7877     inoutStats.statistics.blockCount++;
7878     inoutStats.statistics.blockBytes += size;
7879 
7880     VkDeviceSize lastOffset = 0;
7881 
7882     if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
7883     {
7884         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
7885         size_t nextAlloc2ndIndex = 0;
7886         while (lastOffset < freeSpace2ndTo1stEnd)
7887         {
7888             // Find next non-null allocation or move nextAllocIndex to the end.
7889             while (nextAlloc2ndIndex < suballoc2ndCount &&
7890                 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
7891             {
7892                 ++nextAlloc2ndIndex;
7893             }
7894 
7895             // Found non-null allocation.
7896             if (nextAlloc2ndIndex < suballoc2ndCount)
7897             {
7898                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
7899 
7900                 // 1. Process free space before this allocation.
7901                 if (lastOffset < suballoc.offset)
7902                 {
7903                     // There is free space from lastOffset to suballoc.offset.
7904                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
7905                     VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
7906                 }
7907 
7908                 // 2. Process this allocation.
7909                 // There is allocation with suballoc.offset, suballoc.size.
7910                 VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size);
7911 
7912                 // 3. Prepare for next iteration.
7913                 lastOffset = suballoc.offset + suballoc.size;
7914                 ++nextAlloc2ndIndex;
7915             }
7916             // We are at the end.
7917             else
7918             {
7919                 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
7920                 if (lastOffset < freeSpace2ndTo1stEnd)
7921                 {
7922                     const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
7923                     VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
7924                 }
7925 
7926                 // End of loop.
7927                 lastOffset = freeSpace2ndTo1stEnd;
7928             }
7929         }
7930     }
7931 
7932     size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
7933     const VkDeviceSize freeSpace1stTo2ndEnd =
7934         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
7935     while (lastOffset < freeSpace1stTo2ndEnd)
7936     {
7937         // Find next non-null allocation or move nextAllocIndex to the end.
7938         while (nextAlloc1stIndex < suballoc1stCount &&
7939             suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)
7940         {
7941             ++nextAlloc1stIndex;
7942         }
7943 
7944         // Found non-null allocation.
7945         if (nextAlloc1stIndex < suballoc1stCount)
7946         {
7947             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
7948 
7949             // 1. Process free space before this allocation.
7950             if (lastOffset < suballoc.offset)
7951             {
7952                 // There is free space from lastOffset to suballoc.offset.
7953                 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
7954                 VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
7955             }
7956 
7957             // 2. Process this allocation.
7958             // There is allocation with suballoc.offset, suballoc.size.
7959             VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size);
7960 
7961             // 3. Prepare for next iteration.
7962             lastOffset = suballoc.offset + suballoc.size;
7963             ++nextAlloc1stIndex;
7964         }
7965         // We are at the end.
7966         else
7967         {
7968             // There is free space from lastOffset to freeSpace1stTo2ndEnd.
7969             if (lastOffset < freeSpace1stTo2ndEnd)
7970             {
7971                 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
7972                 VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
7973             }
7974 
7975             // End of loop.
7976             lastOffset = freeSpace1stTo2ndEnd;
7977         }
7978     }
7979 
7980     if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
7981     {
7982         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
7983         while (lastOffset < size)
7984         {
7985             // Find next non-null allocation or move nextAllocIndex to the end.
7986             while (nextAlloc2ndIndex != SIZE_MAX &&
7987                 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
7988             {
7989                 --nextAlloc2ndIndex;
7990             }
7991 
7992             // Found non-null allocation.
7993             if (nextAlloc2ndIndex != SIZE_MAX)
7994             {
7995                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
7996 
7997                 // 1. Process free space before this allocation.
7998                 if (lastOffset < suballoc.offset)
7999                 {
8000                     // There is free space from lastOffset to suballoc.offset.
8001                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
8002                     VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
8003                 }
8004 
8005                 // 2. Process this allocation.
8006                 // There is allocation with suballoc.offset, suballoc.size.
8007                 VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size);
8008 
8009                 // 3. Prepare for next iteration.
8010                 lastOffset = suballoc.offset + suballoc.size;
8011                 --nextAlloc2ndIndex;
8012             }
8013             // We are at the end.
8014             else
8015             {
8016                 // There is free space from lastOffset to size.
8017                 if (lastOffset < size)
8018                 {
8019                     const VkDeviceSize unusedRangeSize = size - lastOffset;
8020                     VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
8021                 }
8022 
8023                 // End of loop.
8024                 lastOffset = size;
8025             }
8026         }
8027     }
8028 }
8029 
AddStatistics(VmaStatistics & inoutStats)8030 void VmaBlockMetadata_Linear::AddStatistics(VmaStatistics& inoutStats) const
8031 {
8032     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8033     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8034     const VkDeviceSize size = GetSize();
8035     const size_t suballoc1stCount = suballocations1st.size();
8036     const size_t suballoc2ndCount = suballocations2nd.size();
8037 
8038     inoutStats.blockCount++;
8039     inoutStats.blockBytes += size;
8040     inoutStats.allocationBytes += size - m_SumFreeSize;
8041 
8042     VkDeviceSize lastOffset = 0;
8043 
8044     if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8045     {
8046         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
8047         size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
8048         while (lastOffset < freeSpace2ndTo1stEnd)
8049         {
8050             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
8051             while (nextAlloc2ndIndex < suballoc2ndCount &&
8052                 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
8053             {
8054                 ++nextAlloc2ndIndex;
8055             }
8056 
8057             // Found non-null allocation.
8058             if (nextAlloc2ndIndex < suballoc2ndCount)
8059             {
8060                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
8061 
8062                 // 1. Process free space before this allocation.
8063                 if (lastOffset < suballoc.offset)
8064                 {
8065                     // There is free space from lastOffset to suballoc.offset.
8066                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
8067                 }
8068 
8069                 // 2. Process this allocation.
8070                 // There is allocation with suballoc.offset, suballoc.size.
8071                 ++inoutStats.allocationCount;
8072 
8073                 // 3. Prepare for next iteration.
8074                 lastOffset = suballoc.offset + suballoc.size;
8075                 ++nextAlloc2ndIndex;
8076             }
8077             // We are at the end.
8078             else
8079             {
8080                 if (lastOffset < freeSpace2ndTo1stEnd)
8081                 {
8082                     // There is free space from lastOffset to freeSpace2ndTo1stEnd.
8083                     const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
8084                 }
8085 
8086                 // End of loop.
8087                 lastOffset = freeSpace2ndTo1stEnd;
8088             }
8089         }
8090     }
8091 
8092     size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
8093     const VkDeviceSize freeSpace1stTo2ndEnd =
8094         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
8095     while (lastOffset < freeSpace1stTo2ndEnd)
8096     {
8097         // Find next non-null allocation or move nextAllocIndex to the end.
8098         while (nextAlloc1stIndex < suballoc1stCount &&
8099             suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)
8100         {
8101             ++nextAlloc1stIndex;
8102         }
8103 
8104         // Found non-null allocation.
8105         if (nextAlloc1stIndex < suballoc1stCount)
8106         {
8107             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
8108 
8109             // 1. Process free space before this allocation.
8110             if (lastOffset < suballoc.offset)
8111             {
8112                 // There is free space from lastOffset to suballoc.offset.
8113                 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
8114             }
8115 
8116             // 2. Process this allocation.
8117             // There is allocation with suballoc.offset, suballoc.size.
8118             ++inoutStats.allocationCount;
8119 
8120             // 3. Prepare for next iteration.
8121             lastOffset = suballoc.offset + suballoc.size;
8122             ++nextAlloc1stIndex;
8123         }
8124         // We are at the end.
8125         else
8126         {
8127             if (lastOffset < freeSpace1stTo2ndEnd)
8128             {
8129                 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
8130                 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
8131             }
8132 
8133             // End of loop.
8134             lastOffset = freeSpace1stTo2ndEnd;
8135         }
8136     }
8137 
8138     if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
8139     {
8140         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
8141         while (lastOffset < size)
8142         {
8143             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
8144             while (nextAlloc2ndIndex != SIZE_MAX &&
8145                 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
8146             {
8147                 --nextAlloc2ndIndex;
8148             }
8149 
8150             // Found non-null allocation.
8151             if (nextAlloc2ndIndex != SIZE_MAX)
8152             {
8153                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
8154 
8155                 // 1. Process free space before this allocation.
8156                 if (lastOffset < suballoc.offset)
8157                 {
8158                     // There is free space from lastOffset to suballoc.offset.
8159                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
8160                 }
8161 
8162                 // 2. Process this allocation.
8163                 // There is allocation with suballoc.offset, suballoc.size.
8164                 ++inoutStats.allocationCount;
8165 
8166                 // 3. Prepare for next iteration.
8167                 lastOffset = suballoc.offset + suballoc.size;
8168                 --nextAlloc2ndIndex;
8169             }
8170             // We are at the end.
8171             else
8172             {
8173                 if (lastOffset < size)
8174                 {
8175                     // There is free space from lastOffset to size.
8176                     const VkDeviceSize unusedRangeSize = size - lastOffset;
8177                 }
8178 
8179                 // End of loop.
8180                 lastOffset = size;
8181             }
8182         }
8183     }
8184 }
8185 
8186 #if VMA_STATS_STRING_ENABLED
PrintDetailedMap(class VmaJsonWriter & json)8187 void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
8188 {
8189     const VkDeviceSize size = GetSize();
8190     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8191     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8192     const size_t suballoc1stCount = suballocations1st.size();
8193     const size_t suballoc2ndCount = suballocations2nd.size();
8194 
8195     // FIRST PASS
8196 
8197     size_t unusedRangeCount = 0;
8198     VkDeviceSize usedBytes = 0;
8199 
8200     VkDeviceSize lastOffset = 0;
8201 
8202     size_t alloc2ndCount = 0;
8203     if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8204     {
8205         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
8206         size_t nextAlloc2ndIndex = 0;
8207         while (lastOffset < freeSpace2ndTo1stEnd)
8208         {
8209             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
8210             while (nextAlloc2ndIndex < suballoc2ndCount &&
8211                 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
8212             {
8213                 ++nextAlloc2ndIndex;
8214             }
8215 
8216             // Found non-null allocation.
8217             if (nextAlloc2ndIndex < suballoc2ndCount)
8218             {
8219                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
8220 
8221                 // 1. Process free space before this allocation.
8222                 if (lastOffset < suballoc.offset)
8223                 {
8224                     // There is free space from lastOffset to suballoc.offset.
8225                     ++unusedRangeCount;
8226                 }
8227 
8228                 // 2. Process this allocation.
8229                 // There is allocation with suballoc.offset, suballoc.size.
8230                 ++alloc2ndCount;
8231                 usedBytes += suballoc.size;
8232 
8233                 // 3. Prepare for next iteration.
8234                 lastOffset = suballoc.offset + suballoc.size;
8235                 ++nextAlloc2ndIndex;
8236             }
8237             // We are at the end.
8238             else
8239             {
8240                 if (lastOffset < freeSpace2ndTo1stEnd)
8241                 {
8242                     // There is free space from lastOffset to freeSpace2ndTo1stEnd.
8243                     ++unusedRangeCount;
8244                 }
8245 
8246                 // End of loop.
8247                 lastOffset = freeSpace2ndTo1stEnd;
8248             }
8249         }
8250     }
8251 
8252     size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
8253     size_t alloc1stCount = 0;
8254     const VkDeviceSize freeSpace1stTo2ndEnd =
8255         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
8256     while (lastOffset < freeSpace1stTo2ndEnd)
8257     {
8258         // Find next non-null allocation or move nextAllocIndex to the end.
8259         while (nextAlloc1stIndex < suballoc1stCount &&
8260             suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)
8261         {
8262             ++nextAlloc1stIndex;
8263         }
8264 
8265         // Found non-null allocation.
8266         if (nextAlloc1stIndex < suballoc1stCount)
8267         {
8268             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
8269 
8270             // 1. Process free space before this allocation.
8271             if (lastOffset < suballoc.offset)
8272             {
8273                 // There is free space from lastOffset to suballoc.offset.
8274                 ++unusedRangeCount;
8275             }
8276 
8277             // 2. Process this allocation.
8278             // There is allocation with suballoc.offset, suballoc.size.
8279             ++alloc1stCount;
8280             usedBytes += suballoc.size;
8281 
8282             // 3. Prepare for next iteration.
8283             lastOffset = suballoc.offset + suballoc.size;
8284             ++nextAlloc1stIndex;
8285         }
8286         // We are at the end.
8287         else
8288         {
8289             if (lastOffset < size)
8290             {
8291                 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
8292                 ++unusedRangeCount;
8293             }
8294 
8295             // End of loop.
8296             lastOffset = freeSpace1stTo2ndEnd;
8297         }
8298     }
8299 
8300     if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
8301     {
8302         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
8303         while (lastOffset < size)
8304         {
8305             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
8306             while (nextAlloc2ndIndex != SIZE_MAX &&
8307                 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
8308             {
8309                 --nextAlloc2ndIndex;
8310             }
8311 
8312             // Found non-null allocation.
8313             if (nextAlloc2ndIndex != SIZE_MAX)
8314             {
8315                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
8316 
8317                 // 1. Process free space before this allocation.
8318                 if (lastOffset < suballoc.offset)
8319                 {
8320                     // There is free space from lastOffset to suballoc.offset.
8321                     ++unusedRangeCount;
8322                 }
8323 
8324                 // 2. Process this allocation.
8325                 // There is allocation with suballoc.offset, suballoc.size.
8326                 ++alloc2ndCount;
8327                 usedBytes += suballoc.size;
8328 
8329                 // 3. Prepare for next iteration.
8330                 lastOffset = suballoc.offset + suballoc.size;
8331                 --nextAlloc2ndIndex;
8332             }
8333             // We are at the end.
8334             else
8335             {
8336                 if (lastOffset < size)
8337                 {
8338                     // There is free space from lastOffset to size.
8339                     ++unusedRangeCount;
8340                 }
8341 
8342                 // End of loop.
8343                 lastOffset = size;
8344             }
8345         }
8346     }
8347 
8348     const VkDeviceSize unusedBytes = size - usedBytes;
8349     PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
8350 
8351     // SECOND PASS
8352     lastOffset = 0;
8353 
8354     if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8355     {
8356         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
8357         size_t nextAlloc2ndIndex = 0;
8358         while (lastOffset < freeSpace2ndTo1stEnd)
8359         {
8360             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
8361             while (nextAlloc2ndIndex < suballoc2ndCount &&
8362                 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
8363             {
8364                 ++nextAlloc2ndIndex;
8365             }
8366 
8367             // Found non-null allocation.
8368             if (nextAlloc2ndIndex < suballoc2ndCount)
8369             {
8370                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
8371 
8372                 // 1. Process free space before this allocation.
8373                 if (lastOffset < suballoc.offset)
8374                 {
8375                     // There is free space from lastOffset to suballoc.offset.
8376                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
8377                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
8378                 }
8379 
8380                 // 2. Process this allocation.
8381                 // There is allocation with suballoc.offset, suballoc.size.
8382                 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);
8383 
8384                 // 3. Prepare for next iteration.
8385                 lastOffset = suballoc.offset + suballoc.size;
8386                 ++nextAlloc2ndIndex;
8387             }
8388             // We are at the end.
8389             else
8390             {
8391                 if (lastOffset < freeSpace2ndTo1stEnd)
8392                 {
8393                     // There is free space from lastOffset to freeSpace2ndTo1stEnd.
8394                     const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
8395                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
8396                 }
8397 
8398                 // End of loop.
8399                 lastOffset = freeSpace2ndTo1stEnd;
8400             }
8401         }
8402     }
8403 
8404     nextAlloc1stIndex = m_1stNullItemsBeginCount;
8405     while (lastOffset < freeSpace1stTo2ndEnd)
8406     {
8407         // Find next non-null allocation or move nextAllocIndex to the end.
8408         while (nextAlloc1stIndex < suballoc1stCount &&
8409             suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)
8410         {
8411             ++nextAlloc1stIndex;
8412         }
8413 
8414         // Found non-null allocation.
8415         if (nextAlloc1stIndex < suballoc1stCount)
8416         {
8417             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
8418 
8419             // 1. Process free space before this allocation.
8420             if (lastOffset < suballoc.offset)
8421             {
8422                 // There is free space from lastOffset to suballoc.offset.
8423                 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
8424                 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
8425             }
8426 
8427             // 2. Process this allocation.
8428             // There is allocation with suballoc.offset, suballoc.size.
8429             PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);
8430 
8431             // 3. Prepare for next iteration.
8432             lastOffset = suballoc.offset + suballoc.size;
8433             ++nextAlloc1stIndex;
8434         }
8435         // We are at the end.
8436         else
8437         {
8438             if (lastOffset < freeSpace1stTo2ndEnd)
8439             {
8440                 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
8441                 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
8442                 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
8443             }
8444 
8445             // End of loop.
8446             lastOffset = freeSpace1stTo2ndEnd;
8447         }
8448     }
8449 
8450     if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
8451     {
8452         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
8453         while (lastOffset < size)
8454         {
8455             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
8456             while (nextAlloc2ndIndex != SIZE_MAX &&
8457                 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
8458             {
8459                 --nextAlloc2ndIndex;
8460             }
8461 
8462             // Found non-null allocation.
8463             if (nextAlloc2ndIndex != SIZE_MAX)
8464             {
8465                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
8466 
8467                 // 1. Process free space before this allocation.
8468                 if (lastOffset < suballoc.offset)
8469                 {
8470                     // There is free space from lastOffset to suballoc.offset.
8471                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
8472                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
8473                 }
8474 
8475                 // 2. Process this allocation.
8476                 // There is allocation with suballoc.offset, suballoc.size.
8477                 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);
8478 
8479                 // 3. Prepare for next iteration.
8480                 lastOffset = suballoc.offset + suballoc.size;
8481                 --nextAlloc2ndIndex;
8482             }
8483             // We are at the end.
8484             else
8485             {
8486                 if (lastOffset < size)
8487                 {
8488                     // There is free space from lastOffset to size.
8489                     const VkDeviceSize unusedRangeSize = size - lastOffset;
8490                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
8491                 }
8492 
8493                 // End of loop.
8494                 lastOffset = size;
8495             }
8496         }
8497     }
8498 
8499     PrintDetailedMap_End(json);
8500 }
8501 #endif // VMA_STATS_STRING_ENABLED
8502 
CreateAllocationRequest(VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)8503 bool VmaBlockMetadata_Linear::CreateAllocationRequest(
8504     VkDeviceSize allocSize,
8505     VkDeviceSize allocAlignment,
8506     bool upperAddress,
8507     VmaSuballocationType allocType,
8508     uint32_t strategy,
8509     VmaAllocationRequest* pAllocationRequest)
8510 {
8511     VMA_ASSERT(allocSize > 0);
8512     VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
8513     VMA_ASSERT(pAllocationRequest != VMA_NULL);
8514     VMA_HEAVY_ASSERT(Validate());
8515     pAllocationRequest->size = allocSize;
8516     return upperAddress ?
8517         CreateAllocationRequest_UpperAddress(
8518             allocSize, allocAlignment, allocType, strategy, pAllocationRequest) :
8519         CreateAllocationRequest_LowerAddress(
8520             allocSize, allocAlignment, allocType, strategy, pAllocationRequest);
8521 }
8522 
CheckCorruption(const void * pBlockData)8523 VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
8524 {
8525     VMA_ASSERT(!IsVirtual());
8526     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8527     for (size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
8528     {
8529         const VmaSuballocation& suballoc = suballocations1st[i];
8530         if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
8531         {
8532             if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
8533             {
8534                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
8535                 return VK_ERROR_UNKNOWN_COPY;
8536             }
8537         }
8538     }
8539 
8540     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8541     for (size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
8542     {
8543         const VmaSuballocation& suballoc = suballocations2nd[i];
8544         if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
8545         {
8546             if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
8547             {
8548                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
8549                 return VK_ERROR_UNKNOWN_COPY;
8550             }
8551         }
8552     }
8553 
8554     return VK_SUCCESS;
8555 }
8556 
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,void * userData)8557 void VmaBlockMetadata_Linear::Alloc(
8558     const VmaAllocationRequest& request,
8559     VmaSuballocationType type,
8560     void* userData)
8561 {
8562     const VkDeviceSize offset = (VkDeviceSize)request.allocHandle - 1;
8563     const VmaSuballocation newSuballoc = { offset, request.size, userData, type };
8564 
8565     switch (request.type)
8566     {
8567     case VmaAllocationRequestType::UpperAddress:
8568     {
8569         VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
8570             "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
8571         SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8572         suballocations2nd.push_back(newSuballoc);
8573         m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
8574     }
8575     break;
8576     case VmaAllocationRequestType::EndOf1st:
8577     {
8578         SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8579 
8580         VMA_ASSERT(suballocations1st.empty() ||
8581             offset >= suballocations1st.back().offset + suballocations1st.back().size);
8582         // Check if it fits before the end of the block.
8583         VMA_ASSERT(offset + request.size <= GetSize());
8584 
8585         suballocations1st.push_back(newSuballoc);
8586     }
8587     break;
8588     case VmaAllocationRequestType::EndOf2nd:
8589     {
8590         SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8591         // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
8592         VMA_ASSERT(!suballocations1st.empty() &&
8593             offset + request.size <= suballocations1st[m_1stNullItemsBeginCount].offset);
8594         SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8595 
8596         switch (m_2ndVectorMode)
8597         {
8598         case SECOND_VECTOR_EMPTY:
8599             // First allocation from second part ring buffer.
8600             VMA_ASSERT(suballocations2nd.empty());
8601             m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
8602             break;
8603         case SECOND_VECTOR_RING_BUFFER:
8604             // 2-part ring buffer is already started.
8605             VMA_ASSERT(!suballocations2nd.empty());
8606             break;
8607         case SECOND_VECTOR_DOUBLE_STACK:
8608             VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
8609             break;
8610         default:
8611             VMA_ASSERT(0);
8612         }
8613 
8614         suballocations2nd.push_back(newSuballoc);
8615     }
8616     break;
8617     default:
8618         VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
8619     }
8620 
8621     m_SumFreeSize -= newSuballoc.size;
8622 }
8623 
Free(VmaAllocHandle allocHandle)8624 void VmaBlockMetadata_Linear::Free(VmaAllocHandle allocHandle)
8625 {
8626     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8627     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8628     VkDeviceSize offset = (VkDeviceSize)allocHandle - 1;
8629 
8630     if (!suballocations1st.empty())
8631     {
8632         // First allocation: Mark it as next empty at the beginning.
8633         VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
8634         if (firstSuballoc.offset == offset)
8635         {
8636             firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8637             firstSuballoc.userData = VMA_NULL;
8638             m_SumFreeSize += firstSuballoc.size;
8639             ++m_1stNullItemsBeginCount;
8640             CleanupAfterFree();
8641             return;
8642         }
8643     }
8644 
8645     // Last allocation in 2-part ring buffer or top of upper stack (same logic).
8646     if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
8647         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
8648     {
8649         VmaSuballocation& lastSuballoc = suballocations2nd.back();
8650         if (lastSuballoc.offset == offset)
8651         {
8652             m_SumFreeSize += lastSuballoc.size;
8653             suballocations2nd.pop_back();
8654             CleanupAfterFree();
8655             return;
8656         }
8657     }
8658     // Last allocation in 1st vector.
8659     else if (m_2ndVectorMode == SECOND_VECTOR_EMPTY)
8660     {
8661         VmaSuballocation& lastSuballoc = suballocations1st.back();
8662         if (lastSuballoc.offset == offset)
8663         {
8664             m_SumFreeSize += lastSuballoc.size;
8665             suballocations1st.pop_back();
8666             CleanupAfterFree();
8667             return;
8668         }
8669     }
8670 
8671     VmaSuballocation refSuballoc;
8672     refSuballoc.offset = offset;
8673     // Rest of members stays uninitialized intentionally for better performance.
8674 
8675     // Item from the middle of 1st vector.
8676     {
8677         const SuballocationVectorType::iterator it = VmaBinaryFindSorted(
8678             suballocations1st.begin() + m_1stNullItemsBeginCount,
8679             suballocations1st.end(),
8680             refSuballoc,
8681             VmaSuballocationOffsetLess());
8682         if (it != suballocations1st.end())
8683         {
8684             it->type = VMA_SUBALLOCATION_TYPE_FREE;
8685             it->userData = VMA_NULL;
8686             ++m_1stNullItemsMiddleCount;
8687             m_SumFreeSize += it->size;
8688             CleanupAfterFree();
8689             return;
8690         }
8691     }
8692 
8693     if (m_2ndVectorMode != SECOND_VECTOR_EMPTY)
8694     {
8695         // Item from the middle of 2nd vector.
8696         const SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
8697             VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :
8698             VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());
8699         if (it != suballocations2nd.end())
8700         {
8701             it->type = VMA_SUBALLOCATION_TYPE_FREE;
8702             it->userData = VMA_NULL;
8703             ++m_2ndNullItemsCount;
8704             m_SumFreeSize += it->size;
8705             CleanupAfterFree();
8706             return;
8707         }
8708     }
8709 
8710     VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
8711 }
8712 
GetAllocationInfo(VmaAllocHandle allocHandle,VmaVirtualAllocationInfo & outInfo)8713 void VmaBlockMetadata_Linear::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo)
8714 {
8715     outInfo.offset = (VkDeviceSize)allocHandle - 1;
8716     VmaSuballocation& suballoc = FindSuballocation(outInfo.offset);
8717     outInfo.size = suballoc.size;
8718     outInfo.pUserData = suballoc.userData;
8719 }
8720 
GetAllocationUserData(VmaAllocHandle allocHandle)8721 void* VmaBlockMetadata_Linear::GetAllocationUserData(VmaAllocHandle allocHandle) const
8722 {
8723     return FindSuballocation((VkDeviceSize)allocHandle - 1).userData;
8724 }
8725 
GetAllocationListBegin()8726 VmaAllocHandle VmaBlockMetadata_Linear::GetAllocationListBegin() const
8727 {
8728     // Function only used for defragmentation, which is disabled for this algorithm
8729     VMA_ASSERT(0);
8730     return VK_NULL_HANDLE;
8731 }
8732 
GetNextAllocation(VmaAllocHandle prevAlloc)8733 VmaAllocHandle VmaBlockMetadata_Linear::GetNextAllocation(VmaAllocHandle prevAlloc) const
8734 {
8735     // Function only used for defragmentation, which is disabled for this algorithm
8736     VMA_ASSERT(0);
8737     return VK_NULL_HANDLE;
8738 }
8739 
GetNextFreeRegionSize(VmaAllocHandle alloc)8740 VkDeviceSize VmaBlockMetadata_Linear::GetNextFreeRegionSize(VmaAllocHandle alloc) const
8741 {
8742     // Function only used for defragmentation, which is disabled for this algorithm
8743     VMA_ASSERT(0);
8744     return 0;
8745 }
8746 
Clear()8747 void VmaBlockMetadata_Linear::Clear()
8748 {
8749     m_SumFreeSize = GetSize();
8750     m_Suballocations0.clear();
8751     m_Suballocations1.clear();
8752     // Leaving m_1stVectorIndex unchanged - it doesn't matter.
8753     m_2ndVectorMode = SECOND_VECTOR_EMPTY;
8754     m_1stNullItemsBeginCount = 0;
8755     m_1stNullItemsMiddleCount = 0;
8756     m_2ndNullItemsCount = 0;
8757 }
8758 
SetAllocationUserData(VmaAllocHandle allocHandle,void * userData)8759 void VmaBlockMetadata_Linear::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData)
8760 {
8761     VmaSuballocation& suballoc = FindSuballocation((VkDeviceSize)allocHandle - 1);
8762     suballoc.userData = userData;
8763 }
8764 
DebugLogAllAllocations()8765 void VmaBlockMetadata_Linear::DebugLogAllAllocations() const
8766 {
8767     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8768     for (auto it = suballocations1st.begin() + m_1stNullItemsBeginCount; it != suballocations1st.end(); ++it)
8769         if (it->type != VMA_SUBALLOCATION_TYPE_FREE)
8770             DebugLogAllocation(it->offset, it->size, it->userData);
8771 
8772     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8773     for (auto it = suballocations2nd.begin(); it != suballocations2nd.end(); ++it)
8774         if (it->type != VMA_SUBALLOCATION_TYPE_FREE)
8775             DebugLogAllocation(it->offset, it->size, it->userData);
8776 }
8777 
FindSuballocation(VkDeviceSize offset)8778 VmaSuballocation& VmaBlockMetadata_Linear::FindSuballocation(VkDeviceSize offset) const
8779 {
8780     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8781     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8782 
8783     VmaSuballocation refSuballoc;
8784     refSuballoc.offset = offset;
8785     // Rest of members stays uninitialized intentionally for better performance.
8786 
8787     // Item from the 1st vector.
8788     {
8789         SuballocationVectorType::const_iterator it = VmaBinaryFindSorted(
8790             suballocations1st.begin() + m_1stNullItemsBeginCount,
8791             suballocations1st.end(),
8792             refSuballoc,
8793             VmaSuballocationOffsetLess());
8794         if (it != suballocations1st.end())
8795         {
8796             return const_cast<VmaSuballocation&>(*it);
8797         }
8798     }
8799 
8800     if (m_2ndVectorMode != SECOND_VECTOR_EMPTY)
8801     {
8802         // Rest of members stays uninitialized intentionally for better performance.
8803         SuballocationVectorType::const_iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
8804             VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :
8805             VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());
8806         if (it != suballocations2nd.end())
8807         {
8808             return const_cast<VmaSuballocation&>(*it);
8809         }
8810     }
8811 
8812     VMA_ASSERT(0 && "Allocation not found in linear allocator!");
8813     return const_cast<VmaSuballocation&>(suballocations1st.back()); // Should never occur.
8814 }
8815 
ShouldCompact1st()8816 bool VmaBlockMetadata_Linear::ShouldCompact1st() const
8817 {
8818     const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
8819     const size_t suballocCount = AccessSuballocations1st().size();
8820     return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
8821 }
8822 
CleanupAfterFree()8823 void VmaBlockMetadata_Linear::CleanupAfterFree()
8824 {
8825     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8826     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8827 
8828     if (IsEmpty())
8829     {
8830         suballocations1st.clear();
8831         suballocations2nd.clear();
8832         m_1stNullItemsBeginCount = 0;
8833         m_1stNullItemsMiddleCount = 0;
8834         m_2ndNullItemsCount = 0;
8835         m_2ndVectorMode = SECOND_VECTOR_EMPTY;
8836     }
8837     else
8838     {
8839         const size_t suballoc1stCount = suballocations1st.size();
8840         const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
8841         VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
8842 
8843         // Find more null items at the beginning of 1st vector.
8844         while (m_1stNullItemsBeginCount < suballoc1stCount &&
8845             suballocations1st[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE)
8846         {
8847             ++m_1stNullItemsBeginCount;
8848             --m_1stNullItemsMiddleCount;
8849         }
8850 
8851         // Find more null items at the end of 1st vector.
8852         while (m_1stNullItemsMiddleCount > 0 &&
8853             suballocations1st.back().type == VMA_SUBALLOCATION_TYPE_FREE)
8854         {
8855             --m_1stNullItemsMiddleCount;
8856             suballocations1st.pop_back();
8857         }
8858 
8859         // Find more null items at the end of 2nd vector.
8860         while (m_2ndNullItemsCount > 0 &&
8861             suballocations2nd.back().type == VMA_SUBALLOCATION_TYPE_FREE)
8862         {
8863             --m_2ndNullItemsCount;
8864             suballocations2nd.pop_back();
8865         }
8866 
8867         // Find more null items at the beginning of 2nd vector.
8868         while (m_2ndNullItemsCount > 0 &&
8869             suballocations2nd[0].type == VMA_SUBALLOCATION_TYPE_FREE)
8870         {
8871             --m_2ndNullItemsCount;
8872             VmaVectorRemove(suballocations2nd, 0);
8873         }
8874 
8875         if (ShouldCompact1st())
8876         {
8877             const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
8878             size_t srcIndex = m_1stNullItemsBeginCount;
8879             for (size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
8880             {
8881                 while (suballocations1st[srcIndex].type == VMA_SUBALLOCATION_TYPE_FREE)
8882                 {
8883                     ++srcIndex;
8884                 }
8885                 if (dstIndex != srcIndex)
8886                 {
8887                     suballocations1st[dstIndex] = suballocations1st[srcIndex];
8888                 }
8889                 ++srcIndex;
8890             }
8891             suballocations1st.resize(nonNullItemCount);
8892             m_1stNullItemsBeginCount = 0;
8893             m_1stNullItemsMiddleCount = 0;
8894         }
8895 
8896         // 2nd vector became empty.
8897         if (suballocations2nd.empty())
8898         {
8899             m_2ndVectorMode = SECOND_VECTOR_EMPTY;
8900         }
8901 
8902         // 1st vector became empty.
8903         if (suballocations1st.size() - m_1stNullItemsBeginCount == 0)
8904         {
8905             suballocations1st.clear();
8906             m_1stNullItemsBeginCount = 0;
8907 
8908             if (!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8909             {
8910                 // Swap 1st with 2nd. Now 2nd is empty.
8911                 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
8912                 m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
8913                 while (m_1stNullItemsBeginCount < suballocations2nd.size() &&
8914                     suballocations2nd[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE)
8915                 {
8916                     ++m_1stNullItemsBeginCount;
8917                     --m_1stNullItemsMiddleCount;
8918                 }
8919                 m_2ndNullItemsCount = 0;
8920                 m_1stVectorIndex ^= 1;
8921             }
8922         }
8923     }
8924 
8925     VMA_HEAVY_ASSERT(Validate());
8926 }
8927 
CreateAllocationRequest_LowerAddress(VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)8928 bool VmaBlockMetadata_Linear::CreateAllocationRequest_LowerAddress(
8929     VkDeviceSize allocSize,
8930     VkDeviceSize allocAlignment,
8931     VmaSuballocationType allocType,
8932     uint32_t strategy,
8933     VmaAllocationRequest* pAllocationRequest)
8934 {
8935     const VkDeviceSize blockSize = GetSize();
8936     const VkDeviceSize debugMargin = GetDebugMargin();
8937     const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity();
8938     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8939     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8940 
8941     if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
8942     {
8943         // Try to allocate at the end of 1st vector.
8944 
8945         VkDeviceSize resultBaseOffset = 0;
8946         if (!suballocations1st.empty())
8947         {
8948             const VmaSuballocation& lastSuballoc = suballocations1st.back();
8949             resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + debugMargin;
8950         }
8951 
8952         // Start from offset equal to beginning of free space.
8953         VkDeviceSize resultOffset = resultBaseOffset;
8954 
8955         // Apply alignment.
8956         resultOffset = VmaAlignUp(resultOffset, allocAlignment);
8957 
8958         // Check previous suballocations for BufferImageGranularity conflicts.
8959         // Make bigger alignment if necessary.
8960         if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations1st.empty())
8961         {
8962             bool bufferImageGranularityConflict = false;
8963             for (size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
8964             {
8965                 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
8966                 if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
8967                 {
8968                     if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
8969                     {
8970                         bufferImageGranularityConflict = true;
8971                         break;
8972                     }
8973                 }
8974                 else
8975                     // Already on previous page.
8976                     break;
8977             }
8978             if (bufferImageGranularityConflict)
8979             {
8980                 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
8981             }
8982         }
8983 
8984         const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
8985             suballocations2nd.back().offset : blockSize;
8986 
8987         // There is enough free space at the end after alignment.
8988         if (resultOffset + allocSize + debugMargin <= freeSpaceEnd)
8989         {
8990             // Check next suballocations for BufferImageGranularity conflicts.
8991             // If conflict exists, allocation cannot be made here.
8992             if ((allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity) && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
8993             {
8994                 for (size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
8995                 {
8996                     const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
8997                     if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8998                     {
8999                         if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9000                         {
9001                             return false;
9002                         }
9003                     }
9004                     else
9005                     {
9006                         // Already on previous page.
9007                         break;
9008                     }
9009                 }
9010             }
9011 
9012             // All tests passed: Success.
9013             pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1);
9014             // pAllocationRequest->item, customData unused.
9015             pAllocationRequest->type = VmaAllocationRequestType::EndOf1st;
9016             return true;
9017         }
9018     }
9019 
9020     // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
9021     // beginning of 1st vector as the end of free space.
9022     if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9023     {
9024         VMA_ASSERT(!suballocations1st.empty());
9025 
9026         VkDeviceSize resultBaseOffset = 0;
9027         if (!suballocations2nd.empty())
9028         {
9029             const VmaSuballocation& lastSuballoc = suballocations2nd.back();
9030             resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + debugMargin;
9031         }
9032 
9033         // Start from offset equal to beginning of free space.
9034         VkDeviceSize resultOffset = resultBaseOffset;
9035 
9036         // Apply alignment.
9037         resultOffset = VmaAlignUp(resultOffset, allocAlignment);
9038 
9039         // Check previous suballocations for BufferImageGranularity conflicts.
9040         // Make bigger alignment if necessary.
9041         if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty())
9042         {
9043             bool bufferImageGranularityConflict = false;
9044             for (size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
9045             {
9046                 const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
9047                 if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9048                 {
9049                     if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9050                     {
9051                         bufferImageGranularityConflict = true;
9052                         break;
9053                     }
9054                 }
9055                 else
9056                     // Already on previous page.
9057                     break;
9058             }
9059             if (bufferImageGranularityConflict)
9060             {
9061                 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
9062             }
9063         }
9064 
9065         size_t index1st = m_1stNullItemsBeginCount;
9066 
9067         // There is enough free space at the end after alignment.
9068         if ((index1st == suballocations1st.size() && resultOffset + allocSize + debugMargin <= blockSize) ||
9069             (index1st < suballocations1st.size() && resultOffset + allocSize + debugMargin <= suballocations1st[index1st].offset))
9070         {
9071             // Check next suballocations for BufferImageGranularity conflicts.
9072             // If conflict exists, allocation cannot be made here.
9073             if (allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity)
9074             {
9075                 for (size_t nextSuballocIndex = index1st;
9076                     nextSuballocIndex < suballocations1st.size();
9077                     nextSuballocIndex++)
9078                 {
9079                     const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
9080                     if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9081                     {
9082                         if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9083                         {
9084                             return false;
9085                         }
9086                     }
9087                     else
9088                     {
9089                         // Already on next page.
9090                         break;
9091                     }
9092                 }
9093             }
9094 
9095             // All tests passed: Success.
9096             pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1);
9097             pAllocationRequest->type = VmaAllocationRequestType::EndOf2nd;
9098             // pAllocationRequest->item, customData unused.
9099             return true;
9100         }
9101     }
9102 
9103     return false;
9104 }
9105 
CreateAllocationRequest_UpperAddress(VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)9106 bool VmaBlockMetadata_Linear::CreateAllocationRequest_UpperAddress(
9107     VkDeviceSize allocSize,
9108     VkDeviceSize allocAlignment,
9109     VmaSuballocationType allocType,
9110     uint32_t strategy,
9111     VmaAllocationRequest* pAllocationRequest)
9112 {
9113     const VkDeviceSize blockSize = GetSize();
9114     const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity();
9115     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9116     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9117 
9118     if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9119     {
9120         VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
9121         return false;
9122     }
9123 
9124     // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
9125     if (allocSize > blockSize)
9126     {
9127         return false;
9128     }
9129     VkDeviceSize resultBaseOffset = blockSize - allocSize;
9130     if (!suballocations2nd.empty())
9131     {
9132         const VmaSuballocation& lastSuballoc = suballocations2nd.back();
9133         resultBaseOffset = lastSuballoc.offset - allocSize;
9134         if (allocSize > lastSuballoc.offset)
9135         {
9136             return false;
9137         }
9138     }
9139 
9140     // Start from offset equal to end of free space.
9141     VkDeviceSize resultOffset = resultBaseOffset;
9142 
9143     const VkDeviceSize debugMargin = GetDebugMargin();
9144 
9145     // Apply debugMargin at the end.
9146     if (debugMargin > 0)
9147     {
9148         if (resultOffset < debugMargin)
9149         {
9150             return false;
9151         }
9152         resultOffset -= debugMargin;
9153     }
9154 
9155     // Apply alignment.
9156     resultOffset = VmaAlignDown(resultOffset, allocAlignment);
9157 
9158     // Check next suballocations from 2nd for BufferImageGranularity conflicts.
9159     // Make bigger alignment if necessary.
9160     if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty())
9161     {
9162         bool bufferImageGranularityConflict = false;
9163         for (size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
9164         {
9165             const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
9166             if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9167             {
9168                 if (VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
9169                 {
9170                     bufferImageGranularityConflict = true;
9171                     break;
9172                 }
9173             }
9174             else
9175                 // Already on previous page.
9176                 break;
9177         }
9178         if (bufferImageGranularityConflict)
9179         {
9180             resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
9181         }
9182     }
9183 
9184     // There is enough free space.
9185     const VkDeviceSize endOf1st = !suballocations1st.empty() ?
9186         suballocations1st.back().offset + suballocations1st.back().size :
9187         0;
9188     if (endOf1st + debugMargin <= resultOffset)
9189     {
9190         // Check previous suballocations for BufferImageGranularity conflicts.
9191         // If conflict exists, allocation cannot be made here.
9192         if (bufferImageGranularity > 1)
9193         {
9194             for (size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
9195             {
9196                 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
9197                 if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9198                 {
9199                     if (VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
9200                     {
9201                         return false;
9202                     }
9203                 }
9204                 else
9205                 {
9206                     // Already on next page.
9207                     break;
9208                 }
9209             }
9210         }
9211 
9212         // All tests passed: Success.
9213         pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1);
9214         // pAllocationRequest->item unused.
9215         pAllocationRequest->type = VmaAllocationRequestType::UpperAddress;
9216         return true;
9217     }
9218 
9219     return false;
9220 }
9221 #endif // _VMA_BLOCK_METADATA_LINEAR_FUNCTIONS
9222 #endif // _VMA_BLOCK_METADATA_LINEAR
9223 
9224 #if 0
9225 #ifndef _VMA_BLOCK_METADATA_BUDDY
9226 /*
9227 - GetSize() is the original size of allocated memory block.
9228 - m_UsableSize is this size aligned down to a power of two.
9229   All allocations and calculations happen relative to m_UsableSize.
9230 - GetUnusableSize() is the difference between them.
9231   It is reported as separate, unused range, not available for allocations.
9232 
9233 Node at level 0 has size = m_UsableSize.
9234 Each next level contains nodes with size 2 times smaller than current level.
9235 m_LevelCount is the maximum number of levels to use in the current object.
9236 */
9237 class VmaBlockMetadata_Buddy : public VmaBlockMetadata
9238 {
9239     VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
9240 public:
9241     VmaBlockMetadata_Buddy(const VkAllocationCallbacks* pAllocationCallbacks,
9242         VkDeviceSize bufferImageGranularity, bool isVirtual);
9243     virtual ~VmaBlockMetadata_Buddy();
9244 
9245     size_t GetAllocationCount() const override { return m_AllocationCount; }
9246     VkDeviceSize GetSumFreeSize() const override { return m_SumFreeSize + GetUnusableSize(); }
9247     bool IsEmpty() const override { return m_Root->type == Node::TYPE_FREE; }
9248     VkResult CheckCorruption(const void* pBlockData) override { return VK_ERROR_FEATURE_NOT_PRESENT; }
9249     VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle - 1; };
9250     void DebugLogAllAllocations() const override { DebugLogAllAllocationNode(m_Root, 0); }
9251 
9252     void Init(VkDeviceSize size) override;
9253     bool Validate() const override;
9254 
9255     void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override;
9256     void AddStatistics(VmaStatistics& inoutStats) const override;
9257 
9258 #if VMA_STATS_STRING_ENABLED
9259     void PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const override;
9260 #endif
9261 
9262     bool CreateAllocationRequest(
9263         VkDeviceSize allocSize,
9264         VkDeviceSize allocAlignment,
9265         bool upperAddress,
9266         VmaSuballocationType allocType,
9267         uint32_t strategy,
9268         VmaAllocationRequest* pAllocationRequest) override;
9269 
9270     void Alloc(
9271         const VmaAllocationRequest& request,
9272         VmaSuballocationType type,
9273         void* userData) override;
9274 
9275     void Free(VmaAllocHandle allocHandle) override;
9276     void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override;
9277     void* GetAllocationUserData(VmaAllocHandle allocHandle) const override;
9278     VmaAllocHandle GetAllocationListBegin() const override;
9279     VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override;
9280     void Clear() override;
9281     void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override;
9282 
9283 private:
9284     static const size_t MAX_LEVELS = 48;
9285 
9286     struct ValidationContext
9287     {
9288         size_t calculatedAllocationCount = 0;
9289         size_t calculatedFreeCount = 0;
9290         VkDeviceSize calculatedSumFreeSize = 0;
9291     };
9292     struct Node
9293     {
9294         VkDeviceSize offset;
9295         enum TYPE
9296         {
9297             TYPE_FREE,
9298             TYPE_ALLOCATION,
9299             TYPE_SPLIT,
9300             TYPE_COUNT
9301         } type;
9302         Node* parent;
9303         Node* buddy;
9304 
9305         union
9306         {
9307             struct
9308             {
9309                 Node* prev;
9310                 Node* next;
9311             } free;
9312             struct
9313             {
9314                 void* userData;
9315             } allocation;
9316             struct
9317             {
9318                 Node* leftChild;
9319             } split;
9320         };
9321     };
9322 
9323     // Size of the memory block aligned down to a power of two.
9324     VkDeviceSize m_UsableSize;
9325     uint32_t m_LevelCount;
9326     VmaPoolAllocator<Node> m_NodeAllocator;
9327     Node* m_Root;
9328     struct
9329     {
9330         Node* front;
9331         Node* back;
9332     } m_FreeList[MAX_LEVELS];
9333 
9334     // Number of nodes in the tree with type == TYPE_ALLOCATION.
9335     size_t m_AllocationCount;
9336     // Number of nodes in the tree with type == TYPE_FREE.
9337     size_t m_FreeCount;
9338     // Doesn't include space wasted due to internal fragmentation - allocation sizes are just aligned up to node sizes.
9339     // Doesn't include unusable size.
9340     VkDeviceSize m_SumFreeSize;
9341 
9342     VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
9343     VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
9344 
9345     VkDeviceSize AlignAllocationSize(VkDeviceSize size) const
9346     {
9347         if (!IsVirtual())
9348         {
9349             size = VmaAlignUp(size, (VkDeviceSize)16);
9350         }
9351         return VmaNextPow2(size);
9352     }
9353     Node* FindAllocationNode(VkDeviceSize offset, uint32_t& outLevel) const;
9354     void DeleteNodeChildren(Node* node);
9355     bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
9356     uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
9357     void AddNodeToDetailedStatistics(VmaDetailedStatistics& inoutStats, const Node* node, VkDeviceSize levelNodeSize) const;
9358     // Adds node to the front of FreeList at given level.
9359     // node->type must be FREE.
9360     // node->free.prev, next can be undefined.
9361     void AddToFreeListFront(uint32_t level, Node* node);
9362     // Removes node from FreeList at given level.
9363     // node->type must be FREE.
9364     // node->free.prev, next stay untouched.
9365     void RemoveFromFreeList(uint32_t level, Node* node);
9366     void DebugLogAllAllocationNode(Node* node, uint32_t level) const;
9367 
9368 #if VMA_STATS_STRING_ENABLED
9369     void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
9370 #endif
9371 };
9372 
9373 #ifndef _VMA_BLOCK_METADATA_BUDDY_FUNCTIONS
9374 VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(const VkAllocationCallbacks* pAllocationCallbacks,
9375     VkDeviceSize bufferImageGranularity, bool isVirtual)
9376     : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual),
9377     m_NodeAllocator(pAllocationCallbacks, 32), // firstBlockCapacity
9378     m_Root(VMA_NULL),
9379     m_AllocationCount(0),
9380     m_FreeCount(1),
9381     m_SumFreeSize(0)
9382 {
9383     memset(m_FreeList, 0, sizeof(m_FreeList));
9384 }
9385 
9386 VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
9387 {
9388     DeleteNodeChildren(m_Root);
9389     m_NodeAllocator.Free(m_Root);
9390 }
9391 
9392 void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
9393 {
9394     VmaBlockMetadata::Init(size);
9395 
9396     m_UsableSize = VmaPrevPow2(size);
9397     m_SumFreeSize = m_UsableSize;
9398 
9399     // Calculate m_LevelCount.
9400     const VkDeviceSize minNodeSize = IsVirtual() ? 1 : 16;
9401     m_LevelCount = 1;
9402     while (m_LevelCount < MAX_LEVELS &&
9403         LevelToNodeSize(m_LevelCount) >= minNodeSize)
9404     {
9405         ++m_LevelCount;
9406     }
9407 
9408     Node* rootNode = m_NodeAllocator.Alloc();
9409     rootNode->offset = 0;
9410     rootNode->type = Node::TYPE_FREE;
9411     rootNode->parent = VMA_NULL;
9412     rootNode->buddy = VMA_NULL;
9413 
9414     m_Root = rootNode;
9415     AddToFreeListFront(0, rootNode);
9416 }
9417 
9418 bool VmaBlockMetadata_Buddy::Validate() const
9419 {
9420     // Validate tree.
9421     ValidationContext ctx;
9422     if (!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
9423     {
9424         VMA_VALIDATE(false && "ValidateNode failed.");
9425     }
9426     VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
9427     VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
9428 
9429     // Validate free node lists.
9430     for (uint32_t level = 0; level < m_LevelCount; ++level)
9431     {
9432         VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
9433             m_FreeList[level].front->free.prev == VMA_NULL);
9434 
9435         for (Node* node = m_FreeList[level].front;
9436             node != VMA_NULL;
9437             node = node->free.next)
9438         {
9439             VMA_VALIDATE(node->type == Node::TYPE_FREE);
9440 
9441             if (node->free.next == VMA_NULL)
9442             {
9443                 VMA_VALIDATE(m_FreeList[level].back == node);
9444             }
9445             else
9446             {
9447                 VMA_VALIDATE(node->free.next->free.prev == node);
9448             }
9449         }
9450     }
9451 
9452     // Validate that free lists ar higher levels are empty.
9453     for (uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
9454     {
9455         VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
9456     }
9457 
9458     return true;
9459 }
9460 
9461 void VmaBlockMetadata_Buddy::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const
9462 {
9463     inoutStats.statistics.blockCount++;
9464     inoutStats.statistics.blockBytes += GetSize();
9465 
9466     AddNodeToDetailedStatistics(inoutStats, m_Root, LevelToNodeSize(0));
9467 
9468     const VkDeviceSize unusableSize = GetUnusableSize();
9469     if (unusableSize > 0)
9470         VmaAddDetailedStatisticsUnusedRange(inoutStats, unusableSize);
9471 }
9472 
9473 void VmaBlockMetadata_Buddy::AddStatistics(VmaStatistics& inoutStats) const
9474 {
9475     inoutStats.blockCount++;
9476     inoutStats.allocationCount += (uint32_t)m_AllocationCount;
9477     inoutStats.blockBytes += GetSize();
9478     inoutStats.allocationBytes += GetSize() - m_SumFreeSize;
9479 }
9480 
9481 #if VMA_STATS_STRING_ENABLED
9482 void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const
9483 {
9484     VmaDetailedStatistics stats;
9485     VmaClearDetailedStatistics(stats);
9486     AddDetailedStatistics(stats);
9487 
9488     PrintDetailedMap_Begin(
9489         json,
9490         stats.statistics.blockBytes - stats.statistics.allocationBytes,
9491         stats.statistics.allocationCount,
9492         stats.unusedRangeCount,
9493         mapRefCount);
9494 
9495     PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
9496 
9497     const VkDeviceSize unusableSize = GetUnusableSize();
9498     if (unusableSize > 0)
9499     {
9500         PrintDetailedMap_UnusedRange(json,
9501             m_UsableSize, // offset
9502             unusableSize); // size
9503     }
9504 
9505     PrintDetailedMap_End(json);
9506 }
9507 #endif // VMA_STATS_STRING_ENABLED
9508 
9509 bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
9510     VkDeviceSize allocSize,
9511     VkDeviceSize allocAlignment,
9512     bool upperAddress,
9513     VmaSuballocationType allocType,
9514     uint32_t strategy,
9515     VmaAllocationRequest* pAllocationRequest)
9516 {
9517     VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
9518 
9519     allocSize = AlignAllocationSize(allocSize);
9520 
9521     // Simple way to respect bufferImageGranularity. May be optimized some day.
9522     // Whenever it might be an OPTIMAL image...
9523     if (allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
9524         allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
9525         allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
9526     {
9527         allocAlignment = VMA_MAX(allocAlignment, GetBufferImageGranularity());
9528         allocSize = VmaAlignUp(allocSize, GetBufferImageGranularity());
9529     }
9530 
9531     if (allocSize > m_UsableSize)
9532     {
9533         return false;
9534     }
9535 
9536     const uint32_t targetLevel = AllocSizeToLevel(allocSize);
9537     for (uint32_t level = targetLevel; level--; )
9538     {
9539         for (Node* freeNode = m_FreeList[level].front;
9540             freeNode != VMA_NULL;
9541             freeNode = freeNode->free.next)
9542         {
9543             if (freeNode->offset % allocAlignment == 0)
9544             {
9545                 pAllocationRequest->type = VmaAllocationRequestType::Normal;
9546                 pAllocationRequest->allocHandle = (VmaAllocHandle)(freeNode->offset + 1);
9547                 pAllocationRequest->size = allocSize;
9548                 pAllocationRequest->customData = (void*)(uintptr_t)level;
9549                 return true;
9550             }
9551         }
9552     }
9553 
9554     return false;
9555 }
9556 
9557 void VmaBlockMetadata_Buddy::Alloc(
9558     const VmaAllocationRequest& request,
9559     VmaSuballocationType type,
9560     void* userData)
9561 {
9562     VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
9563 
9564     const uint32_t targetLevel = AllocSizeToLevel(request.size);
9565     uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
9566 
9567     Node* currNode = m_FreeList[currLevel].front;
9568     VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
9569     const VkDeviceSize offset = (VkDeviceSize)request.allocHandle - 1;
9570     while (currNode->offset != offset)
9571     {
9572         currNode = currNode->free.next;
9573         VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
9574     }
9575 
9576     // Go down, splitting free nodes.
9577     while (currLevel < targetLevel)
9578     {
9579         // currNode is already first free node at currLevel.
9580         // Remove it from list of free nodes at this currLevel.
9581         RemoveFromFreeList(currLevel, currNode);
9582 
9583         const uint32_t childrenLevel = currLevel + 1;
9584 
9585         // Create two free sub-nodes.
9586         Node* leftChild = m_NodeAllocator.Alloc();
9587         Node* rightChild = m_NodeAllocator.Alloc();
9588 
9589         leftChild->offset = currNode->offset;
9590         leftChild->type = Node::TYPE_FREE;
9591         leftChild->parent = currNode;
9592         leftChild->buddy = rightChild;
9593 
9594         rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
9595         rightChild->type = Node::TYPE_FREE;
9596         rightChild->parent = currNode;
9597         rightChild->buddy = leftChild;
9598 
9599         // Convert current currNode to split type.
9600         currNode->type = Node::TYPE_SPLIT;
9601         currNode->split.leftChild = leftChild;
9602 
9603         // Add child nodes to free list. Order is important!
9604         AddToFreeListFront(childrenLevel, rightChild);
9605         AddToFreeListFront(childrenLevel, leftChild);
9606 
9607         ++m_FreeCount;
9608         ++currLevel;
9609         currNode = m_FreeList[currLevel].front;
9610 
9611         /*
9612         We can be sure that currNode, as left child of node previously split,
9613         also fulfills the alignment requirement.
9614         */
9615     }
9616 
9617     // Remove from free list.
9618     VMA_ASSERT(currLevel == targetLevel &&
9619         currNode != VMA_NULL &&
9620         currNode->type == Node::TYPE_FREE);
9621     RemoveFromFreeList(currLevel, currNode);
9622 
9623     // Convert to allocation node.
9624     currNode->type = Node::TYPE_ALLOCATION;
9625     currNode->allocation.userData = userData;
9626 
9627     ++m_AllocationCount;
9628     --m_FreeCount;
9629     m_SumFreeSize -= request.size;
9630 }
9631 
9632 void VmaBlockMetadata_Buddy::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo)
9633 {
9634     uint32_t level = 0;
9635     outInfo.offset = (VkDeviceSize)allocHandle - 1;
9636     const Node* const node = FindAllocationNode(outInfo.offset, level);
9637     outInfo.size = LevelToNodeSize(level);
9638     outInfo.pUserData = node->allocation.userData;
9639 }
9640 
9641 void* VmaBlockMetadata_Buddy::GetAllocationUserData(VmaAllocHandle allocHandle) const
9642 {
9643     uint32_t level = 0;
9644     const Node* const node = FindAllocationNode((VkDeviceSize)allocHandle - 1, level);
9645     return node->allocation.userData;
9646 }
9647 
9648 VmaAllocHandle VmaBlockMetadata_Buddy::GetAllocationListBegin() const
9649 {
9650     // Function only used for defragmentation, which is disabled for this algorithm
9651     return VK_NULL_HANDLE;
9652 }
9653 
9654 VmaAllocHandle VmaBlockMetadata_Buddy::GetNextAllocation(VmaAllocHandle prevAlloc) const
9655 {
9656     // Function only used for defragmentation, which is disabled for this algorithm
9657     return VK_NULL_HANDLE;
9658 }
9659 
9660 void VmaBlockMetadata_Buddy::DeleteNodeChildren(Node* node)
9661 {
9662     if (node->type == Node::TYPE_SPLIT)
9663     {
9664         DeleteNodeChildren(node->split.leftChild->buddy);
9665         DeleteNodeChildren(node->split.leftChild);
9666         const VkAllocationCallbacks* allocationCallbacks = GetAllocationCallbacks();
9667         m_NodeAllocator.Free(node->split.leftChild->buddy);
9668         m_NodeAllocator.Free(node->split.leftChild);
9669     }
9670 }
9671 
9672 void VmaBlockMetadata_Buddy::Clear()
9673 {
9674     DeleteNodeChildren(m_Root);
9675     m_Root->type = Node::TYPE_FREE;
9676     m_AllocationCount = 0;
9677     m_FreeCount = 1;
9678     m_SumFreeSize = m_UsableSize;
9679 }
9680 
9681 void VmaBlockMetadata_Buddy::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData)
9682 {
9683     uint32_t level = 0;
9684     Node* const node = FindAllocationNode((VkDeviceSize)allocHandle - 1, level);
9685     node->allocation.userData = userData;
9686 }
9687 
9688 VmaBlockMetadata_Buddy::Node* VmaBlockMetadata_Buddy::FindAllocationNode(VkDeviceSize offset, uint32_t& outLevel) const
9689 {
9690     Node* node = m_Root;
9691     VkDeviceSize nodeOffset = 0;
9692     outLevel = 0;
9693     VkDeviceSize levelNodeSize = LevelToNodeSize(0);
9694     while (node->type == Node::TYPE_SPLIT)
9695     {
9696         const VkDeviceSize nextLevelNodeSize = levelNodeSize >> 1;
9697         if (offset < nodeOffset + nextLevelNodeSize)
9698         {
9699             node = node->split.leftChild;
9700         }
9701         else
9702         {
9703             node = node->split.leftChild->buddy;
9704             nodeOffset += nextLevelNodeSize;
9705         }
9706         ++outLevel;
9707         levelNodeSize = nextLevelNodeSize;
9708     }
9709 
9710     VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
9711     return node;
9712 }
9713 
9714 bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
9715 {
9716     VMA_VALIDATE(level < m_LevelCount);
9717     VMA_VALIDATE(curr->parent == parent);
9718     VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
9719     VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
9720     switch (curr->type)
9721     {
9722     case Node::TYPE_FREE:
9723         // curr->free.prev, next are validated separately.
9724         ctx.calculatedSumFreeSize += levelNodeSize;
9725         ++ctx.calculatedFreeCount;
9726         break;
9727     case Node::TYPE_ALLOCATION:
9728         ++ctx.calculatedAllocationCount;
9729         if (!IsVirtual())
9730         {
9731             VMA_VALIDATE(curr->allocation.userData != VMA_NULL);
9732         }
9733         break;
9734     case Node::TYPE_SPLIT:
9735     {
9736         const uint32_t childrenLevel = level + 1;
9737         const VkDeviceSize childrenLevelNodeSize = levelNodeSize >> 1;
9738         const Node* const leftChild = curr->split.leftChild;
9739         VMA_VALIDATE(leftChild != VMA_NULL);
9740         VMA_VALIDATE(leftChild->offset == curr->offset);
9741         if (!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
9742         {
9743             VMA_VALIDATE(false && "ValidateNode for left child failed.");
9744         }
9745         const Node* const rightChild = leftChild->buddy;
9746         VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
9747         if (!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
9748         {
9749             VMA_VALIDATE(false && "ValidateNode for right child failed.");
9750         }
9751     }
9752     break;
9753     default:
9754         return false;
9755     }
9756 
9757     return true;
9758 }
9759 
9760 uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
9761 {
9762     // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
9763     uint32_t level = 0;
9764     VkDeviceSize currLevelNodeSize = m_UsableSize;
9765     VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
9766     while (allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
9767     {
9768         ++level;
9769         currLevelNodeSize >>= 1;
9770         nextLevelNodeSize >>= 1;
9771     }
9772     return level;
9773 }
9774 
9775 void VmaBlockMetadata_Buddy::Free(VmaAllocHandle allocHandle)
9776 {
9777     uint32_t level = 0;
9778     Node* node = FindAllocationNode((VkDeviceSize)allocHandle - 1, level);
9779 
9780     ++m_FreeCount;
9781     --m_AllocationCount;
9782     m_SumFreeSize += LevelToNodeSize(level);
9783 
9784     node->type = Node::TYPE_FREE;
9785 
9786     // Join free nodes if possible.
9787     while (level > 0 && node->buddy->type == Node::TYPE_FREE)
9788     {
9789         RemoveFromFreeList(level, node->buddy);
9790         Node* const parent = node->parent;
9791 
9792         m_NodeAllocator.Free(node->buddy);
9793         m_NodeAllocator.Free(node);
9794         parent->type = Node::TYPE_FREE;
9795 
9796         node = parent;
9797         --level;
9798         --m_FreeCount;
9799     }
9800 
9801     AddToFreeListFront(level, node);
9802 }
9803 
9804 void VmaBlockMetadata_Buddy::AddNodeToDetailedStatistics(VmaDetailedStatistics& inoutStats, const Node* node, VkDeviceSize levelNodeSize) const
9805 {
9806     switch (node->type)
9807     {
9808     case Node::TYPE_FREE:
9809         VmaAddDetailedStatisticsUnusedRange(inoutStats, levelNodeSize);
9810         break;
9811     case Node::TYPE_ALLOCATION:
9812         VmaAddDetailedStatisticsAllocation(inoutStats, levelNodeSize);
9813         break;
9814     case Node::TYPE_SPLIT:
9815     {
9816         const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
9817         const Node* const leftChild = node->split.leftChild;
9818         AddNodeToDetailedStatistics(inoutStats, leftChild, childrenNodeSize);
9819         const Node* const rightChild = leftChild->buddy;
9820         AddNodeToDetailedStatistics(inoutStats, rightChild, childrenNodeSize);
9821     }
9822     break;
9823     default:
9824         VMA_ASSERT(0);
9825     }
9826 }
9827 
9828 void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
9829 {
9830     VMA_ASSERT(node->type == Node::TYPE_FREE);
9831 
9832     // List is empty.
9833     Node* const frontNode = m_FreeList[level].front;
9834     if (frontNode == VMA_NULL)
9835     {
9836         VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
9837         node->free.prev = node->free.next = VMA_NULL;
9838         m_FreeList[level].front = m_FreeList[level].back = node;
9839     }
9840     else
9841     {
9842         VMA_ASSERT(frontNode->free.prev == VMA_NULL);
9843         node->free.prev = VMA_NULL;
9844         node->free.next = frontNode;
9845         frontNode->free.prev = node;
9846         m_FreeList[level].front = node;
9847     }
9848 }
9849 
9850 void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
9851 {
9852     VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
9853 
9854     // It is at the front.
9855     if (node->free.prev == VMA_NULL)
9856     {
9857         VMA_ASSERT(m_FreeList[level].front == node);
9858         m_FreeList[level].front = node->free.next;
9859     }
9860     else
9861     {
9862         Node* const prevFreeNode = node->free.prev;
9863         VMA_ASSERT(prevFreeNode->free.next == node);
9864         prevFreeNode->free.next = node->free.next;
9865     }
9866 
9867     // It is at the back.
9868     if (node->free.next == VMA_NULL)
9869     {
9870         VMA_ASSERT(m_FreeList[level].back == node);
9871         m_FreeList[level].back = node->free.prev;
9872     }
9873     else
9874     {
9875         Node* const nextFreeNode = node->free.next;
9876         VMA_ASSERT(nextFreeNode->free.prev == node);
9877         nextFreeNode->free.prev = node->free.prev;
9878     }
9879 }
9880 
9881 void VmaBlockMetadata_Buddy::DebugLogAllAllocationNode(Node* node, uint32_t level) const
9882 {
9883     switch (node->type)
9884     {
9885     case Node::TYPE_FREE:
9886         break;
9887     case Node::TYPE_ALLOCATION:
9888         DebugLogAllocation(node->offset, LevelToNodeSize(level), node->allocation.userData);
9889         break;
9890     case Node::TYPE_SPLIT:
9891     {
9892         ++level;
9893         DebugLogAllAllocationNode(node->split.leftChild, level);
9894         DebugLogAllAllocationNode(node->split.leftChild->buddy, level);
9895     }
9896     break;
9897     default:
9898         VMA_ASSERT(0);
9899     }
9900 }
9901 
9902 #if VMA_STATS_STRING_ENABLED
9903 void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
9904 {
9905     switch (node->type)
9906     {
9907     case Node::TYPE_FREE:
9908         PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
9909         break;
9910     case Node::TYPE_ALLOCATION:
9911         PrintDetailedMap_Allocation(json, node->offset, levelNodeSize, node->allocation.userData);
9912         break;
9913     case Node::TYPE_SPLIT:
9914     {
9915         const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
9916         const Node* const leftChild = node->split.leftChild;
9917         PrintDetailedMapNode(json, leftChild, childrenNodeSize);
9918         const Node* const rightChild = leftChild->buddy;
9919         PrintDetailedMapNode(json, rightChild, childrenNodeSize);
9920     }
9921     break;
9922     default:
9923         VMA_ASSERT(0);
9924     }
9925 }
9926 #endif // VMA_STATS_STRING_ENABLED
9927 #endif // _VMA_BLOCK_METADATA_BUDDY_FUNCTIONS
9928 #endif // _VMA_BLOCK_METADATA_BUDDY
9929 #endif // #if 0
9930 
9931 #ifndef _VMA_BLOCK_METADATA_TLSF
9932 // To not search current larger region if first allocation won't succeed and skip to smaller range
9933 // use with VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT as strategy in CreateAllocationRequest().
9934 // When fragmentation and reusal of previous blocks doesn't matter then use with
9935 // VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT for fastest alloc time possible.
9936 class VmaBlockMetadata_TLSF : public VmaBlockMetadata
9937 {
9938     VMA_CLASS_NO_COPY(VmaBlockMetadata_TLSF)
9939 public:
9940     VmaBlockMetadata_TLSF(const VkAllocationCallbacks* pAllocationCallbacks,
9941         VkDeviceSize bufferImageGranularity, bool isVirtual);
9942     virtual ~VmaBlockMetadata_TLSF();
9943 
GetAllocationCount()9944     size_t GetAllocationCount() const override { return m_AllocCount; }
GetFreeRegionsCount()9945     size_t GetFreeRegionsCount() const override { return m_BlocksFreeCount + 1; }
GetSumFreeSize()9946     VkDeviceSize GetSumFreeSize() const override { return m_BlocksFreeSize + m_NullBlock->size; }
IsEmpty()9947     bool IsEmpty() const override { return m_NullBlock->offset == 0; }
GetAllocationOffset(VmaAllocHandle allocHandle)9948     VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return ((Block*)allocHandle)->offset; };
9949 
9950     void Init(VkDeviceSize size) override;
9951     bool Validate() const override;
9952 
9953     void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override;
9954     void AddStatistics(VmaStatistics& inoutStats) const override;
9955 
9956 #if VMA_STATS_STRING_ENABLED
9957     void PrintDetailedMap(class VmaJsonWriter& json) const override;
9958 #endif
9959 
9960     bool CreateAllocationRequest(
9961         VkDeviceSize allocSize,
9962         VkDeviceSize allocAlignment,
9963         bool upperAddress,
9964         VmaSuballocationType allocType,
9965         uint32_t strategy,
9966         VmaAllocationRequest* pAllocationRequest) override;
9967 
9968     VkResult CheckCorruption(const void* pBlockData) override;
9969     void Alloc(
9970         const VmaAllocationRequest& request,
9971         VmaSuballocationType type,
9972         void* userData) override;
9973 
9974     void Free(VmaAllocHandle allocHandle) override;
9975     void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override;
9976     void* GetAllocationUserData(VmaAllocHandle allocHandle) const override;
9977     VmaAllocHandle GetAllocationListBegin() const override;
9978     VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override;
9979     VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const override;
9980     void Clear() override;
9981     void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override;
9982     void DebugLogAllAllocations() const override;
9983 
9984 private:
9985     // According to original paper it should be preferable 4 or 5:
9986     // M. Masmano, I. Ripoll, A. Crespo, and J. Real "TLSF: a New Dynamic Memory Allocator for Real-Time Systems"
9987     // http://www.gii.upv.es/tlsf/files/ecrts04_tlsf.pdf
9988     static const uint8_t SECOND_LEVEL_INDEX = 5;
9989     static const uint16_t SMALL_BUFFER_SIZE = 256;
9990     static const uint32_t INITIAL_BLOCK_ALLOC_COUNT = 16;
9991     static const uint8_t MEMORY_CLASS_SHIFT = 7;
9992     static const uint8_t MAX_MEMORY_CLASSES = 65 - MEMORY_CLASS_SHIFT;
9993 
9994     class Block
9995     {
9996     public:
9997         VkDeviceSize offset;
9998         VkDeviceSize size;
9999         Block* prevPhysical;
10000         Block* nextPhysical;
10001 
MarkFree()10002         void MarkFree() { prevFree = VMA_NULL; }
MarkTaken()10003         void MarkTaken() { prevFree = this; }
IsFree()10004         bool IsFree() const { return prevFree != this; }
UserData()10005         void*& UserData() { VMA_HEAVY_ASSERT(!IsFree()); return userData; }
PrevFree()10006         Block*& PrevFree() { return prevFree; }
NextFree()10007         Block*& NextFree() { VMA_HEAVY_ASSERT(IsFree()); return nextFree; }
10008 
10009     private:
10010         Block* prevFree; // Address of the same block here indicates that block is taken
10011         union
10012         {
10013             Block* nextFree;
10014             void* userData;
10015         };
10016     };
10017 
10018     size_t m_AllocCount;
10019     // Total number of free blocks besides null block
10020     size_t m_BlocksFreeCount;
10021     // Total size of free blocks excluding null block
10022     VkDeviceSize m_BlocksFreeSize;
10023     uint32_t m_IsFreeBitmap;
10024     uint8_t m_MemoryClasses;
10025     uint32_t m_InnerIsFreeBitmap[MAX_MEMORY_CLASSES];
10026     uint32_t m_ListsCount;
10027     /*
10028     * 0: 0-3 lists for small buffers
10029     * 1+: 0-(2^SLI-1) lists for normal buffers
10030     */
10031     Block** m_FreeList;
10032     VmaPoolAllocator<Block> m_BlockAllocator;
10033     Block* m_NullBlock;
10034     VmaBlockBufferImageGranularity m_GranularityHandler;
10035 
10036     uint8_t SizeToMemoryClass(VkDeviceSize size) const;
10037     uint16_t SizeToSecondIndex(VkDeviceSize size, uint8_t memoryClass) const;
10038     uint32_t GetListIndex(uint8_t memoryClass, uint16_t secondIndex) const;
10039     uint32_t GetListIndex(VkDeviceSize size) const;
10040 
10041     void RemoveFreeBlock(Block* block);
10042     void InsertFreeBlock(Block* block);
10043     void MergeBlock(Block* block, Block* prev);
10044 
10045     Block* FindFreeBlock(VkDeviceSize size, uint32_t& listIndex) const;
10046     bool CheckBlock(
10047         Block& block,
10048         uint32_t listIndex,
10049         VkDeviceSize allocSize,
10050         VkDeviceSize allocAlignment,
10051         VmaSuballocationType allocType,
10052         VmaAllocationRequest* pAllocationRequest);
10053 };
10054 
10055 #ifndef _VMA_BLOCK_METADATA_TLSF_FUNCTIONS
VmaBlockMetadata_TLSF(const VkAllocationCallbacks * pAllocationCallbacks,VkDeviceSize bufferImageGranularity,bool isVirtual)10056 VmaBlockMetadata_TLSF::VmaBlockMetadata_TLSF(const VkAllocationCallbacks* pAllocationCallbacks,
10057     VkDeviceSize bufferImageGranularity, bool isVirtual)
10058     : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual),
10059     m_AllocCount(0),
10060     m_BlocksFreeCount(0),
10061     m_BlocksFreeSize(0),
10062     m_IsFreeBitmap(0),
10063     m_MemoryClasses(0),
10064     m_ListsCount(0),
10065     m_FreeList(VMA_NULL),
10066     m_BlockAllocator(pAllocationCallbacks, INITIAL_BLOCK_ALLOC_COUNT),
10067     m_NullBlock(VMA_NULL),
10068     m_GranularityHandler(bufferImageGranularity) {}
10069 
~VmaBlockMetadata_TLSF()10070 VmaBlockMetadata_TLSF::~VmaBlockMetadata_TLSF()
10071 {
10072     if (m_FreeList)
10073         vma_delete_array(GetAllocationCallbacks(), m_FreeList, m_ListsCount);
10074     m_GranularityHandler.Destroy(GetAllocationCallbacks());
10075 }
10076 
Init(VkDeviceSize size)10077 void VmaBlockMetadata_TLSF::Init(VkDeviceSize size)
10078 {
10079     VmaBlockMetadata::Init(size);
10080 
10081     if (!IsVirtual())
10082         m_GranularityHandler.Init(GetAllocationCallbacks(), size);
10083 
10084     m_NullBlock = m_BlockAllocator.Alloc();
10085     m_NullBlock->size = size;
10086     m_NullBlock->offset = 0;
10087     m_NullBlock->prevPhysical = VMA_NULL;
10088     m_NullBlock->nextPhysical = VMA_NULL;
10089     m_NullBlock->MarkFree();
10090     m_NullBlock->NextFree() = VMA_NULL;
10091     m_NullBlock->PrevFree() = VMA_NULL;
10092     uint8_t memoryClass = SizeToMemoryClass(size);
10093     uint16_t sli = SizeToSecondIndex(size, memoryClass);
10094     m_ListsCount = (memoryClass == 0 ? 0 : (memoryClass - 1) * (1UL << SECOND_LEVEL_INDEX) + sli) + 1;
10095     if (IsVirtual())
10096         m_ListsCount += 1UL << SECOND_LEVEL_INDEX;
10097     else
10098         m_ListsCount += 4;
10099 
10100     m_MemoryClasses = memoryClass + 2;
10101     memset(m_InnerIsFreeBitmap, 0, MAX_MEMORY_CLASSES * sizeof(uint32_t));
10102 
10103     m_FreeList = vma_new_array(GetAllocationCallbacks(), Block*, m_ListsCount);
10104     memset(m_FreeList, 0, m_ListsCount * sizeof(Block*));
10105 }
10106 
Validate()10107 bool VmaBlockMetadata_TLSF::Validate() const
10108 {
10109     VMA_VALIDATE(GetSumFreeSize() <= GetSize());
10110 
10111     VkDeviceSize calculatedSize = m_NullBlock->size;
10112     VkDeviceSize calculatedFreeSize = m_NullBlock->size;
10113     size_t allocCount = 0;
10114     size_t freeCount = 0;
10115 
10116     // Check integrity of free lists
10117     for (uint32_t list = 0; list < m_ListsCount; ++list)
10118     {
10119         Block* block = m_FreeList[list];
10120         if (block != VMA_NULL)
10121         {
10122             VMA_VALIDATE(block->IsFree());
10123             VMA_VALIDATE(block->PrevFree() == VMA_NULL);
10124             while (block->NextFree())
10125             {
10126                 VMA_VALIDATE(block->NextFree()->IsFree());
10127                 VMA_VALIDATE(block->NextFree()->PrevFree() == block);
10128                 block = block->NextFree();
10129             }
10130         }
10131     }
10132 
10133     VkDeviceSize nextOffset = m_NullBlock->offset;
10134     auto validateCtx = m_GranularityHandler.StartValidation(GetAllocationCallbacks(), IsVirtual());
10135 
10136     VMA_VALIDATE(m_NullBlock->nextPhysical == VMA_NULL);
10137     if (m_NullBlock->prevPhysical)
10138     {
10139         VMA_VALIDATE(m_NullBlock->prevPhysical->nextPhysical == m_NullBlock);
10140     }
10141     // Check all blocks
10142     for (Block* prev = m_NullBlock->prevPhysical; prev != VMA_NULL; prev = prev->prevPhysical)
10143     {
10144         VMA_VALIDATE(prev->offset + prev->size == nextOffset);
10145         nextOffset = prev->offset;
10146         calculatedSize += prev->size;
10147 
10148         uint32_t listIndex = GetListIndex(prev->size);
10149         if (prev->IsFree())
10150         {
10151             ++freeCount;
10152             // Check if free block belongs to free list
10153             Block* freeBlock = m_FreeList[listIndex];
10154             VMA_VALIDATE(freeBlock != VMA_NULL);
10155 
10156             bool found = false;
10157             do
10158             {
10159                 if (freeBlock == prev)
10160                     found = true;
10161 
10162                 freeBlock = freeBlock->NextFree();
10163             } while (!found && freeBlock != VMA_NULL);
10164 
10165             VMA_VALIDATE(found);
10166             calculatedFreeSize += prev->size;
10167         }
10168         else
10169         {
10170             ++allocCount;
10171             // Check if taken block is not on a free list
10172             Block* freeBlock = m_FreeList[listIndex];
10173             while (freeBlock)
10174             {
10175                 VMA_VALIDATE(freeBlock != prev);
10176                 freeBlock = freeBlock->NextFree();
10177             }
10178 
10179             if (!IsVirtual())
10180             {
10181                 VMA_VALIDATE(m_GranularityHandler.Validate(validateCtx, prev->offset, prev->size));
10182             }
10183         }
10184 
10185         if (prev->prevPhysical)
10186         {
10187             VMA_VALIDATE(prev->prevPhysical->nextPhysical == prev);
10188         }
10189     }
10190 
10191     if (!IsVirtual())
10192     {
10193         VMA_VALIDATE(m_GranularityHandler.FinishValidation(validateCtx));
10194     }
10195 
10196     VMA_VALIDATE(nextOffset == 0);
10197     VMA_VALIDATE(calculatedSize == GetSize());
10198     VMA_VALIDATE(calculatedFreeSize == GetSumFreeSize());
10199     VMA_VALIDATE(allocCount == m_AllocCount);
10200     VMA_VALIDATE(freeCount == m_BlocksFreeCount);
10201 
10202     return true;
10203 }
10204 
AddDetailedStatistics(VmaDetailedStatistics & inoutStats)10205 void VmaBlockMetadata_TLSF::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const
10206 {
10207     inoutStats.statistics.blockCount++;
10208     inoutStats.statistics.blockBytes += GetSize();
10209     if (m_NullBlock->size > 0)
10210         VmaAddDetailedStatisticsUnusedRange(inoutStats, m_NullBlock->size);
10211 
10212     for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)
10213     {
10214         if (block->IsFree())
10215             VmaAddDetailedStatisticsUnusedRange(inoutStats, block->size);
10216         else
10217             VmaAddDetailedStatisticsAllocation(inoutStats, block->size);
10218     }
10219 }
10220 
AddStatistics(VmaStatistics & inoutStats)10221 void VmaBlockMetadata_TLSF::AddStatistics(VmaStatistics& inoutStats) const
10222 {
10223     inoutStats.blockCount++;
10224     inoutStats.allocationCount += (uint32_t)m_AllocCount;
10225     inoutStats.blockBytes += GetSize();
10226     inoutStats.allocationBytes += GetSize() - GetSumFreeSize();
10227 }
10228 
10229 #if VMA_STATS_STRING_ENABLED
PrintDetailedMap(class VmaJsonWriter & json)10230 void VmaBlockMetadata_TLSF::PrintDetailedMap(class VmaJsonWriter& json) const
10231 {
10232     size_t blockCount = m_AllocCount + m_BlocksFreeCount;
10233     VmaStlAllocator<Block*> allocator(GetAllocationCallbacks());
10234     VmaVector<Block*, VmaStlAllocator<Block*>> blockList(blockCount, allocator);
10235 
10236     size_t i = blockCount;
10237     for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)
10238     {
10239         blockList[--i] = block;
10240     }
10241     VMA_ASSERT(i == 0);
10242 
10243     VmaDetailedStatistics stats;
10244     VmaClearDetailedStatistics(stats);
10245     AddDetailedStatistics(stats);
10246 
10247     PrintDetailedMap_Begin(json,
10248         stats.statistics.blockBytes - stats.statistics.allocationBytes,
10249         stats.statistics.allocationCount,
10250         stats.unusedRangeCount);
10251 
10252     for (; i < blockCount; ++i)
10253     {
10254         Block* block = blockList[i];
10255         if (block->IsFree())
10256             PrintDetailedMap_UnusedRange(json, block->offset, block->size);
10257         else
10258             PrintDetailedMap_Allocation(json, block->offset, block->size, block->UserData());
10259     }
10260     if (m_NullBlock->size > 0)
10261         PrintDetailedMap_UnusedRange(json, m_NullBlock->offset, m_NullBlock->size);
10262 
10263     PrintDetailedMap_End(json);
10264 }
10265 #endif
10266 
CreateAllocationRequest(VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)10267 bool VmaBlockMetadata_TLSF::CreateAllocationRequest(
10268     VkDeviceSize allocSize,
10269     VkDeviceSize allocAlignment,
10270     bool upperAddress,
10271     VmaSuballocationType allocType,
10272     uint32_t strategy,
10273     VmaAllocationRequest* pAllocationRequest)
10274 {
10275     VMA_ASSERT(allocSize > 0 && "Cannot allocate empty block!");
10276     VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
10277 
10278     // For small granularity round up
10279     if (!IsVirtual())
10280         m_GranularityHandler.RoundupAllocRequest(allocType, allocSize, allocAlignment);
10281 
10282     allocSize += GetDebugMargin();
10283     // Quick check for too small pool
10284     if (allocSize > GetSumFreeSize())
10285         return false;
10286 
10287     // If no free blocks in pool then check only null block
10288     if (m_BlocksFreeCount == 0)
10289         return CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest);
10290 
10291     // Round up to the next block
10292     VkDeviceSize sizeForNextList = allocSize;
10293     VkDeviceSize smallSizeStep = SMALL_BUFFER_SIZE / (IsVirtual() ? 1 << SECOND_LEVEL_INDEX : 4);
10294     if (allocSize > SMALL_BUFFER_SIZE)
10295     {
10296         sizeForNextList += (1ULL << (VMA_BITSCAN_MSB(allocSize) - SECOND_LEVEL_INDEX));
10297     }
10298     else if (allocSize > SMALL_BUFFER_SIZE - smallSizeStep)
10299         sizeForNextList = SMALL_BUFFER_SIZE + 1;
10300     else
10301         sizeForNextList += smallSizeStep;
10302 
10303     uint32_t nextListIndex = 0;
10304     uint32_t prevListIndex = 0;
10305     Block* nextListBlock = VMA_NULL;
10306     Block* prevListBlock = VMA_NULL;
10307 
10308     // Check blocks according to strategies
10309     if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT)
10310     {
10311         // Quick check for larger block first
10312         nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);
10313         if (nextListBlock != VMA_NULL && CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
10314             return true;
10315 
10316         // If not fitted then null block
10317         if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest))
10318             return true;
10319 
10320         // Null block failed, search larger bucket
10321         while (nextListBlock)
10322         {
10323             if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
10324                 return true;
10325             nextListBlock = nextListBlock->NextFree();
10326         }
10327 
10328         // Failed again, check best fit bucket
10329         prevListBlock = FindFreeBlock(allocSize, prevListIndex);
10330         while (prevListBlock)
10331         {
10332             if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
10333                 return true;
10334             prevListBlock = prevListBlock->NextFree();
10335         }
10336     }
10337     else if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT)
10338     {
10339         // Check best fit bucket
10340         prevListBlock = FindFreeBlock(allocSize, prevListIndex);
10341         while (prevListBlock)
10342         {
10343             if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
10344                 return true;
10345             prevListBlock = prevListBlock->NextFree();
10346         }
10347 
10348         // If failed check null block
10349         if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest))
10350             return true;
10351 
10352         // Check larger bucket
10353         nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);
10354         while (nextListBlock)
10355         {
10356             if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
10357                 return true;
10358             nextListBlock = nextListBlock->NextFree();
10359         }
10360     }
10361     else if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT )
10362     {
10363         // Perform search from the start
10364         VmaStlAllocator<Block*> allocator(GetAllocationCallbacks());
10365         VmaVector<Block*, VmaStlAllocator<Block*>> blockList(m_BlocksFreeCount, allocator);
10366 
10367         size_t i = m_BlocksFreeCount;
10368         for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)
10369         {
10370             if (block->IsFree() && block->size >= allocSize)
10371                 blockList[--i] = block;
10372         }
10373 
10374         for (; i < m_BlocksFreeCount; ++i)
10375         {
10376             Block& block = *blockList[i];
10377             if (CheckBlock(block, GetListIndex(block.size), allocSize, allocAlignment, allocType, pAllocationRequest))
10378                 return true;
10379         }
10380 
10381         // If failed check null block
10382         if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest))
10383             return true;
10384 
10385         // Whole range searched, no more memory
10386         return false;
10387     }
10388     else
10389     {
10390         // Check larger bucket
10391         nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);
10392         while (nextListBlock)
10393         {
10394             if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
10395                 return true;
10396             nextListBlock = nextListBlock->NextFree();
10397         }
10398 
10399         // If failed check null block
10400         if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest))
10401             return true;
10402 
10403         // Check best fit bucket
10404         prevListBlock = FindFreeBlock(allocSize, prevListIndex);
10405         while (prevListBlock)
10406         {
10407             if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
10408                 return true;
10409             prevListBlock = prevListBlock->NextFree();
10410         }
10411     }
10412 
10413     // Worst case, full search has to be done
10414     while (++nextListIndex < m_ListsCount)
10415     {
10416         nextListBlock = m_FreeList[nextListIndex];
10417         while (nextListBlock)
10418         {
10419             if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
10420                 return true;
10421             nextListBlock = nextListBlock->NextFree();
10422         }
10423     }
10424 
10425     // No more memory sadly
10426     return false;
10427 }
10428 
CheckCorruption(const void * pBlockData)10429 VkResult VmaBlockMetadata_TLSF::CheckCorruption(const void* pBlockData)
10430 {
10431     for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)
10432     {
10433         if (!block->IsFree())
10434         {
10435             if (!VmaValidateMagicValue(pBlockData, block->offset + block->size))
10436             {
10437                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
10438                 return VK_ERROR_UNKNOWN_COPY;
10439             }
10440         }
10441     }
10442 
10443     return VK_SUCCESS;
10444 }
10445 
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,void * userData)10446 void VmaBlockMetadata_TLSF::Alloc(
10447     const VmaAllocationRequest& request,
10448     VmaSuballocationType type,
10449     void* userData)
10450 {
10451     VMA_ASSERT(request.type == VmaAllocationRequestType::TLSF);
10452 
10453     // Get block and pop it from the free list
10454     Block* currentBlock = (Block*)request.allocHandle;
10455     VkDeviceSize offset = request.algorithmData;
10456     VMA_ASSERT(currentBlock != VMA_NULL);
10457     VMA_ASSERT(currentBlock->offset <= offset);
10458 
10459     if (currentBlock != m_NullBlock)
10460         RemoveFreeBlock(currentBlock);
10461 
10462     VkDeviceSize debugMargin = GetDebugMargin();
10463     VkDeviceSize misssingAlignment = offset - currentBlock->offset;
10464 
10465     // Append missing alignment to prev block or create new one
10466     if (misssingAlignment)
10467     {
10468         Block* prevBlock = currentBlock->prevPhysical;
10469         VMA_ASSERT(prevBlock != VMA_NULL && "There should be no missing alignment at offset 0!");
10470 
10471         if (prevBlock->IsFree() && prevBlock->size != debugMargin)
10472         {
10473             uint32_t oldList = GetListIndex(prevBlock->size);
10474             prevBlock->size += misssingAlignment;
10475             // Check if new size crosses list bucket
10476             if (oldList != GetListIndex(prevBlock->size))
10477             {
10478                 prevBlock->size -= misssingAlignment;
10479                 RemoveFreeBlock(prevBlock);
10480                 prevBlock->size += misssingAlignment;
10481                 InsertFreeBlock(prevBlock);
10482             }
10483             else
10484                 m_BlocksFreeSize += misssingAlignment;
10485         }
10486         else
10487         {
10488             Block* newBlock = m_BlockAllocator.Alloc();
10489             currentBlock->prevPhysical = newBlock;
10490             prevBlock->nextPhysical = newBlock;
10491             newBlock->prevPhysical = prevBlock;
10492             newBlock->nextPhysical = currentBlock;
10493             newBlock->size = misssingAlignment;
10494             newBlock->offset = currentBlock->offset;
10495             newBlock->MarkTaken();
10496 
10497             InsertFreeBlock(newBlock);
10498         }
10499 
10500         currentBlock->size -= misssingAlignment;
10501         currentBlock->offset += misssingAlignment;
10502     }
10503 
10504     VkDeviceSize size = request.size + debugMargin;
10505     if (currentBlock->size == size)
10506     {
10507         if (currentBlock == m_NullBlock)
10508         {
10509             // Setup new null block
10510             m_NullBlock = m_BlockAllocator.Alloc();
10511             m_NullBlock->size = 0;
10512             m_NullBlock->offset = currentBlock->offset + size;
10513             m_NullBlock->prevPhysical = currentBlock;
10514             m_NullBlock->nextPhysical = VMA_NULL;
10515             m_NullBlock->MarkFree();
10516             m_NullBlock->PrevFree() = VMA_NULL;
10517             m_NullBlock->NextFree() = VMA_NULL;
10518             currentBlock->nextPhysical = m_NullBlock;
10519             currentBlock->MarkTaken();
10520         }
10521     }
10522     else
10523     {
10524         VMA_ASSERT(currentBlock->size > size && "Proper block already found, shouldn't find smaller one!");
10525 
10526         // Create new free block
10527         Block* newBlock = m_BlockAllocator.Alloc();
10528         newBlock->size = currentBlock->size - size;
10529         newBlock->offset = currentBlock->offset + size;
10530         newBlock->prevPhysical = currentBlock;
10531         newBlock->nextPhysical = currentBlock->nextPhysical;
10532         currentBlock->nextPhysical = newBlock;
10533         currentBlock->size = size;
10534 
10535         if (currentBlock == m_NullBlock)
10536         {
10537             m_NullBlock = newBlock;
10538             m_NullBlock->MarkFree();
10539             m_NullBlock->NextFree() = VMA_NULL;
10540             m_NullBlock->PrevFree() = VMA_NULL;
10541             currentBlock->MarkTaken();
10542         }
10543         else
10544         {
10545             newBlock->nextPhysical->prevPhysical = newBlock;
10546             newBlock->MarkTaken();
10547             InsertFreeBlock(newBlock);
10548         }
10549     }
10550     currentBlock->UserData() = userData;
10551 
10552     if (debugMargin > 0)
10553     {
10554         currentBlock->size -= debugMargin;
10555         Block* newBlock = m_BlockAllocator.Alloc();
10556         newBlock->size = debugMargin;
10557         newBlock->offset = currentBlock->offset + currentBlock->size;
10558         newBlock->prevPhysical = currentBlock;
10559         newBlock->nextPhysical = currentBlock->nextPhysical;
10560         newBlock->MarkTaken();
10561         currentBlock->nextPhysical->prevPhysical = newBlock;
10562         currentBlock->nextPhysical = newBlock;
10563         InsertFreeBlock(newBlock);
10564     }
10565 
10566     if (!IsVirtual())
10567         m_GranularityHandler.AllocPages((uint8_t)(uintptr_t)request.customData,
10568             currentBlock->offset, currentBlock->size);
10569     ++m_AllocCount;
10570 }
10571 
Free(VmaAllocHandle allocHandle)10572 void VmaBlockMetadata_TLSF::Free(VmaAllocHandle allocHandle)
10573 {
10574     Block* block = (Block*)allocHandle;
10575     Block* next = block->nextPhysical;
10576     VMA_ASSERT(!block->IsFree() && "Block is already free!");
10577 
10578     if (!IsVirtual())
10579         m_GranularityHandler.FreePages(block->offset, block->size);
10580     --m_AllocCount;
10581 
10582     VkDeviceSize debugMargin = GetDebugMargin();
10583     if (debugMargin > 0)
10584     {
10585         RemoveFreeBlock(next);
10586         MergeBlock(next, block);
10587         block = next;
10588         next = next->nextPhysical;
10589     }
10590 
10591     // Try merging
10592     Block* prev = block->prevPhysical;
10593     if (prev != VMA_NULL && prev->IsFree() && prev->size != debugMargin)
10594     {
10595         RemoveFreeBlock(prev);
10596         MergeBlock(block, prev);
10597     }
10598 
10599     if (!next->IsFree())
10600         InsertFreeBlock(block);
10601     else if (next == m_NullBlock)
10602         MergeBlock(m_NullBlock, block);
10603     else
10604     {
10605         RemoveFreeBlock(next);
10606         MergeBlock(next, block);
10607         InsertFreeBlock(next);
10608     }
10609 }
10610 
GetAllocationInfo(VmaAllocHandle allocHandle,VmaVirtualAllocationInfo & outInfo)10611 void VmaBlockMetadata_TLSF::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo)
10612 {
10613     Block* block = (Block*)allocHandle;
10614     VMA_ASSERT(!block->IsFree() && "Cannot get allocation info for free block!");
10615     outInfo.offset = block->offset;
10616     outInfo.size = block->size;
10617     outInfo.pUserData = block->UserData();
10618 }
10619 
GetAllocationUserData(VmaAllocHandle allocHandle)10620 void* VmaBlockMetadata_TLSF::GetAllocationUserData(VmaAllocHandle allocHandle) const
10621 {
10622     Block* block = (Block*)allocHandle;
10623     VMA_ASSERT(!block->IsFree() && "Cannot get user data for free block!");
10624     return block->UserData();
10625 }
10626 
GetAllocationListBegin()10627 VmaAllocHandle VmaBlockMetadata_TLSF::GetAllocationListBegin() const
10628 {
10629     if (m_AllocCount == 0)
10630         return VK_NULL_HANDLE;
10631 
10632     for (Block* block = m_NullBlock->prevPhysical; block; block = block->prevPhysical)
10633     {
10634         if (!block->IsFree())
10635             return (VmaAllocHandle)block;
10636     }
10637     VMA_ASSERT(false && "If m_AllocCount > 0 then should find any allocation!");
10638     return VK_NULL_HANDLE;
10639 }
10640 
GetNextAllocation(VmaAllocHandle prevAlloc)10641 VmaAllocHandle VmaBlockMetadata_TLSF::GetNextAllocation(VmaAllocHandle prevAlloc) const
10642 {
10643     Block* startBlock = (Block*)prevAlloc;
10644     VMA_ASSERT(!startBlock->IsFree() && "Incorrect block!");
10645 
10646     for (Block* block = startBlock->prevPhysical; block; block = block->prevPhysical)
10647     {
10648         if (!block->IsFree())
10649             return (VmaAllocHandle)block;
10650     }
10651     return VK_NULL_HANDLE;
10652 }
10653 
GetNextFreeRegionSize(VmaAllocHandle alloc)10654 VkDeviceSize VmaBlockMetadata_TLSF::GetNextFreeRegionSize(VmaAllocHandle alloc) const
10655 {
10656     Block* block = (Block*)alloc;
10657     VMA_ASSERT(!block->IsFree() && "Incorrect block!");
10658 
10659     if (block->prevPhysical)
10660         return block->prevPhysical->IsFree() ? block->prevPhysical->size : 0;
10661     return 0;
10662 }
10663 
Clear()10664 void VmaBlockMetadata_TLSF::Clear()
10665 {
10666     m_AllocCount = 0;
10667     m_BlocksFreeCount = 0;
10668     m_BlocksFreeSize = 0;
10669     m_IsFreeBitmap = 0;
10670     m_NullBlock->offset = 0;
10671     m_NullBlock->size = GetSize();
10672     Block* block = m_NullBlock->prevPhysical;
10673     m_NullBlock->prevPhysical = VMA_NULL;
10674     while (block)
10675     {
10676         Block* prev = block->prevPhysical;
10677         m_BlockAllocator.Free(block);
10678         block = prev;
10679     }
10680     memset(m_FreeList, 0, m_ListsCount * sizeof(Block*));
10681     memset(m_InnerIsFreeBitmap, 0, m_MemoryClasses * sizeof(uint32_t));
10682     m_GranularityHandler.Clear();
10683 }
10684 
SetAllocationUserData(VmaAllocHandle allocHandle,void * userData)10685 void VmaBlockMetadata_TLSF::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData)
10686 {
10687     Block* block = (Block*)allocHandle;
10688     VMA_ASSERT(!block->IsFree() && "Trying to set user data for not allocated block!");
10689     block->UserData() = userData;
10690 }
10691 
DebugLogAllAllocations()10692 void VmaBlockMetadata_TLSF::DebugLogAllAllocations() const
10693 {
10694     for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)
10695         if (!block->IsFree())
10696             DebugLogAllocation(block->offset, block->size, block->UserData());
10697 }
10698 
SizeToMemoryClass(VkDeviceSize size)10699 uint8_t VmaBlockMetadata_TLSF::SizeToMemoryClass(VkDeviceSize size) const
10700 {
10701     if (size > SMALL_BUFFER_SIZE)
10702         return VMA_BITSCAN_MSB(size) - MEMORY_CLASS_SHIFT;
10703     return 0;
10704 }
10705 
SizeToSecondIndex(VkDeviceSize size,uint8_t memoryClass)10706 uint16_t VmaBlockMetadata_TLSF::SizeToSecondIndex(VkDeviceSize size, uint8_t memoryClass) const
10707 {
10708     if (memoryClass == 0)
10709     {
10710         if (IsVirtual())
10711             return static_cast<uint16_t>((size - 1) / 8);
10712         else
10713             return static_cast<uint16_t>((size - 1) / 64);
10714     }
10715     return static_cast<uint16_t>((size >> (memoryClass + MEMORY_CLASS_SHIFT - SECOND_LEVEL_INDEX)) ^ (1U << SECOND_LEVEL_INDEX));
10716 }
10717 
GetListIndex(uint8_t memoryClass,uint16_t secondIndex)10718 uint32_t VmaBlockMetadata_TLSF::GetListIndex(uint8_t memoryClass, uint16_t secondIndex) const
10719 {
10720     if (memoryClass == 0)
10721         return secondIndex;
10722 
10723     const uint32_t index = static_cast<uint32_t>(memoryClass - 1) * (1 << SECOND_LEVEL_INDEX) + secondIndex;
10724     if (IsVirtual())
10725         return index + (1 << SECOND_LEVEL_INDEX);
10726     else
10727         return index + 4;
10728 }
10729 
GetListIndex(VkDeviceSize size)10730 uint32_t VmaBlockMetadata_TLSF::GetListIndex(VkDeviceSize size) const
10731 {
10732     uint8_t memoryClass = SizeToMemoryClass(size);
10733     return GetListIndex(memoryClass, SizeToSecondIndex(size, memoryClass));
10734 }
10735 
RemoveFreeBlock(Block * block)10736 void VmaBlockMetadata_TLSF::RemoveFreeBlock(Block* block)
10737 {
10738     VMA_ASSERT(block != m_NullBlock);
10739     VMA_ASSERT(block->IsFree());
10740 
10741     if (block->NextFree() != VMA_NULL)
10742         block->NextFree()->PrevFree() = block->PrevFree();
10743     if (block->PrevFree() != VMA_NULL)
10744         block->PrevFree()->NextFree() = block->NextFree();
10745     else
10746     {
10747         uint8_t memClass = SizeToMemoryClass(block->size);
10748         uint16_t secondIndex = SizeToSecondIndex(block->size, memClass);
10749         uint32_t index = GetListIndex(memClass, secondIndex);
10750         VMA_ASSERT(m_FreeList[index] == block);
10751         m_FreeList[index] = block->NextFree();
10752         if (block->NextFree() == VMA_NULL)
10753         {
10754             m_InnerIsFreeBitmap[memClass] &= ~(1U << secondIndex);
10755             if (m_InnerIsFreeBitmap[memClass] == 0)
10756                 m_IsFreeBitmap &= ~(1UL << memClass);
10757         }
10758     }
10759     block->MarkTaken();
10760     block->UserData() = VMA_NULL;
10761     --m_BlocksFreeCount;
10762     m_BlocksFreeSize -= block->size;
10763 }
10764 
InsertFreeBlock(Block * block)10765 void VmaBlockMetadata_TLSF::InsertFreeBlock(Block* block)
10766 {
10767     VMA_ASSERT(block != m_NullBlock);
10768     VMA_ASSERT(!block->IsFree() && "Cannot insert block twice!");
10769 
10770     uint8_t memClass = SizeToMemoryClass(block->size);
10771     uint16_t secondIndex = SizeToSecondIndex(block->size, memClass);
10772     uint32_t index = GetListIndex(memClass, secondIndex);
10773     VMA_ASSERT(index < m_ListsCount);
10774     block->PrevFree() = VMA_NULL;
10775     block->NextFree() = m_FreeList[index];
10776     m_FreeList[index] = block;
10777     if (block->NextFree() != VMA_NULL)
10778         block->NextFree()->PrevFree() = block;
10779     else
10780     {
10781         m_InnerIsFreeBitmap[memClass] |= 1U << secondIndex;
10782         m_IsFreeBitmap |= 1UL << memClass;
10783     }
10784     ++m_BlocksFreeCount;
10785     m_BlocksFreeSize += block->size;
10786 }
10787 
MergeBlock(Block * block,Block * prev)10788 void VmaBlockMetadata_TLSF::MergeBlock(Block* block, Block* prev)
10789 {
10790     VMA_ASSERT(block->prevPhysical == prev && "Cannot merge seperate physical regions!");
10791     VMA_ASSERT(!prev->IsFree() && "Cannot merge block that belongs to free list!");
10792 
10793     block->offset = prev->offset;
10794     block->size += prev->size;
10795     block->prevPhysical = prev->prevPhysical;
10796     if (block->prevPhysical)
10797         block->prevPhysical->nextPhysical = block;
10798     m_BlockAllocator.Free(prev);
10799 }
10800 
FindFreeBlock(VkDeviceSize size,uint32_t & listIndex)10801 VmaBlockMetadata_TLSF::Block* VmaBlockMetadata_TLSF::FindFreeBlock(VkDeviceSize size, uint32_t& listIndex) const
10802 {
10803     uint8_t memoryClass = SizeToMemoryClass(size);
10804     uint32_t innerFreeMap = m_InnerIsFreeBitmap[memoryClass] & (~0U << SizeToSecondIndex(size, memoryClass));
10805     if (!innerFreeMap)
10806     {
10807         // Check higher levels for avaiable blocks
10808         uint32_t freeMap = m_IsFreeBitmap & (~0UL << (memoryClass + 1));
10809         if (!freeMap)
10810             return VMA_NULL; // No more memory avaible
10811 
10812         // Find lowest free region
10813         memoryClass = VMA_BITSCAN_LSB(freeMap);
10814         innerFreeMap = m_InnerIsFreeBitmap[memoryClass];
10815         VMA_ASSERT(innerFreeMap != 0);
10816     }
10817     // Find lowest free subregion
10818     listIndex = GetListIndex(memoryClass, VMA_BITSCAN_LSB(innerFreeMap));
10819     VMA_ASSERT(m_FreeList[listIndex]);
10820     return m_FreeList[listIndex];
10821 }
10822 
CheckBlock(Block & block,uint32_t listIndex,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,VmaAllocationRequest * pAllocationRequest)10823 bool VmaBlockMetadata_TLSF::CheckBlock(
10824     Block& block,
10825     uint32_t listIndex,
10826     VkDeviceSize allocSize,
10827     VkDeviceSize allocAlignment,
10828     VmaSuballocationType allocType,
10829     VmaAllocationRequest* pAllocationRequest)
10830 {
10831     VMA_ASSERT(block.IsFree() && "Block is already taken!");
10832 
10833     VkDeviceSize alignedOffset = VmaAlignUp(block.offset, allocAlignment);
10834     if (block.size < allocSize + alignedOffset - block.offset)
10835         return false;
10836 
10837     // Check for granularity conflicts
10838     if (!IsVirtual() &&
10839         m_GranularityHandler.CheckConflictAndAlignUp(alignedOffset, allocSize, block.offset, block.size, allocType))
10840         return false;
10841 
10842     // Alloc successful
10843     pAllocationRequest->type = VmaAllocationRequestType::TLSF;
10844     pAllocationRequest->allocHandle = (VmaAllocHandle)&block;
10845     pAllocationRequest->size = allocSize - GetDebugMargin();
10846     pAllocationRequest->customData = (void*)allocType;
10847     pAllocationRequest->algorithmData = alignedOffset;
10848 
10849     // Place block at the start of list if it's normal block
10850     if (listIndex != m_ListsCount && block.PrevFree())
10851     {
10852         block.PrevFree()->NextFree() = block.NextFree();
10853         if (block.NextFree())
10854             block.NextFree()->PrevFree() = block.PrevFree();
10855         block.PrevFree() = VMA_NULL;
10856         block.NextFree() = m_FreeList[listIndex];
10857         m_FreeList[listIndex] = &block;
10858         if (block.NextFree())
10859             block.NextFree()->PrevFree() = &block;
10860     }
10861 
10862     return true;
10863 }
10864 #endif // _VMA_BLOCK_METADATA_TLSF_FUNCTIONS
10865 #endif // _VMA_BLOCK_METADATA_TLSF
10866 
10867 #ifndef _VMA_BLOCK_VECTOR
10868 /*
10869 Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
10870 Vulkan memory type.
10871 
10872 Synchronized internally with a mutex.
10873 */
10874 class VmaBlockVector
10875 {
10876     friend struct VmaDefragmentationContext_T;
10877     VMA_CLASS_NO_COPY(VmaBlockVector)
10878 public:
10879     VmaBlockVector(
10880         VmaAllocator hAllocator,
10881         VmaPool hParentPool,
10882         uint32_t memoryTypeIndex,
10883         VkDeviceSize preferredBlockSize,
10884         size_t minBlockCount,
10885         size_t maxBlockCount,
10886         VkDeviceSize bufferImageGranularity,
10887         bool explicitBlockSize,
10888         uint32_t algorithm,
10889         float priority,
10890         VkDeviceSize minAllocationAlignment,
10891         void* pMemoryAllocateNext);
10892     ~VmaBlockVector();
10893 
GetAllocator()10894     VmaAllocator GetAllocator() const { return m_hAllocator; }
GetParentPool()10895     VmaPool GetParentPool() const { return m_hParentPool; }
IsCustomPool()10896     bool IsCustomPool() const { return m_hParentPool != VMA_NULL; }
GetMemoryTypeIndex()10897     uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetPreferredBlockSize()10898     VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
GetBufferImageGranularity()10899     VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
GetAlgorithm()10900     uint32_t GetAlgorithm() const { return m_Algorithm; }
HasExplicitBlockSize()10901     bool HasExplicitBlockSize() const { return m_ExplicitBlockSize; }
GetPriority()10902     float GetPriority() const { return m_Priority; }
GetAllocationNextPtr()10903     const void* GetAllocationNextPtr() const { return m_pMemoryAllocateNext; }
10904     // To be used only while the m_Mutex is locked. Used during defragmentation.
GetBlockCount()10905     size_t GetBlockCount() const { return m_Blocks.size(); }
10906     // To be used only while the m_Mutex is locked. Used during defragmentation.
GetBlock(size_t index)10907     VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
GetMutex()10908     VMA_RW_MUTEX &GetMutex() { return m_Mutex; }
10909 
10910     VkResult CreateMinBlocks();
10911     void AddStatistics(VmaStatistics& inoutStats);
10912     void AddDetailedStatistics(VmaDetailedStatistics& inoutStats);
10913     bool IsEmpty();
10914     bool IsCorruptionDetectionEnabled() const;
10915 
10916     VkResult Allocate(
10917         VkDeviceSize size,
10918         VkDeviceSize alignment,
10919         const VmaAllocationCreateInfo& createInfo,
10920         VmaSuballocationType suballocType,
10921         size_t allocationCount,
10922         VmaAllocation* pAllocations);
10923 
10924     void Free(const VmaAllocation hAllocation);
10925 
10926     void FreeEmptyBlock();
10927 
10928 #if VMA_STATS_STRING_ENABLED
10929     void PrintDetailedMap(class VmaJsonWriter& json);
10930 #endif
10931 
10932     VkResult CheckCorruption();
10933 
10934 private:
10935     const VmaAllocator m_hAllocator;
10936     const VmaPool m_hParentPool;
10937     const uint32_t m_MemoryTypeIndex;
10938     const VkDeviceSize m_PreferredBlockSize;
10939     const size_t m_MinBlockCount;
10940     const size_t m_MaxBlockCount;
10941     const VkDeviceSize m_BufferImageGranularity;
10942     const bool m_ExplicitBlockSize;
10943     const uint32_t m_Algorithm;
10944     const float m_Priority;
10945     const VkDeviceSize m_MinAllocationAlignment;
10946 
10947     void* const m_pMemoryAllocateNext;
10948     VMA_RW_MUTEX m_Mutex;
10949     // Incrementally sorted by sumFreeSize, ascending.
10950     VmaVector<VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*>> m_Blocks;
10951     uint32_t m_NextBlockId;
10952     bool m_IncrementalSort = true;
10953 
SetIncrementalSort(bool val)10954     void SetIncrementalSort(bool val) { m_IncrementalSort = val; }
10955 
10956     VkDeviceSize CalcMaxBlockSize() const;
10957     // Finds and removes given block from vector.
10958     void Remove(VmaDeviceMemoryBlock* pBlock);
10959     // Performs single step in sorting m_Blocks. They may not be fully sorted
10960     // after this call.
10961     void IncrementallySortBlocks();
10962     void SortByFreeSize();
10963 
10964     VkResult AllocatePage(
10965         VkDeviceSize size,
10966         VkDeviceSize alignment,
10967         const VmaAllocationCreateInfo& createInfo,
10968         VmaSuballocationType suballocType,
10969         VmaAllocation* pAllocation);
10970 
10971     VkResult AllocateFromBlock(
10972         VmaDeviceMemoryBlock* pBlock,
10973         VkDeviceSize size,
10974         VkDeviceSize alignment,
10975         VmaAllocationCreateFlags allocFlags,
10976         void* pUserData,
10977         VmaSuballocationType suballocType,
10978         uint32_t strategy,
10979         VmaAllocation* pAllocation);
10980 
10981     VkResult CommitAllocationRequest(
10982         VmaAllocationRequest& allocRequest,
10983         VmaDeviceMemoryBlock* pBlock,
10984         VkDeviceSize alignment,
10985         VmaAllocationCreateFlags allocFlags,
10986         void* pUserData,
10987         VmaSuballocationType suballocType,
10988         VmaAllocation* pAllocation);
10989 
10990     VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
10991     bool HasEmptyBlock();
10992 };
10993 #endif // _VMA_BLOCK_VECTOR
10994 
10995 #ifndef _VMA_DEFRAGMENTATION_CONTEXT
10996 struct VmaDefragmentationContext_T
10997 {
10998     VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
10999 public:
11000     VmaDefragmentationContext_T(
11001         VmaAllocator hAllocator,
11002         const VmaDefragmentationInfo& info);
11003     ~VmaDefragmentationContext_T();
11004 
GetStatsVmaDefragmentationContext_T11005     void GetStats(VmaDefragmentationStats& outStats) { outStats = m_GlobalStats; }
11006 
11007     VkResult DefragmentPassBegin(VmaDefragmentationPassMoveInfo& moveInfo);
11008     VkResult DefragmentPassEnd(VmaDefragmentationPassMoveInfo& moveInfo);
11009 
11010 private:
11011     // Max number of allocations to ignore due to size constraints before ending single pass
11012     static const uint8_t MAX_ALLOCS_TO_IGNORE = 16;
11013     enum class CounterStatus { Pass, Ignore, End };
11014 
11015     struct FragmentedBlock
11016     {
11017         uint32_t data;
11018         VmaDeviceMemoryBlock* block;
11019     };
11020     struct StateBalanced
11021     {
11022         VkDeviceSize avgFreeSize = 0;
11023         VkDeviceSize avgAllocSize = UINT64_MAX;
11024     };
11025     struct StateExtensive
11026     {
11027         enum class Operation : uint8_t
11028         {
11029             FindFreeBlockBuffer, FindFreeBlockTexture, FindFreeBlockAll,
11030             MoveBuffers, MoveTextures, MoveAll,
11031             Cleanup, Done
11032         };
11033 
11034         Operation operation = Operation::FindFreeBlockTexture;
11035         size_t firstFreeBlock = SIZE_MAX;
11036     };
11037     struct MoveAllocationData
11038     {
11039         VkDeviceSize size;
11040         VkDeviceSize alignment;
11041         VmaSuballocationType type;
11042         VmaAllocationCreateFlags flags;
11043         VmaDefragmentationMove move = {};
11044     };
11045 
11046     const VkDeviceSize m_MaxPassBytes;
11047     const uint32_t m_MaxPassAllocations;
11048 
11049     VmaStlAllocator<VmaDefragmentationMove> m_MoveAllocator;
11050     VmaVector<VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove>> m_Moves;
11051 
11052     uint8_t m_IgnoredAllocs = 0;
11053     uint32_t m_Algorithm;
11054     uint32_t m_BlockVectorCount;
11055     VmaBlockVector* m_PoolBlockVector;
11056     VmaBlockVector** m_pBlockVectors;
11057     size_t m_ImmovableBlockCount = 0;
11058     VmaDefragmentationStats m_GlobalStats = { 0 };
11059     VmaDefragmentationStats m_PassStats = { 0 };
11060     void* m_AlgorithmState = VMA_NULL;
11061 
11062     static MoveAllocationData GetMoveData(VmaAllocHandle handle, VmaBlockMetadata* metadata);
11063     CounterStatus CheckCounters(VkDeviceSize bytes);
11064     bool IncrementCounters(VkDeviceSize bytes);
11065     bool ReallocWithinBlock(VmaBlockVector& vector, VmaDeviceMemoryBlock* block);
11066     bool AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, VmaBlockVector& vector);
11067 
11068     bool ComputeDefragmentation(VmaBlockVector& vector, size_t index);
11069     bool ComputeDefragmentation_Fast(VmaBlockVector& vector);
11070     bool ComputeDefragmentation_Balanced(VmaBlockVector& vector, size_t index, bool update);
11071     bool ComputeDefragmentation_Full(VmaBlockVector& vector);
11072     bool ComputeDefragmentation_Extensive(VmaBlockVector& vector, size_t index);
11073 
11074     void UpdateVectorStatistics(VmaBlockVector& vector, StateBalanced& state);
11075     bool MoveDataToFreeBlocks(VmaSuballocationType currentType,
11076         VmaBlockVector& vector, size_t firstFreeBlock,
11077         bool& texturePresent, bool& bufferPresent, bool& otherPresent);
11078 };
11079 #endif // _VMA_DEFRAGMENTATION_CONTEXT
11080 
11081 #ifndef _VMA_POOL_T
11082 struct VmaPool_T
11083 {
11084     friend struct VmaPoolListItemTraits;
11085     VMA_CLASS_NO_COPY(VmaPool_T)
11086 public:
11087     VmaBlockVector m_BlockVector;
11088     VmaDedicatedAllocationList m_DedicatedAllocations;
11089 
11090     VmaPool_T(
11091         VmaAllocator hAllocator,
11092         const VmaPoolCreateInfo& createInfo,
11093         VkDeviceSize preferredBlockSize);
11094     ~VmaPool_T();
11095 
GetIdVmaPool_T11096     uint32_t GetId() const { return m_Id; }
SetIdVmaPool_T11097     void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
11098 
GetNameVmaPool_T11099     const char* GetName() const { return m_Name; }
11100     void SetName(const char* pName);
11101 
11102 #if VMA_STATS_STRING_ENABLED
11103     //void PrintDetailedMap(class VmaStringBuilder& sb);
11104 #endif
11105 
11106 private:
11107     uint32_t m_Id;
11108     char* m_Name;
11109     VmaPool_T* m_PrevPool = VMA_NULL;
11110     VmaPool_T* m_NextPool = VMA_NULL;
11111 };
11112 
11113 struct VmaPoolListItemTraits
11114 {
11115     typedef VmaPool_T ItemType;
11116 
GetPrevVmaPoolListItemTraits11117     static ItemType* GetPrev(const ItemType* item) { return item->m_PrevPool; }
GetNextVmaPoolListItemTraits11118     static ItemType* GetNext(const ItemType* item) { return item->m_NextPool; }
AccessPrevVmaPoolListItemTraits11119     static ItemType*& AccessPrev(ItemType* item) { return item->m_PrevPool; }
AccessNextVmaPoolListItemTraits11120     static ItemType*& AccessNext(ItemType* item) { return item->m_NextPool; }
11121 };
11122 #endif // _VMA_POOL_T
11123 
11124 #ifndef _VMA_CURRENT_BUDGET_DATA
11125 struct VmaCurrentBudgetData
11126 {
11127     VMA_ATOMIC_UINT32 m_BlockCount[VK_MAX_MEMORY_HEAPS];
11128     VMA_ATOMIC_UINT32 m_AllocationCount[VK_MAX_MEMORY_HEAPS];
11129     VMA_ATOMIC_UINT64 m_BlockBytes[VK_MAX_MEMORY_HEAPS];
11130     VMA_ATOMIC_UINT64 m_AllocationBytes[VK_MAX_MEMORY_HEAPS];
11131 
11132 #if VMA_MEMORY_BUDGET
11133     VMA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch;
11134     VMA_RW_MUTEX m_BudgetMutex;
11135     uint64_t m_VulkanUsage[VK_MAX_MEMORY_HEAPS];
11136     uint64_t m_VulkanBudget[VK_MAX_MEMORY_HEAPS];
11137     uint64_t m_BlockBytesAtBudgetFetch[VK_MAX_MEMORY_HEAPS];
11138 #endif // VMA_MEMORY_BUDGET
11139 
11140     VmaCurrentBudgetData();
11141 
11142     void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize);
11143     void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize);
11144 };
11145 
11146 #ifndef _VMA_CURRENT_BUDGET_DATA_FUNCTIONS
VmaCurrentBudgetData()11147 VmaCurrentBudgetData::VmaCurrentBudgetData()
11148 {
11149     for (uint32_t heapIndex = 0; heapIndex < VK_MAX_MEMORY_HEAPS; ++heapIndex)
11150     {
11151         m_BlockCount[heapIndex] = 0;
11152         m_AllocationCount[heapIndex] = 0;
11153         m_BlockBytes[heapIndex] = 0;
11154         m_AllocationBytes[heapIndex] = 0;
11155 #if VMA_MEMORY_BUDGET
11156         m_VulkanUsage[heapIndex] = 0;
11157         m_VulkanBudget[heapIndex] = 0;
11158         m_BlockBytesAtBudgetFetch[heapIndex] = 0;
11159 #endif
11160     }
11161 
11162 #if VMA_MEMORY_BUDGET
11163     m_OperationsSinceBudgetFetch = 0;
11164 #endif
11165 }
11166 
AddAllocation(uint32_t heapIndex,VkDeviceSize allocationSize)11167 void VmaCurrentBudgetData::AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
11168 {
11169     m_AllocationBytes[heapIndex] += allocationSize;
11170     ++m_AllocationCount[heapIndex];
11171 #if VMA_MEMORY_BUDGET
11172     ++m_OperationsSinceBudgetFetch;
11173 #endif
11174 }
11175 
RemoveAllocation(uint32_t heapIndex,VkDeviceSize allocationSize)11176 void VmaCurrentBudgetData::RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
11177 {
11178     VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize);
11179     m_AllocationBytes[heapIndex] -= allocationSize;
11180     VMA_ASSERT(m_AllocationCount[heapIndex] > 0);
11181     --m_AllocationCount[heapIndex];
11182 #if VMA_MEMORY_BUDGET
11183     ++m_OperationsSinceBudgetFetch;
11184 #endif
11185 }
11186 #endif // _VMA_CURRENT_BUDGET_DATA_FUNCTIONS
11187 #endif // _VMA_CURRENT_BUDGET_DATA
11188 
11189 #ifndef _VMA_ALLOCATION_OBJECT_ALLOCATOR
11190 /*
11191 Thread-safe wrapper over VmaPoolAllocator free list, for allocation of VmaAllocation_T objects.
11192 */
11193 class VmaAllocationObjectAllocator
11194 {
VMA_CLASS_NO_COPY(VmaAllocationObjectAllocator)11195     VMA_CLASS_NO_COPY(VmaAllocationObjectAllocator)
11196 public:
11197     VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks)
11198         : m_Allocator(pAllocationCallbacks, 1024) {}
11199 
11200     template<typename... Types> VmaAllocation Allocate(Types&&... args);
11201     void Free(VmaAllocation hAlloc);
11202 
11203 private:
11204     VMA_MUTEX m_Mutex;
11205     VmaPoolAllocator<VmaAllocation_T> m_Allocator;
11206 };
11207 
11208 template<typename... Types>
Allocate(Types &&...args)11209 VmaAllocation VmaAllocationObjectAllocator::Allocate(Types&&... args)
11210 {
11211     VmaMutexLock mutexLock(m_Mutex);
11212     return m_Allocator.Alloc<Types...>(std::forward<Types>(args)...);
11213 }
11214 
Free(VmaAllocation hAlloc)11215 void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc)
11216 {
11217     VmaMutexLock mutexLock(m_Mutex);
11218     m_Allocator.Free(hAlloc);
11219 }
11220 #endif // _VMA_ALLOCATION_OBJECT_ALLOCATOR
11221 
11222 #ifndef _VMA_VIRTUAL_BLOCK_T
11223 struct VmaVirtualBlock_T
11224 {
11225     VMA_CLASS_NO_COPY(VmaVirtualBlock_T)
11226 public:
11227     const bool m_AllocationCallbacksSpecified;
11228     const VkAllocationCallbacks m_AllocationCallbacks;
11229 
11230     VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo);
11231     ~VmaVirtualBlock_T();
11232 
InitVmaVirtualBlock_T11233     VkResult Init() { return VK_SUCCESS; }
IsEmptyVmaVirtualBlock_T11234     bool IsEmpty() const { return m_Metadata->IsEmpty(); }
FreeVmaVirtualBlock_T11235     void Free(VmaVirtualAllocation allocation) { m_Metadata->Free((VmaAllocHandle)allocation); }
SetAllocationUserDataVmaVirtualBlock_T11236     void SetAllocationUserData(VmaVirtualAllocation allocation, void* userData) { m_Metadata->SetAllocationUserData((VmaAllocHandle)allocation, userData); }
ClearVmaVirtualBlock_T11237     void Clear() { m_Metadata->Clear(); }
11238 
11239     const VkAllocationCallbacks* GetAllocationCallbacks() const;
11240     void GetAllocationInfo(VmaVirtualAllocation allocation, VmaVirtualAllocationInfo& outInfo);
11241     VkResult Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VmaVirtualAllocation& outAllocation,
11242         VkDeviceSize* outOffset);
11243     void GetStatistics(VmaStatistics& outStats) const;
11244     void CalculateDetailedStatistics(VmaDetailedStatistics& outStats) const;
11245 #if VMA_STATS_STRING_ENABLED
11246     void BuildStatsString(bool detailedMap, VmaStringBuilder& sb) const;
11247 #endif
11248 
11249 private:
11250     VmaBlockMetadata* m_Metadata;
11251 };
11252 
11253 #ifndef _VMA_VIRTUAL_BLOCK_T_FUNCTIONS
VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo & createInfo)11254 VmaVirtualBlock_T::VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo)
11255     : m_AllocationCallbacksSpecified(createInfo.pAllocationCallbacks != VMA_NULL),
11256     m_AllocationCallbacks(createInfo.pAllocationCallbacks != VMA_NULL ? *createInfo.pAllocationCallbacks : VmaEmptyAllocationCallbacks)
11257 {
11258     const uint32_t algorithm = createInfo.flags & VMA_VIRTUAL_BLOCK_CREATE_ALGORITHM_MASK;
11259     switch (algorithm)
11260     {
11261     default:
11262         VMA_ASSERT(0);
11263     case 0:
11264         m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_TLSF)(VK_NULL_HANDLE, 1, true);
11265         break;
11266     case VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT:
11267         m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_Linear)(VK_NULL_HANDLE, 1, true);
11268         break;
11269     }
11270 
11271     m_Metadata->Init(createInfo.size);
11272 }
11273 
~VmaVirtualBlock_T()11274 VmaVirtualBlock_T::~VmaVirtualBlock_T()
11275 {
11276     // Define macro VMA_DEBUG_LOG to receive the list of the unfreed allocations
11277     if (!m_Metadata->IsEmpty())
11278         m_Metadata->DebugLogAllAllocations();
11279     // This is the most important assert in the entire library.
11280     // Hitting it means you have some memory leak - unreleased virtual allocations.
11281     VMA_ASSERT(m_Metadata->IsEmpty() && "Some virtual allocations were not freed before destruction of this virtual block!");
11282 
11283     vma_delete(GetAllocationCallbacks(), m_Metadata);
11284 }
11285 
GetAllocationCallbacks()11286 const VkAllocationCallbacks* VmaVirtualBlock_T::GetAllocationCallbacks() const
11287 {
11288     return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL;
11289 }
11290 
GetAllocationInfo(VmaVirtualAllocation allocation,VmaVirtualAllocationInfo & outInfo)11291 void VmaVirtualBlock_T::GetAllocationInfo(VmaVirtualAllocation allocation, VmaVirtualAllocationInfo& outInfo)
11292 {
11293     m_Metadata->GetAllocationInfo((VmaAllocHandle)allocation, outInfo);
11294 }
11295 
Allocate(const VmaVirtualAllocationCreateInfo & createInfo,VmaVirtualAllocation & outAllocation,VkDeviceSize * outOffset)11296 VkResult VmaVirtualBlock_T::Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VmaVirtualAllocation& outAllocation,
11297     VkDeviceSize* outOffset)
11298 {
11299     VmaAllocationRequest request = {};
11300     if (m_Metadata->CreateAllocationRequest(
11301         createInfo.size, // allocSize
11302         VMA_MAX(createInfo.alignment, (VkDeviceSize)1), // allocAlignment
11303         (createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0, // upperAddress
11304         VMA_SUBALLOCATION_TYPE_UNKNOWN, // allocType - unimportant
11305         createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK, // strategy
11306         &request))
11307     {
11308         m_Metadata->Alloc(request,
11309             VMA_SUBALLOCATION_TYPE_UNKNOWN, // type - unimportant
11310             createInfo.pUserData);
11311         outAllocation = (VmaVirtualAllocation)request.allocHandle;
11312         if(outOffset)
11313             *outOffset = m_Metadata->GetAllocationOffset(request.allocHandle);
11314         return VK_SUCCESS;
11315     }
11316     outAllocation = (VmaVirtualAllocation)VK_NULL_HANDLE;
11317     if (outOffset)
11318         *outOffset = UINT64_MAX;
11319     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11320 }
11321 
GetStatistics(VmaStatistics & outStats)11322 void VmaVirtualBlock_T::GetStatistics(VmaStatistics& outStats) const
11323 {
11324     VmaClearStatistics(outStats);
11325     m_Metadata->AddStatistics(outStats);
11326 }
11327 
CalculateDetailedStatistics(VmaDetailedStatistics & outStats)11328 void VmaVirtualBlock_T::CalculateDetailedStatistics(VmaDetailedStatistics& outStats) const
11329 {
11330     VmaClearDetailedStatistics(outStats);
11331     m_Metadata->AddDetailedStatistics(outStats);
11332 }
11333 
11334 #if VMA_STATS_STRING_ENABLED
BuildStatsString(bool detailedMap,VmaStringBuilder & sb)11335 void VmaVirtualBlock_T::BuildStatsString(bool detailedMap, VmaStringBuilder& sb) const
11336 {
11337     VmaJsonWriter json(GetAllocationCallbacks(), sb);
11338     json.BeginObject();
11339 
11340     VmaDetailedStatistics stats;
11341     CalculateDetailedStatistics(stats);
11342 
11343     json.WriteString("Stats");
11344     VmaPrintDetailedStatistics(json, stats);
11345 
11346     if (detailedMap)
11347     {
11348         json.WriteString("Details");
11349         json.BeginObject();
11350         m_Metadata->PrintDetailedMap(json);
11351         json.EndObject();
11352     }
11353 
11354     json.EndObject();
11355 }
11356 #endif // VMA_STATS_STRING_ENABLED
11357 #endif // _VMA_VIRTUAL_BLOCK_T_FUNCTIONS
11358 #endif // _VMA_VIRTUAL_BLOCK_T
11359 
11360 
11361 // Main allocator object.
11362 struct VmaAllocator_T
11363 {
11364     VMA_CLASS_NO_COPY(VmaAllocator_T)
11365 public:
11366     bool m_UseMutex;
11367     uint32_t m_VulkanApiVersion;
11368     bool m_UseKhrDedicatedAllocation; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
11369     bool m_UseKhrBindMemory2; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
11370     bool m_UseExtMemoryBudget;
11371     bool m_UseAmdDeviceCoherentMemory;
11372     bool m_UseKhrBufferDeviceAddress;
11373     bool m_UseExtMemoryPriority;
11374     VkDevice m_hDevice;
11375     VkInstance m_hInstance;
11376     bool m_AllocationCallbacksSpecified;
11377     VkAllocationCallbacks m_AllocationCallbacks;
11378     VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
11379     VmaAllocationObjectAllocator m_AllocationObjectAllocator;
11380 
11381     // Each bit (1 << i) is set if HeapSizeLimit is enabled for that heap, so cannot allocate more than the heap size.
11382     uint32_t m_HeapSizeLimitMask;
11383 
11384     VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
11385     VkPhysicalDeviceMemoryProperties m_MemProps;
11386 
11387     // Default pools.
11388     VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
11389     VmaDedicatedAllocationList m_DedicatedAllocations[VK_MAX_MEMORY_TYPES];
11390 
11391     VmaCurrentBudgetData m_Budget;
11392     VMA_ATOMIC_UINT32 m_DeviceMemoryCount; // Total number of VkDeviceMemory objects.
11393 
11394     VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
11395     VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
11396     ~VmaAllocator_T();
11397 
GetAllocationCallbacksVmaAllocator_T11398     const VkAllocationCallbacks* GetAllocationCallbacks() const
11399     {
11400         return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL;
11401     }
GetVulkanFunctionsVmaAllocator_T11402     const VmaVulkanFunctions& GetVulkanFunctions() const
11403     {
11404         return m_VulkanFunctions;
11405     }
11406 
GetPhysicalDeviceVmaAllocator_T11407     VkPhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; }
11408 
GetBufferImageGranularityVmaAllocator_T11409     VkDeviceSize GetBufferImageGranularity() const
11410     {
11411         return VMA_MAX(
11412             static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
11413             m_PhysicalDeviceProperties.limits.bufferImageGranularity);
11414     }
11415 
GetMemoryHeapCountVmaAllocator_T11416     uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
GetMemoryTypeCountVmaAllocator_T11417     uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
11418 
MemoryTypeIndexToHeapIndexVmaAllocator_T11419     uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
11420     {
11421         VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
11422         return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
11423     }
11424     // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
IsMemoryTypeNonCoherentVmaAllocator_T11425     bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
11426     {
11427         return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
11428             VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
11429     }
11430     // Minimum alignment for all allocations in specific memory type.
GetMemoryTypeMinAlignmentVmaAllocator_T11431     VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
11432     {
11433         return IsMemoryTypeNonCoherent(memTypeIndex) ?
11434             VMA_MAX((VkDeviceSize)VMA_MIN_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
11435             (VkDeviceSize)VMA_MIN_ALIGNMENT;
11436     }
11437 
IsIntegratedGpuVmaAllocator_T11438     bool IsIntegratedGpu() const
11439     {
11440         return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
11441     }
11442 
GetGlobalMemoryTypeBitsVmaAllocator_T11443     uint32_t GetGlobalMemoryTypeBits() const { return m_GlobalMemoryTypeBits; }
11444 
11445     void GetBufferMemoryRequirements(
11446         VkBuffer hBuffer,
11447         VkMemoryRequirements& memReq,
11448         bool& requiresDedicatedAllocation,
11449         bool& prefersDedicatedAllocation) const;
11450     void GetImageMemoryRequirements(
11451         VkImage hImage,
11452         VkMemoryRequirements& memReq,
11453         bool& requiresDedicatedAllocation,
11454         bool& prefersDedicatedAllocation) const;
11455     VkResult FindMemoryTypeIndex(
11456         uint32_t memoryTypeBits,
11457         const VmaAllocationCreateInfo* pAllocationCreateInfo,
11458         VkFlags bufImgUsage, // VkBufferCreateInfo::usage or VkImageCreateInfo::usage. UINT32_MAX if unknown.
11459         uint32_t* pMemoryTypeIndex) const;
11460 
11461     // Main allocation function.
11462     VkResult AllocateMemory(
11463         const VkMemoryRequirements& vkMemReq,
11464         bool requiresDedicatedAllocation,
11465         bool prefersDedicatedAllocation,
11466         VkBuffer dedicatedBuffer,
11467         VkImage dedicatedImage,
11468         VkFlags dedicatedBufferImageUsage, // UINT32_MAX if unknown.
11469         const VmaAllocationCreateInfo& createInfo,
11470         VmaSuballocationType suballocType,
11471         size_t allocationCount,
11472         VmaAllocation* pAllocations);
11473 
11474     // Main deallocation function.
11475     void FreeMemory(
11476         size_t allocationCount,
11477         const VmaAllocation* pAllocations);
11478 
11479     void CalculateStatistics(VmaTotalStatistics* pStats);
11480 
11481     void GetHeapBudgets(
11482         VmaBudget* outBudgets, uint32_t firstHeap, uint32_t heapCount);
11483 
11484 #if VMA_STATS_STRING_ENABLED
11485     void PrintDetailedMap(class VmaJsonWriter& json);
11486 #endif
11487 
11488     void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
11489 
11490     VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
11491     void DestroyPool(VmaPool pool);
11492     void GetPoolStatistics(VmaPool pool, VmaStatistics* pPoolStats);
11493     void CalculatePoolStatistics(VmaPool pool, VmaDetailedStatistics* pPoolStats);
11494 
11495     void SetCurrentFrameIndex(uint32_t frameIndex);
GetCurrentFrameIndexVmaAllocator_T11496     uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
11497 
11498     VkResult CheckPoolCorruption(VmaPool hPool);
11499     VkResult CheckCorruption(uint32_t memoryTypeBits);
11500 
11501     // Call to Vulkan function vkAllocateMemory with accompanying bookkeeping.
11502     VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
11503     // Call to Vulkan function vkFreeMemory with accompanying bookkeeping.
11504     void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
11505     // Call to Vulkan function vkBindBufferMemory or vkBindBufferMemory2KHR.
11506     VkResult BindVulkanBuffer(
11507         VkDeviceMemory memory,
11508         VkDeviceSize memoryOffset,
11509         VkBuffer buffer,
11510         const void* pNext);
11511     // Call to Vulkan function vkBindImageMemory or vkBindImageMemory2KHR.
11512     VkResult BindVulkanImage(
11513         VkDeviceMemory memory,
11514         VkDeviceSize memoryOffset,
11515         VkImage image,
11516         const void* pNext);
11517 
11518     VkResult Map(VmaAllocation hAllocation, void** ppData);
11519     void Unmap(VmaAllocation hAllocation);
11520 
11521     VkResult BindBufferMemory(
11522         VmaAllocation hAllocation,
11523         VkDeviceSize allocationLocalOffset,
11524         VkBuffer hBuffer,
11525         const void* pNext);
11526     VkResult BindImageMemory(
11527         VmaAllocation hAllocation,
11528         VkDeviceSize allocationLocalOffset,
11529         VkImage hImage,
11530         const void* pNext);
11531 
11532     VkResult FlushOrInvalidateAllocation(
11533         VmaAllocation hAllocation,
11534         VkDeviceSize offset, VkDeviceSize size,
11535         VMA_CACHE_OPERATION op);
11536     VkResult FlushOrInvalidateAllocations(
11537         uint32_t allocationCount,
11538         const VmaAllocation* allocations,
11539         const VkDeviceSize* offsets, const VkDeviceSize* sizes,
11540         VMA_CACHE_OPERATION op);
11541 
11542     void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
11543 
11544     /*
11545     Returns bit mask of memory types that can support defragmentation on GPU as
11546     they support creation of required buffer for copy operations.
11547     */
11548     uint32_t GetGpuDefragmentationMemoryTypeBits();
11549 
11550 #if VMA_EXTERNAL_MEMORY
GetExternalMemoryHandleTypeFlagsVmaAllocator_T11551     VkExternalMemoryHandleTypeFlagsKHR GetExternalMemoryHandleTypeFlags(uint32_t memTypeIndex) const
11552     {
11553         return m_TypeExternalMemoryHandleTypes[memTypeIndex];
11554     }
11555 #endif // #if VMA_EXTERNAL_MEMORY
11556 
11557     void FreeEmptyBlock();
11558 
11559 private:
11560     VkDeviceSize m_PreferredLargeHeapBlockSize;
11561 
11562     VkPhysicalDevice m_PhysicalDevice;
11563     VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
11564     VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized.
11565 #if VMA_EXTERNAL_MEMORY
11566     VkExternalMemoryHandleTypeFlagsKHR m_TypeExternalMemoryHandleTypes[VK_MAX_MEMORY_TYPES];
11567 #endif // #if VMA_EXTERNAL_MEMORY
11568 
11569     VMA_RW_MUTEX m_PoolsMutex;
11570     typedef VmaIntrusiveLinkedList<VmaPoolListItemTraits> PoolList;
11571     // Protected by m_PoolsMutex.
11572     PoolList m_Pools;
11573     uint32_t m_NextPoolId;
11574 
11575     VmaVulkanFunctions m_VulkanFunctions;
11576 
11577     // Global bit mask AND-ed with any memoryTypeBits to disallow certain memory types.
11578     uint32_t m_GlobalMemoryTypeBits;
11579 
11580     void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
11581 
11582 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
11583     void ImportVulkanFunctions_Static();
11584 #endif
11585 
11586     void ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions);
11587 
11588 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
11589     void ImportVulkanFunctions_Dynamic();
11590 #endif
11591 
11592     void ValidateVulkanFunctions();
11593 
11594     VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
11595 
11596     VkResult AllocateMemoryOfType(
11597         VmaPool pool,
11598         VkDeviceSize size,
11599         VkDeviceSize alignment,
11600         bool dedicatedPreferred,
11601         VkBuffer dedicatedBuffer,
11602         VkImage dedicatedImage,
11603         VkFlags dedicatedBufferImageUsage,
11604         const VmaAllocationCreateInfo& createInfo,
11605         uint32_t memTypeIndex,
11606         VmaSuballocationType suballocType,
11607         VmaDedicatedAllocationList& dedicatedAllocations,
11608         VmaBlockVector& blockVector,
11609         size_t allocationCount,
11610         VmaAllocation* pAllocations);
11611 
11612     // Helper function only to be used inside AllocateDedicatedMemory.
11613     VkResult AllocateDedicatedMemoryPage(
11614         VmaPool pool,
11615         VkDeviceSize size,
11616         VmaSuballocationType suballocType,
11617         uint32_t memTypeIndex,
11618         const VkMemoryAllocateInfo& allocInfo,
11619         bool map,
11620         bool isUserDataString,
11621         bool isMappingAllowed,
11622         void* pUserData,
11623         VmaAllocation* pAllocation);
11624 
11625     // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
11626     VkResult AllocateDedicatedMemory(
11627         VmaPool pool,
11628         VkDeviceSize size,
11629         VmaSuballocationType suballocType,
11630         VmaDedicatedAllocationList& dedicatedAllocations,
11631         uint32_t memTypeIndex,
11632         bool map,
11633         bool isUserDataString,
11634         bool isMappingAllowed,
11635         bool canAliasMemory,
11636         void* pUserData,
11637         float priority,
11638         VkBuffer dedicatedBuffer,
11639         VkImage dedicatedImage,
11640         VkFlags dedicatedBufferImageUsage,
11641         size_t allocationCount,
11642         VmaAllocation* pAllocations,
11643         const void* pNextChain = nullptr);
11644 
11645     void FreeDedicatedMemory(const VmaAllocation allocation);
11646 
11647     VkResult CalcMemTypeParams(
11648         VmaAllocationCreateInfo& outCreateInfo,
11649         uint32_t memTypeIndex,
11650         VkDeviceSize size,
11651         size_t allocationCount);
11652     VkResult CalcAllocationParams(
11653         VmaAllocationCreateInfo& outCreateInfo,
11654         bool dedicatedRequired,
11655         bool dedicatedPreferred);
11656 
11657     /*
11658     Calculates and returns bit mask of memory types that can support defragmentation
11659     on GPU as they support creation of required buffer for copy operations.
11660     */
11661     uint32_t CalculateGpuDefragmentationMemoryTypeBits() const;
11662     uint32_t CalculateGlobalMemoryTypeBits() const;
11663 
11664     bool GetFlushOrInvalidateRange(
11665         VmaAllocation allocation,
11666         VkDeviceSize offset, VkDeviceSize size,
11667         VkMappedMemoryRange& outRange) const;
11668 
11669 #if VMA_MEMORY_BUDGET
11670     void UpdateVulkanBudget();
11671 #endif // #if VMA_MEMORY_BUDGET
11672 };
11673 
11674 
11675 #ifndef _VMA_MEMORY_FUNCTIONS
VmaMalloc(VmaAllocator hAllocator,size_t size,size_t alignment)11676 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
11677 {
11678     return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
11679 }
11680 
VmaFree(VmaAllocator hAllocator,void * ptr)11681 static void VmaFree(VmaAllocator hAllocator, void* ptr)
11682 {
11683     VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
11684 }
11685 
11686 template<typename T>
VmaAllocate(VmaAllocator hAllocator)11687 static T* VmaAllocate(VmaAllocator hAllocator)
11688 {
11689     return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
11690 }
11691 
11692 template<typename T>
VmaAllocateArray(VmaAllocator hAllocator,size_t count)11693 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
11694 {
11695     return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
11696 }
11697 
11698 template<typename T>
vma_delete(VmaAllocator hAllocator,T * ptr)11699 static void vma_delete(VmaAllocator hAllocator, T* ptr)
11700 {
11701     if(ptr != VMA_NULL)
11702     {
11703         ptr->~T();
11704         VmaFree(hAllocator, ptr);
11705     }
11706 }
11707 
11708 template<typename T>
vma_delete_array(VmaAllocator hAllocator,T * ptr,size_t count)11709 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
11710 {
11711     if(ptr != VMA_NULL)
11712     {
11713         for(size_t i = count; i--; )
11714             ptr[i].~T();
11715         VmaFree(hAllocator, ptr);
11716     }
11717 }
11718 #endif // _VMA_MEMORY_FUNCTIONS
11719 
11720 #ifndef _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS
VmaDeviceMemoryBlock(VmaAllocator hAllocator)11721 VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator)
11722     : m_pMetadata(VMA_NULL),
11723     m_MemoryTypeIndex(UINT32_MAX),
11724     m_Id(0),
11725     m_hMemory(VK_NULL_HANDLE),
11726     m_MapCount(0),
11727     m_pMappedData(VMA_NULL) {}
11728 
~VmaDeviceMemoryBlock()11729 VmaDeviceMemoryBlock::~VmaDeviceMemoryBlock()
11730 {
11731     VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
11732     VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
11733 }
11734 
Init(VmaAllocator hAllocator,VmaPool hParentPool,uint32_t newMemoryTypeIndex,VkDeviceMemory newMemory,VkDeviceSize newSize,uint32_t id,uint32_t algorithm,VkDeviceSize bufferImageGranularity)11735 void VmaDeviceMemoryBlock::Init(
11736     VmaAllocator hAllocator,
11737     VmaPool hParentPool,
11738     uint32_t newMemoryTypeIndex,
11739     VkDeviceMemory newMemory,
11740     VkDeviceSize newSize,
11741     uint32_t id,
11742     uint32_t algorithm,
11743     VkDeviceSize bufferImageGranularity)
11744 {
11745     VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
11746 
11747     m_hParentPool = hParentPool;
11748     m_MemoryTypeIndex = newMemoryTypeIndex;
11749     m_Id = id;
11750     m_hMemory = newMemory;
11751 
11752     switch (algorithm)
11753     {
11754     case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
11755         m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator->GetAllocationCallbacks(),
11756             bufferImageGranularity, false); // isVirtual
11757         break;
11758     default:
11759         VMA_ASSERT(0);
11760         // Fall-through.
11761     case 0:
11762         m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_TLSF)(hAllocator->GetAllocationCallbacks(),
11763             bufferImageGranularity, false); // isVirtual
11764     }
11765     m_pMetadata->Init(newSize);
11766 }
11767 
Destroy(VmaAllocator allocator)11768 void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
11769 {
11770     // Define macro VMA_DEBUG_LOG to receive the list of the unfreed allocations
11771     if (!m_pMetadata->IsEmpty())
11772         m_pMetadata->DebugLogAllAllocations();
11773     // This is the most important assert in the entire library.
11774     // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
11775     VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
11776 
11777     VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
11778     allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
11779     m_hMemory = VK_NULL_HANDLE;
11780 
11781     vma_delete(allocator, m_pMetadata);
11782     m_pMetadata = VMA_NULL;
11783 }
11784 
PostFree(VmaAllocator hAllocator)11785 void VmaDeviceMemoryBlock::PostFree(VmaAllocator hAllocator)
11786 {
11787     if(m_MappingHysteresis.PostFree())
11788     {
11789         VMA_ASSERT(m_MappingHysteresis.GetExtraMapping() == 0);
11790         if (m_MapCount == 0)
11791         {
11792             m_pMappedData = VMA_NULL;
11793             (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
11794         }
11795     }
11796 }
11797 
Validate()11798 bool VmaDeviceMemoryBlock::Validate() const
11799 {
11800     VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
11801         (m_pMetadata->GetSize() != 0));
11802 
11803     return m_pMetadata->Validate();
11804 }
11805 
CheckCorruption(VmaAllocator hAllocator)11806 VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
11807 {
11808     void* pData = nullptr;
11809     VkResult res = Map(hAllocator, 1, &pData);
11810     if (res != VK_SUCCESS)
11811     {
11812         return res;
11813     }
11814 
11815     res = m_pMetadata->CheckCorruption(pData);
11816 
11817     Unmap(hAllocator, 1);
11818 
11819     return res;
11820 }
11821 
Map(VmaAllocator hAllocator,uint32_t count,void ** ppData)11822 VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
11823 {
11824     if (count == 0)
11825     {
11826         return VK_SUCCESS;
11827     }
11828 
11829     VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);
11830     const uint32_t oldTotalMapCount = m_MapCount + m_MappingHysteresis.GetExtraMapping();
11831     m_MappingHysteresis.PostMap();
11832     if (oldTotalMapCount != 0)
11833     {
11834         m_MapCount += count;
11835         VMA_ASSERT(m_pMappedData != VMA_NULL);
11836         if (ppData != VMA_NULL)
11837         {
11838             *ppData = m_pMappedData;
11839         }
11840         return VK_SUCCESS;
11841     }
11842     else
11843     {
11844         VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
11845             hAllocator->m_hDevice,
11846             m_hMemory,
11847             0, // offset
11848             VK_WHOLE_SIZE,
11849             0, // flags
11850             &m_pMappedData);
11851         if (result == VK_SUCCESS)
11852         {
11853             if (ppData != VMA_NULL)
11854             {
11855                 *ppData = m_pMappedData;
11856             }
11857             m_MapCount = count;
11858         }
11859         return result;
11860     }
11861 }
11862 
Unmap(VmaAllocator hAllocator,uint32_t count)11863 void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
11864 {
11865     if (count == 0)
11866     {
11867         return;
11868     }
11869 
11870     VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);
11871     if (m_MapCount >= count)
11872     {
11873         m_MapCount -= count;
11874         const uint32_t totalMapCount = m_MapCount + m_MappingHysteresis.GetExtraMapping();
11875         if (totalMapCount == 0)
11876         {
11877             m_pMappedData = VMA_NULL;
11878             (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
11879         }
11880         m_MappingHysteresis.PostUnmap();
11881     }
11882     else
11883     {
11884         VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
11885     }
11886 }
11887 
WriteMagicValueAfterAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)11888 VkResult VmaDeviceMemoryBlock::WriteMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
11889 {
11890     VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
11891 
11892     void* pData;
11893     VkResult res = Map(hAllocator, 1, &pData);
11894     if (res != VK_SUCCESS)
11895     {
11896         return res;
11897     }
11898 
11899     VmaWriteMagicValue(pData, allocOffset + allocSize);
11900 
11901     Unmap(hAllocator, 1);
11902     return VK_SUCCESS;
11903 }
11904 
ValidateMagicValueAfterAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)11905 VkResult VmaDeviceMemoryBlock::ValidateMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
11906 {
11907     VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
11908 
11909     void* pData;
11910     VkResult res = Map(hAllocator, 1, &pData);
11911     if (res != VK_SUCCESS)
11912     {
11913         return res;
11914     }
11915 
11916     if (!VmaValidateMagicValue(pData, allocOffset + allocSize))
11917     {
11918         VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
11919     }
11920 
11921     Unmap(hAllocator, 1);
11922     return VK_SUCCESS;
11923 }
11924 
BindBufferMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkBuffer hBuffer,const void * pNext)11925 VkResult VmaDeviceMemoryBlock::BindBufferMemory(
11926     const VmaAllocator hAllocator,
11927     const VmaAllocation hAllocation,
11928     VkDeviceSize allocationLocalOffset,
11929     VkBuffer hBuffer,
11930     const void* pNext)
11931 {
11932     VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
11933         hAllocation->GetBlock() == this);
11934     VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
11935         "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
11936     const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
11937     // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
11938     VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);
11939     return hAllocator->BindVulkanBuffer(m_hMemory, memoryOffset, hBuffer, pNext);
11940 }
11941 
BindImageMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkImage hImage,const void * pNext)11942 VkResult VmaDeviceMemoryBlock::BindImageMemory(
11943     const VmaAllocator hAllocator,
11944     const VmaAllocation hAllocation,
11945     VkDeviceSize allocationLocalOffset,
11946     VkImage hImage,
11947     const void* pNext)
11948 {
11949     VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
11950         hAllocation->GetBlock() == this);
11951     VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
11952         "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
11953     const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
11954     // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
11955     VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);
11956     return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext);
11957 }
11958 #endif // _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS
11959 
11960 #ifndef _VMA_ALLOCATION_T_FUNCTIONS
VmaAllocation_T(bool mappingAllowed)11961 VmaAllocation_T::VmaAllocation_T(bool mappingAllowed)
11962     : m_Alignment{ 1 },
11963     m_Size{ 0 },
11964     m_pUserData{ VMA_NULL },
11965     m_pName{ VMA_NULL },
11966     m_MemoryTypeIndex{ 0 },
11967     m_Type{ (uint8_t)ALLOCATION_TYPE_NONE },
11968     m_SuballocationType{ (uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN },
11969     m_MapCount{ 0 },
11970     m_Flags{ 0 }
11971 {
11972     if(mappingAllowed)
11973         m_Flags |= (uint8_t)FLAG_MAPPING_ALLOWED;
11974 
11975 #if VMA_STATS_STRING_ENABLED
11976     m_BufferImageUsage = 0;
11977 #endif
11978 }
11979 
~VmaAllocation_T()11980 VmaAllocation_T::~VmaAllocation_T()
11981 {
11982     VMA_ASSERT(m_MapCount == 0 && "Allocation was not unmapped before destruction.");
11983 
11984     // Check if owned string was freed.
11985     VMA_ASSERT(m_pName == VMA_NULL);
11986 }
11987 
InitBlockAllocation(VmaDeviceMemoryBlock * block,VmaAllocHandle allocHandle,VkDeviceSize alignment,VkDeviceSize size,uint32_t memoryTypeIndex,VmaSuballocationType suballocationType,bool mapped)11988 void VmaAllocation_T::InitBlockAllocation(
11989     VmaDeviceMemoryBlock* block,
11990     VmaAllocHandle allocHandle,
11991     VkDeviceSize alignment,
11992     VkDeviceSize size,
11993     uint32_t memoryTypeIndex,
11994     VmaSuballocationType suballocationType,
11995     bool mapped)
11996 {
11997     VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
11998     VMA_ASSERT(block != VMA_NULL);
11999     m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
12000     m_Alignment = alignment;
12001     m_Size = size;
12002     m_MemoryTypeIndex = memoryTypeIndex;
12003     if(mapped)
12004     {
12005         VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");
12006         m_Flags |= (uint8_t)FLAG_PERSISTENT_MAP;
12007     }
12008     m_SuballocationType = (uint8_t)suballocationType;
12009     m_BlockAllocation.m_Block = block;
12010     m_BlockAllocation.m_AllocHandle = allocHandle;
12011 }
12012 
InitDedicatedAllocation(VmaPool hParentPool,uint32_t memoryTypeIndex,VkDeviceMemory hMemory,VmaSuballocationType suballocationType,void * pMappedData,VkDeviceSize size)12013 void VmaAllocation_T::InitDedicatedAllocation(
12014     VmaPool hParentPool,
12015     uint32_t memoryTypeIndex,
12016     VkDeviceMemory hMemory,
12017     VmaSuballocationType suballocationType,
12018     void* pMappedData,
12019     VkDeviceSize size)
12020 {
12021     VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
12022     VMA_ASSERT(hMemory != VK_NULL_HANDLE);
12023     m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
12024     m_Alignment = 0;
12025     m_Size = size;
12026     m_MemoryTypeIndex = memoryTypeIndex;
12027     m_SuballocationType = (uint8_t)suballocationType;
12028     if(pMappedData != VMA_NULL)
12029     {
12030         VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");
12031         m_Flags |= (uint8_t)FLAG_PERSISTENT_MAP;
12032     }
12033     m_DedicatedAllocation.m_hParentPool = hParentPool;
12034     m_DedicatedAllocation.m_hMemory = hMemory;
12035     m_DedicatedAllocation.m_pMappedData = pMappedData;
12036     m_DedicatedAllocation.m_Prev = VMA_NULL;
12037     m_DedicatedAllocation.m_Next = VMA_NULL;
12038 }
12039 
SetName(VmaAllocator hAllocator,const char * pName)12040 void VmaAllocation_T::SetName(VmaAllocator hAllocator, const char* pName)
12041 {
12042     VMA_ASSERT(pName == VMA_NULL || pName != m_pName);
12043 
12044     FreeName(hAllocator);
12045 
12046     if (pName != VMA_NULL)
12047         m_pName = VmaCreateStringCopy(hAllocator->GetAllocationCallbacks(), pName);
12048 }
12049 
SwapBlockAllocation(VmaAllocator hAllocator,VmaAllocation allocation)12050 uint8_t VmaAllocation_T::SwapBlockAllocation(VmaAllocator hAllocator, VmaAllocation allocation)
12051 {
12052     VMA_ASSERT(allocation != VMA_NULL);
12053     VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
12054     VMA_ASSERT(allocation->m_Type == ALLOCATION_TYPE_BLOCK);
12055 
12056     if (m_MapCount != 0)
12057         m_BlockAllocation.m_Block->Unmap(hAllocator, m_MapCount);
12058 
12059     m_BlockAllocation.m_Block->m_pMetadata->SetAllocationUserData(m_BlockAllocation.m_AllocHandle, allocation);
12060     VMA_SWAP(m_BlockAllocation, allocation->m_BlockAllocation);
12061     m_BlockAllocation.m_Block->m_pMetadata->SetAllocationUserData(m_BlockAllocation.m_AllocHandle, this);
12062 
12063 #if VMA_STATS_STRING_ENABLED
12064     VMA_SWAP(m_BufferImageUsage, allocation->m_BufferImageUsage);
12065 #endif
12066     return m_MapCount;
12067 }
12068 
GetAllocHandle()12069 VmaAllocHandle VmaAllocation_T::GetAllocHandle() const
12070 {
12071     switch (m_Type)
12072     {
12073     case ALLOCATION_TYPE_BLOCK:
12074         return m_BlockAllocation.m_AllocHandle;
12075     case ALLOCATION_TYPE_DEDICATED:
12076         return VK_NULL_HANDLE;
12077     default:
12078         VMA_ASSERT(0);
12079         return VK_NULL_HANDLE;
12080     }
12081 }
12082 
GetOffset()12083 VkDeviceSize VmaAllocation_T::GetOffset() const
12084 {
12085     switch (m_Type)
12086     {
12087     case ALLOCATION_TYPE_BLOCK:
12088         return m_BlockAllocation.m_Block->m_pMetadata->GetAllocationOffset(m_BlockAllocation.m_AllocHandle);
12089     case ALLOCATION_TYPE_DEDICATED:
12090         return 0;
12091     default:
12092         VMA_ASSERT(0);
12093         return 0;
12094     }
12095 }
12096 
GetParentPool()12097 VmaPool VmaAllocation_T::GetParentPool() const
12098 {
12099     switch (m_Type)
12100     {
12101     case ALLOCATION_TYPE_BLOCK:
12102         return m_BlockAllocation.m_Block->GetParentPool();
12103     case ALLOCATION_TYPE_DEDICATED:
12104         return m_DedicatedAllocation.m_hParentPool;
12105     default:
12106         VMA_ASSERT(0);
12107         return VK_NULL_HANDLE;
12108     }
12109 }
12110 
GetMemory()12111 VkDeviceMemory VmaAllocation_T::GetMemory() const
12112 {
12113     switch (m_Type)
12114     {
12115     case ALLOCATION_TYPE_BLOCK:
12116         return m_BlockAllocation.m_Block->GetDeviceMemory();
12117     case ALLOCATION_TYPE_DEDICATED:
12118         return m_DedicatedAllocation.m_hMemory;
12119     default:
12120         VMA_ASSERT(0);
12121         return VK_NULL_HANDLE;
12122     }
12123 }
12124 
GetMappedData()12125 void* VmaAllocation_T::GetMappedData() const
12126 {
12127     switch (m_Type)
12128     {
12129     case ALLOCATION_TYPE_BLOCK:
12130         if (m_MapCount != 0 || IsPersistentMap())
12131         {
12132             void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
12133             VMA_ASSERT(pBlockData != VMA_NULL);
12134             return (char*)pBlockData + GetOffset();
12135         }
12136         else
12137         {
12138             return VMA_NULL;
12139         }
12140         break;
12141     case ALLOCATION_TYPE_DEDICATED:
12142         VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0 || IsPersistentMap()));
12143         return m_DedicatedAllocation.m_pMappedData;
12144     default:
12145         VMA_ASSERT(0);
12146         return VMA_NULL;
12147     }
12148 }
12149 
BlockAllocMap()12150 void VmaAllocation_T::BlockAllocMap()
12151 {
12152     VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
12153     VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");
12154 
12155     if (m_MapCount < 0xFF)
12156     {
12157         ++m_MapCount;
12158     }
12159     else
12160     {
12161         VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
12162     }
12163 }
12164 
BlockAllocUnmap()12165 void VmaAllocation_T::BlockAllocUnmap()
12166 {
12167     VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
12168 
12169     if (m_MapCount > 0)
12170     {
12171         --m_MapCount;
12172     }
12173     else
12174     {
12175         VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
12176     }
12177 }
12178 
DedicatedAllocMap(VmaAllocator hAllocator,void ** ppData)12179 VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
12180 {
12181     VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
12182     VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");
12183 
12184     if (m_MapCount != 0 || IsPersistentMap())
12185     {
12186         if (m_MapCount < 0xFF)
12187         {
12188             VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
12189             *ppData = m_DedicatedAllocation.m_pMappedData;
12190             ++m_MapCount;
12191             return VK_SUCCESS;
12192         }
12193         else
12194         {
12195             VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
12196             return VK_ERROR_MEMORY_MAP_FAILED;
12197         }
12198     }
12199     else
12200     {
12201         VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
12202             hAllocator->m_hDevice,
12203             m_DedicatedAllocation.m_hMemory,
12204             0, // offset
12205             VK_WHOLE_SIZE,
12206             0, // flags
12207             ppData);
12208         if (result == VK_SUCCESS)
12209         {
12210             m_DedicatedAllocation.m_pMappedData = *ppData;
12211             m_MapCount = 1;
12212         }
12213         return result;
12214     }
12215 }
12216 
DedicatedAllocUnmap(VmaAllocator hAllocator)12217 void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
12218 {
12219     VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
12220 
12221     if (m_MapCount > 0)
12222     {
12223         --m_MapCount;
12224         if (m_MapCount == 0 && !IsPersistentMap())
12225         {
12226             m_DedicatedAllocation.m_pMappedData = VMA_NULL;
12227             (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
12228                 hAllocator->m_hDevice,
12229                 m_DedicatedAllocation.m_hMemory);
12230         }
12231     }
12232     else
12233     {
12234         VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
12235     }
12236 }
12237 
12238 #if VMA_STATS_STRING_ENABLED
InitBufferImageUsage(uint32_t bufferImageUsage)12239 void VmaAllocation_T::InitBufferImageUsage(uint32_t bufferImageUsage)
12240 {
12241     VMA_ASSERT(m_BufferImageUsage == 0);
12242     m_BufferImageUsage = bufferImageUsage;
12243 }
12244 
PrintParameters(class VmaJsonWriter & json)12245 void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
12246 {
12247     json.WriteString("Type");
12248     json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
12249 
12250     json.WriteString("Size");
12251     json.WriteNumber(m_Size);
12252     json.WriteString("Usage");
12253     json.WriteNumber(m_BufferImageUsage);
12254 
12255     if (m_pUserData != VMA_NULL)
12256     {
12257         json.WriteString("CustomData");
12258         json.BeginString();
12259         json.ContinueString_Pointer(m_pUserData);
12260         json.EndString();
12261     }
12262     if (m_pName != VMA_NULL)
12263     {
12264         json.WriteString("Name");
12265         json.WriteString(m_pName);
12266     }
12267 }
12268 #endif // VMA_STATS_STRING_ENABLED
12269 
FreeName(VmaAllocator hAllocator)12270 void VmaAllocation_T::FreeName(VmaAllocator hAllocator)
12271 {
12272     if(m_pName)
12273     {
12274         VmaFreeString(hAllocator->GetAllocationCallbacks(), m_pName);
12275         m_pName = VMA_NULL;
12276     }
12277 }
12278 #endif // _VMA_ALLOCATION_T_FUNCTIONS
12279 
12280 #ifndef _VMA_BLOCK_VECTOR_FUNCTIONS
VmaBlockVector(VmaAllocator hAllocator,VmaPool hParentPool,uint32_t memoryTypeIndex,VkDeviceSize preferredBlockSize,size_t minBlockCount,size_t maxBlockCount,VkDeviceSize bufferImageGranularity,bool explicitBlockSize,uint32_t algorithm,float priority,VkDeviceSize minAllocationAlignment,void * pMemoryAllocateNext)12281 VmaBlockVector::VmaBlockVector(
12282     VmaAllocator hAllocator,
12283     VmaPool hParentPool,
12284     uint32_t memoryTypeIndex,
12285     VkDeviceSize preferredBlockSize,
12286     size_t minBlockCount,
12287     size_t maxBlockCount,
12288     VkDeviceSize bufferImageGranularity,
12289     bool explicitBlockSize,
12290     uint32_t algorithm,
12291     float priority,
12292     VkDeviceSize minAllocationAlignment,
12293     void* pMemoryAllocateNext)
12294     : m_hAllocator(hAllocator),
12295     m_hParentPool(hParentPool),
12296     m_MemoryTypeIndex(memoryTypeIndex),
12297     m_PreferredBlockSize(preferredBlockSize),
12298     m_MinBlockCount(minBlockCount),
12299     m_MaxBlockCount(maxBlockCount),
12300     m_BufferImageGranularity(bufferImageGranularity),
12301     m_ExplicitBlockSize(explicitBlockSize),
12302     m_Algorithm(algorithm),
12303     m_Priority(priority),
12304     m_MinAllocationAlignment(minAllocationAlignment),
12305     m_pMemoryAllocateNext(pMemoryAllocateNext),
12306     m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
12307     m_NextBlockId(0) {}
12308 
~VmaBlockVector()12309 VmaBlockVector::~VmaBlockVector()
12310 {
12311     for (size_t i = m_Blocks.size(); i--; )
12312     {
12313         m_Blocks[i]->Destroy(m_hAllocator);
12314         vma_delete(m_hAllocator, m_Blocks[i]);
12315     }
12316 }
12317 
CreateMinBlocks()12318 VkResult VmaBlockVector::CreateMinBlocks()
12319 {
12320     for (size_t i = 0; i < m_MinBlockCount; ++i)
12321     {
12322         VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
12323         if (res != VK_SUCCESS)
12324         {
12325             return res;
12326         }
12327     }
12328     return VK_SUCCESS;
12329 }
12330 
AddStatistics(VmaStatistics & inoutStats)12331 void VmaBlockVector::AddStatistics(VmaStatistics& inoutStats)
12332 {
12333     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12334 
12335     const size_t blockCount = m_Blocks.size();
12336     for (uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12337     {
12338         const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12339         VMA_ASSERT(pBlock);
12340         VMA_HEAVY_ASSERT(pBlock->Validate());
12341         pBlock->m_pMetadata->AddStatistics(inoutStats);
12342     }
12343 }
12344 
AddDetailedStatistics(VmaDetailedStatistics & inoutStats)12345 void VmaBlockVector::AddDetailedStatistics(VmaDetailedStatistics& inoutStats)
12346 {
12347     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12348 
12349     const size_t blockCount = m_Blocks.size();
12350     for (uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12351     {
12352         const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12353         VMA_ASSERT(pBlock);
12354         VMA_HEAVY_ASSERT(pBlock->Validate());
12355         pBlock->m_pMetadata->AddDetailedStatistics(inoutStats);
12356     }
12357 }
12358 
IsEmpty()12359 bool VmaBlockVector::IsEmpty()
12360 {
12361     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12362     return m_Blocks.empty();
12363 }
12364 
IsCorruptionDetectionEnabled()12365 bool VmaBlockVector::IsCorruptionDetectionEnabled() const
12366 {
12367     const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
12368     return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
12369         (VMA_DEBUG_MARGIN > 0) &&
12370         (m_Algorithm == 0 || m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) &&
12371         (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
12372 }
12373 
Allocate(VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)12374 VkResult VmaBlockVector::Allocate(
12375     VkDeviceSize size,
12376     VkDeviceSize alignment,
12377     const VmaAllocationCreateInfo& createInfo,
12378     VmaSuballocationType suballocType,
12379     size_t allocationCount,
12380     VmaAllocation* pAllocations)
12381 {
12382     size_t allocIndex;
12383     VkResult res = VK_SUCCESS;
12384 
12385     alignment = VMA_MAX(alignment, m_MinAllocationAlignment);
12386 
12387     if (IsCorruptionDetectionEnabled())
12388     {
12389         size = VmaAlignUp<VkDeviceSize>(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12390         alignment = VmaAlignUp<VkDeviceSize>(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12391     }
12392 
12393     {
12394         VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12395         for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
12396         {
12397             res = AllocatePage(
12398                 size,
12399                 alignment,
12400                 createInfo,
12401                 suballocType,
12402                 pAllocations + allocIndex);
12403             if (res != VK_SUCCESS)
12404             {
12405                 break;
12406             }
12407         }
12408     }
12409 
12410     if (res != VK_SUCCESS)
12411     {
12412         // Free all already created allocations.
12413         while (allocIndex--)
12414             Free(pAllocations[allocIndex]);
12415         memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
12416     }
12417 
12418     return res;
12419 }
12420 
AllocatePage(VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)12421 VkResult VmaBlockVector::AllocatePage(
12422     VkDeviceSize size,
12423     VkDeviceSize alignment,
12424     const VmaAllocationCreateInfo& createInfo,
12425     VmaSuballocationType suballocType,
12426     VmaAllocation* pAllocation)
12427 {
12428     const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
12429 
12430     VkDeviceSize freeMemory;
12431     {
12432         const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
12433         VmaBudget heapBudget = {};
12434         m_hAllocator->GetHeapBudgets(&heapBudget, heapIndex, 1);
12435         freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0;
12436     }
12437 
12438     const bool canFallbackToDedicated = !HasExplicitBlockSize() &&
12439         (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0;
12440     const bool canCreateNewBlock =
12441         ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
12442         (m_Blocks.size() < m_MaxBlockCount) &&
12443         (freeMemory >= size || !canFallbackToDedicated);
12444     uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
12445 
12446     // Upper address can only be used with linear allocator and within single memory block.
12447     if (isUpperAddress &&
12448         (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
12449     {
12450         return VK_ERROR_FEATURE_NOT_PRESENT;
12451     }
12452 
12453     // Early reject: requested allocation size is larger that maximum block size for this block vector.
12454     if (size + VMA_DEBUG_MARGIN > m_PreferredBlockSize)
12455     {
12456         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12457     }
12458 
12459     // 1. Search existing allocations. Try to allocate.
12460     if (m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
12461     {
12462         // Use only last block.
12463         if (!m_Blocks.empty())
12464         {
12465             VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
12466             VMA_ASSERT(pCurrBlock);
12467             VkResult res = AllocateFromBlock(
12468                 pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);
12469             if (res == VK_SUCCESS)
12470             {
12471                 VMA_DEBUG_LOG("    Returned from last block #%u", pCurrBlock->GetId());
12472                 IncrementallySortBlocks();
12473                 return VK_SUCCESS;
12474             }
12475         }
12476     }
12477     else
12478     {
12479         if (strategy != VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT) // MIN_MEMORY or default
12480         {
12481             const bool isHostVisible =
12482                 (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
12483             if(isHostVisible)
12484             {
12485                 const bool isMappingAllowed = (createInfo.flags &
12486                     (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0;
12487                 /*
12488                 For non-mappable allocations, check blocks that are not mapped first.
12489                 For mappable allocations, check blocks that are already mapped first.
12490                 This way, having many blocks, we will separate mappable and non-mappable allocations,
12491                 hopefully limiting the number of blocks that are mapped, which will help tools like RenderDoc.
12492                 */
12493                 for(size_t mappingI = 0; mappingI < 2; ++mappingI)
12494                 {
12495                     // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12496                     for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12497                     {
12498                         VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12499                         VMA_ASSERT(pCurrBlock);
12500                         const bool isBlockMapped = pCurrBlock->GetMappedData() != VMA_NULL;
12501                         if((mappingI == 0) == (isMappingAllowed == isBlockMapped))
12502                         {
12503                             VkResult res = AllocateFromBlock(
12504                                 pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);
12505                             if (res == VK_SUCCESS)
12506                             {
12507                                 VMA_DEBUG_LOG("    Returned from existing block #%u", pCurrBlock->GetId());
12508                                 IncrementallySortBlocks();
12509                                 return VK_SUCCESS;
12510                             }
12511                         }
12512                     }
12513                 }
12514             }
12515             else
12516             {
12517                 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12518                 for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12519                 {
12520                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12521                     VMA_ASSERT(pCurrBlock);
12522                     VkResult res = AllocateFromBlock(
12523                         pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);
12524                     if (res == VK_SUCCESS)
12525                     {
12526                         VMA_DEBUG_LOG("    Returned from existing block #%u", pCurrBlock->GetId());
12527                         IncrementallySortBlocks();
12528                         return VK_SUCCESS;
12529                     }
12530                 }
12531             }
12532         }
12533         else // VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT
12534         {
12535             // Backward order in m_Blocks - prefer blocks with largest amount of free space.
12536             for (size_t blockIndex = m_Blocks.size(); blockIndex--; )
12537             {
12538                 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12539                 VMA_ASSERT(pCurrBlock);
12540                 VkResult res = AllocateFromBlock(pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);
12541                 if (res == VK_SUCCESS)
12542                 {
12543                     VMA_DEBUG_LOG("    Returned from existing block #%u", pCurrBlock->GetId());
12544                     IncrementallySortBlocks();
12545                     return VK_SUCCESS;
12546                 }
12547             }
12548         }
12549     }
12550 
12551     // 2. Try to create new block.
12552     if (canCreateNewBlock)
12553     {
12554         // Calculate optimal size for new block.
12555         VkDeviceSize newBlockSize = m_PreferredBlockSize;
12556         uint32_t newBlockSizeShift = 0;
12557         const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
12558 
12559         if (!m_ExplicitBlockSize)
12560         {
12561             // Allocate 1/8, 1/4, 1/2 as first blocks.
12562             const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
12563             for (uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
12564             {
12565                 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12566                 if (smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
12567                 {
12568                     newBlockSize = smallerNewBlockSize;
12569                     ++newBlockSizeShift;
12570                 }
12571                 else
12572                 {
12573                     break;
12574                 }
12575             }
12576         }
12577 
12578         size_t newBlockIndex = 0;
12579         VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12580             CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12581         // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
12582         if (!m_ExplicitBlockSize)
12583         {
12584             while (res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
12585             {
12586                 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12587                 if (smallerNewBlockSize >= size)
12588                 {
12589                     newBlockSize = smallerNewBlockSize;
12590                     ++newBlockSizeShift;
12591                     res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12592                         CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12593                 }
12594                 else
12595                 {
12596                     break;
12597                 }
12598             }
12599         }
12600 
12601         if (res == VK_SUCCESS)
12602         {
12603             VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
12604             VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
12605 
12606             res = AllocateFromBlock(
12607                 pBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);
12608             if (res == VK_SUCCESS)
12609             {
12610                 VMA_DEBUG_LOG("    Created new block #%u Size=%llu", pBlock->GetId(), newBlockSize);
12611                 IncrementallySortBlocks();
12612                 return VK_SUCCESS;
12613             }
12614             else
12615             {
12616                 // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
12617                 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12618             }
12619         }
12620     }
12621 
12622     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12623 }
12624 
Free(const VmaAllocation hAllocation)12625 void VmaBlockVector::Free(const VmaAllocation hAllocation)
12626 {
12627     VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
12628 
12629     bool budgetExceeded = false;
12630     {
12631         const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
12632         VmaBudget heapBudget = {};
12633         m_hAllocator->GetHeapBudgets(&heapBudget, heapIndex, 1);
12634         budgetExceeded = heapBudget.usage >= heapBudget.budget;
12635     }
12636 
12637     // Scope for lock.
12638     {
12639         VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12640 
12641         VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
12642 
12643         if (IsCorruptionDetectionEnabled())
12644         {
12645             VkResult res = pBlock->ValidateMagicValueAfterAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
12646             VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
12647         }
12648 
12649         if (hAllocation->IsPersistentMap())
12650         {
12651             pBlock->Unmap(m_hAllocator, 1);
12652         }
12653 
12654         const bool hadEmptyBlockBeforeFree = HasEmptyBlock();
12655         pBlock->m_pMetadata->Free(hAllocation->GetAllocHandle());
12656         pBlock->PostFree(m_hAllocator);
12657         VMA_HEAVY_ASSERT(pBlock->Validate());
12658 
12659         VMA_DEBUG_LOG("  Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
12660 
12661         const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount;
12662         // pBlock became empty after this deallocation.
12663         if (pBlock->m_pMetadata->IsEmpty())
12664         {
12665             // Already had empty block. We don't want to have two, so delete this one.
12666             if ((hadEmptyBlockBeforeFree || budgetExceeded) && canDeleteBlock)
12667             {
12668                 pBlockToDelete = pBlock;
12669                 Remove(pBlock);
12670             }
12671             // else: We now have one empty block - leave it. A hysteresis to avoid allocating whole block back and forth.
12672         }
12673         // pBlock didn't become empty, but we have another empty block - find and free that one.
12674         // (This is optional, heuristics.)
12675         else if (hadEmptyBlockBeforeFree && canDeleteBlock)
12676         {
12677             VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
12678             if (pLastBlock->m_pMetadata->IsEmpty())
12679             {
12680                 pBlockToDelete = pLastBlock;
12681                 m_Blocks.pop_back();
12682             }
12683         }
12684 
12685         IncrementallySortBlocks();
12686     }
12687 
12688     // Destruction of a free block. Deferred until this point, outside of mutex
12689     // lock, for performance reason.
12690     if (pBlockToDelete != VMA_NULL)
12691     {
12692         VMA_DEBUG_LOG("    Deleted empty block #%u", pBlockToDelete->GetId());
12693         pBlockToDelete->Destroy(m_hAllocator);
12694         vma_delete(m_hAllocator, pBlockToDelete);
12695     }
12696 
12697     m_hAllocator->m_Budget.RemoveAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), hAllocation->GetSize());
12698     m_hAllocator->m_AllocationObjectAllocator.Free(hAllocation);
12699 }
12700 
CalcMaxBlockSize()12701 VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
12702 {
12703     VkDeviceSize result = 0;
12704     for (size_t i = m_Blocks.size(); i--; )
12705     {
12706         result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
12707         if (result >= m_PreferredBlockSize)
12708         {
12709             break;
12710         }
12711     }
12712     return result;
12713 }
12714 
Remove(VmaDeviceMemoryBlock * pBlock)12715 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
12716 {
12717     for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12718     {
12719         if (m_Blocks[blockIndex] == pBlock)
12720         {
12721             VmaVectorRemove(m_Blocks, blockIndex);
12722             return;
12723         }
12724     }
12725     VMA_ASSERT(0);
12726 }
12727 
IncrementallySortBlocks()12728 void VmaBlockVector::IncrementallySortBlocks()
12729 {
12730     if (!m_IncrementalSort)
12731         return;
12732     if (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
12733     {
12734         // Bubble sort only until first swap.
12735         for (size_t i = 1; i < m_Blocks.size(); ++i)
12736         {
12737             if (m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
12738             {
12739                 VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
12740                 return;
12741             }
12742         }
12743     }
12744 }
12745 
SortByFreeSize()12746 void VmaBlockVector::SortByFreeSize()
12747 {
12748     VMA_SORT(m_Blocks.begin(), m_Blocks.end(),
12749         [](VmaDeviceMemoryBlock* b1, VmaDeviceMemoryBlock* b2) -> bool
12750         {
12751             return b1->m_pMetadata->GetSumFreeSize() < b2->m_pMetadata->GetSumFreeSize();
12752         });
12753 }
12754 
AllocateFromBlock(VmaDeviceMemoryBlock * pBlock,VkDeviceSize size,VkDeviceSize alignment,VmaAllocationCreateFlags allocFlags,void * pUserData,VmaSuballocationType suballocType,uint32_t strategy,VmaAllocation * pAllocation)12755 VkResult VmaBlockVector::AllocateFromBlock(
12756     VmaDeviceMemoryBlock* pBlock,
12757     VkDeviceSize size,
12758     VkDeviceSize alignment,
12759     VmaAllocationCreateFlags allocFlags,
12760     void* pUserData,
12761     VmaSuballocationType suballocType,
12762     uint32_t strategy,
12763     VmaAllocation* pAllocation)
12764 {
12765     const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
12766 
12767     VmaAllocationRequest currRequest = {};
12768     if (pBlock->m_pMetadata->CreateAllocationRequest(
12769         size,
12770         alignment,
12771         isUpperAddress,
12772         suballocType,
12773         strategy,
12774         &currRequest))
12775     {
12776         return CommitAllocationRequest(currRequest, pBlock, alignment, allocFlags, pUserData, suballocType, pAllocation);
12777     }
12778     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12779 }
12780 
CommitAllocationRequest(VmaAllocationRequest & allocRequest,VmaDeviceMemoryBlock * pBlock,VkDeviceSize alignment,VmaAllocationCreateFlags allocFlags,void * pUserData,VmaSuballocationType suballocType,VmaAllocation * pAllocation)12781 VkResult VmaBlockVector::CommitAllocationRequest(
12782     VmaAllocationRequest& allocRequest,
12783     VmaDeviceMemoryBlock* pBlock,
12784     VkDeviceSize alignment,
12785     VmaAllocationCreateFlags allocFlags,
12786     void* pUserData,
12787     VmaSuballocationType suballocType,
12788     VmaAllocation* pAllocation)
12789 {
12790     const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
12791     const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
12792     const bool isMappingAllowed = (allocFlags &
12793         (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0;
12794 
12795     pBlock->PostAlloc();
12796     // Allocate from pCurrBlock.
12797     if (mapped)
12798     {
12799         VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
12800         if (res != VK_SUCCESS)
12801         {
12802             return res;
12803         }
12804     }
12805 
12806     *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(isMappingAllowed);
12807     pBlock->m_pMetadata->Alloc(allocRequest, suballocType, *pAllocation);
12808     (*pAllocation)->InitBlockAllocation(
12809         pBlock,
12810         allocRequest.allocHandle,
12811         alignment,
12812         allocRequest.size, // Not size, as actual allocation size may be larger than requested!
12813         m_MemoryTypeIndex,
12814         suballocType,
12815         mapped);
12816     VMA_HEAVY_ASSERT(pBlock->Validate());
12817     if (isUserDataString)
12818         (*pAllocation)->SetName(m_hAllocator, (const char*)pUserData);
12819     else
12820         (*pAllocation)->SetUserData(m_hAllocator, pUserData);
12821     m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), allocRequest.size);
12822     if (VMA_DEBUG_INITIALIZE_ALLOCATIONS)
12823     {
12824         m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
12825     }
12826     if (IsCorruptionDetectionEnabled())
12827     {
12828         VkResult res = pBlock->WriteMagicValueAfterAllocation(m_hAllocator, (*pAllocation)->GetOffset(), allocRequest.size);
12829         VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
12830     }
12831     return VK_SUCCESS;
12832 }
12833 
CreateBlock(VkDeviceSize blockSize,size_t * pNewBlockIndex)12834 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
12835 {
12836     VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
12837     allocInfo.pNext = m_pMemoryAllocateNext;
12838     allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
12839     allocInfo.allocationSize = blockSize;
12840 
12841 #if VMA_BUFFER_DEVICE_ADDRESS
12842     // Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature.
12843     VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
12844     if (m_hAllocator->m_UseKhrBufferDeviceAddress)
12845     {
12846         allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
12847         VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
12848     }
12849 #endif // VMA_BUFFER_DEVICE_ADDRESS
12850 
12851 #if VMA_MEMORY_PRIORITY
12852     VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };
12853     if (m_hAllocator->m_UseExtMemoryPriority)
12854     {
12855         VMA_ASSERT(m_Priority >= 0.f && m_Priority <= 1.f);
12856         priorityInfo.priority = m_Priority;
12857         VmaPnextChainPushFront(&allocInfo, &priorityInfo);
12858     }
12859 #endif // VMA_MEMORY_PRIORITY
12860 
12861 #if VMA_EXTERNAL_MEMORY
12862     // Attach VkExportMemoryAllocateInfoKHR if necessary.
12863     VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR };
12864     exportMemoryAllocInfo.handleTypes = m_hAllocator->GetExternalMemoryHandleTypeFlags(m_MemoryTypeIndex);
12865     if (exportMemoryAllocInfo.handleTypes != 0)
12866     {
12867         VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo);
12868     }
12869 #endif // VMA_EXTERNAL_MEMORY
12870 
12871     VkDeviceMemory mem = VK_NULL_HANDLE;
12872     VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
12873     if (res < 0)
12874     {
12875         return res;
12876     }
12877 
12878     // New VkDeviceMemory successfully created.
12879 
12880     // Create new Allocation for it.
12881     VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
12882     pBlock->Init(
12883         m_hAllocator,
12884         m_hParentPool,
12885         m_MemoryTypeIndex,
12886         mem,
12887         allocInfo.allocationSize,
12888         m_NextBlockId++,
12889         m_Algorithm,
12890         m_BufferImageGranularity);
12891 
12892     m_Blocks.push_back(pBlock);
12893     if (pNewBlockIndex != VMA_NULL)
12894     {
12895         *pNewBlockIndex = m_Blocks.size() - 1;
12896     }
12897 
12898     return VK_SUCCESS;
12899 }
12900 
HasEmptyBlock()12901 bool VmaBlockVector::HasEmptyBlock()
12902 {
12903     for (size_t index = 0, count = m_Blocks.size(); index < count; ++index)
12904     {
12905         VmaDeviceMemoryBlock* const pBlock = m_Blocks[index];
12906         if (pBlock->m_pMetadata->IsEmpty())
12907         {
12908             return true;
12909         }
12910     }
12911     return false;
12912 }
12913 
12914 #if VMA_STATS_STRING_ENABLED
PrintDetailedMap(class VmaJsonWriter & json)12915 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
12916 {
12917     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12918 
12919 
12920     json.BeginObject();
12921     for (size_t i = 0; i < m_Blocks.size(); ++i)
12922     {
12923         json.BeginString();
12924         json.ContinueString(m_Blocks[i]->GetId());
12925         json.EndString();
12926 
12927         json.BeginObject();
12928         json.WriteString("MapRefCount");
12929         json.WriteNumber(m_Blocks[i]->GetMapRefCount());
12930 
12931         m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
12932         json.EndObject();
12933     }
12934     json.EndObject();
12935 }
12936 #endif // VMA_STATS_STRING_ENABLED
12937 
CheckCorruption()12938 VkResult VmaBlockVector::CheckCorruption()
12939 {
12940     if (!IsCorruptionDetectionEnabled())
12941     {
12942         return VK_ERROR_FEATURE_NOT_PRESENT;
12943     }
12944 
12945     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12946     for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12947     {
12948         VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12949         VMA_ASSERT(pBlock);
12950         VkResult res = pBlock->CheckCorruption(m_hAllocator);
12951         if (res != VK_SUCCESS)
12952         {
12953             return res;
12954         }
12955     }
12956     return VK_SUCCESS;
12957 }
12958 
FreeEmptyBlock()12959 void VmaBlockVector::FreeEmptyBlock()
12960 {
12961     // Force deletion of empty blocks.
12962     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12963     for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12964     {
12965         VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12966         if(pBlock->m_pMetadata->IsEmpty())
12967         {
12968             if(m_Blocks.size() > m_MinBlockCount)
12969             {
12970                 VmaVectorRemove(m_Blocks, blockIndex);
12971                 pBlock->Destroy(m_hAllocator);
12972                 vma_delete(m_hAllocator, pBlock);
12973             }
12974             else
12975             {
12976                 break;
12977             }
12978         }
12979     }
12980 }
12981 
12982 #endif // _VMA_BLOCK_VECTOR_FUNCTIONS
12983 
12984 #ifndef _VMA_DEFRAGMENTATION_CONTEXT_FUNCTIONS
VmaDefragmentationContext_T(VmaAllocator hAllocator,const VmaDefragmentationInfo & info)12985 VmaDefragmentationContext_T::VmaDefragmentationContext_T(
12986     VmaAllocator hAllocator,
12987     const VmaDefragmentationInfo& info)
12988     : m_MaxPassBytes(info.maxBytesPerPass == 0 ? VK_WHOLE_SIZE : info.maxBytesPerPass),
12989     m_MaxPassAllocations(info.maxAllocationsPerPass == 0 ? UINT32_MAX : info.maxAllocationsPerPass),
12990     m_MoveAllocator(hAllocator->GetAllocationCallbacks()),
12991     m_Moves(m_MoveAllocator)
12992 {
12993     m_Algorithm = info.flags & VMA_DEFRAGMENTATION_FLAG_ALGORITHM_MASK;
12994 
12995     if (info.pool != VMA_NULL)
12996     {
12997         m_BlockVectorCount = 1;
12998         m_PoolBlockVector = &info.pool->m_BlockVector;
12999         m_pBlockVectors = &m_PoolBlockVector;
13000         m_PoolBlockVector->SetIncrementalSort(false);
13001         m_PoolBlockVector->SortByFreeSize();
13002     }
13003     else
13004     {
13005         m_BlockVectorCount = hAllocator->GetMemoryTypeCount();
13006         m_PoolBlockVector = VMA_NULL;
13007         m_pBlockVectors = hAllocator->m_pBlockVectors;
13008         for (uint32_t i = 0; i < m_BlockVectorCount; ++i)
13009         {
13010             VmaBlockVector* vector = m_pBlockVectors[i];
13011             if (vector != VMA_NULL)
13012             {
13013                 vector->SetIncrementalSort(false);
13014                 vector->SortByFreeSize();
13015             }
13016         }
13017     }
13018 
13019     switch (m_Algorithm)
13020     {
13021     case 0: // Default algorithm
13022         m_Algorithm = VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT;
13023     case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT:
13024     {
13025         m_AlgorithmState = vma_new_array(hAllocator, StateBalanced, m_BlockVectorCount);
13026         break;
13027     }
13028     case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT:
13029     {
13030         if (hAllocator->GetBufferImageGranularity() > 1)
13031         {
13032             m_AlgorithmState = vma_new_array(hAllocator, StateExtensive, m_BlockVectorCount);
13033         }
13034         break;
13035     }
13036     }
13037 }
13038 
~VmaDefragmentationContext_T()13039 VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
13040 {
13041     if (m_PoolBlockVector != VMA_NULL)
13042     {
13043         m_PoolBlockVector->SetIncrementalSort(true);
13044     }
13045     else
13046     {
13047         for (uint32_t i = 0; i < m_BlockVectorCount; ++i)
13048         {
13049             VmaBlockVector* vector = m_pBlockVectors[i];
13050             if (vector != VMA_NULL)
13051                 vector->SetIncrementalSort(true);
13052         }
13053     }
13054 
13055     if (m_AlgorithmState)
13056     {
13057         switch (m_Algorithm)
13058         {
13059         case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT:
13060             vma_delete_array(m_MoveAllocator.m_pCallbacks, reinterpret_cast<StateBalanced*>(m_AlgorithmState), m_BlockVectorCount);
13061             break;
13062         case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT:
13063             vma_delete_array(m_MoveAllocator.m_pCallbacks, reinterpret_cast<StateExtensive*>(m_AlgorithmState), m_BlockVectorCount);
13064             break;
13065         default:
13066             VMA_ASSERT(0);
13067         }
13068     }
13069 }
13070 
DefragmentPassBegin(VmaDefragmentationPassMoveInfo & moveInfo)13071 VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassMoveInfo& moveInfo)
13072 {
13073     if (m_PoolBlockVector != VMA_NULL)
13074     {
13075         VmaMutexLockWrite lock(m_PoolBlockVector->GetMutex(), m_PoolBlockVector->GetAllocator()->m_UseMutex);
13076 
13077         if (m_PoolBlockVector->GetBlockCount() > 1)
13078             ComputeDefragmentation(*m_PoolBlockVector, 0);
13079         else if (m_PoolBlockVector->GetBlockCount() == 1)
13080             ReallocWithinBlock(*m_PoolBlockVector, m_PoolBlockVector->GetBlock(0));
13081     }
13082     else
13083     {
13084         for (uint32_t i = 0; i < m_BlockVectorCount; ++i)
13085         {
13086             if (m_pBlockVectors[i] != VMA_NULL)
13087             {
13088                 VmaMutexLockWrite lock(m_pBlockVectors[i]->GetMutex(), m_pBlockVectors[i]->GetAllocator()->m_UseMutex);
13089 
13090                 if (m_pBlockVectors[i]->GetBlockCount() > 1)
13091                 {
13092                     if (ComputeDefragmentation(*m_pBlockVectors[i], i))
13093                         break;
13094                 }
13095                 else if (m_pBlockVectors[i]->GetBlockCount() == 1)
13096                 {
13097                     if (ReallocWithinBlock(*m_pBlockVectors[i], m_pBlockVectors[i]->GetBlock(0)))
13098                         break;
13099                 }
13100             }
13101         }
13102     }
13103 
13104     moveInfo.moveCount = static_cast<uint32_t>(m_Moves.size());
13105     if (moveInfo.moveCount > 0)
13106     {
13107         moveInfo.pMoves = m_Moves.data();
13108         return VK_INCOMPLETE;
13109     }
13110 
13111     moveInfo.pMoves = VMA_NULL;
13112     return VK_SUCCESS;
13113 }
13114 
DefragmentPassEnd(VmaDefragmentationPassMoveInfo & moveInfo)13115 VkResult VmaDefragmentationContext_T::DefragmentPassEnd(VmaDefragmentationPassMoveInfo& moveInfo)
13116 {
13117     VMA_ASSERT(moveInfo.moveCount > 0 ? moveInfo.pMoves != VMA_NULL : true);
13118 
13119     VkResult result = VK_SUCCESS;
13120     VmaStlAllocator<FragmentedBlock> blockAllocator(m_MoveAllocator.m_pCallbacks);
13121     VmaVector<FragmentedBlock, VmaStlAllocator<FragmentedBlock>> immovableBlocks(blockAllocator);
13122     VmaVector<FragmentedBlock, VmaStlAllocator<FragmentedBlock>> mappedBlocks(blockAllocator);
13123 
13124     VmaAllocator allocator = VMA_NULL;
13125     for (uint32_t i = 0; i < moveInfo.moveCount; ++i)
13126     {
13127         VmaDefragmentationMove& move = moveInfo.pMoves[i];
13128         size_t prevCount = 0, currentCount = 0;
13129         VkDeviceSize freedBlockSize = 0;
13130 
13131         uint32_t vectorIndex;
13132         VmaBlockVector* vector;
13133         if (m_PoolBlockVector != VMA_NULL)
13134         {
13135             vectorIndex = 0;
13136             vector = m_PoolBlockVector;
13137         }
13138         else
13139         {
13140             vectorIndex = move.srcAllocation->GetMemoryTypeIndex();
13141             vector = m_pBlockVectors[vectorIndex];
13142             VMA_ASSERT(vector != VMA_NULL);
13143         }
13144 
13145         switch (move.operation)
13146         {
13147         case VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY:
13148         {
13149             uint8_t mapCount = move.srcAllocation->SwapBlockAllocation(vector->m_hAllocator, move.dstTmpAllocation);
13150             if (mapCount > 0)
13151             {
13152                 allocator = vector->m_hAllocator;
13153                 VmaDeviceMemoryBlock* newMapBlock = move.srcAllocation->GetBlock();
13154                 bool notPresent = true;
13155                 for (FragmentedBlock& block : mappedBlocks)
13156                 {
13157                     if (block.block == newMapBlock)
13158                     {
13159                         notPresent = false;
13160                         block.data += mapCount;
13161                         break;
13162                     }
13163                 }
13164                 if (notPresent)
13165                     mappedBlocks.push_back({ mapCount, newMapBlock });
13166             }
13167 
13168             // Scope for locks, Free have it's own lock
13169             {
13170                 VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
13171                 prevCount = vector->GetBlockCount();
13172                 freedBlockSize = move.dstTmpAllocation->GetBlock()->m_pMetadata->GetSize();
13173             }
13174             vector->Free(move.dstTmpAllocation);
13175             {
13176                 VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
13177                 currentCount = vector->GetBlockCount();
13178             }
13179 
13180             result = VK_INCOMPLETE;
13181             break;
13182         }
13183         case VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE:
13184         {
13185             m_PassStats.bytesMoved -= move.srcAllocation->GetSize();
13186             --m_PassStats.allocationsMoved;
13187             vector->Free(move.dstTmpAllocation);
13188 
13189             VmaDeviceMemoryBlock* newBlock = move.srcAllocation->GetBlock();
13190             bool notPresent = true;
13191             for (const FragmentedBlock& block : immovableBlocks)
13192             {
13193                 if (block.block == newBlock)
13194                 {
13195                     notPresent = false;
13196                     break;
13197                 }
13198             }
13199             if (notPresent)
13200                 immovableBlocks.push_back({ vectorIndex, newBlock });
13201             break;
13202         }
13203         case VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY:
13204         {
13205             m_PassStats.bytesMoved -= move.srcAllocation->GetSize();
13206             --m_PassStats.allocationsMoved;
13207             // Scope for locks, Free have it's own lock
13208             {
13209                 VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
13210                 prevCount = vector->GetBlockCount();
13211                 freedBlockSize = move.srcAllocation->GetBlock()->m_pMetadata->GetSize();
13212             }
13213             vector->Free(move.srcAllocation);
13214             {
13215                 VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
13216                 currentCount = vector->GetBlockCount();
13217             }
13218             freedBlockSize *= prevCount - currentCount;
13219 
13220             VkDeviceSize dstBlockSize;
13221             {
13222                 VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
13223                 dstBlockSize = move.dstTmpAllocation->GetBlock()->m_pMetadata->GetSize();
13224             }
13225             vector->Free(move.dstTmpAllocation);
13226             {
13227                 VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
13228                 freedBlockSize += dstBlockSize * (currentCount - vector->GetBlockCount());
13229                 currentCount = vector->GetBlockCount();
13230             }
13231 
13232             result = VK_INCOMPLETE;
13233             break;
13234         }
13235         default:
13236             VMA_ASSERT(0);
13237         }
13238 
13239         if (prevCount > currentCount)
13240         {
13241             size_t freedBlocks = prevCount - currentCount;
13242             m_PassStats.deviceMemoryBlocksFreed += static_cast<uint32_t>(freedBlocks);
13243             m_PassStats.bytesFreed += freedBlockSize;
13244         }
13245 
13246         switch (m_Algorithm)
13247         {
13248         case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT:
13249         {
13250             if (m_AlgorithmState != VMA_NULL)
13251             {
13252                 // Avoid unnecessary tries to allocate when new free block is avaiable
13253                 StateExtensive& state = reinterpret_cast<StateExtensive*>(m_AlgorithmState)[vectorIndex];
13254                 if (state.firstFreeBlock != SIZE_MAX)
13255                 {
13256                     const size_t diff = prevCount - currentCount;
13257                     if (state.firstFreeBlock >= diff)
13258                     {
13259                         state.firstFreeBlock -= diff;
13260                         if (state.firstFreeBlock != 0)
13261                             state.firstFreeBlock -= vector->GetBlock(state.firstFreeBlock - 1)->m_pMetadata->IsEmpty();
13262                     }
13263                     else
13264                         state.firstFreeBlock = 0;
13265                 }
13266             }
13267         }
13268         }
13269     }
13270     moveInfo.moveCount = 0;
13271     moveInfo.pMoves = VMA_NULL;
13272     m_Moves.clear();
13273 
13274     // Update stats
13275     m_GlobalStats.allocationsMoved += m_PassStats.allocationsMoved;
13276     m_GlobalStats.bytesFreed += m_PassStats.bytesFreed;
13277     m_GlobalStats.bytesMoved += m_PassStats.bytesMoved;
13278     m_GlobalStats.deviceMemoryBlocksFreed += m_PassStats.deviceMemoryBlocksFreed;
13279     m_PassStats = { 0 };
13280 
13281     // Move blocks with immovable allocations according to algorithm
13282     if (immovableBlocks.size() > 0)
13283     {
13284         switch (m_Algorithm)
13285         {
13286         case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT:
13287         {
13288             if (m_AlgorithmState != VMA_NULL)
13289             {
13290                 bool swapped = false;
13291                 // Move to the start of free blocks range
13292                 for (const FragmentedBlock& block : immovableBlocks)
13293                 {
13294                     StateExtensive& state = reinterpret_cast<StateExtensive*>(m_AlgorithmState)[block.data];
13295                     if (state.operation != StateExtensive::Operation::Cleanup)
13296                     {
13297                         VmaBlockVector* vector = m_pBlockVectors[block.data];
13298                         VmaMutexLockWrite lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
13299 
13300                         for (size_t i = 0, count = vector->GetBlockCount() - m_ImmovableBlockCount; i < count; ++i)
13301                         {
13302                             if (vector->GetBlock(i) == block.block)
13303                             {
13304                                 VMA_SWAP(vector->m_Blocks[i], vector->m_Blocks[vector->GetBlockCount() - ++m_ImmovableBlockCount]);
13305                                 if (state.firstFreeBlock != SIZE_MAX)
13306                                 {
13307                                     if (i + 1 < state.firstFreeBlock)
13308                                     {
13309                                         if (state.firstFreeBlock > 1)
13310                                             VMA_SWAP(vector->m_Blocks[i], vector->m_Blocks[--state.firstFreeBlock]);
13311                                         else
13312                                             --state.firstFreeBlock;
13313                                     }
13314                                 }
13315                                 swapped = true;
13316                                 break;
13317                             }
13318                         }
13319                     }
13320                 }
13321                 if (swapped)
13322                     result = VK_INCOMPLETE;
13323                 break;
13324             }
13325         }
13326         default:
13327         {
13328             // Move to the begining
13329             for (const FragmentedBlock& block : immovableBlocks)
13330             {
13331                 VmaBlockVector* vector = m_pBlockVectors[block.data];
13332                 VmaMutexLockWrite lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
13333 
13334                 for (size_t i = m_ImmovableBlockCount; i < vector->GetBlockCount(); ++i)
13335                 {
13336                     if (vector->GetBlock(i) == block.block)
13337                     {
13338                         VMA_SWAP(vector->m_Blocks[i], vector->m_Blocks[m_ImmovableBlockCount++]);
13339                         break;
13340                     }
13341                 }
13342             }
13343             break;
13344         }
13345         }
13346     }
13347 
13348     // Bulk-map destination blocks
13349     for (const FragmentedBlock& block : mappedBlocks)
13350     {
13351         VkResult res = block.block->Map(allocator, block.data, VMA_NULL);
13352         VMA_ASSERT(res == VK_SUCCESS);
13353     }
13354     return result;
13355 }
13356 
ComputeDefragmentation(VmaBlockVector & vector,size_t index)13357 bool VmaDefragmentationContext_T::ComputeDefragmentation(VmaBlockVector& vector, size_t index)
13358 {
13359     switch (m_Algorithm)
13360     {
13361     case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT:
13362         return ComputeDefragmentation_Fast(vector);
13363     default:
13364         VMA_ASSERT(0);
13365     case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT:
13366         return ComputeDefragmentation_Balanced(vector, index, true);
13367     case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT:
13368         return ComputeDefragmentation_Full(vector);
13369     case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT:
13370         return ComputeDefragmentation_Extensive(vector, index);
13371     }
13372 }
13373 
GetMoveData(VmaAllocHandle handle,VmaBlockMetadata * metadata)13374 VmaDefragmentationContext_T::MoveAllocationData VmaDefragmentationContext_T::GetMoveData(
13375     VmaAllocHandle handle, VmaBlockMetadata* metadata)
13376 {
13377     MoveAllocationData moveData;
13378     moveData.move.srcAllocation = (VmaAllocation)metadata->GetAllocationUserData(handle);
13379     moveData.size = moveData.move.srcAllocation->GetSize();
13380     moveData.alignment = moveData.move.srcAllocation->GetAlignment();
13381     moveData.type = moveData.move.srcAllocation->GetSuballocationType();
13382     moveData.flags = 0;
13383 
13384     if (moveData.move.srcAllocation->IsPersistentMap())
13385         moveData.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
13386     if (moveData.move.srcAllocation->IsMappingAllowed())
13387         moveData.flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
13388 
13389     return moveData;
13390 }
13391 
CheckCounters(VkDeviceSize bytes)13392 VmaDefragmentationContext_T::CounterStatus VmaDefragmentationContext_T::CheckCounters(VkDeviceSize bytes)
13393 {
13394     // Ignore allocation if will exceed max size for copy
13395     if (m_PassStats.bytesMoved + bytes > m_MaxPassBytes)
13396     {
13397         if (++m_IgnoredAllocs < MAX_ALLOCS_TO_IGNORE)
13398             return CounterStatus::Ignore;
13399         else
13400             return CounterStatus::End;
13401     }
13402     return CounterStatus::Pass;
13403 }
13404 
IncrementCounters(VkDeviceSize bytes)13405 bool VmaDefragmentationContext_T::IncrementCounters(VkDeviceSize bytes)
13406 {
13407     m_PassStats.bytesMoved += bytes;
13408     // Early return when max found
13409     if (++m_PassStats.allocationsMoved >= m_MaxPassAllocations || m_PassStats.bytesMoved >= m_MaxPassBytes)
13410     {
13411         VMA_ASSERT(m_PassStats.allocationsMoved == m_MaxPassAllocations ||
13412             m_PassStats.bytesMoved == m_MaxPassBytes && "Exceeded maximal pass threshold!");
13413         return true;
13414     }
13415     return false;
13416 }
13417 
ReallocWithinBlock(VmaBlockVector & vector,VmaDeviceMemoryBlock * block)13418 bool VmaDefragmentationContext_T::ReallocWithinBlock(VmaBlockVector& vector, VmaDeviceMemoryBlock* block)
13419 {
13420     VmaBlockMetadata* metadata = block->m_pMetadata;
13421 
13422     for (VmaAllocHandle handle = metadata->GetAllocationListBegin();
13423         handle != VK_NULL_HANDLE;
13424         handle = metadata->GetNextAllocation(handle))
13425     {
13426         MoveAllocationData moveData = GetMoveData(handle, metadata);
13427         // Ignore newly created allocations by defragmentation algorithm
13428         if (moveData.move.srcAllocation->GetUserData() == this)
13429             continue;
13430         switch (CheckCounters(moveData.move.srcAllocation->GetSize()))
13431         {
13432         case CounterStatus::Ignore:
13433             continue;
13434         case CounterStatus::End:
13435             return true;
13436         default:
13437             VMA_ASSERT(0);
13438         case CounterStatus::Pass:
13439             break;
13440         }
13441 
13442         VkDeviceSize offset = moveData.move.srcAllocation->GetOffset();
13443         if (offset != 0 && metadata->GetSumFreeSize() >= moveData.size)
13444         {
13445             VmaAllocationRequest request = {};
13446             if (metadata->CreateAllocationRequest(
13447                 moveData.size,
13448                 moveData.alignment,
13449                 false,
13450                 moveData.type,
13451                 VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,
13452                 &request))
13453             {
13454                 if (metadata->GetAllocationOffset(request.allocHandle) < offset)
13455                 {
13456                     if (vector.CommitAllocationRequest(
13457                         request,
13458                         block,
13459                         moveData.alignment,
13460                         moveData.flags,
13461                         this,
13462                         moveData.type,
13463                         &moveData.move.dstTmpAllocation) == VK_SUCCESS)
13464                     {
13465                         m_Moves.push_back(moveData.move);
13466                         if (IncrementCounters(moveData.size))
13467                             return true;
13468                     }
13469                 }
13470             }
13471         }
13472     }
13473     return false;
13474 }
13475 
AllocInOtherBlock(size_t start,size_t end,MoveAllocationData & data,VmaBlockVector & vector)13476 bool VmaDefragmentationContext_T::AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, VmaBlockVector& vector)
13477 {
13478     for (; start < end; ++start)
13479     {
13480         VmaDeviceMemoryBlock* dstBlock = vector.GetBlock(start);
13481         if (dstBlock->m_pMetadata->GetSumFreeSize() >= data.size)
13482         {
13483             if (vector.AllocateFromBlock(dstBlock,
13484                 data.size,
13485                 data.alignment,
13486                 data.flags,
13487                 this,
13488                 data.type,
13489                 0,
13490                 &data.move.dstTmpAllocation) == VK_SUCCESS)
13491             {
13492                 m_Moves.push_back(data.move);
13493                 if (IncrementCounters(data.size))
13494                     return true;
13495                 break;
13496             }
13497         }
13498     }
13499     return false;
13500 }
13501 
ComputeDefragmentation_Fast(VmaBlockVector & vector)13502 bool VmaDefragmentationContext_T::ComputeDefragmentation_Fast(VmaBlockVector& vector)
13503 {
13504     // Move only between blocks
13505 
13506     // Go through allocations in last blocks and try to fit them inside first ones
13507     for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)
13508     {
13509         VmaBlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata;
13510 
13511         for (VmaAllocHandle handle = metadata->GetAllocationListBegin();
13512             handle != VK_NULL_HANDLE;
13513             handle = metadata->GetNextAllocation(handle))
13514         {
13515             MoveAllocationData moveData = GetMoveData(handle, metadata);
13516             // Ignore newly created allocations by defragmentation algorithm
13517             if (moveData.move.srcAllocation->GetUserData() == this)
13518                 continue;
13519             switch (CheckCounters(moveData.move.srcAllocation->GetSize()))
13520             {
13521             case CounterStatus::Ignore:
13522                 continue;
13523             case CounterStatus::End:
13524                 return true;
13525             default:
13526                 VMA_ASSERT(0);
13527             case CounterStatus::Pass:
13528                 break;
13529             }
13530 
13531             // Check all previous blocks for free space
13532             if (AllocInOtherBlock(0, i, moveData, vector))
13533                 return true;
13534         }
13535     }
13536     return false;
13537 }
13538 
ComputeDefragmentation_Balanced(VmaBlockVector & vector,size_t index,bool update)13539 bool VmaDefragmentationContext_T::ComputeDefragmentation_Balanced(VmaBlockVector& vector, size_t index, bool update)
13540 {
13541     // Go over every allocation and try to fit it in previous blocks at lowest offsets,
13542     // if not possible: realloc within single block to minimize offset (exclude offset == 0),
13543     // but only if there are noticable gaps between them (some heuristic, ex. average size of allocation in block)
13544     VMA_ASSERT(m_AlgorithmState != VMA_NULL);
13545 
13546     StateBalanced& vectorState = reinterpret_cast<StateBalanced*>(m_AlgorithmState)[index];
13547     if (update && vectorState.avgAllocSize == UINT64_MAX)
13548         UpdateVectorStatistics(vector, vectorState);
13549 
13550     const size_t startMoveCount = m_Moves.size();
13551     VkDeviceSize minimalFreeRegion = vectorState.avgFreeSize / 2;
13552     for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)
13553     {
13554         VmaDeviceMemoryBlock* block = vector.GetBlock(i);
13555         VmaBlockMetadata* metadata = block->m_pMetadata;
13556         VkDeviceSize prevFreeRegionSize = 0;
13557 
13558         for (VmaAllocHandle handle = metadata->GetAllocationListBegin();
13559             handle != VK_NULL_HANDLE;
13560             handle = metadata->GetNextAllocation(handle))
13561         {
13562             MoveAllocationData moveData = GetMoveData(handle, metadata);
13563             // Ignore newly created allocations by defragmentation algorithm
13564             if (moveData.move.srcAllocation->GetUserData() == this)
13565                 continue;
13566             switch (CheckCounters(moveData.move.srcAllocation->GetSize()))
13567             {
13568             case CounterStatus::Ignore:
13569                 continue;
13570             case CounterStatus::End:
13571                 return true;
13572             default:
13573                 VMA_ASSERT(0);
13574             case CounterStatus::Pass:
13575                 break;
13576             }
13577 
13578             // Check all previous blocks for free space
13579             const size_t prevMoveCount = m_Moves.size();
13580             if (AllocInOtherBlock(0, i, moveData, vector))
13581                 return true;
13582 
13583             VkDeviceSize nextFreeRegionSize = metadata->GetNextFreeRegionSize(handle);
13584             // If no room found then realloc within block for lower offset
13585             VkDeviceSize offset = moveData.move.srcAllocation->GetOffset();
13586             if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size)
13587             {
13588                 // Check if realloc will make sense
13589                 if (prevFreeRegionSize >= minimalFreeRegion ||
13590                     nextFreeRegionSize >= minimalFreeRegion ||
13591                     moveData.size <= vectorState.avgFreeSize ||
13592                     moveData.size <= vectorState.avgAllocSize)
13593                 {
13594                     VmaAllocationRequest request = {};
13595                     if (metadata->CreateAllocationRequest(
13596                         moveData.size,
13597                         moveData.alignment,
13598                         false,
13599                         moveData.type,
13600                         VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,
13601                         &request))
13602                     {
13603                         if (metadata->GetAllocationOffset(request.allocHandle) < offset)
13604                         {
13605                             if (vector.CommitAllocationRequest(
13606                                 request,
13607                                 block,
13608                                 moveData.alignment,
13609                                 moveData.flags,
13610                                 this,
13611                                 moveData.type,
13612                                 &moveData.move.dstTmpAllocation) == VK_SUCCESS)
13613                             {
13614                                 m_Moves.push_back(moveData.move);
13615                                 if (IncrementCounters(moveData.size))
13616                                     return true;
13617                             }
13618                         }
13619                     }
13620                 }
13621             }
13622             prevFreeRegionSize = nextFreeRegionSize;
13623         }
13624     }
13625 
13626     // No moves perfomed, update statistics to current vector state
13627     if (startMoveCount == m_Moves.size() && !update)
13628     {
13629         vectorState.avgAllocSize = UINT64_MAX;
13630         return ComputeDefragmentation_Balanced(vector, index, false);
13631     }
13632     return false;
13633 }
13634 
ComputeDefragmentation_Full(VmaBlockVector & vector)13635 bool VmaDefragmentationContext_T::ComputeDefragmentation_Full(VmaBlockVector& vector)
13636 {
13637     // Go over every allocation and try to fit it in previous blocks at lowest offsets,
13638     // if not possible: realloc within single block to minimize offset (exclude offset == 0)
13639 
13640     for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)
13641     {
13642         VmaDeviceMemoryBlock* block = vector.GetBlock(i);
13643         VmaBlockMetadata* metadata = block->m_pMetadata;
13644 
13645         for (VmaAllocHandle handle = metadata->GetAllocationListBegin();
13646             handle != VK_NULL_HANDLE;
13647             handle = metadata->GetNextAllocation(handle))
13648         {
13649             MoveAllocationData moveData = GetMoveData(handle, metadata);
13650             // Ignore newly created allocations by defragmentation algorithm
13651             if (moveData.move.srcAllocation->GetUserData() == this)
13652                 continue;
13653             switch (CheckCounters(moveData.move.srcAllocation->GetSize()))
13654             {
13655             case CounterStatus::Ignore:
13656                 continue;
13657             case CounterStatus::End:
13658                 return true;
13659             default:
13660                 VMA_ASSERT(0);
13661             case CounterStatus::Pass:
13662                 break;
13663             }
13664 
13665             // Check all previous blocks for free space
13666             const size_t prevMoveCount = m_Moves.size();
13667             if (AllocInOtherBlock(0, i, moveData, vector))
13668                 return true;
13669 
13670             // If no room found then realloc within block for lower offset
13671             VkDeviceSize offset = moveData.move.srcAllocation->GetOffset();
13672             if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size)
13673             {
13674                 VmaAllocationRequest request = {};
13675                 if (metadata->CreateAllocationRequest(
13676                     moveData.size,
13677                     moveData.alignment,
13678                     false,
13679                     moveData.type,
13680                     VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,
13681                     &request))
13682                 {
13683                     if (metadata->GetAllocationOffset(request.allocHandle) < offset)
13684                     {
13685                         if (vector.CommitAllocationRequest(
13686                             request,
13687                             block,
13688                             moveData.alignment,
13689                             moveData.flags,
13690                             this,
13691                             moveData.type,
13692                             &moveData.move.dstTmpAllocation) == VK_SUCCESS)
13693                         {
13694                             m_Moves.push_back(moveData.move);
13695                             if (IncrementCounters(moveData.size))
13696                                 return true;
13697                         }
13698                     }
13699                 }
13700             }
13701         }
13702     }
13703     return false;
13704 }
13705 
ComputeDefragmentation_Extensive(VmaBlockVector & vector,size_t index)13706 bool VmaDefragmentationContext_T::ComputeDefragmentation_Extensive(VmaBlockVector& vector, size_t index)
13707 {
13708     // First free single block, then populate it to the brim, then free another block, and so on
13709 
13710     // Fallback to previous algorithm since without granularity conflicts it can achieve max packing
13711     if (vector.m_BufferImageGranularity == 1)
13712         return ComputeDefragmentation_Full(vector);
13713 
13714     VMA_ASSERT(m_AlgorithmState != VMA_NULL);
13715 
13716     StateExtensive& vectorState = reinterpret_cast<StateExtensive*>(m_AlgorithmState)[index];
13717 
13718     bool texturePresent = false, bufferPresent = false, otherPresent = false;
13719     switch (vectorState.operation)
13720     {
13721     case StateExtensive::Operation::Done: // Vector defragmented
13722         return false;
13723     case StateExtensive::Operation::FindFreeBlockBuffer:
13724     case StateExtensive::Operation::FindFreeBlockTexture:
13725     case StateExtensive::Operation::FindFreeBlockAll:
13726     {
13727         // No more blocks to free, just perform fast realloc and move to cleanup
13728         if (vectorState.firstFreeBlock == 0)
13729         {
13730             vectorState.operation = StateExtensive::Operation::Cleanup;
13731             return ComputeDefragmentation_Fast(vector);
13732         }
13733 
13734         // No free blocks, have to clear last one
13735         size_t last = (vectorState.firstFreeBlock == SIZE_MAX ? vector.GetBlockCount() : vectorState.firstFreeBlock) - 1;
13736         VmaBlockMetadata* freeMetadata = vector.GetBlock(last)->m_pMetadata;
13737 
13738         const size_t prevMoveCount = m_Moves.size();
13739         for (VmaAllocHandle handle = freeMetadata->GetAllocationListBegin();
13740             handle != VK_NULL_HANDLE;
13741             handle = freeMetadata->GetNextAllocation(handle))
13742         {
13743             MoveAllocationData moveData = GetMoveData(handle, freeMetadata);
13744             switch (CheckCounters(moveData.move.srcAllocation->GetSize()))
13745             {
13746             case CounterStatus::Ignore:
13747                 continue;
13748             case CounterStatus::End:
13749                 return true;
13750             default:
13751                 VMA_ASSERT(0);
13752             case CounterStatus::Pass:
13753                 break;
13754             }
13755 
13756             // Check all previous blocks for free space
13757             if (AllocInOtherBlock(0, last, moveData, vector))
13758             {
13759                 // Full clear performed already
13760                 if (prevMoveCount != m_Moves.size() && freeMetadata->GetNextAllocation(handle) == VK_NULL_HANDLE)
13761                     reinterpret_cast<size_t*>(m_AlgorithmState)[index] = last;
13762                 return true;
13763             }
13764         }
13765 
13766         if (prevMoveCount == m_Moves.size())
13767         {
13768             // Cannot perform full clear, have to move data in other blocks around
13769             if (last != 0)
13770             {
13771                 for (size_t i = last - 1; i; --i)
13772                 {
13773                     if (ReallocWithinBlock(vector, vector.GetBlock(i)))
13774                         return true;
13775                 }
13776             }
13777 
13778             if (prevMoveCount == m_Moves.size())
13779             {
13780                 // No possible reallocs within blocks, try to move them around fast
13781                 return ComputeDefragmentation_Fast(vector);
13782             }
13783         }
13784         else
13785         {
13786             switch (vectorState.operation)
13787             {
13788             case StateExtensive::Operation::FindFreeBlockBuffer:
13789                 vectorState.operation = StateExtensive::Operation::MoveBuffers;
13790                 break;
13791             default:
13792                 VMA_ASSERT(0);
13793             case StateExtensive::Operation::FindFreeBlockTexture:
13794                 vectorState.operation = StateExtensive::Operation::MoveTextures;
13795                 break;
13796             case StateExtensive::Operation::FindFreeBlockAll:
13797                 vectorState.operation = StateExtensive::Operation::MoveAll;
13798                 break;
13799             }
13800             vectorState.firstFreeBlock = last;
13801             // Nothing done, block found without reallocations, can perform another reallocs in same pass
13802             return ComputeDefragmentation_Extensive(vector, index);
13803         }
13804         break;
13805     }
13806     case StateExtensive::Operation::MoveTextures:
13807     {
13808         if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL, vector,
13809             vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent))
13810         {
13811             if (texturePresent)
13812             {
13813                 vectorState.operation = StateExtensive::Operation::FindFreeBlockTexture;
13814                 return ComputeDefragmentation_Extensive(vector, index);
13815             }
13816 
13817             if (!bufferPresent && !otherPresent)
13818             {
13819                 vectorState.operation = StateExtensive::Operation::Cleanup;
13820                 break;
13821             }
13822 
13823             // No more textures to move, check buffers
13824             vectorState.operation = StateExtensive::Operation::MoveBuffers;
13825             bufferPresent = false;
13826             otherPresent = false;
13827         }
13828         else
13829             break;
13830     }
13831     case StateExtensive::Operation::MoveBuffers:
13832     {
13833         if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_BUFFER, vector,
13834             vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent))
13835         {
13836             if (bufferPresent)
13837             {
13838                 vectorState.operation = StateExtensive::Operation::FindFreeBlockBuffer;
13839                 return ComputeDefragmentation_Extensive(vector, index);
13840             }
13841 
13842             if (!otherPresent)
13843             {
13844                 vectorState.operation = StateExtensive::Operation::Cleanup;
13845                 break;
13846             }
13847 
13848             // No more buffers to move, check all others
13849             vectorState.operation = StateExtensive::Operation::MoveAll;
13850             otherPresent = false;
13851         }
13852         else
13853             break;
13854     }
13855     case StateExtensive::Operation::MoveAll:
13856     {
13857         if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_FREE, vector,
13858             vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent))
13859         {
13860             if (otherPresent)
13861             {
13862                 vectorState.operation = StateExtensive::Operation::FindFreeBlockBuffer;
13863                 return ComputeDefragmentation_Extensive(vector, index);
13864             }
13865             // Everything moved
13866             vectorState.operation = StateExtensive::Operation::Cleanup;
13867         }
13868         break;
13869     }
13870     case StateExtensive::Operation::Cleanup:
13871         // Cleanup is handled below so that other operations may reuse the cleanup code. This case is here to prevent the unhandled enum value warning (C4062).
13872         break;
13873     }
13874 
13875     if (vectorState.operation == StateExtensive::Operation::Cleanup)
13876     {
13877         // All other work done, pack data in blocks even tighter if possible
13878         const size_t prevMoveCount = m_Moves.size();
13879         for (size_t i = 0; i < vector.GetBlockCount(); ++i)
13880         {
13881             if (ReallocWithinBlock(vector, vector.GetBlock(i)))
13882                 return true;
13883         }
13884 
13885         if (prevMoveCount == m_Moves.size())
13886             vectorState.operation = StateExtensive::Operation::Done;
13887     }
13888     return false;
13889 }
13890 
UpdateVectorStatistics(VmaBlockVector & vector,StateBalanced & state)13891 void VmaDefragmentationContext_T::UpdateVectorStatistics(VmaBlockVector& vector, StateBalanced& state)
13892 {
13893     size_t allocCount = 0;
13894     size_t freeCount = 0;
13895     state.avgFreeSize = 0;
13896     state.avgAllocSize = 0;
13897 
13898     for (size_t i = 0; i < vector.GetBlockCount(); ++i)
13899     {
13900         VmaBlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata;
13901 
13902         allocCount += metadata->GetAllocationCount();
13903         freeCount += metadata->GetFreeRegionsCount();
13904         state.avgFreeSize += metadata->GetSumFreeSize();
13905         state.avgAllocSize += metadata->GetSize();
13906     }
13907 
13908     state.avgAllocSize = (state.avgAllocSize - state.avgFreeSize) / allocCount;
13909     state.avgFreeSize /= freeCount;
13910 }
13911 
MoveDataToFreeBlocks(VmaSuballocationType currentType,VmaBlockVector & vector,size_t firstFreeBlock,bool & texturePresent,bool & bufferPresent,bool & otherPresent)13912 bool VmaDefragmentationContext_T::MoveDataToFreeBlocks(VmaSuballocationType currentType,
13913     VmaBlockVector& vector, size_t firstFreeBlock,
13914     bool& texturePresent, bool& bufferPresent, bool& otherPresent)
13915 {
13916     const size_t prevMoveCount = m_Moves.size();
13917     for (size_t i = firstFreeBlock ; i;)
13918     {
13919         VmaDeviceMemoryBlock* block = vector.GetBlock(--i);
13920         VmaBlockMetadata* metadata = block->m_pMetadata;
13921 
13922         for (VmaAllocHandle handle = metadata->GetAllocationListBegin();
13923             handle != VK_NULL_HANDLE;
13924             handle = metadata->GetNextAllocation(handle))
13925         {
13926             MoveAllocationData moveData = GetMoveData(handle, metadata);
13927             // Ignore newly created allocations by defragmentation algorithm
13928             if (moveData.move.srcAllocation->GetUserData() == this)
13929                 continue;
13930             switch (CheckCounters(moveData.move.srcAllocation->GetSize()))
13931             {
13932             case CounterStatus::Ignore:
13933                 continue;
13934             case CounterStatus::End:
13935                 return true;
13936             default:
13937                 VMA_ASSERT(0);
13938             case CounterStatus::Pass:
13939                 break;
13940             }
13941 
13942             // Move only single type of resources at once
13943             if (!VmaIsBufferImageGranularityConflict(moveData.type, currentType))
13944             {
13945                 // Try to fit allocation into free blocks
13946                 if (AllocInOtherBlock(firstFreeBlock, vector.GetBlockCount(), moveData, vector))
13947                     return false;
13948             }
13949 
13950             if (!VmaIsBufferImageGranularityConflict(moveData.type, VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL))
13951                 texturePresent = true;
13952             else if (!VmaIsBufferImageGranularityConflict(moveData.type, VMA_SUBALLOCATION_TYPE_BUFFER))
13953                 bufferPresent = true;
13954             else
13955                 otherPresent = true;
13956         }
13957     }
13958     return prevMoveCount == m_Moves.size();
13959 }
13960 #endif // _VMA_DEFRAGMENTATION_CONTEXT_FUNCTIONS
13961 
13962 #ifndef _VMA_POOL_T_FUNCTIONS
VmaPool_T(VmaAllocator hAllocator,const VmaPoolCreateInfo & createInfo,VkDeviceSize preferredBlockSize)13963 VmaPool_T::VmaPool_T(
13964     VmaAllocator hAllocator,
13965     const VmaPoolCreateInfo& createInfo,
13966     VkDeviceSize preferredBlockSize)
13967     : m_BlockVector(
13968         hAllocator,
13969         this, // hParentPool
13970         createInfo.memoryTypeIndex,
13971         createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
13972         createInfo.minBlockCount,
13973         createInfo.maxBlockCount,
13974         (createInfo.flags& VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
13975         createInfo.blockSize != 0, // explicitBlockSize
13976         createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK, // algorithm
13977         createInfo.priority,
13978         VMA_MAX(hAllocator->GetMemoryTypeMinAlignment(createInfo.memoryTypeIndex), createInfo.minAllocationAlignment),
13979         createInfo.pMemoryAllocateNext),
13980     m_Id(0),
13981     m_Name(VMA_NULL) {}
13982 
~VmaPool_T()13983 VmaPool_T::~VmaPool_T()
13984 {
13985     VMA_ASSERT(m_PrevPool == VMA_NULL && m_NextPool == VMA_NULL);
13986 }
13987 
SetName(const char * pName)13988 void VmaPool_T::SetName(const char* pName)
13989 {
13990     const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks();
13991     VmaFreeString(allocs, m_Name);
13992 
13993     if (pName != VMA_NULL)
13994     {
13995         m_Name = VmaCreateStringCopy(allocs, pName);
13996     }
13997     else
13998     {
13999         m_Name = VMA_NULL;
14000     }
14001 }
14002 #endif // _VMA_POOL_T_FUNCTIONS
14003 
14004 #ifndef _VMA_ALLOCATOR_T_FUNCTIONS
VmaAllocator_T(const VmaAllocatorCreateInfo * pCreateInfo)14005 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
14006     m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
14007     m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0),
14008     m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
14009     m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0),
14010     m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0),
14011     m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0),
14012     m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0),
14013     m_UseExtMemoryPriority((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT) != 0),
14014     m_hDevice(pCreateInfo->device),
14015     m_hInstance(pCreateInfo->instance),
14016     m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
14017     m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
14018         *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
14019     m_AllocationObjectAllocator(&m_AllocationCallbacks),
14020     m_HeapSizeLimitMask(0),
14021     m_DeviceMemoryCount(0),
14022     m_PreferredLargeHeapBlockSize(0),
14023     m_PhysicalDevice(pCreateInfo->physicalDevice),
14024     m_GpuDefragmentationMemoryTypeBits(UINT32_MAX),
14025     m_NextPoolId(0),
14026     m_GlobalMemoryTypeBits(UINT32_MAX)
14027 {
14028     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
14029     {
14030         m_UseKhrDedicatedAllocation = false;
14031         m_UseKhrBindMemory2 = false;
14032     }
14033 
14034     if(VMA_DEBUG_DETECT_CORRUPTION)
14035     {
14036         // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
14037         VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
14038     }
14039 
14040     VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance);
14041 
14042     if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
14043     {
14044 #if !(VMA_DEDICATED_ALLOCATION)
14045         if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
14046         {
14047             VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
14048         }
14049 #endif
14050 #if !(VMA_BIND_MEMORY2)
14051         if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0)
14052         {
14053             VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros.");
14054         }
14055 #endif
14056     }
14057 #if !(VMA_MEMORY_BUDGET)
14058     if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0)
14059     {
14060         VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros.");
14061     }
14062 #endif
14063 #if !(VMA_BUFFER_DEVICE_ADDRESS)
14064     if(m_UseKhrBufferDeviceAddress)
14065     {
14066         VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT is set but required extension or Vulkan 1.2 is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
14067     }
14068 #endif
14069 #if VMA_VULKAN_VERSION < 1002000
14070     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0))
14071     {
14072         VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros.");
14073     }
14074 #endif
14075 #if VMA_VULKAN_VERSION < 1001000
14076     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
14077     {
14078         VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros.");
14079     }
14080 #endif
14081 #if !(VMA_MEMORY_PRIORITY)
14082     if(m_UseExtMemoryPriority)
14083     {
14084         VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
14085     }
14086 #endif
14087 
14088     memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
14089     memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
14090     memset(&m_MemProps, 0, sizeof(m_MemProps));
14091 
14092     memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
14093     memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions));
14094 
14095 #if VMA_EXTERNAL_MEMORY
14096     memset(&m_TypeExternalMemoryHandleTypes, 0, sizeof(m_TypeExternalMemoryHandleTypes));
14097 #endif // #if VMA_EXTERNAL_MEMORY
14098 
14099     if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
14100     {
14101         m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData;
14102         m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
14103         m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
14104     }
14105 
14106     ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
14107 
14108     (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
14109     (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
14110 
14111     VMA_ASSERT(VmaIsPow2(VMA_MIN_ALIGNMENT));
14112     VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
14113     VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
14114     VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
14115 
14116     m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
14117         pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
14118 
14119     m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits();
14120 
14121 #if VMA_EXTERNAL_MEMORY
14122     if(pCreateInfo->pTypeExternalMemoryHandleTypes != VMA_NULL)
14123     {
14124         memcpy(m_TypeExternalMemoryHandleTypes, pCreateInfo->pTypeExternalMemoryHandleTypes,
14125             sizeof(VkExternalMemoryHandleTypeFlagsKHR) * GetMemoryTypeCount());
14126     }
14127 #endif // #if VMA_EXTERNAL_MEMORY
14128 
14129     if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
14130     {
14131         for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
14132         {
14133             const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
14134             if(limit != VK_WHOLE_SIZE)
14135             {
14136                 m_HeapSizeLimitMask |= 1u << heapIndex;
14137                 if(limit < m_MemProps.memoryHeaps[heapIndex].size)
14138                 {
14139                     m_MemProps.memoryHeaps[heapIndex].size = limit;
14140                 }
14141             }
14142         }
14143     }
14144 
14145     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14146     {
14147         // Create only supported types
14148         if((m_GlobalMemoryTypeBits & (1u << memTypeIndex)) != 0)
14149         {
14150             const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
14151             m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
14152                 this,
14153                 VK_NULL_HANDLE, // hParentPool
14154                 memTypeIndex,
14155                 preferredBlockSize,
14156                 0,
14157                 pCreateInfo->maxBlockCount,
14158                 GetBufferImageGranularity(),
14159                 false, // explicitBlockSize
14160                 0, // algorithm
14161                 0.5f, // priority (0.5 is the default per Vulkan spec)
14162                 GetMemoryTypeMinAlignment(memTypeIndex), // minAllocationAlignment
14163                 VMA_NULL); // // pMemoryAllocateNext
14164             // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
14165             // becase minBlockCount is 0.
14166         }
14167     }
14168 }
14169 
Init(const VmaAllocatorCreateInfo * pCreateInfo)14170 VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
14171 {
14172     VkResult res = VK_SUCCESS;
14173 
14174 #if VMA_MEMORY_BUDGET
14175     if(m_UseExtMemoryBudget)
14176     {
14177         UpdateVulkanBudget();
14178     }
14179 #endif // #if VMA_MEMORY_BUDGET
14180 
14181     return res;
14182 }
14183 
~VmaAllocator_T()14184 VmaAllocator_T::~VmaAllocator_T()
14185 {
14186     VMA_ASSERT(m_Pools.IsEmpty());
14187 
14188     for(size_t memTypeIndex = GetMemoryTypeCount(); memTypeIndex--; )
14189     {
14190         vma_delete(this, m_pBlockVectors[memTypeIndex]);
14191     }
14192 }
14193 
ImportVulkanFunctions(const VmaVulkanFunctions * pVulkanFunctions)14194 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
14195 {
14196 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
14197     ImportVulkanFunctions_Static();
14198 #endif
14199 
14200     if(pVulkanFunctions != VMA_NULL)
14201     {
14202         ImportVulkanFunctions_Custom(pVulkanFunctions);
14203     }
14204 
14205 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
14206     ImportVulkanFunctions_Dynamic();
14207 #endif
14208 
14209     ValidateVulkanFunctions();
14210 }
14211 
14212 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
14213 
ImportVulkanFunctions_Static()14214 void VmaAllocator_T::ImportVulkanFunctions_Static()
14215 {
14216     // Vulkan 1.0
14217     m_VulkanFunctions.vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)vkGetInstanceProcAddr;
14218     m_VulkanFunctions.vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)vkGetDeviceProcAddr;
14219     m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties;
14220     m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties;
14221     m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
14222     m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory;
14223     m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory;
14224     m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory;
14225     m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges;
14226     m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges;
14227     m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory;
14228     m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory;
14229     m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements;
14230     m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements;
14231     m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer;
14232     m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer;
14233     m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage;
14234     m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage;
14235     m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer;
14236 
14237     // Vulkan 1.1
14238 #if VMA_VULKAN_VERSION >= 1001000
14239     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
14240     {
14241         m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2;
14242         m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2;
14243         m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2;
14244         m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2;
14245         m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2;
14246     }
14247 #endif
14248 
14249 #if VMA_VULKAN_VERSION >= 1003000
14250     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0))
14251     {
14252         m_VulkanFunctions.vkGetDeviceBufferMemoryRequirements = (PFN_vkGetDeviceBufferMemoryRequirements)vkGetDeviceBufferMemoryRequirements;
14253         m_VulkanFunctions.vkGetDeviceImageMemoryRequirements = (PFN_vkGetDeviceImageMemoryRequirements)vkGetDeviceImageMemoryRequirements;
14254     }
14255 #endif
14256 }
14257 
14258 #endif // VMA_STATIC_VULKAN_FUNCTIONS == 1
14259 
ImportVulkanFunctions_Custom(const VmaVulkanFunctions * pVulkanFunctions)14260 void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions)
14261 {
14262     VMA_ASSERT(pVulkanFunctions != VMA_NULL);
14263 
14264 #define VMA_COPY_IF_NOT_NULL(funcName) \
14265     if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
14266 
14267     VMA_COPY_IF_NOT_NULL(vkGetInstanceProcAddr);
14268     VMA_COPY_IF_NOT_NULL(vkGetDeviceProcAddr);
14269     VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
14270     VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
14271     VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
14272     VMA_COPY_IF_NOT_NULL(vkFreeMemory);
14273     VMA_COPY_IF_NOT_NULL(vkMapMemory);
14274     VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
14275     VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
14276     VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
14277     VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
14278     VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
14279     VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
14280     VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
14281     VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
14282     VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
14283     VMA_COPY_IF_NOT_NULL(vkCreateImage);
14284     VMA_COPY_IF_NOT_NULL(vkDestroyImage);
14285     VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
14286 
14287 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
14288     VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
14289     VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
14290 #endif
14291 
14292 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
14293     VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR);
14294     VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR);
14295 #endif
14296 
14297 #if VMA_MEMORY_BUDGET
14298     VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR);
14299 #endif
14300 
14301 #if VMA_VULKAN_VERSION >= 1003000
14302     VMA_COPY_IF_NOT_NULL(vkGetDeviceBufferMemoryRequirements);
14303     VMA_COPY_IF_NOT_NULL(vkGetDeviceImageMemoryRequirements);
14304 #endif
14305 
14306 #undef VMA_COPY_IF_NOT_NULL
14307 }
14308 
14309 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
14310 
ImportVulkanFunctions_Dynamic()14311 void VmaAllocator_T::ImportVulkanFunctions_Dynamic()
14312 {
14313     VMA_ASSERT(m_VulkanFunctions.vkGetInstanceProcAddr && m_VulkanFunctions.vkGetDeviceProcAddr &&
14314         "To use VMA_DYNAMIC_VULKAN_FUNCTIONS in new versions of VMA you now have to pass "
14315         "VmaVulkanFunctions::vkGetInstanceProcAddr and vkGetDeviceProcAddr as VmaAllocatorCreateInfo::pVulkanFunctions. "
14316         "Other members can be null.");
14317 
14318 #define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \
14319     if(m_VulkanFunctions.memberName == VMA_NULL) \
14320         m_VulkanFunctions.memberName = \
14321             (functionPointerType)m_VulkanFunctions.vkGetInstanceProcAddr(m_hInstance, functionNameString);
14322 #define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \
14323     if(m_VulkanFunctions.memberName == VMA_NULL) \
14324         m_VulkanFunctions.memberName = \
14325             (functionPointerType)m_VulkanFunctions.vkGetDeviceProcAddr(m_hDevice, functionNameString);
14326 
14327     VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");
14328     VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties");
14329     VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory");
14330     VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory");
14331     VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory");
14332     VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory");
14333     VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges");
14334     VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges");
14335     VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory");
14336     VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory");
14337     VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements");
14338     VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements");
14339     VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer");
14340     VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer");
14341     VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage");
14342     VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage");
14343     VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer");
14344 
14345 #if VMA_VULKAN_VERSION >= 1001000
14346     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
14347     {
14348         VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2, "vkGetBufferMemoryRequirements2");
14349         VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2, "vkGetImageMemoryRequirements2");
14350         VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2, "vkBindBufferMemory2");
14351         VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2, "vkBindImageMemory2");
14352         VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2, "vkGetPhysicalDeviceMemoryProperties2");
14353     }
14354 #endif
14355 
14356 #if VMA_DEDICATED_ALLOCATION
14357     if(m_UseKhrDedicatedAllocation)
14358     {
14359         VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR");
14360         VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR");
14361     }
14362 #endif
14363 
14364 #if VMA_BIND_MEMORY2
14365     if(m_UseKhrBindMemory2)
14366     {
14367         VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR");
14368         VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR");
14369     }
14370 #endif // #if VMA_BIND_MEMORY2
14371 
14372 #if VMA_MEMORY_BUDGET
14373     if(m_UseExtMemoryBudget)
14374     {
14375         VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");
14376     }
14377 #endif // #if VMA_MEMORY_BUDGET
14378 
14379 #if VMA_VULKAN_VERSION >= 1003000
14380     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0))
14381     {
14382         VMA_FETCH_DEVICE_FUNC(vkGetDeviceBufferMemoryRequirements, PFN_vkGetDeviceBufferMemoryRequirements, "vkGetDeviceBufferMemoryRequirements");
14383         VMA_FETCH_DEVICE_FUNC(vkGetDeviceImageMemoryRequirements, PFN_vkGetDeviceImageMemoryRequirements, "vkGetDeviceImageMemoryRequirements");
14384     }
14385 #endif
14386 
14387 #undef VMA_FETCH_DEVICE_FUNC
14388 #undef VMA_FETCH_INSTANCE_FUNC
14389 }
14390 
14391 #endif // VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
14392 
ValidateVulkanFunctions()14393 void VmaAllocator_T::ValidateVulkanFunctions()
14394 {
14395     VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
14396     VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
14397     VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
14398     VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
14399     VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
14400     VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
14401     VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
14402     VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
14403     VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
14404     VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
14405     VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
14406     VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
14407     VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
14408     VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
14409     VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
14410     VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
14411     VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
14412 
14413 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
14414     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation)
14415     {
14416         VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
14417         VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
14418     }
14419 #endif
14420 
14421 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
14422     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2)
14423     {
14424         VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL);
14425         VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL);
14426     }
14427 #endif
14428 
14429 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
14430     if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
14431     {
14432         VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);
14433     }
14434 #endif
14435 
14436 #if VMA_VULKAN_VERSION >= 1003000
14437     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0))
14438     {
14439         VMA_ASSERT(m_VulkanFunctions.vkGetDeviceBufferMemoryRequirements != VMA_NULL);
14440         VMA_ASSERT(m_VulkanFunctions.vkGetDeviceImageMemoryRequirements != VMA_NULL);
14441     }
14442 #endif
14443 }
14444 
CalcPreferredBlockSize(uint32_t memTypeIndex)14445 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
14446 {
14447     const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
14448     const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
14449     const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
14450     return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32);
14451 }
14452 
AllocateMemoryOfType(VmaPool pool,VkDeviceSize size,VkDeviceSize alignment,bool dedicatedPreferred,VkBuffer dedicatedBuffer,VkImage dedicatedImage,VkFlags dedicatedBufferImageUsage,const VmaAllocationCreateInfo & createInfo,uint32_t memTypeIndex,VmaSuballocationType suballocType,VmaDedicatedAllocationList & dedicatedAllocations,VmaBlockVector & blockVector,size_t allocationCount,VmaAllocation * pAllocations)14453 VkResult VmaAllocator_T::AllocateMemoryOfType(
14454     VmaPool pool,
14455     VkDeviceSize size,
14456     VkDeviceSize alignment,
14457     bool dedicatedPreferred,
14458     VkBuffer dedicatedBuffer,
14459     VkImage dedicatedImage,
14460     VkFlags dedicatedBufferImageUsage,
14461     const VmaAllocationCreateInfo& createInfo,
14462     uint32_t memTypeIndex,
14463     VmaSuballocationType suballocType,
14464     VmaDedicatedAllocationList& dedicatedAllocations,
14465     VmaBlockVector& blockVector,
14466     size_t allocationCount,
14467     VmaAllocation* pAllocations)
14468 {
14469     VMA_ASSERT(pAllocations != VMA_NULL);
14470     VMA_DEBUG_LOG("  AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size);
14471 
14472     VmaAllocationCreateInfo finalCreateInfo = createInfo;
14473     VkResult res = CalcMemTypeParams(
14474         finalCreateInfo,
14475         memTypeIndex,
14476         size,
14477         allocationCount);
14478     if(res != VK_SUCCESS)
14479         return res;
14480 
14481     if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
14482     {
14483         return AllocateDedicatedMemory(
14484             pool,
14485             size,
14486             suballocType,
14487             dedicatedAllocations,
14488             memTypeIndex,
14489             (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
14490             (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
14491             (finalCreateInfo.flags &
14492                 (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0,
14493             (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0,
14494             finalCreateInfo.pUserData,
14495             finalCreateInfo.priority,
14496             dedicatedBuffer,
14497             dedicatedImage,
14498             dedicatedBufferImageUsage,
14499             allocationCount,
14500             pAllocations,
14501             blockVector.GetAllocationNextPtr());
14502     }
14503     else
14504     {
14505         const bool canAllocateDedicated =
14506             (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
14507             (pool == VK_NULL_HANDLE || !blockVector.HasExplicitBlockSize());
14508 
14509         if(canAllocateDedicated)
14510         {
14511             // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
14512             if(size > blockVector.GetPreferredBlockSize() / 2)
14513             {
14514                 dedicatedPreferred = true;
14515             }
14516             // Protection against creating each allocation as dedicated when we reach or exceed heap size/budget,
14517             // which can quickly deplete maxMemoryAllocationCount: Don't prefer dedicated allocations when above
14518             // 3/4 of the maximum allocation count.
14519             if(m_DeviceMemoryCount.load() > m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount * 3 / 4)
14520             {
14521                 dedicatedPreferred = false;
14522             }
14523 
14524             if(dedicatedPreferred)
14525             {
14526                 res = AllocateDedicatedMemory(
14527                     pool,
14528                     size,
14529                     suballocType,
14530                     dedicatedAllocations,
14531                     memTypeIndex,
14532                     (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
14533                     (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
14534                     (finalCreateInfo.flags &
14535                         (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0,
14536                     (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0,
14537                     finalCreateInfo.pUserData,
14538                     finalCreateInfo.priority,
14539                     dedicatedBuffer,
14540                     dedicatedImage,
14541                     dedicatedBufferImageUsage,
14542                     allocationCount,
14543                     pAllocations,
14544                     blockVector.GetAllocationNextPtr());
14545                 if(res == VK_SUCCESS)
14546                 {
14547                     // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
14548                     VMA_DEBUG_LOG("    Allocated as DedicatedMemory");
14549                     return VK_SUCCESS;
14550                 }
14551             }
14552         }
14553 
14554         res = blockVector.Allocate(
14555             size,
14556             alignment,
14557             finalCreateInfo,
14558             suballocType,
14559             allocationCount,
14560             pAllocations);
14561         if(res == VK_SUCCESS)
14562             return VK_SUCCESS;
14563 
14564         // Try dedicated memory.
14565         if(canAllocateDedicated && !dedicatedPreferred)
14566         {
14567             res = AllocateDedicatedMemory(
14568                 pool,
14569                 size,
14570                 suballocType,
14571                 dedicatedAllocations,
14572                 memTypeIndex,
14573                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
14574                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
14575                 (finalCreateInfo.flags &
14576                     (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0,
14577                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0,
14578                 finalCreateInfo.pUserData,
14579                 finalCreateInfo.priority,
14580                 dedicatedBuffer,
14581                 dedicatedImage,
14582                 dedicatedBufferImageUsage,
14583                 allocationCount,
14584                 pAllocations,
14585                 blockVector.GetAllocationNextPtr());
14586             if(res == VK_SUCCESS)
14587             {
14588                 // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
14589                 VMA_DEBUG_LOG("    Allocated as DedicatedMemory");
14590                 return VK_SUCCESS;
14591             }
14592         }
14593         // Everything failed: Return error code.
14594         VMA_DEBUG_LOG("    vkAllocateMemory FAILED");
14595         return res;
14596     }
14597 }
14598 
AllocateDedicatedMemory(VmaPool pool,VkDeviceSize size,VmaSuballocationType suballocType,VmaDedicatedAllocationList & dedicatedAllocations,uint32_t memTypeIndex,bool map,bool isUserDataString,bool isMappingAllowed,bool canAliasMemory,void * pUserData,float priority,VkBuffer dedicatedBuffer,VkImage dedicatedImage,VkFlags dedicatedBufferImageUsage,size_t allocationCount,VmaAllocation * pAllocations,const void * pNextChain)14599 VkResult VmaAllocator_T::AllocateDedicatedMemory(
14600     VmaPool pool,
14601     VkDeviceSize size,
14602     VmaSuballocationType suballocType,
14603     VmaDedicatedAllocationList& dedicatedAllocations,
14604     uint32_t memTypeIndex,
14605     bool map,
14606     bool isUserDataString,
14607     bool isMappingAllowed,
14608     bool canAliasMemory,
14609     void* pUserData,
14610     float priority,
14611     VkBuffer dedicatedBuffer,
14612     VkImage dedicatedImage,
14613     VkFlags dedicatedBufferImageUsage,
14614     size_t allocationCount,
14615     VmaAllocation* pAllocations,
14616     const void* pNextChain)
14617 {
14618     VMA_ASSERT(allocationCount > 0 && pAllocations);
14619 
14620     VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
14621     allocInfo.memoryTypeIndex = memTypeIndex;
14622     allocInfo.allocationSize = size;
14623     allocInfo.pNext = pNextChain;
14624 
14625 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
14626     VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
14627     if(!canAliasMemory)
14628     {
14629         if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
14630         {
14631             if(dedicatedBuffer != VK_NULL_HANDLE)
14632             {
14633                 VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
14634                 dedicatedAllocInfo.buffer = dedicatedBuffer;
14635                 VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
14636             }
14637             else if(dedicatedImage != VK_NULL_HANDLE)
14638             {
14639                 dedicatedAllocInfo.image = dedicatedImage;
14640                 VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
14641             }
14642         }
14643     }
14644 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
14645 
14646 #if VMA_BUFFER_DEVICE_ADDRESS
14647     VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
14648     if(m_UseKhrBufferDeviceAddress)
14649     {
14650         bool canContainBufferWithDeviceAddress = true;
14651         if(dedicatedBuffer != VK_NULL_HANDLE)
14652         {
14653             canContainBufferWithDeviceAddress = dedicatedBufferImageUsage == UINT32_MAX || // Usage flags unknown
14654                 (dedicatedBufferImageUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) != 0;
14655         }
14656         else if(dedicatedImage != VK_NULL_HANDLE)
14657         {
14658             canContainBufferWithDeviceAddress = false;
14659         }
14660         if(canContainBufferWithDeviceAddress)
14661         {
14662             allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
14663             VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
14664         }
14665     }
14666 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
14667 
14668 #if VMA_MEMORY_PRIORITY
14669     VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };
14670     if(m_UseExtMemoryPriority)
14671     {
14672         VMA_ASSERT(priority >= 0.f && priority <= 1.f);
14673         priorityInfo.priority = priority;
14674         VmaPnextChainPushFront(&allocInfo, &priorityInfo);
14675     }
14676 #endif // #if VMA_MEMORY_PRIORITY
14677 
14678 #if VMA_EXTERNAL_MEMORY
14679     // Attach VkExportMemoryAllocateInfoKHR if necessary.
14680     VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR };
14681     exportMemoryAllocInfo.handleTypes = GetExternalMemoryHandleTypeFlags(memTypeIndex);
14682     if(exportMemoryAllocInfo.handleTypes != 0)
14683     {
14684         VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo);
14685     }
14686 #endif // #if VMA_EXTERNAL_MEMORY
14687 
14688     size_t allocIndex;
14689     VkResult res = VK_SUCCESS;
14690     for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14691     {
14692         res = AllocateDedicatedMemoryPage(
14693             pool,
14694             size,
14695             suballocType,
14696             memTypeIndex,
14697             allocInfo,
14698             map,
14699             isUserDataString,
14700             isMappingAllowed,
14701             pUserData,
14702             pAllocations + allocIndex);
14703         if(res != VK_SUCCESS)
14704         {
14705             break;
14706         }
14707     }
14708 
14709     if(res == VK_SUCCESS)
14710     {
14711         for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14712         {
14713             dedicatedAllocations.Register(pAllocations[allocIndex]);
14714         }
14715         VMA_DEBUG_LOG("    Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
14716     }
14717     else
14718     {
14719         // Free all already created allocations.
14720         while(allocIndex--)
14721         {
14722             VmaAllocation currAlloc = pAllocations[allocIndex];
14723             VkDeviceMemory hMemory = currAlloc->GetMemory();
14724 
14725             /*
14726             There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
14727             before vkFreeMemory.
14728 
14729             if(currAlloc->GetMappedData() != VMA_NULL)
14730             {
14731                 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
14732             }
14733             */
14734 
14735             FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
14736             m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize());
14737             m_AllocationObjectAllocator.Free(currAlloc);
14738         }
14739 
14740         memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
14741     }
14742 
14743     return res;
14744 }
14745 
AllocateDedicatedMemoryPage(VmaPool pool,VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,const VkMemoryAllocateInfo & allocInfo,bool map,bool isUserDataString,bool isMappingAllowed,void * pUserData,VmaAllocation * pAllocation)14746 VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
14747     VmaPool pool,
14748     VkDeviceSize size,
14749     VmaSuballocationType suballocType,
14750     uint32_t memTypeIndex,
14751     const VkMemoryAllocateInfo& allocInfo,
14752     bool map,
14753     bool isUserDataString,
14754     bool isMappingAllowed,
14755     void* pUserData,
14756     VmaAllocation* pAllocation)
14757 {
14758     VkDeviceMemory hMemory = VK_NULL_HANDLE;
14759     VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
14760     if(res < 0)
14761     {
14762         VMA_DEBUG_LOG("    vkAllocateMemory FAILED");
14763         return res;
14764     }
14765 
14766     void* pMappedData = VMA_NULL;
14767     if(map)
14768     {
14769         res = (*m_VulkanFunctions.vkMapMemory)(
14770             m_hDevice,
14771             hMemory,
14772             0,
14773             VK_WHOLE_SIZE,
14774             0,
14775             &pMappedData);
14776         if(res < 0)
14777         {
14778             VMA_DEBUG_LOG("    vkMapMemory FAILED");
14779             FreeVulkanMemory(memTypeIndex, size, hMemory);
14780             return res;
14781         }
14782     }
14783 
14784     *pAllocation = m_AllocationObjectAllocator.Allocate(isMappingAllowed);
14785     (*pAllocation)->InitDedicatedAllocation(pool, memTypeIndex, hMemory, suballocType, pMappedData, size);
14786     if (isUserDataString)
14787         (*pAllocation)->SetName(this, (const char*)pUserData);
14788     else
14789         (*pAllocation)->SetUserData(this, pUserData);
14790     m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size);
14791     if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
14792     {
14793         FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
14794     }
14795 
14796     return VK_SUCCESS;
14797 }
14798 
GetBufferMemoryRequirements(VkBuffer hBuffer,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)14799 void VmaAllocator_T::GetBufferMemoryRequirements(
14800     VkBuffer hBuffer,
14801     VkMemoryRequirements& memReq,
14802     bool& requiresDedicatedAllocation,
14803     bool& prefersDedicatedAllocation) const
14804 {
14805 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
14806     if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
14807     {
14808         VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
14809         memReqInfo.buffer = hBuffer;
14810 
14811         VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
14812 
14813         VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
14814         VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
14815 
14816         (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
14817 
14818         memReq = memReq2.memoryRequirements;
14819         requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
14820         prefersDedicatedAllocation  = (memDedicatedReq.prefersDedicatedAllocation  != VK_FALSE);
14821     }
14822     else
14823 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
14824     {
14825         (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
14826         requiresDedicatedAllocation = false;
14827         prefersDedicatedAllocation  = false;
14828     }
14829 }
14830 
GetImageMemoryRequirements(VkImage hImage,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)14831 void VmaAllocator_T::GetImageMemoryRequirements(
14832     VkImage hImage,
14833     VkMemoryRequirements& memReq,
14834     bool& requiresDedicatedAllocation,
14835     bool& prefersDedicatedAllocation) const
14836 {
14837 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
14838     if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
14839     {
14840         VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
14841         memReqInfo.image = hImage;
14842 
14843         VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
14844 
14845         VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
14846         VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
14847 
14848         (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
14849 
14850         memReq = memReq2.memoryRequirements;
14851         requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
14852         prefersDedicatedAllocation  = (memDedicatedReq.prefersDedicatedAllocation  != VK_FALSE);
14853     }
14854     else
14855 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
14856     {
14857         (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
14858         requiresDedicatedAllocation = false;
14859         prefersDedicatedAllocation  = false;
14860     }
14861 }
14862 
FindMemoryTypeIndex(uint32_t memoryTypeBits,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkFlags bufImgUsage,uint32_t * pMemoryTypeIndex)14863 VkResult VmaAllocator_T::FindMemoryTypeIndex(
14864     uint32_t memoryTypeBits,
14865     const VmaAllocationCreateInfo* pAllocationCreateInfo,
14866     VkFlags bufImgUsage,
14867     uint32_t* pMemoryTypeIndex) const
14868 {
14869     memoryTypeBits &= GetGlobalMemoryTypeBits();
14870 
14871     if(pAllocationCreateInfo->memoryTypeBits != 0)
14872     {
14873         memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
14874     }
14875 
14876     VkMemoryPropertyFlags requiredFlags = 0, preferredFlags = 0, notPreferredFlags = 0;
14877     if(!FindMemoryPreferences(
14878         IsIntegratedGpu(),
14879         *pAllocationCreateInfo,
14880         bufImgUsage,
14881         requiredFlags, preferredFlags, notPreferredFlags))
14882     {
14883         return VK_ERROR_FEATURE_NOT_PRESENT;
14884     }
14885 
14886     *pMemoryTypeIndex = UINT32_MAX;
14887     uint32_t minCost = UINT32_MAX;
14888     for(uint32_t memTypeIndex = 0, memTypeBit = 1;
14889         memTypeIndex < GetMemoryTypeCount();
14890         ++memTypeIndex, memTypeBit <<= 1)
14891     {
14892         // This memory type is acceptable according to memoryTypeBits bitmask.
14893         if((memTypeBit & memoryTypeBits) != 0)
14894         {
14895             const VkMemoryPropertyFlags currFlags =
14896                 m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
14897             // This memory type contains requiredFlags.
14898             if((requiredFlags & ~currFlags) == 0)
14899             {
14900                 // Calculate cost as number of bits from preferredFlags not present in this memory type.
14901                 uint32_t currCost = VMA_COUNT_BITS_SET(preferredFlags & ~currFlags) +
14902                     VMA_COUNT_BITS_SET(currFlags & notPreferredFlags);
14903                 // Remember memory type with lowest cost.
14904                 if(currCost < minCost)
14905                 {
14906                     *pMemoryTypeIndex = memTypeIndex;
14907                     if(currCost == 0)
14908                     {
14909                         return VK_SUCCESS;
14910                     }
14911                     minCost = currCost;
14912                 }
14913             }
14914         }
14915     }
14916     return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
14917 }
14918 
CalcMemTypeParams(VmaAllocationCreateInfo & inoutCreateInfo,uint32_t memTypeIndex,VkDeviceSize size,size_t allocationCount)14919 VkResult VmaAllocator_T::CalcMemTypeParams(
14920     VmaAllocationCreateInfo& inoutCreateInfo,
14921     uint32_t memTypeIndex,
14922     VkDeviceSize size,
14923     size_t allocationCount)
14924 {
14925     // If memory type is not HOST_VISIBLE, disable MAPPED.
14926     if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
14927         (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
14928     {
14929         inoutCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
14930     }
14931 
14932     if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
14933         (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0)
14934     {
14935         const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
14936         VmaBudget heapBudget = {};
14937         GetHeapBudgets(&heapBudget, heapIndex, 1);
14938         if(heapBudget.usage + size * allocationCount > heapBudget.budget)
14939         {
14940             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14941         }
14942     }
14943     return VK_SUCCESS;
14944 }
14945 
CalcAllocationParams(VmaAllocationCreateInfo & inoutCreateInfo,bool dedicatedRequired,bool dedicatedPreferred)14946 VkResult VmaAllocator_T::CalcAllocationParams(
14947     VmaAllocationCreateInfo& inoutCreateInfo,
14948     bool dedicatedRequired,
14949     bool dedicatedPreferred)
14950 {
14951     VMA_ASSERT((inoutCreateInfo.flags &
14952         (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) !=
14953         (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT) &&
14954         "Specifying both flags VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT and VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT is incorrect.");
14955     VMA_ASSERT((((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT) == 0 ||
14956         (inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0)) &&
14957         "Specifying VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT requires also VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.");
14958     if(inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO || inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE || inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_HOST)
14959     {
14960         if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0)
14961         {
14962             VMA_ASSERT((inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0 &&
14963                 "When using VMA_ALLOCATION_CREATE_MAPPED_BIT and usage = VMA_MEMORY_USAGE_AUTO*, you must also specify VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.");
14964         }
14965     }
14966 
14967     // If memory is lazily allocated, it should be always dedicated.
14968     if(dedicatedRequired ||
14969         inoutCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED)
14970     {
14971         inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
14972     }
14973 
14974     if(inoutCreateInfo.pool != VK_NULL_HANDLE)
14975     {
14976         if(inoutCreateInfo.pool->m_BlockVector.HasExplicitBlockSize() &&
14977             (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
14978         {
14979             VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT while current custom pool doesn't support dedicated allocations.");
14980             return VK_ERROR_FEATURE_NOT_PRESENT;
14981         }
14982         inoutCreateInfo.priority = inoutCreateInfo.pool->m_BlockVector.GetPriority();
14983     }
14984 
14985     if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
14986         (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14987     {
14988         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
14989         return VK_ERROR_FEATURE_NOT_PRESENT;
14990     }
14991 
14992     if(VMA_DEBUG_ALWAYS_DEDICATED_MEMORY &&
14993         (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14994     {
14995         inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
14996     }
14997 
14998     // Non-auto USAGE values imply HOST_ACCESS flags.
14999     // And so does VMA_MEMORY_USAGE_UNKNOWN because it is used with custom pools.
15000     // Which specific flag is used doesn't matter. They change things only when used with VMA_MEMORY_USAGE_AUTO*.
15001     // Otherwise they just protect from assert on mapping.
15002     if(inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO &&
15003         inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE &&
15004         inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO_PREFER_HOST)
15005     {
15006         if((inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) == 0)
15007         {
15008             inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
15009         }
15010     }
15011 
15012     return VK_SUCCESS;
15013 }
15014 
AllocateMemory(const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,VkBuffer dedicatedBuffer,VkImage dedicatedImage,VkFlags dedicatedBufferImageUsage,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)15015 VkResult VmaAllocator_T::AllocateMemory(
15016     const VkMemoryRequirements& vkMemReq,
15017     bool requiresDedicatedAllocation,
15018     bool prefersDedicatedAllocation,
15019     VkBuffer dedicatedBuffer,
15020     VkImage dedicatedImage,
15021     VkFlags dedicatedBufferImageUsage,
15022     const VmaAllocationCreateInfo& createInfo,
15023     VmaSuballocationType suballocType,
15024     size_t allocationCount,
15025     VmaAllocation* pAllocations)
15026 {
15027     memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
15028 
15029     VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
15030 
15031     if(vkMemReq.size == 0)
15032     {
15033         return VK_ERROR_INITIALIZATION_FAILED;
15034     }
15035 
15036     VmaAllocationCreateInfo createInfoFinal = createInfo;
15037     VkResult res = CalcAllocationParams(createInfoFinal, requiresDedicatedAllocation, prefersDedicatedAllocation);
15038     if(res != VK_SUCCESS)
15039         return res;
15040 
15041     if(createInfoFinal.pool != VK_NULL_HANDLE)
15042     {
15043         VmaBlockVector& blockVector = createInfoFinal.pool->m_BlockVector;
15044         return AllocateMemoryOfType(
15045             createInfoFinal.pool,
15046             vkMemReq.size,
15047             vkMemReq.alignment,
15048             prefersDedicatedAllocation,
15049             dedicatedBuffer,
15050             dedicatedImage,
15051             dedicatedBufferImageUsage,
15052             createInfoFinal,
15053             blockVector.GetMemoryTypeIndex(),
15054             suballocType,
15055             createInfoFinal.pool->m_DedicatedAllocations,
15056             blockVector,
15057             allocationCount,
15058             pAllocations);
15059     }
15060     else
15061     {
15062         // Bit mask of memory Vulkan types acceptable for this allocation.
15063         uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
15064         uint32_t memTypeIndex = UINT32_MAX;
15065         res = FindMemoryTypeIndex(memoryTypeBits, &createInfoFinal, dedicatedBufferImageUsage, &memTypeIndex);
15066         // Can't find any single memory type matching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
15067         if(res != VK_SUCCESS)
15068             return res;
15069         do
15070         {
15071             VmaBlockVector* blockVector = m_pBlockVectors[memTypeIndex];
15072             VMA_ASSERT(blockVector && "Trying to use unsupported memory type!");
15073             res = AllocateMemoryOfType(
15074                 VK_NULL_HANDLE,
15075                 vkMemReq.size,
15076                 vkMemReq.alignment,
15077                 requiresDedicatedAllocation || prefersDedicatedAllocation,
15078                 dedicatedBuffer,
15079                 dedicatedImage,
15080                 dedicatedBufferImageUsage,
15081                 createInfoFinal,
15082                 memTypeIndex,
15083                 suballocType,
15084                 m_DedicatedAllocations[memTypeIndex],
15085                 *blockVector,
15086                 allocationCount,
15087                 pAllocations);
15088             // Allocation succeeded
15089             if(res == VK_SUCCESS)
15090                 return VK_SUCCESS;
15091 
15092             // Remove old memTypeIndex from list of possibilities.
15093             memoryTypeBits &= ~(1u << memTypeIndex);
15094             // Find alternative memTypeIndex.
15095             res = FindMemoryTypeIndex(memoryTypeBits, &createInfoFinal, dedicatedBufferImageUsage, &memTypeIndex);
15096         } while(res == VK_SUCCESS);
15097 
15098         // No other matching memory type index could be found.
15099         // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
15100         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15101     }
15102 }
15103 
FreeMemory(size_t allocationCount,const VmaAllocation * pAllocations)15104 void VmaAllocator_T::FreeMemory(
15105     size_t allocationCount,
15106     const VmaAllocation* pAllocations)
15107 {
15108     VMA_ASSERT(pAllocations);
15109 
15110     for(size_t allocIndex = allocationCount; allocIndex--; )
15111     {
15112         VmaAllocation allocation = pAllocations[allocIndex];
15113 
15114         if(allocation != VK_NULL_HANDLE)
15115         {
15116             if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
15117             {
15118                 FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
15119             }
15120 
15121             allocation->FreeName(this);
15122 
15123             switch(allocation->GetType())
15124             {
15125             case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15126                 {
15127                     VmaBlockVector* pBlockVector = VMA_NULL;
15128                     VmaPool hPool = allocation->GetParentPool();
15129                     if(hPool != VK_NULL_HANDLE)
15130                     {
15131                         pBlockVector = &hPool->m_BlockVector;
15132                     }
15133                     else
15134                     {
15135                         const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
15136                         pBlockVector = m_pBlockVectors[memTypeIndex];
15137                         VMA_ASSERT(pBlockVector && "Trying to free memory of unsupported type!");
15138                     }
15139                     pBlockVector->Free(allocation);
15140                 }
15141                 break;
15142             case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15143                 FreeDedicatedMemory(allocation);
15144                 break;
15145             default:
15146                 VMA_ASSERT(0);
15147             }
15148         }
15149     }
15150 }
15151 
CalculateStatistics(VmaTotalStatistics * pStats)15152 void VmaAllocator_T::CalculateStatistics(VmaTotalStatistics* pStats)
15153 {
15154     // Initialize.
15155     VmaClearDetailedStatistics(pStats->total);
15156     for(uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
15157         VmaClearDetailedStatistics(pStats->memoryType[i]);
15158     for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
15159         VmaClearDetailedStatistics(pStats->memoryHeap[i]);
15160 
15161     // Process default pools.
15162     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15163     {
15164         VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
15165         if (pBlockVector != VMA_NULL)
15166             pBlockVector->AddDetailedStatistics(pStats->memoryType[memTypeIndex]);
15167     }
15168 
15169     // Process custom pools.
15170     {
15171         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15172         for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
15173         {
15174             VmaBlockVector& blockVector = pool->m_BlockVector;
15175             const uint32_t memTypeIndex = blockVector.GetMemoryTypeIndex();
15176             blockVector.AddDetailedStatistics(pStats->memoryType[memTypeIndex]);
15177             pool->m_DedicatedAllocations.AddDetailedStatistics(pStats->memoryType[memTypeIndex]);
15178         }
15179     }
15180 
15181     // Process dedicated allocations.
15182     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15183     {
15184         m_DedicatedAllocations[memTypeIndex].AddDetailedStatistics(pStats->memoryType[memTypeIndex]);
15185     }
15186 
15187     // Sum from memory types to memory heaps.
15188     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15189     {
15190         const uint32_t memHeapIndex = m_MemProps.memoryTypes[memTypeIndex].heapIndex;
15191         VmaAddDetailedStatistics(pStats->memoryHeap[memHeapIndex], pStats->memoryType[memTypeIndex]);
15192     }
15193 
15194     // Sum from memory heaps to total.
15195     for(uint32_t memHeapIndex = 0; memHeapIndex < GetMemoryHeapCount(); ++memHeapIndex)
15196         VmaAddDetailedStatistics(pStats->total, pStats->memoryHeap[memHeapIndex]);
15197 
15198     VMA_ASSERT(pStats->total.statistics.allocationCount == 0 ||
15199         pStats->total.allocationSizeMax >= pStats->total.allocationSizeMin);
15200     VMA_ASSERT(pStats->total.unusedRangeCount == 0 ||
15201         pStats->total.unusedRangeSizeMax >= pStats->total.unusedRangeSizeMin);
15202 }
15203 
FreeEmptyBlock()15204 void VmaAllocator_T::FreeEmptyBlock()
15205 {
15206     for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) {
15207         VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
15208         VMA_ASSERT(pBlockVector);
15209         pBlockVector->FreeEmptyBlock();
15210     }
15211 }
15212 
GetHeapBudgets(VmaBudget * outBudgets,uint32_t firstHeap,uint32_t heapCount)15213 void VmaAllocator_T::GetHeapBudgets(VmaBudget* outBudgets, uint32_t firstHeap, uint32_t heapCount)
15214 {
15215 #if VMA_MEMORY_BUDGET
15216     if(m_UseExtMemoryBudget)
15217     {
15218         if(m_Budget.m_OperationsSinceBudgetFetch < 30)
15219         {
15220             VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex);
15221             for(uint32_t i = 0; i < heapCount; ++i, ++outBudgets)
15222             {
15223                 const uint32_t heapIndex = firstHeap + i;
15224 
15225                 outBudgets->statistics.blockCount = m_Budget.m_BlockCount[heapIndex];
15226                 outBudgets->statistics.allocationCount = m_Budget.m_AllocationCount[heapIndex];
15227                 outBudgets->statistics.blockBytes = m_Budget.m_BlockBytes[heapIndex];
15228                 outBudgets->statistics.allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
15229 
15230                 if(m_Budget.m_VulkanUsage[heapIndex] + outBudgets->statistics.blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex])
15231                 {
15232                     outBudgets->usage = m_Budget.m_VulkanUsage[heapIndex] +
15233                         outBudgets->statistics.blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
15234                 }
15235                 else
15236                 {
15237                     outBudgets->usage = 0;
15238                 }
15239 
15240                 // Have to take MIN with heap size because explicit HeapSizeLimit is included in it.
15241                 outBudgets->budget = VMA_MIN(
15242                     m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size);
15243             }
15244         }
15245         else
15246         {
15247             UpdateVulkanBudget(); // Outside of mutex lock
15248             GetHeapBudgets(outBudgets, firstHeap, heapCount); // Recursion
15249         }
15250     }
15251     else
15252 #endif
15253     {
15254         for(uint32_t i = 0; i < heapCount; ++i, ++outBudgets)
15255         {
15256             const uint32_t heapIndex = firstHeap + i;
15257 
15258             outBudgets->statistics.blockCount = m_Budget.m_BlockCount[heapIndex];
15259             outBudgets->statistics.allocationCount = m_Budget.m_AllocationCount[heapIndex];
15260             outBudgets->statistics.blockBytes = m_Budget.m_BlockBytes[heapIndex];
15261             outBudgets->statistics.allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
15262 
15263             outBudgets->usage = outBudgets->statistics.blockBytes;
15264             outBudgets->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
15265         }
15266     }
15267 }
15268 
GetAllocationInfo(VmaAllocation hAllocation,VmaAllocationInfo * pAllocationInfo)15269 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
15270 {
15271     pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
15272     pAllocationInfo->deviceMemory = hAllocation->GetMemory();
15273     pAllocationInfo->offset = hAllocation->GetOffset();
15274     pAllocationInfo->size = hAllocation->GetSize();
15275     pAllocationInfo->pMappedData = hAllocation->GetMappedData();
15276     pAllocationInfo->pUserData = hAllocation->GetUserData();
15277     pAllocationInfo->pName = hAllocation->GetName();
15278 }
15279 
CreatePool(const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)15280 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
15281 {
15282     VMA_DEBUG_LOG("  CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
15283 
15284     VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
15285 
15286     // Protection against uninitialized new structure member. If garbage data are left there, this pointer dereference would crash.
15287     if(pCreateInfo->pMemoryAllocateNext)
15288     {
15289         VMA_ASSERT(((const VkBaseInStructure*)pCreateInfo->pMemoryAllocateNext)->sType != 0);
15290     }
15291 
15292     if(newCreateInfo.maxBlockCount == 0)
15293     {
15294         newCreateInfo.maxBlockCount = SIZE_MAX;
15295     }
15296     if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
15297     {
15298         return VK_ERROR_INITIALIZATION_FAILED;
15299     }
15300     // Memory type index out of range or forbidden.
15301     if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() ||
15302         ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0)
15303     {
15304         return VK_ERROR_FEATURE_NOT_PRESENT;
15305     }
15306     if(newCreateInfo.minAllocationAlignment > 0)
15307     {
15308         VMA_ASSERT(VmaIsPow2(newCreateInfo.minAllocationAlignment));
15309     }
15310 
15311     const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
15312 
15313     *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
15314 
15315     VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
15316     if(res != VK_SUCCESS)
15317     {
15318         vma_delete(this, *pPool);
15319         *pPool = VMA_NULL;
15320         return res;
15321     }
15322 
15323     // Add to m_Pools.
15324     {
15325         VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15326         (*pPool)->SetId(m_NextPoolId++);
15327         m_Pools.PushBack(*pPool);
15328     }
15329 
15330     return VK_SUCCESS;
15331 }
15332 
DestroyPool(VmaPool pool)15333 void VmaAllocator_T::DestroyPool(VmaPool pool)
15334 {
15335     // Remove from m_Pools.
15336     {
15337         VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15338         m_Pools.Remove(pool);
15339     }
15340 
15341     vma_delete(this, pool);
15342 }
15343 
GetPoolStatistics(VmaPool pool,VmaStatistics * pPoolStats)15344 void VmaAllocator_T::GetPoolStatistics(VmaPool pool, VmaStatistics* pPoolStats)
15345 {
15346     VmaClearStatistics(*pPoolStats);
15347     pool->m_BlockVector.AddStatistics(*pPoolStats);
15348     pool->m_DedicatedAllocations.AddStatistics(*pPoolStats);
15349 }
15350 
CalculatePoolStatistics(VmaPool pool,VmaDetailedStatistics * pPoolStats)15351 void VmaAllocator_T::CalculatePoolStatistics(VmaPool pool, VmaDetailedStatistics* pPoolStats)
15352 {
15353     VmaClearDetailedStatistics(*pPoolStats);
15354     pool->m_BlockVector.AddDetailedStatistics(*pPoolStats);
15355     pool->m_DedicatedAllocations.AddDetailedStatistics(*pPoolStats);
15356 }
15357 
SetCurrentFrameIndex(uint32_t frameIndex)15358 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
15359 {
15360     m_CurrentFrameIndex.store(frameIndex);
15361 
15362 #if VMA_MEMORY_BUDGET
15363     if(m_UseExtMemoryBudget)
15364     {
15365         UpdateVulkanBudget();
15366     }
15367 #endif // #if VMA_MEMORY_BUDGET
15368 }
15369 
CheckPoolCorruption(VmaPool hPool)15370 VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
15371 {
15372     return hPool->m_BlockVector.CheckCorruption();
15373 }
15374 
CheckCorruption(uint32_t memoryTypeBits)15375 VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
15376 {
15377     VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
15378 
15379     // Process default pools.
15380     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15381     {
15382         VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
15383         if(pBlockVector != VMA_NULL)
15384         {
15385             VkResult localRes = pBlockVector->CheckCorruption();
15386             switch(localRes)
15387             {
15388             case VK_ERROR_FEATURE_NOT_PRESENT:
15389                 break;
15390             case VK_SUCCESS:
15391                 finalRes = VK_SUCCESS;
15392                 break;
15393             default:
15394                 return localRes;
15395             }
15396         }
15397     }
15398 
15399     // Process custom pools.
15400     {
15401         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15402         for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
15403         {
15404             if(((1u << pool->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
15405             {
15406                 VkResult localRes = pool->m_BlockVector.CheckCorruption();
15407                 switch(localRes)
15408                 {
15409                 case VK_ERROR_FEATURE_NOT_PRESENT:
15410                     break;
15411                 case VK_SUCCESS:
15412                     finalRes = VK_SUCCESS;
15413                     break;
15414                 default:
15415                     return localRes;
15416                 }
15417             }
15418         }
15419     }
15420 
15421     return finalRes;
15422 }
15423 
AllocateVulkanMemory(const VkMemoryAllocateInfo * pAllocateInfo,VkDeviceMemory * pMemory)15424 VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
15425 {
15426     AtomicTransactionalIncrement<uint32_t> deviceMemoryCountIncrement;
15427     const uint64_t prevDeviceMemoryCount = deviceMemoryCountIncrement.Increment(&m_DeviceMemoryCount);
15428 #if VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT
15429     if(prevDeviceMemoryCount >= m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount)
15430     {
15431         return VK_ERROR_TOO_MANY_OBJECTS;
15432     }
15433 #endif
15434 
15435     const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
15436 
15437     // HeapSizeLimit is in effect for this heap.
15438     if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0)
15439     {
15440         const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
15441         VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex];
15442         for(;;)
15443         {
15444             const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize;
15445             if(blockBytesAfterAllocation > heapSize)
15446             {
15447                 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15448             }
15449             if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation))
15450             {
15451                 break;
15452             }
15453         }
15454     }
15455     else
15456     {
15457         m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize;
15458     }
15459     ++m_Budget.m_BlockCount[heapIndex];
15460 
15461     // VULKAN CALL vkAllocateMemory.
15462     VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
15463 
15464     if(res == VK_SUCCESS)
15465     {
15466 #if VMA_MEMORY_BUDGET
15467         ++m_Budget.m_OperationsSinceBudgetFetch;
15468 #endif
15469 
15470         // Informative callback.
15471         if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
15472         {
15473             (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData);
15474         }
15475 
15476         deviceMemoryCountIncrement.Commit();
15477     }
15478     else
15479     {
15480         --m_Budget.m_BlockCount[heapIndex];
15481         m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize;
15482     }
15483 
15484     return res;
15485 }
15486 
FreeVulkanMemory(uint32_t memoryType,VkDeviceSize size,VkDeviceMemory hMemory)15487 void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
15488 {
15489     // Informative callback.
15490     if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
15491     {
15492         (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData);
15493     }
15494 
15495     // VULKAN CALL vkFreeMemory.
15496     (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
15497 
15498     const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memoryType);
15499     --m_Budget.m_BlockCount[heapIndex];
15500     m_Budget.m_BlockBytes[heapIndex] -= size;
15501 
15502     --m_DeviceMemoryCount;
15503 }
15504 
BindVulkanBuffer(VkDeviceMemory memory,VkDeviceSize memoryOffset,VkBuffer buffer,const void * pNext)15505 VkResult VmaAllocator_T::BindVulkanBuffer(
15506     VkDeviceMemory memory,
15507     VkDeviceSize memoryOffset,
15508     VkBuffer buffer,
15509     const void* pNext)
15510 {
15511     if(pNext != VMA_NULL)
15512     {
15513 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
15514         if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
15515             m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL)
15516         {
15517             VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR };
15518             bindBufferMemoryInfo.pNext = pNext;
15519             bindBufferMemoryInfo.buffer = buffer;
15520             bindBufferMemoryInfo.memory = memory;
15521             bindBufferMemoryInfo.memoryOffset = memoryOffset;
15522             return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
15523         }
15524         else
15525 #endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
15526         {
15527             return VK_ERROR_EXTENSION_NOT_PRESENT;
15528         }
15529     }
15530     else
15531     {
15532         return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset);
15533     }
15534 }
15535 
BindVulkanImage(VkDeviceMemory memory,VkDeviceSize memoryOffset,VkImage image,const void * pNext)15536 VkResult VmaAllocator_T::BindVulkanImage(
15537     VkDeviceMemory memory,
15538     VkDeviceSize memoryOffset,
15539     VkImage image,
15540     const void* pNext)
15541 {
15542     if(pNext != VMA_NULL)
15543     {
15544 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
15545         if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
15546             m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL)
15547         {
15548             VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR };
15549             bindBufferMemoryInfo.pNext = pNext;
15550             bindBufferMemoryInfo.image = image;
15551             bindBufferMemoryInfo.memory = memory;
15552             bindBufferMemoryInfo.memoryOffset = memoryOffset;
15553             return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
15554         }
15555         else
15556 #endif // #if VMA_BIND_MEMORY2
15557         {
15558             return VK_ERROR_EXTENSION_NOT_PRESENT;
15559         }
15560     }
15561     else
15562     {
15563         return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset);
15564     }
15565 }
15566 
Map(VmaAllocation hAllocation,void ** ppData)15567 VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
15568 {
15569     switch(hAllocation->GetType())
15570     {
15571     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15572         {
15573             VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
15574             char *pBytes = VMA_NULL;
15575             VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
15576             if(res == VK_SUCCESS)
15577             {
15578                 *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
15579                 hAllocation->BlockAllocMap();
15580             }
15581             return res;
15582         }
15583     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15584         return hAllocation->DedicatedAllocMap(this, ppData);
15585     default:
15586         VMA_ASSERT(0);
15587         return VK_ERROR_MEMORY_MAP_FAILED;
15588     }
15589 }
15590 
Unmap(VmaAllocation hAllocation)15591 void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
15592 {
15593     switch(hAllocation->GetType())
15594     {
15595     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15596         {
15597             VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
15598             hAllocation->BlockAllocUnmap();
15599             pBlock->Unmap(this, 1);
15600         }
15601         break;
15602     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15603         hAllocation->DedicatedAllocUnmap(this);
15604         break;
15605     default:
15606         VMA_ASSERT(0);
15607     }
15608 }
15609 
BindBufferMemory(VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkBuffer hBuffer,const void * pNext)15610 VkResult VmaAllocator_T::BindBufferMemory(
15611     VmaAllocation hAllocation,
15612     VkDeviceSize allocationLocalOffset,
15613     VkBuffer hBuffer,
15614     const void* pNext)
15615 {
15616     VkResult res = VK_SUCCESS;
15617     switch(hAllocation->GetType())
15618     {
15619     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15620         res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext);
15621         break;
15622     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15623     {
15624         VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
15625         VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block.");
15626         res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext);
15627         break;
15628     }
15629     default:
15630         VMA_ASSERT(0);
15631     }
15632     return res;
15633 }
15634 
BindImageMemory(VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkImage hImage,const void * pNext)15635 VkResult VmaAllocator_T::BindImageMemory(
15636     VmaAllocation hAllocation,
15637     VkDeviceSize allocationLocalOffset,
15638     VkImage hImage,
15639     const void* pNext)
15640 {
15641     VkResult res = VK_SUCCESS;
15642     switch(hAllocation->GetType())
15643     {
15644     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15645         res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext);
15646         break;
15647     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15648     {
15649         VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
15650         VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block.");
15651         res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext);
15652         break;
15653     }
15654     default:
15655         VMA_ASSERT(0);
15656     }
15657     return res;
15658 }
15659 
FlushOrInvalidateAllocation(VmaAllocation hAllocation,VkDeviceSize offset,VkDeviceSize size,VMA_CACHE_OPERATION op)15660 VkResult VmaAllocator_T::FlushOrInvalidateAllocation(
15661     VmaAllocation hAllocation,
15662     VkDeviceSize offset, VkDeviceSize size,
15663     VMA_CACHE_OPERATION op)
15664 {
15665     VkResult res = VK_SUCCESS;
15666 
15667     VkMappedMemoryRange memRange = {};
15668     if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange))
15669     {
15670         switch(op)
15671         {
15672         case VMA_CACHE_FLUSH:
15673             res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
15674             break;
15675         case VMA_CACHE_INVALIDATE:
15676             res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
15677             break;
15678         default:
15679             VMA_ASSERT(0);
15680         }
15681     }
15682     // else: Just ignore this call.
15683     return res;
15684 }
15685 
FlushOrInvalidateAllocations(uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes,VMA_CACHE_OPERATION op)15686 VkResult VmaAllocator_T::FlushOrInvalidateAllocations(
15687     uint32_t allocationCount,
15688     const VmaAllocation* allocations,
15689     const VkDeviceSize* offsets, const VkDeviceSize* sizes,
15690     VMA_CACHE_OPERATION op)
15691 {
15692     typedef VmaStlAllocator<VkMappedMemoryRange> RangeAllocator;
15693     typedef VmaSmallVector<VkMappedMemoryRange, RangeAllocator, 16> RangeVector;
15694     RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks()));
15695 
15696     for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
15697     {
15698         const VmaAllocation alloc = allocations[allocIndex];
15699         const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0;
15700         const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE;
15701         VkMappedMemoryRange newRange;
15702         if(GetFlushOrInvalidateRange(alloc, offset, size, newRange))
15703         {
15704             ranges.push_back(newRange);
15705         }
15706     }
15707 
15708     VkResult res = VK_SUCCESS;
15709     if(!ranges.empty())
15710     {
15711         switch(op)
15712         {
15713         case VMA_CACHE_FLUSH:
15714             res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
15715             break;
15716         case VMA_CACHE_INVALIDATE:
15717             res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
15718             break;
15719         default:
15720             VMA_ASSERT(0);
15721         }
15722     }
15723     // else: Just ignore this call.
15724     return res;
15725 }
15726 
FreeDedicatedMemory(const VmaAllocation allocation)15727 void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
15728 {
15729     VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
15730 
15731     const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
15732     VmaPool parentPool = allocation->GetParentPool();
15733     if(parentPool == VK_NULL_HANDLE)
15734     {
15735         // Default pool
15736         m_DedicatedAllocations[memTypeIndex].Unregister(allocation);
15737     }
15738     else
15739     {
15740         // Custom pool
15741         parentPool->m_DedicatedAllocations.Unregister(allocation);
15742     }
15743 
15744     VkDeviceMemory hMemory = allocation->GetMemory();
15745 
15746     /*
15747     There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
15748     before vkFreeMemory.
15749 
15750     if(allocation->GetMappedData() != VMA_NULL)
15751     {
15752         (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
15753     }
15754     */
15755 
15756     FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
15757 
15758     m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
15759     m_AllocationObjectAllocator.Free(allocation);
15760 
15761     VMA_DEBUG_LOG("    Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
15762 }
15763 
CalculateGpuDefragmentationMemoryTypeBits()15764 uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const
15765 {
15766     VkBufferCreateInfo dummyBufCreateInfo;
15767     VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo);
15768 
15769     uint32_t memoryTypeBits = 0;
15770 
15771     // Create buffer.
15772     VkBuffer buf = VK_NULL_HANDLE;
15773     VkResult res = (*GetVulkanFunctions().vkCreateBuffer)(
15774         m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf);
15775     if(res == VK_SUCCESS)
15776     {
15777         // Query for supported memory types.
15778         VkMemoryRequirements memReq;
15779         (*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq);
15780         memoryTypeBits = memReq.memoryTypeBits;
15781 
15782         // Destroy buffer.
15783         (*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks());
15784     }
15785 
15786     return memoryTypeBits;
15787 }
15788 
CalculateGlobalMemoryTypeBits()15789 uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const
15790 {
15791     // Make sure memory information is already fetched.
15792     VMA_ASSERT(GetMemoryTypeCount() > 0);
15793 
15794     uint32_t memoryTypeBits = UINT32_MAX;
15795 
15796     if(!m_UseAmdDeviceCoherentMemory)
15797     {
15798         // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD.
15799         for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15800         {
15801             if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
15802             {
15803                 memoryTypeBits &= ~(1u << memTypeIndex);
15804             }
15805         }
15806     }
15807 
15808     return memoryTypeBits;
15809 }
15810 
GetFlushOrInvalidateRange(VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size,VkMappedMemoryRange & outRange)15811 bool VmaAllocator_T::GetFlushOrInvalidateRange(
15812     VmaAllocation allocation,
15813     VkDeviceSize offset, VkDeviceSize size,
15814     VkMappedMemoryRange& outRange) const
15815 {
15816     const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
15817     if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
15818     {
15819         const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
15820         const VkDeviceSize allocationSize = allocation->GetSize();
15821         VMA_ASSERT(offset <= allocationSize);
15822 
15823         outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
15824         outRange.pNext = VMA_NULL;
15825         outRange.memory = allocation->GetMemory();
15826 
15827         switch(allocation->GetType())
15828         {
15829         case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15830             outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
15831             if(size == VK_WHOLE_SIZE)
15832             {
15833                 outRange.size = allocationSize - outRange.offset;
15834             }
15835             else
15836             {
15837                 VMA_ASSERT(offset + size <= allocationSize);
15838                 outRange.size = VMA_MIN(
15839                     VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize),
15840                     allocationSize - outRange.offset);
15841             }
15842             break;
15843         case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15844         {
15845             // 1. Still within this allocation.
15846             outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
15847             if(size == VK_WHOLE_SIZE)
15848             {
15849                 size = allocationSize - offset;
15850             }
15851             else
15852             {
15853                 VMA_ASSERT(offset + size <= allocationSize);
15854             }
15855             outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize);
15856 
15857             // 2. Adjust to whole block.
15858             const VkDeviceSize allocationOffset = allocation->GetOffset();
15859             VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
15860             const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize();
15861             outRange.offset += allocationOffset;
15862             outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset);
15863 
15864             break;
15865         }
15866         default:
15867             VMA_ASSERT(0);
15868         }
15869         return true;
15870     }
15871     return false;
15872 }
15873 
15874 #if VMA_MEMORY_BUDGET
UpdateVulkanBudget()15875 void VmaAllocator_T::UpdateVulkanBudget()
15876 {
15877     VMA_ASSERT(m_UseExtMemoryBudget);
15878 
15879     VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR };
15880 
15881     VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };
15882     VmaPnextChainPushFront(&memProps, &budgetProps);
15883 
15884     GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps);
15885 
15886     {
15887         VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex);
15888 
15889         for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
15890         {
15891             m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex];
15892             m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex];
15893             m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load();
15894 
15895             // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size.
15896             if(m_Budget.m_VulkanBudget[heapIndex] == 0)
15897             {
15898                 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
15899             }
15900             else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size)
15901             {
15902                 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size;
15903             }
15904             if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0)
15905             {
15906                 m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
15907             }
15908         }
15909         m_Budget.m_OperationsSinceBudgetFetch = 0;
15910     }
15911 }
15912 #endif // VMA_MEMORY_BUDGET
15913 
FillAllocation(const VmaAllocation hAllocation,uint8_t pattern)15914 void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
15915 {
15916     if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
15917         hAllocation->IsMappingAllowed() &&
15918         (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
15919     {
15920         void* pData = VMA_NULL;
15921         VkResult res = Map(hAllocation, &pData);
15922         if(res == VK_SUCCESS)
15923         {
15924             memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
15925             FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
15926             Unmap(hAllocation);
15927         }
15928         else
15929         {
15930             VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
15931         }
15932     }
15933 }
15934 
GetGpuDefragmentationMemoryTypeBits()15935 uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits()
15936 {
15937     uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load();
15938     if(memoryTypeBits == UINT32_MAX)
15939     {
15940         memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits();
15941         m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits);
15942     }
15943     return memoryTypeBits;
15944 }
15945 
15946 #if VMA_STATS_STRING_ENABLED
PrintDetailedMap(VmaJsonWriter & json)15947 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
15948 {
15949     json.WriteString("DefaultPools");
15950     json.BeginObject();
15951     {
15952         for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15953         {
15954             VmaBlockVector* pBlockVector = m_pBlockVectors[memTypeIndex];
15955             VmaDedicatedAllocationList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex];
15956             if (pBlockVector != VMA_NULL)
15957             {
15958                 json.BeginString("Type ");
15959                 json.ContinueString(memTypeIndex);
15960                 json.EndString();
15961                 json.BeginObject();
15962                 {
15963                     json.WriteString("PreferredBlockSize");
15964                     json.WriteNumber(pBlockVector->GetPreferredBlockSize());
15965 
15966                     json.WriteString("Blocks");
15967                     pBlockVector->PrintDetailedMap(json);
15968 
15969                     json.WriteString("DedicatedAllocations");
15970                     dedicatedAllocList.BuildStatsString(json);
15971                 }
15972                 json.EndObject();
15973             }
15974         }
15975     }
15976     json.EndObject();
15977 
15978     json.WriteString("CustomPools");
15979     json.BeginObject();
15980     {
15981         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15982         if (!m_Pools.IsEmpty())
15983         {
15984             for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15985             {
15986                 bool displayType = true;
15987                 size_t index = 0;
15988                 for (VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
15989                 {
15990                     VmaBlockVector& blockVector = pool->m_BlockVector;
15991                     if (blockVector.GetMemoryTypeIndex() == memTypeIndex)
15992                     {
15993                         if (displayType)
15994                         {
15995                             json.BeginString("Type ");
15996                             json.ContinueString(memTypeIndex);
15997                             json.EndString();
15998                             json.BeginArray();
15999                             displayType = false;
16000                         }
16001 
16002                         json.BeginObject();
16003                         {
16004                             json.WriteString("Name");
16005                             json.BeginString();
16006                             json.ContinueString_Size(index++);
16007                             if (pool->GetName())
16008                             {
16009                                 json.ContinueString(" - ");
16010                                 json.ContinueString(pool->GetName());
16011                             }
16012                             json.EndString();
16013 
16014                             json.WriteString("PreferredBlockSize");
16015                             json.WriteNumber(blockVector.GetPreferredBlockSize());
16016 
16017                             json.WriteString("Blocks");
16018                             blockVector.PrintDetailedMap(json);
16019 
16020                             json.WriteString("DedicatedAllocations");
16021                             pool->m_DedicatedAllocations.BuildStatsString(json);
16022                         }
16023                         json.EndObject();
16024                     }
16025                 }
16026 
16027                 if (!displayType)
16028                     json.EndArray();
16029             }
16030         }
16031     }
16032     json.EndObject();
16033 }
16034 #endif // VMA_STATS_STRING_ENABLED
16035 #endif // _VMA_ALLOCATOR_T_FUNCTIONS
16036 
16037 
16038 #ifndef _VMA_PUBLIC_INTERFACE
vmaCreateAllocator(const VmaAllocatorCreateInfo * pCreateInfo,VmaAllocator * pAllocator)16039 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
16040     const VmaAllocatorCreateInfo* pCreateInfo,
16041     VmaAllocator* pAllocator)
16042 {
16043     VMA_ASSERT(pCreateInfo && pAllocator);
16044     VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 ||
16045         (VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 3));
16046     VMA_DEBUG_LOG("vmaCreateAllocator");
16047     *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
16048     VkResult result = (*pAllocator)->Init(pCreateInfo);
16049     if(result < 0)
16050     {
16051         vma_delete(pCreateInfo->pAllocationCallbacks, *pAllocator);
16052         *pAllocator = VK_NULL_HANDLE;
16053     }
16054     return result;
16055 }
16056 
vmaDestroyAllocator(VmaAllocator allocator)16057 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
16058     VmaAllocator allocator)
16059 {
16060     if(allocator != VK_NULL_HANDLE)
16061     {
16062         VMA_DEBUG_LOG("vmaDestroyAllocator");
16063         VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks; // Have to copy the callbacks when destroying.
16064         vma_delete(&allocationCallbacks, allocator);
16065     }
16066 }
16067 
vmaGetAllocatorInfo(VmaAllocator allocator,VmaAllocatorInfo * pAllocatorInfo)16068 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo)
16069 {
16070     VMA_ASSERT(allocator && pAllocatorInfo);
16071     pAllocatorInfo->instance = allocator->m_hInstance;
16072     pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice();
16073     pAllocatorInfo->device = allocator->m_hDevice;
16074 }
16075 
vmaGetPhysicalDeviceProperties(VmaAllocator allocator,const VkPhysicalDeviceProperties ** ppPhysicalDeviceProperties)16076 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
16077     VmaAllocator allocator,
16078     const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
16079 {
16080     VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
16081     *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
16082 }
16083 
vmaGetMemoryProperties(VmaAllocator allocator,const VkPhysicalDeviceMemoryProperties ** ppPhysicalDeviceMemoryProperties)16084 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
16085     VmaAllocator allocator,
16086     const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
16087 {
16088     VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
16089     *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
16090 }
16091 
vmaGetMemoryTypeProperties(VmaAllocator allocator,uint32_t memoryTypeIndex,VkMemoryPropertyFlags * pFlags)16092 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
16093     VmaAllocator allocator,
16094     uint32_t memoryTypeIndex,
16095     VkMemoryPropertyFlags* pFlags)
16096 {
16097     VMA_ASSERT(allocator && pFlags);
16098     VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
16099     *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
16100 }
16101 
vmaSetCurrentFrameIndex(VmaAllocator allocator,uint32_t frameIndex)16102 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
16103     VmaAllocator allocator,
16104     uint32_t frameIndex)
16105 {
16106     VMA_ASSERT(allocator);
16107 
16108     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16109 
16110     allocator->SetCurrentFrameIndex(frameIndex);
16111 }
16112 
vmaCalculateStatistics(VmaAllocator allocator,VmaTotalStatistics * pStats)16113 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStatistics(
16114     VmaAllocator allocator,
16115     VmaTotalStatistics* pStats)
16116 {
16117     VMA_ASSERT(allocator && pStats);
16118     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16119     allocator->CalculateStatistics(pStats);
16120 }
16121 
vmaFreeEmptyBlock(VmaAllocator allocator)16122 VMA_CALL_PRE void VMA_CALL_POST vmaFreeEmptyBlock(
16123     VmaAllocator allocator)
16124 {
16125     VMA_ASSERT(allocator);
16126     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16127     allocator->FreeEmptyBlock();
16128 }
16129 
vmaGetHeapBudgets(VmaAllocator allocator,VmaBudget * pBudgets)16130 VMA_CALL_PRE void VMA_CALL_POST vmaGetHeapBudgets(
16131     VmaAllocator allocator,
16132     VmaBudget* pBudgets)
16133 {
16134     VMA_ASSERT(allocator && pBudgets);
16135     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16136     allocator->GetHeapBudgets(pBudgets, 0, allocator->GetMemoryHeapCount());
16137 }
16138 
16139 #if VMA_STATS_STRING_ENABLED
16140 
vmaBuildStatsString(VmaAllocator allocator,char ** ppStatsString,VkBool32 detailedMap)16141 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
16142     VmaAllocator allocator,
16143     char** ppStatsString,
16144     VkBool32 detailedMap)
16145 {
16146     VMA_ASSERT(allocator && ppStatsString);
16147     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16148 
16149     VmaStringBuilder sb(allocator->GetAllocationCallbacks());
16150     {
16151         VmaBudget budgets[VK_MAX_MEMORY_HEAPS];
16152         allocator->GetHeapBudgets(budgets, 0, allocator->GetMemoryHeapCount());
16153 
16154         VmaTotalStatistics stats;
16155         allocator->CalculateStatistics(&stats);
16156 
16157         VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
16158         json.BeginObject();
16159         {
16160             json.WriteString("General");
16161             json.BeginObject();
16162             {
16163                 const VkPhysicalDeviceProperties& deviceProperties = allocator->m_PhysicalDeviceProperties;
16164                 const VkPhysicalDeviceMemoryProperties& memoryProperties = allocator->m_MemProps;
16165 
16166                 json.WriteString("API");
16167                 json.WriteString("Vulkan");
16168 
16169                 json.WriteString("apiVersion");
16170                 json.BeginString();
16171                 json.ContinueString(VK_API_VERSION_MAJOR(deviceProperties.apiVersion));
16172                 json.ContinueString(".");
16173                 json.ContinueString(VK_API_VERSION_MINOR(deviceProperties.apiVersion));
16174                 json.ContinueString(".");
16175                 json.ContinueString(VK_API_VERSION_PATCH(deviceProperties.apiVersion));
16176                 json.EndString();
16177 
16178                 json.WriteString("GPU");
16179                 json.WriteString(deviceProperties.deviceName);
16180                 json.WriteString("deviceType");
16181                 json.WriteNumber(static_cast<uint32_t>(deviceProperties.deviceType));
16182 
16183                 json.WriteString("maxMemoryAllocationCount");
16184                 json.WriteNumber(deviceProperties.limits.maxMemoryAllocationCount);
16185                 json.WriteString("bufferImageGranularity");
16186                 json.WriteNumber(deviceProperties.limits.bufferImageGranularity);
16187                 json.WriteString("nonCoherentAtomSize");
16188                 json.WriteNumber(deviceProperties.limits.nonCoherentAtomSize);
16189 
16190                 json.WriteString("memoryHeapCount");
16191                 json.WriteNumber(memoryProperties.memoryHeapCount);
16192                 json.WriteString("memoryTypeCount");
16193                 json.WriteNumber(memoryProperties.memoryTypeCount);
16194             }
16195             json.EndObject();
16196         }
16197         {
16198             json.WriteString("Total");
16199             VmaPrintDetailedStatistics(json, stats.total);
16200         }
16201         {
16202             json.WriteString("MemoryInfo");
16203             json.BeginObject();
16204             {
16205                 for (uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
16206                 {
16207                     json.BeginString("Heap ");
16208                     json.ContinueString(heapIndex);
16209                     json.EndString();
16210                     json.BeginObject();
16211                     {
16212                         const VkMemoryHeap& heapInfo = allocator->m_MemProps.memoryHeaps[heapIndex];
16213                         json.WriteString("Flags");
16214                         json.BeginArray(true);
16215                         {
16216                             if (heapInfo.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT)
16217                                 json.WriteString("DEVICE_LOCAL");
16218                         #if VMA_VULKAN_VERSION >= 1001000
16219                             if (heapInfo.flags & VK_MEMORY_HEAP_MULTI_INSTANCE_BIT)
16220                                 json.WriteString("MULTI_INSTANCE");
16221                         #endif
16222 
16223                             VkMemoryHeapFlags flags = heapInfo.flags &
16224                                 ~(VK_MEMORY_HEAP_DEVICE_LOCAL_BIT
16225                         #if VMA_VULKAN_VERSION >= 1001000
16226                                     | VK_MEMORY_HEAP_MULTI_INSTANCE_BIT
16227                         #endif
16228                                     );
16229                             if (flags != 0)
16230                                 json.WriteNumber(flags);
16231                         }
16232                         json.EndArray();
16233 
16234                         json.WriteString("Size");
16235                         json.WriteNumber(heapInfo.size);
16236 
16237                         json.WriteString("Budget");
16238                         json.BeginObject();
16239                         {
16240                             json.WriteString("BudgetBytes");
16241                             json.WriteNumber(budgets[heapIndex].budget);
16242                             json.WriteString("UsageBytes");
16243                             json.WriteNumber(budgets[heapIndex].usage);
16244                         }
16245                         json.EndObject();
16246 
16247                         json.WriteString("Stats");
16248                         VmaPrintDetailedStatistics(json, stats.memoryHeap[heapIndex]);
16249 
16250                         json.WriteString("MemoryPools");
16251                         json.BeginObject();
16252                         {
16253                             for (uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
16254                             {
16255                                 if (allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
16256                                 {
16257                                     json.BeginString("Type ");
16258                                     json.ContinueString(typeIndex);
16259                                     json.EndString();
16260                                     json.BeginObject();
16261                                     {
16262                                         json.WriteString("Flags");
16263                                         json.BeginArray(true);
16264                                         {
16265                                             VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
16266                                             if (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
16267                                                 json.WriteString("DEVICE_LOCAL");
16268                                             if (flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
16269                                                 json.WriteString("HOST_VISIBLE");
16270                                             if (flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
16271                                                 json.WriteString("HOST_COHERENT");
16272                                             if (flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT)
16273                                                 json.WriteString("HOST_CACHED");
16274                                             if (flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT)
16275                                                 json.WriteString("LAZILY_ALLOCATED");
16276                                         #if VMA_VULKAN_VERSION >= 1001000
16277                                             if (flags & VK_MEMORY_PROPERTY_PROTECTED_BIT)
16278                                                 json.WriteString("PROTECTED");
16279                                         #endif
16280                                         #if VK_AMD_device_coherent_memory
16281                                             if (flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY)
16282                                                 json.WriteString("DEVICE_COHERENT_AMD");
16283                                             if (flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)
16284                                                 json.WriteString("DEVICE_UNCACHED_AMD");
16285                                         #endif
16286 
16287                                             flags &= ~(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
16288                                         #if VMA_VULKAN_VERSION >= 1001000
16289                                                 | VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT
16290                                         #endif
16291                                         #if VK_AMD_device_coherent_memory
16292                                                 | VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY
16293                                                 | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY
16294                                         #endif
16295                                                 | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
16296                                                 | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
16297                                                 | VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
16298                                             if (flags != 0)
16299                                                 json.WriteNumber(flags);
16300                                         }
16301                                         json.EndArray();
16302 
16303                                         json.WriteString("Stats");
16304                                         VmaPrintDetailedStatistics(json, stats.memoryType[typeIndex]);
16305                                     }
16306                                     json.EndObject();
16307                                 }
16308                             }
16309 
16310                         }
16311                         json.EndObject();
16312                     }
16313                     json.EndObject();
16314                 }
16315             }
16316             json.EndObject();
16317         }
16318 
16319         if (detailedMap == VK_TRUE)
16320             allocator->PrintDetailedMap(json);
16321 
16322         json.EndObject();
16323     }
16324 
16325     *ppStatsString = VmaCreateStringCopy(allocator->GetAllocationCallbacks(), sb.GetData(), sb.GetLength());
16326 }
16327 
vmaFreeStatsString(VmaAllocator allocator,char * pStatsString)16328 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
16329     VmaAllocator allocator,
16330     char* pStatsString)
16331 {
16332     if(pStatsString != VMA_NULL)
16333     {
16334         VMA_ASSERT(allocator);
16335         VmaFreeString(allocator->GetAllocationCallbacks(), pStatsString);
16336     }
16337 }
16338 
16339 #endif // VMA_STATS_STRING_ENABLED
16340 
16341 /*
16342 This function is not protected by any mutex because it just reads immutable data.
16343 */
vmaFindMemoryTypeIndex(VmaAllocator allocator,uint32_t memoryTypeBits,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)16344 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
16345     VmaAllocator allocator,
16346     uint32_t memoryTypeBits,
16347     const VmaAllocationCreateInfo* pAllocationCreateInfo,
16348     uint32_t* pMemoryTypeIndex)
16349 {
16350     VMA_ASSERT(allocator != VK_NULL_HANDLE);
16351     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
16352     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
16353 
16354     return allocator->FindMemoryTypeIndex(memoryTypeBits, pAllocationCreateInfo, UINT32_MAX, pMemoryTypeIndex);
16355 }
16356 
vmaFindMemoryTypeIndexForBufferInfo(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)16357 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
16358     VmaAllocator allocator,
16359     const VkBufferCreateInfo* pBufferCreateInfo,
16360     const VmaAllocationCreateInfo* pAllocationCreateInfo,
16361     uint32_t* pMemoryTypeIndex)
16362 {
16363     VMA_ASSERT(allocator != VK_NULL_HANDLE);
16364     VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
16365     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
16366     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
16367 
16368     const VkDevice hDev = allocator->m_hDevice;
16369     const VmaVulkanFunctions* funcs = &allocator->GetVulkanFunctions();
16370     VkResult res;
16371 
16372 #if VMA_VULKAN_VERSION >= 1003000
16373     if(funcs->vkGetDeviceBufferMemoryRequirements)
16374     {
16375         // Can query straight from VkBufferCreateInfo :)
16376         VkDeviceBufferMemoryRequirements devBufMemReq = {VK_STRUCTURE_TYPE_DEVICE_BUFFER_MEMORY_REQUIREMENTS};
16377         devBufMemReq.pCreateInfo = pBufferCreateInfo;
16378 
16379         VkMemoryRequirements2 memReq = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2};
16380         (*funcs->vkGetDeviceBufferMemoryRequirements)(hDev, &devBufMemReq, &memReq);
16381 
16382         res = allocator->FindMemoryTypeIndex(
16383             memReq.memoryRequirements.memoryTypeBits, pAllocationCreateInfo, pBufferCreateInfo->usage, pMemoryTypeIndex);
16384     }
16385     else
16386 #endif // #if VMA_VULKAN_VERSION >= 1003000
16387     {
16388         // Must create a dummy buffer to query :(
16389         VkBuffer hBuffer = VK_NULL_HANDLE;
16390         res = funcs->vkCreateBuffer(
16391             hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
16392         if(res == VK_SUCCESS)
16393         {
16394             VkMemoryRequirements memReq = {};
16395             funcs->vkGetBufferMemoryRequirements(hDev, hBuffer, &memReq);
16396 
16397             res = allocator->FindMemoryTypeIndex(
16398                 memReq.memoryTypeBits, pAllocationCreateInfo, pBufferCreateInfo->usage, pMemoryTypeIndex);
16399 
16400             funcs->vkDestroyBuffer(
16401                 hDev, hBuffer, allocator->GetAllocationCallbacks());
16402         }
16403     }
16404     return res;
16405 }
16406 
vmaFindMemoryTypeIndexForImageInfo(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)16407 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
16408     VmaAllocator allocator,
16409     const VkImageCreateInfo* pImageCreateInfo,
16410     const VmaAllocationCreateInfo* pAllocationCreateInfo,
16411     uint32_t* pMemoryTypeIndex)
16412 {
16413     VMA_ASSERT(allocator != VK_NULL_HANDLE);
16414     VMA_ASSERT(pImageCreateInfo != VMA_NULL);
16415     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
16416     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
16417 
16418     const VkDevice hDev = allocator->m_hDevice;
16419     const VmaVulkanFunctions* funcs = &allocator->GetVulkanFunctions();
16420     VkResult res;
16421 
16422 #if VMA_VULKAN_VERSION >= 1003000
16423     if(funcs->vkGetDeviceImageMemoryRequirements)
16424     {
16425         // Can query straight from VkImageCreateInfo :)
16426         VkDeviceImageMemoryRequirements devImgMemReq = {VK_STRUCTURE_TYPE_DEVICE_IMAGE_MEMORY_REQUIREMENTS};
16427         devImgMemReq.pCreateInfo = pImageCreateInfo;
16428         VMA_ASSERT(pImageCreateInfo->tiling != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT_COPY && (pImageCreateInfo->flags & VK_IMAGE_CREATE_DISJOINT_BIT_COPY) == 0 &&
16429             "Cannot use this VkImageCreateInfo with vmaFindMemoryTypeIndexForImageInfo as I don't know what to pass as VkDeviceImageMemoryRequirements::planeAspect.");
16430 
16431         VkMemoryRequirements2 memReq = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2};
16432         (*funcs->vkGetDeviceImageMemoryRequirements)(hDev, &devImgMemReq, &memReq);
16433 
16434         res = allocator->FindMemoryTypeIndex(
16435             memReq.memoryRequirements.memoryTypeBits, pAllocationCreateInfo, pImageCreateInfo->usage, pMemoryTypeIndex);
16436     }
16437     else
16438 #endif // #if VMA_VULKAN_VERSION >= 1003000
16439     {
16440         // Must create a dummy image to query :(
16441         VkImage hImage = VK_NULL_HANDLE;
16442         res = funcs->vkCreateImage(
16443             hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
16444         if(res == VK_SUCCESS)
16445         {
16446             VkMemoryRequirements memReq = {};
16447             funcs->vkGetImageMemoryRequirements(hDev, hImage, &memReq);
16448 
16449             res = allocator->FindMemoryTypeIndex(
16450                 memReq.memoryTypeBits, pAllocationCreateInfo, pImageCreateInfo->usage, pMemoryTypeIndex);
16451 
16452             funcs->vkDestroyImage(
16453                 hDev, hImage, allocator->GetAllocationCallbacks());
16454         }
16455     }
16456     return res;
16457 }
16458 
vmaCreatePool(VmaAllocator allocator,const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)16459 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
16460     VmaAllocator allocator,
16461     const VmaPoolCreateInfo* pCreateInfo,
16462     VmaPool* pPool)
16463 {
16464     VMA_ASSERT(allocator && pCreateInfo && pPool);
16465 
16466     VMA_DEBUG_LOG("vmaCreatePool");
16467 
16468     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16469 
16470     return allocator->CreatePool(pCreateInfo, pPool);
16471 }
16472 
vmaDestroyPool(VmaAllocator allocator,VmaPool pool)16473 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
16474     VmaAllocator allocator,
16475     VmaPool pool)
16476 {
16477     VMA_ASSERT(allocator);
16478 
16479     if(pool == VK_NULL_HANDLE)
16480     {
16481         return;
16482     }
16483 
16484     VMA_DEBUG_LOG("vmaDestroyPool");
16485 
16486     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16487 
16488     allocator->DestroyPool(pool);
16489 }
16490 
vmaGetPoolStatistics(VmaAllocator allocator,VmaPool pool,VmaStatistics * pPoolStats)16491 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStatistics(
16492     VmaAllocator allocator,
16493     VmaPool pool,
16494     VmaStatistics* pPoolStats)
16495 {
16496     VMA_ASSERT(allocator && pool && pPoolStats);
16497 
16498     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16499 
16500     allocator->GetPoolStatistics(pool, pPoolStats);
16501 }
16502 
vmaCalculatePoolStatistics(VmaAllocator allocator,VmaPool pool,VmaDetailedStatistics * pPoolStats)16503 VMA_CALL_PRE void VMA_CALL_POST vmaCalculatePoolStatistics(
16504     VmaAllocator allocator,
16505     VmaPool pool,
16506     VmaDetailedStatistics* pPoolStats)
16507 {
16508     VMA_ASSERT(allocator && pool && pPoolStats);
16509 
16510     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16511 
16512     allocator->CalculatePoolStatistics(pool, pPoolStats);
16513 }
16514 
vmaCheckPoolCorruption(VmaAllocator allocator,VmaPool pool)16515 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
16516 {
16517     VMA_ASSERT(allocator && pool);
16518 
16519     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16520 
16521     VMA_DEBUG_LOG("vmaCheckPoolCorruption");
16522 
16523     return allocator->CheckPoolCorruption(pool);
16524 }
16525 
vmaGetPoolName(VmaAllocator allocator,VmaPool pool,const char ** ppName)16526 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
16527     VmaAllocator allocator,
16528     VmaPool pool,
16529     const char** ppName)
16530 {
16531     VMA_ASSERT(allocator && pool && ppName);
16532 
16533     VMA_DEBUG_LOG("vmaGetPoolName");
16534 
16535     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16536 
16537     *ppName = pool->GetName();
16538 }
16539 
vmaSetPoolName(VmaAllocator allocator,VmaPool pool,const char * pName)16540 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
16541     VmaAllocator allocator,
16542     VmaPool pool,
16543     const char* pName)
16544 {
16545     VMA_ASSERT(allocator && pool);
16546 
16547     VMA_DEBUG_LOG("vmaSetPoolName");
16548 
16549     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16550 
16551     pool->SetName(pName);
16552 }
16553 
vmaAllocateMemory(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)16554 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
16555     VmaAllocator allocator,
16556     const VkMemoryRequirements* pVkMemoryRequirements,
16557     const VmaAllocationCreateInfo* pCreateInfo,
16558     VmaAllocation* pAllocation,
16559     VmaAllocationInfo* pAllocationInfo)
16560 {
16561     VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
16562 
16563     VMA_DEBUG_LOG("vmaAllocateMemory");
16564 
16565     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16566 
16567     VkResult result = allocator->AllocateMemory(
16568         *pVkMemoryRequirements,
16569         false, // requiresDedicatedAllocation
16570         false, // prefersDedicatedAllocation
16571         VK_NULL_HANDLE, // dedicatedBuffer
16572         VK_NULL_HANDLE, // dedicatedImage
16573         UINT32_MAX, // dedicatedBufferImageUsage
16574         *pCreateInfo,
16575         VMA_SUBALLOCATION_TYPE_UNKNOWN,
16576         1, // allocationCount
16577         pAllocation);
16578 
16579     if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
16580     {
16581         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16582     }
16583 
16584     return result;
16585 }
16586 
vmaAllocateMemoryPages(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,size_t allocationCount,VmaAllocation * pAllocations,VmaAllocationInfo * pAllocationInfo)16587 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
16588     VmaAllocator allocator,
16589     const VkMemoryRequirements* pVkMemoryRequirements,
16590     const VmaAllocationCreateInfo* pCreateInfo,
16591     size_t allocationCount,
16592     VmaAllocation* pAllocations,
16593     VmaAllocationInfo* pAllocationInfo)
16594 {
16595     if(allocationCount == 0)
16596     {
16597         return VK_SUCCESS;
16598     }
16599 
16600     VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
16601 
16602     VMA_DEBUG_LOG("vmaAllocateMemoryPages");
16603 
16604     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16605 
16606     VkResult result = allocator->AllocateMemory(
16607         *pVkMemoryRequirements,
16608         false, // requiresDedicatedAllocation
16609         false, // prefersDedicatedAllocation
16610         VK_NULL_HANDLE, // dedicatedBuffer
16611         VK_NULL_HANDLE, // dedicatedImage
16612         UINT32_MAX, // dedicatedBufferImageUsage
16613         *pCreateInfo,
16614         VMA_SUBALLOCATION_TYPE_UNKNOWN,
16615         allocationCount,
16616         pAllocations);
16617 
16618     if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
16619     {
16620         for(size_t i = 0; i < allocationCount; ++i)
16621         {
16622             allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
16623         }
16624     }
16625 
16626     return result;
16627 }
16628 
vmaAllocateMemoryForBuffer(VmaAllocator allocator,VkBuffer buffer,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)16629 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
16630     VmaAllocator allocator,
16631     VkBuffer buffer,
16632     const VmaAllocationCreateInfo* pCreateInfo,
16633     VmaAllocation* pAllocation,
16634     VmaAllocationInfo* pAllocationInfo)
16635 {
16636     VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
16637 
16638     VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
16639 
16640     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16641 
16642     VkMemoryRequirements vkMemReq = {};
16643     bool requiresDedicatedAllocation = false;
16644     bool prefersDedicatedAllocation = false;
16645     allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
16646         requiresDedicatedAllocation,
16647         prefersDedicatedAllocation);
16648 
16649     VkResult result = allocator->AllocateMemory(
16650         vkMemReq,
16651         requiresDedicatedAllocation,
16652         prefersDedicatedAllocation,
16653         buffer, // dedicatedBuffer
16654         VK_NULL_HANDLE, // dedicatedImage
16655         UINT32_MAX, // dedicatedBufferImageUsage
16656         *pCreateInfo,
16657         VMA_SUBALLOCATION_TYPE_BUFFER,
16658         1, // allocationCount
16659         pAllocation);
16660 
16661     if(pAllocationInfo && result == VK_SUCCESS)
16662     {
16663         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16664     }
16665 
16666     return result;
16667 }
16668 
vmaAllocateMemoryForImage(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)16669 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
16670     VmaAllocator allocator,
16671     VkImage image,
16672     const VmaAllocationCreateInfo* pCreateInfo,
16673     VmaAllocation* pAllocation,
16674     VmaAllocationInfo* pAllocationInfo)
16675 {
16676     VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
16677 
16678     VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
16679 
16680     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16681 
16682     VkMemoryRequirements vkMemReq = {};
16683     bool requiresDedicatedAllocation = false;
16684     bool prefersDedicatedAllocation  = false;
16685     allocator->GetImageMemoryRequirements(image, vkMemReq,
16686         requiresDedicatedAllocation, prefersDedicatedAllocation);
16687 
16688     VkResult result = allocator->AllocateMemory(
16689         vkMemReq,
16690         requiresDedicatedAllocation,
16691         prefersDedicatedAllocation,
16692         VK_NULL_HANDLE, // dedicatedBuffer
16693         image, // dedicatedImage
16694         UINT32_MAX, // dedicatedBufferImageUsage
16695         *pCreateInfo,
16696         VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
16697         1, // allocationCount
16698         pAllocation);
16699 
16700     if(pAllocationInfo && result == VK_SUCCESS)
16701     {
16702         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16703     }
16704 
16705     return result;
16706 }
16707 
vmaFreeMemory(VmaAllocator allocator,VmaAllocation allocation)16708 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
16709     VmaAllocator allocator,
16710     VmaAllocation allocation)
16711 {
16712     VMA_ASSERT(allocator);
16713 
16714     if(allocation == VK_NULL_HANDLE)
16715     {
16716         return;
16717     }
16718 
16719     VMA_DEBUG_LOG("vmaFreeMemory");
16720 
16721     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16722 
16723     allocator->FreeMemory(
16724         1, // allocationCount
16725         &allocation);
16726 }
16727 
vmaFreeMemoryPages(VmaAllocator allocator,size_t allocationCount,const VmaAllocation * pAllocations)16728 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
16729     VmaAllocator allocator,
16730     size_t allocationCount,
16731     const VmaAllocation* pAllocations)
16732 {
16733     if(allocationCount == 0)
16734     {
16735         return;
16736     }
16737 
16738     VMA_ASSERT(allocator);
16739 
16740     VMA_DEBUG_LOG("vmaFreeMemoryPages");
16741 
16742     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16743 
16744     allocator->FreeMemory(allocationCount, pAllocations);
16745 }
16746 
vmaGetAllocationInfo(VmaAllocator allocator,VmaAllocation allocation,VmaAllocationInfo * pAllocationInfo)16747 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
16748     VmaAllocator allocator,
16749     VmaAllocation allocation,
16750     VmaAllocationInfo* pAllocationInfo)
16751 {
16752     VMA_ASSERT(allocator && allocation && pAllocationInfo);
16753 
16754     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16755 
16756     allocator->GetAllocationInfo(allocation, pAllocationInfo);
16757 }
16758 
vmaSetAllocationUserData(VmaAllocator allocator,VmaAllocation allocation,void * pUserData)16759 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
16760     VmaAllocator allocator,
16761     VmaAllocation allocation,
16762     void* pUserData)
16763 {
16764     VMA_ASSERT(allocator && allocation);
16765 
16766     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16767 
16768     allocation->SetUserData(allocator, pUserData);
16769 }
16770 
vmaSetAllocationName(VmaAllocator VMA_NOT_NULL allocator,VmaAllocation VMA_NOT_NULL allocation,const char * VMA_NULLABLE pName)16771 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationName(
16772     VmaAllocator VMA_NOT_NULL allocator,
16773     VmaAllocation VMA_NOT_NULL allocation,
16774     const char* VMA_NULLABLE pName)
16775 {
16776     allocation->SetName(allocator, pName);
16777 }
16778 
vmaGetAllocationMemoryProperties(VmaAllocator VMA_NOT_NULL allocator,VmaAllocation VMA_NOT_NULL allocation,VkMemoryPropertyFlags * VMA_NOT_NULL pFlags)16779 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationMemoryProperties(
16780     VmaAllocator VMA_NOT_NULL allocator,
16781     VmaAllocation VMA_NOT_NULL allocation,
16782     VkMemoryPropertyFlags* VMA_NOT_NULL pFlags)
16783 {
16784     VMA_ASSERT(allocator && allocation && pFlags);
16785     const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16786     *pFlags = allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
16787 }
16788 
vmaMapMemory(VmaAllocator allocator,VmaAllocation allocation,void ** ppData)16789 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
16790     VmaAllocator allocator,
16791     VmaAllocation allocation,
16792     void** ppData)
16793 {
16794     VMA_ASSERT(allocator && allocation && ppData);
16795 
16796     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16797 
16798     return allocator->Map(allocation, ppData);
16799 }
16800 
vmaUnmapMemory(VmaAllocator allocator,VmaAllocation allocation)16801 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
16802     VmaAllocator allocator,
16803     VmaAllocation allocation)
16804 {
16805     VMA_ASSERT(allocator && allocation);
16806 
16807     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16808 
16809     allocator->Unmap(allocation);
16810 }
16811 
vmaFlushAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)16812 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(
16813     VmaAllocator allocator,
16814     VmaAllocation allocation,
16815     VkDeviceSize offset,
16816     VkDeviceSize size)
16817 {
16818     VMA_ASSERT(allocator && allocation);
16819 
16820     VMA_DEBUG_LOG("vmaFlushAllocation");
16821 
16822     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16823 
16824     const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
16825 
16826     return res;
16827 }
16828 
vmaInvalidateAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)16829 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(
16830     VmaAllocator allocator,
16831     VmaAllocation allocation,
16832     VkDeviceSize offset,
16833     VkDeviceSize size)
16834 {
16835     VMA_ASSERT(allocator && allocation);
16836 
16837     VMA_DEBUG_LOG("vmaInvalidateAllocation");
16838 
16839     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16840 
16841     const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
16842 
16843     return res;
16844 }
16845 
vmaFlushAllocations(VmaAllocator allocator,uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes)16846 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
16847     VmaAllocator allocator,
16848     uint32_t allocationCount,
16849     const VmaAllocation* allocations,
16850     const VkDeviceSize* offsets,
16851     const VkDeviceSize* sizes)
16852 {
16853     VMA_ASSERT(allocator);
16854 
16855     if(allocationCount == 0)
16856     {
16857         return VK_SUCCESS;
16858     }
16859 
16860     VMA_ASSERT(allocations);
16861 
16862     VMA_DEBUG_LOG("vmaFlushAllocations");
16863 
16864     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16865 
16866     const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH);
16867 
16868     return res;
16869 }
16870 
vmaInvalidateAllocations(VmaAllocator allocator,uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes)16871 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
16872     VmaAllocator allocator,
16873     uint32_t allocationCount,
16874     const VmaAllocation* allocations,
16875     const VkDeviceSize* offsets,
16876     const VkDeviceSize* sizes)
16877 {
16878     VMA_ASSERT(allocator);
16879 
16880     if(allocationCount == 0)
16881     {
16882         return VK_SUCCESS;
16883     }
16884 
16885     VMA_ASSERT(allocations);
16886 
16887     VMA_DEBUG_LOG("vmaInvalidateAllocations");
16888 
16889     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16890 
16891     const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE);
16892 
16893     return res;
16894 }
16895 
vmaCheckCorruption(VmaAllocator allocator,uint32_t memoryTypeBits)16896 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(
16897     VmaAllocator allocator,
16898     uint32_t memoryTypeBits)
16899 {
16900     VMA_ASSERT(allocator);
16901 
16902     VMA_DEBUG_LOG("vmaCheckCorruption");
16903 
16904     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16905 
16906     return allocator->CheckCorruption(memoryTypeBits);
16907 }
16908 
vmaBeginDefragmentation(VmaAllocator allocator,const VmaDefragmentationInfo * pInfo,VmaDefragmentationContext * pContext)16909 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentation(
16910     VmaAllocator allocator,
16911     const VmaDefragmentationInfo* pInfo,
16912     VmaDefragmentationContext* pContext)
16913 {
16914     VMA_ASSERT(allocator && pInfo && pContext);
16915 
16916     VMA_DEBUG_LOG("vmaBeginDefragmentation");
16917 
16918     if (pInfo->pool != VMA_NULL)
16919     {
16920         // Check if run on supported algorithms
16921         if (pInfo->pool->m_BlockVector.GetAlgorithm() & VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
16922             return VK_ERROR_FEATURE_NOT_PRESENT;
16923     }
16924 
16925     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16926 
16927     *pContext = vma_new(allocator, VmaDefragmentationContext_T)(allocator, *pInfo);
16928     return VK_SUCCESS;
16929 }
16930 
vmaEndDefragmentation(VmaAllocator allocator,VmaDefragmentationContext context,VmaDefragmentationStats * pStats)16931 VMA_CALL_PRE void VMA_CALL_POST vmaEndDefragmentation(
16932     VmaAllocator allocator,
16933     VmaDefragmentationContext context,
16934     VmaDefragmentationStats* pStats)
16935 {
16936     VMA_ASSERT(allocator && context);
16937 
16938     VMA_DEBUG_LOG("vmaEndDefragmentation");
16939 
16940     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16941 
16942     if (pStats)
16943         context->GetStats(*pStats);
16944     vma_delete(allocator, context);
16945 }
16946 
vmaBeginDefragmentationPass(VmaAllocator VMA_NOT_NULL allocator,VmaDefragmentationContext VMA_NOT_NULL context,VmaDefragmentationPassMoveInfo * VMA_NOT_NULL pPassInfo)16947 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
16948     VmaAllocator VMA_NOT_NULL allocator,
16949     VmaDefragmentationContext VMA_NOT_NULL context,
16950     VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo)
16951 {
16952     VMA_ASSERT(context && pPassInfo);
16953 
16954     VMA_DEBUG_LOG("vmaBeginDefragmentationPass");
16955 
16956     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16957 
16958     return context->DefragmentPassBegin(*pPassInfo);
16959 }
16960 
vmaEndDefragmentationPass(VmaAllocator VMA_NOT_NULL allocator,VmaDefragmentationContext VMA_NOT_NULL context,VmaDefragmentationPassMoveInfo * VMA_NOT_NULL pPassInfo)16961 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
16962     VmaAllocator VMA_NOT_NULL allocator,
16963     VmaDefragmentationContext VMA_NOT_NULL context,
16964     VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo)
16965 {
16966     VMA_ASSERT(context && pPassInfo);
16967 
16968     VMA_DEBUG_LOG("vmaEndDefragmentationPass");
16969 
16970     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16971 
16972     return context->DefragmentPassEnd(*pPassInfo);
16973 }
16974 
vmaBindBufferMemory(VmaAllocator allocator,VmaAllocation allocation,VkBuffer buffer)16975 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
16976     VmaAllocator allocator,
16977     VmaAllocation allocation,
16978     VkBuffer buffer)
16979 {
16980     VMA_ASSERT(allocator && allocation && buffer);
16981 
16982     VMA_DEBUG_LOG("vmaBindBufferMemory");
16983 
16984     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16985 
16986     return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL);
16987 }
16988 
vmaBindBufferMemory2(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize allocationLocalOffset,VkBuffer buffer,const void * pNext)16989 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
16990     VmaAllocator allocator,
16991     VmaAllocation allocation,
16992     VkDeviceSize allocationLocalOffset,
16993     VkBuffer buffer,
16994     const void* pNext)
16995 {
16996     VMA_ASSERT(allocator && allocation && buffer);
16997 
16998     VMA_DEBUG_LOG("vmaBindBufferMemory2");
16999 
17000     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17001 
17002     return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext);
17003 }
17004 
vmaBindImageMemory(VmaAllocator allocator,VmaAllocation allocation,VkImage image)17005 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
17006     VmaAllocator allocator,
17007     VmaAllocation allocation,
17008     VkImage image)
17009 {
17010     VMA_ASSERT(allocator && allocation && image);
17011 
17012     VMA_DEBUG_LOG("vmaBindImageMemory");
17013 
17014     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17015 
17016     return allocator->BindImageMemory(allocation, 0, image, VMA_NULL);
17017 }
17018 
vmaBindImageMemory2(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize allocationLocalOffset,VkImage image,const void * pNext)17019 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
17020     VmaAllocator allocator,
17021     VmaAllocation allocation,
17022     VkDeviceSize allocationLocalOffset,
17023     VkImage image,
17024     const void* pNext)
17025 {
17026     VMA_ASSERT(allocator && allocation && image);
17027 
17028     VMA_DEBUG_LOG("vmaBindImageMemory2");
17029 
17030     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17031 
17032         return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext);
17033 }
17034 
vmaCreateBuffer(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkBuffer * pBuffer,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)17035 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
17036     VmaAllocator allocator,
17037     const VkBufferCreateInfo* pBufferCreateInfo,
17038     const VmaAllocationCreateInfo* pAllocationCreateInfo,
17039     VkBuffer* pBuffer,
17040     VmaAllocation* pAllocation,
17041     VmaAllocationInfo* pAllocationInfo)
17042 {
17043     VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
17044 
17045     if(pBufferCreateInfo->size == 0)
17046     {
17047         return VK_ERROR_INITIALIZATION_FAILED;
17048     }
17049     if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
17050         !allocator->m_UseKhrBufferDeviceAddress)
17051     {
17052         VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used.");
17053         return VK_ERROR_INITIALIZATION_FAILED;
17054     }
17055 
17056     VMA_DEBUG_LOG("vmaCreateBuffer");
17057 
17058     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17059 
17060     *pBuffer = VK_NULL_HANDLE;
17061     *pAllocation = VK_NULL_HANDLE;
17062 
17063     // 1. Create VkBuffer.
17064     VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
17065         allocator->m_hDevice,
17066         pBufferCreateInfo,
17067         allocator->GetAllocationCallbacks(),
17068         pBuffer);
17069     if(res >= 0)
17070     {
17071         // 2. vkGetBufferMemoryRequirements.
17072         VkMemoryRequirements vkMemReq = {};
17073         bool requiresDedicatedAllocation = false;
17074         bool prefersDedicatedAllocation  = false;
17075         allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
17076             requiresDedicatedAllocation, prefersDedicatedAllocation);
17077 
17078         // 3. Allocate memory using allocator.
17079         res = allocator->AllocateMemory(
17080             vkMemReq,
17081             requiresDedicatedAllocation,
17082             prefersDedicatedAllocation,
17083             *pBuffer, // dedicatedBuffer
17084             VK_NULL_HANDLE, // dedicatedImage
17085             pBufferCreateInfo->usage, // dedicatedBufferImageUsage
17086             *pAllocationCreateInfo,
17087             VMA_SUBALLOCATION_TYPE_BUFFER,
17088             1, // allocationCount
17089             pAllocation);
17090 
17091         if(res >= 0)
17092         {
17093             // 3. Bind buffer with memory.
17094             if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
17095             {
17096                 res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
17097             }
17098             if(res >= 0)
17099             {
17100                 // All steps succeeded.
17101                 #if VMA_STATS_STRING_ENABLED
17102                     (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
17103                 #endif
17104                 if(pAllocationInfo != VMA_NULL)
17105                 {
17106                     allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
17107                 }
17108 
17109                 return VK_SUCCESS;
17110             }
17111             allocator->FreeMemory(
17112                 1, // allocationCount
17113                 pAllocation);
17114             *pAllocation = VK_NULL_HANDLE;
17115             (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
17116             *pBuffer = VK_NULL_HANDLE;
17117             return res;
17118         }
17119         (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
17120         *pBuffer = VK_NULL_HANDLE;
17121         return res;
17122     }
17123     return res;
17124 }
17125 
vmaCreateBufferWithAlignment(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkDeviceSize minAlignment,VkBuffer * pBuffer,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)17126 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment(
17127     VmaAllocator allocator,
17128     const VkBufferCreateInfo* pBufferCreateInfo,
17129     const VmaAllocationCreateInfo* pAllocationCreateInfo,
17130     VkDeviceSize minAlignment,
17131     VkBuffer* pBuffer,
17132     VmaAllocation* pAllocation,
17133     VmaAllocationInfo* pAllocationInfo)
17134 {
17135     VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && VmaIsPow2(minAlignment) && pBuffer && pAllocation);
17136 
17137     if(pBufferCreateInfo->size == 0)
17138     {
17139         return VK_ERROR_INITIALIZATION_FAILED;
17140     }
17141     if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
17142         !allocator->m_UseKhrBufferDeviceAddress)
17143     {
17144         VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used.");
17145         return VK_ERROR_INITIALIZATION_FAILED;
17146     }
17147 
17148     VMA_DEBUG_LOG("vmaCreateBufferWithAlignment");
17149 
17150     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17151 
17152     *pBuffer = VK_NULL_HANDLE;
17153     *pAllocation = VK_NULL_HANDLE;
17154 
17155     // 1. Create VkBuffer.
17156     VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
17157         allocator->m_hDevice,
17158         pBufferCreateInfo,
17159         allocator->GetAllocationCallbacks(),
17160         pBuffer);
17161     if(res >= 0)
17162     {
17163         // 2. vkGetBufferMemoryRequirements.
17164         VkMemoryRequirements vkMemReq = {};
17165         bool requiresDedicatedAllocation = false;
17166         bool prefersDedicatedAllocation  = false;
17167         allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
17168             requiresDedicatedAllocation, prefersDedicatedAllocation);
17169 
17170         // 2a. Include minAlignment
17171         vkMemReq.alignment = VMA_MAX(vkMemReq.alignment, minAlignment);
17172 
17173         // 3. Allocate memory using allocator.
17174         res = allocator->AllocateMemory(
17175             vkMemReq,
17176             requiresDedicatedAllocation,
17177             prefersDedicatedAllocation,
17178             *pBuffer, // dedicatedBuffer
17179             VK_NULL_HANDLE, // dedicatedImage
17180             pBufferCreateInfo->usage, // dedicatedBufferImageUsage
17181             *pAllocationCreateInfo,
17182             VMA_SUBALLOCATION_TYPE_BUFFER,
17183             1, // allocationCount
17184             pAllocation);
17185 
17186         if(res >= 0)
17187         {
17188             // 3. Bind buffer with memory.
17189             if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
17190             {
17191                 res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
17192             }
17193             if(res >= 0)
17194             {
17195                 // All steps succeeded.
17196                 #if VMA_STATS_STRING_ENABLED
17197                     (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
17198                 #endif
17199                 if(pAllocationInfo != VMA_NULL)
17200                 {
17201                     allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
17202                 }
17203 
17204                 return VK_SUCCESS;
17205             }
17206             allocator->FreeMemory(
17207                 1, // allocationCount
17208                 pAllocation);
17209             *pAllocation = VK_NULL_HANDLE;
17210             (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
17211             *pBuffer = VK_NULL_HANDLE;
17212             return res;
17213         }
17214         (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
17215         *pBuffer = VK_NULL_HANDLE;
17216         return res;
17217     }
17218     return res;
17219 }
17220 
vmaCreateAliasingBuffer(VmaAllocator VMA_NOT_NULL allocator,VmaAllocation VMA_NOT_NULL allocation,const VkBufferCreateInfo * VMA_NOT_NULL pBufferCreateInfo,VkBuffer VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pBuffer)17221 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer(
17222     VmaAllocator VMA_NOT_NULL allocator,
17223     VmaAllocation VMA_NOT_NULL allocation,
17224     const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
17225     VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer)
17226 {
17227     VMA_ASSERT(allocator && pBufferCreateInfo && pBuffer && allocation);
17228 
17229     VMA_DEBUG_LOG("vmaCreateAliasingBuffer");
17230 
17231     *pBuffer = VK_NULL_HANDLE;
17232 
17233     if (pBufferCreateInfo->size == 0)
17234     {
17235         return VK_ERROR_INITIALIZATION_FAILED;
17236     }
17237     if ((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
17238         !allocator->m_UseKhrBufferDeviceAddress)
17239     {
17240         VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used.");
17241         return VK_ERROR_INITIALIZATION_FAILED;
17242     }
17243 
17244     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17245 
17246     // 1. Create VkBuffer.
17247     VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
17248         allocator->m_hDevice,
17249         pBufferCreateInfo,
17250         allocator->GetAllocationCallbacks(),
17251         pBuffer);
17252     if (res >= 0)
17253     {
17254         // 2. Bind buffer with memory.
17255         res = allocator->BindBufferMemory(allocation, 0, *pBuffer, VMA_NULL);
17256         if (res >= 0)
17257         {
17258             return VK_SUCCESS;
17259         }
17260         (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
17261     }
17262     return res;
17263 }
17264 
vmaDestroyBuffer(VmaAllocator allocator,VkBuffer buffer,VmaAllocation allocation)17265 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
17266     VmaAllocator allocator,
17267     VkBuffer buffer,
17268     VmaAllocation allocation)
17269 {
17270     VMA_ASSERT(allocator);
17271 
17272     if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
17273     {
17274         return;
17275     }
17276 
17277     VMA_DEBUG_LOG("vmaDestroyBuffer");
17278 
17279     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17280 
17281     if(buffer != VK_NULL_HANDLE)
17282     {
17283         (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
17284     }
17285 
17286     if(allocation != VK_NULL_HANDLE)
17287     {
17288         allocator->FreeMemory(
17289             1, // allocationCount
17290             &allocation);
17291     }
17292 }
17293 
vmaCreateImage(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkImage * pImage,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)17294 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
17295     VmaAllocator allocator,
17296     const VkImageCreateInfo* pImageCreateInfo,
17297     const VmaAllocationCreateInfo* pAllocationCreateInfo,
17298     VkImage* pImage,
17299     VmaAllocation* pAllocation,
17300     VmaAllocationInfo* pAllocationInfo)
17301 {
17302     VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
17303 
17304     if(pImageCreateInfo->extent.width == 0 ||
17305         pImageCreateInfo->extent.height == 0 ||
17306         pImageCreateInfo->extent.depth == 0 ||
17307         pImageCreateInfo->mipLevels == 0 ||
17308         pImageCreateInfo->arrayLayers == 0)
17309     {
17310         return VK_ERROR_INITIALIZATION_FAILED;
17311     }
17312 
17313     VMA_DEBUG_LOG("vmaCreateImage");
17314 
17315     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17316 
17317     *pImage = VK_NULL_HANDLE;
17318     *pAllocation = VK_NULL_HANDLE;
17319 
17320     // 1. Create VkImage.
17321     VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
17322         allocator->m_hDevice,
17323         pImageCreateInfo,
17324         allocator->GetAllocationCallbacks(),
17325         pImage);
17326     if(res >= 0)
17327     {
17328         VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
17329             VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
17330             VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
17331 
17332         // 2. Allocate memory using allocator.
17333         VkMemoryRequirements vkMemReq = {};
17334         bool requiresDedicatedAllocation = false;
17335         bool prefersDedicatedAllocation  = false;
17336         allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
17337             requiresDedicatedAllocation, prefersDedicatedAllocation);
17338 
17339         res = allocator->AllocateMemory(
17340             vkMemReq,
17341             requiresDedicatedAllocation,
17342             prefersDedicatedAllocation,
17343             VK_NULL_HANDLE, // dedicatedBuffer
17344             *pImage, // dedicatedImage
17345             pImageCreateInfo->usage, // dedicatedBufferImageUsage
17346             *pAllocationCreateInfo,
17347             suballocType,
17348             1, // allocationCount
17349             pAllocation);
17350 
17351         if(res >= 0)
17352         {
17353             // 3. Bind image with memory.
17354             if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
17355             {
17356                 res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);
17357             }
17358             if(res >= 0)
17359             {
17360                 // All steps succeeded.
17361                 #if VMA_STATS_STRING_ENABLED
17362                     (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
17363                 #endif
17364                 if(pAllocationInfo != VMA_NULL)
17365                 {
17366                     allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
17367                 }
17368 
17369                 return VK_SUCCESS;
17370             }
17371             allocator->FreeMemory(
17372                 1, // allocationCount
17373                 pAllocation);
17374             *pAllocation = VK_NULL_HANDLE;
17375             (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
17376             *pImage = VK_NULL_HANDLE;
17377             return res;
17378         }
17379         (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
17380         *pImage = VK_NULL_HANDLE;
17381         return res;
17382     }
17383     return res;
17384 }
17385 
vmaCreateAliasingImage(VmaAllocator VMA_NOT_NULL allocator,VmaAllocation VMA_NOT_NULL allocation,const VkImageCreateInfo * VMA_NOT_NULL pImageCreateInfo,VkImage VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pImage)17386 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage(
17387     VmaAllocator VMA_NOT_NULL allocator,
17388     VmaAllocation VMA_NOT_NULL allocation,
17389     const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
17390     VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage)
17391 {
17392     VMA_ASSERT(allocator && pImageCreateInfo && pImage && allocation);
17393 
17394     *pImage = VK_NULL_HANDLE;
17395 
17396     VMA_DEBUG_LOG("vmaCreateImage");
17397 
17398     if (pImageCreateInfo->extent.width == 0 ||
17399         pImageCreateInfo->extent.height == 0 ||
17400         pImageCreateInfo->extent.depth == 0 ||
17401         pImageCreateInfo->mipLevels == 0 ||
17402         pImageCreateInfo->arrayLayers == 0)
17403     {
17404         return VK_ERROR_INITIALIZATION_FAILED;
17405     }
17406 
17407     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17408 
17409     // 1. Create VkImage.
17410     VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
17411         allocator->m_hDevice,
17412         pImageCreateInfo,
17413         allocator->GetAllocationCallbacks(),
17414         pImage);
17415     if (res >= 0)
17416     {
17417         // 2. Bind image with memory.
17418         res = allocator->BindImageMemory(allocation, 0, *pImage, VMA_NULL);
17419         if (res >= 0)
17420         {
17421             return VK_SUCCESS;
17422         }
17423         (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
17424     }
17425     return res;
17426 }
17427 
vmaDestroyImage(VmaAllocator VMA_NOT_NULL allocator,VkImage VMA_NULLABLE_NON_DISPATCHABLE image,VmaAllocation VMA_NULLABLE allocation)17428 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
17429     VmaAllocator VMA_NOT_NULL allocator,
17430     VkImage VMA_NULLABLE_NON_DISPATCHABLE image,
17431     VmaAllocation VMA_NULLABLE allocation)
17432 {
17433     VMA_ASSERT(allocator);
17434 
17435     if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
17436     {
17437         return;
17438     }
17439 
17440     VMA_DEBUG_LOG("vmaDestroyImage");
17441 
17442     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17443 
17444     if(image != VK_NULL_HANDLE)
17445     {
17446         (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
17447     }
17448     if(allocation != VK_NULL_HANDLE)
17449     {
17450         allocator->FreeMemory(
17451             1, // allocationCount
17452             &allocation);
17453     }
17454 }
17455 
vmaCreateVirtualBlock(const VmaVirtualBlockCreateInfo * VMA_NOT_NULL pCreateInfo,VmaVirtualBlock VMA_NULLABLE * VMA_NOT_NULL pVirtualBlock)17456 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock(
17457     const VmaVirtualBlockCreateInfo* VMA_NOT_NULL pCreateInfo,
17458     VmaVirtualBlock VMA_NULLABLE * VMA_NOT_NULL pVirtualBlock)
17459 {
17460     VMA_ASSERT(pCreateInfo && pVirtualBlock);
17461     VMA_ASSERT(pCreateInfo->size > 0);
17462     VMA_DEBUG_LOG("vmaCreateVirtualBlock");
17463     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17464     *pVirtualBlock = vma_new(pCreateInfo->pAllocationCallbacks, VmaVirtualBlock_T)(*pCreateInfo);
17465     VkResult res = (*pVirtualBlock)->Init();
17466     if(res < 0)
17467     {
17468         vma_delete(pCreateInfo->pAllocationCallbacks, *pVirtualBlock);
17469         *pVirtualBlock = VK_NULL_HANDLE;
17470     }
17471     return res;
17472 }
17473 
vmaDestroyVirtualBlock(VmaVirtualBlock VMA_NULLABLE virtualBlock)17474 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyVirtualBlock(VmaVirtualBlock VMA_NULLABLE virtualBlock)
17475 {
17476     if(virtualBlock != VK_NULL_HANDLE)
17477     {
17478         VMA_DEBUG_LOG("vmaDestroyVirtualBlock");
17479         VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17480         VkAllocationCallbacks allocationCallbacks = virtualBlock->m_AllocationCallbacks; // Have to copy the callbacks when destroying.
17481         vma_delete(&allocationCallbacks, virtualBlock);
17482     }
17483 }
17484 
vmaIsVirtualBlockEmpty(VmaVirtualBlock VMA_NOT_NULL virtualBlock)17485 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaIsVirtualBlockEmpty(VmaVirtualBlock VMA_NOT_NULL virtualBlock)
17486 {
17487     VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
17488     VMA_DEBUG_LOG("vmaIsVirtualBlockEmpty");
17489     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17490     return virtualBlock->IsEmpty() ? VK_TRUE : VK_FALSE;
17491 }
17492 
vmaGetVirtualAllocationInfo(VmaVirtualBlock VMA_NOT_NULL virtualBlock,VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation,VmaVirtualAllocationInfo * VMA_NOT_NULL pVirtualAllocInfo)17493 VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
17494     VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo)
17495 {
17496     VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pVirtualAllocInfo != VMA_NULL);
17497     VMA_DEBUG_LOG("vmaGetVirtualAllocationInfo");
17498     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17499     virtualBlock->GetAllocationInfo(allocation, *pVirtualAllocInfo);
17500 }
17501 
vmaVirtualAllocate(VmaVirtualBlock VMA_NOT_NULL virtualBlock,const VmaVirtualAllocationCreateInfo * VMA_NOT_NULL pCreateInfo,VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pAllocation,VkDeviceSize * VMA_NULLABLE pOffset)17502 VMA_CALL_PRE VkResult VMA_CALL_POST vmaVirtualAllocate(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
17503     const VmaVirtualAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pAllocation,
17504     VkDeviceSize* VMA_NULLABLE pOffset)
17505 {
17506     VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pCreateInfo != VMA_NULL && pAllocation != VMA_NULL);
17507     VMA_DEBUG_LOG("vmaVirtualAllocate");
17508     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17509     return virtualBlock->Allocate(*pCreateInfo, *pAllocation, pOffset);
17510 }
17511 
vmaVirtualFree(VmaVirtualBlock VMA_NOT_NULL virtualBlock,VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE allocation)17512 VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree(VmaVirtualBlock VMA_NOT_NULL virtualBlock, VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE allocation)
17513 {
17514     if(allocation != VK_NULL_HANDLE)
17515     {
17516         VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
17517         VMA_DEBUG_LOG("vmaVirtualFree");
17518         VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17519         virtualBlock->Free(allocation);
17520     }
17521 }
17522 
vmaClearVirtualBlock(VmaVirtualBlock VMA_NOT_NULL virtualBlock)17523 VMA_CALL_PRE void VMA_CALL_POST vmaClearVirtualBlock(VmaVirtualBlock VMA_NOT_NULL virtualBlock)
17524 {
17525     VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
17526     VMA_DEBUG_LOG("vmaClearVirtualBlock");
17527     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17528     virtualBlock->Clear();
17529 }
17530 
vmaSetVirtualAllocationUserData(VmaVirtualBlock VMA_NOT_NULL virtualBlock,VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation,void * VMA_NULLABLE pUserData)17531 VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
17532     VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, void* VMA_NULLABLE pUserData)
17533 {
17534     VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
17535     VMA_DEBUG_LOG("vmaSetVirtualAllocationUserData");
17536     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17537     virtualBlock->SetAllocationUserData(allocation, pUserData);
17538 }
17539 
vmaGetVirtualBlockStatistics(VmaVirtualBlock VMA_NOT_NULL virtualBlock,VmaStatistics * VMA_NOT_NULL pStats)17540 VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualBlockStatistics(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
17541     VmaStatistics* VMA_NOT_NULL pStats)
17542 {
17543     VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pStats != VMA_NULL);
17544     VMA_DEBUG_LOG("vmaGetVirtualBlockStatistics");
17545     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17546     virtualBlock->GetStatistics(*pStats);
17547 }
17548 
vmaCalculateVirtualBlockStatistics(VmaVirtualBlock VMA_NOT_NULL virtualBlock,VmaDetailedStatistics * VMA_NOT_NULL pStats)17549 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateVirtualBlockStatistics(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
17550     VmaDetailedStatistics* VMA_NOT_NULL pStats)
17551 {
17552     VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pStats != VMA_NULL);
17553     VMA_DEBUG_LOG("vmaCalculateVirtualBlockStatistics");
17554     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17555     virtualBlock->CalculateDetailedStatistics(*pStats);
17556 }
17557 
17558 #if VMA_STATS_STRING_ENABLED
17559 
vmaBuildVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,char * VMA_NULLABLE * VMA_NOT_NULL ppStatsString,VkBool32 detailedMap)17560 VMA_CALL_PRE void VMA_CALL_POST vmaBuildVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
17561     char* VMA_NULLABLE * VMA_NOT_NULL ppStatsString, VkBool32 detailedMap)
17562 {
17563     VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && ppStatsString != VMA_NULL);
17564     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17565     const VkAllocationCallbacks* allocationCallbacks = virtualBlock->GetAllocationCallbacks();
17566     VmaStringBuilder sb(allocationCallbacks);
17567     virtualBlock->BuildStatsString(detailedMap != VK_FALSE, sb);
17568     *ppStatsString = VmaCreateStringCopy(allocationCallbacks, sb.GetData(), sb.GetLength());
17569 }
17570 
vmaFreeVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,char * VMA_NULLABLE pStatsString)17571 VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
17572     char* VMA_NULLABLE pStatsString)
17573 {
17574     if(pStatsString != VMA_NULL)
17575     {
17576         VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
17577         VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17578         VmaFreeString(virtualBlock->GetAllocationCallbacks(), pStatsString);
17579     }
17580 }
17581 #endif // VMA_STATS_STRING_ENABLED
17582 #endif // _VMA_PUBLIC_INTERFACE
17583 #endif // VMA_IMPLEMENTATION
17584 
17585 /**
17586 \page quick_start Quick start
17587 
17588 \section quick_start_project_setup Project setup
17589 
17590 Vulkan Memory Allocator comes in form of a "stb-style" single header file.
17591 You don't need to build it as a separate library project.
17592 You can add this file directly to your project and submit it to code repository next to your other source files.
17593 
17594 "Single header" doesn't mean that everything is contained in C/C++ declarations,
17595 like it tends to be in case of inline functions or C++ templates.
17596 It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro.
17597 If you don't do it properly, you will get linker errors.
17598 
17599 To do it properly:
17600 
17601 -# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library.
17602    This includes declarations of all members of the library.
17603 -# In exactly one CPP file define following macro before this include.
17604    It enables also internal definitions.
17605 
17606 \code
17607 #define VMA_IMPLEMENTATION
17608 #include "vk_mem_alloc.h"
17609 \endcode
17610 
17611 It may be a good idea to create dedicated CPP file just for this purpose.
17612 
17613 This library includes header `<vulkan/vulkan.h>`, which in turn
17614 includes `<windows.h>` on Windows. If you need some specific macros defined
17615 before including these headers (like `WIN32_LEAN_AND_MEAN` or
17616 `WINVER` for Windows, `VK_USE_PLATFORM_WIN32_KHR` for Vulkan), you must define
17617 them before every `#include` of this library.
17618 
17619 This library is written in C++, but has C-compatible interface.
17620 Thus you can include and use vk_mem_alloc.h in C or C++ code, but full
17621 implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C.
17622 Some features of C++14 used. STL containers, RTTI, or C++ exceptions are not used.
17623 
17624 
17625 \section quick_start_initialization Initialization
17626 
17627 At program startup:
17628 
17629 -# Initialize Vulkan to have `VkPhysicalDevice`, `VkDevice` and `VkInstance` object.
17630 -# Fill VmaAllocatorCreateInfo structure and create #VmaAllocator object by
17631    calling vmaCreateAllocator().
17632 
17633 Only members `physicalDevice`, `device`, `instance` are required.
17634 However, you should inform the library which Vulkan version do you use by setting
17635 VmaAllocatorCreateInfo::vulkanApiVersion and which extensions did you enable
17636 by setting VmaAllocatorCreateInfo::flags (like #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT for VK_KHR_buffer_device_address).
17637 Otherwise, VMA would use only features of Vulkan 1.0 core with no extensions.
17638 
17639 You may need to configure importing Vulkan functions. There are 3 ways to do this:
17640 
17641 -# **If you link with Vulkan static library** (e.g. "vulkan-1.lib" on Windows):
17642    - You don't need to do anything.
17643    - VMA will use these, as macro `VMA_STATIC_VULKAN_FUNCTIONS` is defined to 1 by default.
17644 -# **If you want VMA to fetch pointers to Vulkan functions dynamically** using `vkGetInstanceProcAddr`,
17645    `vkGetDeviceProcAddr` (this is the option presented in the example below):
17646    - Define `VMA_STATIC_VULKAN_FUNCTIONS` to 0, `VMA_DYNAMIC_VULKAN_FUNCTIONS` to 1.
17647    - Provide pointers to these two functions via VmaVulkanFunctions::vkGetInstanceProcAddr,
17648      VmaVulkanFunctions::vkGetDeviceProcAddr.
17649    - The library will fetch pointers to all other functions it needs internally.
17650 -# **If you fetch pointers to all Vulkan functions in a custom way**, e.g. using some loader like
17651    [Volk](https://github.com/zeux/volk):
17652    - Define `VMA_STATIC_VULKAN_FUNCTIONS` and `VMA_DYNAMIC_VULKAN_FUNCTIONS` to 0.
17653    - Pass these pointers via structure #VmaVulkanFunctions.
17654 
17655 \code
17656 VmaVulkanFunctions vulkanFunctions = {};
17657 vulkanFunctions.vkGetInstanceProcAddr = &vkGetInstanceProcAddr;
17658 vulkanFunctions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr;
17659 
17660 VmaAllocatorCreateInfo allocatorCreateInfo = {};
17661 allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_2;
17662 allocatorCreateInfo.physicalDevice = physicalDevice;
17663 allocatorCreateInfo.device = device;
17664 allocatorCreateInfo.instance = instance;
17665 allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions;
17666 
17667 VmaAllocator allocator;
17668 vmaCreateAllocator(&allocatorCreateInfo, &allocator);
17669 \endcode
17670 
17671 
17672 \section quick_start_resource_allocation Resource allocation
17673 
17674 When you want to create a buffer or image:
17675 
17676 -# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure.
17677 -# Fill VmaAllocationCreateInfo structure.
17678 -# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory
17679    already allocated and bound to it, plus #VmaAllocation objects that represents its underlying memory.
17680 
17681 \code
17682 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
17683 bufferInfo.size = 65536;
17684 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
17685 
17686 VmaAllocationCreateInfo allocInfo = {};
17687 allocInfo.usage = VMA_MEMORY_USAGE_AUTO;
17688 
17689 VkBuffer buffer;
17690 VmaAllocation allocation;
17691 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
17692 \endcode
17693 
17694 Don't forget to destroy your objects when no longer needed:
17695 
17696 \code
17697 vmaDestroyBuffer(allocator, buffer, allocation);
17698 vmaDestroyAllocator(allocator);
17699 \endcode
17700 
17701 
17702 \page choosing_memory_type Choosing memory type
17703 
17704 Physical devices in Vulkan support various combinations of memory heaps and
17705 types. Help with choosing correct and optimal memory type for your specific
17706 resource is one of the key features of this library. You can use it by filling
17707 appropriate members of VmaAllocationCreateInfo structure, as described below.
17708 You can also combine multiple methods.
17709 
17710 -# If you just want to find memory type index that meets your requirements, you
17711    can use function: vmaFindMemoryTypeIndexForBufferInfo(),
17712    vmaFindMemoryTypeIndexForImageInfo(), vmaFindMemoryTypeIndex().
17713 -# If you want to allocate a region of device memory without association with any
17714    specific image or buffer, you can use function vmaAllocateMemory(). Usage of
17715    this function is not recommended and usually not needed.
17716    vmaAllocateMemoryPages() function is also provided for creating multiple allocations at once,
17717    which may be useful for sparse binding.
17718 -# If you already have a buffer or an image created, you want to allocate memory
17719    for it and then you will bind it yourself, you can use function
17720    vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage().
17721    For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory()
17722    or their extended versions: vmaBindBufferMemory2(), vmaBindImageMemory2().
17723 -# **This is the easiest and recommended way to use this library:**
17724    If you want to create a buffer or an image, allocate memory for it and bind
17725    them together, all in one call, you can use function vmaCreateBuffer(),
17726    vmaCreateImage().
17727 
17728 When using 3. or 4., the library internally queries Vulkan for memory types
17729 supported for that buffer or image (function `vkGetBufferMemoryRequirements()`)
17730 and uses only one of these types.
17731 
17732 If no memory type can be found that meets all the requirements, these functions
17733 return `VK_ERROR_FEATURE_NOT_PRESENT`.
17734 
17735 You can leave VmaAllocationCreateInfo structure completely filled with zeros.
17736 It means no requirements are specified for memory type.
17737 It is valid, although not very useful.
17738 
17739 \section choosing_memory_type_usage Usage
17740 
17741 The easiest way to specify memory requirements is to fill member
17742 VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage.
17743 It defines high level, common usage types.
17744 Since version 3 of the library, it is recommended to use #VMA_MEMORY_USAGE_AUTO to let it select best memory type for your resource automatically.
17745 
17746 For example, if you want to create a uniform buffer that will be filled using
17747 transfer only once or infrequently and then used for rendering every frame as a uniform buffer, you can
17748 do it using following code. The buffer will most likely end up in a memory type with
17749 `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT` to be fast to access by the GPU device.
17750 
17751 \code
17752 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
17753 bufferInfo.size = 65536;
17754 bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
17755 
17756 VmaAllocationCreateInfo allocInfo = {};
17757 allocInfo.usage = VMA_MEMORY_USAGE_AUTO;
17758 
17759 VkBuffer buffer;
17760 VmaAllocation allocation;
17761 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
17762 \endcode
17763 
17764 If you have a preference for putting the resource in GPU (device) memory or CPU (host) memory
17765 on systems with discrete graphics card that have the memories separate, you can use
17766 #VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE or #VMA_MEMORY_USAGE_AUTO_PREFER_HOST.
17767 
17768 When using `VMA_MEMORY_USAGE_AUTO*` while you want to map the allocated memory,
17769 you also need to specify one of the host access flags:
17770 #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.
17771 This will help the library decide about preferred memory type to ensure it has `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`
17772 so you can map it.
17773 
17774 For example, a staging buffer that will be filled via mapped pointer and then
17775 used as a source of transfer to the buffer decribed previously can be created like this.
17776 It will likely and up in a memory type that is `HOST_VISIBLE` and `HOST_COHERENT`
17777 but not `HOST_CACHED` (meaning uncached, write-combined) and not `DEVICE_LOCAL` (meaning system RAM).
17778 
17779 \code
17780 VkBufferCreateInfo stagingBufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
17781 stagingBufferInfo.size = 65536;
17782 stagingBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
17783 
17784 VmaAllocationCreateInfo stagingAllocInfo = {};
17785 stagingAllocInfo.usage = VMA_MEMORY_USAGE_AUTO;
17786 stagingAllocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
17787 
17788 VkBuffer stagingBuffer;
17789 VmaAllocation stagingAllocation;
17790 vmaCreateBuffer(allocator, &stagingBufferInfo, &stagingAllocInfo, &stagingBuffer, &stagingAllocation, nullptr);
17791 \endcode
17792 
17793 For more examples of creating different kinds of resources, see chapter \ref usage_patterns.
17794 
17795 Usage values `VMA_MEMORY_USAGE_AUTO*` are legal to use only when the library knows
17796 about the resource being created by having `VkBufferCreateInfo` / `VkImageCreateInfo` passed,
17797 so they work with functions like: vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo() etc.
17798 If you allocate raw memory using function vmaAllocateMemory(), you have to use other means of selecting
17799 memory type, as decribed below.
17800 
17801 \note
17802 Old usage values (`VMA_MEMORY_USAGE_GPU_ONLY`, `VMA_MEMORY_USAGE_CPU_ONLY`,
17803 `VMA_MEMORY_USAGE_CPU_TO_GPU`, `VMA_MEMORY_USAGE_GPU_TO_CPU`, `VMA_MEMORY_USAGE_CPU_COPY`)
17804 are still available and work same way as in previous versions of the library
17805 for backward compatibility, but they are not recommended.
17806 
17807 \section choosing_memory_type_required_preferred_flags Required and preferred flags
17808 
17809 You can specify more detailed requirements by filling members
17810 VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags
17811 with a combination of bits from enum `VkMemoryPropertyFlags`. For example,
17812 if you want to create a buffer that will be persistently mapped on host (so it
17813 must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`,
17814 use following code:
17815 
17816 \code
17817 VmaAllocationCreateInfo allocInfo = {};
17818 allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
17819 allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
17820 allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
17821 
17822 VkBuffer buffer;
17823 VmaAllocation allocation;
17824 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
17825 \endcode
17826 
17827 A memory type is chosen that has all the required flags and as many preferred
17828 flags set as possible.
17829 
17830 Value passed in VmaAllocationCreateInfo::usage is internally converted to a set of required and preferred flags,
17831 plus some extra "magic" (heuristics).
17832 
17833 \section choosing_memory_type_explicit_memory_types Explicit memory types
17834 
17835 If you inspected memory types available on the physical device and you have
17836 a preference for memory types that you want to use, you can fill member
17837 VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set
17838 means that a memory type with that index is allowed to be used for the
17839 allocation. Special value 0, just like `UINT32_MAX`, means there are no
17840 restrictions to memory type index.
17841 
17842 Please note that this member is NOT just a memory type index.
17843 Still you can use it to choose just one, specific memory type.
17844 For example, if you already determined that your buffer should be created in
17845 memory type 2, use following code:
17846 
17847 \code
17848 uint32_t memoryTypeIndex = 2;
17849 
17850 VmaAllocationCreateInfo allocInfo = {};
17851 allocInfo.memoryTypeBits = 1u << memoryTypeIndex;
17852 
17853 VkBuffer buffer;
17854 VmaAllocation allocation;
17855 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
17856 \endcode
17857 
17858 
17859 \section choosing_memory_type_custom_memory_pools Custom memory pools
17860 
17861 If you allocate from custom memory pool, all the ways of specifying memory
17862 requirements described above are not applicable and the aforementioned members
17863 of VmaAllocationCreateInfo structure are ignored. Memory type is selected
17864 explicitly when creating the pool and then used to make all the allocations from
17865 that pool. For further details, see \ref custom_memory_pools.
17866 
17867 \section choosing_memory_type_dedicated_allocations Dedicated allocations
17868 
17869 Memory for allocations is reserved out of larger block of `VkDeviceMemory`
17870 allocated from Vulkan internally. That is the main feature of this whole library.
17871 You can still request a separate memory block to be created for an allocation,
17872 just like you would do in a trivial solution without using any allocator.
17873 In that case, a buffer or image is always bound to that memory at offset 0.
17874 This is called a "dedicated allocation".
17875 You can explicitly request it by using flag #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
17876 The library can also internally decide to use dedicated allocation in some cases, e.g.:
17877 
17878 - When the size of the allocation is large.
17879 - When [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension is enabled
17880   and it reports that dedicated allocation is required or recommended for the resource.
17881 - When allocation of next big memory block fails due to not enough device memory,
17882   but allocation with the exact requested size succeeds.
17883 
17884 
17885 \page memory_mapping Memory mapping
17886 
17887 To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`,
17888 to be able to read from it or write to it in CPU code.
17889 Mapping is possible only of memory allocated from a memory type that has
17890 `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag.
17891 Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose.
17892 You can use them directly with memory allocated by this library,
17893 but it is not recommended because of following issue:
17894 Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed.
17895 This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan.
17896 Because of this, Vulkan Memory Allocator provides following facilities:
17897 
17898 \note If you want to be able to map an allocation, you need to specify one of the flags
17899 #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT
17900 in VmaAllocationCreateInfo::flags. These flags are required for an allocation to be mappable
17901 when using #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` enum values.
17902 For other usage values they are ignored and every such allocation made in `HOST_VISIBLE` memory type is mappable,
17903 but they can still be used for consistency.
17904 
17905 \section memory_mapping_mapping_functions Mapping functions
17906 
17907 The library provides following functions for mapping of a specific #VmaAllocation: vmaMapMemory(), vmaUnmapMemory().
17908 They are safer and more convenient to use than standard Vulkan functions.
17909 You can map an allocation multiple times simultaneously - mapping is reference-counted internally.
17910 You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block.
17911 The way it is implemented is that the library always maps entire memory block, not just region of the allocation.
17912 For further details, see description of vmaMapMemory() function.
17913 Example:
17914 
17915 \code
17916 // Having these objects initialized:
17917 struct ConstantBuffer
17918 {
17919     ...
17920 };
17921 ConstantBuffer constantBufferData = ...
17922 
17923 VmaAllocator allocator = ...
17924 VkBuffer constantBuffer = ...
17925 VmaAllocation constantBufferAllocation = ...
17926 
17927 // You can map and fill your buffer using following code:
17928 
17929 void* mappedData;
17930 vmaMapMemory(allocator, constantBufferAllocation, &mappedData);
17931 memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
17932 vmaUnmapMemory(allocator, constantBufferAllocation);
17933 \endcode
17934 
17935 When mapping, you may see a warning from Vulkan validation layer similar to this one:
17936 
17937 <i>Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.</i>
17938 
17939 It happens because the library maps entire `VkDeviceMemory` block, where different
17940 types of images and buffers may end up together, especially on GPUs with unified memory like Intel.
17941 You can safely ignore it if you are sure you access only memory of the intended
17942 object that you wanted to map.
17943 
17944 
17945 \section memory_mapping_persistently_mapped_memory Persistently mapped memory
17946 
17947 Kepping your memory persistently mapped is generally OK in Vulkan.
17948 You don't need to unmap it before using its data on the GPU.
17949 The library provides a special feature designed for that:
17950 Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in
17951 VmaAllocationCreateInfo::flags stay mapped all the time,
17952 so you can just access CPU pointer to it any time
17953 without a need to call any "map" or "unmap" function.
17954 Example:
17955 
17956 \code
17957 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
17958 bufCreateInfo.size = sizeof(ConstantBuffer);
17959 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
17960 
17961 VmaAllocationCreateInfo allocCreateInfo = {};
17962 allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
17963 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
17964     VMA_ALLOCATION_CREATE_MAPPED_BIT;
17965 
17966 VkBuffer buf;
17967 VmaAllocation alloc;
17968 VmaAllocationInfo allocInfo;
17969 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
17970 
17971 // Buffer is already mapped. You can access its memory.
17972 memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
17973 \endcode
17974 
17975 \note #VMA_ALLOCATION_CREATE_MAPPED_BIT by itself doesn't guarantee that the allocation will end up
17976 in a mappable memory type.
17977 For this, you need to also specify #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or
17978 #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.
17979 #VMA_ALLOCATION_CREATE_MAPPED_BIT only guarantees that if the memory is `HOST_VISIBLE`, the allocation will be mapped on creation.
17980 For an example of how to make use of this fact, see section \ref usage_patterns_advanced_data_uploading.
17981 
17982 \section memory_mapping_cache_control Cache flush and invalidate
17983 
17984 Memory in Vulkan doesn't need to be unmapped before using it on GPU,
17985 but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set,
17986 you need to manually **invalidate** cache before reading of mapped pointer
17987 and **flush** cache after writing to mapped pointer.
17988 Map/unmap operations don't do that automatically.
17989 Vulkan provides following functions for this purpose `vkFlushMappedMemoryRanges()`,
17990 `vkInvalidateMappedMemoryRanges()`, but this library provides more convenient
17991 functions that refer to given allocation object: vmaFlushAllocation(),
17992 vmaInvalidateAllocation(),
17993 or multiple objects at once: vmaFlushAllocations(), vmaInvalidateAllocations().
17994 
17995 Regions of memory specified for flush/invalidate must be aligned to
17996 `VkPhysicalDeviceLimits::nonCoherentAtomSize`. This is automatically ensured by the library.
17997 In any memory type that is `HOST_VISIBLE` but not `HOST_COHERENT`, all allocations
17998 within blocks are aligned to this value, so their offsets are always multiply of
17999 `nonCoherentAtomSize` and two different allocations never share same "line" of this size.
18000 
18001 Also, Windows drivers from all 3 PC GPU vendors (AMD, Intel, NVIDIA)
18002 currently provide `HOST_COHERENT` flag on all memory types that are
18003 `HOST_VISIBLE`, so on PC you may not need to bother.
18004 
18005 
18006 \page staying_within_budget Staying within budget
18007 
18008 When developing a graphics-intensive game or program, it is important to avoid allocating
18009 more GPU memory than it is physically available. When the memory is over-committed,
18010 various bad things can happen, depending on the specific GPU, graphics driver, and
18011 operating system:
18012 
18013 - It may just work without any problems.
18014 - The application may slow down because some memory blocks are moved to system RAM
18015   and the GPU has to access them through PCI Express bus.
18016 - A new allocation may take very long time to complete, even few seconds, and possibly
18017   freeze entire system.
18018 - The new allocation may fail with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
18019 - It may even result in GPU crash (TDR), observed as `VK_ERROR_DEVICE_LOST`
18020   returned somewhere later.
18021 
18022 \section staying_within_budget_querying_for_budget Querying for budget
18023 
18024 To query for current memory usage and available budget, use function vmaGetHeapBudgets().
18025 Returned structure #VmaBudget contains quantities expressed in bytes, per Vulkan memory heap.
18026 
18027 Please note that this function returns different information and works faster than
18028 vmaCalculateStatistics(). vmaGetHeapBudgets() can be called every frame or even before every
18029 allocation, while vmaCalculateStatistics() is intended to be used rarely,
18030 only to obtain statistical information, e.g. for debugging purposes.
18031 
18032 It is recommended to use <b>VK_EXT_memory_budget</b> device extension to obtain information
18033 about the budget from Vulkan device. VMA is able to use this extension automatically.
18034 When not enabled, the allocator behaves same way, but then it estimates current usage
18035 and available budget based on its internal information and Vulkan memory heap sizes,
18036 which may be less precise. In order to use this extension:
18037 
18038 1. Make sure extensions VK_EXT_memory_budget and VK_KHR_get_physical_device_properties2
18039    required by it are available and enable them. Please note that the first is a device
18040    extension and the second is instance extension!
18041 2. Use flag #VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT when creating #VmaAllocator object.
18042 3. Make sure to call vmaSetCurrentFrameIndex() every frame. Budget is queried from
18043    Vulkan inside of it to avoid overhead of querying it with every allocation.
18044 
18045 \section staying_within_budget_controlling_memory_usage Controlling memory usage
18046 
18047 There are many ways in which you can try to stay within the budget.
18048 
18049 First, when making new allocation requires allocating a new memory block, the library
18050 tries not to exceed the budget automatically. If a block with default recommended size
18051 (e.g. 256 MB) would go over budget, a smaller block is allocated, possibly even
18052 dedicated memory for just this resource.
18053 
18054 If the size of the requested resource plus current memory usage is more than the
18055 budget, by default the library still tries to create it, leaving it to the Vulkan
18056 implementation whether the allocation succeeds or fails. You can change this behavior
18057 by using #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag. With it, the allocation is
18058 not made if it would exceed the budget or if the budget is already exceeded.
18059 VMA then tries to make the allocation from the next eligible Vulkan memory type.
18060 The all of them fail, the call then fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
18061 Example usage pattern may be to pass the #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag
18062 when creating resources that are not essential for the application (e.g. the texture
18063 of a specific object) and not to pass it when creating critically important resources
18064 (e.g. render targets).
18065 
18066 On AMD graphics cards there is a custom vendor extension available: <b>VK_AMD_memory_overallocation_behavior</b>
18067 that allows to control the behavior of the Vulkan implementation in out-of-memory cases -
18068 whether it should fail with an error code or still allow the allocation.
18069 Usage of this extension involves only passing extra structure on Vulkan device creation,
18070 so it is out of scope of this library.
18071 
18072 Finally, you can also use #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT flag to make sure
18073 a new allocation is created only when it fits inside one of the existing memory blocks.
18074 If it would require to allocate a new block, if fails instead with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
18075 This also ensures that the function call is very fast because it never goes to Vulkan
18076 to obtain a new block.
18077 
18078 \note Creating \ref custom_memory_pools with VmaPoolCreateInfo::minBlockCount
18079 set to more than 0 will currently try to allocate memory blocks without checking whether they
18080 fit within budget.
18081 
18082 
18083 \page resource_aliasing Resource aliasing (overlap)
18084 
18085 New explicit graphics APIs (Vulkan and Direct3D 12), thanks to manual memory
18086 management, give an opportunity to alias (overlap) multiple resources in the
18087 same region of memory - a feature not available in the old APIs (Direct3D 11, OpenGL).
18088 It can be useful to save video memory, but it must be used with caution.
18089 
18090 For example, if you know the flow of your whole render frame in advance, you
18091 are going to use some intermediate textures or buffers only during a small range of render passes,
18092 and you know these ranges don't overlap in time, you can bind these resources to
18093 the same place in memory, even if they have completely different parameters (width, height, format etc.).
18094 
18095 ![Resource aliasing (overlap)](../gfx/Aliasing.png)
18096 
18097 Such scenario is possible using VMA, but you need to create your images manually.
18098 Then you need to calculate parameters of an allocation to be made using formula:
18099 
18100 - allocation size = max(size of each image)
18101 - allocation alignment = max(alignment of each image)
18102 - allocation memoryTypeBits = bitwise AND(memoryTypeBits of each image)
18103 
18104 Following example shows two different images bound to the same place in memory,
18105 allocated to fit largest of them.
18106 
18107 \code
18108 // A 512x512 texture to be sampled.
18109 VkImageCreateInfo img1CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
18110 img1CreateInfo.imageType = VK_IMAGE_TYPE_2D;
18111 img1CreateInfo.extent.width = 512;
18112 img1CreateInfo.extent.height = 512;
18113 img1CreateInfo.extent.depth = 1;
18114 img1CreateInfo.mipLevels = 10;
18115 img1CreateInfo.arrayLayers = 1;
18116 img1CreateInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
18117 img1CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
18118 img1CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
18119 img1CreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
18120 img1CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
18121 
18122 // A full screen texture to be used as color attachment.
18123 VkImageCreateInfo img2CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
18124 img2CreateInfo.imageType = VK_IMAGE_TYPE_2D;
18125 img2CreateInfo.extent.width = 1920;
18126 img2CreateInfo.extent.height = 1080;
18127 img2CreateInfo.extent.depth = 1;
18128 img2CreateInfo.mipLevels = 1;
18129 img2CreateInfo.arrayLayers = 1;
18130 img2CreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
18131 img2CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
18132 img2CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
18133 img2CreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
18134 img2CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
18135 
18136 VkImage img1;
18137 res = vkCreateImage(device, &img1CreateInfo, nullptr, &img1);
18138 VkImage img2;
18139 res = vkCreateImage(device, &img2CreateInfo, nullptr, &img2);
18140 
18141 VkMemoryRequirements img1MemReq;
18142 vkGetImageMemoryRequirements(device, img1, &img1MemReq);
18143 VkMemoryRequirements img2MemReq;
18144 vkGetImageMemoryRequirements(device, img2, &img2MemReq);
18145 
18146 VkMemoryRequirements finalMemReq = {};
18147 finalMemReq.size = std::max(img1MemReq.size, img2MemReq.size);
18148 finalMemReq.alignment = std::max(img1MemReq.alignment, img2MemReq.alignment);
18149 finalMemReq.memoryTypeBits = img1MemReq.memoryTypeBits & img2MemReq.memoryTypeBits;
18150 // Validate if(finalMemReq.memoryTypeBits != 0)
18151 
18152 VmaAllocationCreateInfo allocCreateInfo = {};
18153 allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18154 
18155 VmaAllocation alloc;
18156 res = vmaAllocateMemory(allocator, &finalMemReq, &allocCreateInfo, &alloc, nullptr);
18157 
18158 res = vmaBindImageMemory(allocator, alloc, img1);
18159 res = vmaBindImageMemory(allocator, alloc, img2);
18160 
18161 // You can use img1, img2 here, but not at the same time!
18162 
18163 vmaFreeMemory(allocator, alloc);
18164 vkDestroyImage(allocator, img2, nullptr);
18165 vkDestroyImage(allocator, img1, nullptr);
18166 \endcode
18167 
18168 Remember that using resources that alias in memory requires proper synchronization.
18169 You need to issue a memory barrier to make sure commands that use `img1` and `img2`
18170 don't overlap on GPU timeline.
18171 You also need to treat a resource after aliasing as uninitialized - containing garbage data.
18172 For example, if you use `img1` and then want to use `img2`, you need to issue
18173 an image memory barrier for `img2` with `oldLayout` = `VK_IMAGE_LAYOUT_UNDEFINED`.
18174 
18175 Additional considerations:
18176 
18177 - Vulkan also allows to interpret contents of memory between aliasing resources consistently in some cases.
18178 See chapter 11.8. "Memory Aliasing" of Vulkan specification or `VK_IMAGE_CREATE_ALIAS_BIT` flag.
18179 - You can create more complex layout where different images and buffers are bound
18180 at different offsets inside one large allocation. For example, one can imagine
18181 a big texture used in some render passes, aliasing with a set of many small buffers
18182 used between in some further passes. To bind a resource at non-zero offset in an allocation,
18183 use vmaBindBufferMemory2() / vmaBindImageMemory2().
18184 - Before allocating memory for the resources you want to alias, check `memoryTypeBits`
18185 returned in memory requirements of each resource to make sure the bits overlap.
18186 Some GPUs may expose multiple memory types suitable e.g. only for buffers or
18187 images with `COLOR_ATTACHMENT` usage, so the sets of memory types supported by your
18188 resources may be disjoint. Aliasing them is not possible in that case.
18189 
18190 
18191 \page custom_memory_pools Custom memory pools
18192 
18193 A memory pool contains a number of `VkDeviceMemory` blocks.
18194 The library automatically creates and manages default pool for each memory type available on the device.
18195 Default memory pool automatically grows in size.
18196 Size of allocated blocks is also variable and managed automatically.
18197 
18198 You can create custom pool and allocate memory out of it.
18199 It can be useful if you want to:
18200 
18201 - Keep certain kind of allocations separate from others.
18202 - Enforce particular, fixed size of Vulkan memory blocks.
18203 - Limit maximum amount of Vulkan memory allocated for that pool.
18204 - Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool.
18205 - Use extra parameters for a set of your allocations that are available in #VmaPoolCreateInfo but not in
18206   #VmaAllocationCreateInfo - e.g., custom minimum alignment, custom `pNext` chain.
18207 - Perform defragmentation on a specific subset of your allocations.
18208 
18209 To use custom memory pools:
18210 
18211 -# Fill VmaPoolCreateInfo structure.
18212 -# Call vmaCreatePool() to obtain #VmaPool handle.
18213 -# When making an allocation, set VmaAllocationCreateInfo::pool to this handle.
18214    You don't need to specify any other parameters of this structure, like `usage`.
18215 
18216 Example:
18217 
18218 \code
18219 // Find memoryTypeIndex for the pool.
18220 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
18221 sampleBufCreateInfo.size = 0x10000; // Doesn't matter.
18222 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
18223 
18224 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
18225 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
18226 
18227 uint32_t memTypeIndex;
18228 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(allocator,
18229     &sampleBufCreateInfo, &sampleAllocCreateInfo, &memTypeIndex);
18230 // Check res...
18231 
18232 // Create a pool that can have at most 2 blocks, 128 MiB each.
18233 VmaPoolCreateInfo poolCreateInfo = {};
18234 poolCreateInfo.memoryTypeIndex = memTypeIndex;
18235 poolCreateInfo.blockSize = 128ull * 1024 * 1024;
18236 poolCreateInfo.maxBlockCount = 2;
18237 
18238 VmaPool pool;
18239 res = vmaCreatePool(allocator, &poolCreateInfo, &pool);
18240 // Check res...
18241 
18242 // Allocate a buffer out of it.
18243 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
18244 bufCreateInfo.size = 1024;
18245 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
18246 
18247 VmaAllocationCreateInfo allocCreateInfo = {};
18248 allocCreateInfo.pool = pool;
18249 
18250 VkBuffer buf;
18251 VmaAllocation alloc;
18252 res = vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr);
18253 // Check res...
18254 \endcode
18255 
18256 You have to free all allocations made from this pool before destroying it.
18257 
18258 \code
18259 vmaDestroyBuffer(allocator, buf, alloc);
18260 vmaDestroyPool(allocator, pool);
18261 \endcode
18262 
18263 New versions of this library support creating dedicated allocations in custom pools.
18264 It is supported only when VmaPoolCreateInfo::blockSize = 0.
18265 To use this feature, set VmaAllocationCreateInfo::pool to the pointer to your custom pool and
18266 VmaAllocationCreateInfo::flags to #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
18267 
18268 \note Excessive use of custom pools is a common mistake when using this library.
18269 Custom pools may be useful for special purposes - when you want to
18270 keep certain type of resources separate e.g. to reserve minimum amount of memory
18271 for them or limit maximum amount of memory they can occupy. For most
18272 resources this is not needed and so it is not recommended to create #VmaPool
18273 objects and allocations out of them. Allocating from the default pool is sufficient.
18274 
18275 
18276 \section custom_memory_pools_MemTypeIndex Choosing memory type index
18277 
18278 When creating a pool, you must explicitly specify memory type index.
18279 To find the one suitable for your buffers or images, you can use helper functions
18280 vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo().
18281 You need to provide structures with example parameters of buffers or images
18282 that you are going to create in that pool.
18283 
18284 \code
18285 VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
18286 exampleBufCreateInfo.size = 1024; // Doesn't matter
18287 exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
18288 
18289 VmaAllocationCreateInfo allocCreateInfo = {};
18290 allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
18291 
18292 uint32_t memTypeIndex;
18293 vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex);
18294 
18295 VmaPoolCreateInfo poolCreateInfo = {};
18296 poolCreateInfo.memoryTypeIndex = memTypeIndex;
18297 // ...
18298 \endcode
18299 
18300 When creating buffers/images allocated in that pool, provide following parameters:
18301 
18302 - `VkBufferCreateInfo`: Prefer to pass same parameters as above.
18303   Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior.
18304   Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers
18305   or the other way around.
18306 - VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member.
18307   Other members are ignored anyway.
18308 
18309 \section linear_algorithm Linear allocation algorithm
18310 
18311 Each Vulkan memory block managed by this library has accompanying metadata that
18312 keeps track of used and unused regions. By default, the metadata structure and
18313 algorithm tries to find best place for new allocations among free regions to
18314 optimize memory usage. This way you can allocate and free objects in any order.
18315 
18316 ![Default allocation algorithm](../gfx/Linear_allocator_1_algo_default.png)
18317 
18318 Sometimes there is a need to use simpler, linear allocation algorithm. You can
18319 create custom pool that uses such algorithm by adding flag
18320 #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
18321 #VmaPool object. Then an alternative metadata management is used. It always
18322 creates new allocations after last one and doesn't reuse free regions after
18323 allocations freed in the middle. It results in better allocation performance and
18324 less memory consumed by metadata.
18325 
18326 ![Linear allocation algorithm](../gfx/Linear_allocator_2_algo_linear.png)
18327 
18328 With this one flag, you can create a custom pool that can be used in many ways:
18329 free-at-once, stack, double stack, and ring buffer. See below for details.
18330 You don't need to specify explicitly which of these options you are going to use - it is detected automatically.
18331 
18332 \subsection linear_algorithm_free_at_once Free-at-once
18333 
18334 In a pool that uses linear algorithm, you still need to free all the allocations
18335 individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free
18336 them in any order. New allocations are always made after last one - free space
18337 in the middle is not reused. However, when you release all the allocation and
18338 the pool becomes empty, allocation starts from the beginning again. This way you
18339 can use linear algorithm to speed up creation of allocations that you are going
18340 to release all at once.
18341 
18342 ![Free-at-once](../gfx/Linear_allocator_3_free_at_once.png)
18343 
18344 This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
18345 value that allows multiple memory blocks.
18346 
18347 \subsection linear_algorithm_stack Stack
18348 
18349 When you free an allocation that was created last, its space can be reused.
18350 Thanks to this, if you always release allocations in the order opposite to their
18351 creation (LIFO - Last In First Out), you can achieve behavior of a stack.
18352 
18353 ![Stack](../gfx/Linear_allocator_4_stack.png)
18354 
18355 This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
18356 value that allows multiple memory blocks.
18357 
18358 \subsection linear_algorithm_double_stack Double stack
18359 
18360 The space reserved by a custom pool with linear algorithm may be used by two
18361 stacks:
18362 
18363 - First, default one, growing up from offset 0.
18364 - Second, "upper" one, growing down from the end towards lower offsets.
18365 
18366 To make allocation from the upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT
18367 to VmaAllocationCreateInfo::flags.
18368 
18369 ![Double stack](../gfx/Linear_allocator_7_double_stack.png)
18370 
18371 Double stack is available only in pools with one memory block -
18372 VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
18373 
18374 When the two stacks' ends meet so there is not enough space between them for a
18375 new allocation, such allocation fails with usual
18376 `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
18377 
18378 \subsection linear_algorithm_ring_buffer Ring buffer
18379 
18380 When you free some allocations from the beginning and there is not enough free space
18381 for a new one at the end of a pool, allocator's "cursor" wraps around to the
18382 beginning and starts allocation there. Thanks to this, if you always release
18383 allocations in the same order as you created them (FIFO - First In First Out),
18384 you can achieve behavior of a ring buffer / queue.
18385 
18386 ![Ring buffer](../gfx/Linear_allocator_5_ring_buffer.png)
18387 
18388 Ring buffer is available only in pools with one memory block -
18389 VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
18390 
18391 \note \ref defragmentation is not supported in custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT.
18392 
18393 
18394 \page defragmentation Defragmentation
18395 
18396 Interleaved allocations and deallocations of many objects of varying size can
18397 cause fragmentation over time, which can lead to a situation where the library is unable
18398 to find a continuous range of free memory for a new allocation despite there is
18399 enough free space, just scattered across many small free ranges between existing
18400 allocations.
18401 
18402 To mitigate this problem, you can use defragmentation feature.
18403 It doesn't happen automatically though and needs your cooperation,
18404 because VMA is a low level library that only allocates memory.
18405 It cannot recreate buffers and images in a new place as it doesn't remember the contents of `VkBufferCreateInfo` / `VkImageCreateInfo` structures.
18406 It cannot copy their contents as it doesn't record any commands to a command buffer.
18407 
18408 Example:
18409 
18410 \code
18411 VmaDefragmentationInfo defragInfo = {};
18412 defragInfo.pool = myPool;
18413 defragInfo.flags = VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT;
18414 
18415 VmaDefragmentationContext defragCtx;
18416 VkResult res = vmaBeginDefragmentation(allocator, &defragInfo, &defragCtx);
18417 // Check res...
18418 
18419 for(;;)
18420 {
18421     VmaDefragmentationPassMoveInfo pass;
18422     res = vmaBeginDefragmentationPass(allocator, defragCtx, &pass);
18423     if(res == VK_SUCCESS)
18424         break;
18425     else if(res != VK_INCOMPLETE)
18426         // Handle error...
18427 
18428     for(uint32_t i = 0; i < pass.moveCount; ++i)
18429     {
18430         // Inspect pass.pMoves[i].srcAllocation, identify what buffer/image it represents.
18431         VmaAllocationInfo allocInfo;
18432         vmaGetAllocationInfo(allocator, pMoves[i].srcAllocation, &allocInfo);
18433         MyEngineResourceData* resData = (MyEngineResourceData*)allocInfo.pUserData;
18434 
18435         // Recreate and bind this buffer/image at: pass.pMoves[i].dstMemory, pass.pMoves[i].dstOffset.
18436         VkImageCreateInfo imgCreateInfo = ...
18437         VkImage newImg;
18438         res = vkCreateImage(device, &imgCreateInfo, nullptr, &newImg);
18439         // Check res...
18440         res = vmaBindImageMemory(allocator, pMoves[i].dstTmpAllocation, newImg);
18441         // Check res...
18442 
18443         // Issue a vkCmdCopyBuffer/vkCmdCopyImage to copy its content to the new place.
18444         vkCmdCopyImage(cmdBuf, resData->img, ..., newImg, ...);
18445     }
18446 
18447     // Make sure the copy commands finished executing.
18448     vkWaitForFences(...);
18449 
18450     // Destroy old buffers/images bound with pass.pMoves[i].srcAllocation.
18451     for(uint32_t i = 0; i < pass.moveCount; ++i)
18452     {
18453         // ...
18454         vkDestroyImage(device, resData->img, nullptr);
18455     }
18456 
18457     // Update appropriate descriptors to point to the new places...
18458 
18459     res = vmaEndDefragmentationPass(allocator, defragCtx, &pass);
18460     if(res == VK_SUCCESS)
18461         break;
18462     else if(res != VK_INCOMPLETE)
18463         // Handle error...
18464 }
18465 
18466 vmaEndDefragmentation(allocator, defragCtx, nullptr);
18467 \endcode
18468 
18469 Although functions like vmaCreateBuffer(), vmaCreateImage(), vmaDestroyBuffer(), vmaDestroyImage()
18470 create/destroy an allocation and a buffer/image at once, these are just a shortcut for
18471 creating the resource, allocating memory, and binding them together.
18472 Defragmentation works on memory allocations only. You must handle the rest manually.
18473 Defragmentation is an iterative process that should repreat "passes" as long as related functions
18474 return `VK_INCOMPLETE` not `VK_SUCCESS`.
18475 In each pass:
18476 
18477 1. vmaBeginDefragmentationPass() function call:
18478    - Calculates and returns the list of allocations to be moved in this pass.
18479      Note this can be a time-consuming process.
18480    - Reserves destination memory for them by creating temporary destination allocations
18481      that you can query for their `VkDeviceMemory` + offset using vmaGetAllocationInfo().
18482 2. Inside the pass, **you should**:
18483    - Inspect the returned list of allocations to be moved.
18484    - Create new buffers/images and bind them at the returned destination temporary allocations.
18485    - Copy data from source to destination resources if necessary.
18486    - Destroy the source buffers/images, but NOT their allocations.
18487 3. vmaEndDefragmentationPass() function call:
18488    - Frees the source memory reserved for the allocations that are moved.
18489    - Modifies source #VmaAllocation objects that are moved to point to the destination reserved memory.
18490    - Frees `VkDeviceMemory` blocks that became empty.
18491 
18492 Unlike in previous iterations of the defragmentation API, there is no list of "movable" allocations passed as a parameter.
18493 Defragmentation algorithm tries to move all suitable allocations.
18494 You can, however, refuse to move some of them inside a defragmentation pass, by setting
18495 `pass.pMoves[i].operation` to #VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE.
18496 This is not recommended and may result in suboptimal packing of the allocations after defragmentation.
18497 If you cannot ensure any allocation can be moved, it is better to keep movable allocations separate in a custom pool.
18498 
18499 Inside a pass, for each allocation that should be moved:
18500 
18501 - You should copy its data from the source to the destination place by calling e.g. `vkCmdCopyBuffer()`, `vkCmdCopyImage()`.
18502   - You need to make sure these commands finished executing before destroying the source buffers/images and before calling vmaEndDefragmentationPass().
18503 - If a resource doesn't contain any meaningful data, e.g. it is a transient color attachment image to be cleared,
18504   filled, and used temporarily in each rendering frame, you can just recreate this image
18505   without copying its data.
18506 - If the resource is in `HOST_VISIBLE` and `HOST_CACHED` memory, you can copy its data on the CPU
18507   using `memcpy()`.
18508 - If you cannot move the allocation, you can set `pass.pMoves[i].operation` to #VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE.
18509   This will cancel the move.
18510   - vmaEndDefragmentationPass() will then free the destination memory
18511     not the source memory of the allocation, leaving it unchanged.
18512 - If you decide the allocation is unimportant and can be destroyed instead of moved (e.g. it wasn't used for long time),
18513   you can set `pass.pMoves[i].operation` to #VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY.
18514   - vmaEndDefragmentationPass() will then free both source and destination memory, and will destroy the source #VmaAllocation object.
18515 
18516 You can defragment a specific custom pool by setting VmaDefragmentationInfo::pool
18517 (like in the example above) or all the default pools by setting this member to null.
18518 
18519 Defragmentation is always performed in each pool separately.
18520 Allocations are never moved between different Vulkan memory types.
18521 The size of the destination memory reserved for a moved allocation is the same as the original one.
18522 Alignment of an allocation as it was determined using `vkGetBufferMemoryRequirements()` etc. is also respected after defragmentation.
18523 Buffers/images should be recreated with the same `VkBufferCreateInfo` / `VkImageCreateInfo` parameters as the original ones.
18524 
18525 You can perform the defragmentation incrementally to limit the number of allocations and bytes to be moved
18526 in each pass, e.g. to call it in sync with render frames and not to experience too big hitches.
18527 See members: VmaDefragmentationInfo::maxBytesPerPass, VmaDefragmentationInfo::maxAllocationsPerPass.
18528 
18529 It is also safe to perform the defragmentation asynchronously to render frames and other Vulkan and VMA
18530 usage, possibly from multiple threads, with the exception that allocations
18531 returned in VmaDefragmentationPassMoveInfo::pMoves shouldn't be destroyed until the defragmentation pass is ended.
18532 
18533 <b>Mapping</b> is preserved on allocations that are moved during defragmentation.
18534 Whether through #VMA_ALLOCATION_CREATE_MAPPED_BIT or vmaMapMemory(), the allocations
18535 are mapped at their new place. Of course, pointer to the mapped data changes, so it needs to be queried
18536 using VmaAllocationInfo::pMappedData.
18537 
18538 \note Defragmentation is not supported in custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT.
18539 
18540 
18541 \page statistics Statistics
18542 
18543 This library contains several functions that return information about its internal state,
18544 especially the amount of memory allocated from Vulkan.
18545 
18546 \section statistics_numeric_statistics Numeric statistics
18547 
18548 If you need to obtain basic statistics about memory usage per heap, together with current budget,
18549 you can call function vmaGetHeapBudgets() and inspect structure #VmaBudget.
18550 This is useful to keep track of memory usage and stay withing budget
18551 (see also \ref staying_within_budget).
18552 Example:
18553 
18554 \code
18555 uint32_t heapIndex = ...
18556 
18557 VmaBudget budgets[VK_MAX_MEMORY_HEAPS];
18558 vmaGetHeapBudgets(allocator, budgets);
18559 
18560 printf("My heap currently has %u allocations taking %llu B,\n",
18561     budgets[heapIndex].statistics.allocationCount,
18562     budgets[heapIndex].statistics.allocationBytes);
18563 printf("allocated out of %u Vulkan device memory blocks taking %llu B,\n",
18564     budgets[heapIndex].statistics.blockCount,
18565     budgets[heapIndex].statistics.blockBytes);
18566 printf("Vulkan reports total usage %llu B with budget %llu B.\n",
18567     budgets[heapIndex].usage,
18568     budgets[heapIndex].budget);
18569 \endcode
18570 
18571 You can query for more detailed statistics per memory heap, type, and totals,
18572 including minimum and maximum allocation size and unused range size,
18573 by calling function vmaCalculateStatistics() and inspecting structure #VmaTotalStatistics.
18574 This function is slower though, as it has to traverse all the internal data structures,
18575 so it should be used only for debugging purposes.
18576 
18577 You can query for statistics of a custom pool using function vmaGetPoolStatistics()
18578 or vmaCalculatePoolStatistics().
18579 
18580 You can query for information about a specific allocation using function vmaGetAllocationInfo().
18581 It fill structure #VmaAllocationInfo.
18582 
18583 \section statistics_json_dump JSON dump
18584 
18585 You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString().
18586 The result is guaranteed to be correct JSON.
18587 It uses ANSI encoding.
18588 Any strings provided by user (see [Allocation names](@ref allocation_names))
18589 are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding,
18590 this JSON string can be treated as using this encoding.
18591 It must be freed using function vmaFreeStatsString().
18592 
18593 The format of this JSON string is not part of official documentation of the library,
18594 but it will not change in backward-incompatible way without increasing library major version number
18595 and appropriate mention in changelog.
18596 
18597 The JSON string contains all the data that can be obtained using vmaCalculateStatistics().
18598 It can also contain detailed map of allocated memory blocks and their regions -
18599 free and occupied by allocations.
18600 This allows e.g. to visualize the memory or assess fragmentation.
18601 
18602 
18603 \page allocation_annotation Allocation names and user data
18604 
18605 \section allocation_user_data Allocation user data
18606 
18607 You can annotate allocations with your own information, e.g. for debugging purposes.
18608 To do that, fill VmaAllocationCreateInfo::pUserData field when creating
18609 an allocation. It is an opaque `void*` pointer. You can use it e.g. as a pointer,
18610 some handle, index, key, ordinal number or any other value that would associate
18611 the allocation with your custom metadata.
18612 It it useful to identify appropriate data structures in your engine given #VmaAllocation,
18613 e.g. when doing \ref defragmentation.
18614 
18615 \code
18616 VkBufferCreateInfo bufCreateInfo = ...
18617 
18618 MyBufferMetadata* pMetadata = CreateBufferMetadata();
18619 
18620 VmaAllocationCreateInfo allocCreateInfo = {};
18621 allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
18622 allocCreateInfo.pUserData = pMetadata;
18623 
18624 VkBuffer buffer;
18625 VmaAllocation allocation;
18626 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buffer, &allocation, nullptr);
18627 \endcode
18628 
18629 The pointer may be later retrieved as VmaAllocationInfo::pUserData:
18630 
18631 \code
18632 VmaAllocationInfo allocInfo;
18633 vmaGetAllocationInfo(allocator, allocation, &allocInfo);
18634 MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData;
18635 \endcode
18636 
18637 It can also be changed using function vmaSetAllocationUserData().
18638 
18639 Values of (non-zero) allocations' `pUserData` are printed in JSON report created by
18640 vmaBuildStatsString() in hexadecimal form.
18641 
18642 \section allocation_names Allocation names
18643 
18644 An allocation can also carry a null-terminated string, giving a name to the allocation.
18645 To set it, call vmaSetAllocationName().
18646 The library creates internal copy of the string, so the pointer you pass doesn't need
18647 to be valid for whole lifetime of the allocation. You can free it after the call.
18648 
18649 \code
18650 std::string imageName = "Texture: ";
18651 imageName += fileName;
18652 vmaSetAllocationName(allocator, allocation, imageName.c_str());
18653 \endcode
18654 
18655 The string can be later retrieved by inspecting VmaAllocationInfo::pName.
18656 It is also printed in JSON report created by vmaBuildStatsString().
18657 
18658 \note Setting string name to VMA allocation doesn't automatically set it to the Vulkan buffer or image created with it.
18659 You must do it manually using an extension like VK_EXT_debug_utils, which is independent of this library.
18660 
18661 
18662 \page virtual_allocator Virtual allocator
18663 
18664 As an extra feature, the core allocation algorithm of the library is exposed through a simple and convenient API of "virtual allocator".
18665 It doesn't allocate any real GPU memory. It just keeps track of used and free regions of a "virtual block".
18666 You can use it to allocate your own memory or other objects, even completely unrelated to Vulkan.
18667 A common use case is sub-allocation of pieces of one large GPU buffer.
18668 
18669 \section virtual_allocator_creating_virtual_block Creating virtual block
18670 
18671 To use this functionality, there is no main "allocator" object.
18672 You don't need to have #VmaAllocator object created.
18673 All you need to do is to create a separate #VmaVirtualBlock object for each block of memory you want to be managed by the allocator:
18674 
18675 -# Fill in #VmaVirtualBlockCreateInfo structure.
18676 -# Call vmaCreateVirtualBlock(). Get new #VmaVirtualBlock object.
18677 
18678 Example:
18679 
18680 \code
18681 VmaVirtualBlockCreateInfo blockCreateInfo = {};
18682 blockCreateInfo.size = 1048576; // 1 MB
18683 
18684 VmaVirtualBlock block;
18685 VkResult res = vmaCreateVirtualBlock(&blockCreateInfo, &block);
18686 \endcode
18687 
18688 \section virtual_allocator_making_virtual_allocations Making virtual allocations
18689 
18690 #VmaVirtualBlock object contains internal data structure that keeps track of free and occupied regions
18691 using the same code as the main Vulkan memory allocator.
18692 Similarly to #VmaAllocation for standard GPU allocations, there is #VmaVirtualAllocation type
18693 that represents an opaque handle to an allocation withing the virtual block.
18694 
18695 In order to make such allocation:
18696 
18697 -# Fill in #VmaVirtualAllocationCreateInfo structure.
18698 -# Call vmaVirtualAllocate(). Get new #VmaVirtualAllocation object that represents the allocation.
18699    You can also receive `VkDeviceSize offset` that was assigned to the allocation.
18700 
18701 Example:
18702 
18703 \code
18704 VmaVirtualAllocationCreateInfo allocCreateInfo = {};
18705 allocCreateInfo.size = 4096; // 4 KB
18706 
18707 VmaVirtualAllocation alloc;
18708 VkDeviceSize offset;
18709 res = vmaVirtualAllocate(block, &allocCreateInfo, &alloc, &offset);
18710 if(res == VK_SUCCESS)
18711 {
18712     // Use the 4 KB of your memory starting at offset.
18713 }
18714 else
18715 {
18716     // Allocation failed - no space for it could be found. Handle this error!
18717 }
18718 \endcode
18719 
18720 \section virtual_allocator_deallocation Deallocation
18721 
18722 When no longer needed, an allocation can be freed by calling vmaVirtualFree().
18723 You can only pass to this function an allocation that was previously returned by vmaVirtualAllocate()
18724 called for the same #VmaVirtualBlock.
18725 
18726 When whole block is no longer needed, the block object can be released by calling vmaDestroyVirtualBlock().
18727 All allocations must be freed before the block is destroyed, which is checked internally by an assert.
18728 However, if you don't want to call vmaVirtualFree() for each allocation, you can use vmaClearVirtualBlock() to free them all at once -
18729 a feature not available in normal Vulkan memory allocator. Example:
18730 
18731 \code
18732 vmaVirtualFree(block, alloc);
18733 vmaDestroyVirtualBlock(block);
18734 \endcode
18735 
18736 \section virtual_allocator_allocation_parameters Allocation parameters
18737 
18738 You can attach a custom pointer to each allocation by using vmaSetVirtualAllocationUserData().
18739 Its default value is null.
18740 It can be used to store any data that needs to be associated with that allocation - e.g. an index, a handle, or a pointer to some
18741 larger data structure containing more information. Example:
18742 
18743 \code
18744 struct CustomAllocData
18745 {
18746     std::string m_AllocName;
18747 };
18748 CustomAllocData* allocData = new CustomAllocData();
18749 allocData->m_AllocName = "My allocation 1";
18750 vmaSetVirtualAllocationUserData(block, alloc, allocData);
18751 \endcode
18752 
18753 The pointer can later be fetched, along with allocation offset and size, by passing the allocation handle to function
18754 vmaGetVirtualAllocationInfo() and inspecting returned structure #VmaVirtualAllocationInfo.
18755 If you allocated a new object to be used as the custom pointer, don't forget to delete that object before freeing the allocation!
18756 Example:
18757 
18758 \code
18759 VmaVirtualAllocationInfo allocInfo;
18760 vmaGetVirtualAllocationInfo(block, alloc, &allocInfo);
18761 delete (CustomAllocData*)allocInfo.pUserData;
18762 
18763 vmaVirtualFree(block, alloc);
18764 \endcode
18765 
18766 \section virtual_allocator_alignment_and_units Alignment and units
18767 
18768 It feels natural to express sizes and offsets in bytes.
18769 If an offset of an allocation needs to be aligned to a multiply of some number (e.g. 4 bytes), you can fill optional member
18770 VmaVirtualAllocationCreateInfo::alignment to request it. Example:
18771 
18772 \code
18773 VmaVirtualAllocationCreateInfo allocCreateInfo = {};
18774 allocCreateInfo.size = 4096; // 4 KB
18775 allocCreateInfo.alignment = 4; // Returned offset must be a multiply of 4 B
18776 
18777 VmaVirtualAllocation alloc;
18778 res = vmaVirtualAllocate(block, &allocCreateInfo, &alloc, nullptr);
18779 \endcode
18780 
18781 Alignments of different allocations made from one block may vary.
18782 However, if all alignments and sizes are always multiply of some size e.g. 4 B or `sizeof(MyDataStruct)`,
18783 you can express all sizes, alignments, and offsets in multiples of that size instead of individual bytes.
18784 It might be more convenient, but you need to make sure to use this new unit consistently in all the places:
18785 
18786 - VmaVirtualBlockCreateInfo::size
18787 - VmaVirtualAllocationCreateInfo::size and VmaVirtualAllocationCreateInfo::alignment
18788 - Using offset returned by vmaVirtualAllocate() or in VmaVirtualAllocationInfo::offset
18789 
18790 \section virtual_allocator_statistics Statistics
18791 
18792 You can obtain statistics of a virtual block using vmaGetVirtualBlockStatistics()
18793 (to get brief statistics that are fast to calculate)
18794 or vmaCalculateVirtualBlockStatistics() (to get more detailed statistics, slower to calculate).
18795 The functions fill structures #VmaStatistics, #VmaDetailedStatistics respectively - same as used by the normal Vulkan memory allocator.
18796 Example:
18797 
18798 \code
18799 VmaStatistics stats;
18800 vmaGetVirtualBlockStatistics(block, &stats);
18801 printf("My virtual block has %llu bytes used by %u virtual allocations\n",
18802     stats.allocationBytes, stats.allocationCount);
18803 \endcode
18804 
18805 You can also request a full list of allocations and free regions as a string in JSON format by calling
18806 vmaBuildVirtualBlockStatsString().
18807 Returned string must be later freed using vmaFreeVirtualBlockStatsString().
18808 The format of this string differs from the one returned by the main Vulkan allocator, but it is similar.
18809 
18810 \section virtual_allocator_additional_considerations Additional considerations
18811 
18812 The "virtual allocator" functionality is implemented on a level of individual memory blocks.
18813 Keeping track of a whole collection of blocks, allocating new ones when out of free space,
18814 deleting empty ones, and deciding which one to try first for a new allocation must be implemented by the user.
18815 
18816 Alternative allocation algorithms are supported, just like in custom pools of the real GPU memory.
18817 See enum #VmaVirtualBlockCreateFlagBits to learn how to specify them (e.g. #VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT).
18818 You can find their description in chapter \ref custom_memory_pools.
18819 Allocation strategies are also supported.
18820 See enum #VmaVirtualAllocationCreateFlagBits to learn how to specify them (e.g. #VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT).
18821 
18822 Following features are supported only by the allocator of the real GPU memory and not by virtual allocations:
18823 buffer-image granularity, `VMA_DEBUG_MARGIN`, `VMA_MIN_ALIGNMENT`.
18824 
18825 
18826 \page debugging_memory_usage Debugging incorrect memory usage
18827 
18828 If you suspect a bug with memory usage, like usage of uninitialized memory or
18829 memory being overwritten out of bounds of an allocation,
18830 you can use debug features of this library to verify this.
18831 
18832 \section debugging_memory_usage_initialization Memory initialization
18833 
18834 If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used,
18835 you can enable automatic memory initialization to verify this.
18836 To do it, define macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to 1.
18837 
18838 \code
18839 #define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1
18840 #include "vk_mem_alloc.h"
18841 \endcode
18842 
18843 It makes memory of new allocations initialized to bit pattern `0xDCDCDCDC`.
18844 Before an allocation is destroyed, its memory is filled with bit pattern `0xEFEFEFEF`.
18845 Memory is automatically mapped and unmapped if necessary.
18846 
18847 If you find these values while debugging your program, good chances are that you incorrectly
18848 read Vulkan memory that is allocated but not initialized, or already freed, respectively.
18849 
18850 Memory initialization works only with memory types that are `HOST_VISIBLE` and with allocations that can be mapped.
18851 It works also with dedicated allocations.
18852 
18853 \section debugging_memory_usage_margins Margins
18854 
18855 By default, allocations are laid out in memory blocks next to each other if possible
18856 (considering required alignment, `bufferImageGranularity`, and `nonCoherentAtomSize`).
18857 
18858 ![Allocations without margin](../gfx/Margins_1.png)
18859 
18860 Define macro `VMA_DEBUG_MARGIN` to some non-zero value (e.g. 16) to enforce specified
18861 number of bytes as a margin after every allocation.
18862 
18863 \code
18864 #define VMA_DEBUG_MARGIN 16
18865 #include "vk_mem_alloc.h"
18866 \endcode
18867 
18868 ![Allocations with margin](../gfx/Margins_2.png)
18869 
18870 If your bug goes away after enabling margins, it means it may be caused by memory
18871 being overwritten outside of allocation boundaries. It is not 100% certain though.
18872 Change in application behavior may also be caused by different order and distribution
18873 of allocations across memory blocks after margins are applied.
18874 
18875 Margins work with all types of memory.
18876 
18877 Margin is applied only to allocations made out of memory blocks and not to dedicated
18878 allocations, which have their own memory block of specific size.
18879 It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag
18880 or those automatically decided to put into dedicated allocations, e.g. due to its
18881 large size or recommended by VK_KHR_dedicated_allocation extension.
18882 
18883 Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space.
18884 
18885 Note that enabling margins increases memory usage and fragmentation.
18886 
18887 Margins do not apply to \ref virtual_allocator.
18888 
18889 \section debugging_memory_usage_corruption_detection Corruption detection
18890 
18891 You can additionally define macro `VMA_DEBUG_DETECT_CORRUPTION` to 1 to enable validation
18892 of contents of the margins.
18893 
18894 \code
18895 #define VMA_DEBUG_MARGIN 16
18896 #define VMA_DEBUG_DETECT_CORRUPTION 1
18897 #include "vk_mem_alloc.h"
18898 \endcode
18899 
18900 When this feature is enabled, number of bytes specified as `VMA_DEBUG_MARGIN`
18901 (it must be multiply of 4) after every allocation is filled with a magic number.
18902 This idea is also know as "canary".
18903 Memory is automatically mapped and unmapped if necessary.
18904 
18905 This number is validated automatically when the allocation is destroyed.
18906 If it is not equal to the expected value, `VMA_ASSERT()` is executed.
18907 It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation,
18908 which indicates a serious bug.
18909 
18910 You can also explicitly request checking margins of all allocations in all memory blocks
18911 that belong to specified memory types by using function vmaCheckCorruption(),
18912 or in memory blocks that belong to specified custom pool, by using function
18913 vmaCheckPoolCorruption().
18914 
18915 Margin validation (corruption detection) works only for memory types that are
18916 `HOST_VISIBLE` and `HOST_COHERENT`.
18917 
18918 
18919 \page opengl_interop OpenGL Interop
18920 
18921 VMA provides some features that help with interoperability with OpenGL.
18922 
18923 \section opengl_interop_exporting_memory Exporting memory
18924 
18925 If you want to attach `VkExportMemoryAllocateInfoKHR` structure to `pNext` chain of memory allocations made by the library:
18926 
18927 It is recommended to create \ref custom_memory_pools for such allocations.
18928 Define and fill in your `VkExportMemoryAllocateInfoKHR` structure and attach it to VmaPoolCreateInfo::pMemoryAllocateNext
18929 while creating the custom pool.
18930 Please note that the structure must remain alive and unchanged for the whole lifetime of the #VmaPool,
18931 not only while creating it, as no copy of the structure is made,
18932 but its original pointer is used for each allocation instead.
18933 
18934 If you want to export all memory allocated by the library from certain memory types,
18935 also dedicated allocations or other allocations made from default pools,
18936 an alternative solution is to fill in VmaAllocatorCreateInfo::pTypeExternalMemoryHandleTypes.
18937 It should point to an array with `VkExternalMemoryHandleTypeFlagsKHR` to be automatically passed by the library
18938 through `VkExportMemoryAllocateInfoKHR` on each allocation made from a specific memory type.
18939 Please note that new versions of the library also support dedicated allocations created in custom pools.
18940 
18941 You should not mix these two methods in a way that allows to apply both to the same memory type.
18942 Otherwise, `VkExportMemoryAllocateInfoKHR` structure would be attached twice to the `pNext` chain of `VkMemoryAllocateInfo`.
18943 
18944 
18945 \section opengl_interop_custom_alignment Custom alignment
18946 
18947 Buffers or images exported to a different API like OpenGL may require a different alignment,
18948 higher than the one used by the library automatically, queried from functions like `vkGetBufferMemoryRequirements`.
18949 To impose such alignment:
18950 
18951 It is recommended to create \ref custom_memory_pools for such allocations.
18952 Set VmaPoolCreateInfo::minAllocationAlignment member to the minimum alignment required for each allocation
18953 to be made out of this pool.
18954 The alignment actually used will be the maximum of this member and the alignment returned for the specific buffer or image
18955 from a function like `vkGetBufferMemoryRequirements`, which is called by VMA automatically.
18956 
18957 If you want to create a buffer with a specific minimum alignment out of default pools,
18958 use special function vmaCreateBufferWithAlignment(), which takes additional parameter `minAlignment`.
18959 
18960 Note the problem of alignment affects only resources placed inside bigger `VkDeviceMemory` blocks and not dedicated
18961 allocations, as these, by definition, always have alignment = 0 because the resource is bound to the beginning of its dedicated block.
18962 Contrary to Direct3D 12, Vulkan doesn't have a concept of alignment of the entire memory block passed on its allocation.
18963 
18964 
18965 \page usage_patterns Recommended usage patterns
18966 
18967 Vulkan gives great flexibility in memory allocation.
18968 This chapter shows the most common patterns.
18969 
18970 See also slides from talk:
18971 [Sawicki, Adam. Advanced Graphics Techniques Tutorial: Memory management in Vulkan and DX12. Game Developers Conference, 2018](https://www.gdcvault.com/play/1025458/Advanced-Graphics-Techniques-Tutorial-New)
18972 
18973 
18974 \section usage_patterns_gpu_only GPU-only resource
18975 
18976 <b>When:</b>
18977 Any resources that you frequently write and read on GPU,
18978 e.g. images used as color attachments (aka "render targets"), depth-stencil attachments,
18979 images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)").
18980 
18981 <b>What to do:</b>
18982 Let the library select the optimal memory type, which will likely have `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
18983 
18984 \code
18985 VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
18986 imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
18987 imgCreateInfo.extent.width = 3840;
18988 imgCreateInfo.extent.height = 2160;
18989 imgCreateInfo.extent.depth = 1;
18990 imgCreateInfo.mipLevels = 1;
18991 imgCreateInfo.arrayLayers = 1;
18992 imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
18993 imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
18994 imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
18995 imgCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
18996 imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
18997 
18998 VmaAllocationCreateInfo allocCreateInfo = {};
18999 allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
19000 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
19001 allocCreateInfo.priority = 1.0f;
19002 
19003 VkImage img;
19004 VmaAllocation alloc;
19005 vmaCreateImage(allocator, &imgCreateInfo, &allocCreateInfo, &img, &alloc, nullptr);
19006 \endcode
19007 
19008 <b>Also consider:</b>
19009 Consider creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
19010 especially if they are large or if you plan to destroy and recreate them with different sizes
19011 e.g. when display resolution changes.
19012 Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later.
19013 When VK_EXT_memory_priority extension is enabled, it is also worth setting high priority to such allocation
19014 to decrease chances to be evicted to system memory by the operating system.
19015 
19016 \section usage_patterns_staging_copy_upload Staging copy for upload
19017 
19018 <b>When:</b>
19019 A "staging" buffer than you want to map and fill from CPU code, then use as a source od transfer
19020 to some GPU resource.
19021 
19022 <b>What to do:</b>
19023 Use flag #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT.
19024 Let the library select the optimal memory type, which will always have `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`.
19025 
19026 \code
19027 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
19028 bufCreateInfo.size = 65536;
19029 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
19030 
19031 VmaAllocationCreateInfo allocCreateInfo = {};
19032 allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
19033 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
19034     VMA_ALLOCATION_CREATE_MAPPED_BIT;
19035 
19036 VkBuffer buf;
19037 VmaAllocation alloc;
19038 VmaAllocationInfo allocInfo;
19039 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
19040 
19041 ...
19042 
19043 memcpy(allocInfo.pMappedData, myData, myDataSize);
19044 \endcode
19045 
19046 <b>Also consider:</b>
19047 You can map the allocation using vmaMapMemory() or you can create it as persistenly mapped
19048 using #VMA_ALLOCATION_CREATE_MAPPED_BIT, as in the example above.
19049 
19050 
19051 \section usage_patterns_readback Readback
19052 
19053 <b>When:</b>
19054 Buffers for data written by or transferred from the GPU that you want to read back on the CPU,
19055 e.g. results of some computations.
19056 
19057 <b>What to do:</b>
19058 Use flag #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.
19059 Let the library select the optimal memory type, which will always have `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`
19060 and `VK_MEMORY_PROPERTY_HOST_CACHED_BIT`.
19061 
19062 \code
19063 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
19064 bufCreateInfo.size = 65536;
19065 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
19066 
19067 VmaAllocationCreateInfo allocCreateInfo = {};
19068 allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
19069 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT |
19070     VMA_ALLOCATION_CREATE_MAPPED_BIT;
19071 
19072 VkBuffer buf;
19073 VmaAllocation alloc;
19074 VmaAllocationInfo allocInfo;
19075 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
19076 
19077 ...
19078 
19079 const float* downloadedData = (const float*)allocInfo.pMappedData;
19080 \endcode
19081 
19082 
19083 \section usage_patterns_advanced_data_uploading Advanced data uploading
19084 
19085 For resources that you frequently write on CPU via mapped pointer and
19086 freqnently read on GPU e.g. as a uniform buffer (also called "dynamic"), multiple options are possible:
19087 
19088 -# Easiest solution is to have one copy of the resource in `HOST_VISIBLE` memory,
19089    even if it means system RAM (not `DEVICE_LOCAL`) on systems with a discrete graphics card,
19090    and make the device reach out to that resource directly.
19091    - Reads performed by the device will then go through PCI Express bus.
19092      The performace of this access may be limited, but it may be fine depending on the size
19093      of this resource (whether it is small enough to quickly end up in GPU cache) and the sparsity
19094      of access.
19095 -# On systems with unified memory (e.g. AMD APU or Intel integrated graphics, mobile chips),
19096    a memory type may be available that is both `HOST_VISIBLE` (available for mapping) and `DEVICE_LOCAL`
19097    (fast to access from the GPU). Then, it is likely the best choice for such type of resource.
19098 -# Systems with a discrete graphics card and separate video memory may or may not expose
19099    a memory type that is both `HOST_VISIBLE` and `DEVICE_LOCAL`, also known as Base Address Register (BAR).
19100    If they do, it represents a piece of VRAM (or entire VRAM, if ReBAR is enabled in the motherboard BIOS)
19101    that is available to CPU for mapping.
19102    - Writes performed by the host to that memory go through PCI Express bus.
19103      The performance of these writes may be limited, but it may be fine, especially on PCIe 4.0,
19104      as long as rules of using uncached and write-combined memory are followed - only sequential writes and no reads.
19105 -# Finally, you may need or prefer to create a separate copy of the resource in `DEVICE_LOCAL` memory,
19106    a separate "staging" copy in `HOST_VISIBLE` memory and perform an explicit transfer command between them.
19107 
19108 Thankfully, VMA offers an aid to create and use such resources in the the way optimal
19109 for the current Vulkan device. To help the library make the best choice,
19110 use flag #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT together with
19111 #VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT.
19112 It will then prefer a memory type that is both `DEVICE_LOCAL` and `HOST_VISIBLE` (integrated memory or BAR),
19113 but if no such memory type is available or allocation from it fails
19114 (PC graphics cards have only 256 MB of BAR by default, unless ReBAR is supported and enabled in BIOS),
19115 it will fall back to `DEVICE_LOCAL` memory for fast GPU access.
19116 It is then up to you to detect that the allocation ended up in a memory type that is not `HOST_VISIBLE`,
19117 so you need to create another "staging" allocation and perform explicit transfers.
19118 
19119 \code
19120 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
19121 bufCreateInfo.size = 65536;
19122 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
19123 
19124 VmaAllocationCreateInfo allocCreateInfo = {};
19125 allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
19126 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
19127     VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT |
19128     VMA_ALLOCATION_CREATE_MAPPED_BIT;
19129 
19130 VkBuffer buf;
19131 VmaAllocation alloc;
19132 VmaAllocationInfo allocInfo;
19133 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
19134 
19135 VkMemoryPropertyFlags memPropFlags;
19136 vmaGetAllocationMemoryProperties(allocator, alloc, &memPropFlags);
19137 
19138 if(memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
19139 {
19140     // Allocation ended up in a mappable memory and is already mapped - write to it directly.
19141 
19142     // [Executed in runtime]:
19143     memcpy(allocInfo.pMappedData, myData, myDataSize);
19144 }
19145 else
19146 {
19147     // Allocation ended up in a non-mappable memory - need to transfer.
19148     VkBufferCreateInfo stagingBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
19149     stagingBufCreateInfo.size = 65536;
19150     stagingBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
19151 
19152     VmaAllocationCreateInfo stagingAllocCreateInfo = {};
19153     stagingAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
19154     stagingAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
19155         VMA_ALLOCATION_CREATE_MAPPED_BIT;
19156 
19157     VkBuffer stagingBuf;
19158     VmaAllocation stagingAlloc;
19159     VmaAllocationInfo stagingAllocInfo;
19160     vmaCreateBuffer(allocator, &stagingBufCreateInfo, &stagingAllocCreateInfo,
19161         &stagingBuf, &stagingAlloc, stagingAllocInfo);
19162 
19163     // [Executed in runtime]:
19164     memcpy(stagingAllocInfo.pMappedData, myData, myDataSize);
19165     //vkCmdPipelineBarrier: VK_ACCESS_HOST_WRITE_BIT --> VK_ACCESS_TRANSFER_READ_BIT
19166     VkBufferCopy bufCopy = {
19167         0, // srcOffset
19168         0, // dstOffset,
19169         myDataSize); // size
19170     vkCmdCopyBuffer(cmdBuf, stagingBuf, buf, 1, &bufCopy);
19171 }
19172 \endcode
19173 
19174 \section usage_patterns_other_use_cases Other use cases
19175 
19176 Here are some other, less obvious use cases and their recommended settings:
19177 
19178 - An image that is used only as transfer source and destination, but it should stay on the device,
19179   as it is used to temporarily store a copy of some texture, e.g. from the current to the next frame,
19180   for temporal antialiasing or other temporal effects.
19181   - Use `VkImageCreateInfo::usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT`
19182   - Use VmaAllocationCreateInfo::usage = #VMA_MEMORY_USAGE_AUTO
19183 - An image that is used only as transfer source and destination, but it should be placed
19184   in the system RAM despite it doesn't need to be mapped, because it serves as a "swap" copy to evict
19185   least recently used textures from VRAM.
19186   - Use `VkImageCreateInfo::usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT`
19187   - Use VmaAllocationCreateInfo::usage = #VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
19188     as VMA needs a hint here to differentiate from the previous case.
19189 - A buffer that you want to map and write from the CPU, directly read from the GPU
19190   (e.g. as a uniform or vertex buffer), but you have a clear preference to place it in device or
19191   host memory due to its large size.
19192   - Use `VkBufferCreateInfo::usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT`
19193   - Use VmaAllocationCreateInfo::usage = #VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE or #VMA_MEMORY_USAGE_AUTO_PREFER_HOST
19194   - Use VmaAllocationCreateInfo::flags = #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT
19195 
19196 
19197 \page configuration Configuration
19198 
19199 Please check "CONFIGURATION SECTION" in the code to find macros that you can define
19200 before each include of this file or change directly in this file to provide
19201 your own implementation of basic facilities like assert, `min()` and `max()` functions,
19202 mutex, atomic etc.
19203 The library uses its own implementation of containers by default, but you can switch to using
19204 STL containers instead.
19205 
19206 For example, define `VMA_ASSERT(expr)` before including the library to provide
19207 custom implementation of the assertion, compatible with your project.
19208 By default it is defined to standard C `assert(expr)` in `_DEBUG` configuration
19209 and empty otherwise.
19210 
19211 \section config_Vulkan_functions Pointers to Vulkan functions
19212 
19213 There are multiple ways to import pointers to Vulkan functions in the library.
19214 In the simplest case you don't need to do anything.
19215 If the compilation or linking of your program or the initialization of the #VmaAllocator
19216 doesn't work for you, you can try to reconfigure it.
19217 
19218 First, the allocator tries to fetch pointers to Vulkan functions linked statically,
19219 like this:
19220 
19221 \code
19222 m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
19223 \endcode
19224 
19225 If you want to disable this feature, set configuration macro: `#define VMA_STATIC_VULKAN_FUNCTIONS 0`.
19226 
19227 Second, you can provide the pointers yourself by setting member VmaAllocatorCreateInfo::pVulkanFunctions.
19228 You can fetch them e.g. using functions `vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` or
19229 by using a helper library like [volk](https://github.com/zeux/volk).
19230 
19231 Third, VMA tries to fetch remaining pointers that are still null by calling
19232 `vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` on its own.
19233 You need to only fill in VmaVulkanFunctions::vkGetInstanceProcAddr and VmaVulkanFunctions::vkGetDeviceProcAddr.
19234 Other pointers will be fetched automatically.
19235 If you want to disable this feature, set configuration macro: `#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0`.
19236 
19237 Finally, all the function pointers required by the library (considering selected
19238 Vulkan version and enabled extensions) are checked with `VMA_ASSERT` if they are not null.
19239 
19240 
19241 \section custom_memory_allocator Custom host memory allocator
19242 
19243 If you use custom allocator for CPU memory rather than default operator `new`
19244 and `delete` from C++, you can make this library using your allocator as well
19245 by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These
19246 functions will be passed to Vulkan, as well as used by the library itself to
19247 make any CPU-side allocations.
19248 
19249 \section allocation_callbacks Device memory allocation callbacks
19250 
19251 The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally.
19252 You can setup callbacks to be informed about these calls, e.g. for the purpose
19253 of gathering some statistics. To do it, fill optional member
19254 VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
19255 
19256 \section heap_memory_limit Device heap memory limit
19257 
19258 When device memory of certain heap runs out of free space, new allocations may
19259 fail (returning error code) or they may succeed, silently pushing some existing_
19260 memory blocks from GPU VRAM to system RAM (which degrades performance). This
19261 behavior is implementation-dependent - it depends on GPU vendor and graphics
19262 driver.
19263 
19264 On AMD cards it can be controlled while creating Vulkan device object by using
19265 VK_AMD_memory_overallocation_behavior extension, if available.
19266 
19267 Alternatively, if you want to test how your program behaves with limited amount of Vulkan device
19268 memory available without switching your graphics card to one that really has
19269 smaller VRAM, you can use a feature of this library intended for this purpose.
19270 To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit.
19271 
19272 
19273 
19274 \page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation
19275 
19276 VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve
19277 performance on some GPUs. It augments Vulkan API with possibility to query
19278 driver whether it prefers particular buffer or image to have its own, dedicated
19279 allocation (separate `VkDeviceMemory` block) for better efficiency - to be able
19280 to do some internal optimizations. The extension is supported by this library.
19281 It will be used automatically when enabled.
19282 
19283 It has been promoted to core Vulkan 1.1, so if you use eligible Vulkan version
19284 and inform VMA about it by setting VmaAllocatorCreateInfo::vulkanApiVersion,
19285 you are all set.
19286 
19287 Otherwise, if you want to use it as an extension:
19288 
19289 1 . When creating Vulkan device, check if following 2 device extensions are
19290 supported (call `vkEnumerateDeviceExtensionProperties()`).
19291 If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`).
19292 
19293 - VK_KHR_get_memory_requirements2
19294 - VK_KHR_dedicated_allocation
19295 
19296 If you enabled these extensions:
19297 
19298 2 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating
19299 your #VmaAllocator to inform the library that you enabled required extensions
19300 and you want the library to use them.
19301 
19302 \code
19303 allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
19304 
19305 vmaCreateAllocator(&allocatorInfo, &allocator);
19306 \endcode
19307 
19308 That is all. The extension will be automatically used whenever you create a
19309 buffer using vmaCreateBuffer() or image using vmaCreateImage().
19310 
19311 When using the extension together with Vulkan Validation Layer, you will receive
19312 warnings like this:
19313 
19314 _vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer._
19315 
19316 It is OK, you should just ignore it. It happens because you use function
19317 `vkGetBufferMemoryRequirements2KHR()` instead of standard
19318 `vkGetBufferMemoryRequirements()`, while the validation layer seems to be
19319 unaware of it.
19320 
19321 To learn more about this extension, see:
19322 
19323 - [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap50.html#VK_KHR_dedicated_allocation)
19324 - [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5)
19325 
19326 
19327 
19328 \page vk_ext_memory_priority VK_EXT_memory_priority
19329 
19330 VK_EXT_memory_priority is a device extension that allows to pass additional "priority"
19331 value to Vulkan memory allocations that the implementation may use prefer certain
19332 buffers and images that are critical for performance to stay in device-local memory
19333 in cases when the memory is over-subscribed, while some others may be moved to the system memory.
19334 
19335 VMA offers convenient usage of this extension.
19336 If you enable it, you can pass "priority" parameter when creating allocations or custom pools
19337 and the library automatically passes the value to Vulkan using this extension.
19338 
19339 If you want to use this extension in connection with VMA, follow these steps:
19340 
19341 \section vk_ext_memory_priority_initialization Initialization
19342 
19343 1) Call `vkEnumerateDeviceExtensionProperties` for the physical device.
19344 Check if the extension is supported - if returned array of `VkExtensionProperties` contains "VK_EXT_memory_priority".
19345 
19346 2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`.
19347 Attach additional structure `VkPhysicalDeviceMemoryPriorityFeaturesEXT` to `VkPhysicalDeviceFeatures2::pNext` to be returned.
19348 Check if the device feature is really supported - check if `VkPhysicalDeviceMemoryPriorityFeaturesEXT::memoryPriority` is true.
19349 
19350 3) While creating device with `vkCreateDevice`, enable this extension - add "VK_EXT_memory_priority"
19351 to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`.
19352 
19353 4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`.
19354 Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`.
19355 Enable this device feature - attach additional structure `VkPhysicalDeviceMemoryPriorityFeaturesEXT` to
19356 `VkPhysicalDeviceFeatures2::pNext` chain and set its member `memoryPriority` to `VK_TRUE`.
19357 
19358 5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you
19359 have enabled this extension and feature - add #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT
19360 to VmaAllocatorCreateInfo::flags.
19361 
19362 \section vk_ext_memory_priority_usage Usage
19363 
19364 When using this extension, you should initialize following member:
19365 
19366 - VmaAllocationCreateInfo::priority when creating a dedicated allocation with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
19367 - VmaPoolCreateInfo::priority when creating a custom pool.
19368 
19369 It should be a floating-point value between `0.0f` and `1.0f`, where recommended default is `0.5f`.
19370 Memory allocated with higher value can be treated by the Vulkan implementation as higher priority
19371 and so it can have lower chances of being pushed out to system memory, experiencing degraded performance.
19372 
19373 It might be a good idea to create performance-critical resources like color-attachment or depth-stencil images
19374 as dedicated and set high priority to them. For example:
19375 
19376 \code
19377 VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
19378 imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
19379 imgCreateInfo.extent.width = 3840;
19380 imgCreateInfo.extent.height = 2160;
19381 imgCreateInfo.extent.depth = 1;
19382 imgCreateInfo.mipLevels = 1;
19383 imgCreateInfo.arrayLayers = 1;
19384 imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
19385 imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
19386 imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
19387 imgCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
19388 imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
19389 
19390 VmaAllocationCreateInfo allocCreateInfo = {};
19391 allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
19392 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
19393 allocCreateInfo.priority = 1.0f;
19394 
19395 VkImage img;
19396 VmaAllocation alloc;
19397 vmaCreateImage(allocator, &imgCreateInfo, &allocCreateInfo, &img, &alloc, nullptr);
19398 \endcode
19399 
19400 `priority` member is ignored in the following situations:
19401 
19402 - Allocations created in custom pools: They inherit the priority, along with all other allocation parameters
19403   from the parametrs passed in #VmaPoolCreateInfo when the pool was created.
19404 - Allocations created in default pools: They inherit the priority from the parameters
19405   VMA used when creating default pools, which means `priority == 0.5f`.
19406 
19407 
19408 \page vk_amd_device_coherent_memory VK_AMD_device_coherent_memory
19409 
19410 VK_AMD_device_coherent_memory is a device extension that enables access to
19411 additional memory types with `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and
19412 `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flag. It is useful mostly for
19413 allocation of buffers intended for writing "breadcrumb markers" in between passes
19414 or draw calls, which in turn are useful for debugging GPU crash/hang/TDR cases.
19415 
19416 When the extension is available but has not been enabled, Vulkan physical device
19417 still exposes those memory types, but their usage is forbidden. VMA automatically
19418 takes care of that - it returns `VK_ERROR_FEATURE_NOT_PRESENT` when an attempt
19419 to allocate memory of such type is made.
19420 
19421 If you want to use this extension in connection with VMA, follow these steps:
19422 
19423 \section vk_amd_device_coherent_memory_initialization Initialization
19424 
19425 1) Call `vkEnumerateDeviceExtensionProperties` for the physical device.
19426 Check if the extension is supported - if returned array of `VkExtensionProperties` contains "VK_AMD_device_coherent_memory".
19427 
19428 2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`.
19429 Attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to `VkPhysicalDeviceFeatures2::pNext` to be returned.
19430 Check if the device feature is really supported - check if `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true.
19431 
19432 3) While creating device with `vkCreateDevice`, enable this extension - add "VK_AMD_device_coherent_memory"
19433 to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`.
19434 
19435 4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`.
19436 Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`.
19437 Enable this device feature - attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to
19438 `VkPhysicalDeviceFeatures2::pNext` and set its member `deviceCoherentMemory` to `VK_TRUE`.
19439 
19440 5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you
19441 have enabled this extension and feature - add #VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT
19442 to VmaAllocatorCreateInfo::flags.
19443 
19444 \section vk_amd_device_coherent_memory_usage Usage
19445 
19446 After following steps described above, you can create VMA allocations and custom pools
19447 out of the special `DEVICE_COHERENT` and `DEVICE_UNCACHED` memory types on eligible
19448 devices. There are multiple ways to do it, for example:
19449 
19450 - You can request or prefer to allocate out of such memory types by adding
19451   `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` to VmaAllocationCreateInfo::requiredFlags
19452   or VmaAllocationCreateInfo::preferredFlags. Those flags can be freely mixed with
19453   other ways of \ref choosing_memory_type, like setting VmaAllocationCreateInfo::usage.
19454 - If you manually found memory type index to use for this purpose, force allocation
19455   from this specific index by setting VmaAllocationCreateInfo::memoryTypeBits `= 1u << index`.
19456 
19457 \section vk_amd_device_coherent_memory_more_information More information
19458 
19459 To learn more about this extension, see [VK_AMD_device_coherent_memory in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_AMD_device_coherent_memory.html)
19460 
19461 Example use of this extension can be found in the code of the sample and test suite
19462 accompanying this library.
19463 
19464 
19465 \page enabling_buffer_device_address Enabling buffer device address
19466 
19467 Device extension VK_KHR_buffer_device_address
19468 allow to fetch raw GPU pointer to a buffer and pass it for usage in a shader code.
19469 It has been promoted to core Vulkan 1.2.
19470 
19471 If you want to use this feature in connection with VMA, follow these steps:
19472 
19473 \section enabling_buffer_device_address_initialization Initialization
19474 
19475 1) (For Vulkan version < 1.2) Call `vkEnumerateDeviceExtensionProperties` for the physical device.
19476 Check if the extension is supported - if returned array of `VkExtensionProperties` contains
19477 "VK_KHR_buffer_device_address".
19478 
19479 2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`.
19480 Attach additional structure `VkPhysicalDeviceBufferDeviceAddressFeatures*` to `VkPhysicalDeviceFeatures2::pNext` to be returned.
19481 Check if the device feature is really supported - check if `VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress` is true.
19482 
19483 3) (For Vulkan version < 1.2) While creating device with `vkCreateDevice`, enable this extension - add
19484 "VK_KHR_buffer_device_address" to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`.
19485 
19486 4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`.
19487 Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`.
19488 Enable this device feature - attach additional structure `VkPhysicalDeviceBufferDeviceAddressFeatures*` to
19489 `VkPhysicalDeviceFeatures2::pNext` and set its member `bufferDeviceAddress` to `VK_TRUE`.
19490 
19491 5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you
19492 have enabled this feature - add #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT
19493 to VmaAllocatorCreateInfo::flags.
19494 
19495 \section enabling_buffer_device_address_usage Usage
19496 
19497 After following steps described above, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*` using VMA.
19498 The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT*` to
19499 allocated memory blocks wherever it might be needed.
19500 
19501 Please note that the library supports only `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*`.
19502 The second part of this functionality related to "capture and replay" is not supported,
19503 as it is intended for usage in debugging tools like RenderDoc, not in everyday Vulkan usage.
19504 
19505 \section enabling_buffer_device_address_more_information More information
19506 
19507 To learn more about this extension, see [VK_KHR_buffer_device_address in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap46.html#VK_KHR_buffer_device_address)
19508 
19509 Example use of this extension can be found in the code of the sample and test suite
19510 accompanying this library.
19511 
19512 \page general_considerations General considerations
19513 
19514 \section general_considerations_thread_safety Thread safety
19515 
19516 - The library has no global state, so separate #VmaAllocator objects can be used
19517   independently.
19518   There should be no need to create multiple such objects though - one per `VkDevice` is enough.
19519 - By default, all calls to functions that take #VmaAllocator as first parameter
19520   are safe to call from multiple threads simultaneously because they are
19521   synchronized internally when needed.
19522   This includes allocation and deallocation from default memory pool, as well as custom #VmaPool.
19523 - When the allocator is created with #VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT
19524   flag, calls to functions that take such #VmaAllocator object must be
19525   synchronized externally.
19526 - Access to a #VmaAllocation object must be externally synchronized. For example,
19527   you must not call vmaGetAllocationInfo() and vmaMapMemory() from different
19528   threads at the same time if you pass the same #VmaAllocation object to these
19529   functions.
19530 - #VmaVirtualBlock is not safe to be used from multiple threads simultaneously.
19531 
19532 \section general_considerations_versioning_and_compatibility Versioning and compatibility
19533 
19534 The library uses [**Semantic Versioning**](https://semver.org/),
19535 which means version numbers follow convention: Major.Minor.Patch (e.g. 2.3.0), where:
19536 
19537 - Incremented Patch version means a release is backward- and forward-compatible,
19538   introducing only some internal improvements, bug fixes, optimizations etc.
19539   or changes that are out of scope of the official API described in this documentation.
19540 - Incremented Minor version means a release is backward-compatible,
19541   so existing code that uses the library should continue to work, while some new
19542   symbols could have been added: new structures, functions, new values in existing
19543   enums and bit flags, new structure members, but not new function parameters.
19544 - Incrementing Major version means a release could break some backward compatibility.
19545 
19546 All changes between official releases are documented in file "CHANGELOG.md".
19547 
19548 \warning Backward compatiblity is considered on the level of C++ source code, not binary linkage.
19549 Adding new members to existing structures is treated as backward compatible if initializing
19550 the new members to binary zero results in the old behavior.
19551 You should always fully initialize all library structures to zeros and not rely on their
19552 exact binary size.
19553 
19554 \section general_considerations_validation_layer_warnings Validation layer warnings
19555 
19556 When using this library, you can meet following types of warnings issued by
19557 Vulkan validation layer. They don't necessarily indicate a bug, so you may need
19558 to just ignore them.
19559 
19560 - *vkBindBufferMemory(): Binding memory to buffer 0xeb8e4 but vkGetBufferMemoryRequirements() has not been called on that buffer.*
19561   - It happens when VK_KHR_dedicated_allocation extension is enabled.
19562     `vkGetBufferMemoryRequirements2KHR` function is used instead, while validation layer seems to be unaware of it.
19563 - *Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.*
19564   - It happens when you map a buffer or image, because the library maps entire
19565     `VkDeviceMemory` block, where different types of images and buffers may end
19566     up together, especially on GPUs with unified memory like Intel.
19567 - *Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug.*
19568   - It may happen when you use [defragmentation](@ref defragmentation).
19569 
19570 \section general_considerations_allocation_algorithm Allocation algorithm
19571 
19572 The library uses following algorithm for allocation, in order:
19573 
19574 -# Try to find free range of memory in existing blocks.
19575 -# If failed, try to create a new block of `VkDeviceMemory`, with preferred block size.
19576 -# If failed, try to create such block with size / 2, size / 4, size / 8.
19577 -# If failed, try to allocate separate `VkDeviceMemory` for this allocation,
19578    just like when you use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
19579 -# If failed, choose other memory type that meets the requirements specified in
19580    VmaAllocationCreateInfo and go to point 1.
19581 -# If failed, return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
19582 
19583 \section general_considerations_features_not_supported Features not supported
19584 
19585 Features deliberately excluded from the scope of this library:
19586 
19587 -# **Data transfer.** Uploading (streaming) and downloading data of buffers and images
19588    between CPU and GPU memory and related synchronization is responsibility of the user.
19589    Defining some "texture" object that would automatically stream its data from a
19590    staging copy in CPU memory to GPU memory would rather be a feature of another,
19591    higher-level library implemented on top of VMA.
19592    VMA doesn't record any commands to a `VkCommandBuffer`. It just allocates memory.
19593 -# **Recreation of buffers and images.** Although the library has functions for
19594    buffer and image creation: vmaCreateBuffer(), vmaCreateImage(), you need to
19595    recreate these objects yourself after defragmentation. That is because the big
19596    structures `VkBufferCreateInfo`, `VkImageCreateInfo` are not stored in
19597    #VmaAllocation object.
19598 -# **Handling CPU memory allocation failures.** When dynamically creating small C++
19599    objects in CPU memory (not Vulkan memory), allocation failures are not checked
19600    and handled gracefully, because that would complicate code significantly and
19601    is usually not needed in desktop PC applications anyway.
19602    Success of an allocation is just checked with an assert.
19603 -# **Code free of any compiler warnings.** Maintaining the library to compile and
19604    work correctly on so many different platforms is hard enough. Being free of
19605    any warnings, on any version of any compiler, is simply not feasible.
19606    There are many preprocessor macros that make some variables unused, function parameters unreferenced,
19607    or conditional expressions constant in some configurations.
19608    The code of this library should not be bigger or more complicated just to silence these warnings.
19609    It is recommended to disable such warnings instead.
19610 -# This is a C++ library with C interface. **Bindings or ports to any other programming languages** are welcome as external projects but
19611    are not going to be included into this repository.
19612 */
19613