• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (c) 2017-2021 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.0-development</b> (2021-06-21)
29 
30 Copyright (c) 2017-2021 Advanced Micro Devices, Inc. All rights reserved. \n
31 License: MIT
32 
33 Documentation of all members: vk_mem_alloc.h
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     - [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable)
53   - \subpage staying_within_budget
54     - [Querying for budget](@ref staying_within_budget_querying_for_budget)
55     - [Controlling memory usage](@ref staying_within_budget_controlling_memory_usage)
56   - \subpage resource_aliasing
57   - \subpage custom_memory_pools
58     - [Choosing memory type index](@ref custom_memory_pools_MemTypeIndex)
59     - [Linear allocation algorithm](@ref linear_algorithm)
60       - [Free-at-once](@ref linear_algorithm_free_at_once)
61       - [Stack](@ref linear_algorithm_stack)
62       - [Double stack](@ref linear_algorithm_double_stack)
63       - [Ring buffer](@ref linear_algorithm_ring_buffer)
64     - [Buddy allocation algorithm](@ref buddy_algorithm)
65   - \subpage defragmentation
66       - [Defragmenting CPU memory](@ref defragmentation_cpu)
67       - [Defragmenting GPU memory](@ref defragmentation_gpu)
68       - [Additional notes](@ref defragmentation_additional_notes)
69       - [Writing custom allocation algorithm](@ref defragmentation_custom_algorithm)
70   - \subpage lost_allocations
71   - \subpage statistics
72     - [Numeric statistics](@ref statistics_numeric_statistics)
73     - [JSON dump](@ref statistics_json_dump)
74   - \subpage allocation_annotation
75     - [Allocation user data](@ref allocation_user_data)
76     - [Allocation names](@ref allocation_names)
77   - \subpage virtual_allocator
78   - \subpage debugging_memory_usage
79     - [Memory initialization](@ref debugging_memory_usage_initialization)
80     - [Margins](@ref debugging_memory_usage_margins)
81     - [Corruption detection](@ref debugging_memory_usage_corruption_detection)
82   - \subpage record_and_replay
83   - \subpage opengl_interop
84 - \subpage usage_patterns
85   - [Common mistakes](@ref usage_patterns_common_mistakes)
86   - [Simple patterns](@ref usage_patterns_simple)
87   - [Advanced patterns](@ref usage_patterns_advanced)
88 - \subpage configuration
89   - [Pointers to Vulkan functions](@ref config_Vulkan_functions)
90   - [Custom host memory allocator](@ref custom_memory_allocator)
91   - [Device memory allocation callbacks](@ref allocation_callbacks)
92   - [Device heap memory limit](@ref heap_memory_limit)
93   - \subpage vk_khr_dedicated_allocation
94   - \subpage enabling_buffer_device_address
95   - \subpage vk_amd_device_coherent_memory
96 - \subpage general_considerations
97   - [Thread safety](@ref general_considerations_thread_safety)
98   - [Validation layer warnings](@ref general_considerations_validation_layer_warnings)
99   - [Allocation algorithm](@ref general_considerations_allocation_algorithm)
100   - [Features not supported](@ref general_considerations_features_not_supported)
101 
102 \section main_see_also See also
103 
104 - [Product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-memory-allocator/)
105 - [Source repository on GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)
106 */
107 
108 #ifdef __cplusplus
109 extern "C" {
110 #endif
111 
112 /*
113 Define this macro to 0/1 to disable/enable support for recording functionality,
114 available through VmaAllocatorCreateInfo::pRecordSettings.
115 */
116 #ifndef VMA_RECORDING_ENABLED
117     #define VMA_RECORDING_ENABLED 0
118 #endif
119 
120 #if defined(__ANDROID__) && defined(VK_NO_PROTOTYPES) && VMA_STATIC_VULKAN_FUNCTIONS
121     extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
122     extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
123     extern PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
124     extern PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
125     extern PFN_vkAllocateMemory vkAllocateMemory;
126     extern PFN_vkFreeMemory vkFreeMemory;
127     extern PFN_vkMapMemory vkMapMemory;
128     extern PFN_vkUnmapMemory vkUnmapMemory;
129     extern PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
130     extern PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
131     extern PFN_vkBindBufferMemory vkBindBufferMemory;
132     extern PFN_vkBindImageMemory vkBindImageMemory;
133     extern PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
134     extern PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
135     extern PFN_vkCreateBuffer vkCreateBuffer;
136     extern PFN_vkDestroyBuffer vkDestroyBuffer;
137     extern PFN_vkCreateImage vkCreateImage;
138     extern PFN_vkDestroyImage vkDestroyImage;
139     extern PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
140     #if VMA_VULKAN_VERSION >= 1001000
141         extern PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2;
142         extern PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2;
143         extern PFN_vkBindBufferMemory2 vkBindBufferMemory2;
144         extern PFN_vkBindImageMemory2 vkBindImageMemory2;
145         extern PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2;
146     #endif // #if VMA_VULKAN_VERSION >= 1001000
147 #endif // #if defined(__ANDROID__) && VMA_STATIC_VULKAN_FUNCTIONS && VK_NO_PROTOTYPES
148 
149 #ifndef VULKAN_H_
150     #include <vulkan/vulkan.h>
151 #endif
152 
153 // Define this macro to declare maximum supported Vulkan version in format AAABBBCCC,
154 // where AAA = major, BBB = minor, CCC = patch.
155 // If you want to use version > 1.0, it still needs to be enabled via VmaAllocatorCreateInfo::vulkanApiVersion.
156 #if !defined(VMA_VULKAN_VERSION)
157     #if defined(VK_VERSION_1_2)
158         #define VMA_VULKAN_VERSION 1002000
159     #elif defined(VK_VERSION_1_1)
160         #define VMA_VULKAN_VERSION 1001000
161     #else
162         #define VMA_VULKAN_VERSION 1000000
163     #endif
164 #endif
165 
166 #if !defined(VMA_DEDICATED_ALLOCATION)
167     #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation
168         #define VMA_DEDICATED_ALLOCATION 1
169     #else
170         #define VMA_DEDICATED_ALLOCATION 0
171     #endif
172 #endif
173 
174 #if !defined(VMA_BIND_MEMORY2)
175     #if VK_KHR_bind_memory2
176         #define VMA_BIND_MEMORY2 1
177     #else
178         #define VMA_BIND_MEMORY2 0
179     #endif
180 #endif
181 
182 #if !defined(VMA_MEMORY_BUDGET)
183     #if VK_EXT_memory_budget && (VK_KHR_get_physical_device_properties2 || VMA_VULKAN_VERSION >= 1001000)
184         #define VMA_MEMORY_BUDGET 1
185     #else
186         #define VMA_MEMORY_BUDGET 0
187     #endif
188 #endif
189 
190 // Defined to 1 when VK_KHR_buffer_device_address device extension or equivalent core Vulkan 1.2 feature is defined in its headers.
191 #if !defined(VMA_BUFFER_DEVICE_ADDRESS)
192     #if VK_KHR_buffer_device_address || VMA_VULKAN_VERSION >= 1002000
193         #define VMA_BUFFER_DEVICE_ADDRESS 1
194     #else
195         #define VMA_BUFFER_DEVICE_ADDRESS 0
196     #endif
197 #endif
198 
199 // Defined to 1 when VK_EXT_memory_priority device extension is defined in Vulkan headers.
200 #if !defined(VMA_MEMORY_PRIORITY)
201     #if VK_EXT_memory_priority
202         #define VMA_MEMORY_PRIORITY 1
203     #else
204         #define VMA_MEMORY_PRIORITY 0
205     #endif
206 #endif
207 
208 // Defined to 1 when VK_KHR_external_memory device extension is defined in Vulkan headers.
209 #if !defined(VMA_EXTERNAL_MEMORY)
210     #if VK_KHR_external_memory
211         #define VMA_EXTERNAL_MEMORY 1
212     #else
213         #define VMA_EXTERNAL_MEMORY 0
214     #endif
215 #endif
216 
217 // Define these macros to decorate all public functions with additional code,
218 // before and after returned type, appropriately. This may be useful for
219 // exporting the functions when compiling VMA as a separate library. Example:
220 // #define VMA_CALL_PRE  __declspec(dllexport)
221 // #define VMA_CALL_POST __cdecl
222 #ifndef VMA_CALL_PRE
223     #define VMA_CALL_PRE
224 #endif
225 #ifndef VMA_CALL_POST
226     #define VMA_CALL_POST
227 #endif
228 
229 // Define this macro to decorate pointers with an attribute specifying the
230 // length of the array they point to if they are not null.
231 //
232 // The length may be one of
233 // - The name of another parameter in the argument list where the pointer is declared
234 // - The name of another member in the struct where the pointer is declared
235 // - The name of a member of a struct type, meaning the value of that member in
236 //   the context of the call. For example
237 //   VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount"),
238 //   this means the number of memory heaps available in the device associated
239 //   with the VmaAllocator being dealt with.
240 #ifndef VMA_LEN_IF_NOT_NULL
241     #define VMA_LEN_IF_NOT_NULL(len)
242 #endif
243 
244 // The VMA_NULLABLE macro is defined to be _Nullable when compiling with Clang.
245 // see: https://clang.llvm.org/docs/AttributeReference.html#nullable
246 #ifndef VMA_NULLABLE
247     #ifdef __clang__
248         #define VMA_NULLABLE _Nullable
249     #else
250         #define VMA_NULLABLE
251     #endif
252 #endif
253 
254 // The VMA_NOT_NULL macro is defined to be _Nonnull when compiling with Clang.
255 // see: https://clang.llvm.org/docs/AttributeReference.html#nonnull
256 #ifndef VMA_NOT_NULL
257     #ifdef __clang__
258         #define VMA_NOT_NULL _Nonnull
259     #else
260         #define VMA_NOT_NULL
261     #endif
262 #endif
263 
264 // If non-dispatchable handles are represented as pointers then we can give
265 // then nullability annotations
266 #ifndef VMA_NOT_NULL_NON_DISPATCHABLE
267     #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
268         #define VMA_NOT_NULL_NON_DISPATCHABLE VMA_NOT_NULL
269     #else
270         #define VMA_NOT_NULL_NON_DISPATCHABLE
271     #endif
272 #endif
273 
274 #ifndef VMA_NULLABLE_NON_DISPATCHABLE
275     #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
276         #define VMA_NULLABLE_NON_DISPATCHABLE VMA_NULLABLE
277     #else
278         #define VMA_NULLABLE_NON_DISPATCHABLE
279     #endif
280 #endif
281 
282 // Used to silence warnings for implicit fallthrough.
283 #if __has_cpp_attribute(clang::fallthrough)
284 #define VMA_FALLTHROUGH [[clang::fallthrough]];
285 #else
286 #define VMA_FALLTHROUGH
287 #endif
288 
289 /** \struct VmaAllocator
290 \brief Represents main object of this library initialized.
291 
292 Fill structure #VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it.
293 Call function vmaDestroyAllocator() to destroy it.
294 
295 It is recommended to create just one object of this type per `VkDevice` object,
296 right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed.
297 */
298 VK_DEFINE_HANDLE(VmaAllocator)
299 
300 /// Callback function called after successful vkAllocateMemory.
301 typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)(
302     VmaAllocator VMA_NOT_NULL                    allocator,
303     uint32_t                                     memoryType,
304     VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,
305     VkDeviceSize                                 size,
306     void* VMA_NULLABLE                           pUserData);
307 /// Callback function called before vkFreeMemory.
308 typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)(
309     VmaAllocator VMA_NOT_NULL                    allocator,
310     uint32_t                                     memoryType,
311     VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,
312     VkDeviceSize                                 size,
313     void* VMA_NULLABLE                           pUserData);
314 
315 /** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`.
316 
317 Provided for informative purpose, e.g. to gather statistics about number of
318 allocations or total amount of memory allocated in Vulkan.
319 
320 Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
321 */
322 typedef struct VmaDeviceMemoryCallbacks {
323     /// Optional, can be null.
324     PFN_vmaAllocateDeviceMemoryFunction VMA_NULLABLE pfnAllocate;
325     /// Optional, can be null.
326     PFN_vmaFreeDeviceMemoryFunction VMA_NULLABLE pfnFree;
327     /// Optional, can be null.
328     void* VMA_NULLABLE pUserData;
329 } VmaDeviceMemoryCallbacks;
330 
331 /// Flags for created #VmaAllocator.
332 typedef enum VmaAllocatorCreateFlagBits {
333     /** \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.
334 
335     Using this flag may increase performance because internal mutexes are not used.
336     */
337     VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001,
338     /** \brief Enables usage of VK_KHR_dedicated_allocation extension.
339 
340     The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`.
341     When it is `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1.
342 
343     Using this extension will automatically allocate dedicated blocks of memory for
344     some buffers and images instead of suballocating place for them out of bigger
345     memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
346     flag) when it is recommended by the driver. It may improve performance on some
347     GPUs.
348 
349     You may set this flag only if you found out that following device extensions are
350     supported, you enabled them while creating Vulkan device passed as
351     VmaAllocatorCreateInfo::device, and you want them to be used internally by this
352     library:
353 
354     - VK_KHR_get_memory_requirements2 (device extension)
355     - VK_KHR_dedicated_allocation (device extension)
356 
357     When this flag is set, you can experience following warnings reported by Vulkan
358     validation layer. You can ignore them.
359 
360     > vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer.
361     */
362     VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002,
363     /**
364     Enables usage of VK_KHR_bind_memory2 extension.
365 
366     The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`.
367     When it is `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1.
368 
369     You may set this flag only if you found out that this device extension is supported,
370     you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,
371     and you want it to be used internally by this library.
372 
373     The extension provides functions `vkBindBufferMemory2KHR` and `vkBindImageMemory2KHR`,
374     which allow to pass a chain of `pNext` structures while binding.
375     This flag is required if you use `pNext` parameter in vmaBindBufferMemory2() or vmaBindImageMemory2().
376     */
377     VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT = 0x00000004,
378     /**
379     Enables usage of VK_EXT_memory_budget extension.
380 
381     You may set this flag only if you found out that this device extension is supported,
382     you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,
383     and you want it to be used internally by this library, along with another instance extension
384     VK_KHR_get_physical_device_properties2, which is required by it (or Vulkan 1.1, where this extension is promoted).
385 
386     The extension provides query for current memory usage and budget, which will probably
387     be more accurate than an estimation used by the library otherwise.
388     */
389     VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT = 0x00000008,
390     /**
391     Enables usage of VK_AMD_device_coherent_memory extension.
392 
393     You may set this flag only if you:
394 
395     - found out that this device extension is supported and enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,
396     - checked that `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true and set it while creating the Vulkan device,
397     - want it to be used internally by this library.
398 
399     The extension and accompanying device feature provide access to memory types with
400     `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flags.
401     They are useful mostly for writing breadcrumb markers - a common method for debugging GPU crash/hang/TDR.
402 
403     When the extension is not enabled, such memory types are still enumerated, but their usage is illegal.
404     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,
405     returning `VK_ERROR_FEATURE_NOT_PRESENT`.
406     */
407     VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT = 0x00000010,
408     /**
409     Enables usage of "buffer device address" feature, which allows you to use function
410     `vkGetBufferDeviceAddress*` to get raw GPU pointer to a buffer and pass it for usage inside a shader.
411 
412     You may set this flag only if you:
413 
414     1. (For Vulkan version < 1.2) Found as available and enabled device extension
415     VK_KHR_buffer_device_address.
416     This extension is promoted to core Vulkan 1.2.
417     2. Found as available and enabled device feature `VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress`.
418 
419     When this flag is set, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT` using VMA.
420     The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT` to
421     allocated memory blocks wherever it might be needed.
422 
423     For more information, see documentation chapter \ref enabling_buffer_device_address.
424     */
425     VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT = 0x00000020,
426     /**
427     Enables usage of VK_EXT_memory_priority extension in the library.
428 
429     You may set this flag only if you found available and enabled this device extension,
430     along with `VkPhysicalDeviceMemoryPriorityFeaturesEXT::memoryPriority == VK_TRUE`,
431     while creating Vulkan device passed as VmaAllocatorCreateInfo::device.
432 
433     When this flag is used, VmaAllocationCreateInfo::priority and VmaPoolCreateInfo::priority
434     are used to set priorities of allocated Vulkan memory. Without it, these variables are ignored.
435 
436     A priority must be a floating-point value between 0 and 1, indicating the priority of the allocation relative to other memory allocations.
437     Larger values are higher priority. The granularity of the priorities is implementation-dependent.
438     It is automatically passed to every call to `vkAllocateMemory` done by the library using structure `VkMemoryPriorityAllocateInfoEXT`.
439     The value to be used for default priority is 0.5.
440     For more details, see the documentation of the VK_EXT_memory_priority extension.
441     */
442     VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT = 0x00000040,
443 
444     VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
445 } VmaAllocatorCreateFlagBits;
446 typedef VkFlags VmaAllocatorCreateFlags;
447 
448 /** \brief Pointers to some Vulkan functions - a subset used by the library.
449 
450 Used in VmaAllocatorCreateInfo::pVulkanFunctions.
451 */
452 typedef struct VmaVulkanFunctions {
453     PFN_vkGetPhysicalDeviceProperties VMA_NULLABLE vkGetPhysicalDeviceProperties;
454     PFN_vkGetPhysicalDeviceMemoryProperties VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties;
455     PFN_vkAllocateMemory VMA_NULLABLE vkAllocateMemory;
456     PFN_vkFreeMemory VMA_NULLABLE vkFreeMemory;
457     PFN_vkMapMemory VMA_NULLABLE vkMapMemory;
458     PFN_vkUnmapMemory VMA_NULLABLE vkUnmapMemory;
459     PFN_vkFlushMappedMemoryRanges VMA_NULLABLE vkFlushMappedMemoryRanges;
460     PFN_vkInvalidateMappedMemoryRanges VMA_NULLABLE vkInvalidateMappedMemoryRanges;
461     PFN_vkBindBufferMemory VMA_NULLABLE vkBindBufferMemory;
462     PFN_vkBindImageMemory VMA_NULLABLE vkBindImageMemory;
463     PFN_vkGetBufferMemoryRequirements VMA_NULLABLE vkGetBufferMemoryRequirements;
464     PFN_vkGetImageMemoryRequirements VMA_NULLABLE vkGetImageMemoryRequirements;
465     PFN_vkCreateBuffer VMA_NULLABLE vkCreateBuffer;
466     PFN_vkDestroyBuffer VMA_NULLABLE vkDestroyBuffer;
467     PFN_vkCreateImage VMA_NULLABLE vkCreateImage;
468     PFN_vkDestroyImage VMA_NULLABLE vkDestroyImage;
469     PFN_vkCmdCopyBuffer VMA_NULLABLE vkCmdCopyBuffer;
470 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
471     /// Fetch "vkGetBufferMemoryRequirements2" on Vulkan >= 1.1, fetch "vkGetBufferMemoryRequirements2KHR" when using VK_KHR_dedicated_allocation extension.
472     PFN_vkGetBufferMemoryRequirements2KHR VMA_NULLABLE vkGetBufferMemoryRequirements2KHR;
473     /// Fetch "vkGetImageMemoryRequirements 2" on Vulkan >= 1.1, fetch "vkGetImageMemoryRequirements2KHR" when using VK_KHR_dedicated_allocation extension.
474     PFN_vkGetImageMemoryRequirements2KHR VMA_NULLABLE vkGetImageMemoryRequirements2KHR;
475 #endif
476 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
477     /// Fetch "vkBindBufferMemory2" on Vulkan >= 1.1, fetch "vkBindBufferMemory2KHR" when using VK_KHR_bind_memory2 extension.
478     PFN_vkBindBufferMemory2KHR VMA_NULLABLE vkBindBufferMemory2KHR;
479     /// Fetch "vkBindImageMemory2" on Vulkan >= 1.1, fetch "vkBindImageMemory2KHR" when using VK_KHR_bind_memory2 extension.
480     PFN_vkBindImageMemory2KHR VMA_NULLABLE vkBindImageMemory2KHR;
481 #endif
482 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
483     PFN_vkGetPhysicalDeviceMemoryProperties2KHR VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties2KHR;
484 #endif
485 } VmaVulkanFunctions;
486 
487 /// Flags to be used in VmaRecordSettings::flags.
488 typedef enum VmaRecordFlagBits {
489     /** \brief Enables flush after recording every function call.
490 
491     Enable it if you expect your application to crash, which may leave recording file truncated.
492     It may degrade performance though.
493     */
494     VMA_RECORD_FLUSH_AFTER_CALL_BIT = 0x00000001,
495 
496     VMA_RECORD_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
497 } VmaRecordFlagBits;
498 typedef VkFlags VmaRecordFlags;
499 
500 /// Parameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSettings.
501 typedef struct VmaRecordSettings
502 {
503     /// Flags for recording. Use #VmaRecordFlagBits enum.
504     VmaRecordFlags flags;
505     /** \brief Path to the file that should be written by the recording.
506 
507     Suggested extension: "csv".
508     If the file already exists, it will be overwritten.
509     It will be opened for the whole time #VmaAllocator object is alive.
510     If opening this file fails, creation of the whole allocator object fails.
511     */
512     const char* VMA_NOT_NULL pFilePath;
513 } VmaRecordSettings;
514 
515 /// Description of a Allocator to be created.
516 typedef struct VmaAllocatorCreateInfo
517 {
518     /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum.
519     VmaAllocatorCreateFlags flags;
520     /// Vulkan physical device.
521     /** It must be valid throughout whole lifetime of created allocator. */
522     VkPhysicalDevice VMA_NOT_NULL physicalDevice;
523     /// Vulkan device.
524     /** It must be valid throughout whole lifetime of created allocator. */
525     VkDevice VMA_NOT_NULL device;
526     /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional.
527     /** Set to 0 to use default, which is currently 256 MiB. */
528     VkDeviceSize preferredLargeHeapBlockSize;
529     /// Custom CPU memory allocation callbacks. Optional.
530     /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */
531     const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks;
532     /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional.
533     /** Optional, can be null. */
534     const VmaDeviceMemoryCallbacks* VMA_NULLABLE pDeviceMemoryCallbacks;
535     /** \brief Maximum number of additional frames that are in use at the same time as current frame.
536 
537     This value is used only when you make allocations with
538     VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
539     lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
540 
541     For example, if you double-buffer your command buffers, so resources used for
542     rendering in previous frame may still be in use by the GPU at the moment you
543     allocate resources needed for the current frame, set this value to 1.
544 
545     If you want to allow any allocations other than used in the current frame to
546     become lost, set this value to 0.
547     */
548     uint32_t frameInUseCount;
549     /** \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.
550 
551     If not NULL, it must be a pointer to an array of
552     `VkPhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on
553     maximum number of bytes that can be allocated out of particular Vulkan memory
554     heap.
555 
556     Any of the elements may be equal to `VK_WHOLE_SIZE`, which means no limit on that
557     heap. This is also the default in case of `pHeapSizeLimit` = NULL.
558 
559     If there is a limit defined for a heap:
560 
561     - If user tries to allocate more memory from that heap using this allocator,
562       the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
563     - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the
564       value of this limit will be reported instead when using vmaGetMemoryProperties().
565 
566     Warning! Using this feature may not be equivalent to installing a GPU with
567     smaller amount of memory, because graphics driver doesn't necessary fail new
568     allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is
569     exceeded. It may return success and just silently migrate some device memory
570     blocks to system RAM. This driver behavior can also be controlled using
571     VK_AMD_memory_overallocation_behavior extension.
572     */
573     const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pHeapSizeLimit;
574 
575     /** \brief Pointers to Vulkan functions. Can be null.
576 
577     For details see [Pointers to Vulkan functions](@ref config_Vulkan_functions).
578     */
579     const VmaVulkanFunctions* VMA_NULLABLE pVulkanFunctions;
580     /** \brief Parameters for recording of VMA calls. Can be null.
581 
582     If not null, it enables recording of calls to VMA functions to a file.
583     If support for recording is not enabled using `VMA_RECORDING_ENABLED` macro,
584     creation of the allocator object fails with `VK_ERROR_FEATURE_NOT_PRESENT`.
585     */
586     const VmaRecordSettings* VMA_NULLABLE pRecordSettings;
587     /** \brief Handle to Vulkan instance object.
588 
589     Starting from version 3.0.0 this member is no longer optional, it must be set!
590     */
591     VkInstance VMA_NOT_NULL instance;
592     /** \brief Optional. The highest version of Vulkan that the application is designed to use.
593 
594     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`.
595     The patch version number specified is ignored. Only the major and minor versions are considered.
596     It must be less or equal (preferably equal) to value as passed to `vkCreateInstance` as `VkApplicationInfo::apiVersion`.
597     Only versions 1.0, 1.1, 1.2 are supported by the current implementation.
598     Leaving it initialized to zero is equivalent to `VK_API_VERSION_1_0`.
599     */
600     uint32_t vulkanApiVersion;
601 #if VMA_EXTERNAL_MEMORY
602     /** \brief Either null or a pointer to an array of external memory handle types for each Vulkan memory type.
603 
604     If not NULL, it must be a pointer to an array of `VkPhysicalDeviceMemoryProperties::memoryTypeCount`
605     elements, defining external memory handle types of particular Vulkan memory type,
606     to be passed using `VkExportMemoryAllocateInfoKHR`.
607 
608     Any of the elements may be equal to 0, which means not to use `VkExportMemoryAllocateInfoKHR` on this memory type.
609     This is also the default in case of `pTypeExternalMemoryHandleTypes` = NULL.
610     */
611     const VkExternalMemoryHandleTypeFlagsKHR* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryTypeCount") pTypeExternalMemoryHandleTypes;
612 #endif // #if VMA_EXTERNAL_MEMORY
613 } VmaAllocatorCreateInfo;
614 
615 /// Creates Allocator object.
616 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
617     const VmaAllocatorCreateInfo* VMA_NOT_NULL pCreateInfo,
618     VmaAllocator VMA_NULLABLE * VMA_NOT_NULL pAllocator);
619 
620 /// Destroys allocator object.
621 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
622     VmaAllocator VMA_NULLABLE allocator);
623 
624 /** \brief Information about existing #VmaAllocator object.
625 */
626 typedef struct VmaAllocatorInfo
627 {
628     /** \brief Handle to Vulkan instance object.
629 
630     This is the same value as has been passed through VmaAllocatorCreateInfo::instance.
631     */
632     VkInstance VMA_NOT_NULL instance;
633     /** \brief Handle to Vulkan physical device object.
634 
635     This is the same value as has been passed through VmaAllocatorCreateInfo::physicalDevice.
636     */
637     VkPhysicalDevice VMA_NOT_NULL physicalDevice;
638     /** \brief Handle to Vulkan device object.
639 
640     This is the same value as has been passed through VmaAllocatorCreateInfo::device.
641     */
642     VkDevice VMA_NOT_NULL device;
643 } VmaAllocatorInfo;
644 
645 /** \brief Returns information about existing #VmaAllocator object - handle to Vulkan device etc.
646 
647 It might be useful if you want to keep just the #VmaAllocator handle and fetch other required handles to
648 `VkPhysicalDevice`, `VkDevice` etc. every time using this function.
649 */
650 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator VMA_NOT_NULL allocator, VmaAllocatorInfo* VMA_NOT_NULL pAllocatorInfo);
651 
652 /**
653 PhysicalDeviceProperties are fetched from physicalDevice by the allocator.
654 You can access it here, without fetching it again on your own.
655 */
656 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
657     VmaAllocator VMA_NOT_NULL allocator,
658     const VkPhysicalDeviceProperties* VMA_NULLABLE * VMA_NOT_NULL ppPhysicalDeviceProperties);
659 
660 /**
661 PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator.
662 You can access it here, without fetching it again on your own.
663 */
664 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
665     VmaAllocator VMA_NOT_NULL allocator,
666     const VkPhysicalDeviceMemoryProperties* VMA_NULLABLE * VMA_NOT_NULL ppPhysicalDeviceMemoryProperties);
667 
668 /**
669 \brief Given Memory Type Index, returns Property Flags of this memory type.
670 
671 This is just a convenience function. Same information can be obtained using
672 vmaGetMemoryProperties().
673 */
674 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
675     VmaAllocator VMA_NOT_NULL allocator,
676     uint32_t memoryTypeIndex,
677     VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);
678 
679 /** \brief Sets index of the current frame.
680 
681 This function must be used if you make allocations with
682 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT and
683 #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flags to inform the allocator
684 when a new frame begins. Allocations queried using vmaGetAllocationInfo() cannot
685 become lost in the current frame.
686 */
687 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
688     VmaAllocator VMA_NOT_NULL allocator,
689     uint32_t frameIndex);
690 
691 /** \brief Calculated statistics of memory usage in entire allocator.
692 */
693 typedef struct VmaStatInfo
694 {
695     /// Number of `VkDeviceMemory` Vulkan memory blocks allocated.
696     uint32_t blockCount;
697     /// Number of #VmaAllocation allocation objects allocated.
698     uint32_t allocationCount;
699     /// Number of free ranges of memory between allocations.
700     uint32_t unusedRangeCount;
701     /// Total number of bytes occupied by all allocations.
702     VkDeviceSize usedBytes;
703     /// Total number of bytes occupied by unused ranges.
704     VkDeviceSize unusedBytes;
705     VkDeviceSize allocationSizeMin, allocationSizeAvg, allocationSizeMax;
706     VkDeviceSize unusedRangeSizeMin, unusedRangeSizeAvg, unusedRangeSizeMax;
707 } VmaStatInfo;
708 
709 /// General statistics from current state of Allocator.
710 typedef struct VmaStats
711 {
712     VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
713     VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
714     VmaStatInfo total;
715 } VmaStats;
716 
717 /** \brief Retrieves statistics from current state of the Allocator.
718 
719 This function is called "calculate" not "get" because it has to traverse all
720 internal data structures, so it may be quite slow. For faster but more brief statistics
721 suitable to be called every frame or every allocation, use vmaGetBudget().
722 
723 Note that when using allocator from multiple threads, returned information may immediately
724 become outdated.
725 */
726 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
727     VmaAllocator VMA_NOT_NULL allocator,
728     VmaStats* VMA_NOT_NULL pStats);
729 
730 /** \brief Statistics of current memory usage and available budget, in bytes, for specific memory heap.
731 */
732 typedef struct VmaBudget
733 {
734     /** \brief Sum size of all `VkDeviceMemory` blocks allocated from particular heap, in bytes.
735     */
736     VkDeviceSize blockBytes;
737 
738     /** \brief Sum size of all allocations created in particular heap, in bytes.
739 
740     Usually less or equal than `blockBytes`.
741     Difference `blockBytes - allocationBytes` is the amount of memory allocated but unused -
742     available for new allocations or wasted due to fragmentation.
743 
744     It might be greater than `blockBytes` if there are some allocations in lost state, as they account
745     to this value as well.
746     */
747     VkDeviceSize allocationBytes;
748 
749     /** \brief Estimated current memory usage of the program, in bytes.
750 
751     Fetched from system using `VK_EXT_memory_budget` extension if enabled.
752 
753     It might be different than `blockBytes` (usually higher) due to additional implicit objects
754     also occupying the memory, like swapchain, pipelines, descriptor heaps, command buffers, or
755     `VkDeviceMemory` blocks allocated outside of this library, if any.
756     */
757     VkDeviceSize usage;
758 
759     /** \brief Estimated amount of memory available to the program, in bytes.
760 
761     Fetched from system using `VK_EXT_memory_budget` extension if enabled.
762 
763     It might be different (most probably smaller) than `VkMemoryHeap::size[heapIndex]` due to factors
764     external to the program, like other programs also consuming system resources.
765     Difference `budget - usage` is the amount of additional memory that can probably
766     be allocated without problems. Exceeding the budget may result in various problems.
767     */
768     VkDeviceSize budget;
769 } VmaBudget;
770 
771 /** \brief Retrieves information about current memory budget for all memory heaps.
772 
773 \param allocator
774 \param[out] pBudget Must point to array with number of elements at least equal to number of memory heaps in physical device used.
775 
776 This function is called "get" not "calculate" because it is very fast, suitable to be called
777 every frame or every allocation. For more detailed statistics use vmaCalculateStats().
778 
779 Note that when using allocator from multiple threads, returned information may immediately
780 become outdated.
781 */
782 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
783     VmaAllocator VMA_NOT_NULL allocator,
784     VmaBudget* VMA_NOT_NULL pBudget);
785 
786 #ifndef VMA_STATS_STRING_ENABLED
787 #define VMA_STATS_STRING_ENABLED 1
788 #endif
789 
790 #if VMA_STATS_STRING_ENABLED
791 
792 /// Builds and returns statistics as a null-terminated string in JSON format.
793 /**
794 @param allocator
795 @param[out] ppStatsString Must be freed using vmaFreeStatsString() function.
796 @param detailedMap
797 */
798 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
799     VmaAllocator VMA_NOT_NULL allocator,
800     char* VMA_NULLABLE * VMA_NOT_NULL ppStatsString,
801     VkBool32 detailedMap);
802 
803 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
804     VmaAllocator VMA_NOT_NULL allocator,
805     char* VMA_NULLABLE pStatsString);
806 
807 #endif // #if VMA_STATS_STRING_ENABLED
808 
809 /** \struct VmaPool
810 \brief Represents custom memory pool
811 
812 Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it.
813 Call function vmaDestroyPool() to destroy it.
814 
815 For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools).
816 */
817 VK_DEFINE_HANDLE(VmaPool)
818 
819 typedef enum VmaMemoryUsage
820 {
821     /** No intended memory usage specified.
822     Use other members of VmaAllocationCreateInfo to specify your requirements.
823     */
824     VMA_MEMORY_USAGE_UNKNOWN = 0,
825     /** Memory will be used on device only, so fast access from the device is preferred.
826     It usually means device-local GPU (video) memory.
827     No need to be mappable on host.
828     It is roughly equivalent of `D3D12_HEAP_TYPE_DEFAULT`.
829 
830     Usage:
831 
832     - Resources written and read by device, e.g. images used as attachments.
833     - Resources transferred from host once (immutable) or infrequently and read by
834       device multiple times, e.g. textures to be sampled, vertex buffers, uniform
835       (constant) buffers, and majority of other types of resources used on GPU.
836 
837     Allocation may still end up in `HOST_VISIBLE` memory on some implementations.
838     In such case, you are free to map it.
839     You can use #VMA_ALLOCATION_CREATE_MAPPED_BIT with this usage type.
840     */
841     VMA_MEMORY_USAGE_GPU_ONLY = 1,
842     /** Memory will be mappable on host.
843     It usually means CPU (system) memory.
844     Guarantees to be `HOST_VISIBLE` and `HOST_COHERENT`.
845     CPU access is typically uncached. Writes may be write-combined.
846     Resources created in this pool may still be accessible to the device, but access to them can be slow.
847     It is roughly equivalent of `D3D12_HEAP_TYPE_UPLOAD`.
848 
849     Usage: Staging copy of resources used as transfer source.
850     */
851     VMA_MEMORY_USAGE_CPU_ONLY = 2,
852     /**
853     Memory that is both mappable on host (guarantees to be `HOST_VISIBLE`) and preferably fast to access by GPU.
854     CPU access is typically uncached. Writes may be write-combined.
855 
856     Usage: Resources written frequently by host (dynamic), read by device. E.g. textures (with LINEAR layout), vertex buffers, uniform buffers updated every frame or every draw call.
857     */
858     VMA_MEMORY_USAGE_CPU_TO_GPU = 3,
859     /** Memory mappable on host (guarantees to be `HOST_VISIBLE`) and cached.
860     It is roughly equivalent of `D3D12_HEAP_TYPE_READBACK`.
861 
862     Usage:
863 
864     - Resources written by device, read by host - results of some computations, e.g. screen capture, average scene luminance for HDR tone mapping.
865     - Any resources read or accessed randomly on host, e.g. CPU-side copy of vertex buffer used as source of transfer, but also used for collision detection.
866     */
867     VMA_MEMORY_USAGE_GPU_TO_CPU = 4,
868     /** CPU memory - memory that is preferably not `DEVICE_LOCAL`, but also not guaranteed to be `HOST_VISIBLE`.
869 
870     Usage: Staging copy of resources moved from GPU memory to CPU memory as part
871     of custom paging/residency mechanism, to be moved back to GPU memory when needed.
872     */
873     VMA_MEMORY_USAGE_CPU_COPY = 5,
874     /** Lazily allocated GPU memory having `VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT`.
875     Exists mostly on mobile platforms. Using it on desktop PC or other GPUs with no such memory type present will fail the allocation.
876 
877     Usage: Memory for transient attachment images (color attachments, depth attachments etc.), created with `VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT`.
878 
879     Allocations with this usage are always created as dedicated - it implies #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
880     */
881     VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED = 6,
882 
883     VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
884 } VmaMemoryUsage;
885 
886 /// Flags to be passed as VmaAllocationCreateInfo::flags.
887 typedef enum VmaAllocationCreateFlagBits {
888     /** \brief Set this flag if the allocation should have its own memory block.
889 
890     Use it for special, big resources, like fullscreen images used as attachments.
891 
892     You should not use this flag if VmaAllocationCreateInfo::pool is not null.
893     */
894     VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001,
895 
896     /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block.
897 
898     If new allocation cannot be placed in any of the existing blocks, allocation
899     fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
900 
901     You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and
902     #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense.
903 
904     If VmaAllocationCreateInfo::pool is not null, this flag is implied and ignored. */
905     VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002,
906     /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it.
907 
908     Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData.
909 
910     It is valid to use this flag for allocation made from memory type that is not
911     `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is
912     useful if you need an allocation that is efficient to use on GPU
913     (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that
914     support it (e.g. Intel GPU).
915 
916     You should not use this flag together with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT.
917     */
918     VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004,
919     /** Allocation created with this flag can become lost as a result of another
920     allocation with #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag, so you
921     must check it before use.
922 
923     To check if allocation is not lost, call vmaGetAllocationInfo() and check if
924     VmaAllocationInfo::deviceMemory is not `VK_NULL_HANDLE`.
925 
926     For details about supporting lost allocations, see Lost Allocations
927     chapter of User Guide on Main Page.
928 
929     You should not use this flag together with #VMA_ALLOCATION_CREATE_MAPPED_BIT.
930     */
931     VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT = 0x00000008,
932     /** While creating allocation using this flag, other allocations that were
933     created with flag #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT can become lost.
934 
935     For details about supporting lost allocations, see Lost Allocations
936     chapter of User Guide on Main Page.
937     */
938     VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT = 0x00000010,
939     /** Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a
940     null-terminated string. Instead of copying pointer value, a local copy of the
941     string is made and stored in allocation's `pUserData`. The string is automatically
942     freed together with the allocation. It is also used in vmaBuildStatsString().
943     */
944     VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020,
945     /** Allocation will be created from upper stack in a double stack pool.
946 
947     This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag.
948     */
949     VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040,
950     /** Create both buffer/image and allocation, but don't bind them together.
951     It is useful when you want to bind yourself to do some more advanced binding, e.g. using some extensions.
952     The flag is meaningful only with functions that bind by default: vmaCreateBuffer(), vmaCreateImage().
953     Otherwise it is ignored.
954     */
955     VMA_ALLOCATION_CREATE_DONT_BIND_BIT = 0x00000080,
956     /** Create allocation only if additional device memory required for it, if any, won't exceed
957     memory budget. Otherwise return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
958     */
959     VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT = 0x00000100,
960 
961     /** Allocation strategy that chooses smallest possible free range for the
962     allocation.
963     */
964     VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT  = 0x00010000,
965     /** Allocation strategy that chooses biggest possible free range for the
966     allocation.
967     */
968     VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT = 0x00020000,
969     /** Allocation strategy that chooses first suitable free range for the
970     allocation.
971 
972     "First" doesn't necessarily means the one with smallest offset in memory,
973     but rather the one that is easiest and fastest to find.
974     */
975     VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = 0x00040000,
976 
977     /** Allocation strategy that tries to minimize memory usage.
978     */
979     VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT,
980     /** Allocation strategy that tries to minimize allocation time.
981     */
982     VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
983     /** Allocation strategy that tries to minimize memory fragmentation.
984     */
985     VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT,
986 
987     /** A bit mask to extract only `STRATEGY` bits from entire set of flags.
988     */
989     VMA_ALLOCATION_CREATE_STRATEGY_MASK =
990         VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT |
991         VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT |
992         VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
993 
994     VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
995 } VmaAllocationCreateFlagBits;
996 typedef VkFlags VmaAllocationCreateFlags;
997 
998 typedef struct VmaAllocationCreateInfo
999 {
1000     /// Use #VmaAllocationCreateFlagBits enum.
1001     VmaAllocationCreateFlags flags;
1002     /** \brief Intended usage of memory.
1003 
1004     You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n
1005     If `pool` is not null, this member is ignored.
1006     */
1007     VmaMemoryUsage usage;
1008     /** \brief Flags that must be set in a Memory Type chosen for an allocation.
1009 
1010     Leave 0 if you specify memory requirements in other way. \n
1011     If `pool` is not null, this member is ignored.*/
1012     VkMemoryPropertyFlags requiredFlags;
1013     /** \brief Flags that preferably should be set in a memory type chosen for an allocation.
1014 
1015     Set to 0 if no additional flags are preferred. \n
1016     If `pool` is not null, this member is ignored. */
1017     VkMemoryPropertyFlags preferredFlags;
1018     /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation.
1019 
1020     Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if
1021     it meets other requirements specified by this structure, with no further
1022     restrictions on memory type index. \n
1023     If `pool` is not null, this member is ignored.
1024     */
1025     uint32_t memoryTypeBits;
1026     /** \brief Pool that this allocation should be created in.
1027 
1028     Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members:
1029     `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.
1030     */
1031     VmaPool VMA_NULLABLE pool;
1032     /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().
1033 
1034     If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either
1035     null or pointer to a null-terminated string. The string will be then copied to
1036     internal buffer, so it doesn't need to be valid after allocation call.
1037     */
1038     void* VMA_NULLABLE pUserData;
1039     /** \brief A floating-point value between 0 and 1, indicating the priority of the allocation relative to other memory allocations.
1040 
1041     It is used only when #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT flag was used during creation of the #VmaAllocator object
1042     and this allocation ends up as dedicated or is explicitly forced as dedicated using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
1043     Otherwise, it has the priority of a memory block where it is placed and this variable is ignored.
1044     */
1045     float priority;
1046 } VmaAllocationCreateInfo;
1047 
1048 /**
1049 \brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.
1050 
1051 This algorithm tries to find a memory type that:
1052 
1053 - Is allowed by memoryTypeBits.
1054 - Contains all the flags from pAllocationCreateInfo->requiredFlags.
1055 - Matches intended usage.
1056 - Has as many flags from pAllocationCreateInfo->preferredFlags as possible.
1057 
1058 \return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result
1059 from this function or any other allocating function probably means that your
1060 device doesn't support any memory type with requested features for the specific
1061 type of resource you want to use it for. Please check parameters of your
1062 resource, like image layout (OPTIMAL versus LINEAR) or mip level count.
1063 */
1064 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
1065     VmaAllocator VMA_NOT_NULL allocator,
1066     uint32_t memoryTypeBits,
1067     const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
1068     uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
1069 
1070 /**
1071 \brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.
1072 
1073 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
1074 It internally creates a temporary, dummy buffer that never has memory bound.
1075 It is just a convenience function, equivalent to calling:
1076 
1077 - `vkCreateBuffer`
1078 - `vkGetBufferMemoryRequirements`
1079 - `vmaFindMemoryTypeIndex`
1080 - `vkDestroyBuffer`
1081 */
1082 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
1083     VmaAllocator VMA_NOT_NULL allocator,
1084     const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
1085     const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
1086     uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
1087 
1088 /**
1089 \brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.
1090 
1091 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
1092 It internally creates a temporary, dummy image that never has memory bound.
1093 It is just a convenience function, equivalent to calling:
1094 
1095 - `vkCreateImage`
1096 - `vkGetImageMemoryRequirements`
1097 - `vmaFindMemoryTypeIndex`
1098 - `vkDestroyImage`
1099 */
1100 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
1101     VmaAllocator VMA_NOT_NULL allocator,
1102     const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
1103     const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
1104     uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
1105 
1106 /// Flags to be passed as VmaPoolCreateInfo::flags.
1107 typedef enum VmaPoolCreateFlagBits {
1108     /** \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.
1109 
1110     This is an optional optimization flag.
1111 
1112     If you always allocate using vmaCreateBuffer(), vmaCreateImage(),
1113     vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator
1114     knows exact type of your allocations so it can handle Buffer-Image Granularity
1115     in the optimal way.
1116 
1117     If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(),
1118     exact type of such allocations is not known, so allocator must be conservative
1119     in handling Buffer-Image Granularity, which can lead to suboptimal allocation
1120     (wasted memory). In that case, if you can make sure you always allocate only
1121     buffers and linear images or only optimal images out of this pool, use this flag
1122     to make allocator disregard Buffer-Image Granularity and so make allocations
1123     faster and more optimal.
1124     */
1125     VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002,
1126 
1127     /** \brief Enables alternative, linear allocation algorithm in this pool.
1128 
1129     Specify this flag to enable linear allocation algorithm, which always creates
1130     new allocations after last one and doesn't reuse space from allocations freed in
1131     between. It trades memory consumption for simplified algorithm and data
1132     structure, which has better performance and uses less memory for metadata.
1133 
1134     By using this flag, you can achieve behavior of free-at-once, stack,
1135     ring buffer, and double stack.
1136     For details, see documentation chapter \ref linear_algorithm.
1137 
1138     When using this flag, you must specify VmaPoolCreateInfo::maxBlockCount == 1 (or 0 for default).
1139     */
1140     VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004,
1141 
1142     /** \brief Enables alternative, buddy allocation algorithm in this pool.
1143 
1144     It operates on a tree of blocks, each having size that is a power of two and
1145     a half of its parent's size. Comparing to default algorithm, this one provides
1146     faster allocation and deallocation and decreased external fragmentation,
1147     at the expense of more memory wasted (internal fragmentation).
1148     For details, see documentation chapter \ref buddy_algorithm.
1149     */
1150     VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT = 0x00000008,
1151 
1152     /** Bit mask to extract only `ALGORITHM` bits from entire set of flags.
1153     */
1154     VMA_POOL_CREATE_ALGORITHM_MASK =
1155         VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT |
1156         VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT,
1157 
1158     VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1159 } VmaPoolCreateFlagBits;
1160 /// Flags to be passed as VmaPoolCreateInfo::flags. See #VmaPoolCreateFlagBits.
1161 typedef VkFlags VmaPoolCreateFlags;
1162 
1163 /** \brief Describes parameter of created #VmaPool.
1164 */
1165 typedef struct VmaPoolCreateInfo {
1166     /** \brief Vulkan memory type index to allocate this pool from.
1167     */
1168     uint32_t memoryTypeIndex;
1169     /** \brief Use combination of #VmaPoolCreateFlagBits.
1170     */
1171     VmaPoolCreateFlags flags;
1172     /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. Optional.
1173 
1174     Specify nonzero to set explicit, constant size of memory blocks used by this
1175     pool.
1176 
1177     Leave 0 to use default and let the library manage block sizes automatically.
1178     Sizes of particular blocks may vary.
1179     */
1180     VkDeviceSize blockSize;
1181     /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty.
1182 
1183     Set to 0 to have no preallocated blocks and allow the pool be completely empty.
1184     */
1185     size_t minBlockCount;
1186     /** \brief Maximum number of blocks that can be allocated in this pool. Optional.
1187 
1188     Set to 0 to use default, which is `SIZE_MAX`, which means no limit.
1189 
1190     Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated
1191     throughout whole lifetime of this pool.
1192     */
1193     size_t maxBlockCount;
1194     /** \brief Maximum number of additional frames that are in use at the same time as current frame.
1195 
1196     This value is used only when you make allocations with
1197     #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
1198     lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
1199 
1200     For example, if you double-buffer your command buffers, so resources used for
1201     rendering in previous frame may still be in use by the GPU at the moment you
1202     allocate resources needed for the current frame, set this value to 1.
1203 
1204     If you want to allow any allocations other than used in the current frame to
1205     become lost, set this value to 0.
1206     */
1207     uint32_t frameInUseCount;
1208     /** \brief A floating-point value between 0 and 1, indicating the priority of the allocations in this pool relative to other memory allocations.
1209 
1210     It is used only when #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT flag was used during creation of the #VmaAllocator object.
1211     Otherwise, this variable is ignored.
1212     */
1213     float priority;
1214     /** \brief Additional minimum alignment to be used for all allocations created from this pool. Can be 0.
1215 
1216     Leave 0 (default) not to impose any additional alignment. If not 0, it must be a power of two.
1217     It can be useful in cases where alignment returned by Vulkan by functions like `vkGetBufferMemoryRequirements` is not enough,
1218     e.g. when doing interop with OpenGL.
1219     */
1220     VkDeviceSize minAllocationAlignment;
1221     /** \brief Additional `pNext` chain to be attached to `VkMemoryAllocateInfo` used for every allocation made by this pool. Optional.
1222 
1223     Optional, can be null. If not null, it must point to a `pNext` chain of structures that can be attached to `VkMemoryAllocateInfo`.
1224     It can be useful for special needs such as adding `VkExportMemoryAllocateInfoKHR`.
1225     Structures pointed by this member must remain alive and unchanged for the whole lifetime of the custom pool.
1226 
1227     Please note that some structures, e.g. `VkMemoryPriorityAllocateInfoEXT`, `VkMemoryDedicatedAllocateInfoKHR`,
1228     can be attached automatically by this library when using other, more convenient of its features.
1229     */
1230     void* VMA_NULLABLE pMemoryAllocateNext;
1231 } VmaPoolCreateInfo;
1232 
1233 /** \brief Describes parameter of existing #VmaPool.
1234 */
1235 typedef struct VmaPoolStats {
1236     /** \brief Total amount of `VkDeviceMemory` allocated from Vulkan for this pool, in bytes.
1237     */
1238     VkDeviceSize size;
1239     /** \brief Total number of bytes in the pool not used by any #VmaAllocation.
1240     */
1241     VkDeviceSize unusedSize;
1242     /** \brief Number of #VmaAllocation objects created from this pool that were not destroyed or lost.
1243     */
1244     size_t allocationCount;
1245     /** \brief Number of continuous memory ranges in the pool not used by any #VmaAllocation.
1246     */
1247     size_t unusedRangeCount;
1248     /** \brief Size of the largest continuous free memory region available for new allocation.
1249 
1250     Making a new allocation of that size is not guaranteed to succeed because of
1251     possible additional margin required to respect alignment and buffer/image
1252     granularity.
1253     */
1254     VkDeviceSize unusedRangeSizeMax;
1255     /** \brief Number of `VkDeviceMemory` blocks allocated for this pool.
1256     */
1257     size_t blockCount;
1258 } VmaPoolStats;
1259 
1260 /** \brief Allocates Vulkan device memory and creates #VmaPool object.
1261 
1262 @param allocator Allocator object.
1263 @param pCreateInfo Parameters of pool to create.
1264 @param[out] pPool Handle to created pool.
1265 */
1266 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
1267     VmaAllocator VMA_NOT_NULL allocator,
1268     const VmaPoolCreateInfo* VMA_NOT_NULL pCreateInfo,
1269     VmaPool VMA_NULLABLE * VMA_NOT_NULL pPool);
1270 
1271 /** \brief Destroys #VmaPool object and frees Vulkan device memory.
1272 */
1273 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
1274     VmaAllocator VMA_NOT_NULL allocator,
1275     VmaPool VMA_NULLABLE pool);
1276 
1277 /** \brief Retrieves statistics of existing #VmaPool object.
1278 
1279 @param allocator Allocator object.
1280 @param pool Pool object.
1281 @param[out] pPoolStats Statistics of specified pool.
1282 */
1283 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
1284     VmaAllocator VMA_NOT_NULL allocator,
1285     VmaPool VMA_NOT_NULL pool,
1286     VmaPoolStats* VMA_NOT_NULL pPoolStats);
1287 
1288 /** \brief Marks all allocations in given pool as lost if they are not used in current frame or VmaPoolCreateInfo::frameInUseCount back from now.
1289 
1290 @param allocator Allocator object.
1291 @param pool Pool.
1292 @param[out] pLostAllocationCount Number of allocations marked as lost. Optional - pass null if you don't need this information.
1293 */
1294 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
1295     VmaAllocator VMA_NOT_NULL allocator,
1296     VmaPool VMA_NOT_NULL pool,
1297     size_t* VMA_NULLABLE pLostAllocationCount);
1298 
1299 /** \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions.
1300 
1301 Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
1302 `VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is
1303 `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
1304 
1305 Possible return values:
1306 
1307 - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool.
1308 - `VK_SUCCESS` - corruption detection has been performed and succeeded.
1309 - `VK_ERROR_UNKNOWN` - corruption detection has been performed and found memory corruptions around one of the allocations.
1310   `VMA_ASSERT` is also fired in that case.
1311 - Other value: Error returned by Vulkan, e.g. memory mapping failure.
1312 */
1313 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator VMA_NOT_NULL allocator, VmaPool VMA_NOT_NULL pool);
1314 
1315 /** \brief Retrieves name of a custom pool.
1316 
1317 After the call `ppName` is either null or points to an internally-owned null-terminated string
1318 containing name of the pool that was previously set. The pointer becomes invalid when the pool is
1319 destroyed or its name is changed using vmaSetPoolName().
1320 */
1321 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
1322     VmaAllocator VMA_NOT_NULL allocator,
1323     VmaPool VMA_NOT_NULL pool,
1324     const char* VMA_NULLABLE * VMA_NOT_NULL ppName);
1325 
1326 /** \brief Sets name of a custom pool.
1327 
1328 `pName` can be either null or pointer to a null-terminated string with new name for the pool.
1329 Function makes internal copy of the string, so it can be changed or freed immediately after this call.
1330 */
1331 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
1332     VmaAllocator VMA_NOT_NULL allocator,
1333     VmaPool VMA_NOT_NULL pool,
1334     const char* VMA_NULLABLE pName);
1335 
1336 /** \struct VmaAllocation
1337 \brief Represents single memory allocation.
1338 
1339 It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type
1340 plus unique offset.
1341 
1342 There are multiple ways to create such object.
1343 You need to fill structure VmaAllocationCreateInfo.
1344 For more information see [Choosing memory type](@ref choosing_memory_type).
1345 
1346 Although the library provides convenience functions that create Vulkan buffer or image,
1347 allocate memory for it and bind them together,
1348 binding of the allocation to a buffer or an image is out of scope of the allocation itself.
1349 Allocation object can exist without buffer/image bound,
1350 binding can be done manually by the user, and destruction of it can be done
1351 independently of destruction of the allocation.
1352 
1353 The object also remembers its size and some other information.
1354 To retrieve this information, use function vmaGetAllocationInfo() and inspect
1355 returned structure VmaAllocationInfo.
1356 
1357 Some kinds allocations can be in lost state.
1358 For more information, see [Lost allocations](@ref lost_allocations).
1359 */
1360 VK_DEFINE_HANDLE(VmaAllocation)
1361 
1362 /** \brief Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
1363 */
1364 typedef struct VmaAllocationInfo {
1365     /** \brief Memory type index that this allocation was allocated from.
1366 
1367     It never changes.
1368     */
1369     uint32_t memoryType;
1370     /** \brief Handle to Vulkan memory object.
1371 
1372     Same memory object can be shared by multiple allocations.
1373 
1374     It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
1375 
1376     If the allocation is lost, it is equal to `VK_NULL_HANDLE`.
1377     */
1378     VkDeviceMemory VMA_NULLABLE_NON_DISPATCHABLE deviceMemory;
1379     /** \brief Offset in `VkDeviceMemory` object to the beginning of this allocation, in bytes. `(deviceMemory, offset)` pair is unique to this allocation.
1380 
1381     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
1382     vmaCreateBuffer(), vmaCreateImage(), functions that operate on these resources refer to the beginning of the buffer or image,
1383     not entire device memory block. Functions like vmaMapMemory(), vmaBindBufferMemory() also refer to the beginning of the allocation
1384     and apply this offset automatically.
1385 
1386     It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
1387     */
1388     VkDeviceSize offset;
1389     /** \brief Size of this allocation, in bytes.
1390 
1391     It never changes, unless allocation is lost.
1392 
1393     \note Allocation size returned in this variable may be greater than the size
1394     requested for the resource e.g. as `VkBufferCreateInfo::size`. Whole size of the
1395     allocation is accessible for operations on memory e.g. using a pointer after
1396     mapping with vmaMapMemory(), but operations on the resource e.g. using
1397     `vkCmdCopyBuffer` must be limited to the size of the resource.
1398     */
1399     VkDeviceSize size;
1400     /** \brief Pointer to the beginning of this allocation as mapped data.
1401 
1402     If the allocation hasn't been mapped using vmaMapMemory() and hasn't been
1403     created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value is null.
1404 
1405     It can change after call to vmaMapMemory(), vmaUnmapMemory().
1406     It can also change after call to vmaDefragment() if this allocation is passed to the function.
1407     */
1408     void* VMA_NULLABLE pMappedData;
1409     /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData().
1410 
1411     It can change after call to vmaSetAllocationUserData() for this allocation.
1412     */
1413     void* VMA_NULLABLE pUserData;
1414 } VmaAllocationInfo;
1415 
1416 /** \brief General purpose memory allocation.
1417 
1418 @param allocator
1419 @param pVkMemoryRequirements
1420 @param pCreateInfo
1421 @param[out] pAllocation Handle to allocated memory.
1422 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
1423 
1424 You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
1425 
1426 It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(),
1427 vmaCreateBuffer(), vmaCreateImage() instead whenever possible.
1428 */
1429 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
1430     VmaAllocator VMA_NOT_NULL allocator,
1431     const VkMemoryRequirements* VMA_NOT_NULL pVkMemoryRequirements,
1432     const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
1433     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
1434     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
1435 
1436 /** \brief General purpose memory allocation for multiple allocation objects at once.
1437 
1438 @param allocator Allocator object.
1439 @param pVkMemoryRequirements Memory requirements for each allocation.
1440 @param pCreateInfo Creation parameters for each alloction.
1441 @param allocationCount Number of allocations to make.
1442 @param[out] pAllocations Pointer to array that will be filled with handles to created allocations.
1443 @param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations.
1444 
1445 You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
1446 
1447 Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding.
1448 It is just a general purpose allocation function able to make multiple allocations at once.
1449 It may be internally optimized to be more efficient than calling vmaAllocateMemory() `allocationCount` times.
1450 
1451 All allocations are made using same parameters. All of them are created out of the same memory pool and type.
1452 If any allocation fails, all allocations already made within this function call are also freed, so that when
1453 returned result is not `VK_SUCCESS`, `pAllocation` array is always entirely filled with `VK_NULL_HANDLE`.
1454 */
1455 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
1456     VmaAllocator VMA_NOT_NULL allocator,
1457     const VkMemoryRequirements* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pVkMemoryRequirements,
1458     const VmaAllocationCreateInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pCreateInfo,
1459     size_t allocationCount,
1460     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
1461     VmaAllocationInfo* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationInfo);
1462 
1463 /**
1464 @param allocator
1465 @param buffer
1466 @param pCreateInfo
1467 @param[out] pAllocation Handle to allocated memory.
1468 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
1469 
1470 You should free the memory using vmaFreeMemory().
1471 */
1472 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
1473     VmaAllocator VMA_NOT_NULL allocator,
1474     VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
1475     const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
1476     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
1477     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
1478 
1479 /// Function similar to vmaAllocateMemoryForBuffer().
1480 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
1481     VmaAllocator VMA_NOT_NULL allocator,
1482     VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
1483     const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
1484     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
1485     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
1486 
1487 /** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().
1488 
1489 Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped.
1490 */
1491 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
1492     VmaAllocator VMA_NOT_NULL allocator,
1493     const VmaAllocation VMA_NULLABLE allocation);
1494 
1495 /** \brief Frees memory and destroys multiple allocations.
1496 
1497 Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding.
1498 It is just a general purpose function to free memory and destroy allocations made using e.g. vmaAllocateMemory(),
1499 vmaAllocateMemoryPages() and other functions.
1500 It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times.
1501 
1502 Allocations in `pAllocations` array can come from any memory pools and types.
1503 Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped.
1504 */
1505 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
1506     VmaAllocator VMA_NOT_NULL allocator,
1507     size_t allocationCount,
1508     const VmaAllocation VMA_NULLABLE * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations);
1509 
1510 /** \brief Returns current information about specified allocation and atomically marks it as used in current frame.
1511 
1512 Current paramteres of given allocation are returned in `pAllocationInfo`.
1513 
1514 This function also atomically "touches" allocation - marks it as used in current frame,
1515 just like vmaTouchAllocation().
1516 If the allocation is in lost state, `pAllocationInfo->deviceMemory == VK_NULL_HANDLE`.
1517 
1518 Although this function uses atomics and doesn't lock any mutex, so it should be quite efficient,
1519 you can avoid calling it too often.
1520 
1521 - You can retrieve same VmaAllocationInfo structure while creating your resource, from function
1522   vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change
1523   (e.g. due to defragmentation or allocation becoming lost).
1524 - If you just want to check if allocation is not lost, vmaTouchAllocation() will work faster.
1525 */
1526 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
1527     VmaAllocator VMA_NOT_NULL allocator,
1528     VmaAllocation VMA_NOT_NULL allocation,
1529     VmaAllocationInfo* VMA_NOT_NULL pAllocationInfo);
1530 
1531 /** \brief Returns `VK_TRUE` if allocation is not lost and atomically marks it as used in current frame.
1532 
1533 If the allocation has been created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
1534 this function returns `VK_TRUE` if it is not in lost state, so it can still be used.
1535 It then also atomically "touches" the allocation - marks it as used in current frame,
1536 so that you can be sure it won't become lost in current frame or next `frameInUseCount` frames.
1537 
1538 If the allocation is in lost state, the function returns `VK_FALSE`.
1539 Memory of such allocation, as well as buffer or image bound to it, should not be used.
1540 Lost allocation and the buffer/image still need to be destroyed.
1541 
1542 If the allocation has been created without #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
1543 this function always returns `VK_TRUE`.
1544 */
1545 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
1546     VmaAllocator VMA_NOT_NULL allocator,
1547     VmaAllocation VMA_NOT_NULL allocation);
1548 
1549 /** \brief Sets pUserData in given allocation to new value.
1550 
1551 If the allocation was created with VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT,
1552 pUserData must be either null, or pointer to a null-terminated string. The function
1553 makes local copy of the string and sets it as allocation's `pUserData`. String
1554 passed as pUserData doesn't need to be valid for whole lifetime of the allocation -
1555 you can free it after this call. String previously pointed by allocation's
1556 pUserData is freed from memory.
1557 
1558 If the flag was not used, the value of pointer `pUserData` is just copied to
1559 allocation's `pUserData`. It is opaque, so you can use it however you want - e.g.
1560 as a pointer, ordinal number or some handle to you own data.
1561 */
1562 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
1563     VmaAllocator VMA_NOT_NULL allocator,
1564     VmaAllocation VMA_NOT_NULL allocation,
1565     void* VMA_NULLABLE pUserData);
1566 
1567 /** \brief Creates new allocation that is in lost state from the beginning.
1568 
1569 It can be useful if you need a dummy, non-null allocation.
1570 
1571 You still need to destroy created object using vmaFreeMemory().
1572 
1573 Returned allocation is not tied to any specific memory pool or memory type and
1574 not bound to any image or buffer. It has size = 0. It cannot be turned into
1575 a real, non-empty allocation.
1576 */
1577 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
1578     VmaAllocator VMA_NOT_NULL allocator,
1579     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation);
1580 
1581 /** \brief Maps memory represented by given allocation and returns pointer to it.
1582 
1583 Maps memory represented by given allocation to make it accessible to CPU code.
1584 When succeeded, `*ppData` contains pointer to first byte of this memory.
1585 If the allocation is part of bigger `VkDeviceMemory` block, the pointer is
1586 correctly offsetted to the beginning of region assigned to this particular
1587 allocation.
1588 
1589 Mapping is internally reference-counted and synchronized, so despite raw Vulkan
1590 function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory`
1591 multiple times simultaneously, it is safe to call this function on allocations
1592 assigned to the same memory block. Actual Vulkan memory will be mapped on first
1593 mapping and unmapped on last unmapping.
1594 
1595 If the function succeeded, you must call vmaUnmapMemory() to unmap the
1596 allocation when mapping is no longer needed or before freeing the allocation, at
1597 the latest.
1598 
1599 It also safe to call this function multiple times on the same allocation. You
1600 must call vmaUnmapMemory() same number of times as you called vmaMapMemory().
1601 
1602 It is also safe to call this function on allocation created with
1603 #VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time.
1604 You must still call vmaUnmapMemory() same number of times as you called
1605 vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the
1606 "0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag.
1607 
1608 This function fails when used on allocation made in memory type that is not
1609 `HOST_VISIBLE`.
1610 
1611 This function always fails when called for allocation that was created with
1612 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocations cannot be
1613 mapped.
1614 
1615 This function doesn't automatically flush or invalidate caches.
1616 If the allocation is made from a memory types that is not `HOST_COHERENT`,
1617 you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification.
1618 */
1619 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
1620     VmaAllocator VMA_NOT_NULL allocator,
1621     VmaAllocation VMA_NOT_NULL allocation,
1622     void* VMA_NULLABLE * VMA_NOT_NULL ppData);
1623 
1624 /** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
1625 
1626 For details, see description of vmaMapMemory().
1627 
1628 This function doesn't automatically flush or invalidate caches.
1629 If the allocation is made from a memory types that is not `HOST_COHERENT`,
1630 you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification.
1631 */
1632 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
1633     VmaAllocator VMA_NOT_NULL allocator,
1634     VmaAllocation VMA_NOT_NULL allocation);
1635 
1636 /** \brief Flushes memory of given allocation.
1637 
1638 Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation.
1639 It needs to be called after writing to a mapped memory for memory types that are not `HOST_COHERENT`.
1640 Unmap operation doesn't do that automatically.
1641 
1642 - `offset` must be relative to the beginning of allocation.
1643 - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
1644 - `offset` and `size` don't have to be aligned.
1645   They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
1646 - If `size` is 0, this call is ignored.
1647 - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
1648   this call is ignored.
1649 
1650 Warning! `offset` and `size` are relative to the contents of given `allocation`.
1651 If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively.
1652 Do not pass allocation's offset as `offset`!!!
1653 
1654 This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is
1655 called, otherwise `VK_SUCCESS`.
1656 */
1657 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(
1658     VmaAllocator VMA_NOT_NULL allocator,
1659     VmaAllocation VMA_NOT_NULL allocation,
1660     VkDeviceSize offset,
1661     VkDeviceSize size);
1662 
1663 /** \brief Invalidates memory of given allocation.
1664 
1665 Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation.
1666 It needs to be called before reading from a mapped memory for memory types that are not `HOST_COHERENT`.
1667 Map operation doesn't do that automatically.
1668 
1669 - `offset` must be relative to the beginning of allocation.
1670 - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
1671 - `offset` and `size` don't have to be aligned.
1672   They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
1673 - If `size` is 0, this call is ignored.
1674 - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
1675   this call is ignored.
1676 
1677 Warning! `offset` and `size` are relative to the contents of given `allocation`.
1678 If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively.
1679 Do not pass allocation's offset as `offset`!!!
1680 
1681 This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if
1682 it is called, otherwise `VK_SUCCESS`.
1683 */
1684 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(
1685     VmaAllocator VMA_NOT_NULL allocator,
1686     VmaAllocation VMA_NOT_NULL allocation,
1687     VkDeviceSize offset,
1688     VkDeviceSize size);
1689 
1690 /** \brief Flushes memory of given set of allocations.
1691 
1692 Calls `vkFlushMappedMemoryRanges()` for memory associated with given ranges of given allocations.
1693 For more information, see documentation of vmaFlushAllocation().
1694 
1695 \param allocator
1696 \param allocationCount
1697 \param allocations
1698 \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.
1699 \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.
1700 
1701 This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is
1702 called, otherwise `VK_SUCCESS`.
1703 */
1704 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
1705     VmaAllocator VMA_NOT_NULL allocator,
1706     uint32_t allocationCount,
1707     const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
1708     const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
1709     const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
1710 
1711 /** \brief Invalidates memory of given set of allocations.
1712 
1713 Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given ranges of given allocations.
1714 For more information, see documentation of vmaInvalidateAllocation().
1715 
1716 \param allocator
1717 \param allocationCount
1718 \param allocations
1719 \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.
1720 \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.
1721 
1722 This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if it is
1723 called, otherwise `VK_SUCCESS`.
1724 */
1725 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
1726     VmaAllocator VMA_NOT_NULL allocator,
1727     uint32_t allocationCount,
1728     const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
1729     const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
1730     const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
1731 
1732 /** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions.
1733 
1734 @param allocator
1735 @param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked.
1736 
1737 Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
1738 `VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are
1739 `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
1740 
1741 Possible return values:
1742 
1743 - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types.
1744 - `VK_SUCCESS` - corruption detection has been performed and succeeded.
1745 - `VK_ERROR_UNKNOWN` - corruption detection has been performed and found memory corruptions around one of the allocations.
1746   `VMA_ASSERT` is also fired in that case.
1747 - Other value: Error returned by Vulkan, e.g. memory mapping failure.
1748 */
1749 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator VMA_NOT_NULL allocator, uint32_t memoryTypeBits);
1750 
1751 /** \struct VmaDefragmentationContext
1752 \brief Represents Opaque object that represents started defragmentation process.
1753 
1754 Fill structure #VmaDefragmentationInfo2 and call function vmaDefragmentationBegin() to create it.
1755 Call function vmaDefragmentationEnd() to destroy it.
1756 */
1757 VK_DEFINE_HANDLE(VmaDefragmentationContext)
1758 
1759 /// Flags to be used in vmaDefragmentationBegin(). None at the moment. Reserved for future use.
1760 typedef enum VmaDefragmentationFlagBits {
1761     VMA_DEFRAGMENTATION_FLAG_INCREMENTAL = 0x1,
1762     VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1763 } VmaDefragmentationFlagBits;
1764 typedef VkFlags VmaDefragmentationFlags;
1765 
1766 /** \brief Parameters for defragmentation.
1767 
1768 To be used with function vmaDefragmentationBegin().
1769 */
1770 typedef struct VmaDefragmentationInfo2 {
1771     /** \brief Reserved for future use. Should be 0.
1772     */
1773     VmaDefragmentationFlags flags;
1774     /** \brief Number of allocations in `pAllocations` array.
1775     */
1776     uint32_t allocationCount;
1777     /** \brief Pointer to array of allocations that can be defragmented.
1778 
1779     The array should have `allocationCount` elements.
1780     The array should not contain nulls.
1781     Elements in the array should be unique - same allocation cannot occur twice.
1782     It is safe to pass allocations that are in the lost state - they are ignored.
1783     All allocations not present in this array are considered non-moveable during this defragmentation.
1784     */
1785     const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations;
1786     /** \brief Optional, output. Pointer to array that will be filled with information whether the allocation at certain index has been changed during defragmentation.
1787 
1788     The array should have `allocationCount` elements.
1789     You can pass null if you are not interested in this information.
1790     */
1791     VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged;
1792     /** \brief Numer of pools in `pPools` array.
1793     */
1794     uint32_t poolCount;
1795     /** \brief Either null or pointer to array of pools to be defragmented.
1796 
1797     All the allocations in the specified pools can be moved during defragmentation
1798     and there is no way to check if they were really moved as in `pAllocationsChanged`,
1799     so you must query all the allocations in all these pools for new `VkDeviceMemory`
1800     and offset using vmaGetAllocationInfo() if you might need to recreate buffers
1801     and images bound to them.
1802 
1803     The array should have `poolCount` elements.
1804     The array should not contain nulls.
1805     Elements in the array should be unique - same pool cannot occur twice.
1806 
1807     Using this array is equivalent to specifying all allocations from the pools in `pAllocations`.
1808     It might be more efficient.
1809     */
1810     const VmaPool VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(poolCount) pPools;
1811     /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on CPU side, like `memcpy()`, `memmove()`.
1812 
1813     `VK_WHOLE_SIZE` means no limit.
1814     */
1815     VkDeviceSize maxCpuBytesToMove;
1816     /** \brief Maximum number of allocations that can be moved to a different place using transfers on CPU side, like `memcpy()`, `memmove()`.
1817 
1818     `UINT32_MAX` means no limit.
1819     */
1820     uint32_t maxCpuAllocationsToMove;
1821     /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on GPU side, posted to `commandBuffer`.
1822 
1823     `VK_WHOLE_SIZE` means no limit.
1824     */
1825     VkDeviceSize maxGpuBytesToMove;
1826     /** \brief Maximum number of allocations that can be moved to a different place using transfers on GPU side, posted to `commandBuffer`.
1827 
1828     `UINT32_MAX` means no limit.
1829     */
1830     uint32_t maxGpuAllocationsToMove;
1831     /** \brief Optional. Command buffer where GPU copy commands will be posted.
1832 
1833     If not null, it must be a valid command buffer handle that supports Transfer queue type.
1834     It must be in the recording state and outside of a render pass instance.
1835     You need to submit it and make sure it finished execution before calling vmaDefragmentationEnd().
1836 
1837     Passing null means that only CPU defragmentation will be performed.
1838     */
1839     VkCommandBuffer VMA_NULLABLE commandBuffer;
1840 } VmaDefragmentationInfo2;
1841 
1842 typedef struct VmaDefragmentationPassMoveInfo {
1843     VmaAllocation VMA_NOT_NULL allocation;
1844     VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory;
1845     VkDeviceSize offset;
1846 } VmaDefragmentationPassMoveInfo;
1847 
1848 /** \brief Parameters for incremental defragmentation steps.
1849 
1850 To be used with function vmaBeginDefragmentationPass().
1851 */
1852 typedef struct VmaDefragmentationPassInfo {
1853     uint32_t moveCount;
1854     VmaDefragmentationPassMoveInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(moveCount) pMoves;
1855 } VmaDefragmentationPassInfo;
1856 
1857 /** \brief Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
1858 
1859 \deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
1860 */
1861 typedef struct VmaDefragmentationInfo {
1862     /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places.
1863 
1864     Default is `VK_WHOLE_SIZE`, which means no limit.
1865     */
1866     VkDeviceSize maxBytesToMove;
1867     /** \brief Maximum number of allocations that can be moved to different place.
1868 
1869     Default is `UINT32_MAX`, which means no limit.
1870     */
1871     uint32_t maxAllocationsToMove;
1872 } VmaDefragmentationInfo;
1873 
1874 /** \brief Statistics returned by function vmaDefragment(). */
1875 typedef struct VmaDefragmentationStats {
1876     /// Total number of bytes that have been copied while moving allocations to different places.
1877     VkDeviceSize bytesMoved;
1878     /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects.
1879     VkDeviceSize bytesFreed;
1880     /// Number of allocations that have been moved to different places.
1881     uint32_t allocationsMoved;
1882     /// Number of empty `VkDeviceMemory` objects that have been released to the system.
1883     uint32_t deviceMemoryBlocksFreed;
1884 } VmaDefragmentationStats;
1885 
1886 /** \brief Begins defragmentation process.
1887 
1888 @param allocator Allocator object.
1889 @param pInfo Structure filled with parameters of defragmentation.
1890 @param[out] pStats Optional. Statistics of defragmentation. You can pass null if you are not interested in this information.
1891 @param[out] pContext Context object that must be passed to vmaDefragmentationEnd() to finish defragmentation.
1892 @return `VK_SUCCESS` and `*pContext == null` if defragmentation finished within this function call. `VK_NOT_READY` and `*pContext != null` if defragmentation has been started and you need to call vmaDefragmentationEnd() to finish it. Negative value in case of error.
1893 
1894 Use this function instead of old, deprecated vmaDefragment().
1895 
1896 Warning! Between the call to vmaDefragmentationBegin() and vmaDefragmentationEnd():
1897 
1898 - You should not use any of allocations passed as `pInfo->pAllocations` or
1899   any allocations that belong to pools passed as `pInfo->pPools`,
1900   including calling vmaGetAllocationInfo(), vmaTouchAllocation(), or access
1901   their data.
1902 - Some mutexes protecting internal data structures may be locked, so trying to
1903   make or free any allocations, bind buffers or images, map memory, or launch
1904   another simultaneous defragmentation in between may cause stall (when done on
1905   another thread) or deadlock (when done on the same thread), unless you are
1906   100% sure that defragmented allocations are in different pools.
1907 - Information returned via `pStats` and `pInfo->pAllocationsChanged` are undefined.
1908   They become valid after call to vmaDefragmentationEnd().
1909 - If `pInfo->commandBuffer` is not null, you must submit that command buffer
1910   and make sure it finished execution before calling vmaDefragmentationEnd().
1911 
1912 For more information and important limitations regarding defragmentation, see documentation chapter:
1913 [Defragmentation](@ref defragmentation).
1914 */
1915 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
1916     VmaAllocator VMA_NOT_NULL allocator,
1917     const VmaDefragmentationInfo2* VMA_NOT_NULL pInfo,
1918     VmaDefragmentationStats* VMA_NULLABLE pStats,
1919     VmaDefragmentationContext VMA_NULLABLE * VMA_NOT_NULL pContext);
1920 
1921 /** \brief Ends defragmentation process.
1922 
1923 Use this function to finish defragmentation started by vmaDefragmentationBegin().
1924 It is safe to pass `context == null`. The function then does nothing.
1925 */
1926 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
1927     VmaAllocator VMA_NOT_NULL allocator,
1928     VmaDefragmentationContext VMA_NULLABLE context);
1929 
1930 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
1931     VmaAllocator VMA_NOT_NULL allocator,
1932     VmaDefragmentationContext VMA_NULLABLE context,
1933     VmaDefragmentationPassInfo* VMA_NOT_NULL pInfo
1934 );
1935 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
1936     VmaAllocator VMA_NOT_NULL allocator,
1937     VmaDefragmentationContext VMA_NULLABLE context
1938 );
1939 
1940 /** \brief Deprecated. Compacts memory by moving allocations.
1941 
1942 @param allocator
1943 @param pAllocations Array of allocations that can be moved during this compation.
1944 @param allocationCount Number of elements in pAllocations and pAllocationsChanged arrays.
1945 @param[out] pAllocationsChanged Array of boolean values that will indicate whether matching allocation in pAllocations array has been moved. This parameter is optional. Pass null if you don't need this information.
1946 @param pDefragmentationInfo Configuration parameters. Optional - pass null to use default values.
1947 @param[out] pDefragmentationStats Statistics returned by the function. Optional - pass null if you don't need this information.
1948 @return `VK_SUCCESS` if completed, negative error code in case of error.
1949 
1950 \deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
1951 
1952 This function works by moving allocations to different places (different
1953 `VkDeviceMemory` objects and/or different offsets) in order to optimize memory
1954 usage. Only allocations that are in `pAllocations` array can be moved. All other
1955 allocations are considered nonmovable in this call. Basic rules:
1956 
1957 - Only allocations made in memory types that have
1958   `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`
1959   flags can be compacted. You may pass other allocations but it makes no sense -
1960   these will never be moved.
1961 - Custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT or
1962   #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag are not defragmented. Allocations
1963   passed to this function that come from such pools are ignored.
1964 - Allocations created with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT or
1965   created as dedicated allocations for any other reason are also ignored.
1966 - Both allocations made with or without #VMA_ALLOCATION_CREATE_MAPPED_BIT
1967   flag can be compacted. If not persistently mapped, memory will be mapped
1968   temporarily inside this function if needed.
1969 - You must not pass same #VmaAllocation object multiple times in `pAllocations` array.
1970 
1971 The function also frees empty `VkDeviceMemory` blocks.
1972 
1973 Warning: This function may be time-consuming, so you shouldn't call it too often
1974 (like after every resource creation/destruction).
1975 You can call it on special occasions (like when reloading a game level or
1976 when you just destroyed a lot of objects). Calling it every frame may be OK, but
1977 you should measure that on your platform.
1978 
1979 For more information, see [Defragmentation](@ref defragmentation) chapter.
1980 */
1981 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
1982     VmaAllocator VMA_NOT_NULL allocator,
1983     const VmaAllocation VMA_NOT_NULL * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
1984     size_t allocationCount,
1985     VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged,
1986     const VmaDefragmentationInfo* VMA_NULLABLE pDefragmentationInfo,
1987     VmaDefragmentationStats* VMA_NULLABLE pDefragmentationStats);
1988 
1989 /** \brief Binds buffer to allocation.
1990 
1991 Binds specified buffer to region of memory represented by specified allocation.
1992 Gets `VkDeviceMemory` handle and offset from the allocation.
1993 If you want to create a buffer, allocate memory for it and bind them together separately,
1994 you should use this function for binding instead of standard `vkBindBufferMemory()`,
1995 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
1996 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
1997 (which is illegal in Vulkan).
1998 
1999 It is recommended to use function vmaCreateBuffer() instead of this one.
2000 */
2001 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
2002     VmaAllocator VMA_NOT_NULL allocator,
2003     VmaAllocation VMA_NOT_NULL allocation,
2004     VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer);
2005 
2006 /** \brief Binds buffer to allocation with additional parameters.
2007 
2008 @param allocator
2009 @param allocation
2010 @param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0.
2011 @param buffer
2012 @param pNext A chain of structures to be attached to `VkBindBufferMemoryInfoKHR` structure used internally. Normally it should be null.
2013 
2014 This function is similar to vmaBindBufferMemory(), but it provides additional parameters.
2015 
2016 If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag
2017 or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails.
2018 */
2019 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
2020     VmaAllocator VMA_NOT_NULL allocator,
2021     VmaAllocation VMA_NOT_NULL allocation,
2022     VkDeviceSize allocationLocalOffset,
2023     VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
2024     const void* VMA_NULLABLE pNext);
2025 
2026 /** \brief Binds image to allocation.
2027 
2028 Binds specified image to region of memory represented by specified allocation.
2029 Gets `VkDeviceMemory` handle and offset from the allocation.
2030 If you want to create an image, allocate memory for it and bind them together separately,
2031 you should use this function for binding instead of standard `vkBindImageMemory()`,
2032 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
2033 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
2034 (which is illegal in Vulkan).
2035 
2036 It is recommended to use function vmaCreateImage() instead of this one.
2037 */
2038 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
2039     VmaAllocator VMA_NOT_NULL allocator,
2040     VmaAllocation VMA_NOT_NULL allocation,
2041     VkImage VMA_NOT_NULL_NON_DISPATCHABLE image);
2042 
2043 /** \brief Binds image to allocation with additional parameters.
2044 
2045 @param allocator
2046 @param allocation
2047 @param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0.
2048 @param image
2049 @param pNext A chain of structures to be attached to `VkBindImageMemoryInfoKHR` structure used internally. Normally it should be null.
2050 
2051 This function is similar to vmaBindImageMemory(), but it provides additional parameters.
2052 
2053 If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag
2054 or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails.
2055 */
2056 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
2057     VmaAllocator VMA_NOT_NULL allocator,
2058     VmaAllocation VMA_NOT_NULL allocation,
2059     VkDeviceSize allocationLocalOffset,
2060     VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
2061     const void* VMA_NULLABLE pNext);
2062 
2063 /**
2064 @param allocator
2065 @param pBufferCreateInfo
2066 @param pAllocationCreateInfo
2067 @param[out] pBuffer Buffer that was created.
2068 @param[out] pAllocation Allocation that was created.
2069 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
2070 
2071 This function automatically:
2072 
2073 -# Creates buffer.
2074 -# Allocates appropriate memory for it.
2075 -# Binds the buffer with the memory.
2076 
2077 If any of these operations fail, buffer and allocation are not created,
2078 returned value is negative error code, *pBuffer and *pAllocation are null.
2079 
2080 If the function succeeded, you must destroy both buffer and allocation when you
2081 no longer need them using either convenience function vmaDestroyBuffer() or
2082 separately, using `vkDestroyBuffer()` and vmaFreeMemory().
2083 
2084 If #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used,
2085 VK_KHR_dedicated_allocation extension is used internally to query driver whether
2086 it requires or prefers the new buffer to have dedicated allocation. If yes,
2087 and if dedicated allocation is possible (VmaAllocationCreateInfo::pool is null
2088 and #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated
2089 allocation for this buffer, just like when using
2090 #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
2091 
2092 \note This function creates a new `VkBuffer`. Sub-allocation of parts of one large buffer,
2093 although recommended as a good practice, is out of scope of this library and could be implemented
2094 by the user as a higher-level logic on top of VMA.
2095 */
2096 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
2097     VmaAllocator VMA_NOT_NULL allocator,
2098     const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
2099     const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2100     VkBuffer VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pBuffer,
2101     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
2102     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
2103 
2104 /** \brief Creates a buffer with additional minimum alignment.
2105 
2106 Similar to vmaCreateBuffer() but provides additional parameter `minAlignment` which allows to specify custom,
2107 minimum alignment to be used when placing the buffer inside a larger memory block, which may be needed e.g.
2108 for interop with OpenGL.
2109 */
2110 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment(
2111     VmaAllocator VMA_NOT_NULL allocator,
2112     const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
2113     const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2114     VkDeviceSize minAlignment,
2115     VkBuffer VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pBuffer,
2116     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
2117     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
2118 
2119 /** \brief Destroys Vulkan buffer and frees allocated memory.
2120 
2121 This is just a convenience function equivalent to:
2122 
2123 \code
2124 vkDestroyBuffer(device, buffer, allocationCallbacks);
2125 vmaFreeMemory(allocator, allocation);
2126 \endcode
2127 
2128 It it safe to pass null as buffer and/or allocation.
2129 */
2130 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
2131     VmaAllocator VMA_NOT_NULL allocator,
2132     VkBuffer VMA_NULLABLE_NON_DISPATCHABLE buffer,
2133     VmaAllocation VMA_NULLABLE allocation);
2134 
2135 /// Function similar to vmaCreateBuffer().
2136 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
2137     VmaAllocator VMA_NOT_NULL allocator,
2138     const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
2139     const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2140     VkImage VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pImage,
2141     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
2142     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
2143 
2144 /** \brief Destroys Vulkan image and frees allocated memory.
2145 
2146 This is just a convenience function equivalent to:
2147 
2148 \code
2149 vkDestroyImage(device, image, allocationCallbacks);
2150 vmaFreeMemory(allocator, allocation);
2151 \endcode
2152 
2153 It it safe to pass null as image and/or allocation.
2154 */
2155 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
2156     VmaAllocator VMA_NOT_NULL allocator,
2157     VkImage VMA_NULLABLE_NON_DISPATCHABLE image,
2158     VmaAllocation VMA_NULLABLE allocation);
2159 
2160 /// Flags to be passed as VmaVirtualBlockCreateInfo::flags.
2161 typedef enum VmaVirtualBlockCreateFlagBits {
2162     /** \brief Enables alternative, linear allocation algorithm in this virtual block.
2163 
2164     Specify this flag to enable linear allocation algorithm, which always creates
2165     new allocations after last one and doesn't reuse space from allocations freed in
2166     between. It trades memory consumption for simplified algorithm and data
2167     structure, which has better performance and uses less memory for metadata.
2168 
2169     By using this flag, you can achieve behavior of free-at-once, stack,
2170     ring buffer, and double stack.
2171     For details, see documentation chapter \ref linear_algorithm.
2172     */
2173     VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT = 0x00000001,
2174 
2175     /** \brief Enables alternative, buddy allocation algorithm in this virtual block.
2176 
2177     It operates on a tree of blocks, each having size that is a power of two and
2178     a half of its parent's size. Comparing to default algorithm, this one provides
2179     faster allocation and deallocation and decreased external fragmentation,
2180     at the expense of more memory wasted (internal fragmentation).
2181     For details, see documentation chapter \ref buddy_algorithm.
2182     */
2183     VMA_VIRTUAL_BLOCK_CREATE_BUDDY_ALGORITHM_BIT = 0x00000002,
2184 
2185     /** \brief Bit mask to extract only `ALGORITHM` bits from entire set of flags.
2186     */
2187     VMA_VIRTUAL_BLOCK_CREATE_ALGORITHM_MASK =
2188         VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT |
2189         VMA_VIRTUAL_BLOCK_CREATE_BUDDY_ALGORITHM_BIT,
2190 
2191     VMA_VIRTUAL_BLOCK_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2192 } VmaVirtualBlockCreateFlagBits;
2193 /// Flags to be passed as VmaVirtualBlockCreateInfo::flags. See #VmaVirtualBlockCreateFlagBits.
2194 typedef VkFlags VmaVirtualBlockCreateFlags;
2195 
2196 /// Parameters of created #VmaVirtualBlock object to be passed to vmaCreateVirtualBlock().
2197 typedef struct VmaVirtualBlockCreateInfo
2198 {
2199     /** \brief Total size of the virtual block.
2200 
2201     Sizes can be expressed in bytes or any units you want as long as you are consistent in using them.
2202     For example, if you allocate from some array of structures, 1 can mean single instance of entire structure.
2203     */
2204     VkDeviceSize size;
2205 
2206     /** \brief Use combination of #VmaVirtualBlockCreateFlagBits.
2207     */
2208     VmaVirtualBlockCreateFlagBits flags;
2209 
2210     /** \brief Custom CPU memory allocation callbacks. Optional.
2211 
2212     Optional, can be null. When specified, they will be used for all CPU-side memory allocations.
2213     */
2214     const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks;
2215 } VmaVirtualBlockCreateInfo;
2216 
2217 /// Flags to be passed as VmaVirtualAllocationCreateInfo::flags.
2218 typedef enum VmaVirtualAllocationCreateFlagBits {
2219     /** \brief Allocation will be created from upper stack in a double stack pool.
2220 
2221     This flag is only allowed for virtual blocks created with #VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT flag.
2222     */
2223     VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT,
2224     /** \brief Allocation strategy that tries to minimize memory usage.
2225     */
2226     VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT,
2227     /** \brief Allocation strategy that tries to minimize allocation time.
2228     */
2229     VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT,
2230     /** \brief Allocation strategy that tries to minimize memory fragmentation.
2231     */
2232     VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT,
2233     /** \brief A bit mask to extract only `STRATEGY` bits from entire set of flags.
2234 
2235     These stategy flags are binary compatible with equivalent flags in #VmaAllocationCreateFlagBits.
2236     */
2237     VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK = VMA_ALLOCATION_CREATE_STRATEGY_MASK,
2238 
2239     VMA_VIRTUAL_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2240 } VmaVirtualAllocationCreateFlagBits;
2241 /// Flags to be passed as VmaVirtualAllocationCreateInfo::flags. See #VmaVirtualAllocationCreateFlagBits.
2242 typedef VkFlags VmaVirtualAllocationCreateFlags;
2243 
2244 /// Parameters of created virtual allocation to be passed to vmaVirtualAllocate().
2245 typedef struct VmaVirtualAllocationCreateInfo
2246 {
2247     /** \brief Size of the allocation.
2248 
2249     Cannot be zero.
2250     */
2251     VkDeviceSize size;
2252     /** \brief Required alignment of the allocation. Optional.
2253 
2254     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.
2255     */
2256     VkDeviceSize alignment;
2257     /** \brief Use combination of #VmaVirtualAllocationCreateFlagBits.
2258     */
2259     VmaVirtualAllocationCreateFlags flags;
2260     /** \brief Custom pointer to be associated with the allocation. Optional.
2261 
2262     It can be any value and can be used for user-defined purposes. It can be fetched or changed later.
2263     */
2264     void* VMA_NULLABLE pUserData;
2265 } VmaVirtualAllocationCreateInfo;
2266 
2267 /// Parameters of an existing virtual allocation, returned by vmaGetVirtualAllocationInfo().
2268 typedef struct VmaVirtualAllocationInfo
2269 {
2270     /** \brief Size of the allocation.
2271 
2272     Same value as passed in VmaVirtualAllocationCreateInfo::size.
2273     */
2274     VkDeviceSize size;
2275     /** \brief Custom pointer associated with the allocation.
2276 
2277     Same value as passed in VmaVirtualAllocationCreateInfo::pUserData or to vmaSetVirtualAllocationUserData().
2278     */
2279     void* VMA_NULLABLE pUserData;
2280 } VmaVirtualAllocationInfo;
2281 
2282 /** \struct VmaVirtualBlock
2283 \brief Handle to a virtual block object that allows to use core allocation algorithm without allocating any real GPU memory.
2284 
2285 Fill in #VmaVirtualBlockCreateInfo structure and Use vmaCreateVirtualBlock() to create it. Use vmaDestroyVirtualBlock() to destroy it.
2286 For more information, see documentation chapter \ref virtual_allocator.
2287 */
2288 VK_DEFINE_HANDLE(VmaVirtualBlock);
2289 
2290 /** \brief Creates new #VmaVirtualBlock object.
2291 
2292 \param pCreateInfo Parameters for creation.
2293 \param[out] pVirtualBlock Returned virtual block object or `VMA_NULL` if creation failed.
2294 */
2295 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock(
2296     const VmaVirtualBlockCreateInfo* VMA_NOT_NULL pCreateInfo,
2297     VmaVirtualBlock VMA_NULLABLE * VMA_NOT_NULL pVirtualBlock);
2298 
2299 /** \brief Destroys #VmaVirtualBlock object.
2300 
2301 Please note that you should consciously handle virtual allocations that could remain unfreed in the block.
2302 You should either free them individually using vmaVirtualFree() or call vmaClearVirtualBlock()
2303 if you are sure this is what you want. If you do neither, an assert is called.
2304 
2305 If you keep pointers to some additional metadata associated with your virtual allocations in their `pUserData`,
2306 don't forget to free them.
2307 */
2308 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyVirtualBlock(VmaVirtualBlock VMA_NULLABLE virtualBlock);
2309 
2310 /** \brief Returns true of the #VmaVirtualBlock is empty - contains 0 virtual allocations and has all its space available for new allocations.
2311 */
2312 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaIsVirtualBlockEmpty(VmaVirtualBlock VMA_NOT_NULL virtualBlock);
2313 
2314 /** \brief Returns information about a specific virtual allocation within a virtual block, like its size and `pUserData` pointer.
2315 */
2316 VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2317     VkDeviceSize offset, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo);
2318 
2319 /** \brief Allocates new virtual allocation inside given #VmaVirtualBlock.
2320 
2321 There is no handle type for a virtual allocation.
2322 Virtual allocations within a specific virtual block are uniquely identified by their offsets.
2323 
2324 If the allocation fails due to not enough free space available, `VK_ERROR_OUT_OF_DEVICE_MEMORY` is returned
2325 (despite the function doesn't ever allocate actual GPU memory).
2326 */
2327 VMA_CALL_PRE VkResult VMA_CALL_POST vmaVirtualAllocate(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2328     const VmaVirtualAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, VkDeviceSize* VMA_NOT_NULL pOffset);
2329 
2330 /** \brief Frees virtual allocation inside given #VmaVirtualBlock.
2331 */
2332 VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree(VmaVirtualBlock VMA_NOT_NULL virtualBlock, VkDeviceSize offset);
2333 
2334 /** \brief Frees all virtual allocations inside given #VmaVirtualBlock.
2335 
2336 You must either call this function or free each virtual allocation individually with vmaVirtualFree()
2337 before destroying a virtual block. Otherwise, an assert is called.
2338 
2339 If you keep pointer to some additional metadata associated with your virtual allocation in its `pUserData`,
2340 don't forget to free it as well.
2341 */
2342 VMA_CALL_PRE void VMA_CALL_POST vmaClearVirtualBlock(VmaVirtualBlock VMA_NOT_NULL virtualBlock);
2343 
2344 /** \brief Changes custom pointer associated with given virtual allocation.
2345 */
2346 VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2347     VkDeviceSize offset, void* VMA_NULLABLE pUserData);
2348 
2349 /** \brief Calculates and returns statistics about virtual allocations and memory usage in given #VmaVirtualBlock.
2350 */
2351 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateVirtualBlockStats(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2352     VmaStatInfo* VMA_NOT_NULL pStatInfo);
2353 
2354 /** \brief Builds and returns a null-terminated string in JSON format with information about given #VmaVirtualBlock.
2355 \param virtualBlock Virtual block.
2356 \param[out] ppStatsString Returned string.
2357 \param detailedMap Pass `VK_FALSE` to only obtain statistics as returned by vmaCalculateVirtualBlockStats(). Pass `VK_TRUE` to also obtain full list of allocations and free spaces.
2358 
2359 Returned string must be freed using vmaFreeVirtualBlockStatsString().
2360 */
2361 VMA_CALL_PRE void VMA_CALL_POST vmaBuildVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2362     char* VMA_NULLABLE * VMA_NOT_NULL ppStatsString, VkBool32 detailedMap);
2363 
2364 /** \brief Frees a string returned by vmaBuildVirtualBlockStatsString().
2365 */
2366 VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2367     char* VMA_NULLABLE pStatsString);
2368 
2369 #ifdef __cplusplus
2370 }
2371 #endif
2372 
2373 #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
2374 
2375 ////////////////////////////////////////////////////////////////////////////////
2376 ////////////////////////////////////////////////////////////////////////////////
2377 //
2378 //    IMPLEMENTATION
2379 //
2380 ////////////////////////////////////////////////////////////////////////////////
2381 ////////////////////////////////////////////////////////////////////////////////
2382 
2383 // For Visual Studio IntelliSense.
2384 #if defined(__cplusplus) && defined(__INTELLISENSE__)
2385 #define VMA_IMPLEMENTATION
2386 #endif
2387 
2388 #ifdef VMA_IMPLEMENTATION
2389 #undef VMA_IMPLEMENTATION
2390 
2391 #include <cstdint>
2392 #include <cstdlib>
2393 #include <cstring>
2394 #include <utility>
2395 
2396 #if VMA_RECORDING_ENABLED
2397     #include <chrono>
2398     #if defined(_WIN32)
2399         #include <windows.h>
2400     #else
2401         #include <sstream>
2402         #include <thread>
2403     #endif
2404 #endif
2405 
2406 /*******************************************************************************
2407 CONFIGURATION SECTION
2408 
2409 Define some of these macros before each #include of this header or change them
2410 here if you need other then default behavior depending on your environment.
2411 */
2412 
2413 /*
2414 Define this macro to 1 to make the library fetch pointers to Vulkan functions
2415 internally, like:
2416 
2417     vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
2418 */
2419 #if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
2420     #define VMA_STATIC_VULKAN_FUNCTIONS 1
2421 #endif
2422 
2423 /*
2424 Define this macro to 1 to make the library fetch pointers to Vulkan functions
2425 internally, like:
2426 
2427     vulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkGetDeviceProcAddr(m_hDevice, vkAllocateMemory);
2428 */
2429 #if !defined(VMA_DYNAMIC_VULKAN_FUNCTIONS)
2430     #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
2431     #if defined(VK_NO_PROTOTYPES)
2432         extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
2433         extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
2434     #endif
2435 #endif
2436 
2437 // Define this macro to 1 to make the library use STL containers instead of its own implementation.
2438 //#define VMA_USE_STL_CONTAINERS 1
2439 
2440 /* Set this macro to 1 to make the library including and using STL containers:
2441 std::pair, std::vector, std::list, std::unordered_map.
2442 
2443 Set it to 0 or undefined to make the library using its own implementation of
2444 the containers.
2445 */
2446 #if VMA_USE_STL_CONTAINERS
2447    #define VMA_USE_STL_VECTOR 1
2448    #define VMA_USE_STL_UNORDERED_MAP 1
2449    #define VMA_USE_STL_LIST 1
2450 #endif
2451 
2452 #ifndef VMA_USE_STL_SHARED_MUTEX
2453     // Compiler conforms to C++17.
2454     #if __cplusplus >= 201703L
2455         #define VMA_USE_STL_SHARED_MUTEX 1
2456     // Visual studio defines __cplusplus properly only when passed additional parameter: /Zc:__cplusplus
2457     // Otherwise it is always 199711L, despite shared_mutex works since Visual Studio 2015 Update 2.
2458     #elif defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && __cplusplus == 199711L && _MSVC_LANG >= 201703L
2459         #define VMA_USE_STL_SHARED_MUTEX 1
2460     #else
2461         #define VMA_USE_STL_SHARED_MUTEX 0
2462     #endif
2463 #endif
2464 
2465 /*
2466 THESE INCLUDES ARE NOT ENABLED BY DEFAULT.
2467 Library has its own container implementation.
2468 */
2469 #if VMA_USE_STL_VECTOR
2470    #include <vector>
2471 #endif
2472 
2473 #if VMA_USE_STL_UNORDERED_MAP
2474    #include <unordered_map>
2475 #endif
2476 
2477 #if VMA_USE_STL_LIST
2478    #include <list>
2479 #endif
2480 
2481 /*
2482 Following headers are used in this CONFIGURATION section only, so feel free to
2483 remove them if not needed.
2484 */
2485 #include <cassert> // for assert
2486 #include <algorithm> // for min, max
2487 #include <mutex>
2488 
2489 #ifndef VMA_NULL
2490    // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
2491    #define VMA_NULL   nullptr
2492 #endif
2493 
2494 #if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
2495 #include <cstdlib>
vma_aligned_alloc(size_t alignment,size_t size)2496 void *vma_aligned_alloc(size_t alignment, size_t size)
2497 {
2498     // alignment must be >= sizeof(void*)
2499     if(alignment < sizeof(void*))
2500     {
2501         alignment = sizeof(void*);
2502     }
2503 
2504     return memalign(alignment, size);
2505 }
2506 #elif defined(__APPLE__) || defined(__ANDROID__) || (defined(__linux__) && defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC))
2507 #include <cstdlib>
2508 
2509 #if defined(__APPLE__)
2510 #include <AvailabilityMacros.h>
2511 #endif
2512 
vma_aligned_alloc(size_t alignment,size_t size)2513 void *vma_aligned_alloc(size_t alignment, size_t size)
2514 {
2515 #if defined(__APPLE__) && (defined(MAC_OS_X_VERSION_10_16) || defined(__IPHONE_14_0))
2516 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_16 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
2517     // For C++14, usr/include/malloc/_malloc.h declares aligned_alloc()) only
2518     // with the MacOSX11.0 SDK in Xcode 12 (which is what adds
2519     // MAC_OS_X_VERSION_10_16), even though the function is marked
2520     // available for 10.15. That's why the preprocessor checks for 10.16 but
2521     // the __builtin_available checks for 10.15.
2522     // People who use C++17 could call aligned_alloc with the 10.15 SDK already.
2523     if (__builtin_available(macOS 10.15, iOS 13, *))
2524         return aligned_alloc(alignment, size);
2525 #endif
2526 #endif
2527     // alignment must be >= sizeof(void*)
2528     if(alignment < sizeof(void*))
2529     {
2530         alignment = sizeof(void*);
2531     }
2532 
2533     void *pointer;
2534     if(posix_memalign(&pointer, alignment, size) == 0)
2535         return pointer;
2536     return VMA_NULL;
2537 }
2538 #elif defined(_WIN32)
vma_aligned_alloc(size_t alignment,size_t size)2539 void *vma_aligned_alloc(size_t alignment, size_t size)
2540 {
2541     return _aligned_malloc(size, alignment);
2542 }
2543 #else
vma_aligned_alloc(size_t alignment,size_t size)2544 void *vma_aligned_alloc(size_t alignment, size_t size)
2545 {
2546     return aligned_alloc(alignment, size);
2547 }
2548 #endif
2549 
2550 #if defined(_WIN32)
vma_aligned_free(void * ptr)2551 static void vma_aligned_free(void* ptr)
2552 {
2553     _aligned_free(ptr);
2554 }
2555 #else
vma_aligned_free(void * VMA_NULLABLE ptr)2556 static void vma_aligned_free(void* VMA_NULLABLE ptr)
2557 {
2558     free(ptr);
2559 }
2560 #endif
2561 
2562 // If your compiler is not compatible with C++11 and definition of
2563 // aligned_alloc() function is missing, uncommeting following line may help:
2564 
2565 //#include <malloc.h>
2566 
2567 // Normal assert to check for programmer's errors, especially in Debug configuration.
2568 #ifndef VMA_ASSERT
2569    #ifdef NDEBUG
2570        #define VMA_ASSERT(expr)
2571    #else
2572        #define VMA_ASSERT(expr)         assert(expr)
2573    #endif
2574 #endif
2575 
2576 // Assert that will be called very often, like inside data structures e.g. operator[].
2577 // Making it non-empty can make program slow.
2578 #ifndef VMA_HEAVY_ASSERT
2579    #ifdef NDEBUG
2580        #define VMA_HEAVY_ASSERT(expr)
2581    #else
2582        #define VMA_HEAVY_ASSERT(expr)   //VMA_ASSERT(expr)
2583    #endif
2584 #endif
2585 
2586 #ifndef VMA_ALIGN_OF
2587    #define VMA_ALIGN_OF(type)       (__alignof(type))
2588 #endif
2589 
2590 #ifndef VMA_SYSTEM_ALIGNED_MALLOC
2591    #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) vma_aligned_alloc((alignment), (size))
2592 #endif
2593 
2594 #ifndef VMA_SYSTEM_ALIGNED_FREE
2595    // VMA_SYSTEM_FREE is the old name, but might have been defined by the user
2596    #if defined(VMA_SYSTEM_FREE)
2597       #define VMA_SYSTEM_ALIGNED_FREE(ptr)     VMA_SYSTEM_FREE(ptr)
2598    #else
2599       #define VMA_SYSTEM_ALIGNED_FREE(ptr)     vma_aligned_free(ptr)
2600     #endif
2601 #endif
2602 
2603 #ifndef VMA_MIN
2604    #define VMA_MIN(v1, v2)    ((std::min)((v1), (v2)))
2605 #endif
2606 
2607 #ifndef VMA_MAX
2608    #define VMA_MAX(v1, v2)    ((std::max)((v1), (v2)))
2609 #endif
2610 
2611 #ifndef VMA_SWAP
2612    #define VMA_SWAP(v1, v2)   std::swap((v1), (v2))
2613 #endif
2614 
2615 #ifndef VMA_SORT
2616    #define VMA_SORT(beg, end, cmp)  std::sort(beg, end, cmp)
2617 #endif
2618 
2619 #ifndef VMA_DEBUG_LOG
2620    #define VMA_DEBUG_LOG(format, ...)
2621    /*
2622    #define VMA_DEBUG_LOG(format, ...) do { \
2623        printf(format, __VA_ARGS__); \
2624        printf("\n"); \
2625    } while(false)
2626    */
2627 #endif
2628 
2629 // Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
2630 #if VMA_STATS_STRING_ENABLED
VmaUint32ToStr(char * VMA_NOT_NULL outStr,size_t strLen,uint32_t num)2631     static inline void VmaUint32ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint32_t num)
2632     {
2633         snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
2634     }
VmaUint64ToStr(char * VMA_NOT_NULL outStr,size_t strLen,uint64_t num)2635     static inline void VmaUint64ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint64_t num)
2636     {
2637         snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
2638     }
VmaPtrToStr(char * VMA_NOT_NULL outStr,size_t strLen,const void * ptr)2639     static inline void VmaPtrToStr(char* VMA_NOT_NULL outStr, size_t strLen, const void* ptr)
2640     {
2641         snprintf(outStr, strLen, "%p", ptr);
2642     }
2643 #endif
2644 
2645 #ifndef VMA_MUTEX
2646     class VmaMutex
2647     {
2648     public:
Lock()2649         void Lock() { m_Mutex.lock(); }
Unlock()2650         void Unlock() { m_Mutex.unlock(); }
TryLock()2651         bool TryLock() { return m_Mutex.try_lock(); }
2652     private:
2653         std::mutex m_Mutex;
2654     };
2655     #define VMA_MUTEX VmaMutex
2656 #endif
2657 
2658 // Read-write mutex, where "read" is shared access, "write" is exclusive access.
2659 #ifndef VMA_RW_MUTEX
2660     #if VMA_USE_STL_SHARED_MUTEX
2661         // Use std::shared_mutex from C++17.
2662         #include <shared_mutex>
2663         class VmaRWMutex
2664         {
2665         public:
LockRead()2666             void LockRead() { m_Mutex.lock_shared(); }
UnlockRead()2667             void UnlockRead() { m_Mutex.unlock_shared(); }
TryLockRead()2668             bool TryLockRead() { return m_Mutex.try_lock_shared(); }
LockWrite()2669             void LockWrite() { m_Mutex.lock(); }
UnlockWrite()2670             void UnlockWrite() { m_Mutex.unlock(); }
TryLockWrite()2671             bool TryLockWrite() { return m_Mutex.try_lock(); }
2672         private:
2673             std::shared_mutex m_Mutex;
2674         };
2675         #define VMA_RW_MUTEX VmaRWMutex
2676     #elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600
2677         // Use SRWLOCK from WinAPI.
2678         // Minimum supported client = Windows Vista, server = Windows Server 2008.
2679         class VmaRWMutex
2680         {
2681         public:
VmaRWMutex()2682             VmaRWMutex() { InitializeSRWLock(&m_Lock); }
LockRead()2683             void LockRead() { AcquireSRWLockShared(&m_Lock); }
UnlockRead()2684             void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
TryLockRead()2685             bool TryLockRead() { return TryAcquireSRWLockShared(&m_Lock) != FALSE; }
LockWrite()2686             void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
UnlockWrite()2687             void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
TryLockWrite()2688             bool TryLockWrite() { return TryAcquireSRWLockExclusive(&m_Lock) != FALSE; }
2689         private:
2690             SRWLOCK m_Lock;
2691         };
2692         #define VMA_RW_MUTEX VmaRWMutex
2693     #else
2694         // Less efficient fallback: Use normal mutex.
2695         class VmaRWMutex
2696         {
2697         public:
LockRead()2698             void LockRead() { m_Mutex.Lock(); }
UnlockRead()2699             void UnlockRead() { m_Mutex.Unlock(); }
TryLockRead()2700             bool TryLockRead() { return m_Mutex.TryLock(); }
LockWrite()2701             void LockWrite() { m_Mutex.Lock(); }
UnlockWrite()2702             void UnlockWrite() { m_Mutex.Unlock(); }
TryLockWrite()2703             bool TryLockWrite() { return m_Mutex.TryLock(); }
2704         private:
2705             VMA_MUTEX m_Mutex;
2706         };
2707         #define VMA_RW_MUTEX VmaRWMutex
2708     #endif // #if VMA_USE_STL_SHARED_MUTEX
2709 #endif // #ifndef VMA_RW_MUTEX
2710 
2711 /*
2712 If providing your own implementation, you need to implement a subset of std::atomic.
2713 */
2714 #ifndef VMA_ATOMIC_UINT32
2715     #include <atomic>
2716     #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
2717 #endif
2718 
2719 #ifndef VMA_ATOMIC_UINT64
2720     #include <atomic>
2721     #define VMA_ATOMIC_UINT64 std::atomic<uint64_t>
2722 #endif
2723 
2724 #ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
2725     /**
2726     Every allocation will have its own memory block.
2727     Define to 1 for debugging purposes only.
2728     */
2729     #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
2730 #endif
2731 
2732 #ifndef VMA_MIN_ALIGNMENT
2733     /**
2734     Minimum alignment of all allocations, in bytes.
2735     Set to more than 1 for debugging purposes. Must be power of two.
2736     */
2737     #ifdef VMA_DEBUG_ALIGNMENT // Old name
2738         #define VMA_MIN_ALIGNMENT VMA_DEBUG_ALIGNMENT
2739     #else
2740         #define VMA_MIN_ALIGNMENT (1)
2741     #endif
2742 #endif
2743 
2744 #ifndef VMA_DEBUG_MARGIN
2745     /**
2746     Minimum margin before and after every allocation, in bytes.
2747     Set nonzero for debugging purposes only.
2748     */
2749     #define VMA_DEBUG_MARGIN (0)
2750 #endif
2751 
2752 #ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
2753     /**
2754     Define this macro to 1 to automatically fill new allocations and destroyed
2755     allocations with some bit pattern.
2756     */
2757     #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)
2758 #endif
2759 
2760 #ifndef VMA_DEBUG_DETECT_CORRUPTION
2761     /**
2762     Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to
2763     enable writing magic value to the margin before and after every allocation and
2764     validating it, so that memory corruptions (out-of-bounds writes) are detected.
2765     */
2766     #define VMA_DEBUG_DETECT_CORRUPTION (0)
2767 #endif
2768 
2769 #ifndef VMA_DEBUG_GLOBAL_MUTEX
2770     /**
2771     Set this to 1 for debugging purposes only, to enable single mutex protecting all
2772     entry calls to the library. Can be useful for debugging multithreading issues.
2773     */
2774     #define VMA_DEBUG_GLOBAL_MUTEX (0)
2775 #endif
2776 
2777 #ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
2778     /**
2779     Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.
2780     Set to more than 1 for debugging purposes only. Must be power of two.
2781     */
2782     #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
2783 #endif
2784 
2785 #ifndef VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT
2786     /*
2787     Set this to 1 to make VMA never exceed VkPhysicalDeviceLimits::maxMemoryAllocationCount
2788     and return error instead of leaving up to Vulkan implementation what to do in such cases.
2789     */
2790     #define VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT (0)
2791 #endif
2792 
2793 #ifndef VMA_SMALL_HEAP_MAX_SIZE
2794    /// Maximum size of a memory heap in Vulkan to consider it "small".
2795    #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
2796 #endif
2797 
2798 #ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
2799    /// Default size of a block allocated as single VkDeviceMemory from a "large" heap.
2800    #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
2801 #endif
2802 
2803 #ifndef VMA_CLASS_NO_COPY
2804     #define VMA_CLASS_NO_COPY(className) \
2805         private: \
2806             className(const className&) = delete; \
2807             className& operator=(const className&) = delete;
2808 #endif
2809 
2810 static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX;
2811 
2812 // Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.
2813 static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
2814 
2815 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED   = 0xDC;
2816 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
2817 
2818 /*******************************************************************************
2819 END OF CONFIGURATION
2820 */
2821 
2822 // # Copy of some Vulkan definitions so we don't need to check their existence just to handle few constants.
2823 
2824 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY = 0x00000040;
2825 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY = 0x00000080;
2826 static const uint32_t VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY = 0x00020000;
2827 
2828 static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
2829 
2830 static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
2831     VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
2832 
2833 // Returns number of bits set to 1 in (v).
VmaCountBitsSet(uint32_t v)2834 static inline uint32_t VmaCountBitsSet(uint32_t v)
2835 {
2836     uint32_t c = v - ((v >> 1) & 0x55555555);
2837     c = ((c >>  2) & 0x33333333) + (c & 0x33333333);
2838     c = ((c >>  4) + c) & 0x0F0F0F0F;
2839     c = ((c >>  8) + c) & 0x00FF00FF;
2840     c = ((c >> 16) + c) & 0x0000FFFF;
2841     return c;
2842 }
2843 
2844 /*
2845 Returns true if given number is a power of two.
2846 T must be unsigned integer number or signed integer but always nonnegative.
2847 For 0 returns true.
2848 */
2849 template <typename T>
VmaIsPow2(T x)2850 inline bool VmaIsPow2(T x)
2851 {
2852     return (x & (x-1)) == 0;
2853 }
2854 
2855 // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
2856 // Use types like uint32_t, uint64_t as T.
2857 template <typename T>
VmaAlignUp(T val,T alignment)2858 static inline T VmaAlignUp(T val, T alignment)
2859 {
2860     VMA_HEAVY_ASSERT(VmaIsPow2(alignment));
2861     return (val + alignment - 1) & ~(alignment - 1);
2862 }
2863 // Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8.
2864 // Use types like uint32_t, uint64_t as T.
2865 template <typename T>
VmaAlignDown(T val,T alignment)2866 static inline T VmaAlignDown(T val, T alignment)
2867 {
2868     VMA_HEAVY_ASSERT(VmaIsPow2(alignment));
2869     return val & ~(alignment - 1);
2870 }
2871 
2872 // Division with mathematical rounding to nearest number.
2873 template <typename T>
VmaRoundDiv(T x,T y)2874 static inline T VmaRoundDiv(T x, T y)
2875 {
2876     return (x + (y / (T)2)) / y;
2877 }
2878 
2879 // Returns smallest power of 2 greater or equal to v.
VmaNextPow2(uint32_t v)2880 static inline uint32_t VmaNextPow2(uint32_t v)
2881 {
2882     v--;
2883     v |= v >> 1;
2884     v |= v >> 2;
2885     v |= v >> 4;
2886     v |= v >> 8;
2887     v |= v >> 16;
2888     v++;
2889     return v;
2890 }
VmaNextPow2(uint64_t v)2891 static inline uint64_t VmaNextPow2(uint64_t v)
2892 {
2893     v--;
2894     v |= v >> 1;
2895     v |= v >> 2;
2896     v |= v >> 4;
2897     v |= v >> 8;
2898     v |= v >> 16;
2899     v |= v >> 32;
2900     v++;
2901     return v;
2902 }
2903 
2904 // Returns largest power of 2 less or equal to v.
VmaPrevPow2(uint32_t v)2905 static inline uint32_t VmaPrevPow2(uint32_t v)
2906 {
2907     v |= v >> 1;
2908     v |= v >> 2;
2909     v |= v >> 4;
2910     v |= v >> 8;
2911     v |= v >> 16;
2912     v = v ^ (v >> 1);
2913     return v;
2914 }
VmaPrevPow2(uint64_t v)2915 static inline uint64_t VmaPrevPow2(uint64_t v)
2916 {
2917     v |= v >> 1;
2918     v |= v >> 2;
2919     v |= v >> 4;
2920     v |= v >> 8;
2921     v |= v >> 16;
2922     v |= v >> 32;
2923     v = v ^ (v >> 1);
2924     return v;
2925 }
2926 
VmaStrIsEmpty(const char * pStr)2927 static inline bool VmaStrIsEmpty(const char* pStr)
2928 {
2929     return pStr == VMA_NULL || *pStr == '\0';
2930 }
2931 
2932 #if VMA_STATS_STRING_ENABLED
2933 
VmaAlgorithmToStr(uint32_t algorithm)2934 static const char* VmaAlgorithmToStr(uint32_t algorithm)
2935 {
2936     switch(algorithm)
2937     {
2938     case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
2939         return "Linear";
2940     case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
2941         return "Buddy";
2942     case 0:
2943         return "Default";
2944     default:
2945         VMA_ASSERT(0);
2946         return "";
2947     }
2948 }
2949 
2950 #endif // #if VMA_STATS_STRING_ENABLED
2951 
2952 #ifndef VMA_SORT
2953 
2954 template<typename Iterator, typename Compare>
VmaQuickSortPartition(Iterator beg,Iterator end,Compare cmp)2955 Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
2956 {
2957     Iterator centerValue = end; --centerValue;
2958     Iterator insertIndex = beg;
2959     for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
2960     {
2961         if(cmp(*memTypeIndex, *centerValue))
2962         {
2963             if(insertIndex != memTypeIndex)
2964             {
2965                 VMA_SWAP(*memTypeIndex, *insertIndex);
2966             }
2967             ++insertIndex;
2968         }
2969     }
2970     if(insertIndex != centerValue)
2971     {
2972         VMA_SWAP(*insertIndex, *centerValue);
2973     }
2974     return insertIndex;
2975 }
2976 
2977 template<typename Iterator, typename Compare>
VmaQuickSort(Iterator beg,Iterator end,Compare cmp)2978 void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
2979 {
2980     if(beg < end)
2981     {
2982         Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
2983         VmaQuickSort<Iterator, Compare>(beg, it, cmp);
2984         VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
2985     }
2986 }
2987 
2988 #define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
2989 
2990 #endif // #ifndef VMA_SORT
2991 
2992 /*
2993 Returns true if two memory blocks occupy overlapping pages.
2994 ResourceA must be in less memory offset than ResourceB.
2995 
2996 Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
2997 chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
2998 */
VmaBlocksOnSamePage(VkDeviceSize resourceAOffset,VkDeviceSize resourceASize,VkDeviceSize resourceBOffset,VkDeviceSize pageSize)2999 static inline bool VmaBlocksOnSamePage(
3000     VkDeviceSize resourceAOffset,
3001     VkDeviceSize resourceASize,
3002     VkDeviceSize resourceBOffset,
3003     VkDeviceSize pageSize)
3004 {
3005     VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
3006     VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
3007     VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
3008     VkDeviceSize resourceBStart = resourceBOffset;
3009     VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
3010     return resourceAEndPage == resourceBStartPage;
3011 }
3012 
3013 enum VmaSuballocationType
3014 {
3015     VMA_SUBALLOCATION_TYPE_FREE = 0,
3016     VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
3017     VMA_SUBALLOCATION_TYPE_BUFFER = 2,
3018     VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
3019     VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
3020     VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
3021     VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
3022 };
3023 
3024 /*
3025 Returns true if given suballocation types could conflict and must respect
3026 VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
3027 or linear image and another one is optimal image. If type is unknown, behave
3028 conservatively.
3029 */
VmaIsBufferImageGranularityConflict(VmaSuballocationType suballocType1,VmaSuballocationType suballocType2)3030 static inline bool VmaIsBufferImageGranularityConflict(
3031     VmaSuballocationType suballocType1,
3032     VmaSuballocationType suballocType2)
3033 {
3034     if(suballocType1 > suballocType2)
3035     {
3036         VMA_SWAP(suballocType1, suballocType2);
3037     }
3038 
3039     switch(suballocType1)
3040     {
3041     case VMA_SUBALLOCATION_TYPE_FREE:
3042         return false;
3043     case VMA_SUBALLOCATION_TYPE_UNKNOWN:
3044         return true;
3045     case VMA_SUBALLOCATION_TYPE_BUFFER:
3046         return
3047             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3048             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3049     case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
3050         return
3051             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3052             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
3053             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3054     case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
3055         return
3056             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3057     case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
3058         return false;
3059     default:
3060         VMA_ASSERT(0);
3061         return true;
3062     }
3063 }
3064 
VmaWriteMagicValue(void * pData,VkDeviceSize offset)3065 static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
3066 {
3067 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
3068     uint32_t* pDst = (uint32_t*)((char*)pData + offset);
3069     const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3070     for(size_t i = 0; i < numberCount; ++i, ++pDst)
3071     {
3072         *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
3073     }
3074 #else
3075     // no-op
3076 #endif
3077 }
3078 
VmaValidateMagicValue(const void * pData,VkDeviceSize offset)3079 static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
3080 {
3081 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
3082     const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
3083     const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3084     for(size_t i = 0; i < numberCount; ++i, ++pSrc)
3085     {
3086         if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
3087         {
3088             return false;
3089         }
3090     }
3091 #endif
3092     return true;
3093 }
3094 
3095 /*
3096 Fills structure with parameters of an example buffer to be used for transfers
3097 during GPU memory defragmentation.
3098 */
VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo & outBufCreateInfo)3099 static void VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo& outBufCreateInfo)
3100 {
3101     memset(&outBufCreateInfo, 0, sizeof(outBufCreateInfo));
3102     outBufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
3103     outBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
3104     outBufCreateInfo.size = (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE; // Example size.
3105 }
3106 
3107 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
3108 struct VmaMutexLock
3109 {
VMA_CLASS_NO_COPYVmaMutexLock3110     VMA_CLASS_NO_COPY(VmaMutexLock)
3111 public:
3112     VmaMutexLock(VMA_MUTEX& mutex, bool useMutex = true) :
3113         m_pMutex(useMutex ? &mutex : VMA_NULL)
3114     { if(m_pMutex) { m_pMutex->Lock(); } }
~VmaMutexLockVmaMutexLock3115     ~VmaMutexLock()
3116     { if(m_pMutex) { m_pMutex->Unlock(); } }
3117 private:
3118     VMA_MUTEX* m_pMutex;
3119 };
3120 
3121 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
3122 struct VmaMutexLockRead
3123 {
VMA_CLASS_NO_COPYVmaMutexLockRead3124     VMA_CLASS_NO_COPY(VmaMutexLockRead)
3125 public:
3126     VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
3127         m_pMutex(useMutex ? &mutex : VMA_NULL)
3128     { if(m_pMutex) { m_pMutex->LockRead(); } }
~VmaMutexLockReadVmaMutexLockRead3129     ~VmaMutexLockRead() { if(m_pMutex) { m_pMutex->UnlockRead(); } }
3130 private:
3131     VMA_RW_MUTEX* m_pMutex;
3132 };
3133 
3134 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
3135 struct VmaMutexLockWrite
3136 {
VMA_CLASS_NO_COPYVmaMutexLockWrite3137     VMA_CLASS_NO_COPY(VmaMutexLockWrite)
3138 public:
3139     VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) :
3140         m_pMutex(useMutex ? &mutex : VMA_NULL)
3141     { if(m_pMutex) { m_pMutex->LockWrite(); } }
~VmaMutexLockWriteVmaMutexLockWrite3142     ~VmaMutexLockWrite() { if(m_pMutex) { m_pMutex->UnlockWrite(); } }
3143 private:
3144     VMA_RW_MUTEX* m_pMutex;
3145 };
3146 
3147 #if VMA_DEBUG_GLOBAL_MUTEX
3148     static VMA_MUTEX gDebugGlobalMutex;
3149     #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
3150 #else
3151     #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
3152 #endif
3153 
3154 /*
3155 Performs binary search and returns iterator to first element that is greater or
3156 equal to (key), according to comparison (cmp).
3157 
3158 Cmp should return true if first argument is less than second argument.
3159 
3160 Returned value is the found element, if present in the collection or place where
3161 new element with value (key) should be inserted.
3162 */
3163 template <typename CmpLess, typename IterT, typename KeyT>
VmaBinaryFindFirstNotLess(IterT beg,IterT end,const KeyT & key,const CmpLess & cmp)3164 static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, const CmpLess& cmp)
3165 {
3166     size_t down = 0, up = (end - beg);
3167     while(down < up)
3168     {
3169         const size_t mid = down + (up - down) / 2;  // Overflow-safe midpoint calculation
3170         if(cmp(*(beg+mid), key))
3171         {
3172             down = mid + 1;
3173         }
3174         else
3175         {
3176             up = mid;
3177         }
3178     }
3179     return beg + down;
3180 }
3181 
3182 template<typename CmpLess, typename IterT, typename KeyT>
VmaBinaryFindSorted(const IterT & beg,const IterT & end,const KeyT & value,const CmpLess & cmp)3183 IterT VmaBinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp)
3184 {
3185     IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
3186         beg, end, value, cmp);
3187     if(it == end ||
3188         (!cmp(*it, value) && !cmp(value, *it)))
3189     {
3190         return it;
3191     }
3192     return end;
3193 }
3194 
3195 /*
3196 Returns true if all pointers in the array are not-null and unique.
3197 Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
3198 T must be pointer type, e.g. VmaAllocation, VmaPool.
3199 */
3200 template<typename T>
VmaValidatePointerArray(uint32_t count,const T * arr)3201 static bool VmaValidatePointerArray(uint32_t count, const T* arr)
3202 {
3203     for(uint32_t i = 0; i < count; ++i)
3204     {
3205         const T iPtr = arr[i];
3206         if(iPtr == VMA_NULL)
3207         {
3208             return false;
3209         }
3210         for(uint32_t j = i + 1; j < count; ++j)
3211         {
3212             if(iPtr == arr[j])
3213             {
3214                 return false;
3215             }
3216         }
3217     }
3218     return true;
3219 }
3220 
3221 template<typename MainT, typename NewT>
VmaPnextChainPushFront(MainT * mainStruct,NewT * newStruct)3222 static inline void VmaPnextChainPushFront(MainT* mainStruct, NewT* newStruct)
3223 {
3224     newStruct->pNext = mainStruct->pNext;
3225     mainStruct->pNext = newStruct;
3226 }
3227 
3228 ////////////////////////////////////////////////////////////////////////////////
3229 // Memory allocation
3230 
VmaMalloc(const VkAllocationCallbacks * pAllocationCallbacks,size_t size,size_t alignment)3231 static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
3232 {
3233     void* result = VMA_NULL;
3234     if((pAllocationCallbacks != VMA_NULL) &&
3235         (pAllocationCallbacks->pfnAllocation != VMA_NULL))
3236     {
3237         result = (*pAllocationCallbacks->pfnAllocation)(
3238             pAllocationCallbacks->pUserData,
3239             size,
3240             alignment,
3241             VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
3242     }
3243     else
3244     {
3245         result = VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
3246     }
3247     VMA_ASSERT(result != VMA_NULL && "CPU memory allocation failed.");
3248     return result;
3249 }
3250 
VmaFree(const VkAllocationCallbacks * pAllocationCallbacks,void * ptr)3251 static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
3252 {
3253     if((pAllocationCallbacks != VMA_NULL) &&
3254         (pAllocationCallbacks->pfnFree != VMA_NULL))
3255     {
3256         (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
3257     }
3258     else
3259     {
3260         VMA_SYSTEM_ALIGNED_FREE(ptr);
3261     }
3262 }
3263 
3264 template<typename T>
VmaAllocate(const VkAllocationCallbacks * pAllocationCallbacks)3265 static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
3266 {
3267     return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
3268 }
3269 
3270 template<typename T>
VmaAllocateArray(const VkAllocationCallbacks * pAllocationCallbacks,size_t count)3271 static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
3272 {
3273     return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
3274 }
3275 
3276 #define vma_new(allocator, type)   new(VmaAllocate<type>(allocator))(type)
3277 
3278 #define vma_new_array(allocator, type, count)   new(VmaAllocateArray<type>((allocator), (count)))(type)
3279 
3280 template<typename T>
vma_delete(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr)3281 static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
3282 {
3283     ptr->~T();
3284     VmaFree(pAllocationCallbacks, ptr);
3285 }
3286 
3287 template<typename T>
vma_delete_array(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr,size_t count)3288 static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
3289 {
3290     if(ptr != VMA_NULL)
3291     {
3292         for(size_t i = count; i--; )
3293         {
3294             ptr[i].~T();
3295         }
3296         VmaFree(pAllocationCallbacks, ptr);
3297     }
3298 }
3299 
VmaCreateStringCopy(const VkAllocationCallbacks * allocs,const char * srcStr)3300 static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr)
3301 {
3302     if(srcStr != VMA_NULL)
3303     {
3304         const size_t len = strlen(srcStr);
3305         char* const result = vma_new_array(allocs, char, len + 1);
3306         memcpy(result, srcStr, len + 1);
3307         return result;
3308     }
3309     return VMA_NULL;
3310 }
3311 
VmaCreateStringCopy(const VkAllocationCallbacks * allocs,const char * srcStr,size_t strLen)3312 static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr, size_t strLen)
3313 {
3314     if(srcStr != VMA_NULL)
3315     {
3316         char* const result = vma_new_array(allocs, char, strLen + 1);
3317         memcpy(result, srcStr, strLen);
3318         result[strLen] = '\0';
3319         return result;
3320     }
3321     return VMA_NULL;
3322 }
3323 
VmaFreeString(const VkAllocationCallbacks * allocs,char * str)3324 static void VmaFreeString(const VkAllocationCallbacks* allocs, char* str)
3325 {
3326     if(str != VMA_NULL)
3327     {
3328         const size_t len = strlen(str);
3329         vma_delete_array(allocs, str, len + 1);
3330     }
3331 }
3332 
3333 // STL-compatible allocator.
3334 template<typename T>
3335 class VmaStlAllocator
3336 {
3337 public:
3338     const VkAllocationCallbacks* const m_pCallbacks;
3339     typedef T value_type;
3340 
VmaStlAllocator(const VkAllocationCallbacks * pCallbacks)3341     VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
VmaStlAllocator(const VmaStlAllocator<U> & src)3342     template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
3343 
allocate(size_t n)3344     T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
deallocate(T * p,size_t n)3345     void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
3346 
3347     template<typename U>
3348     bool operator==(const VmaStlAllocator<U>& rhs) const
3349     {
3350         return m_pCallbacks == rhs.m_pCallbacks;
3351     }
3352     template<typename U>
3353     bool operator!=(const VmaStlAllocator<U>& rhs) const
3354     {
3355         return m_pCallbacks != rhs.m_pCallbacks;
3356     }
3357 
3358     VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
3359     VmaStlAllocator(const VmaStlAllocator&) = default;
3360 };
3361 
3362 #if VMA_USE_STL_VECTOR
3363 
3364 #define VmaVector std::vector
3365 
3366 template<typename T, typename allocatorT>
VmaVectorInsert(std::vector<T,allocatorT> & vec,size_t index,const T & item)3367 static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
3368 {
3369     vec.insert(vec.begin() + index, item);
3370 }
3371 
3372 template<typename T, typename allocatorT>
VmaVectorRemove(std::vector<T,allocatorT> & vec,size_t index)3373 static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
3374 {
3375     vec.erase(vec.begin() + index);
3376 }
3377 
3378 #else // #if VMA_USE_STL_VECTOR
3379 
3380 /* Class with interface compatible with subset of std::vector.
3381 T must be POD because constructors and destructors are not called and memcpy is
3382 used for these objects. */
3383 template<typename T, typename AllocatorT>
3384 class VmaVector
3385 {
3386 public:
3387     typedef T value_type;
3388 
VmaVector(const AllocatorT & allocator)3389     VmaVector(const AllocatorT& allocator) :
3390         m_Allocator(allocator),
3391         m_pArray(VMA_NULL),
3392         m_Count(0),
3393         m_Capacity(0)
3394     {
3395     }
3396 
VmaVector(size_t count,const AllocatorT & allocator)3397     VmaVector(size_t count, const AllocatorT& allocator) :
3398         m_Allocator(allocator),
3399         m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
3400         m_Count(count),
3401         m_Capacity(count)
3402     {
3403     }
3404 
3405     // This version of the constructor is here for compatibility with pre-C++14 std::vector.
3406     // value is unused.
VmaVector(size_t count,const T & value,const AllocatorT & allocator)3407     VmaVector(size_t count, const T& value, const AllocatorT& allocator)
3408         : VmaVector(count, allocator) {}
3409 
VmaVector(const VmaVector<T,AllocatorT> & src)3410     VmaVector(const VmaVector<T, AllocatorT>& src) :
3411         m_Allocator(src.m_Allocator),
3412         m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
3413         m_Count(src.m_Count),
3414         m_Capacity(src.m_Count)
3415     {
3416         if(m_Count != 0)
3417         {
3418             memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
3419         }
3420     }
3421 
~VmaVector()3422     ~VmaVector()
3423     {
3424         VmaFree(m_Allocator.m_pCallbacks, m_pArray);
3425     }
3426 
3427     VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
3428     {
3429         if(&rhs != this)
3430         {
3431             resize(rhs.m_Count);
3432             if(m_Count != 0)
3433             {
3434                 memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
3435             }
3436         }
3437         return *this;
3438     }
3439 
empty()3440     bool empty() const { return m_Count == 0; }
size()3441     size_t size() const { return m_Count; }
data()3442     T* data() { return m_pArray; }
data()3443     const T* data() const { return m_pArray; }
3444 
3445     T& operator[](size_t index)
3446     {
3447         VMA_HEAVY_ASSERT(index < m_Count);
3448         return m_pArray[index];
3449     }
3450     const T& operator[](size_t index) const
3451     {
3452         VMA_HEAVY_ASSERT(index < m_Count);
3453         return m_pArray[index];
3454     }
3455 
front()3456     T& front()
3457     {
3458         VMA_HEAVY_ASSERT(m_Count > 0);
3459         return m_pArray[0];
3460     }
front()3461     const T& front() const
3462     {
3463         VMA_HEAVY_ASSERT(m_Count > 0);
3464         return m_pArray[0];
3465     }
back()3466     T& back()
3467     {
3468         VMA_HEAVY_ASSERT(m_Count > 0);
3469         return m_pArray[m_Count - 1];
3470     }
back()3471     const T& back() const
3472     {
3473         VMA_HEAVY_ASSERT(m_Count > 0);
3474         return m_pArray[m_Count - 1];
3475     }
3476 
3477     void reserve(size_t newCapacity, bool freeMemory = false)
3478     {
3479         newCapacity = VMA_MAX(newCapacity, m_Count);
3480 
3481         if((newCapacity < m_Capacity) && !freeMemory)
3482         {
3483             newCapacity = m_Capacity;
3484         }
3485 
3486         if(newCapacity != m_Capacity)
3487         {
3488             T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
3489             if(m_Count != 0)
3490             {
3491                 memcpy(newArray, m_pArray, m_Count * sizeof(T));
3492             }
3493             VmaFree(m_Allocator.m_pCallbacks, m_pArray);
3494             m_Capacity = newCapacity;
3495             m_pArray = newArray;
3496         }
3497     }
3498 
resize(size_t newCount)3499     void resize(size_t newCount)
3500     {
3501         size_t newCapacity = m_Capacity;
3502         if(newCount > m_Capacity)
3503         {
3504             newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
3505         }
3506 
3507         if(newCapacity != m_Capacity)
3508         {
3509             T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
3510             const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
3511             if(elementsToCopy != 0)
3512             {
3513                 memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
3514             }
3515             VmaFree(m_Allocator.m_pCallbacks, m_pArray);
3516             m_Capacity = newCapacity;
3517             m_pArray = newArray;
3518         }
3519 
3520         m_Count = newCount;
3521     }
3522 
clear()3523     void clear()
3524     {
3525         resize(0);
3526     }
3527 
shrink_to_fit()3528     void shrink_to_fit()
3529     {
3530         if(m_Capacity > m_Count)
3531         {
3532             T* newArray = VMA_NULL;
3533             if(m_Count > 0)
3534             {
3535                 newArray = VmaAllocateArray<T>(m_Allocator.m_pCallbacks, m_Count);
3536                 memcpy(newArray, m_pArray, m_Count * sizeof(T));
3537             }
3538             VmaFree(m_Allocator.m_pCallbacks, m_pArray);
3539             m_Capacity = m_Count;
3540             m_pArray = newArray;
3541         }
3542     }
3543 
insert(size_t index,const T & src)3544     void insert(size_t index, const T& src)
3545     {
3546         VMA_HEAVY_ASSERT(index <= m_Count);
3547         const size_t oldCount = size();
3548         resize(oldCount + 1);
3549         if(index < oldCount)
3550         {
3551             memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
3552         }
3553         m_pArray[index] = src;
3554     }
3555 
remove(size_t index)3556     void remove(size_t index)
3557     {
3558         VMA_HEAVY_ASSERT(index < m_Count);
3559         const size_t oldCount = size();
3560         if(index < oldCount - 1)
3561         {
3562             memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
3563         }
3564         resize(oldCount - 1);
3565     }
3566 
push_back(const T & src)3567     void push_back(const T& src)
3568     {
3569         const size_t newIndex = size();
3570         resize(newIndex + 1);
3571         m_pArray[newIndex] = src;
3572     }
3573 
pop_back()3574     void pop_back()
3575     {
3576         VMA_HEAVY_ASSERT(m_Count > 0);
3577         resize(size() - 1);
3578     }
3579 
push_front(const T & src)3580     void push_front(const T& src)
3581     {
3582         insert(0, src);
3583     }
3584 
pop_front()3585     void pop_front()
3586     {
3587         VMA_HEAVY_ASSERT(m_Count > 0);
3588         remove(0);
3589     }
3590 
3591     typedef T* iterator;
3592     typedef const T* const_iterator;
3593 
begin()3594     iterator begin() { return m_pArray; }
end()3595     iterator end() { return m_pArray + m_Count; }
cbegin()3596     const_iterator cbegin() const { return m_pArray; }
cend()3597     const_iterator cend() const { return m_pArray + m_Count; }
begin()3598     const_iterator begin() const { return cbegin(); }
end()3599     const_iterator end() const { return cend(); }
3600 
3601 private:
3602     AllocatorT m_Allocator;
3603     T* m_pArray;
3604     size_t m_Count;
3605     size_t m_Capacity;
3606 };
3607 
3608 template<typename T, typename allocatorT>
VmaVectorInsert(VmaVector<T,allocatorT> & vec,size_t index,const T & item)3609 static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
3610 {
3611     vec.insert(index, item);
3612 }
3613 
3614 template<typename T, typename allocatorT>
VmaVectorRemove(VmaVector<T,allocatorT> & vec,size_t index)3615 static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
3616 {
3617     vec.remove(index);
3618 }
3619 
3620 #endif // #if VMA_USE_STL_VECTOR
3621 
3622 template<typename CmpLess, typename VectorT>
VmaVectorInsertSorted(VectorT & vector,const typename VectorT::value_type & value)3623 size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
3624 {
3625     const size_t indexToInsert = VmaBinaryFindFirstNotLess(
3626         vector.data(),
3627         vector.data() + vector.size(),
3628         value,
3629         CmpLess()) - vector.data();
3630     VmaVectorInsert(vector, indexToInsert, value);
3631     return indexToInsert;
3632 }
3633 
3634 template<typename CmpLess, typename VectorT>
VmaVectorRemoveSorted(VectorT & vector,const typename VectorT::value_type & value)3635 bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
3636 {
3637     CmpLess comparator;
3638     typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
3639         vector.begin(),
3640         vector.end(),
3641         value,
3642         comparator);
3643     if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
3644     {
3645         size_t indexToRemove = it - vector.begin();
3646         VmaVectorRemove(vector, indexToRemove);
3647         return true;
3648     }
3649     return false;
3650 }
3651 
3652 ////////////////////////////////////////////////////////////////////////////////
3653 // class VmaSmallVector
3654 
3655 /*
3656 This is a vector (a variable-sized array), optimized for the case when the array is small.
3657 
3658 It contains some number of elements in-place, which allows it to avoid heap allocation
3659 when the actual number of elements is below that threshold. This allows normal "small"
3660 cases to be fast without losing generality for large inputs.
3661 */
3662 
3663 template<typename T, typename AllocatorT, size_t N>
3664 class VmaSmallVector
3665 {
3666 public:
3667     typedef T value_type;
3668 
VmaSmallVector(const AllocatorT & allocator)3669     VmaSmallVector(const AllocatorT& allocator) :
3670         m_Count(0),
3671         m_DynamicArray(allocator)
3672     {
3673     }
VmaSmallVector(size_t count,const AllocatorT & allocator)3674     VmaSmallVector(size_t count, const AllocatorT& allocator) :
3675         m_Count(count),
3676         m_DynamicArray(count > N ? count : 0, allocator)
3677     {
3678     }
3679     template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
3680     VmaSmallVector(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>& src) = delete;
3681     template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
3682     VmaSmallVector<T, AllocatorT, N>& operator=(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>& rhs) = delete;
3683 
empty()3684     bool empty() const { return m_Count == 0; }
size()3685     size_t size() const { return m_Count; }
data()3686     T* data() { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
data()3687     const T* data() const { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
3688 
3689     T& operator[](size_t index)
3690     {
3691         VMA_HEAVY_ASSERT(index < m_Count);
3692         return data()[index];
3693     }
3694     const T& operator[](size_t index) const
3695     {
3696         VMA_HEAVY_ASSERT(index < m_Count);
3697         return data()[index];
3698     }
3699 
front()3700     T& front()
3701     {
3702         VMA_HEAVY_ASSERT(m_Count > 0);
3703         return data()[0];
3704     }
front()3705     const T& front() const
3706     {
3707         VMA_HEAVY_ASSERT(m_Count > 0);
3708         return data()[0];
3709     }
back()3710     T& back()
3711     {
3712         VMA_HEAVY_ASSERT(m_Count > 0);
3713         return data()[m_Count - 1];
3714     }
back()3715     const T& back() const
3716     {
3717         VMA_HEAVY_ASSERT(m_Count > 0);
3718         return data()[m_Count - 1];
3719     }
3720 
3721     void resize(size_t newCount, bool freeMemory = false)
3722     {
3723         if(newCount > N && m_Count > N)
3724         {
3725             // Any direction, staying in m_DynamicArray
3726             m_DynamicArray.resize(newCount);
3727             if(freeMemory)
3728             {
3729                 m_DynamicArray.shrink_to_fit();
3730             }
3731         }
3732         else if(newCount > N && m_Count <= N)
3733         {
3734             // Growing, moving from m_StaticArray to m_DynamicArray
3735             m_DynamicArray.resize(newCount);
3736             if(m_Count > 0)
3737             {
3738                 memcpy(m_DynamicArray.data(), m_StaticArray, m_Count * sizeof(T));
3739             }
3740         }
3741         else if(newCount <= N && m_Count > N)
3742         {
3743             // Shrinking, moving from m_DynamicArray to m_StaticArray
3744             if(newCount > 0)
3745             {
3746                 memcpy(m_StaticArray, m_DynamicArray.data(), newCount * sizeof(T));
3747             }
3748             m_DynamicArray.resize(0);
3749             if(freeMemory)
3750             {
3751                 m_DynamicArray.shrink_to_fit();
3752             }
3753         }
3754         else
3755         {
3756             // Any direction, staying in m_StaticArray - nothing to do here
3757         }
3758         m_Count = newCount;
3759     }
3760 
3761     void clear(bool freeMemory = false)
3762     {
3763         m_DynamicArray.clear();
3764         if(freeMemory)
3765         {
3766             m_DynamicArray.shrink_to_fit();
3767         }
3768         m_Count = 0;
3769     }
3770 
insert(size_t index,const T & src)3771     void insert(size_t index, const T& src)
3772     {
3773         VMA_HEAVY_ASSERT(index <= m_Count);
3774         const size_t oldCount = size();
3775         resize(oldCount + 1);
3776         T* const dataPtr = data();
3777         if(index < oldCount)
3778         {
3779             //  I know, this could be more optimal for case where memmove can be memcpy directly from m_StaticArray to m_DynamicArray.
3780             memmove(dataPtr + (index + 1), dataPtr + index, (oldCount - index) * sizeof(T));
3781         }
3782         dataPtr[index] = src;
3783     }
3784 
remove(size_t index)3785     void remove(size_t index)
3786     {
3787         VMA_HEAVY_ASSERT(index < m_Count);
3788         const size_t oldCount = size();
3789         if(index < oldCount - 1)
3790         {
3791             //  I know, this could be more optimal for case where memmove can be memcpy directly from m_DynamicArray to m_StaticArray.
3792             T* const dataPtr = data();
3793             memmove(dataPtr + index, dataPtr + (index + 1), (oldCount - index - 1) * sizeof(T));
3794         }
3795         resize(oldCount - 1);
3796     }
3797 
push_back(const T & src)3798     void push_back(const T& src)
3799     {
3800         const size_t newIndex = size();
3801         resize(newIndex + 1);
3802         data()[newIndex] = src;
3803     }
3804 
pop_back()3805     void pop_back()
3806     {
3807         VMA_HEAVY_ASSERT(m_Count > 0);
3808         resize(size() - 1);
3809     }
3810 
push_front(const T & src)3811     void push_front(const T& src)
3812     {
3813         insert(0, src);
3814     }
3815 
pop_front()3816     void pop_front()
3817     {
3818         VMA_HEAVY_ASSERT(m_Count > 0);
3819         remove(0);
3820     }
3821 
3822     typedef T* iterator;
3823 
begin()3824     iterator begin() { return data(); }
end()3825     iterator end() { return data() + m_Count; }
3826 
3827 private:
3828     size_t m_Count;
3829     T m_StaticArray[N]; // Used when m_Size <= N
3830     VmaVector<T, AllocatorT> m_DynamicArray; // Used when m_Size > N
3831 };
3832 
3833 ////////////////////////////////////////////////////////////////////////////////
3834 // class VmaPoolAllocator
3835 
3836 /*
3837 Allocator for objects of type T using a list of arrays (pools) to speed up
3838 allocation. Number of elements that can be allocated is not bounded because
3839 allocator can create multiple blocks.
3840 */
3841 template<typename T>
3842 class VmaPoolAllocator
3843 {
3844     VMA_CLASS_NO_COPY(VmaPoolAllocator)
3845 public:
3846     VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity);
3847     ~VmaPoolAllocator();
3848     template<typename... Types> T* Alloc(Types&&... args);
3849     void Free(T* ptr);
3850 
3851 private:
3852     union Item
3853     {
3854         uint32_t NextFreeIndex;
3855         alignas(T) char Value[sizeof(T)];
3856     };
3857 
3858     struct ItemBlock
3859     {
3860         Item* pItems;
3861         uint32_t Capacity;
3862         uint32_t FirstFreeIndex;
3863     };
3864 
3865     const VkAllocationCallbacks* m_pAllocationCallbacks;
3866     const uint32_t m_FirstBlockCapacity;
3867     VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
3868 
3869     ItemBlock& CreateNewBlock();
3870 };
3871 
3872 template<typename T>
VmaPoolAllocator(const VkAllocationCallbacks * pAllocationCallbacks,uint32_t firstBlockCapacity)3873 VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity) :
3874     m_pAllocationCallbacks(pAllocationCallbacks),
3875     m_FirstBlockCapacity(firstBlockCapacity),
3876     m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
3877 {
3878     VMA_ASSERT(m_FirstBlockCapacity > 1);
3879 }
3880 
3881 template<typename T>
~VmaPoolAllocator()3882 VmaPoolAllocator<T>::~VmaPoolAllocator()
3883 {
3884     for(size_t i = m_ItemBlocks.size(); i--; )
3885         vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity);
3886     m_ItemBlocks.clear();
3887 }
3888 
3889 template<typename T>
Alloc(Types &&...args)3890 template<typename... Types> T* VmaPoolAllocator<T>::Alloc(Types&&... args)
3891 {
3892     for(size_t i = m_ItemBlocks.size(); i--; )
3893     {
3894         ItemBlock& block = m_ItemBlocks[i];
3895         // This block has some free items: Use first one.
3896         if(block.FirstFreeIndex != UINT32_MAX)
3897         {
3898             Item* const pItem = &block.pItems[block.FirstFreeIndex];
3899             block.FirstFreeIndex = pItem->NextFreeIndex;
3900             T* result = (T*)&pItem->Value;
3901             new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
3902             return result;
3903         }
3904     }
3905 
3906     // No block has free item: Create new one and use it.
3907     ItemBlock& newBlock = CreateNewBlock();
3908     Item* const pItem = &newBlock.pItems[0];
3909     newBlock.FirstFreeIndex = pItem->NextFreeIndex;
3910     T* result = (T*)&pItem->Value;
3911     new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
3912     return result;
3913 }
3914 
3915 template<typename T>
Free(T * ptr)3916 void VmaPoolAllocator<T>::Free(T* ptr)
3917 {
3918     // Search all memory blocks to find ptr.
3919     for(size_t i = m_ItemBlocks.size(); i--; )
3920     {
3921         ItemBlock& block = m_ItemBlocks[i];
3922 
3923         // Casting to union.
3924         Item* pItemPtr;
3925         memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
3926 
3927         // Check if pItemPtr is in address range of this block.
3928         if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity))
3929         {
3930             ptr->~T(); // Explicit destructor call.
3931             const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
3932             pItemPtr->NextFreeIndex = block.FirstFreeIndex;
3933             block.FirstFreeIndex = index;
3934             return;
3935         }
3936     }
3937     VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
3938 }
3939 
3940 template<typename T>
CreateNewBlock()3941 typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
3942 {
3943     const uint32_t newBlockCapacity = m_ItemBlocks.empty() ?
3944         m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2;
3945 
3946     const ItemBlock newBlock = {
3947         vma_new_array(m_pAllocationCallbacks, Item, newBlockCapacity),
3948         newBlockCapacity,
3949         0 };
3950 
3951     m_ItemBlocks.push_back(newBlock);
3952 
3953     // Setup singly-linked list of all free items in this block.
3954     for(uint32_t i = 0; i < newBlockCapacity - 1; ++i)
3955         newBlock.pItems[i].NextFreeIndex = i + 1;
3956     newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX;
3957     return m_ItemBlocks.back();
3958 }
3959 
3960 ////////////////////////////////////////////////////////////////////////////////
3961 // class VmaRawList, VmaList
3962 
3963 #if VMA_USE_STL_LIST
3964 
3965 #define VmaList std::list
3966 
3967 #else // #if VMA_USE_STL_LIST
3968 
3969 template<typename T>
3970 struct VmaListItem
3971 {
3972     VmaListItem* pPrev;
3973     VmaListItem* pNext;
3974     T Value;
3975 };
3976 
3977 // Doubly linked list.
3978 template<typename T>
3979 class VmaRawList
3980 {
3981     VMA_CLASS_NO_COPY(VmaRawList)
3982 public:
3983     typedef VmaListItem<T> ItemType;
3984 
3985     VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
3986     ~VmaRawList();
3987     void Clear();
3988 
GetCount()3989     size_t GetCount() const { return m_Count; }
IsEmpty()3990     bool IsEmpty() const { return m_Count == 0; }
3991 
Front()3992     ItemType* Front() { return m_pFront; }
Front()3993     const ItemType* Front() const { return m_pFront; }
Back()3994     ItemType* Back() { return m_pBack; }
Back()3995     const ItemType* Back() const { return m_pBack; }
3996 
3997     ItemType* PushBack();
3998     ItemType* PushFront();
3999     ItemType* PushBack(const T& value);
4000     ItemType* PushFront(const T& value);
4001     void PopBack();
4002     void PopFront();
4003 
4004     // Item can be null - it means PushBack.
4005     ItemType* InsertBefore(ItemType* pItem);
4006     // Item can be null - it means PushFront.
4007     ItemType* InsertAfter(ItemType* pItem);
4008 
4009     ItemType* InsertBefore(ItemType* pItem, const T& value);
4010     ItemType* InsertAfter(ItemType* pItem, const T& value);
4011 
4012     void Remove(ItemType* pItem);
4013 
4014 private:
4015     const VkAllocationCallbacks* const m_pAllocationCallbacks;
4016     VmaPoolAllocator<ItemType> m_ItemAllocator;
4017     ItemType* m_pFront;
4018     ItemType* m_pBack;
4019     size_t m_Count;
4020 };
4021 
4022 template<typename T>
VmaRawList(const VkAllocationCallbacks * pAllocationCallbacks)4023 VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
4024     m_pAllocationCallbacks(pAllocationCallbacks),
4025     m_ItemAllocator(pAllocationCallbacks, 128),
4026     m_pFront(VMA_NULL),
4027     m_pBack(VMA_NULL),
4028     m_Count(0)
4029 {
4030 }
4031 
4032 template<typename T>
4033 VmaRawList<T>::~VmaRawList() = default;
4034 // Intentionally not calling Clear, because that would be unnecessary
4035 // computations to return all items to m_ItemAllocator as free.
4036 
4037 template<typename T>
Clear()4038 void VmaRawList<T>::Clear()
4039 {
4040     if(IsEmpty() == false)
4041     {
4042         ItemType* pItem = m_pBack;
4043         while(pItem != VMA_NULL)
4044         {
4045             ItemType* const pPrevItem = pItem->pPrev;
4046             m_ItemAllocator.Free(pItem);
4047             pItem = pPrevItem;
4048         }
4049         m_pFront = VMA_NULL;
4050         m_pBack = VMA_NULL;
4051         m_Count = 0;
4052     }
4053 }
4054 
4055 template<typename T>
PushBack()4056 VmaListItem<T>* VmaRawList<T>::PushBack()
4057 {
4058     ItemType* const pNewItem = m_ItemAllocator.Alloc();
4059     pNewItem->pNext = VMA_NULL;
4060     if(IsEmpty())
4061     {
4062         pNewItem->pPrev = VMA_NULL;
4063         m_pFront = pNewItem;
4064         m_pBack = pNewItem;
4065         m_Count = 1;
4066     }
4067     else
4068     {
4069         pNewItem->pPrev = m_pBack;
4070         m_pBack->pNext = pNewItem;
4071         m_pBack = pNewItem;
4072         ++m_Count;
4073     }
4074     return pNewItem;
4075 }
4076 
4077 template<typename T>
PushFront()4078 VmaListItem<T>* VmaRawList<T>::PushFront()
4079 {
4080     ItemType* const pNewItem = m_ItemAllocator.Alloc();
4081     pNewItem->pPrev = VMA_NULL;
4082     if(IsEmpty())
4083     {
4084         pNewItem->pNext = VMA_NULL;
4085         m_pFront = pNewItem;
4086         m_pBack = pNewItem;
4087         m_Count = 1;
4088     }
4089     else
4090     {
4091         pNewItem->pNext = m_pFront;
4092         m_pFront->pPrev = pNewItem;
4093         m_pFront = pNewItem;
4094         ++m_Count;
4095     }
4096     return pNewItem;
4097 }
4098 
4099 template<typename T>
PushBack(const T & value)4100 VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
4101 {
4102     ItemType* const pNewItem = PushBack();
4103     pNewItem->Value = value;
4104     return pNewItem;
4105 }
4106 
4107 template<typename T>
PushFront(const T & value)4108 VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
4109 {
4110     ItemType* const pNewItem = PushFront();
4111     pNewItem->Value = value;
4112     return pNewItem;
4113 }
4114 
4115 template<typename T>
PopBack()4116 void VmaRawList<T>::PopBack()
4117 {
4118     VMA_HEAVY_ASSERT(m_Count > 0);
4119     ItemType* const pBackItem = m_pBack;
4120     ItemType* const pPrevItem = pBackItem->pPrev;
4121     if(pPrevItem != VMA_NULL)
4122     {
4123         pPrevItem->pNext = VMA_NULL;
4124     }
4125     m_pBack = pPrevItem;
4126     m_ItemAllocator.Free(pBackItem);
4127     --m_Count;
4128 }
4129 
4130 template<typename T>
PopFront()4131 void VmaRawList<T>::PopFront()
4132 {
4133     VMA_HEAVY_ASSERT(m_Count > 0);
4134     ItemType* const pFrontItem = m_pFront;
4135     ItemType* const pNextItem = pFrontItem->pNext;
4136     if(pNextItem != VMA_NULL)
4137     {
4138         pNextItem->pPrev = VMA_NULL;
4139     }
4140     m_pFront = pNextItem;
4141     m_ItemAllocator.Free(pFrontItem);
4142     --m_Count;
4143 }
4144 
4145 template<typename T>
Remove(ItemType * pItem)4146 void VmaRawList<T>::Remove(ItemType* pItem)
4147 {
4148     VMA_HEAVY_ASSERT(pItem != VMA_NULL);
4149     VMA_HEAVY_ASSERT(m_Count > 0);
4150 
4151     if(pItem->pPrev != VMA_NULL)
4152     {
4153         pItem->pPrev->pNext = pItem->pNext;
4154     }
4155     else
4156     {
4157         VMA_HEAVY_ASSERT(m_pFront == pItem);
4158         m_pFront = pItem->pNext;
4159     }
4160 
4161     if(pItem->pNext != VMA_NULL)
4162     {
4163         pItem->pNext->pPrev = pItem->pPrev;
4164     }
4165     else
4166     {
4167         VMA_HEAVY_ASSERT(m_pBack == pItem);
4168         m_pBack = pItem->pPrev;
4169     }
4170 
4171     m_ItemAllocator.Free(pItem);
4172     --m_Count;
4173 }
4174 
4175 template<typename T>
InsertBefore(ItemType * pItem)4176 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
4177 {
4178     if(pItem != VMA_NULL)
4179     {
4180         ItemType* const prevItem = pItem->pPrev;
4181         ItemType* const newItem = m_ItemAllocator.Alloc();
4182         newItem->pPrev = prevItem;
4183         newItem->pNext = pItem;
4184         pItem->pPrev = newItem;
4185         if(prevItem != VMA_NULL)
4186         {
4187             prevItem->pNext = newItem;
4188         }
4189         else
4190         {
4191             VMA_HEAVY_ASSERT(m_pFront == pItem);
4192             m_pFront = newItem;
4193         }
4194         ++m_Count;
4195         return newItem;
4196     }
4197     else
4198         return PushBack();
4199 }
4200 
4201 template<typename T>
InsertAfter(ItemType * pItem)4202 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
4203 {
4204     if(pItem != VMA_NULL)
4205     {
4206         ItemType* const nextItem = pItem->pNext;
4207         ItemType* const newItem = m_ItemAllocator.Alloc();
4208         newItem->pNext = nextItem;
4209         newItem->pPrev = pItem;
4210         pItem->pNext = newItem;
4211         if(nextItem != VMA_NULL)
4212         {
4213             nextItem->pPrev = newItem;
4214         }
4215         else
4216         {
4217             VMA_HEAVY_ASSERT(m_pBack == pItem);
4218             m_pBack = newItem;
4219         }
4220         ++m_Count;
4221         return newItem;
4222     }
4223     else
4224         return PushFront();
4225 }
4226 
4227 template<typename T>
InsertBefore(ItemType * pItem,const T & value)4228 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
4229 {
4230     ItemType* const newItem = InsertBefore(pItem);
4231     newItem->Value = value;
4232     return newItem;
4233 }
4234 
4235 template<typename T>
InsertAfter(ItemType * pItem,const T & value)4236 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
4237 {
4238     ItemType* const newItem = InsertAfter(pItem);
4239     newItem->Value = value;
4240     return newItem;
4241 }
4242 
4243 template<typename T, typename AllocatorT>
4244 class VmaList
4245 {
VMA_CLASS_NO_COPY(VmaList)4246     VMA_CLASS_NO_COPY(VmaList)
4247 public:
4248     class iterator
4249     {
4250     public:
4251         iterator() :
4252             m_pList(VMA_NULL),
4253             m_pItem(VMA_NULL)
4254         {
4255         }
4256 
4257         T& operator*() const
4258         {
4259             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4260             return m_pItem->Value;
4261         }
4262         T* operator->() const
4263         {
4264             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4265             return &m_pItem->Value;
4266         }
4267 
4268         iterator& operator++()
4269         {
4270             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4271             m_pItem = m_pItem->pNext;
4272             return *this;
4273         }
4274         iterator& operator--()
4275         {
4276             if(m_pItem != VMA_NULL)
4277             {
4278                 m_pItem = m_pItem->pPrev;
4279             }
4280             else
4281             {
4282                 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4283                 m_pItem = m_pList->Back();
4284             }
4285             return *this;
4286         }
4287 
4288         iterator operator++(int)
4289         {
4290             iterator result = *this;
4291             ++*this;
4292             return result;
4293         }
4294         iterator operator--(int)
4295         {
4296             iterator result = *this;
4297             --*this;
4298             return result;
4299         }
4300 
4301         bool operator==(const iterator& rhs) const
4302         {
4303             VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4304             return m_pItem == rhs.m_pItem;
4305         }
4306         bool operator!=(const iterator& rhs) const
4307         {
4308             VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4309             return m_pItem != rhs.m_pItem;
4310         }
4311 
4312     private:
4313         VmaRawList<T>* m_pList;
4314         VmaListItem<T>* m_pItem;
4315 
4316         iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
4317             m_pList(pList),
4318             m_pItem(pItem)
4319         {
4320         }
4321 
4322         friend class VmaList<T, AllocatorT>;
4323     };
4324 
4325     class const_iterator
4326     {
4327     public:
const_iterator()4328         const_iterator() :
4329             m_pList(VMA_NULL),
4330             m_pItem(VMA_NULL)
4331         {
4332         }
4333 
const_iterator(const iterator & src)4334         const_iterator(const iterator& src) :
4335             m_pList(src.m_pList),
4336             m_pItem(src.m_pItem)
4337         {
4338         }
4339 
4340         const T& operator*() const
4341         {
4342             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4343             return m_pItem->Value;
4344         }
4345         const T* operator->() const
4346         {
4347             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4348             return &m_pItem->Value;
4349         }
4350 
4351         const_iterator& operator++()
4352         {
4353             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4354             m_pItem = m_pItem->pNext;
4355             return *this;
4356         }
4357         const_iterator& operator--()
4358         {
4359             if(m_pItem != VMA_NULL)
4360             {
4361                 m_pItem = m_pItem->pPrev;
4362             }
4363             else
4364             {
4365                 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4366                 m_pItem = m_pList->Back();
4367             }
4368             return *this;
4369         }
4370 
4371         const_iterator operator++(int)
4372         {
4373             const_iterator result = *this;
4374             ++*this;
4375             return result;
4376         }
4377         const_iterator operator--(int)
4378         {
4379             const_iterator result = *this;
4380             --*this;
4381             return result;
4382         }
4383 
4384         bool operator==(const const_iterator& rhs) const
4385         {
4386             VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4387             return m_pItem == rhs.m_pItem;
4388         }
4389         bool operator!=(const const_iterator& rhs) const
4390         {
4391             VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4392             return m_pItem != rhs.m_pItem;
4393         }
4394 
4395     private:
const_iterator(const VmaRawList<T> * pList,const VmaListItem<T> * pItem)4396         const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
4397             m_pList(pList),
4398             m_pItem(pItem)
4399         {
4400         }
4401 
4402         const VmaRawList<T>* m_pList;
4403         const VmaListItem<T>* m_pItem;
4404 
4405         friend class VmaList<T, AllocatorT>;
4406     };
4407 
VmaList(const AllocatorT & allocator)4408     VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
4409 
empty()4410     bool empty() const { return m_RawList.IsEmpty(); }
size()4411     size_t size() const { return m_RawList.GetCount(); }
4412 
begin()4413     iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
end()4414     iterator end() { return iterator(&m_RawList, VMA_NULL); }
4415 
cbegin()4416     const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
cend()4417     const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
4418 
begin()4419     const_iterator begin() const { return cbegin(); }
end()4420     const_iterator end() const { return cend(); }
4421 
clear()4422     void clear() { m_RawList.Clear(); }
push_back(const T & value)4423     void push_back(const T& value) { m_RawList.PushBack(value); }
erase(iterator it)4424     void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
insert(iterator it,const T & value)4425     iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
4426 
4427 private:
4428     VmaRawList<T> m_RawList;
4429 };
4430 
4431 #endif // #if VMA_USE_STL_LIST
4432 
4433 ////////////////////////////////////////////////////////////////////////////////
4434 // class VmaIntrusiveLinkedList
4435 
4436 /*
4437 Expected interface of ItemTypeTraits:
4438 struct MyItemTypeTraits
4439 {
4440     typedef MyItem ItemType;
4441     static ItemType* GetPrev(const ItemType* item) { return item->myPrevPtr; }
4442     static ItemType* GetNext(const ItemType* item) { return item->myNextPtr; }
4443     static ItemType*& AccessPrev(ItemType* item) { return item->myPrevPtr; }
4444     static ItemType*& AccessNext(ItemType* item) { return item->myNextPtr; }
4445 };
4446 */
4447 template<typename ItemTypeTraits>
4448 class VmaIntrusiveLinkedList
4449 {
4450 public:
4451     typedef typename ItemTypeTraits::ItemType ItemType;
GetPrev(const ItemType * item)4452     static ItemType* GetPrev(const ItemType* item) { return ItemTypeTraits::GetPrev(item); }
GetNext(const ItemType * item)4453     static ItemType* GetNext(const ItemType* item) { return ItemTypeTraits::GetNext(item); }
4454     // Movable, not copyable.
4455     VmaIntrusiveLinkedList() = default;
4456     VmaIntrusiveLinkedList(const VmaIntrusiveLinkedList<ItemTypeTraits>& src) = delete;
VmaIntrusiveLinkedList(VmaIntrusiveLinkedList<ItemTypeTraits> && src)4457     VmaIntrusiveLinkedList(VmaIntrusiveLinkedList<ItemTypeTraits>&& src) :
4458         m_Front(src.m_Front), m_Back(src.m_Back), m_Count(src.m_Count)
4459     {
4460         src.m_Front = src.m_Back = VMA_NULL;
4461         src.m_Count = 0;
4462     }
~VmaIntrusiveLinkedList()4463     ~VmaIntrusiveLinkedList()
4464     {
4465         VMA_HEAVY_ASSERT(IsEmpty());
4466     }
4467     VmaIntrusiveLinkedList<ItemTypeTraits>& operator=(const VmaIntrusiveLinkedList<ItemTypeTraits>& src) = delete;
4468     VmaIntrusiveLinkedList<ItemTypeTraits>& operator=(VmaIntrusiveLinkedList<ItemTypeTraits>&& src)
4469     {
4470         if(&src != this)
4471         {
4472             VMA_HEAVY_ASSERT(IsEmpty());
4473             m_Front = src.m_Front;
4474             m_Back = src.m_Back;
4475             m_Count = src.m_Count;
4476             src.m_Front = src.m_Back = VMA_NULL;
4477             src.m_Count = 0;
4478         }
4479         return *this;
4480     }
RemoveAll()4481     void RemoveAll()
4482     {
4483         if(!IsEmpty())
4484         {
4485             ItemType* item = m_Back;
4486             while(item != VMA_NULL)
4487             {
4488                 ItemType* const prevItem = ItemTypeTraits::AccessPrev(item);
4489                 ItemTypeTraits::AccessPrev(item) = VMA_NULL;
4490                 ItemTypeTraits::AccessNext(item) = VMA_NULL;
4491                 item = prevItem;
4492             }
4493             m_Front = VMA_NULL;
4494             m_Back = VMA_NULL;
4495             m_Count = 0;
4496         }
4497     }
GetCount()4498     size_t GetCount() const { return m_Count; }
IsEmpty()4499     bool IsEmpty() const { return m_Count == 0; }
Front()4500     ItemType* Front() { return m_Front; }
Front()4501     const ItemType* Front() const { return m_Front; }
Back()4502     ItemType* Back() { return m_Back; }
Back()4503     const ItemType* Back() const { return m_Back; }
PushBack(ItemType * item)4504     void PushBack(ItemType* item)
4505     {
4506         VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL);
4507         if(IsEmpty())
4508         {
4509             m_Front = item;
4510             m_Back = item;
4511             m_Count = 1;
4512         }
4513         else
4514         {
4515             ItemTypeTraits::AccessPrev(item) = m_Back;
4516             ItemTypeTraits::AccessNext(m_Back) = item;
4517             m_Back = item;
4518             ++m_Count;
4519         }
4520     }
PushFront(ItemType * item)4521     void PushFront(ItemType* item)
4522     {
4523         VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL);
4524         if(IsEmpty())
4525         {
4526             m_Front = item;
4527             m_Back = item;
4528             m_Count = 1;
4529         }
4530         else
4531         {
4532             ItemTypeTraits::AccessNext(item) = m_Front;
4533             ItemTypeTraits::AccessPrev(m_Front) = item;
4534             m_Front = item;
4535             ++m_Count;
4536         }
4537     }
PopBack()4538     ItemType* PopBack()
4539     {
4540         VMA_HEAVY_ASSERT(m_Count > 0);
4541         ItemType* const backItem = m_Back;
4542         ItemType* const prevItem = ItemTypeTraits::GetPrev(backItem);
4543         if(prevItem != VMA_NULL)
4544         {
4545             ItemTypeTraits::AccessNext(prevItem) = VMA_NULL;
4546         }
4547         m_Back = prevItem;
4548         --m_Count;
4549         ItemTypeTraits::AccessPrev(backItem) = VMA_NULL;
4550         ItemTypeTraits::AccessNext(backItem) = VMA_NULL;
4551         return backItem;
4552     }
PopFront()4553     ItemType* PopFront()
4554     {
4555         VMA_HEAVY_ASSERT(m_Count > 0);
4556         ItemType* const frontItem = m_Front;
4557         ItemType* const nextItem = ItemTypeTraits::GetNext(frontItem);
4558         if(nextItem != VMA_NULL)
4559         {
4560             ItemTypeTraits::AccessPrev(nextItem) = VMA_NULL;
4561         }
4562         m_Front = nextItem;
4563         --m_Count;
4564         ItemTypeTraits::AccessPrev(frontItem) = VMA_NULL;
4565         ItemTypeTraits::AccessNext(frontItem) = VMA_NULL;
4566         return frontItem;
4567     }
4568 
4569     // MyItem can be null - it means PushBack.
InsertBefore(ItemType * existingItem,ItemType * newItem)4570     void InsertBefore(ItemType* existingItem, ItemType* newItem)
4571     {
4572         VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL);
4573         if(existingItem != VMA_NULL)
4574         {
4575             ItemType* const prevItem = ItemTypeTraits::GetPrev(existingItem);
4576             ItemTypeTraits::AccessPrev(newItem) = prevItem;
4577             ItemTypeTraits::AccessNext(newItem) = existingItem;
4578             ItemTypeTraits::AccessPrev(existingItem) = newItem;
4579             if(prevItem != VMA_NULL)
4580             {
4581                 ItemTypeTraits::AccessNext(prevItem) = newItem;
4582             }
4583             else
4584             {
4585                 VMA_HEAVY_ASSERT(m_Front == existingItem);
4586                 m_Front = newItem;
4587             }
4588             ++m_Count;
4589         }
4590         else
4591             PushBack(newItem);
4592     }
4593     // MyItem can be null - it means PushFront.
InsertAfter(ItemType * existingItem,ItemType * newItem)4594     void InsertAfter(ItemType* existingItem, ItemType* newItem)
4595     {
4596         VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL);
4597         if(existingItem != VMA_NULL)
4598         {
4599             ItemType* const nextItem = ItemTypeTraits::GetNext(existingItem);
4600             ItemTypeTraits::AccessNext(newItem) = nextItem;
4601             ItemTypeTraits::AccessPrev(newItem) = existingItem;
4602             ItemTypeTraits::AccessNext(existingItem) = newItem;
4603             if(nextItem != VMA_NULL)
4604             {
4605                 ItemTypeTraits::AccessPrev(nextItem) = newItem;
4606             }
4607             else
4608             {
4609                 VMA_HEAVY_ASSERT(m_Back == existingItem);
4610                 m_Back = newItem;
4611             }
4612             ++m_Count;
4613         }
4614         else
4615             return PushFront(newItem);
4616     }
Remove(ItemType * item)4617     void Remove(ItemType* item)
4618     {
4619         VMA_HEAVY_ASSERT(item != VMA_NULL && m_Count > 0);
4620         if(ItemTypeTraits::GetPrev(item) != VMA_NULL)
4621         {
4622             ItemTypeTraits::AccessNext(ItemTypeTraits::AccessPrev(item)) = ItemTypeTraits::GetNext(item);
4623         }
4624         else
4625         {
4626             VMA_HEAVY_ASSERT(m_Front == item);
4627             m_Front = ItemTypeTraits::GetNext(item);
4628         }
4629 
4630         if(ItemTypeTraits::GetNext(item) != VMA_NULL)
4631         {
4632             ItemTypeTraits::AccessPrev(ItemTypeTraits::AccessNext(item)) = ItemTypeTraits::GetPrev(item);
4633         }
4634         else
4635         {
4636             VMA_HEAVY_ASSERT(m_Back == item);
4637             m_Back = ItemTypeTraits::GetPrev(item);
4638         }
4639         ItemTypeTraits::AccessPrev(item) = VMA_NULL;
4640         ItemTypeTraits::AccessNext(item) = VMA_NULL;
4641         --m_Count;
4642     }
4643 private:
4644     ItemType* m_Front = VMA_NULL;
4645     ItemType* m_Back = VMA_NULL;
4646     size_t m_Count = 0;
4647 };
4648 
4649 ////////////////////////////////////////////////////////////////////////////////
4650 // class VmaMap
4651 
4652 // Unused in this version.
4653 #if 0
4654 
4655 #if VMA_USE_STL_UNORDERED_MAP
4656 
4657 #define VmaPair std::pair
4658 
4659 #define VMA_MAP_TYPE(KeyT, ValueT) \
4660     std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
4661 
4662 #else // #if VMA_USE_STL_UNORDERED_MAP
4663 
4664 template<typename T1, typename T2>
4665 struct VmaPair
4666 {
4667     T1 first;
4668     T2 second;
4669 
4670     VmaPair() : first(), second() { }
4671     VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
4672 };
4673 
4674 /* Class compatible with subset of interface of std::unordered_map.
4675 KeyT, ValueT must be POD because they will be stored in VmaVector.
4676 */
4677 template<typename KeyT, typename ValueT>
4678 class VmaMap
4679 {
4680 public:
4681     typedef VmaPair<KeyT, ValueT> PairType;
4682     typedef PairType* iterator;
4683 
4684     VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
4685 
4686     iterator begin() { return m_Vector.begin(); }
4687     iterator end() { return m_Vector.end(); }
4688 
4689     void insert(const PairType& pair);
4690     iterator find(const KeyT& key);
4691     void erase(iterator it);
4692 
4693 private:
4694     VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
4695 };
4696 
4697 #define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
4698 
4699 template<typename FirstT, typename SecondT>
4700 struct VmaPairFirstLess
4701 {
4702     bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
4703     {
4704         return lhs.first < rhs.first;
4705     }
4706     bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
4707     {
4708         return lhs.first < rhsFirst;
4709     }
4710 };
4711 
4712 template<typename KeyT, typename ValueT>
4713 void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
4714 {
4715     const size_t indexToInsert = VmaBinaryFindFirstNotLess(
4716         m_Vector.data(),
4717         m_Vector.data() + m_Vector.size(),
4718         pair,
4719         VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
4720     VmaVectorInsert(m_Vector, indexToInsert, pair);
4721 }
4722 
4723 template<typename KeyT, typename ValueT>
4724 VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
4725 {
4726     PairType* it = VmaBinaryFindFirstNotLess(
4727         m_Vector.data(),
4728         m_Vector.data() + m_Vector.size(),
4729         key,
4730         VmaPairFirstLess<KeyT, ValueT>());
4731     if((it != m_Vector.end()) && (it->first == key))
4732     {
4733         return it;
4734     }
4735     else
4736     {
4737         return m_Vector.end();
4738     }
4739 }
4740 
4741 template<typename KeyT, typename ValueT>
4742 void VmaMap<KeyT, ValueT>::erase(iterator it)
4743 {
4744     VmaVectorRemove(m_Vector, it - m_Vector.begin());
4745 }
4746 
4747 #endif // #if VMA_USE_STL_UNORDERED_MAP
4748 
4749 #endif // #if 0
4750 
4751 ////////////////////////////////////////////////////////////////////////////////
4752 
4753 class VmaDeviceMemoryBlock;
4754 
4755 enum VMA_CACHE_OPERATION { VMA_CACHE_FLUSH, VMA_CACHE_INVALIDATE };
4756 
4757 struct VmaAllocation_T
4758 {
4759 private:
4760     static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
4761 
4762     enum FLAGS
4763     {
4764         FLAG_USER_DATA_STRING = 0x01,
4765     };
4766 
4767 public:
4768     enum ALLOCATION_TYPE
4769     {
4770         ALLOCATION_TYPE_NONE,
4771         ALLOCATION_TYPE_BLOCK,
4772         ALLOCATION_TYPE_DEDICATED,
4773     };
4774 
4775     /*
4776     This struct is allocated using VmaPoolAllocator.
4777     */
4778 
VmaAllocation_TVmaAllocation_T4779     VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
4780         m_Alignment{1},
4781         m_Size{0},
4782         m_pUserData{VMA_NULL},
4783         m_LastUseFrameIndex{currentFrameIndex},
4784         m_MemoryTypeIndex{0},
4785         m_Type{(uint8_t)ALLOCATION_TYPE_NONE},
4786         m_SuballocationType{(uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN},
4787         m_MapCount{0},
4788         m_Flags{userDataString ? (uint8_t)FLAG_USER_DATA_STRING : (uint8_t)0}
4789     {
4790 #if VMA_STATS_STRING_ENABLED
4791         m_CreationFrameIndex = currentFrameIndex;
4792         m_BufferImageUsage = 0;
4793 #endif
4794     }
4795 
~VmaAllocation_TVmaAllocation_T4796     ~VmaAllocation_T()
4797     {
4798         VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
4799 
4800         // Check if owned string was freed.
4801         VMA_ASSERT(m_pUserData == VMA_NULL);
4802     }
4803 
InitBlockAllocationVmaAllocation_T4804     void InitBlockAllocation(
4805         VmaDeviceMemoryBlock* block,
4806         VkDeviceSize offset,
4807         VkDeviceSize alignment,
4808         VkDeviceSize size,
4809         uint32_t memoryTypeIndex,
4810         VmaSuballocationType suballocationType,
4811         bool mapped,
4812         bool canBecomeLost)
4813     {
4814         VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4815         VMA_ASSERT(block != VMA_NULL);
4816         m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
4817         m_Alignment = alignment;
4818         m_Size = size;
4819         m_MemoryTypeIndex = memoryTypeIndex;
4820         m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
4821         m_SuballocationType = (uint8_t)suballocationType;
4822         m_BlockAllocation.m_Block = block;
4823         m_BlockAllocation.m_Offset = offset;
4824         m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
4825     }
4826 
InitLostVmaAllocation_T4827     void InitLost()
4828     {
4829         VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4830         VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
4831         m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
4832         m_MemoryTypeIndex = 0;
4833         m_BlockAllocation.m_Block = VMA_NULL;
4834         m_BlockAllocation.m_Offset = 0;
4835         m_BlockAllocation.m_CanBecomeLost = true;
4836     }
4837 
4838     void ChangeBlockAllocation(
4839         VmaAllocator hAllocator,
4840         VmaDeviceMemoryBlock* block,
4841         VkDeviceSize offset);
4842 
4843     void ChangeOffset(VkDeviceSize newOffset);
4844 
4845     // pMappedData not null means allocation is created with MAPPED flag.
InitDedicatedAllocationVmaAllocation_T4846     void InitDedicatedAllocation(
4847         uint32_t memoryTypeIndex,
4848         VkDeviceMemory hMemory,
4849         VmaSuballocationType suballocationType,
4850         void* pMappedData,
4851         VkDeviceSize size)
4852     {
4853         VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4854         VMA_ASSERT(hMemory != VK_NULL_HANDLE);
4855         m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
4856         m_Alignment = 0;
4857         m_Size = size;
4858         m_MemoryTypeIndex = memoryTypeIndex;
4859         m_SuballocationType = (uint8_t)suballocationType;
4860         m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
4861         m_DedicatedAllocation.m_hMemory = hMemory;
4862         m_DedicatedAllocation.m_pMappedData = pMappedData;
4863         m_DedicatedAllocation.m_Prev = VMA_NULL;
4864         m_DedicatedAllocation.m_Next = VMA_NULL;
4865     }
4866 
GetTypeVmaAllocation_T4867     ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
GetAlignmentVmaAllocation_T4868     VkDeviceSize GetAlignment() const { return m_Alignment; }
GetSizeVmaAllocation_T4869     VkDeviceSize GetSize() const { return m_Size; }
IsUserDataStringVmaAllocation_T4870     bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
GetUserDataVmaAllocation_T4871     void* GetUserData() const { return m_pUserData; }
4872     void SetUserData(VmaAllocator hAllocator, void* pUserData);
GetSuballocationTypeVmaAllocation_T4873     VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
4874 
GetBlockVmaAllocation_T4875     VmaDeviceMemoryBlock* GetBlock() const
4876     {
4877         VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
4878         return m_BlockAllocation.m_Block;
4879     }
4880     VkDeviceSize GetOffset() const;
4881     VkDeviceMemory GetMemory() const;
GetMemoryTypeIndexVmaAllocation_T4882     uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
IsPersistentMapVmaAllocation_T4883     bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
4884     void* GetMappedData() const;
4885     bool CanBecomeLost() const;
4886 
GetLastUseFrameIndexVmaAllocation_T4887     uint32_t GetLastUseFrameIndex() const
4888     {
4889         return m_LastUseFrameIndex.load();
4890     }
CompareExchangeLastUseFrameIndexVmaAllocation_T4891     bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
4892     {
4893         return m_LastUseFrameIndex.compare_exchange_weak(expected, desired);
4894     }
4895     /*
4896     - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
4897       makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
4898     - Else, returns false.
4899 
4900     If hAllocation is already lost, assert - you should not call it then.
4901     If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
4902     */
4903     bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
4904 
DedicatedAllocCalcStatsInfoVmaAllocation_T4905     void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
4906     {
4907         VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
4908         outInfo.blockCount = 1;
4909         outInfo.allocationCount = 1;
4910         outInfo.unusedRangeCount = 0;
4911         outInfo.usedBytes = m_Size;
4912         outInfo.unusedBytes = 0;
4913         outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
4914         outInfo.unusedRangeSizeMin = UINT64_MAX;
4915         outInfo.unusedRangeSizeMax = 0;
4916     }
4917 
4918     void BlockAllocMap();
4919     void BlockAllocUnmap();
4920     VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
4921     void DedicatedAllocUnmap(VmaAllocator hAllocator);
4922 
4923 #if VMA_STATS_STRING_ENABLED
GetCreationFrameIndexVmaAllocation_T4924     uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; }
GetBufferImageUsageVmaAllocation_T4925     uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
4926 
InitBufferImageUsageVmaAllocation_T4927     void InitBufferImageUsage(uint32_t bufferImageUsage)
4928     {
4929         VMA_ASSERT(m_BufferImageUsage == 0);
4930         m_BufferImageUsage = bufferImageUsage;
4931     }
4932 
4933     void PrintParameters(class VmaJsonWriter& json) const;
4934 #endif
4935 
4936 private:
4937     VkDeviceSize m_Alignment;
4938     VkDeviceSize m_Size;
4939     void* m_pUserData;
4940     VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
4941     uint32_t m_MemoryTypeIndex;
4942     uint8_t m_Type; // ALLOCATION_TYPE
4943     uint8_t m_SuballocationType; // VmaSuballocationType
4944     // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
4945     // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
4946     uint8_t m_MapCount;
4947     uint8_t m_Flags; // enum FLAGS
4948 
4949     // Allocation out of VmaDeviceMemoryBlock.
4950     struct BlockAllocation
4951     {
4952         VmaDeviceMemoryBlock* m_Block;
4953         VkDeviceSize m_Offset;
4954         bool m_CanBecomeLost;
4955     };
4956 
4957     // Allocation for an object that has its own private VkDeviceMemory.
4958     struct DedicatedAllocation
4959     {
4960         VkDeviceMemory m_hMemory;
4961         void* m_pMappedData; // Not null means memory is mapped.
4962         VmaAllocation_T* m_Prev;
4963         VmaAllocation_T* m_Next;
4964     };
4965 
4966     union
4967     {
4968         // Allocation out of VmaDeviceMemoryBlock.
4969         BlockAllocation m_BlockAllocation;
4970         // Allocation for an object that has its own private VkDeviceMemory.
4971         DedicatedAllocation m_DedicatedAllocation;
4972     };
4973 
4974 #if VMA_STATS_STRING_ENABLED
4975     uint32_t m_CreationFrameIndex;
4976     uint32_t m_BufferImageUsage; // 0 if unknown.
4977 #endif
4978 
4979     void FreeUserDataString(VmaAllocator hAllocator);
4980 
4981     friend struct VmaDedicatedAllocationListItemTraits;
4982 };
4983 
4984 struct VmaDedicatedAllocationListItemTraits
4985 {
4986     typedef VmaAllocation_T ItemType;
GetPrevVmaDedicatedAllocationListItemTraits4987     static ItemType* GetPrev(const ItemType* item)
4988     {
4989         VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
4990         return item->m_DedicatedAllocation.m_Prev;
4991     }
GetNextVmaDedicatedAllocationListItemTraits4992     static ItemType* GetNext(const ItemType* item)
4993     {
4994         VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
4995         return item->m_DedicatedAllocation.m_Next;
4996     }
AccessPrevVmaDedicatedAllocationListItemTraits4997     static ItemType*& AccessPrev(ItemType* item)
4998     {
4999         VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
5000         return item->m_DedicatedAllocation.m_Prev;
5001     }
AccessNextVmaDedicatedAllocationListItemTraits5002     static ItemType*& AccessNext(ItemType* item){
5003         VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
5004         return item->m_DedicatedAllocation.m_Next;
5005     }
5006 };
5007 
5008 /*
5009 Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
5010 allocated memory block or free.
5011 */
5012 struct VmaSuballocation
5013 {
5014     VkDeviceSize offset;
5015     VkDeviceSize size;
5016     void* userData;
5017     VmaSuballocationType type;
5018 };
5019 
5020 // Comparator for offsets.
5021 struct VmaSuballocationOffsetLess
5022 {
operatorVmaSuballocationOffsetLess5023     bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
5024     {
5025         return lhs.offset < rhs.offset;
5026     }
5027 };
5028 struct VmaSuballocationOffsetGreater
5029 {
operatorVmaSuballocationOffsetGreater5030     bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
5031     {
5032         return lhs.offset > rhs.offset;
5033     }
5034 };
5035 
5036 typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
5037 
5038 // Cost of one additional allocation lost, as equivalent in bytes.
5039 static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
5040 
5041 enum class VmaAllocationRequestType
5042 {
5043     Normal,
5044     // Used by "Linear" algorithm.
5045     UpperAddress,
5046     EndOf1st,
5047     EndOf2nd,
5048 };
5049 
5050 /*
5051 Parameters of planned allocation inside a VmaDeviceMemoryBlock.
5052 
5053 If canMakeOtherLost was false:
5054 - item points to a FREE suballocation.
5055 - itemsToMakeLostCount is 0.
5056 
5057 If canMakeOtherLost was true:
5058 - item points to first of sequence of suballocations, which are either FREE,
5059   or point to VmaAllocations that can become lost.
5060 - itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
5061   the requested allocation to succeed.
5062 */
5063 struct VmaAllocationRequest
5064 {
5065     VkDeviceSize offset;
5066     VkDeviceSize size;
5067     VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
5068     VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
5069     VmaSuballocationList::iterator item;
5070     size_t itemsToMakeLostCount;
5071     void* customData;
5072     VmaAllocationRequestType type;
5073 
CalcCostVmaAllocationRequest5074     VkDeviceSize CalcCost() const
5075     {
5076         return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
5077     }
5078 };
5079 
5080 /*
5081 Data structure used for bookkeeping of allocations and unused ranges of memory
5082 in a single VkDeviceMemory block.
5083 */
5084 class VmaBlockMetadata
5085 {
5086 public:
5087     // pAllocationCallbacks, if not null, must be owned externally - alive and unchanged for the whole lifetime of this object.
5088     VmaBlockMetadata(const VkAllocationCallbacks* pAllocationCallbacks, bool isVirtual);
~VmaBlockMetadata()5089     virtual ~VmaBlockMetadata() { }
Init(VkDeviceSize size)5090     virtual void Init(VkDeviceSize size) { m_Size = size; }
5091 
5092     // Validates all data structures inside this object. If not valid, returns false.
5093     virtual bool Validate() const = 0;
IsVirtual()5094     bool IsVirtual() const { return m_IsVirtual; }
GetSize()5095     VkDeviceSize GetSize() const { return m_Size; }
5096     virtual size_t GetAllocationCount() const = 0;
5097     virtual VkDeviceSize GetSumFreeSize() const = 0;
5098     virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0;
5099     // Returns true if this block is empty - contains only single free suballocation.
5100     virtual bool IsEmpty() const = 0;
5101     virtual void GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo) = 0;
5102 
5103     // Must set blockCount to 1.
5104     virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
5105     // Shouldn't modify blockCount.
5106     virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
5107 
5108 #if VMA_STATS_STRING_ENABLED
5109     virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
5110 #endif
5111 
5112     // Tries to find a place for suballocation with given parameters inside this block.
5113     // If succeeded, fills pAllocationRequest and returns true.
5114     // If failed, returns false.
5115     virtual bool CreateAllocationRequest(
5116         uint32_t currentFrameIndex,
5117         uint32_t frameInUseCount,
5118         VkDeviceSize bufferImageGranularity,
5119         VkDeviceSize allocSize,
5120         VkDeviceSize allocAlignment,
5121         bool upperAddress,
5122         VmaSuballocationType allocType,
5123         bool canMakeOtherLost,
5124         // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
5125         uint32_t strategy,
5126         VmaAllocationRequest* pAllocationRequest) = 0;
5127 
5128     virtual bool MakeRequestedAllocationsLost(
5129         uint32_t currentFrameIndex,
5130         uint32_t frameInUseCount,
5131         VmaAllocationRequest* pAllocationRequest) = 0;
5132 
5133     virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0;
5134 
5135     virtual VkResult CheckCorruption(const void* pBlockData) = 0;
5136 
5137     // Makes actual allocation based on request. Request must already be checked and valid.
5138     virtual void Alloc(
5139         const VmaAllocationRequest& request,
5140         VmaSuballocationType type,
5141         void* userData) = 0;
5142 
5143     // Frees suballocation assigned to given memory region.
5144     virtual void FreeAtOffset(VkDeviceSize offset) = 0;
5145 
5146     // Frees all allocations.
5147     // Careful! Don't call it if there are VmaAllocation objects owned by userData of cleared allocations!
5148     virtual void Clear() = 0;
5149 
5150     virtual void SetAllocationUserData(VkDeviceSize offset, void* userData) = 0;
5151 
5152 protected:
GetAllocationCallbacks()5153     const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
GetDebugMargin()5154     VkDeviceSize GetDebugMargin() const
5155     {
5156         return IsVirtual() ? 0 : VMA_DEBUG_MARGIN;
5157     }
5158 
5159 #if VMA_STATS_STRING_ENABLED
5160     void PrintDetailedMap_Begin(class VmaJsonWriter& json,
5161         VkDeviceSize unusedBytes,
5162         size_t allocationCount,
5163         size_t unusedRangeCount) const;
5164     void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
5165         VkDeviceSize offset, VkDeviceSize size, void* userData) const;
5166     void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
5167         VkDeviceSize offset,
5168         VkDeviceSize size) const;
5169     void PrintDetailedMap_End(class VmaJsonWriter& json) const;
5170 #endif
5171 
5172 private:
5173     VkDeviceSize m_Size;
5174     const VkAllocationCallbacks* m_pAllocationCallbacks;
5175     const bool m_IsVirtual;
5176 };
5177 
5178 #define VMA_VALIDATE(cond) do { if(!(cond)) { \
5179         VMA_ASSERT(0 && "Validation failed: " #cond); \
5180         return false; \
5181     } } while(false)
5182 
5183 class VmaBlockMetadata_Generic : public VmaBlockMetadata
5184 {
5185     VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
5186 public:
5187     VmaBlockMetadata_Generic(const VkAllocationCallbacks* pAllocationCallbacks, bool isVirtual);
5188     virtual ~VmaBlockMetadata_Generic();
5189     virtual void Init(VkDeviceSize size);
5190 
5191     virtual bool Validate() const;
GetAllocationCount()5192     virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
GetSumFreeSize()5193     virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
5194     virtual VkDeviceSize GetUnusedRangeSizeMax() const;
5195     virtual bool IsEmpty() const;
5196 
5197     virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5198     virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5199 
5200 #if VMA_STATS_STRING_ENABLED
5201     virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5202 #endif
5203 
5204     virtual bool CreateAllocationRequest(
5205         uint32_t currentFrameIndex,
5206         uint32_t frameInUseCount,
5207         VkDeviceSize bufferImageGranularity,
5208         VkDeviceSize allocSize,
5209         VkDeviceSize allocAlignment,
5210         bool upperAddress,
5211         VmaSuballocationType allocType,
5212         bool canMakeOtherLost,
5213         uint32_t strategy,
5214         VmaAllocationRequest* pAllocationRequest);
5215 
5216     virtual bool MakeRequestedAllocationsLost(
5217         uint32_t currentFrameIndex,
5218         uint32_t frameInUseCount,
5219         VmaAllocationRequest* pAllocationRequest);
5220 
5221     virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5222 
5223     virtual VkResult CheckCorruption(const void* pBlockData);
5224 
5225     virtual void Alloc(
5226         const VmaAllocationRequest& request,
5227         VmaSuballocationType type,
5228         void* userData);
5229 
5230     virtual void FreeAtOffset(VkDeviceSize offset);
5231     virtual void GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo);
5232     virtual void Clear();
5233     virtual void SetAllocationUserData(VkDeviceSize offset, void* userData);
5234 
5235     ////////////////////////////////////////////////////////////////////////////////
5236     // For defragmentation
5237 
5238     bool IsBufferImageGranularityConflictPossible(
5239         VkDeviceSize bufferImageGranularity,
5240         VmaSuballocationType& inOutPrevSuballocType) const;
5241 
5242 private:
5243     friend class VmaDefragmentationAlgorithm_Generic;
5244     friend class VmaDefragmentationAlgorithm_Fast;
5245 
5246     uint32_t m_FreeCount;
5247     VkDeviceSize m_SumFreeSize;
5248     VmaSuballocationList m_Suballocations;
5249     // Suballocations that are free. Sorted by size, ascending.
5250     VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
5251 
AlignAllocationSize(VkDeviceSize size)5252     VkDeviceSize AlignAllocationSize(VkDeviceSize size) const
5253     {
5254         return IsVirtual() ? size : VmaAlignUp(size, (VkDeviceSize)16);
5255     }
5256 
5257     bool ValidateFreeSuballocationList() const;
5258 
5259     // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
5260     // If yes, fills pOffset and returns true. If no, returns false.
5261     bool CheckAllocation(
5262         uint32_t currentFrameIndex,
5263         uint32_t frameInUseCount,
5264         VkDeviceSize bufferImageGranularity,
5265         VkDeviceSize allocSize,
5266         VkDeviceSize allocAlignment,
5267         VmaSuballocationType allocType,
5268         VmaSuballocationList::const_iterator suballocItem,
5269         bool canMakeOtherLost,
5270         VkDeviceSize* pOffset,
5271         size_t* itemsToMakeLostCount,
5272         VkDeviceSize* pSumFreeSize,
5273         VkDeviceSize* pSumItemSize) const;
5274     // Given free suballocation, it merges it with following one, which must also be free.
5275     void MergeFreeWithNext(VmaSuballocationList::iterator item);
5276     // Releases given suballocation, making it free.
5277     // Merges it with adjacent free suballocations if applicable.
5278     // Returns iterator to new free suballocation at this place.
5279     VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
5280     // Given free suballocation, it inserts it into sorted list of
5281     // m_FreeSuballocationsBySize if it is suitable.
5282     void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
5283     // Given free suballocation, it removes it from sorted list of
5284     // m_FreeSuballocationsBySize if it is suitable.
5285     void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
5286 };
5287 
5288 /*
5289 Allocations and their references in internal data structure look like this:
5290 
5291 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
5292 
5293         0 +-------+
5294           |       |
5295           |       |
5296           |       |
5297           +-------+
5298           | Alloc |  1st[m_1stNullItemsBeginCount]
5299           +-------+
5300           | Alloc |  1st[m_1stNullItemsBeginCount + 1]
5301           +-------+
5302           |  ...  |
5303           +-------+
5304           | Alloc |  1st[1st.size() - 1]
5305           +-------+
5306           |       |
5307           |       |
5308           |       |
5309 GetSize() +-------+
5310 
5311 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
5312 
5313         0 +-------+
5314           | Alloc |  2nd[0]
5315           +-------+
5316           | Alloc |  2nd[1]
5317           +-------+
5318           |  ...  |
5319           +-------+
5320           | Alloc |  2nd[2nd.size() - 1]
5321           +-------+
5322           |       |
5323           |       |
5324           |       |
5325           +-------+
5326           | Alloc |  1st[m_1stNullItemsBeginCount]
5327           +-------+
5328           | Alloc |  1st[m_1stNullItemsBeginCount + 1]
5329           +-------+
5330           |  ...  |
5331           +-------+
5332           | Alloc |  1st[1st.size() - 1]
5333           +-------+
5334           |       |
5335 GetSize() +-------+
5336 
5337 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
5338 
5339         0 +-------+
5340           |       |
5341           |       |
5342           |       |
5343           +-------+
5344           | Alloc |  1st[m_1stNullItemsBeginCount]
5345           +-------+
5346           | Alloc |  1st[m_1stNullItemsBeginCount + 1]
5347           +-------+
5348           |  ...  |
5349           +-------+
5350           | Alloc |  1st[1st.size() - 1]
5351           +-------+
5352           |       |
5353           |       |
5354           |       |
5355           +-------+
5356           | Alloc |  2nd[2nd.size() - 1]
5357           +-------+
5358           |  ...  |
5359           +-------+
5360           | Alloc |  2nd[1]
5361           +-------+
5362           | Alloc |  2nd[0]
5363 GetSize() +-------+
5364 
5365 */
5366 class VmaBlockMetadata_Linear : public VmaBlockMetadata
5367 {
5368     VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
5369 public:
5370     VmaBlockMetadata_Linear(const VkAllocationCallbacks* pAllocationCallbacks, bool isVirtual);
5371     virtual ~VmaBlockMetadata_Linear();
5372     virtual void Init(VkDeviceSize size);
5373 
5374     virtual bool Validate() const;
5375     virtual size_t GetAllocationCount() const;
GetSumFreeSize()5376     virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
5377     virtual VkDeviceSize GetUnusedRangeSizeMax() const;
IsEmpty()5378     virtual bool IsEmpty() const { return GetAllocationCount() == 0; }
5379 
5380     virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5381     virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5382 
5383 #if VMA_STATS_STRING_ENABLED
5384     virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5385 #endif
5386 
5387     virtual bool CreateAllocationRequest(
5388         uint32_t currentFrameIndex,
5389         uint32_t frameInUseCount,
5390         VkDeviceSize bufferImageGranularity,
5391         VkDeviceSize allocSize,
5392         VkDeviceSize allocAlignment,
5393         bool upperAddress,
5394         VmaSuballocationType allocType,
5395         bool canMakeOtherLost,
5396         uint32_t strategy,
5397         VmaAllocationRequest* pAllocationRequest);
5398 
5399     virtual bool MakeRequestedAllocationsLost(
5400         uint32_t currentFrameIndex,
5401         uint32_t frameInUseCount,
5402         VmaAllocationRequest* pAllocationRequest);
5403 
5404     virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5405 
5406     virtual VkResult CheckCorruption(const void* pBlockData);
5407 
5408     virtual void Alloc(
5409         const VmaAllocationRequest& request,
5410         VmaSuballocationType type,
5411         void* userData);
5412 
5413     virtual void FreeAtOffset(VkDeviceSize offset);
5414     virtual void GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo);
5415     virtual void Clear();
5416     virtual void SetAllocationUserData(VkDeviceSize offset, void* userData);
5417 
5418 private:
5419     /*
5420     There are two suballocation vectors, used in ping-pong way.
5421     The one with index m_1stVectorIndex is called 1st.
5422     The one with index (m_1stVectorIndex ^ 1) is called 2nd.
5423     2nd can be non-empty only when 1st is not empty.
5424     When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
5425     */
5426     typedef VmaVector< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > SuballocationVectorType;
5427 
5428     enum SECOND_VECTOR_MODE
5429     {
5430         SECOND_VECTOR_EMPTY,
5431         /*
5432         Suballocations in 2nd vector are created later than the ones in 1st, but they
5433         all have smaller offset.
5434         */
5435         SECOND_VECTOR_RING_BUFFER,
5436         /*
5437         Suballocations in 2nd vector are upper side of double stack.
5438         They all have offsets higher than those in 1st vector.
5439         Top of this stack means smaller offsets, but higher indices in this vector.
5440         */
5441         SECOND_VECTOR_DOUBLE_STACK,
5442     };
5443 
5444     VkDeviceSize m_SumFreeSize;
5445     SuballocationVectorType m_Suballocations0, m_Suballocations1;
5446     uint32_t m_1stVectorIndex;
5447     SECOND_VECTOR_MODE m_2ndVectorMode;
5448 
AccessSuballocations1st()5449     SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
AccessSuballocations2nd()5450     SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
AccessSuballocations1st()5451     const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
AccessSuballocations2nd()5452     const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
5453     VmaSuballocation& FindSuballocation(VkDeviceSize offset);
5454 
5455     // Number of items in 1st vector with hAllocation = null at the beginning.
5456     size_t m_1stNullItemsBeginCount;
5457     // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
5458     size_t m_1stNullItemsMiddleCount;
5459     // Number of items in 2nd vector with hAllocation = null.
5460     size_t m_2ndNullItemsCount;
5461 
5462     bool ShouldCompact1st() const;
5463     void CleanupAfterFree();
5464 
5465     bool CreateAllocationRequest_LowerAddress(
5466         uint32_t currentFrameIndex,
5467         uint32_t frameInUseCount,
5468         VkDeviceSize bufferImageGranularity,
5469         VkDeviceSize allocSize,
5470         VkDeviceSize allocAlignment,
5471         VmaSuballocationType allocType,
5472         bool canMakeOtherLost,
5473         uint32_t strategy,
5474         VmaAllocationRequest* pAllocationRequest);
5475     bool CreateAllocationRequest_UpperAddress(
5476         uint32_t currentFrameIndex,
5477         uint32_t frameInUseCount,
5478         VkDeviceSize bufferImageGranularity,
5479         VkDeviceSize allocSize,
5480         VkDeviceSize allocAlignment,
5481         VmaSuballocationType allocType,
5482         bool canMakeOtherLost,
5483         uint32_t strategy,
5484         VmaAllocationRequest* pAllocationRequest);
5485 };
5486 
5487 /*
5488 - GetSize() is the original size of allocated memory block.
5489 - m_UsableSize is this size aligned down to a power of two.
5490   All allocations and calculations happen relative to m_UsableSize.
5491 - GetUnusableSize() is the difference between them.
5492   It is reported as separate, unused range, not available for allocations.
5493 
5494 Node at level 0 has size = m_UsableSize.
5495 Each next level contains nodes with size 2 times smaller than current level.
5496 m_LevelCount is the maximum number of levels to use in the current object.
5497 */
5498 class VmaBlockMetadata_Buddy : public VmaBlockMetadata
5499 {
5500     VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
5501 public:
5502     VmaBlockMetadata_Buddy(const VkAllocationCallbacks* pAllocationCallbacks, bool isVirtual);
5503     virtual ~VmaBlockMetadata_Buddy();
5504     virtual void Init(VkDeviceSize size);
5505 
5506     virtual bool Validate() const;
GetAllocationCount()5507     virtual size_t GetAllocationCount() const { return m_AllocationCount; }
GetSumFreeSize()5508     virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); }
5509     virtual VkDeviceSize GetUnusedRangeSizeMax() const;
IsEmpty()5510     virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; }
5511 
5512     virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5513     virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5514 
5515 #if VMA_STATS_STRING_ENABLED
5516     virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5517 #endif
5518 
5519     virtual bool CreateAllocationRequest(
5520         uint32_t currentFrameIndex,
5521         uint32_t frameInUseCount,
5522         VkDeviceSize bufferImageGranularity,
5523         VkDeviceSize allocSize,
5524         VkDeviceSize allocAlignment,
5525         bool upperAddress,
5526         VmaSuballocationType allocType,
5527         bool canMakeOtherLost,
5528         uint32_t strategy,
5529         VmaAllocationRequest* pAllocationRequest);
5530 
5531     virtual bool MakeRequestedAllocationsLost(
5532         uint32_t currentFrameIndex,
5533         uint32_t frameInUseCount,
5534         VmaAllocationRequest* pAllocationRequest);
5535 
5536     virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5537 
CheckCorruption(const void * pBlockData)5538     virtual VkResult CheckCorruption(const void* pBlockData) { return VK_ERROR_FEATURE_NOT_PRESENT; }
5539 
5540     virtual void Alloc(
5541         const VmaAllocationRequest& request,
5542         VmaSuballocationType type,
5543         void* userData);
5544 
5545     virtual void FreeAtOffset(VkDeviceSize offset);
5546     virtual void GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo);
5547     virtual void Clear();
5548     virtual void SetAllocationUserData(VkDeviceSize offset, void* userData);
5549 
5550 private:
5551     static const size_t MAX_LEVELS = 48;
5552 
5553     struct ValidationContext
5554     {
5555         size_t calculatedAllocationCount = 0;
5556         size_t calculatedFreeCount = 0;
5557         VkDeviceSize calculatedSumFreeSize = 0;
5558     };
5559 
5560     struct Node
5561     {
5562         VkDeviceSize offset;
5563         enum TYPE
5564         {
5565             TYPE_FREE,
5566             TYPE_ALLOCATION,
5567             TYPE_SPLIT,
5568             TYPE_COUNT
5569         } type;
5570         Node* parent;
5571         Node* buddy;
5572 
5573         union
5574         {
5575             struct
5576             {
5577                 Node* prev;
5578                 Node* next;
5579             } free;
5580             struct
5581             {
5582                 void* userData;
5583             } allocation;
5584             struct
5585             {
5586                 Node* leftChild;
5587             } split;
5588         };
5589     };
5590 
5591     // Size of the memory block aligned down to a power of two.
5592     VkDeviceSize m_UsableSize;
5593     uint32_t m_LevelCount;
5594 
5595     VmaPoolAllocator<Node> m_NodeAllocator;
5596 
5597     Node* m_Root;
5598     struct {
5599         Node* front;
5600         Node* back;
5601     } m_FreeList[MAX_LEVELS];
5602     // Number of nodes in the tree with type == TYPE_ALLOCATION.
5603     size_t m_AllocationCount;
5604     // Number of nodes in the tree with type == TYPE_FREE.
5605     size_t m_FreeCount;
5606     // Doesn't include space wasted due to internal fragmentation - allocation sizes are just aligned up to node sizes.
5607     // Doesn't include unusable size.
5608     VkDeviceSize m_SumFreeSize;
5609 
AlignAllocationSize(VkDeviceSize size)5610     VkDeviceSize AlignAllocationSize(VkDeviceSize size) const
5611     {
5612         if(!IsVirtual())
5613         {
5614             size = VmaAlignUp(size, (VkDeviceSize)16);
5615         }
5616         return VmaNextPow2(size);
5617     }
GetUnusableSize()5618     VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
5619     Node* FindAllocationNode(VkDeviceSize offset, uint32_t& outLevel);
5620     void DeleteNodeChildren(Node* node);
5621     bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
5622     uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
LevelToNodeSize(uint32_t level)5623     inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
5624     void CalcAllocationStatInfoNode(VmaStatInfo& inoutInfo, const Node* node, VkDeviceSize levelNodeSize) const;
5625     // Adds node to the front of FreeList at given level.
5626     // node->type must be FREE.
5627     // node->free.prev, next can be undefined.
5628     void AddToFreeListFront(uint32_t level, Node* node);
5629     // Removes node from FreeList at given level.
5630     // node->type must be FREE.
5631     // node->free.prev, next stay untouched.
5632     void RemoveFromFreeList(uint32_t level, Node* node);
5633 
5634 #if VMA_STATS_STRING_ENABLED
5635     void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
5636 #endif
5637 };
5638 
5639 /*
5640 Represents a single block of device memory (`VkDeviceMemory`) with all the
5641 data about its regions (aka suballocations, #VmaAllocation), assigned and free.
5642 
5643 Thread-safety: This class must be externally synchronized.
5644 */
5645 class VmaDeviceMemoryBlock
5646 {
5647     VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
5648 public:
5649     VmaBlockMetadata* m_pMetadata;
5650 
5651     VmaDeviceMemoryBlock(VmaAllocator hAllocator);
5652 
~VmaDeviceMemoryBlock()5653     ~VmaDeviceMemoryBlock()
5654     {
5655         VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
5656         VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
5657     }
5658 
5659     // Always call after construction.
5660     void Init(
5661         VmaAllocator hAllocator,
5662         VmaPool hParentPool,
5663         uint32_t newMemoryTypeIndex,
5664         VkDeviceMemory newMemory,
5665         VkDeviceSize newSize,
5666         uint32_t id,
5667         uint32_t algorithm);
5668     // Always call before destruction.
5669     void Destroy(VmaAllocator allocator);
5670 
GetParentPool()5671     VmaPool GetParentPool() const { return m_hParentPool; }
GetDeviceMemory()5672     VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
GetMemoryTypeIndex()5673     uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetId()5674     uint32_t GetId() const { return m_Id; }
GetMappedData()5675     void* GetMappedData() const { return m_pMappedData; }
5676 
5677     // Validates all data structures inside this object. If not valid, returns false.
5678     bool Validate() const;
5679 
5680     VkResult CheckCorruption(VmaAllocator hAllocator);
5681 
5682     // ppData can be null.
5683     VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
5684     void Unmap(VmaAllocator hAllocator, uint32_t count);
5685 
5686     VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5687     VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5688 
5689     VkResult BindBufferMemory(
5690         const VmaAllocator hAllocator,
5691         const VmaAllocation hAllocation,
5692         VkDeviceSize allocationLocalOffset,
5693         VkBuffer hBuffer,
5694         const void* pNext);
5695     VkResult BindImageMemory(
5696         const VmaAllocator hAllocator,
5697         const VmaAllocation hAllocation,
5698         VkDeviceSize allocationLocalOffset,
5699         VkImage hImage,
5700         const void* pNext);
5701 
5702 private:
5703     VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
5704     uint32_t m_MemoryTypeIndex;
5705     uint32_t m_Id;
5706     VkDeviceMemory m_hMemory;
5707 
5708     /*
5709     Protects access to m_hMemory so it is not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
5710     Also protects m_MapCount, m_pMappedData.
5711     Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
5712     */
5713     VMA_MUTEX m_Mutex;
5714     uint32_t m_MapCount;
5715     void* m_pMappedData;
5716 };
5717 
5718 struct VmaDefragmentationMove
5719 {
5720     size_t srcBlockIndex;
5721     size_t dstBlockIndex;
5722     VkDeviceSize srcOffset;
5723     VkDeviceSize dstOffset;
5724     VkDeviceSize size;
5725     VmaAllocation hAllocation;
5726     VmaDeviceMemoryBlock* pSrcBlock;
5727     VmaDeviceMemoryBlock* pDstBlock;
5728 };
5729 
5730 class VmaDefragmentationAlgorithm;
5731 
5732 /*
5733 Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
5734 Vulkan memory type.
5735 
5736 Synchronized internally with a mutex.
5737 */
5738 struct VmaBlockVector
5739 {
5740     VMA_CLASS_NO_COPY(VmaBlockVector)
5741 public:
5742     VmaBlockVector(
5743         VmaAllocator hAllocator,
5744         VmaPool hParentPool,
5745         uint32_t memoryTypeIndex,
5746         VkDeviceSize preferredBlockSize,
5747         size_t minBlockCount,
5748         size_t maxBlockCount,
5749         VkDeviceSize bufferImageGranularity,
5750         uint32_t frameInUseCount,
5751         bool explicitBlockSize,
5752         uint32_t algorithm,
5753         float priority,
5754         VkDeviceSize minAllocationAlignment,
5755         void* pMemoryAllocateNext);
5756     ~VmaBlockVector();
5757 
5758     VkResult CreateMinBlocks();
5759 
GetAllocatorVmaBlockVector5760     VmaAllocator GetAllocator() const { return m_hAllocator; }
GetParentPoolVmaBlockVector5761     VmaPool GetParentPool() const { return m_hParentPool; }
IsCustomPoolVmaBlockVector5762     bool IsCustomPool() const { return m_hParentPool != VMA_NULL; }
GetMemoryTypeIndexVmaBlockVector5763     uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetPreferredBlockSizeVmaBlockVector5764     VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
GetBufferImageGranularityVmaBlockVector5765     VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
GetFrameInUseCountVmaBlockVector5766     uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
GetAlgorithmVmaBlockVector5767     uint32_t GetAlgorithm() const { return m_Algorithm; }
5768 
5769     void GetPoolStats(VmaPoolStats* pStats);
5770 
5771     bool IsEmpty();
5772     bool IsCorruptionDetectionEnabled() const;
5773 
5774     VkResult Allocate(
5775         uint32_t currentFrameIndex,
5776         VkDeviceSize size,
5777         VkDeviceSize alignment,
5778         const VmaAllocationCreateInfo& createInfo,
5779         VmaSuballocationType suballocType,
5780         size_t allocationCount,
5781         VmaAllocation* pAllocations);
5782 
5783     void Free(const VmaAllocation hAllocation);
5784 
5785     // Adds statistics of this BlockVector to pStats.
5786     void AddStats(VmaStats* pStats);
5787 
5788 #if VMA_STATS_STRING_ENABLED
5789     void PrintDetailedMap(class VmaJsonWriter& json);
5790 #endif
5791 
5792     void MakePoolAllocationsLost(
5793         uint32_t currentFrameIndex,
5794         size_t* pLostAllocationCount);
5795     VkResult CheckCorruption();
5796 
5797     // Saves results in pCtx->res.
5798     void Defragment(
5799         class VmaBlockVectorDefragmentationContext* pCtx,
5800         VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags,
5801         VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
5802         VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
5803         VkCommandBuffer commandBuffer);
5804     void DefragmentationEnd(
5805         class VmaBlockVectorDefragmentationContext* pCtx,
5806         uint32_t flags,
5807         VmaDefragmentationStats* pStats);
5808 
5809     uint32_t ProcessDefragmentations(
5810         class VmaBlockVectorDefragmentationContext *pCtx,
5811         VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves);
5812 
5813     void CommitDefragmentations(
5814         class VmaBlockVectorDefragmentationContext *pCtx,
5815         VmaDefragmentationStats* pStats);
5816 
5817     ////////////////////////////////////////////////////////////////////////////////
5818     // To be used only while the m_Mutex is locked. Used during defragmentation.
5819 
GetBlockCountVmaBlockVector5820     size_t GetBlockCount() const { return m_Blocks.size(); }
GetBlockVmaBlockVector5821     VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
5822     size_t CalcAllocationCount() const;
5823     bool IsBufferImageGranularityConflictPossible() const;
5824 
5825 private:
5826     friend class VmaDefragmentationAlgorithm_Generic;
5827 
5828     const VmaAllocator m_hAllocator;
5829     const VmaPool m_hParentPool;
5830     const uint32_t m_MemoryTypeIndex;
5831     const VkDeviceSize m_PreferredBlockSize;
5832     const size_t m_MinBlockCount;
5833     const size_t m_MaxBlockCount;
5834     const VkDeviceSize m_BufferImageGranularity;
5835     const uint32_t m_FrameInUseCount;
5836     const bool m_ExplicitBlockSize;
5837     const uint32_t m_Algorithm;
5838     const float m_Priority;
5839     const VkDeviceSize m_MinAllocationAlignment;
5840     void* const m_pMemoryAllocateNext;
5841     VMA_RW_MUTEX m_Mutex;
5842 
5843     /* There can be at most one allocation that is completely empty (except when minBlockCount > 0) -
5844     a hysteresis to avoid pessimistic case of alternating creation and destruction of a VkDeviceMemory. */
5845     bool m_HasEmptyBlock;
5846     // Incrementally sorted by sumFreeSize, ascending.
5847     VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
5848     uint32_t m_NextBlockId;
5849 
5850     VkDeviceSize CalcMaxBlockSize() const;
5851 
5852     // Finds and removes given block from vector.
5853     void Remove(VmaDeviceMemoryBlock* pBlock);
5854 
5855     // Performs single step in sorting m_Blocks. They may not be fully sorted
5856     // after this call.
5857     void IncrementallySortBlocks();
5858 
5859     VkResult AllocatePage(
5860         uint32_t currentFrameIndex,
5861         VkDeviceSize size,
5862         VkDeviceSize alignment,
5863         const VmaAllocationCreateInfo& createInfo,
5864         VmaSuballocationType suballocType,
5865         VmaAllocation* pAllocation);
5866 
5867     // To be used only without CAN_MAKE_OTHER_LOST flag.
5868     VkResult AllocateFromBlock(
5869         VmaDeviceMemoryBlock* pBlock,
5870         uint32_t currentFrameIndex,
5871         VkDeviceSize size,
5872         VkDeviceSize alignment,
5873         VmaAllocationCreateFlags allocFlags,
5874         void* pUserData,
5875         VmaSuballocationType suballocType,
5876         uint32_t strategy,
5877         VmaAllocation* pAllocation);
5878 
5879     VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
5880 
5881     // Saves result to pCtx->res.
5882     void ApplyDefragmentationMovesCpu(
5883         class VmaBlockVectorDefragmentationContext* pDefragCtx,
5884         const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
5885     // Saves result to pCtx->res.
5886     void ApplyDefragmentationMovesGpu(
5887         class VmaBlockVectorDefragmentationContext* pDefragCtx,
5888         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
5889         VkCommandBuffer commandBuffer);
5890 
5891     /*
5892     Used during defragmentation. pDefragmentationStats is optional. It is in/out
5893     - updated with new data.
5894     */
5895     void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
5896 
5897     void UpdateHasEmptyBlock();
5898 };
5899 
5900 struct VmaPool_T
5901 {
5902     VMA_CLASS_NO_COPY(VmaPool_T)
5903 public:
5904     VmaBlockVector m_BlockVector;
5905 
5906     VmaPool_T(
5907         VmaAllocator hAllocator,
5908         const VmaPoolCreateInfo& createInfo,
5909         VkDeviceSize preferredBlockSize);
5910     ~VmaPool_T();
5911 
GetIdVmaPool_T5912     uint32_t GetId() const { return m_Id; }
SetIdVmaPool_T5913     void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
5914 
GetNameVmaPool_T5915     const char* GetName() const { return m_Name; }
5916     void SetName(const char* pName);
5917 
5918 #if VMA_STATS_STRING_ENABLED
5919     //void PrintDetailedMap(class VmaStringBuilder& sb);
5920 #endif
5921 
5922 private:
5923     uint32_t m_Id;
5924     char* m_Name;
5925     VmaPool_T* m_PrevPool = VMA_NULL;
5926     VmaPool_T* m_NextPool = VMA_NULL;
5927     friend struct VmaPoolListItemTraits;
5928 };
5929 
5930 struct VmaPoolListItemTraits
5931 {
5932     typedef VmaPool_T ItemType;
GetPrevVmaPoolListItemTraits5933     static ItemType* GetPrev(const ItemType* item) { return item->m_PrevPool; }
GetNextVmaPoolListItemTraits5934     static ItemType* GetNext(const ItemType* item) { return item->m_NextPool; }
AccessPrevVmaPoolListItemTraits5935     static ItemType*& AccessPrev(ItemType* item) { return item->m_PrevPool; }
AccessNextVmaPoolListItemTraits5936     static ItemType*& AccessNext(ItemType* item) { return item->m_NextPool; }
5937 };
5938 
5939 /*
5940 Performs defragmentation:
5941 
5942 - Updates `pBlockVector->m_pMetadata`.
5943 - Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
5944 - Does not move actual data, only returns requested moves as `moves`.
5945 */
5946 class VmaDefragmentationAlgorithm
5947 {
VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)5948     VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
5949 public:
5950     VmaDefragmentationAlgorithm(
5951         VmaAllocator hAllocator,
5952         VmaBlockVector* pBlockVector,
5953         uint32_t currentFrameIndex) :
5954         m_hAllocator(hAllocator),
5955         m_pBlockVector(pBlockVector),
5956         m_CurrentFrameIndex(currentFrameIndex)
5957     {
5958     }
~VmaDefragmentationAlgorithm()5959     virtual ~VmaDefragmentationAlgorithm()
5960     {
5961     }
5962 
5963     virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
5964     virtual void AddAll() = 0;
5965 
5966     virtual VkResult Defragment(
5967         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
5968         VkDeviceSize maxBytesToMove,
5969         uint32_t maxAllocationsToMove,
5970         VmaDefragmentationFlags flags) = 0;
5971 
5972     virtual VkDeviceSize GetBytesMoved() const = 0;
5973     virtual uint32_t GetAllocationsMoved() const = 0;
5974 
5975 protected:
5976     VmaAllocator const m_hAllocator;
5977     VmaBlockVector* const m_pBlockVector;
5978     const uint32_t m_CurrentFrameIndex;
5979 
5980     struct AllocationInfo
5981     {
5982         VmaAllocation m_hAllocation;
5983         VkBool32* m_pChanged;
5984 
AllocationInfoAllocationInfo5985         AllocationInfo() :
5986             m_hAllocation(VK_NULL_HANDLE),
5987             m_pChanged(VMA_NULL)
5988         {
5989         }
AllocationInfoAllocationInfo5990         AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
5991             m_hAllocation(hAlloc),
5992             m_pChanged(pChanged)
5993         {
5994         }
5995     };
5996 };
5997 
5998 class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
5999 {
6000     VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
6001 public:
6002     VmaDefragmentationAlgorithm_Generic(
6003         VmaAllocator hAllocator,
6004         VmaBlockVector* pBlockVector,
6005         uint32_t currentFrameIndex,
6006         bool overlappingMoveSupported);
6007     virtual ~VmaDefragmentationAlgorithm_Generic();
6008 
6009     virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
AddAll()6010     virtual void AddAll() { m_AllAllocations = true; }
6011 
6012     virtual VkResult Defragment(
6013         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6014         VkDeviceSize maxBytesToMove,
6015         uint32_t maxAllocationsToMove,
6016         VmaDefragmentationFlags flags);
6017 
GetBytesMoved()6018     virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
GetAllocationsMoved()6019     virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
6020 
6021 private:
6022     uint32_t m_AllocationCount;
6023     bool m_AllAllocations;
6024 
6025     VkDeviceSize m_BytesMoved;
6026     uint32_t m_AllocationsMoved;
6027 
6028     struct AllocationInfoSizeGreater
6029     {
operatorAllocationInfoSizeGreater6030         bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
6031         {
6032             return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
6033         }
6034     };
6035 
6036     struct AllocationInfoOffsetGreater
6037     {
operatorAllocationInfoOffsetGreater6038         bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
6039         {
6040             return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
6041         }
6042     };
6043 
6044     struct BlockInfo
6045     {
6046         size_t m_OriginalBlockIndex;
6047         VmaDeviceMemoryBlock* m_pBlock;
6048         bool m_HasNonMovableAllocations;
6049         VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
6050 
BlockInfoBlockInfo6051         BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
6052             m_OriginalBlockIndex(SIZE_MAX),
6053             m_pBlock(VMA_NULL),
6054             m_HasNonMovableAllocations(true),
6055             m_Allocations(pAllocationCallbacks)
6056         {
6057         }
6058 
CalcHasNonMovableAllocationsBlockInfo6059         void CalcHasNonMovableAllocations()
6060         {
6061             const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
6062             const size_t defragmentAllocCount = m_Allocations.size();
6063             m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
6064         }
6065 
SortAllocationsBySizeDescendingBlockInfo6066         void SortAllocationsBySizeDescending()
6067         {
6068             VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
6069         }
6070 
SortAllocationsByOffsetDescendingBlockInfo6071         void SortAllocationsByOffsetDescending()
6072         {
6073             VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
6074         }
6075     };
6076 
6077     struct BlockPointerLess
6078     {
operatorBlockPointerLess6079         bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
6080         {
6081             return pLhsBlockInfo->m_pBlock < pRhsBlock;
6082         }
operatorBlockPointerLess6083         bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
6084         {
6085             return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
6086         }
6087     };
6088 
6089     // 1. Blocks with some non-movable allocations go first.
6090     // 2. Blocks with smaller sumFreeSize go first.
6091     struct BlockInfoCompareMoveDestination
6092     {
operatorBlockInfoCompareMoveDestination6093         bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
6094         {
6095             if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
6096             {
6097                 return true;
6098             }
6099             if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
6100             {
6101                 return false;
6102             }
6103             if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
6104             {
6105                 return true;
6106             }
6107             return false;
6108         }
6109     };
6110 
6111     typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
6112     BlockInfoVector m_Blocks;
6113 
6114     VkResult DefragmentRound(
6115         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6116         VkDeviceSize maxBytesToMove,
6117         uint32_t maxAllocationsToMove,
6118         bool freeOldAllocations);
6119 
6120     size_t CalcBlocksWithNonMovableCount() const;
6121 
6122     static bool MoveMakesSense(
6123         size_t dstBlockIndex, VkDeviceSize dstOffset,
6124         size_t srcBlockIndex, VkDeviceSize srcOffset);
6125 };
6126 
6127 class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
6128 {
6129     VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
6130 public:
6131     VmaDefragmentationAlgorithm_Fast(
6132         VmaAllocator hAllocator,
6133         VmaBlockVector* pBlockVector,
6134         uint32_t currentFrameIndex,
6135         bool overlappingMoveSupported);
6136     virtual ~VmaDefragmentationAlgorithm_Fast();
6137 
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)6138     virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { ++m_AllocationCount; }
AddAll()6139     virtual void AddAll() { m_AllAllocations = true; }
6140 
6141     virtual VkResult Defragment(
6142         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6143         VkDeviceSize maxBytesToMove,
6144         uint32_t maxAllocationsToMove,
6145         VmaDefragmentationFlags flags);
6146 
GetBytesMoved()6147     virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
GetAllocationsMoved()6148     virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
6149 
6150 private:
6151     struct BlockInfo
6152     {
6153         size_t origBlockIndex;
6154     };
6155 
6156     class FreeSpaceDatabase
6157     {
6158     public:
FreeSpaceDatabase()6159         FreeSpaceDatabase()
6160         {
6161             FreeSpace s = {};
6162             s.blockInfoIndex = SIZE_MAX;
6163             for(size_t i = 0; i < MAX_COUNT; ++i)
6164             {
6165                 m_FreeSpaces[i] = s;
6166             }
6167         }
6168 
Register(size_t blockInfoIndex,VkDeviceSize offset,VkDeviceSize size)6169         void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
6170         {
6171             // Find first invalid or the smallest structure.
6172             size_t bestIndex = SIZE_MAX;
6173             for(size_t i = 0; i < MAX_COUNT; ++i)
6174             {
6175                 // Empty structure.
6176                 if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
6177                 {
6178                     bestIndex = i;
6179                     break;
6180                 }
6181                 if(m_FreeSpaces[i].size < size &&
6182                     (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
6183                 {
6184                     bestIndex = i;
6185                 }
6186             }
6187 
6188             if(bestIndex != SIZE_MAX)
6189             {
6190                 m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
6191                 m_FreeSpaces[bestIndex].offset = offset;
6192                 m_FreeSpaces[bestIndex].size = size;
6193             }
6194         }
6195 
Fetch(VkDeviceSize alignment,VkDeviceSize size,size_t & outBlockInfoIndex,VkDeviceSize & outDstOffset)6196         bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
6197             size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
6198         {
6199             size_t bestIndex = SIZE_MAX;
6200             VkDeviceSize bestFreeSpaceAfter = 0;
6201             for(size_t i = 0; i < MAX_COUNT; ++i)
6202             {
6203                 // Structure is valid.
6204                 if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
6205                 {
6206                     const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment);
6207                     // Allocation fits into this structure.
6208                     if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
6209                     {
6210                         const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
6211                             (dstOffset + size);
6212                         if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
6213                         {
6214                             bestIndex = i;
6215                             bestFreeSpaceAfter = freeSpaceAfter;
6216                         }
6217                     }
6218                 }
6219             }
6220 
6221             if(bestIndex != SIZE_MAX)
6222             {
6223                 outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
6224                 outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment);
6225 
6226                 // Leave this structure for remaining empty space.
6227                 const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
6228                 m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
6229                 m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
6230 
6231                 return true;
6232             }
6233 
6234             return false;
6235         }
6236 
6237     private:
6238         static const size_t MAX_COUNT = 4;
6239 
6240         struct FreeSpace
6241         {
6242             size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
6243             VkDeviceSize offset;
6244             VkDeviceSize size;
6245         } m_FreeSpaces[MAX_COUNT];
6246     };
6247 
6248     const bool m_OverlappingMoveSupported;
6249 
6250     uint32_t m_AllocationCount;
6251     bool m_AllAllocations;
6252 
6253     VkDeviceSize m_BytesMoved;
6254     uint32_t m_AllocationsMoved;
6255 
6256     VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
6257 
6258     void PreprocessMetadata();
6259     void PostprocessMetadata();
6260     void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
6261 };
6262 
6263 struct VmaBlockDefragmentationContext
6264 {
6265     enum BLOCK_FLAG
6266     {
6267         BLOCK_FLAG_USED = 0x00000001,
6268     };
6269     uint32_t flags;
6270     VkBuffer hBuffer;
6271 };
6272 
6273 class VmaBlockVectorDefragmentationContext
6274 {
6275     VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
6276 public:
6277     VkResult res;
6278     bool mutexLocked;
6279     VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
6280     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > defragmentationMoves;
6281     uint32_t defragmentationMovesProcessed;
6282     uint32_t defragmentationMovesCommitted;
6283     bool hasDefragmentationPlan;
6284 
6285     VmaBlockVectorDefragmentationContext(
6286         VmaAllocator hAllocator,
6287         VmaPool hCustomPool, // Optional.
6288         VmaBlockVector* pBlockVector,
6289         uint32_t currFrameIndex);
6290     ~VmaBlockVectorDefragmentationContext();
6291 
GetCustomPool()6292     VmaPool GetCustomPool() const { return m_hCustomPool; }
GetBlockVector()6293     VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
GetAlgorithm()6294     VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
6295 
6296     void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
AddAll()6297     void AddAll() { m_AllAllocations = true; }
6298 
6299     void Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags);
6300 
6301 private:
6302     const VmaAllocator m_hAllocator;
6303     // Null if not from custom pool.
6304     const VmaPool m_hCustomPool;
6305     // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
6306     VmaBlockVector* const m_pBlockVector;
6307     const uint32_t m_CurrFrameIndex;
6308     // Owner of this object.
6309     VmaDefragmentationAlgorithm* m_pAlgorithm;
6310 
6311     struct AllocInfo
6312     {
6313         VmaAllocation hAlloc;
6314         VkBool32* pChanged;
6315     };
6316     // Used between constructor and Begin.
6317     VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
6318     bool m_AllAllocations;
6319 };
6320 
6321 struct VmaDefragmentationContext_T
6322 {
6323 private:
6324     VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
6325 public:
6326     VmaDefragmentationContext_T(
6327         VmaAllocator hAllocator,
6328         uint32_t currFrameIndex,
6329         uint32_t flags,
6330         VmaDefragmentationStats* pStats);
6331     ~VmaDefragmentationContext_T();
6332 
6333     void AddPools(uint32_t poolCount, const VmaPool* pPools);
6334     void AddAllocations(
6335         uint32_t allocationCount,
6336         const VmaAllocation* pAllocations,
6337         VkBool32* pAllocationsChanged);
6338 
6339     /*
6340     Returns:
6341     - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
6342     - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
6343     - Negative value if error occurred and object can be destroyed immediately.
6344     */
6345     VkResult Defragment(
6346         VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
6347         VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
6348         VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags);
6349 
6350     VkResult DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo);
6351     VkResult DefragmentPassEnd();
6352 
6353 private:
6354     const VmaAllocator m_hAllocator;
6355     const uint32_t m_CurrFrameIndex;
6356     const uint32_t m_Flags;
6357     VmaDefragmentationStats* const m_pStats;
6358 
6359     VkDeviceSize m_MaxCpuBytesToMove;
6360     uint32_t m_MaxCpuAllocationsToMove;
6361     VkDeviceSize m_MaxGpuBytesToMove;
6362     uint32_t m_MaxGpuAllocationsToMove;
6363 
6364     // Owner of these objects.
6365     VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
6366     // Owner of these objects.
6367     VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
6368 };
6369 
6370 #if VMA_RECORDING_ENABLED
6371 
6372 class VmaRecorder
6373 {
6374 public:
6375     VmaRecorder();
6376     VkResult Init(const VmaRecordSettings& settings, bool useMutex);
6377     void WriteConfiguration(
6378         const VkPhysicalDeviceProperties& devProps,
6379         const VkPhysicalDeviceMemoryProperties& memProps,
6380         uint32_t vulkanApiVersion,
6381         bool dedicatedAllocationExtensionEnabled,
6382         bool bindMemory2ExtensionEnabled,
6383         bool memoryBudgetExtensionEnabled,
6384         bool deviceCoherentMemoryExtensionEnabled);
6385     ~VmaRecorder();
6386 
6387     void RecordCreateAllocator(uint32_t frameIndex);
6388     void RecordDestroyAllocator(uint32_t frameIndex);
6389     void RecordCreatePool(uint32_t frameIndex,
6390         const VmaPoolCreateInfo& createInfo,
6391         VmaPool pool);
6392     void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
6393     void RecordAllocateMemory(uint32_t frameIndex,
6394         const VkMemoryRequirements& vkMemReq,
6395         const VmaAllocationCreateInfo& createInfo,
6396         VmaAllocation allocation);
6397     void RecordAllocateMemoryPages(uint32_t frameIndex,
6398         const VkMemoryRequirements& vkMemReq,
6399         const VmaAllocationCreateInfo& createInfo,
6400         uint64_t allocationCount,
6401         const VmaAllocation* pAllocations);
6402     void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
6403         const VkMemoryRequirements& vkMemReq,
6404         bool requiresDedicatedAllocation,
6405         bool prefersDedicatedAllocation,
6406         const VmaAllocationCreateInfo& createInfo,
6407         VmaAllocation allocation);
6408     void RecordAllocateMemoryForImage(uint32_t frameIndex,
6409         const VkMemoryRequirements& vkMemReq,
6410         bool requiresDedicatedAllocation,
6411         bool prefersDedicatedAllocation,
6412         const VmaAllocationCreateInfo& createInfo,
6413         VmaAllocation allocation);
6414     void RecordFreeMemory(uint32_t frameIndex,
6415         VmaAllocation allocation);
6416     void RecordFreeMemoryPages(uint32_t frameIndex,
6417         uint64_t allocationCount,
6418         const VmaAllocation* pAllocations);
6419     void RecordSetAllocationUserData(uint32_t frameIndex,
6420         VmaAllocation allocation,
6421         const void* pUserData);
6422     void RecordCreateLostAllocation(uint32_t frameIndex,
6423         VmaAllocation allocation);
6424     void RecordMapMemory(uint32_t frameIndex,
6425         VmaAllocation allocation);
6426     void RecordUnmapMemory(uint32_t frameIndex,
6427         VmaAllocation allocation);
6428     void RecordFlushAllocation(uint32_t frameIndex,
6429         VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
6430     void RecordInvalidateAllocation(uint32_t frameIndex,
6431         VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
6432     void RecordCreateBuffer(uint32_t frameIndex,
6433         const VkBufferCreateInfo& bufCreateInfo,
6434         const VmaAllocationCreateInfo& allocCreateInfo,
6435         VmaAllocation allocation);
6436     void RecordCreateImage(uint32_t frameIndex,
6437         const VkImageCreateInfo& imageCreateInfo,
6438         const VmaAllocationCreateInfo& allocCreateInfo,
6439         VmaAllocation allocation);
6440     void RecordDestroyBuffer(uint32_t frameIndex,
6441         VmaAllocation allocation);
6442     void RecordDestroyImage(uint32_t frameIndex,
6443         VmaAllocation allocation);
6444     void RecordTouchAllocation(uint32_t frameIndex,
6445         VmaAllocation allocation);
6446     void RecordGetAllocationInfo(uint32_t frameIndex,
6447         VmaAllocation allocation);
6448     void RecordMakePoolAllocationsLost(uint32_t frameIndex,
6449         VmaPool pool);
6450     void RecordDefragmentationBegin(uint32_t frameIndex,
6451         const VmaDefragmentationInfo2& info,
6452         VmaDefragmentationContext ctx);
6453     void RecordDefragmentationEnd(uint32_t frameIndex,
6454         VmaDefragmentationContext ctx);
6455     void RecordSetPoolName(uint32_t frameIndex,
6456         VmaPool pool,
6457         const char* name);
6458 
6459 private:
6460     struct CallParams
6461     {
6462         uint32_t threadId;
6463         double time;
6464     };
6465 
6466     class UserDataString
6467     {
6468     public:
6469         UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
GetString()6470         const char* GetString() const { return m_Str; }
6471 
6472     private:
6473         char m_PtrStr[17];
6474         const char* m_Str;
6475     };
6476 
6477     bool m_UseMutex;
6478     VmaRecordFlags m_Flags;
6479     FILE* m_File;
6480     VMA_MUTEX m_FileMutex;
6481     std::chrono::time_point<std::chrono::high_resolution_clock> m_RecordingStartTime;
6482 
6483     void GetBasicParams(CallParams& outParams);
6484 
6485     // T must be a pointer type, e.g. VmaAllocation, VmaPool.
6486     template<typename T>
PrintPointerList(uint64_t count,const T * pItems)6487     void PrintPointerList(uint64_t count, const T* pItems)
6488     {
6489         if(count)
6490         {
6491             fprintf(m_File, "%p", pItems[0]);
6492             for(uint64_t i = 1; i < count; ++i)
6493             {
6494                 fprintf(m_File, " %p", pItems[i]);
6495             }
6496         }
6497     }
6498 
6499     void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
6500     void Flush();
6501 };
6502 
6503 #endif // #if VMA_RECORDING_ENABLED
6504 
6505 /*
6506 Thread-safe wrapper over VmaPoolAllocator free list, for allocation of VmaAllocation_T objects.
6507 */
6508 class VmaAllocationObjectAllocator
6509 {
6510     VMA_CLASS_NO_COPY(VmaAllocationObjectAllocator)
6511 public:
6512     VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks);
6513 
6514     template<typename... Types> VmaAllocation Allocate(Types&&... args);
6515     void Free(VmaAllocation hAlloc);
6516 
6517 private:
6518     VMA_MUTEX m_Mutex;
6519     VmaPoolAllocator<VmaAllocation_T> m_Allocator;
6520 };
6521 
6522 struct VmaCurrentBudgetData
6523 {
6524     VMA_ATOMIC_UINT64 m_BlockBytes[VK_MAX_MEMORY_HEAPS];
6525     VMA_ATOMIC_UINT64 m_AllocationBytes[VK_MAX_MEMORY_HEAPS];
6526 
6527 #if VMA_MEMORY_BUDGET
6528     VMA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch;
6529     VMA_RW_MUTEX m_BudgetMutex;
6530     uint64_t m_VulkanUsage[VK_MAX_MEMORY_HEAPS];
6531     uint64_t m_VulkanBudget[VK_MAX_MEMORY_HEAPS];
6532     uint64_t m_BlockBytesAtBudgetFetch[VK_MAX_MEMORY_HEAPS];
6533 #endif // #if VMA_MEMORY_BUDGET
6534 
VmaCurrentBudgetDataVmaCurrentBudgetData6535     VmaCurrentBudgetData()
6536     {
6537         for(uint32_t heapIndex = 0; heapIndex < VK_MAX_MEMORY_HEAPS; ++heapIndex)
6538         {
6539             m_BlockBytes[heapIndex] = 0;
6540             m_AllocationBytes[heapIndex] = 0;
6541 #if VMA_MEMORY_BUDGET
6542             m_VulkanUsage[heapIndex] = 0;
6543             m_VulkanBudget[heapIndex] = 0;
6544             m_BlockBytesAtBudgetFetch[heapIndex] = 0;
6545 #endif
6546         }
6547 
6548 #if VMA_MEMORY_BUDGET
6549         m_OperationsSinceBudgetFetch = 0;
6550 #endif
6551     }
6552 
AddAllocationVmaCurrentBudgetData6553     void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
6554     {
6555         m_AllocationBytes[heapIndex] += allocationSize;
6556 #if VMA_MEMORY_BUDGET
6557         ++m_OperationsSinceBudgetFetch;
6558 #endif
6559     }
6560 
RemoveAllocationVmaCurrentBudgetData6561     void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
6562     {
6563         VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize);
6564         m_AllocationBytes[heapIndex] -= allocationSize;
6565 #if VMA_MEMORY_BUDGET
6566         ++m_OperationsSinceBudgetFetch;
6567 #endif
6568     }
6569 };
6570 
6571 // Main allocator object.
6572 struct VmaAllocator_T
6573 {
6574     VMA_CLASS_NO_COPY(VmaAllocator_T)
6575 public:
6576     bool m_UseMutex;
6577     uint32_t m_VulkanApiVersion;
6578     bool m_UseKhrDedicatedAllocation; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
6579     bool m_UseKhrBindMemory2; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
6580     bool m_UseExtMemoryBudget;
6581     bool m_UseAmdDeviceCoherentMemory;
6582     bool m_UseKhrBufferDeviceAddress;
6583     bool m_UseExtMemoryPriority;
6584     VkDevice m_hDevice;
6585     VkInstance m_hInstance;
6586     bool m_AllocationCallbacksSpecified;
6587     VkAllocationCallbacks m_AllocationCallbacks;
6588     VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
6589     VmaAllocationObjectAllocator m_AllocationObjectAllocator;
6590 
6591     // Each bit (1 << i) is set if HeapSizeLimit is enabled for that heap, so cannot allocate more than the heap size.
6592     uint32_t m_HeapSizeLimitMask;
6593 
6594     VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
6595     VkPhysicalDeviceMemoryProperties m_MemProps;
6596 
6597     // Default pools.
6598     VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
6599 
6600     typedef VmaIntrusiveLinkedList<VmaDedicatedAllocationListItemTraits> DedicatedAllocationLinkedList;
6601     DedicatedAllocationLinkedList m_DedicatedAllocations[VK_MAX_MEMORY_TYPES];
6602     VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
6603 
6604     VmaCurrentBudgetData m_Budget;
6605     VMA_ATOMIC_UINT32 m_DeviceMemoryCount; // Total number of VkDeviceMemory objects.
6606 
6607     VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
6608     VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
6609     ~VmaAllocator_T();
6610 
GetAllocationCallbacksVmaAllocator_T6611     const VkAllocationCallbacks* GetAllocationCallbacks() const
6612     {
6613         return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL;
6614     }
GetVulkanFunctionsVmaAllocator_T6615     const VmaVulkanFunctions& GetVulkanFunctions() const
6616     {
6617         return m_VulkanFunctions;
6618     }
6619 
GetPhysicalDeviceVmaAllocator_T6620     VkPhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; }
6621 
GetBufferImageGranularityVmaAllocator_T6622     VkDeviceSize GetBufferImageGranularity() const
6623     {
6624         return VMA_MAX(
6625             static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
6626             m_PhysicalDeviceProperties.limits.bufferImageGranularity);
6627     }
6628 
GetMemoryHeapCountVmaAllocator_T6629     uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
GetMemoryTypeCountVmaAllocator_T6630     uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
6631 
MemoryTypeIndexToHeapIndexVmaAllocator_T6632     uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
6633     {
6634         VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
6635         return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
6636     }
6637     // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
IsMemoryTypeNonCoherentVmaAllocator_T6638     bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
6639     {
6640         return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
6641             VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
6642     }
6643     // Minimum alignment for all allocations in specific memory type.
GetMemoryTypeMinAlignmentVmaAllocator_T6644     VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
6645     {
6646         return IsMemoryTypeNonCoherent(memTypeIndex) ?
6647             VMA_MAX((VkDeviceSize)VMA_MIN_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
6648             (VkDeviceSize)VMA_MIN_ALIGNMENT;
6649     }
6650 
IsIntegratedGpuVmaAllocator_T6651     bool IsIntegratedGpu() const
6652     {
6653         return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
6654     }
6655 
GetGlobalMemoryTypeBitsVmaAllocator_T6656     uint32_t GetGlobalMemoryTypeBits() const { return m_GlobalMemoryTypeBits; }
6657 
6658 #if VMA_RECORDING_ENABLED
GetRecorderVmaAllocator_T6659     VmaRecorder* GetRecorder() const { return m_pRecorder; }
6660 #endif
6661 
6662     void GetBufferMemoryRequirements(
6663         VkBuffer hBuffer,
6664         VkMemoryRequirements& memReq,
6665         bool& requiresDedicatedAllocation,
6666         bool& prefersDedicatedAllocation) const;
6667     void GetImageMemoryRequirements(
6668         VkImage hImage,
6669         VkMemoryRequirements& memReq,
6670         bool& requiresDedicatedAllocation,
6671         bool& prefersDedicatedAllocation) const;
6672 
6673     // Main allocation function.
6674     VkResult AllocateMemory(
6675         const VkMemoryRequirements& vkMemReq,
6676         bool requiresDedicatedAllocation,
6677         bool prefersDedicatedAllocation,
6678         VkBuffer dedicatedBuffer,
6679         VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown.
6680         VkImage dedicatedImage,
6681         const VmaAllocationCreateInfo& createInfo,
6682         VmaSuballocationType suballocType,
6683         size_t allocationCount,
6684         VmaAllocation* pAllocations);
6685 
6686     // Main deallocation function.
6687     void FreeMemory(
6688         size_t allocationCount,
6689         const VmaAllocation* pAllocations);
6690 
6691     void CalculateStats(VmaStats* pStats);
6692 
6693     void GetBudget(
6694         VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount);
6695 
6696 #if VMA_STATS_STRING_ENABLED
6697     void PrintDetailedMap(class VmaJsonWriter& json);
6698 #endif
6699 
6700     VkResult DefragmentationBegin(
6701         const VmaDefragmentationInfo2& info,
6702         VmaDefragmentationStats* pStats,
6703         VmaDefragmentationContext* pContext);
6704     VkResult DefragmentationEnd(
6705         VmaDefragmentationContext context);
6706 
6707     VkResult DefragmentationPassBegin(
6708         VmaDefragmentationPassInfo* pInfo,
6709         VmaDefragmentationContext context);
6710     VkResult DefragmentationPassEnd(
6711         VmaDefragmentationContext context);
6712 
6713     void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
6714     bool TouchAllocation(VmaAllocation hAllocation);
6715 
6716     VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
6717     void DestroyPool(VmaPool pool);
6718     void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
6719 
6720     void SetCurrentFrameIndex(uint32_t frameIndex);
GetCurrentFrameIndexVmaAllocator_T6721     uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
6722 
6723     void MakePoolAllocationsLost(
6724         VmaPool hPool,
6725         size_t* pLostAllocationCount);
6726     VkResult CheckPoolCorruption(VmaPool hPool);
6727     VkResult CheckCorruption(uint32_t memoryTypeBits);
6728 
6729     void CreateLostAllocation(VmaAllocation* pAllocation);
6730 
6731     // Call to Vulkan function vkAllocateMemory with accompanying bookkeeping.
6732     VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
6733     // Call to Vulkan function vkFreeMemory with accompanying bookkeeping.
6734     void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
6735     // Call to Vulkan function vkBindBufferMemory or vkBindBufferMemory2KHR.
6736     VkResult BindVulkanBuffer(
6737         VkDeviceMemory memory,
6738         VkDeviceSize memoryOffset,
6739         VkBuffer buffer,
6740         const void* pNext);
6741     // Call to Vulkan function vkBindImageMemory or vkBindImageMemory2KHR.
6742     VkResult BindVulkanImage(
6743         VkDeviceMemory memory,
6744         VkDeviceSize memoryOffset,
6745         VkImage image,
6746         const void* pNext);
6747 
6748     VkResult Map(VmaAllocation hAllocation, void** ppData);
6749     void Unmap(VmaAllocation hAllocation);
6750 
6751     VkResult BindBufferMemory(
6752         VmaAllocation hAllocation,
6753         VkDeviceSize allocationLocalOffset,
6754         VkBuffer hBuffer,
6755         const void* pNext);
6756     VkResult BindImageMemory(
6757         VmaAllocation hAllocation,
6758         VkDeviceSize allocationLocalOffset,
6759         VkImage hImage,
6760         const void* pNext);
6761 
6762     VkResult FlushOrInvalidateAllocation(
6763         VmaAllocation hAllocation,
6764         VkDeviceSize offset, VkDeviceSize size,
6765         VMA_CACHE_OPERATION op);
6766     VkResult FlushOrInvalidateAllocations(
6767         uint32_t allocationCount,
6768         const VmaAllocation* allocations,
6769         const VkDeviceSize* offsets, const VkDeviceSize* sizes,
6770         VMA_CACHE_OPERATION op);
6771 
6772     void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
6773 
6774     /*
6775     Returns bit mask of memory types that can support defragmentation on GPU as
6776     they support creation of required buffer for copy operations.
6777     */
6778     uint32_t GetGpuDefragmentationMemoryTypeBits();
6779 
6780 #if VMA_EXTERNAL_MEMORY
GetExternalMemoryHandleTypeFlagsVmaAllocator_T6781     VkExternalMemoryHandleTypeFlagsKHR GetExternalMemoryHandleTypeFlags(uint32_t memTypeIndex) const
6782     {
6783         return m_TypeExternalMemoryHandleTypes[memTypeIndex];
6784     }
6785 #endif // #if VMA_EXTERNAL_MEMORY
6786 
6787 private:
6788     VkDeviceSize m_PreferredLargeHeapBlockSize;
6789 
6790     VkPhysicalDevice m_PhysicalDevice;
6791     VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
6792     VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized.
6793 #if VMA_EXTERNAL_MEMORY
6794     VkExternalMemoryHandleTypeFlagsKHR m_TypeExternalMemoryHandleTypes[VK_MAX_MEMORY_TYPES];
6795 #endif // #if VMA_EXTERNAL_MEMORY
6796 
6797     VMA_RW_MUTEX m_PoolsMutex;
6798     typedef VmaIntrusiveLinkedList<VmaPoolListItemTraits> PoolList;
6799     // Protected by m_PoolsMutex.
6800     PoolList m_Pools;
6801     uint32_t m_NextPoolId;
6802 
6803     VmaVulkanFunctions m_VulkanFunctions;
6804 
6805     // Global bit mask AND-ed with any memoryTypeBits to disallow certain memory types.
6806     uint32_t m_GlobalMemoryTypeBits;
6807 
6808 #if VMA_RECORDING_ENABLED
6809     VmaRecorder* m_pRecorder;
6810 #endif
6811 
6812     void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
6813 
6814 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
6815     void ImportVulkanFunctions_Static();
6816 #endif
6817 
6818     void ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions);
6819 
6820 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
6821     void ImportVulkanFunctions_Dynamic();
6822 #endif
6823 
6824     void ValidateVulkanFunctions();
6825 
6826     VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
6827 
6828     VkResult AllocateMemoryOfType(
6829         VkDeviceSize size,
6830         VkDeviceSize alignment,
6831         bool dedicatedAllocation,
6832         VkBuffer dedicatedBuffer,
6833         VkBufferUsageFlags dedicatedBufferUsage,
6834         VkImage dedicatedImage,
6835         const VmaAllocationCreateInfo& createInfo,
6836         uint32_t memTypeIndex,
6837         VmaSuballocationType suballocType,
6838         size_t allocationCount,
6839         VmaAllocation* pAllocations);
6840 
6841     // Helper function only to be used inside AllocateDedicatedMemory.
6842     VkResult AllocateDedicatedMemoryPage(
6843         VkDeviceSize size,
6844         VmaSuballocationType suballocType,
6845         uint32_t memTypeIndex,
6846         const VkMemoryAllocateInfo& allocInfo,
6847         bool map,
6848         bool isUserDataString,
6849         void* pUserData,
6850         VmaAllocation* pAllocation);
6851 
6852     // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
6853     VkResult AllocateDedicatedMemory(
6854         VkDeviceSize size,
6855         VmaSuballocationType suballocType,
6856         uint32_t memTypeIndex,
6857         bool withinBudget,
6858         bool map,
6859         bool isUserDataString,
6860         void* pUserData,
6861         float priority,
6862         VkBuffer dedicatedBuffer,
6863         VkBufferUsageFlags dedicatedBufferUsage,
6864         VkImage dedicatedImage,
6865         size_t allocationCount,
6866         VmaAllocation* pAllocations);
6867 
6868     void FreeDedicatedMemory(const VmaAllocation allocation);
6869 
6870     /*
6871     Calculates and returns bit mask of memory types that can support defragmentation
6872     on GPU as they support creation of required buffer for copy operations.
6873     */
6874     uint32_t CalculateGpuDefragmentationMemoryTypeBits() const;
6875 
6876     uint32_t CalculateGlobalMemoryTypeBits() const;
6877 
6878     bool GetFlushOrInvalidateRange(
6879         VmaAllocation allocation,
6880         VkDeviceSize offset, VkDeviceSize size,
6881         VkMappedMemoryRange& outRange) const;
6882 
6883 #if VMA_MEMORY_BUDGET
6884     void UpdateVulkanBudget();
6885 #endif // #if VMA_MEMORY_BUDGET
6886 };
6887 
6888 class VmaStringBuilder;
6889 
VmaInitStatInfo(VmaStatInfo & outInfo)6890 static void VmaInitStatInfo(VmaStatInfo& outInfo)
6891 {
6892     memset(&outInfo, 0, sizeof(outInfo));
6893     outInfo.allocationSizeMin = UINT64_MAX;
6894     outInfo.unusedRangeSizeMin = UINT64_MAX;
6895 }
6896 
6897 // Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
VmaAddStatInfo(VmaStatInfo & inoutInfo,const VmaStatInfo & srcInfo)6898 static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
6899 {
6900     inoutInfo.blockCount += srcInfo.blockCount;
6901     inoutInfo.allocationCount += srcInfo.allocationCount;
6902     inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
6903     inoutInfo.usedBytes += srcInfo.usedBytes;
6904     inoutInfo.unusedBytes += srcInfo.unusedBytes;
6905     inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
6906     inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
6907     inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
6908     inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
6909 }
6910 
VmaAddStatInfoAllocation(VmaStatInfo & inoutInfo,VkDeviceSize size)6911 static void VmaAddStatInfoAllocation(VmaStatInfo& inoutInfo, VkDeviceSize size)
6912 {
6913     ++inoutInfo.allocationCount;
6914     inoutInfo.usedBytes += size;
6915     if(size < inoutInfo.allocationSizeMin)
6916     {
6917         inoutInfo.allocationSizeMin = size;
6918     }
6919     if(size > inoutInfo.allocationSizeMax)
6920     {
6921         inoutInfo.allocationSizeMax = size;
6922     }
6923 }
6924 
VmaAddStatInfoUnusedRange(VmaStatInfo & inoutInfo,VkDeviceSize size)6925 static void VmaAddStatInfoUnusedRange(VmaStatInfo& inoutInfo, VkDeviceSize size)
6926 {
6927     ++inoutInfo.unusedRangeCount;
6928     inoutInfo.unusedBytes += size;
6929     if(size < inoutInfo.unusedRangeSizeMin)
6930     {
6931         inoutInfo.unusedRangeSizeMin = size;
6932     }
6933     if(size > inoutInfo.unusedRangeSizeMax)
6934     {
6935         inoutInfo.unusedRangeSizeMax = size;
6936     }
6937 }
6938 
VmaPostprocessCalcStatInfo(VmaStatInfo & inoutInfo)6939 static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
6940 {
6941     inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
6942         VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
6943     inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
6944         VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
6945 }
6946 
6947 struct VmaVirtualBlock_T
6948 {
6949     VMA_CLASS_NO_COPY(VmaVirtualBlock_T)
6950 public:
6951     const bool m_AllocationCallbacksSpecified;
6952     const VkAllocationCallbacks m_AllocationCallbacks;
6953 
6954     VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo);
6955     ~VmaVirtualBlock_T();
InitVmaVirtualBlock_T6956     VkResult Init()
6957     {
6958         return VK_SUCCESS;
6959     }
6960 
GetAllocationCallbacksVmaVirtualBlock_T6961     const VkAllocationCallbacks* GetAllocationCallbacks() const
6962     {
6963         return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL;
6964     }
IsEmptyVmaVirtualBlock_T6965     bool IsEmpty() const
6966     {
6967         return m_Metadata->IsEmpty();
6968     }
GetAllocationInfoVmaVirtualBlock_T6969     void GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo)
6970     {
6971         m_Metadata->GetAllocationInfo(offset, outInfo);
6972     }
6973     VkResult Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VkDeviceSize& outOffset);
FreeVmaVirtualBlock_T6974     void Free(VkDeviceSize offset)
6975     {
6976         m_Metadata->FreeAtOffset(offset);
6977     }
ClearVmaVirtualBlock_T6978     void Clear()
6979     {
6980         m_Metadata->Clear();
6981     }
SetAllocationUserDataVmaVirtualBlock_T6982     void SetAllocationUserData(VkDeviceSize offset, void* userData)
6983     {
6984         m_Metadata->SetAllocationUserData(offset, userData);
6985     }
CalculateStatsVmaVirtualBlock_T6986     void CalculateStats(VmaStatInfo& outStatInfo) const
6987     {
6988         m_Metadata->CalcAllocationStatInfo(outStatInfo);
6989         VmaPostprocessCalcStatInfo(outStatInfo);
6990     }
6991 #if VMA_STATS_STRING_ENABLED
6992     void BuildStatsString(bool detailedMap, VmaStringBuilder& sb) const;
6993 #endif
6994 
6995 private:
6996     VmaBlockMetadata* m_Metadata;
6997 };
6998 
6999 ////////////////////////////////////////////////////////////////////////////////
7000 // Memory allocation #2 after VmaAllocator_T definition
7001 
VmaMalloc(VmaAllocator hAllocator,size_t size,size_t alignment)7002 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
7003 {
7004     return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
7005 }
7006 
VmaFree(VmaAllocator hAllocator,void * ptr)7007 static void VmaFree(VmaAllocator hAllocator, void* ptr)
7008 {
7009     VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
7010 }
7011 
7012 template<typename T>
VmaAllocate(VmaAllocator hAllocator)7013 static T* VmaAllocate(VmaAllocator hAllocator)
7014 {
7015     return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
7016 }
7017 
7018 template<typename T>
VmaAllocateArray(VmaAllocator hAllocator,size_t count)7019 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
7020 {
7021     return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
7022 }
7023 
7024 template<typename T>
vma_delete(VmaAllocator hAllocator,T * ptr)7025 static void vma_delete(VmaAllocator hAllocator, T* ptr)
7026 {
7027     if(ptr != VMA_NULL)
7028     {
7029         ptr->~T();
7030         VmaFree(hAllocator, ptr);
7031     }
7032 }
7033 
7034 template<typename T>
vma_delete_array(VmaAllocator hAllocator,T * ptr,size_t count)7035 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
7036 {
7037     if(ptr != VMA_NULL)
7038     {
7039         for(size_t i = count; i--; )
7040             ptr[i].~T();
7041         VmaFree(hAllocator, ptr);
7042     }
7043 }
7044 
7045 ////////////////////////////////////////////////////////////////////////////////
7046 // VmaStringBuilder
7047 
7048 #if VMA_STATS_STRING_ENABLED
7049 
7050 class VmaStringBuilder
7051 {
7052 public:
VmaStringBuilder(const VkAllocationCallbacks * allocationCallbacks)7053     VmaStringBuilder(const VkAllocationCallbacks* allocationCallbacks) : m_Data(VmaStlAllocator<char>(allocationCallbacks)) { }
GetLength()7054     size_t GetLength() const { return m_Data.size(); }
GetData()7055     const char* GetData() const { return m_Data.data(); }
7056 
Add(char ch)7057     void Add(char ch) { m_Data.push_back(ch); }
7058     void Add(const char* pStr);
AddNewLine()7059     void AddNewLine() { Add('\n'); }
7060     void AddNumber(uint32_t num);
7061     void AddNumber(uint64_t num);
7062     void AddPointer(const void* ptr);
7063 
7064 private:
7065     VmaVector< char, VmaStlAllocator<char> > m_Data;
7066 };
7067 
Add(const char * pStr)7068 void VmaStringBuilder::Add(const char* pStr)
7069 {
7070     const size_t strLen = strlen(pStr);
7071     if(strLen > 0)
7072     {
7073         const size_t oldCount = m_Data.size();
7074         m_Data.resize(oldCount + strLen);
7075         memcpy(m_Data.data() + oldCount, pStr, strLen);
7076     }
7077 }
7078 
AddNumber(uint32_t num)7079 void VmaStringBuilder::AddNumber(uint32_t num)
7080 {
7081     char buf[11];
7082     buf[10] = '\0';
7083     char *p = &buf[10];
7084     do
7085     {
7086         *--p = '0' + (num % 10);
7087         num /= 10;
7088     }
7089     while(num);
7090     Add(p);
7091 }
7092 
AddNumber(uint64_t num)7093 void VmaStringBuilder::AddNumber(uint64_t num)
7094 {
7095     char buf[21];
7096     buf[20] = '\0';
7097     char *p = &buf[20];
7098     do
7099     {
7100         *--p = '0' + (num % 10);
7101         num /= 10;
7102     }
7103     while(num);
7104     Add(p);
7105 }
7106 
AddPointer(const void * ptr)7107 void VmaStringBuilder::AddPointer(const void* ptr)
7108 {
7109     char buf[21];
7110     VmaPtrToStr(buf, sizeof(buf), ptr);
7111     Add(buf);
7112 }
7113 
7114 #endif // #if VMA_STATS_STRING_ENABLED
7115 
7116 ////////////////////////////////////////////////////////////////////////////////
7117 // VmaJsonWriter
7118 
7119 #if VMA_STATS_STRING_ENABLED
7120 
7121 class VmaJsonWriter
7122 {
7123     VMA_CLASS_NO_COPY(VmaJsonWriter)
7124 public:
7125     VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
7126     ~VmaJsonWriter();
7127 
7128     void BeginObject(bool singleLine = false);
7129     void EndObject();
7130 
7131     void BeginArray(bool singleLine = false);
7132     void EndArray();
7133 
7134     void WriteString(const char* pStr);
7135     void BeginString(const char* pStr = VMA_NULL);
7136     void ContinueString(const char* pStr);
7137     void ContinueString(uint32_t n);
7138     void ContinueString(uint64_t n);
7139     void ContinueString_Pointer(const void* ptr);
7140     void EndString(const char* pStr = VMA_NULL);
7141 
7142     void WriteNumber(uint32_t n);
7143     void WriteNumber(uint64_t n);
7144     void WriteBool(bool b);
7145     void WriteNull();
7146 
7147 private:
7148     static const char* const INDENT;
7149 
7150     enum COLLECTION_TYPE
7151     {
7152         COLLECTION_TYPE_OBJECT,
7153         COLLECTION_TYPE_ARRAY,
7154     };
7155     struct StackItem
7156     {
7157         COLLECTION_TYPE type;
7158         uint32_t valueCount;
7159         bool singleLineMode;
7160     };
7161 
7162     VmaStringBuilder& m_SB;
7163     VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
7164     bool m_InsideString;
7165 
7166     void BeginValue(bool isString);
7167     void WriteIndent(bool oneLess = false);
7168 };
7169 
7170 const char* const VmaJsonWriter::INDENT = "  ";
7171 
VmaJsonWriter(const VkAllocationCallbacks * pAllocationCallbacks,VmaStringBuilder & sb)7172 VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
7173     m_SB(sb),
7174     m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
7175     m_InsideString(false)
7176 {
7177 }
7178 
~VmaJsonWriter()7179 VmaJsonWriter::~VmaJsonWriter()
7180 {
7181     VMA_ASSERT(!m_InsideString);
7182     VMA_ASSERT(m_Stack.empty());
7183 }
7184 
BeginObject(bool singleLine)7185 void VmaJsonWriter::BeginObject(bool singleLine)
7186 {
7187     VMA_ASSERT(!m_InsideString);
7188 
7189     BeginValue(false);
7190     m_SB.Add('{');
7191 
7192     StackItem item;
7193     item.type = COLLECTION_TYPE_OBJECT;
7194     item.valueCount = 0;
7195     item.singleLineMode = singleLine;
7196     m_Stack.push_back(item);
7197 }
7198 
EndObject()7199 void VmaJsonWriter::EndObject()
7200 {
7201     VMA_ASSERT(!m_InsideString);
7202 
7203     WriteIndent(true);
7204     m_SB.Add('}');
7205 
7206     VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
7207     m_Stack.pop_back();
7208 }
7209 
BeginArray(bool singleLine)7210 void VmaJsonWriter::BeginArray(bool singleLine)
7211 {
7212     VMA_ASSERT(!m_InsideString);
7213 
7214     BeginValue(false);
7215     m_SB.Add('[');
7216 
7217     StackItem item;
7218     item.type = COLLECTION_TYPE_ARRAY;
7219     item.valueCount = 0;
7220     item.singleLineMode = singleLine;
7221     m_Stack.push_back(item);
7222 }
7223 
EndArray()7224 void VmaJsonWriter::EndArray()
7225 {
7226     VMA_ASSERT(!m_InsideString);
7227 
7228     WriteIndent(true);
7229     m_SB.Add(']');
7230 
7231     VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
7232     m_Stack.pop_back();
7233 }
7234 
WriteString(const char * pStr)7235 void VmaJsonWriter::WriteString(const char* pStr)
7236 {
7237     BeginString(pStr);
7238     EndString();
7239 }
7240 
BeginString(const char * pStr)7241 void VmaJsonWriter::BeginString(const char* pStr)
7242 {
7243     VMA_ASSERT(!m_InsideString);
7244 
7245     BeginValue(true);
7246     m_SB.Add('"');
7247     m_InsideString = true;
7248     if(pStr != VMA_NULL && pStr[0] != '\0')
7249     {
7250         ContinueString(pStr);
7251     }
7252 }
7253 
ContinueString(const char * pStr)7254 void VmaJsonWriter::ContinueString(const char* pStr)
7255 {
7256     VMA_ASSERT(m_InsideString);
7257 
7258     const size_t strLen = strlen(pStr);
7259     for(size_t i = 0; i < strLen; ++i)
7260     {
7261         char ch = pStr[i];
7262         if(ch == '\\')
7263         {
7264             m_SB.Add("\\\\");
7265         }
7266         else if(ch == '"')
7267         {
7268             m_SB.Add("\\\"");
7269         }
7270         else if(ch >= 32)
7271         {
7272             m_SB.Add(ch);
7273         }
7274         else switch(ch)
7275         {
7276         case '\b':
7277             m_SB.Add("\\b");
7278             break;
7279         case '\f':
7280             m_SB.Add("\\f");
7281             break;
7282         case '\n':
7283             m_SB.Add("\\n");
7284             break;
7285         case '\r':
7286             m_SB.Add("\\r");
7287             break;
7288         case '\t':
7289             m_SB.Add("\\t");
7290             break;
7291         default:
7292             VMA_ASSERT(0 && "Character not currently supported.");
7293             break;
7294         }
7295     }
7296 }
7297 
ContinueString(uint32_t n)7298 void VmaJsonWriter::ContinueString(uint32_t n)
7299 {
7300     VMA_ASSERT(m_InsideString);
7301     m_SB.AddNumber(n);
7302 }
7303 
ContinueString(uint64_t n)7304 void VmaJsonWriter::ContinueString(uint64_t n)
7305 {
7306     VMA_ASSERT(m_InsideString);
7307     m_SB.AddNumber(n);
7308 }
7309 
ContinueString_Pointer(const void * ptr)7310 void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
7311 {
7312     VMA_ASSERT(m_InsideString);
7313     m_SB.AddPointer(ptr);
7314 }
7315 
EndString(const char * pStr)7316 void VmaJsonWriter::EndString(const char* pStr)
7317 {
7318     VMA_ASSERT(m_InsideString);
7319     if(pStr != VMA_NULL && pStr[0] != '\0')
7320     {
7321         ContinueString(pStr);
7322     }
7323     m_SB.Add('"');
7324     m_InsideString = false;
7325 }
7326 
WriteNumber(uint32_t n)7327 void VmaJsonWriter::WriteNumber(uint32_t n)
7328 {
7329     VMA_ASSERT(!m_InsideString);
7330     BeginValue(false);
7331     m_SB.AddNumber(n);
7332 }
7333 
WriteNumber(uint64_t n)7334 void VmaJsonWriter::WriteNumber(uint64_t n)
7335 {
7336     VMA_ASSERT(!m_InsideString);
7337     BeginValue(false);
7338     m_SB.AddNumber(n);
7339 }
7340 
WriteBool(bool b)7341 void VmaJsonWriter::WriteBool(bool b)
7342 {
7343     VMA_ASSERT(!m_InsideString);
7344     BeginValue(false);
7345     m_SB.Add(b ? "true" : "false");
7346 }
7347 
WriteNull()7348 void VmaJsonWriter::WriteNull()
7349 {
7350     VMA_ASSERT(!m_InsideString);
7351     BeginValue(false);
7352     m_SB.Add("null");
7353 }
7354 
BeginValue(bool isString)7355 void VmaJsonWriter::BeginValue(bool isString)
7356 {
7357     if(!m_Stack.empty())
7358     {
7359         StackItem& currItem = m_Stack.back();
7360         if(currItem.type == COLLECTION_TYPE_OBJECT &&
7361             currItem.valueCount % 2 == 0)
7362         {
7363             VMA_ASSERT(isString);
7364         }
7365 
7366         if(currItem.type == COLLECTION_TYPE_OBJECT &&
7367             currItem.valueCount % 2 != 0)
7368         {
7369             m_SB.Add(": ");
7370         }
7371         else if(currItem.valueCount > 0)
7372         {
7373             m_SB.Add(", ");
7374             WriteIndent();
7375         }
7376         else
7377         {
7378             WriteIndent();
7379         }
7380         ++currItem.valueCount;
7381     }
7382 }
7383 
WriteIndent(bool oneLess)7384 void VmaJsonWriter::WriteIndent(bool oneLess)
7385 {
7386     if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
7387     {
7388         m_SB.AddNewLine();
7389 
7390         size_t count = m_Stack.size();
7391         if(count > 0 && oneLess)
7392         {
7393             --count;
7394         }
7395         for(size_t i = 0; i < count; ++i)
7396         {
7397             m_SB.Add(INDENT);
7398         }
7399     }
7400 }
7401 
7402 #endif // #if VMA_STATS_STRING_ENABLED
7403 
7404 ////////////////////////////////////////////////////////////////////////////////
7405 
SetUserData(VmaAllocator hAllocator,void * pUserData)7406 void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
7407 {
7408     if(IsUserDataString())
7409     {
7410         VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
7411 
7412         FreeUserDataString(hAllocator);
7413 
7414         if(pUserData != VMA_NULL)
7415         {
7416             m_pUserData = VmaCreateStringCopy(hAllocator->GetAllocationCallbacks(), (const char*)pUserData);
7417         }
7418     }
7419     else
7420     {
7421         m_pUserData = pUserData;
7422     }
7423 }
7424 
ChangeBlockAllocation(VmaAllocator hAllocator,VmaDeviceMemoryBlock * block,VkDeviceSize offset)7425 void VmaAllocation_T::ChangeBlockAllocation(
7426     VmaAllocator hAllocator,
7427     VmaDeviceMemoryBlock* block,
7428     VkDeviceSize offset)
7429 {
7430     VMA_ASSERT(block != VMA_NULL);
7431     VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7432 
7433     // Move mapping reference counter from old block to new block.
7434     if(block != m_BlockAllocation.m_Block)
7435     {
7436         uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
7437         if(IsPersistentMap())
7438             ++mapRefCount;
7439         m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
7440         block->Map(hAllocator, mapRefCount, VMA_NULL);
7441     }
7442 
7443     m_BlockAllocation.m_Block = block;
7444     m_BlockAllocation.m_Offset = offset;
7445 }
7446 
ChangeOffset(VkDeviceSize newOffset)7447 void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
7448 {
7449     VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7450     m_BlockAllocation.m_Offset = newOffset;
7451 }
7452 
GetOffset()7453 VkDeviceSize VmaAllocation_T::GetOffset() const
7454 {
7455     switch(m_Type)
7456     {
7457     case ALLOCATION_TYPE_BLOCK:
7458         return m_BlockAllocation.m_Offset;
7459     case ALLOCATION_TYPE_DEDICATED:
7460         return 0;
7461     default:
7462         VMA_ASSERT(0);
7463         return 0;
7464     }
7465 }
7466 
GetMemory()7467 VkDeviceMemory VmaAllocation_T::GetMemory() const
7468 {
7469     switch(m_Type)
7470     {
7471     case ALLOCATION_TYPE_BLOCK:
7472         return m_BlockAllocation.m_Block->GetDeviceMemory();
7473     case ALLOCATION_TYPE_DEDICATED:
7474         return m_DedicatedAllocation.m_hMemory;
7475     default:
7476         VMA_ASSERT(0);
7477         return VK_NULL_HANDLE;
7478     }
7479 }
7480 
GetMappedData()7481 void* VmaAllocation_T::GetMappedData() const
7482 {
7483     switch(m_Type)
7484     {
7485     case ALLOCATION_TYPE_BLOCK:
7486         if(m_MapCount != 0)
7487         {
7488             void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
7489             VMA_ASSERT(pBlockData != VMA_NULL);
7490             return (char*)pBlockData + m_BlockAllocation.m_Offset;
7491         }
7492         else
7493         {
7494             return VMA_NULL;
7495         }
7496         break;
7497     case ALLOCATION_TYPE_DEDICATED:
7498         VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
7499         return m_DedicatedAllocation.m_pMappedData;
7500     default:
7501         VMA_ASSERT(0);
7502         return VMA_NULL;
7503     }
7504 }
7505 
CanBecomeLost()7506 bool VmaAllocation_T::CanBecomeLost() const
7507 {
7508     switch(m_Type)
7509     {
7510     case ALLOCATION_TYPE_BLOCK:
7511         return m_BlockAllocation.m_CanBecomeLost;
7512     case ALLOCATION_TYPE_DEDICATED:
7513         return false;
7514     default:
7515         VMA_ASSERT(0);
7516         return false;
7517     }
7518 }
7519 
MakeLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)7520 bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
7521 {
7522     VMA_ASSERT(CanBecomeLost());
7523 
7524     /*
7525     Warning: This is a carefully designed algorithm.
7526     Do not modify unless you really know what you're doing :)
7527     */
7528     uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
7529     for(;;)
7530     {
7531         if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
7532         {
7533             VMA_ASSERT(0);
7534             return false;
7535         }
7536         else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
7537         {
7538             return false;
7539         }
7540         else // Last use time earlier than current time.
7541         {
7542             if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
7543             {
7544                 // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
7545                 // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
7546                 return true;
7547             }
7548         }
7549     }
7550 }
7551 
7552 #if VMA_STATS_STRING_ENABLED
7553 
7554 // Correspond to values of enum VmaSuballocationType.
7555 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
7556     "FREE",
7557     "UNKNOWN",
7558     "BUFFER",
7559     "IMAGE_UNKNOWN",
7560     "IMAGE_LINEAR",
7561     "IMAGE_OPTIMAL",
7562 };
7563 
PrintParameters(class VmaJsonWriter & json)7564 void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
7565 {
7566     json.WriteString("Type");
7567     json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
7568 
7569     json.WriteString("Size");
7570     json.WriteNumber(m_Size);
7571 
7572     if(m_pUserData != VMA_NULL)
7573     {
7574         json.WriteString("UserData");
7575         if(IsUserDataString())
7576         {
7577             json.WriteString((const char*)m_pUserData);
7578         }
7579         else
7580         {
7581             json.BeginString();
7582             json.ContinueString_Pointer(m_pUserData);
7583             json.EndString();
7584         }
7585     }
7586 
7587     json.WriteString("CreationFrameIndex");
7588     json.WriteNumber(m_CreationFrameIndex);
7589 
7590     json.WriteString("LastUseFrameIndex");
7591     json.WriteNumber(GetLastUseFrameIndex());
7592 
7593     if(m_BufferImageUsage != 0)
7594     {
7595         json.WriteString("Usage");
7596         json.WriteNumber(m_BufferImageUsage);
7597     }
7598 }
7599 
7600 #endif
7601 
FreeUserDataString(VmaAllocator hAllocator)7602 void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
7603 {
7604     VMA_ASSERT(IsUserDataString());
7605     VmaFreeString(hAllocator->GetAllocationCallbacks(), (char*)m_pUserData);
7606     m_pUserData = VMA_NULL;
7607 }
7608 
BlockAllocMap()7609 void VmaAllocation_T::BlockAllocMap()
7610 {
7611     VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
7612 
7613     if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
7614     {
7615         ++m_MapCount;
7616     }
7617     else
7618     {
7619         VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
7620     }
7621 }
7622 
BlockAllocUnmap()7623 void VmaAllocation_T::BlockAllocUnmap()
7624 {
7625     VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
7626 
7627     if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
7628     {
7629         --m_MapCount;
7630     }
7631     else
7632     {
7633         VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
7634     }
7635 }
7636 
DedicatedAllocMap(VmaAllocator hAllocator,void ** ppData)7637 VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
7638 {
7639     VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
7640 
7641     if(m_MapCount != 0)
7642     {
7643         if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
7644         {
7645             VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
7646             *ppData = m_DedicatedAllocation.m_pMappedData;
7647             ++m_MapCount;
7648             return VK_SUCCESS;
7649         }
7650         else
7651         {
7652             VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
7653             return VK_ERROR_MEMORY_MAP_FAILED;
7654         }
7655     }
7656     else
7657     {
7658         VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
7659             hAllocator->m_hDevice,
7660             m_DedicatedAllocation.m_hMemory,
7661             0, // offset
7662             VK_WHOLE_SIZE,
7663             0, // flags
7664             ppData);
7665         if(result == VK_SUCCESS)
7666         {
7667             m_DedicatedAllocation.m_pMappedData = *ppData;
7668             m_MapCount = 1;
7669         }
7670         return result;
7671     }
7672 }
7673 
DedicatedAllocUnmap(VmaAllocator hAllocator)7674 void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
7675 {
7676     VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
7677 
7678     if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
7679     {
7680         --m_MapCount;
7681         if(m_MapCount == 0)
7682         {
7683             m_DedicatedAllocation.m_pMappedData = VMA_NULL;
7684             (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
7685                 hAllocator->m_hDevice,
7686                 m_DedicatedAllocation.m_hMemory);
7687         }
7688     }
7689     else
7690     {
7691         VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
7692     }
7693 }
7694 
7695 #if VMA_STATS_STRING_ENABLED
7696 
VmaPrintStatInfo(VmaJsonWriter & json,const VmaStatInfo & stat)7697 static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
7698 {
7699     json.BeginObject();
7700 
7701     json.WriteString("Blocks");
7702     json.WriteNumber(stat.blockCount);
7703 
7704     json.WriteString("Allocations");
7705     json.WriteNumber(stat.allocationCount);
7706 
7707     json.WriteString("UnusedRanges");
7708     json.WriteNumber(stat.unusedRangeCount);
7709 
7710     json.WriteString("UsedBytes");
7711     json.WriteNumber(stat.usedBytes);
7712 
7713     json.WriteString("UnusedBytes");
7714     json.WriteNumber(stat.unusedBytes);
7715 
7716     if(stat.allocationCount > 1)
7717     {
7718         json.WriteString("AllocationSize");
7719         json.BeginObject(true);
7720         json.WriteString("Min");
7721         json.WriteNumber(stat.allocationSizeMin);
7722         json.WriteString("Avg");
7723         json.WriteNumber(stat.allocationSizeAvg);
7724         json.WriteString("Max");
7725         json.WriteNumber(stat.allocationSizeMax);
7726         json.EndObject();
7727     }
7728 
7729     if(stat.unusedRangeCount > 1)
7730     {
7731         json.WriteString("UnusedRangeSize");
7732         json.BeginObject(true);
7733         json.WriteString("Min");
7734         json.WriteNumber(stat.unusedRangeSizeMin);
7735         json.WriteString("Avg");
7736         json.WriteNumber(stat.unusedRangeSizeAvg);
7737         json.WriteString("Max");
7738         json.WriteNumber(stat.unusedRangeSizeMax);
7739         json.EndObject();
7740     }
7741 
7742     json.EndObject();
7743 }
7744 
7745 #endif // #if VMA_STATS_STRING_ENABLED
7746 
7747 struct VmaSuballocationItemSizeLess
7748 {
operatorVmaSuballocationItemSizeLess7749     bool operator()(
7750         const VmaSuballocationList::iterator lhs,
7751         const VmaSuballocationList::iterator rhs) const
7752     {
7753         return lhs->size < rhs->size;
7754     }
operatorVmaSuballocationItemSizeLess7755     bool operator()(
7756         const VmaSuballocationList::iterator lhs,
7757         VkDeviceSize rhsSize) const
7758     {
7759         return lhs->size < rhsSize;
7760     }
7761 };
7762 
7763 
7764 ////////////////////////////////////////////////////////////////////////////////
7765 // class VmaBlockMetadata
7766 
VmaBlockMetadata(const VkAllocationCallbacks * pAllocationCallbacks,bool isVirtual)7767 VmaBlockMetadata::VmaBlockMetadata(const VkAllocationCallbacks* pAllocationCallbacks, bool isVirtual) :
7768     m_Size(0),
7769     m_pAllocationCallbacks(pAllocationCallbacks),
7770     m_IsVirtual(isVirtual)
7771 {
7772 }
7773 
7774 #if VMA_STATS_STRING_ENABLED
7775 
PrintDetailedMap_Begin(class VmaJsonWriter & json,VkDeviceSize unusedBytes,size_t allocationCount,size_t unusedRangeCount)7776 void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
7777     VkDeviceSize unusedBytes,
7778     size_t allocationCount,
7779     size_t unusedRangeCount) const
7780 {
7781     json.BeginObject();
7782 
7783     json.WriteString("TotalBytes");
7784     json.WriteNumber(GetSize());
7785 
7786     json.WriteString("UnusedBytes");
7787     json.WriteNumber(unusedBytes);
7788 
7789     json.WriteString("Allocations");
7790     json.WriteNumber((uint64_t)allocationCount);
7791 
7792     json.WriteString("UnusedRanges");
7793     json.WriteNumber((uint64_t)unusedRangeCount);
7794 
7795     json.WriteString("Suballocations");
7796     json.BeginArray();
7797 }
7798 
PrintDetailedMap_Allocation(class VmaJsonWriter & json,VkDeviceSize offset,VkDeviceSize size,void * userData)7799 void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
7800     VkDeviceSize offset, VkDeviceSize size, void* userData) const
7801 {
7802     json.BeginObject(true);
7803 
7804     json.WriteString("Offset");
7805     json.WriteNumber(offset);
7806 
7807     if(IsVirtual())
7808     {
7809         json.WriteString("Type");
7810         json.WriteString("VirtualAllocation");
7811 
7812         json.WriteString("Size");
7813         json.WriteNumber(size);
7814 
7815         if(userData != VMA_NULL)
7816         {
7817             json.WriteString("UserData");
7818             json.BeginString();
7819             json.ContinueString_Pointer(userData);
7820             json.EndString();
7821         }
7822     }
7823     else
7824     {
7825         ((VmaAllocation)userData)->PrintParameters(json);
7826     }
7827 
7828     json.EndObject();
7829 }
7830 
PrintDetailedMap_UnusedRange(class VmaJsonWriter & json,VkDeviceSize offset,VkDeviceSize size)7831 void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
7832     VkDeviceSize offset,
7833     VkDeviceSize size) const
7834 {
7835     json.BeginObject(true);
7836 
7837     json.WriteString("Offset");
7838     json.WriteNumber(offset);
7839 
7840     json.WriteString("Type");
7841     json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
7842 
7843     json.WriteString("Size");
7844     json.WriteNumber(size);
7845 
7846     json.EndObject();
7847 }
7848 
PrintDetailedMap_End(class VmaJsonWriter & json)7849 void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
7850 {
7851     json.EndArray();
7852     json.EndObject();
7853 }
7854 
7855 #endif // #if VMA_STATS_STRING_ENABLED
7856 
7857 ////////////////////////////////////////////////////////////////////////////////
7858 // class VmaBlockMetadata_Generic
7859 
VmaBlockMetadata_Generic(const VkAllocationCallbacks * pAllocationCallbacks,bool isVirtual)7860 VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(const VkAllocationCallbacks* pAllocationCallbacks, bool isVirtual) :
7861     VmaBlockMetadata(pAllocationCallbacks, isVirtual),
7862     m_FreeCount(0),
7863     m_SumFreeSize(0),
7864     m_Suballocations(VmaStlAllocator<VmaSuballocation>(pAllocationCallbacks)),
7865     m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(pAllocationCallbacks))
7866 {
7867 }
7868 
~VmaBlockMetadata_Generic()7869 VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
7870 {
7871 }
7872 
Init(VkDeviceSize size)7873 void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
7874 {
7875     VmaBlockMetadata::Init(size);
7876 
7877     m_FreeCount = 1;
7878     m_SumFreeSize = size;
7879 
7880     VmaSuballocation suballoc = {};
7881     suballoc.offset = 0;
7882     suballoc.size = size;
7883     suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
7884 
7885     m_Suballocations.push_back(suballoc);
7886     m_FreeSuballocationsBySize.push_back(m_Suballocations.begin());
7887 }
7888 
Validate()7889 bool VmaBlockMetadata_Generic::Validate() const
7890 {
7891     VMA_VALIDATE(!m_Suballocations.empty());
7892 
7893     // Expected offset of new suballocation as calculated from previous ones.
7894     VkDeviceSize calculatedOffset = 0;
7895     // Expected number of free suballocations as calculated from traversing their list.
7896     uint32_t calculatedFreeCount = 0;
7897     // Expected sum size of free suballocations as calculated from traversing their list.
7898     VkDeviceSize calculatedSumFreeSize = 0;
7899     // Expected number of free suballocations that should be registered in
7900     // m_FreeSuballocationsBySize calculated from traversing their list.
7901     size_t freeSuballocationsToRegister = 0;
7902     // True if previous visited suballocation was free.
7903     bool prevFree = false;
7904 
7905     const VkDeviceSize debugMargin = GetDebugMargin();
7906 
7907     for(const auto& subAlloc : m_Suballocations)
7908     {
7909         // Actual offset of this suballocation doesn't match expected one.
7910         VMA_VALIDATE(subAlloc.offset == calculatedOffset);
7911 
7912         const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
7913         // Two adjacent free suballocations are invalid. They should be merged.
7914         VMA_VALIDATE(!prevFree || !currFree);
7915 
7916         VmaAllocation alloc = (VmaAllocation)subAlloc.userData;
7917         if(!IsVirtual())
7918         {
7919             VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));
7920         }
7921 
7922         if(currFree)
7923         {
7924             calculatedSumFreeSize += subAlloc.size;
7925             ++calculatedFreeCount;
7926             ++freeSuballocationsToRegister;
7927 
7928             // Margin required between allocations - every free space must be at least that large.
7929             VMA_VALIDATE(subAlloc.size >= debugMargin);
7930         }
7931         else
7932         {
7933             if(!IsVirtual())
7934             {
7935                 VMA_VALIDATE(alloc->GetOffset() == subAlloc.offset);
7936                 VMA_VALIDATE(alloc->GetSize() == subAlloc.size);
7937             }
7938 
7939             // Margin required between allocations - previous allocation must be free.
7940             VMA_VALIDATE(debugMargin == 0 || prevFree);
7941         }
7942 
7943         calculatedOffset += subAlloc.size;
7944         prevFree = currFree;
7945     }
7946 
7947     // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
7948     // match expected one.
7949     VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
7950 
7951     VkDeviceSize lastSize = 0;
7952     for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
7953     {
7954         VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
7955 
7956         // Only free suballocations can be registered in m_FreeSuballocationsBySize.
7957         VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
7958         // They must be sorted by size ascending.
7959         VMA_VALIDATE(suballocItem->size >= lastSize);
7960 
7961         lastSize = suballocItem->size;
7962     }
7963 
7964     // Check if totals match calculated values.
7965     VMA_VALIDATE(ValidateFreeSuballocationList());
7966     VMA_VALIDATE(calculatedOffset == GetSize());
7967     VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
7968     VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
7969 
7970     return true;
7971 }
7972 
GetUnusedRangeSizeMax()7973 VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
7974 {
7975     if(!m_FreeSuballocationsBySize.empty())
7976     {
7977         return m_FreeSuballocationsBySize.back()->size;
7978     }
7979     else
7980     {
7981         return 0;
7982     }
7983 }
7984 
IsEmpty()7985 bool VmaBlockMetadata_Generic::IsEmpty() const
7986 {
7987     return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
7988 }
7989 
CalcAllocationStatInfo(VmaStatInfo & outInfo)7990 void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
7991 {
7992     const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
7993     VmaInitStatInfo(outInfo);
7994     outInfo.blockCount = 1;
7995 
7996     for(const auto& suballoc : m_Suballocations)
7997     {
7998         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
7999         {
8000             VmaAddStatInfoAllocation(outInfo, suballoc.size);
8001         }
8002         else
8003         {
8004             VmaAddStatInfoUnusedRange(outInfo, suballoc.size);
8005         }
8006     }
8007 }
8008 
AddPoolStats(VmaPoolStats & inoutStats)8009 void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
8010 {
8011     const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
8012 
8013     inoutStats.size += GetSize();
8014     inoutStats.unusedSize += m_SumFreeSize;
8015     inoutStats.allocationCount += rangeCount - m_FreeCount;
8016     inoutStats.unusedRangeCount += m_FreeCount;
8017     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
8018 }
8019 
8020 #if VMA_STATS_STRING_ENABLED
8021 
PrintDetailedMap(class VmaJsonWriter & json)8022 void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
8023 {
8024     PrintDetailedMap_Begin(json,
8025         m_SumFreeSize, // unusedBytes
8026         m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
8027         m_FreeCount); // unusedRangeCount
8028 
8029     for(const auto& suballoc : m_Suballocations)
8030     {
8031         if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
8032         {
8033             PrintDetailedMap_UnusedRange(json, suballoc.offset, suballoc.size);
8034         }
8035         else
8036         {
8037             PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);
8038         }
8039     }
8040 
8041     PrintDetailedMap_End(json);
8042 }
8043 
8044 #endif // #if VMA_STATS_STRING_ENABLED
8045 
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)8046 bool VmaBlockMetadata_Generic::CreateAllocationRequest(
8047     uint32_t currentFrameIndex,
8048     uint32_t frameInUseCount,
8049     VkDeviceSize bufferImageGranularity,
8050     VkDeviceSize allocSize,
8051     VkDeviceSize allocAlignment,
8052     bool upperAddress,
8053     VmaSuballocationType allocType,
8054     bool canMakeOtherLost,
8055     uint32_t strategy,
8056     VmaAllocationRequest* pAllocationRequest)
8057 {
8058     VMA_ASSERT(allocSize > 0);
8059     VMA_ASSERT(!upperAddress);
8060     VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
8061     VMA_ASSERT(pAllocationRequest != VMA_NULL);
8062     VMA_HEAVY_ASSERT(Validate());
8063 
8064     allocSize = AlignAllocationSize(allocSize);
8065 
8066     pAllocationRequest->type = VmaAllocationRequestType::Normal;
8067     pAllocationRequest->size = allocSize;
8068 
8069     const VkDeviceSize debugMargin = GetDebugMargin();
8070 
8071     // There is not enough total free space in this block to fulfill the request: Early return.
8072     if(canMakeOtherLost == false &&
8073         m_SumFreeSize < allocSize + 2 * debugMargin)
8074     {
8075         return false;
8076     }
8077 
8078     // New algorithm, efficiently searching freeSuballocationsBySize.
8079     const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
8080     if(freeSuballocCount > 0)
8081     {
8082         if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
8083         {
8084             // Find first free suballocation with size not less than allocSize + 2 * debugMargin.
8085             VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
8086                 m_FreeSuballocationsBySize.data(),
8087                 m_FreeSuballocationsBySize.data() + freeSuballocCount,
8088                 allocSize + 2 * debugMargin,
8089                 VmaSuballocationItemSizeLess());
8090             size_t index = it - m_FreeSuballocationsBySize.data();
8091             for(; index < freeSuballocCount; ++index)
8092             {
8093                 if(CheckAllocation(
8094                     currentFrameIndex,
8095                     frameInUseCount,
8096                     bufferImageGranularity,
8097                     allocSize,
8098                     allocAlignment,
8099                     allocType,
8100                     m_FreeSuballocationsBySize[index],
8101                     false, // canMakeOtherLost
8102                     &pAllocationRequest->offset,
8103                     &pAllocationRequest->itemsToMakeLostCount,
8104                     &pAllocationRequest->sumFreeSize,
8105                     &pAllocationRequest->sumItemSize))
8106                 {
8107                     pAllocationRequest->item = m_FreeSuballocationsBySize[index];
8108                     return true;
8109                 }
8110             }
8111         }
8112         else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
8113         {
8114             for(VmaSuballocationList::iterator it = m_Suballocations.begin();
8115                 it != m_Suballocations.end();
8116                 ++it)
8117             {
8118                 if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
8119                     currentFrameIndex,
8120                     frameInUseCount,
8121                     bufferImageGranularity,
8122                     allocSize,
8123                     allocAlignment,
8124                     allocType,
8125                     it,
8126                     false, // canMakeOtherLost
8127                     &pAllocationRequest->offset,
8128                     &pAllocationRequest->itemsToMakeLostCount,
8129                     &pAllocationRequest->sumFreeSize,
8130                     &pAllocationRequest->sumItemSize))
8131                 {
8132                     pAllocationRequest->item = it;
8133                     return true;
8134                 }
8135             }
8136         }
8137         else // WORST_FIT, FIRST_FIT
8138         {
8139             // Search staring from biggest suballocations.
8140             for(size_t index = freeSuballocCount; index--; )
8141             {
8142                 if(CheckAllocation(
8143                     currentFrameIndex,
8144                     frameInUseCount,
8145                     bufferImageGranularity,
8146                     allocSize,
8147                     allocAlignment,
8148                     allocType,
8149                     m_FreeSuballocationsBySize[index],
8150                     false, // canMakeOtherLost
8151                     &pAllocationRequest->offset,
8152                     &pAllocationRequest->itemsToMakeLostCount,
8153                     &pAllocationRequest->sumFreeSize,
8154                     &pAllocationRequest->sumItemSize))
8155                 {
8156                     pAllocationRequest->item = m_FreeSuballocationsBySize[index];
8157                     return true;
8158                 }
8159             }
8160         }
8161     }
8162 
8163     if(canMakeOtherLost)
8164     {
8165         VMA_ASSERT(!IsVirtual());
8166         // Brute-force algorithm. TODO: Come up with something better.
8167 
8168         bool found = false;
8169         VmaAllocationRequest tmpAllocRequest = {};
8170         tmpAllocRequest.type = VmaAllocationRequestType::Normal;
8171         tmpAllocRequest.size = allocSize;
8172         for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
8173             suballocIt != m_Suballocations.end();
8174             ++suballocIt)
8175         {
8176             VmaAllocation const alloc = (VmaAllocation)suballocIt->userData;
8177             if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
8178                 alloc->CanBecomeLost())
8179             {
8180                 if(CheckAllocation(
8181                     currentFrameIndex,
8182                     frameInUseCount,
8183                     bufferImageGranularity,
8184                     allocSize,
8185                     allocAlignment,
8186                     allocType,
8187                     suballocIt,
8188                     canMakeOtherLost,
8189                     &tmpAllocRequest.offset,
8190                     &tmpAllocRequest.itemsToMakeLostCount,
8191                     &tmpAllocRequest.sumFreeSize,
8192                     &tmpAllocRequest.sumItemSize))
8193                 {
8194                     if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
8195                     {
8196                         *pAllocationRequest = tmpAllocRequest;
8197                         pAllocationRequest->item = suballocIt;
8198                         break;
8199                     }
8200                     if(!found || tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost())
8201                     {
8202                         *pAllocationRequest = tmpAllocRequest;
8203                         pAllocationRequest->item = suballocIt;
8204                         found = true;
8205                     }
8206                 }
8207             }
8208         }
8209 
8210         return found;
8211     }
8212 
8213     return false;
8214 }
8215 
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)8216 bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
8217     uint32_t currentFrameIndex,
8218     uint32_t frameInUseCount,
8219     VmaAllocationRequest* pAllocationRequest)
8220 {
8221     VMA_ASSERT(!IsVirtual());
8222     VMA_ASSERT(pAllocationRequest && pAllocationRequest->type == VmaAllocationRequestType::Normal);
8223 
8224     while(pAllocationRequest->itemsToMakeLostCount > 0)
8225     {
8226         if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
8227         {
8228             ++pAllocationRequest->item;
8229         }
8230         VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
8231         VmaAllocation const alloc = (VmaAllocation)pAllocationRequest->item->userData;
8232         VMA_ASSERT(alloc != VK_NULL_HANDLE && alloc->CanBecomeLost());
8233         if(alloc->MakeLost(currentFrameIndex, frameInUseCount))
8234         {
8235             pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
8236             --pAllocationRequest->itemsToMakeLostCount;
8237         }
8238         else
8239         {
8240             return false;
8241         }
8242     }
8243 
8244     VMA_HEAVY_ASSERT(Validate());
8245     VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
8246     VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
8247 
8248     return true;
8249 }
8250 
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)8251 uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
8252 {
8253     VMA_ASSERT(!IsVirtual());
8254     uint32_t lostAllocationCount = 0;
8255     for(VmaSuballocationList::iterator it = m_Suballocations.begin();
8256         it != m_Suballocations.end();
8257         ++it)
8258     {
8259         VmaAllocation const alloc = (VmaAllocation)it->userData;
8260         if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
8261             alloc->CanBecomeLost() &&
8262             alloc->MakeLost(currentFrameIndex, frameInUseCount))
8263         {
8264             it = FreeSuballocation(it);
8265             ++lostAllocationCount;
8266         }
8267     }
8268     return lostAllocationCount;
8269 }
8270 
CheckCorruption(const void * pBlockData)8271 VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
8272 {
8273     for(auto& suballoc : m_Suballocations)
8274     {
8275         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
8276         {
8277             if(!VmaValidateMagicValue(pBlockData, suballoc.offset - GetDebugMargin()))
8278             {
8279                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
8280                 return VK_ERROR_UNKNOWN;
8281             }
8282             if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
8283             {
8284                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
8285                 return VK_ERROR_UNKNOWN;
8286             }
8287         }
8288     }
8289 
8290     return VK_SUCCESS;
8291 }
8292 
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,void * userData)8293 void VmaBlockMetadata_Generic::Alloc(
8294     const VmaAllocationRequest& request,
8295     VmaSuballocationType type,
8296     void* userData)
8297 {
8298     VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
8299     VMA_ASSERT(request.item != m_Suballocations.end());
8300     VmaSuballocation& suballoc = *request.item;
8301     // Given suballocation is a free block.
8302     VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8303     // Given offset is inside this suballocation.
8304     VMA_ASSERT(request.offset >= suballoc.offset);
8305     const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
8306     VMA_ASSERT(suballoc.size >= paddingBegin + request.size);
8307     const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - request.size;
8308 
8309     // Unregister this free suballocation from m_FreeSuballocationsBySize and update
8310     // it to become used.
8311     UnregisterFreeSuballocation(request.item);
8312 
8313     suballoc.offset = request.offset;
8314     suballoc.size = request.size;
8315     suballoc.type = type;
8316     suballoc.userData = userData;
8317 
8318     // If there are any free bytes remaining at the end, insert new free suballocation after current one.
8319     if(paddingEnd)
8320     {
8321         VmaSuballocation paddingSuballoc = {};
8322         paddingSuballoc.offset = request.offset + request.size;
8323         paddingSuballoc.size = paddingEnd;
8324         paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8325         VmaSuballocationList::iterator next = request.item;
8326         ++next;
8327         const VmaSuballocationList::iterator paddingEndItem =
8328             m_Suballocations.insert(next, paddingSuballoc);
8329         RegisterFreeSuballocation(paddingEndItem);
8330     }
8331 
8332     // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
8333     if(paddingBegin)
8334     {
8335         VmaSuballocation paddingSuballoc = {};
8336         paddingSuballoc.offset = request.offset - paddingBegin;
8337         paddingSuballoc.size = paddingBegin;
8338         paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8339         const VmaSuballocationList::iterator paddingBeginItem =
8340             m_Suballocations.insert(request.item, paddingSuballoc);
8341         RegisterFreeSuballocation(paddingBeginItem);
8342     }
8343 
8344     // Update totals.
8345     m_FreeCount = m_FreeCount - 1;
8346     if(paddingBegin > 0)
8347     {
8348         ++m_FreeCount;
8349     }
8350     if(paddingEnd > 0)
8351     {
8352         ++m_FreeCount;
8353     }
8354     m_SumFreeSize -= request.size;
8355 }
8356 
FreeAtOffset(VkDeviceSize offset)8357 void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
8358 {
8359     for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
8360         suballocItem != m_Suballocations.end();
8361         ++suballocItem)
8362     {
8363         VmaSuballocation& suballoc = *suballocItem;
8364         if(suballoc.offset == offset)
8365         {
8366             FreeSuballocation(suballocItem);
8367             return;
8368         }
8369     }
8370     VMA_ASSERT(0 && "Not found!");
8371 }
8372 
GetAllocationInfo(VkDeviceSize offset,VmaVirtualAllocationInfo & outInfo)8373 void VmaBlockMetadata_Generic::GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo)
8374 {
8375     for (VmaSuballocationList::const_iterator suballocItem = m_Suballocations.begin();
8376         suballocItem != m_Suballocations.end();
8377         ++suballocItem)
8378     {
8379         const VmaSuballocation& suballoc = *suballocItem;
8380         if (suballoc.offset == offset)
8381         {
8382             outInfo.size = suballoc.size;
8383             outInfo.pUserData = suballoc.userData;
8384             return;
8385         }
8386     }
8387     VMA_ASSERT(0 && "Not found!");
8388 }
8389 
Clear()8390 void VmaBlockMetadata_Generic::Clear()
8391 {
8392     const VkDeviceSize size = GetSize();
8393 
8394     VMA_ASSERT(IsVirtual());
8395     m_FreeCount = 1;
8396     m_SumFreeSize = size;
8397     m_Suballocations.clear();
8398     m_FreeSuballocationsBySize.clear();
8399 
8400     VmaSuballocation suballoc = {};
8401     suballoc.offset = 0;
8402     suballoc.size = size;
8403     suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8404     m_Suballocations.push_back(suballoc);
8405 
8406     m_FreeSuballocationsBySize.push_back(m_Suballocations.begin());
8407 }
8408 
SetAllocationUserData(VkDeviceSize offset,void * userData)8409 void VmaBlockMetadata_Generic::SetAllocationUserData(VkDeviceSize offset, void* userData)
8410 {
8411     for (VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
8412         suballocItem != m_Suballocations.end();
8413         ++suballocItem)
8414     {
8415         VmaSuballocation& suballoc = *suballocItem;
8416         if (suballoc.offset == offset)
8417         {
8418             suballoc.userData = userData;
8419             return;
8420         }
8421     }
8422     VMA_ASSERT(0 && "Not found!");
8423 }
8424 
ValidateFreeSuballocationList()8425 bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
8426 {
8427     VkDeviceSize lastSize = 0;
8428     for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
8429     {
8430         const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
8431 
8432         VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
8433         VMA_VALIDATE(it->size >= lastSize);
8434         lastSize = it->size;
8435     }
8436     return true;
8437 }
8438 
CheckAllocation(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,VmaSuballocationList::const_iterator suballocItem,bool canMakeOtherLost,VkDeviceSize * pOffset,size_t * itemsToMakeLostCount,VkDeviceSize * pSumFreeSize,VkDeviceSize * pSumItemSize)8439 bool VmaBlockMetadata_Generic::CheckAllocation(
8440     uint32_t currentFrameIndex,
8441     uint32_t frameInUseCount,
8442     VkDeviceSize bufferImageGranularity,
8443     VkDeviceSize allocSize,
8444     VkDeviceSize allocAlignment,
8445     VmaSuballocationType allocType,
8446     VmaSuballocationList::const_iterator suballocItem,
8447     bool canMakeOtherLost,
8448     VkDeviceSize* pOffset,
8449     size_t* itemsToMakeLostCount,
8450     VkDeviceSize* pSumFreeSize,
8451     VkDeviceSize* pSumItemSize) const
8452 {
8453     VMA_ASSERT(allocSize > 0);
8454     VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
8455     VMA_ASSERT(suballocItem != m_Suballocations.cend());
8456     VMA_ASSERT(pOffset != VMA_NULL);
8457 
8458     *itemsToMakeLostCount = 0;
8459     *pSumFreeSize = 0;
8460     *pSumItemSize = 0;
8461 
8462     const VkDeviceSize debugMargin = GetDebugMargin();
8463 
8464     if(canMakeOtherLost)
8465     {
8466         VMA_ASSERT(!IsVirtual());
8467         if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8468         {
8469             *pSumFreeSize = suballocItem->size;
8470         }
8471         else
8472         {
8473             VmaAllocation const alloc = (VmaAllocation)suballocItem->userData;
8474             if(alloc->CanBecomeLost() &&
8475                 alloc->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8476             {
8477                 ++*itemsToMakeLostCount;
8478                 *pSumItemSize = suballocItem->size;
8479             }
8480             else
8481             {
8482                 return false;
8483             }
8484         }
8485 
8486         // Remaining size is too small for this request: Early return.
8487         if(GetSize() - suballocItem->offset < allocSize)
8488         {
8489             return false;
8490         }
8491 
8492         // Start from offset equal to beginning of this suballocation.
8493         *pOffset = suballocItem->offset;
8494 
8495         // Apply debugMargin at the beginning.
8496         if(debugMargin > 0)
8497         {
8498             *pOffset += debugMargin;
8499         }
8500 
8501         // Apply alignment.
8502         *pOffset = VmaAlignUp(*pOffset, allocAlignment);
8503 
8504         // Check previous suballocations for BufferImageGranularity conflicts.
8505         // Make bigger alignment if necessary.
8506         if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment)
8507         {
8508             bool bufferImageGranularityConflict = false;
8509             VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
8510             while(prevSuballocItem != m_Suballocations.cbegin())
8511             {
8512                 --prevSuballocItem;
8513                 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
8514                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
8515                 {
8516                     if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
8517                     {
8518                         bufferImageGranularityConflict = true;
8519                         break;
8520                     }
8521                 }
8522                 else
8523                     // Already on previous page.
8524                     break;
8525             }
8526             if(bufferImageGranularityConflict)
8527             {
8528                 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
8529             }
8530         }
8531 
8532         // Now that we have final *pOffset, check if we are past suballocItem.
8533         // If yes, return false - this function should be called for another suballocItem as starting point.
8534         if(*pOffset >= suballocItem->offset + suballocItem->size)
8535         {
8536             return false;
8537         }
8538 
8539         // Calculate padding at the beginning based on current offset.
8540         const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
8541 
8542         // Calculate required margin at the end.
8543         const VkDeviceSize requiredEndMargin = debugMargin;
8544 
8545         const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
8546         // Another early return check.
8547         if(suballocItem->offset + totalSize > GetSize())
8548         {
8549             return false;
8550         }
8551 
8552         // Advance lastSuballocItem until desired size is reached.
8553         // Update itemsToMakeLostCount.
8554         VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
8555         if(totalSize > suballocItem->size)
8556         {
8557             VkDeviceSize remainingSize = totalSize - suballocItem->size;
8558             while(remainingSize > 0)
8559             {
8560                 ++lastSuballocItem;
8561                 if(lastSuballocItem == m_Suballocations.cend())
8562                 {
8563                     return false;
8564                 }
8565                 if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8566                 {
8567                     *pSumFreeSize += lastSuballocItem->size;
8568                 }
8569                 else
8570                 {
8571                     VmaAllocation const lastSuballocAlloc = (VmaAllocation)lastSuballocItem->userData;
8572                     VMA_ASSERT(lastSuballocAlloc != VK_NULL_HANDLE);
8573                     if(lastSuballocAlloc->CanBecomeLost() &&
8574                         lastSuballocAlloc->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8575                     {
8576                         ++*itemsToMakeLostCount;
8577                         *pSumItemSize += lastSuballocItem->size;
8578                     }
8579                     else
8580                     {
8581                         return false;
8582                     }
8583                 }
8584                 remainingSize = (lastSuballocItem->size < remainingSize) ?
8585                     remainingSize - lastSuballocItem->size : 0;
8586             }
8587         }
8588 
8589         // Check next suballocations for BufferImageGranularity conflicts.
8590         // If conflict exists, we must mark more allocations lost or fail.
8591         if(allocSize % bufferImageGranularity || *pOffset % bufferImageGranularity)
8592         {
8593             VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
8594             ++nextSuballocItem;
8595             while(nextSuballocItem != m_Suballocations.cend())
8596             {
8597                 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
8598                 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8599                 {
8600                     if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
8601                     {
8602                         VmaAllocation const nextSuballocAlloc = (VmaAllocation)nextSuballoc.userData;
8603                         VMA_ASSERT(nextSuballocAlloc != VK_NULL_HANDLE);
8604                         if(nextSuballocAlloc->CanBecomeLost() &&
8605                             nextSuballocAlloc->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8606                         {
8607                             ++*itemsToMakeLostCount;
8608                         }
8609                         else
8610                         {
8611                             return false;
8612                         }
8613                     }
8614                 }
8615                 else
8616                 {
8617                     // Already on next page.
8618                     break;
8619                 }
8620                 ++nextSuballocItem;
8621             }
8622         }
8623     }
8624     else
8625     {
8626         const VmaSuballocation& suballoc = *suballocItem;
8627         VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8628 
8629         *pSumFreeSize = suballoc.size;
8630 
8631         // Size of this suballocation is too small for this request: Early return.
8632         if(suballoc.size < allocSize)
8633         {
8634             return false;
8635         }
8636 
8637         // Start from offset equal to beginning of this suballocation.
8638         *pOffset = suballoc.offset;
8639 
8640         // Apply debugMargin at the beginning.
8641         if(debugMargin > 0)
8642         {
8643             *pOffset += debugMargin;
8644         }
8645 
8646         // Apply alignment.
8647         *pOffset = VmaAlignUp(*pOffset, allocAlignment);
8648 
8649         // Check previous suballocations for BufferImageGranularity conflicts.
8650         // Make bigger alignment if necessary.
8651         if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment)
8652         {
8653             bool bufferImageGranularityConflict = false;
8654             VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
8655             while(prevSuballocItem != m_Suballocations.cbegin())
8656             {
8657                 --prevSuballocItem;
8658                 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
8659                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
8660                 {
8661                     if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
8662                     {
8663                         bufferImageGranularityConflict = true;
8664                         break;
8665                     }
8666                 }
8667                 else
8668                     // Already on previous page.
8669                     break;
8670             }
8671             if(bufferImageGranularityConflict)
8672             {
8673                 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
8674             }
8675         }
8676 
8677         // Calculate padding at the beginning based on current offset.
8678         const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
8679 
8680         // Calculate required margin at the end.
8681         const VkDeviceSize requiredEndMargin = debugMargin;
8682 
8683         // Fail if requested size plus margin before and after is bigger than size of this suballocation.
8684         if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
8685         {
8686             return false;
8687         }
8688 
8689         // Check next suballocations for BufferImageGranularity conflicts.
8690         // If conflict exists, allocation cannot be made here.
8691         if(allocSize % bufferImageGranularity || *pOffset % bufferImageGranularity)
8692         {
8693             VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
8694             ++nextSuballocItem;
8695             while(nextSuballocItem != m_Suballocations.cend())
8696             {
8697                 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
8698                 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8699                 {
8700                     if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
8701                     {
8702                         return false;
8703                     }
8704                 }
8705                 else
8706                 {
8707                     // Already on next page.
8708                     break;
8709                 }
8710                 ++nextSuballocItem;
8711             }
8712         }
8713     }
8714 
8715     // All tests passed: Success. pOffset is already filled.
8716     return true;
8717 }
8718 
MergeFreeWithNext(VmaSuballocationList::iterator item)8719 void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
8720 {
8721     VMA_ASSERT(item != m_Suballocations.end());
8722     VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8723 
8724     VmaSuballocationList::iterator nextItem = item;
8725     ++nextItem;
8726     VMA_ASSERT(nextItem != m_Suballocations.end());
8727     VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
8728 
8729     item->size += nextItem->size;
8730     --m_FreeCount;
8731     m_Suballocations.erase(nextItem);
8732 }
8733 
FreeSuballocation(VmaSuballocationList::iterator suballocItem)8734 VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
8735 {
8736     // Change this suballocation to be marked as free.
8737     VmaSuballocation& suballoc = *suballocItem;
8738     suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8739     suballoc.userData = VMA_NULL;
8740 
8741     // Update totals.
8742     ++m_FreeCount;
8743     m_SumFreeSize += suballoc.size;
8744 
8745     // Merge with previous and/or next suballocation if it's also free.
8746     bool mergeWithNext = false;
8747     bool mergeWithPrev = false;
8748 
8749     VmaSuballocationList::iterator nextItem = suballocItem;
8750     ++nextItem;
8751     if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
8752     {
8753         mergeWithNext = true;
8754     }
8755 
8756     VmaSuballocationList::iterator prevItem = suballocItem;
8757     if(suballocItem != m_Suballocations.begin())
8758     {
8759         --prevItem;
8760         if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8761         {
8762             mergeWithPrev = true;
8763         }
8764     }
8765 
8766     if(mergeWithNext)
8767     {
8768         UnregisterFreeSuballocation(nextItem);
8769         MergeFreeWithNext(suballocItem);
8770     }
8771 
8772     if(mergeWithPrev)
8773     {
8774         UnregisterFreeSuballocation(prevItem);
8775         MergeFreeWithNext(prevItem);
8776         RegisterFreeSuballocation(prevItem);
8777         return prevItem;
8778     }
8779     else
8780     {
8781         RegisterFreeSuballocation(suballocItem);
8782         return suballocItem;
8783     }
8784 }
8785 
RegisterFreeSuballocation(VmaSuballocationList::iterator item)8786 void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
8787 {
8788     VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8789     VMA_ASSERT(item->size > 0);
8790 
8791     // You may want to enable this validation at the beginning or at the end of
8792     // this function, depending on what do you want to check.
8793     VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8794 
8795     if(m_FreeSuballocationsBySize.empty())
8796     {
8797         m_FreeSuballocationsBySize.push_back(item);
8798     }
8799     else
8800     {
8801         VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
8802     }
8803 
8804     //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8805 }
8806 
8807 
UnregisterFreeSuballocation(VmaSuballocationList::iterator item)8808 void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
8809 {
8810     VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8811     VMA_ASSERT(item->size > 0);
8812 
8813     // You may want to enable this validation at the beginning or at the end of
8814     // this function, depending on what do you want to check.
8815     VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8816 
8817     VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
8818         m_FreeSuballocationsBySize.data(),
8819         m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
8820         item,
8821         VmaSuballocationItemSizeLess());
8822     for(size_t index = it - m_FreeSuballocationsBySize.data();
8823         index < m_FreeSuballocationsBySize.size();
8824         ++index)
8825     {
8826         if(m_FreeSuballocationsBySize[index] == item)
8827         {
8828             VmaVectorRemove(m_FreeSuballocationsBySize, index);
8829             return;
8830         }
8831         VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
8832     }
8833     VMA_ASSERT(0 && "Not found.");
8834 
8835     //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8836 }
8837 
IsBufferImageGranularityConflictPossible(VkDeviceSize bufferImageGranularity,VmaSuballocationType & inOutPrevSuballocType)8838 bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
8839     VkDeviceSize bufferImageGranularity,
8840     VmaSuballocationType& inOutPrevSuballocType) const
8841 {
8842     if(bufferImageGranularity == 1 || IsEmpty() || IsVirtual())
8843     {
8844         return false;
8845     }
8846 
8847     VkDeviceSize minAlignment = VK_WHOLE_SIZE;
8848     bool typeConflictFound = false;
8849     for(const auto& suballoc : m_Suballocations)
8850     {
8851         const VmaSuballocationType suballocType = suballoc.type;
8852         if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)
8853         {
8854             VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
8855             minAlignment = VMA_MIN(minAlignment, alloc->GetAlignment());
8856             if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))
8857             {
8858                 typeConflictFound = true;
8859             }
8860             inOutPrevSuballocType = suballocType;
8861         }
8862     }
8863 
8864     return typeConflictFound || minAlignment >= bufferImageGranularity;
8865 }
8866 
8867 ////////////////////////////////////////////////////////////////////////////////
8868 // class VmaBlockMetadata_Linear
8869 
VmaBlockMetadata_Linear(const VkAllocationCallbacks * pAllocationCallbacks,bool isVirtual)8870 VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(const VkAllocationCallbacks* pAllocationCallbacks, bool isVirtual) :
8871     VmaBlockMetadata(pAllocationCallbacks, isVirtual),
8872     m_SumFreeSize(0),
8873     m_Suballocations0(VmaStlAllocator<VmaSuballocation>(pAllocationCallbacks)),
8874     m_Suballocations1(VmaStlAllocator<VmaSuballocation>(pAllocationCallbacks)),
8875     m_1stVectorIndex(0),
8876     m_2ndVectorMode(SECOND_VECTOR_EMPTY),
8877     m_1stNullItemsBeginCount(0),
8878     m_1stNullItemsMiddleCount(0),
8879     m_2ndNullItemsCount(0)
8880 {
8881 }
8882 
~VmaBlockMetadata_Linear()8883 VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
8884 {
8885 }
8886 
Init(VkDeviceSize size)8887 void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
8888 {
8889     VmaBlockMetadata::Init(size);
8890     m_SumFreeSize = size;
8891 }
8892 
Validate()8893 bool VmaBlockMetadata_Linear::Validate() const
8894 {
8895     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8896     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8897 
8898     VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
8899     VMA_VALIDATE(!suballocations1st.empty() ||
8900         suballocations2nd.empty() ||
8901         m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
8902 
8903     if(!suballocations1st.empty())
8904     {
8905         // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
8906         VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].type != VMA_SUBALLOCATION_TYPE_FREE);
8907         // Null item at the end should be just pop_back().
8908         VMA_VALIDATE(suballocations1st.back().type != VMA_SUBALLOCATION_TYPE_FREE);
8909     }
8910     if(!suballocations2nd.empty())
8911     {
8912         // Null item at the end should be just pop_back().
8913         VMA_VALIDATE(suballocations2nd.back().type != VMA_SUBALLOCATION_TYPE_FREE);
8914     }
8915 
8916     VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
8917     VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
8918 
8919     VkDeviceSize sumUsedSize = 0;
8920     const size_t suballoc1stCount = suballocations1st.size();
8921     const VkDeviceSize debugMargin = GetDebugMargin();
8922     VkDeviceSize offset = debugMargin;
8923 
8924     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8925     {
8926         const size_t suballoc2ndCount = suballocations2nd.size();
8927         size_t nullItem2ndCount = 0;
8928         for(size_t i = 0; i < suballoc2ndCount; ++i)
8929         {
8930             const VmaSuballocation& suballoc = suballocations2nd[i];
8931             const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8932 
8933             VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
8934             if(!IsVirtual())
8935             {
8936                 VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));
8937             }
8938             VMA_VALIDATE(suballoc.offset >= offset);
8939 
8940             if(!currFree)
8941             {
8942                 if(!IsVirtual())
8943                 {
8944                     VMA_VALIDATE(alloc->GetOffset() == suballoc.offset);
8945                     VMA_VALIDATE(alloc->GetSize() == suballoc.size);
8946                 }
8947                 sumUsedSize += suballoc.size;
8948             }
8949             else
8950             {
8951                 ++nullItem2ndCount;
8952             }
8953 
8954             offset = suballoc.offset + suballoc.size + debugMargin;
8955         }
8956 
8957         VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
8958     }
8959 
8960     for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
8961     {
8962         const VmaSuballocation& suballoc = suballocations1st[i];
8963         VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
8964             suballoc.userData == VMA_NULL);
8965     }
8966 
8967     size_t nullItem1stCount = m_1stNullItemsBeginCount;
8968 
8969     for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
8970     {
8971         const VmaSuballocation& suballoc = suballocations1st[i];
8972         const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8973 
8974         VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
8975         if(!IsVirtual())
8976         {
8977             VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));
8978         }
8979         VMA_VALIDATE(suballoc.offset >= offset);
8980         VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
8981 
8982         if(!currFree)
8983         {
8984             if(!IsVirtual())
8985             {
8986                 VMA_VALIDATE(alloc->GetOffset() == suballoc.offset);
8987                 VMA_VALIDATE(alloc->GetSize() == suballoc.size);
8988             }
8989             sumUsedSize += suballoc.size;
8990         }
8991         else
8992         {
8993             ++nullItem1stCount;
8994         }
8995 
8996         offset = suballoc.offset + suballoc.size + debugMargin;
8997     }
8998     VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
8999 
9000     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9001     {
9002         const size_t suballoc2ndCount = suballocations2nd.size();
9003         size_t nullItem2ndCount = 0;
9004         for(size_t i = suballoc2ndCount; i--; )
9005         {
9006             const VmaSuballocation& suballoc = suballocations2nd[i];
9007             const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9008 
9009             VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
9010             if(!IsVirtual())
9011             {
9012                 VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));
9013             }
9014             VMA_VALIDATE(suballoc.offset >= offset);
9015 
9016             if(!currFree)
9017             {
9018                 if(!IsVirtual())
9019                 {
9020                     VMA_VALIDATE(alloc->GetOffset() == suballoc.offset);
9021                     VMA_VALIDATE(alloc->GetSize() == suballoc.size);
9022                 }
9023                 sumUsedSize += suballoc.size;
9024             }
9025             else
9026             {
9027                 ++nullItem2ndCount;
9028             }
9029 
9030             offset = suballoc.offset + suballoc.size + debugMargin;
9031         }
9032 
9033         VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
9034     }
9035 
9036     VMA_VALIDATE(offset <= GetSize());
9037     VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
9038 
9039     return true;
9040 }
9041 
GetAllocationCount()9042 size_t VmaBlockMetadata_Linear::GetAllocationCount() const
9043 {
9044     return AccessSuballocations1st().size() - m_1stNullItemsBeginCount - m_1stNullItemsMiddleCount +
9045         AccessSuballocations2nd().size() - m_2ndNullItemsCount;
9046 }
9047 
GetUnusedRangeSizeMax()9048 VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
9049 {
9050     const VkDeviceSize size = GetSize();
9051 
9052     /*
9053     We don't consider gaps inside allocation vectors with freed allocations because
9054     they are not suitable for reuse in linear allocator. We consider only space that
9055     is available for new allocations.
9056     */
9057     if(IsEmpty())
9058     {
9059         return size;
9060     }
9061 
9062     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9063 
9064     switch(m_2ndVectorMode)
9065     {
9066     case SECOND_VECTOR_EMPTY:
9067         /*
9068         Available space is after end of 1st, as well as before beginning of 1st (which
9069         would make it a ring buffer).
9070         */
9071         {
9072             const size_t suballocations1stCount = suballocations1st.size();
9073             VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
9074             const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
9075             const VmaSuballocation& lastSuballoc  = suballocations1st[suballocations1stCount - 1];
9076             return VMA_MAX(
9077                 firstSuballoc.offset,
9078                 size - (lastSuballoc.offset + lastSuballoc.size));
9079         }
9080         break;
9081 
9082     case SECOND_VECTOR_RING_BUFFER:
9083         /*
9084         Available space is only between end of 2nd and beginning of 1st.
9085         */
9086         {
9087             const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9088             const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
9089             const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
9090             return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
9091         }
9092         break;
9093 
9094     case SECOND_VECTOR_DOUBLE_STACK:
9095         /*
9096         Available space is only between end of 1st and top of 2nd.
9097         */
9098         {
9099             const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9100             const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
9101             const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
9102             return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
9103         }
9104         break;
9105 
9106     default:
9107         VMA_ASSERT(0);
9108         return 0;
9109     }
9110 }
9111 
CalcAllocationStatInfo(VmaStatInfo & outInfo)9112 void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
9113 {
9114     const VkDeviceSize size = GetSize();
9115     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9116     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9117     const size_t suballoc1stCount = suballocations1st.size();
9118     const size_t suballoc2ndCount = suballocations2nd.size();
9119 
9120     VmaInitStatInfo(outInfo);
9121     outInfo.blockCount = 1;
9122 
9123     VkDeviceSize lastOffset = 0;
9124 
9125     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9126     {
9127         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9128         size_t nextAlloc2ndIndex = 0;
9129         while(lastOffset < freeSpace2ndTo1stEnd)
9130         {
9131             // Find next non-null allocation or move nextAllocIndex to the end.
9132             while(nextAlloc2ndIndex < suballoc2ndCount &&
9133                 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
9134             {
9135                 ++nextAlloc2ndIndex;
9136             }
9137 
9138             // Found non-null allocation.
9139             if(nextAlloc2ndIndex < suballoc2ndCount)
9140             {
9141                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9142 
9143                 // 1. Process free space before this allocation.
9144                 if(lastOffset < suballoc.offset)
9145                 {
9146                     // There is free space from lastOffset to suballoc.offset.
9147                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9148                     VmaAddStatInfoUnusedRange(outInfo, unusedRangeSize);
9149                 }
9150 
9151                 // 2. Process this allocation.
9152                 // There is allocation with suballoc.offset, suballoc.size.
9153                 VmaAddStatInfoAllocation(outInfo, suballoc.size);
9154 
9155                 // 3. Prepare for next iteration.
9156                 lastOffset = suballoc.offset + suballoc.size;
9157                 ++nextAlloc2ndIndex;
9158             }
9159             // We are at the end.
9160             else
9161             {
9162                 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9163                 if(lastOffset < freeSpace2ndTo1stEnd)
9164                 {
9165                     const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9166                     VmaAddStatInfoUnusedRange(outInfo, unusedRangeSize);
9167                }
9168 
9169                 // End of loop.
9170                 lastOffset = freeSpace2ndTo1stEnd;
9171             }
9172         }
9173     }
9174 
9175     size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9176     const VkDeviceSize freeSpace1stTo2ndEnd =
9177         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9178     while(lastOffset < freeSpace1stTo2ndEnd)
9179     {
9180         // Find next non-null allocation or move nextAllocIndex to the end.
9181         while(nextAlloc1stIndex < suballoc1stCount &&
9182             suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)
9183         {
9184             ++nextAlloc1stIndex;
9185         }
9186 
9187         // Found non-null allocation.
9188         if(nextAlloc1stIndex < suballoc1stCount)
9189         {
9190             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9191 
9192             // 1. Process free space before this allocation.
9193             if(lastOffset < suballoc.offset)
9194             {
9195                 // There is free space from lastOffset to suballoc.offset.
9196                 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9197                 VmaAddStatInfoUnusedRange(outInfo, unusedRangeSize);
9198             }
9199 
9200             // 2. Process this allocation.
9201             // There is allocation with suballoc.offset, suballoc.size.
9202             VmaAddStatInfoAllocation(outInfo, suballoc.size);
9203 
9204             // 3. Prepare for next iteration.
9205             lastOffset = suballoc.offset + suballoc.size;
9206             ++nextAlloc1stIndex;
9207         }
9208         // We are at the end.
9209         else
9210         {
9211             // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9212             if(lastOffset < freeSpace1stTo2ndEnd)
9213             {
9214                 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9215                 VmaAddStatInfoUnusedRange(outInfo, unusedRangeSize);
9216            }
9217 
9218             // End of loop.
9219             lastOffset = freeSpace1stTo2ndEnd;
9220         }
9221     }
9222 
9223     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9224     {
9225         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9226         while(lastOffset < size)
9227         {
9228             // Find next non-null allocation or move nextAllocIndex to the end.
9229             while(nextAlloc2ndIndex != SIZE_MAX &&
9230                 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
9231             {
9232                 --nextAlloc2ndIndex;
9233             }
9234 
9235             // Found non-null allocation.
9236             if(nextAlloc2ndIndex != SIZE_MAX)
9237             {
9238                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9239 
9240                 // 1. Process free space before this allocation.
9241                 if(lastOffset < suballoc.offset)
9242                 {
9243                     // There is free space from lastOffset to suballoc.offset.
9244                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9245                     VmaAddStatInfoUnusedRange(outInfo, unusedRangeSize);
9246                 }
9247 
9248                 // 2. Process this allocation.
9249                 // There is allocation with suballoc.offset, suballoc.size.
9250                 VmaAddStatInfoAllocation(outInfo, suballoc.size);
9251 
9252                 // 3. Prepare for next iteration.
9253                 lastOffset = suballoc.offset + suballoc.size;
9254                 --nextAlloc2ndIndex;
9255             }
9256             // We are at the end.
9257             else
9258             {
9259                 // There is free space from lastOffset to size.
9260                 if(lastOffset < size)
9261                 {
9262                     const VkDeviceSize unusedRangeSize = size - lastOffset;
9263                     VmaAddStatInfoUnusedRange(outInfo, unusedRangeSize);
9264                }
9265 
9266                 // End of loop.
9267                 lastOffset = size;
9268             }
9269         }
9270     }
9271 
9272     outInfo.unusedBytes = size - outInfo.usedBytes;
9273 }
9274 
AddPoolStats(VmaPoolStats & inoutStats)9275 void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
9276 {
9277     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9278     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9279     const VkDeviceSize size = GetSize();
9280     const size_t suballoc1stCount = suballocations1st.size();
9281     const size_t suballoc2ndCount = suballocations2nd.size();
9282 
9283     inoutStats.size += size;
9284 
9285     VkDeviceSize lastOffset = 0;
9286 
9287     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9288     {
9289         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9290         size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
9291         while(lastOffset < freeSpace2ndTo1stEnd)
9292         {
9293             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9294             while(nextAlloc2ndIndex < suballoc2ndCount &&
9295                 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
9296             {
9297                 ++nextAlloc2ndIndex;
9298             }
9299 
9300             // Found non-null allocation.
9301             if(nextAlloc2ndIndex < suballoc2ndCount)
9302             {
9303                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9304 
9305                 // 1. Process free space before this allocation.
9306                 if(lastOffset < suballoc.offset)
9307                 {
9308                     // There is free space from lastOffset to suballoc.offset.
9309                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9310                     inoutStats.unusedSize += unusedRangeSize;
9311                     ++inoutStats.unusedRangeCount;
9312                     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9313                 }
9314 
9315                 // 2. Process this allocation.
9316                 // There is allocation with suballoc.offset, suballoc.size.
9317                 ++inoutStats.allocationCount;
9318 
9319                 // 3. Prepare for next iteration.
9320                 lastOffset = suballoc.offset + suballoc.size;
9321                 ++nextAlloc2ndIndex;
9322             }
9323             // We are at the end.
9324             else
9325             {
9326                 if(lastOffset < freeSpace2ndTo1stEnd)
9327                 {
9328                     // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9329                     const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9330                     inoutStats.unusedSize += unusedRangeSize;
9331                     ++inoutStats.unusedRangeCount;
9332                     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9333                 }
9334 
9335                 // End of loop.
9336                 lastOffset = freeSpace2ndTo1stEnd;
9337             }
9338         }
9339     }
9340 
9341     size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9342     const VkDeviceSize freeSpace1stTo2ndEnd =
9343         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9344     while(lastOffset < freeSpace1stTo2ndEnd)
9345     {
9346         // Find next non-null allocation or move nextAllocIndex to the end.
9347         while(nextAlloc1stIndex < suballoc1stCount &&
9348             suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)
9349         {
9350             ++nextAlloc1stIndex;
9351         }
9352 
9353         // Found non-null allocation.
9354         if(nextAlloc1stIndex < suballoc1stCount)
9355         {
9356             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9357 
9358             // 1. Process free space before this allocation.
9359             if(lastOffset < suballoc.offset)
9360             {
9361                 // There is free space from lastOffset to suballoc.offset.
9362                 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9363                 inoutStats.unusedSize += unusedRangeSize;
9364                 ++inoutStats.unusedRangeCount;
9365                 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9366             }
9367 
9368             // 2. Process this allocation.
9369             // There is allocation with suballoc.offset, suballoc.size.
9370             ++inoutStats.allocationCount;
9371 
9372             // 3. Prepare for next iteration.
9373             lastOffset = suballoc.offset + suballoc.size;
9374             ++nextAlloc1stIndex;
9375         }
9376         // We are at the end.
9377         else
9378         {
9379             if(lastOffset < freeSpace1stTo2ndEnd)
9380             {
9381                 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9382                 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9383                 inoutStats.unusedSize += unusedRangeSize;
9384                 ++inoutStats.unusedRangeCount;
9385                 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9386             }
9387 
9388             // End of loop.
9389             lastOffset = freeSpace1stTo2ndEnd;
9390         }
9391     }
9392 
9393     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9394     {
9395         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9396         while(lastOffset < size)
9397         {
9398             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9399             while(nextAlloc2ndIndex != SIZE_MAX &&
9400                 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
9401             {
9402                 --nextAlloc2ndIndex;
9403             }
9404 
9405             // Found non-null allocation.
9406             if(nextAlloc2ndIndex != SIZE_MAX)
9407             {
9408                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9409 
9410                 // 1. Process free space before this allocation.
9411                 if(lastOffset < suballoc.offset)
9412                 {
9413                     // There is free space from lastOffset to suballoc.offset.
9414                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9415                     inoutStats.unusedSize += unusedRangeSize;
9416                     ++inoutStats.unusedRangeCount;
9417                     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9418                 }
9419 
9420                 // 2. Process this allocation.
9421                 // There is allocation with suballoc.offset, suballoc.size.
9422                 ++inoutStats.allocationCount;
9423 
9424                 // 3. Prepare for next iteration.
9425                 lastOffset = suballoc.offset + suballoc.size;
9426                 --nextAlloc2ndIndex;
9427             }
9428             // We are at the end.
9429             else
9430             {
9431                 if(lastOffset < size)
9432                 {
9433                     // There is free space from lastOffset to size.
9434                     const VkDeviceSize unusedRangeSize = size - lastOffset;
9435                     inoutStats.unusedSize += unusedRangeSize;
9436                     ++inoutStats.unusedRangeCount;
9437                     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9438                 }
9439 
9440                 // End of loop.
9441                 lastOffset = size;
9442             }
9443         }
9444     }
9445 }
9446 
9447 #if VMA_STATS_STRING_ENABLED
PrintDetailedMap(class VmaJsonWriter & json)9448 void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
9449 {
9450     const VkDeviceSize size = GetSize();
9451     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9452     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9453     const size_t suballoc1stCount = suballocations1st.size();
9454     const size_t suballoc2ndCount = suballocations2nd.size();
9455 
9456     // FIRST PASS
9457 
9458     size_t unusedRangeCount = 0;
9459     VkDeviceSize usedBytes = 0;
9460 
9461     VkDeviceSize lastOffset = 0;
9462 
9463     size_t alloc2ndCount = 0;
9464     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9465     {
9466         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9467         size_t nextAlloc2ndIndex = 0;
9468         while(lastOffset < freeSpace2ndTo1stEnd)
9469         {
9470             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9471             while(nextAlloc2ndIndex < suballoc2ndCount &&
9472                 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
9473             {
9474                 ++nextAlloc2ndIndex;
9475             }
9476 
9477             // Found non-null allocation.
9478             if(nextAlloc2ndIndex < suballoc2ndCount)
9479             {
9480                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9481 
9482                 // 1. Process free space before this allocation.
9483                 if(lastOffset < suballoc.offset)
9484                 {
9485                     // There is free space from lastOffset to suballoc.offset.
9486                     ++unusedRangeCount;
9487                 }
9488 
9489                 // 2. Process this allocation.
9490                 // There is allocation with suballoc.offset, suballoc.size.
9491                 ++alloc2ndCount;
9492                 usedBytes += suballoc.size;
9493 
9494                 // 3. Prepare for next iteration.
9495                 lastOffset = suballoc.offset + suballoc.size;
9496                 ++nextAlloc2ndIndex;
9497             }
9498             // We are at the end.
9499             else
9500             {
9501                 if(lastOffset < freeSpace2ndTo1stEnd)
9502                 {
9503                     // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9504                     ++unusedRangeCount;
9505                 }
9506 
9507                 // End of loop.
9508                 lastOffset = freeSpace2ndTo1stEnd;
9509             }
9510         }
9511     }
9512 
9513     size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9514     size_t alloc1stCount = 0;
9515     const VkDeviceSize freeSpace1stTo2ndEnd =
9516         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9517     while(lastOffset < freeSpace1stTo2ndEnd)
9518     {
9519         // Find next non-null allocation or move nextAllocIndex to the end.
9520         while(nextAlloc1stIndex < suballoc1stCount &&
9521             suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)
9522         {
9523             ++nextAlloc1stIndex;
9524         }
9525 
9526         // Found non-null allocation.
9527         if(nextAlloc1stIndex < suballoc1stCount)
9528         {
9529             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9530 
9531             // 1. Process free space before this allocation.
9532             if(lastOffset < suballoc.offset)
9533             {
9534                 // There is free space from lastOffset to suballoc.offset.
9535                 ++unusedRangeCount;
9536             }
9537 
9538             // 2. Process this allocation.
9539             // There is allocation with suballoc.offset, suballoc.size.
9540             ++alloc1stCount;
9541             usedBytes += suballoc.size;
9542 
9543             // 3. Prepare for next iteration.
9544             lastOffset = suballoc.offset + suballoc.size;
9545             ++nextAlloc1stIndex;
9546         }
9547         // We are at the end.
9548         else
9549         {
9550             if(lastOffset < size)
9551             {
9552                 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9553                 ++unusedRangeCount;
9554             }
9555 
9556             // End of loop.
9557             lastOffset = freeSpace1stTo2ndEnd;
9558         }
9559     }
9560 
9561     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9562     {
9563         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9564         while(lastOffset < size)
9565         {
9566             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9567             while(nextAlloc2ndIndex != SIZE_MAX &&
9568                 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
9569             {
9570                 --nextAlloc2ndIndex;
9571             }
9572 
9573             // Found non-null allocation.
9574             if(nextAlloc2ndIndex != SIZE_MAX)
9575             {
9576                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9577 
9578                 // 1. Process free space before this allocation.
9579                 if(lastOffset < suballoc.offset)
9580                 {
9581                     // There is free space from lastOffset to suballoc.offset.
9582                     ++unusedRangeCount;
9583                 }
9584 
9585                 // 2. Process this allocation.
9586                 // There is allocation with suballoc.offset, suballoc.size.
9587                 ++alloc2ndCount;
9588                 usedBytes += suballoc.size;
9589 
9590                 // 3. Prepare for next iteration.
9591                 lastOffset = suballoc.offset + suballoc.size;
9592                 --nextAlloc2ndIndex;
9593             }
9594             // We are at the end.
9595             else
9596             {
9597                 if(lastOffset < size)
9598                 {
9599                     // There is free space from lastOffset to size.
9600                     ++unusedRangeCount;
9601                 }
9602 
9603                 // End of loop.
9604                 lastOffset = size;
9605             }
9606         }
9607     }
9608 
9609     const VkDeviceSize unusedBytes = size - usedBytes;
9610     PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
9611 
9612     // SECOND PASS
9613     lastOffset = 0;
9614 
9615     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9616     {
9617         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9618         size_t nextAlloc2ndIndex = 0;
9619         while(lastOffset < freeSpace2ndTo1stEnd)
9620         {
9621             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9622             while(nextAlloc2ndIndex < suballoc2ndCount &&
9623                 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
9624             {
9625                 ++nextAlloc2ndIndex;
9626             }
9627 
9628             // Found non-null allocation.
9629             if(nextAlloc2ndIndex < suballoc2ndCount)
9630             {
9631                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9632 
9633                 // 1. Process free space before this allocation.
9634                 if(lastOffset < suballoc.offset)
9635                 {
9636                     // There is free space from lastOffset to suballoc.offset.
9637                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9638                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9639                 }
9640 
9641                 // 2. Process this allocation.
9642                 // There is allocation with suballoc.offset, suballoc.size.
9643                 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);
9644 
9645                 // 3. Prepare for next iteration.
9646                 lastOffset = suballoc.offset + suballoc.size;
9647                 ++nextAlloc2ndIndex;
9648             }
9649             // We are at the end.
9650             else
9651             {
9652                 if(lastOffset < freeSpace2ndTo1stEnd)
9653                 {
9654                     // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9655                     const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9656                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9657                 }
9658 
9659                 // End of loop.
9660                 lastOffset = freeSpace2ndTo1stEnd;
9661             }
9662         }
9663     }
9664 
9665     nextAlloc1stIndex = m_1stNullItemsBeginCount;
9666     while(lastOffset < freeSpace1stTo2ndEnd)
9667     {
9668         // Find next non-null allocation or move nextAllocIndex to the end.
9669         while(nextAlloc1stIndex < suballoc1stCount &&
9670             suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)
9671         {
9672             ++nextAlloc1stIndex;
9673         }
9674 
9675         // Found non-null allocation.
9676         if(nextAlloc1stIndex < suballoc1stCount)
9677         {
9678             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9679 
9680             // 1. Process free space before this allocation.
9681             if(lastOffset < suballoc.offset)
9682             {
9683                 // There is free space from lastOffset to suballoc.offset.
9684                 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9685                 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9686             }
9687 
9688             // 2. Process this allocation.
9689             // There is allocation with suballoc.offset, suballoc.size.
9690             PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);
9691 
9692             // 3. Prepare for next iteration.
9693             lastOffset = suballoc.offset + suballoc.size;
9694             ++nextAlloc1stIndex;
9695         }
9696         // We are at the end.
9697         else
9698         {
9699             if(lastOffset < freeSpace1stTo2ndEnd)
9700             {
9701                 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9702                 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9703                 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9704             }
9705 
9706             // End of loop.
9707             lastOffset = freeSpace1stTo2ndEnd;
9708         }
9709     }
9710 
9711     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9712     {
9713         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9714         while(lastOffset < size)
9715         {
9716             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9717             while(nextAlloc2ndIndex != SIZE_MAX &&
9718                 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
9719             {
9720                 --nextAlloc2ndIndex;
9721             }
9722 
9723             // Found non-null allocation.
9724             if(nextAlloc2ndIndex != SIZE_MAX)
9725             {
9726                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9727 
9728                 // 1. Process free space before this allocation.
9729                 if(lastOffset < suballoc.offset)
9730                 {
9731                     // There is free space from lastOffset to suballoc.offset.
9732                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9733                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9734                 }
9735 
9736                 // 2. Process this allocation.
9737                 // There is allocation with suballoc.offset, suballoc.size.
9738                 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);
9739 
9740                 // 3. Prepare for next iteration.
9741                 lastOffset = suballoc.offset + suballoc.size;
9742                 --nextAlloc2ndIndex;
9743             }
9744             // We are at the end.
9745             else
9746             {
9747                 if(lastOffset < size)
9748                 {
9749                     // There is free space from lastOffset to size.
9750                     const VkDeviceSize unusedRangeSize = size - lastOffset;
9751                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9752                 }
9753 
9754                 // End of loop.
9755                 lastOffset = size;
9756             }
9757         }
9758     }
9759 
9760     PrintDetailedMap_End(json);
9761 }
9762 #endif // #if VMA_STATS_STRING_ENABLED
9763 
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)9764 bool VmaBlockMetadata_Linear::CreateAllocationRequest(
9765     uint32_t currentFrameIndex,
9766     uint32_t frameInUseCount,
9767     VkDeviceSize bufferImageGranularity,
9768     VkDeviceSize allocSize,
9769     VkDeviceSize allocAlignment,
9770     bool upperAddress,
9771     VmaSuballocationType allocType,
9772     bool canMakeOtherLost,
9773     uint32_t strategy,
9774     VmaAllocationRequest* pAllocationRequest)
9775 {
9776     VMA_ASSERT(allocSize > 0);
9777     VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9778     VMA_ASSERT(pAllocationRequest != VMA_NULL);
9779     VMA_HEAVY_ASSERT(Validate());
9780     pAllocationRequest->size = allocSize;
9781     return upperAddress ?
9782         CreateAllocationRequest_UpperAddress(
9783             currentFrameIndex, frameInUseCount, bufferImageGranularity,
9784             allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest) :
9785         CreateAllocationRequest_LowerAddress(
9786             currentFrameIndex, frameInUseCount, bufferImageGranularity,
9787             allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest);
9788 }
9789 
CreateAllocationRequest_UpperAddress(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)9790 bool VmaBlockMetadata_Linear::CreateAllocationRequest_UpperAddress(
9791     uint32_t currentFrameIndex,
9792     uint32_t frameInUseCount,
9793     VkDeviceSize bufferImageGranularity,
9794     VkDeviceSize allocSize,
9795     VkDeviceSize allocAlignment,
9796     VmaSuballocationType allocType,
9797     bool canMakeOtherLost,
9798     uint32_t strategy,
9799     VmaAllocationRequest* pAllocationRequest)
9800 {
9801     const VkDeviceSize blockSize = GetSize();
9802     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9803     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9804 
9805     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9806     {
9807         VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
9808         return false;
9809     }
9810 
9811     // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
9812     if(allocSize > blockSize)
9813     {
9814         return false;
9815     }
9816     VkDeviceSize resultBaseOffset = blockSize - allocSize;
9817     if(!suballocations2nd.empty())
9818     {
9819         const VmaSuballocation& lastSuballoc = suballocations2nd.back();
9820         resultBaseOffset = lastSuballoc.offset - allocSize;
9821         if(allocSize > lastSuballoc.offset)
9822         {
9823             return false;
9824         }
9825     }
9826 
9827     // Start from offset equal to end of free space.
9828     VkDeviceSize resultOffset = resultBaseOffset;
9829 
9830     const VkDeviceSize debugMargin = GetDebugMargin();
9831 
9832     // Apply debugMargin at the end.
9833     if(debugMargin > 0)
9834     {
9835         if(resultOffset < debugMargin)
9836         {
9837             return false;
9838         }
9839         resultOffset -= debugMargin;
9840     }
9841 
9842     // Apply alignment.
9843     resultOffset = VmaAlignDown(resultOffset, allocAlignment);
9844 
9845     // Check next suballocations from 2nd for BufferImageGranularity conflicts.
9846     // Make bigger alignment if necessary.
9847     if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty())
9848     {
9849         bool bufferImageGranularityConflict = false;
9850         for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
9851         {
9852             const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
9853             if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9854             {
9855                 if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
9856                 {
9857                     bufferImageGranularityConflict = true;
9858                     break;
9859                 }
9860             }
9861             else
9862                 // Already on previous page.
9863                 break;
9864         }
9865         if(bufferImageGranularityConflict)
9866         {
9867             resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
9868         }
9869     }
9870 
9871     // There is enough free space.
9872     const VkDeviceSize endOf1st = !suballocations1st.empty() ?
9873         suballocations1st.back().offset + suballocations1st.back().size :
9874         0;
9875     if(endOf1st + debugMargin <= resultOffset)
9876     {
9877         // Check previous suballocations for BufferImageGranularity conflicts.
9878         // If conflict exists, allocation cannot be made here.
9879         if(bufferImageGranularity > 1)
9880         {
9881             for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
9882             {
9883                 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
9884                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9885                 {
9886                     if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
9887                     {
9888                         return false;
9889                     }
9890                 }
9891                 else
9892                 {
9893                     // Already on next page.
9894                     break;
9895                 }
9896             }
9897         }
9898 
9899         // All tests passed: Success.
9900         pAllocationRequest->offset = resultOffset;
9901         pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
9902         pAllocationRequest->sumItemSize = 0;
9903         // pAllocationRequest->item unused.
9904         pAllocationRequest->itemsToMakeLostCount = 0;
9905         pAllocationRequest->type = VmaAllocationRequestType::UpperAddress;
9906         return true;
9907     }
9908 
9909     return false;
9910 }
9911 
CreateAllocationRequest_LowerAddress(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)9912 bool VmaBlockMetadata_Linear::CreateAllocationRequest_LowerAddress(
9913     uint32_t currentFrameIndex,
9914     uint32_t frameInUseCount,
9915     VkDeviceSize bufferImageGranularity,
9916     VkDeviceSize allocSize,
9917     VkDeviceSize allocAlignment,
9918     VmaSuballocationType allocType,
9919     bool canMakeOtherLost,
9920     uint32_t strategy,
9921     VmaAllocationRequest* pAllocationRequest)
9922 {
9923     const VkDeviceSize blockSize = GetSize();
9924     const VkDeviceSize debugMargin = GetDebugMargin();
9925     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9926     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9927 
9928     if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9929     {
9930         // Try to allocate at the end of 1st vector.
9931 
9932         VkDeviceSize resultBaseOffset = 0;
9933         if(!suballocations1st.empty())
9934         {
9935             const VmaSuballocation& lastSuballoc = suballocations1st.back();
9936             resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
9937         }
9938 
9939         // Start from offset equal to beginning of free space.
9940         VkDeviceSize resultOffset = resultBaseOffset;
9941 
9942         // Apply debugMargin at the beginning.
9943         if(debugMargin > 0)
9944         {
9945             resultOffset += debugMargin;
9946         }
9947 
9948         // Apply alignment.
9949         resultOffset = VmaAlignUp(resultOffset, allocAlignment);
9950 
9951         // Check previous suballocations for BufferImageGranularity conflicts.
9952         // Make bigger alignment if necessary.
9953         if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations1st.empty())
9954         {
9955             bool bufferImageGranularityConflict = false;
9956             for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
9957             {
9958                 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
9959                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9960                 {
9961                     if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9962                     {
9963                         bufferImageGranularityConflict = true;
9964                         break;
9965                     }
9966                 }
9967                 else
9968                     // Already on previous page.
9969                     break;
9970             }
9971             if(bufferImageGranularityConflict)
9972             {
9973                 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
9974             }
9975         }
9976 
9977         const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
9978             suballocations2nd.back().offset : blockSize;
9979 
9980         // There is enough free space at the end after alignment.
9981         if(resultOffset + allocSize + debugMargin <= freeSpaceEnd)
9982         {
9983             // Check next suballocations for BufferImageGranularity conflicts.
9984             // If conflict exists, allocation cannot be made here.
9985             if((allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity) && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9986             {
9987                 for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
9988                 {
9989                     const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
9990                     if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9991                     {
9992                         if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9993                         {
9994                             return false;
9995                         }
9996                     }
9997                     else
9998                     {
9999                         // Already on previous page.
10000                         break;
10001                     }
10002                 }
10003             }
10004 
10005             // All tests passed: Success.
10006             pAllocationRequest->offset = resultOffset;
10007             pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
10008             pAllocationRequest->sumItemSize = 0;
10009             // pAllocationRequest->item, customData unused.
10010             pAllocationRequest->type = VmaAllocationRequestType::EndOf1st;
10011             pAllocationRequest->itemsToMakeLostCount = 0;
10012             return true;
10013         }
10014     }
10015 
10016     // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
10017     // beginning of 1st vector as the end of free space.
10018     if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10019     {
10020         VMA_ASSERT(!suballocations1st.empty());
10021 
10022         VkDeviceSize resultBaseOffset = 0;
10023         if(!suballocations2nd.empty())
10024         {
10025             const VmaSuballocation& lastSuballoc = suballocations2nd.back();
10026             resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
10027         }
10028 
10029         // Start from offset equal to beginning of free space.
10030         VkDeviceSize resultOffset = resultBaseOffset;
10031 
10032         // Apply debugMargin at the beginning.
10033         if(debugMargin > 0)
10034         {
10035             resultOffset += debugMargin;
10036         }
10037 
10038         // Apply alignment.
10039         resultOffset = VmaAlignUp(resultOffset, allocAlignment);
10040 
10041         // Check previous suballocations for BufferImageGranularity conflicts.
10042         // Make bigger alignment if necessary.
10043         if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty())
10044         {
10045             bool bufferImageGranularityConflict = false;
10046             for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
10047             {
10048                 const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
10049                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
10050                 {
10051                     if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
10052                     {
10053                         bufferImageGranularityConflict = true;
10054                         break;
10055                     }
10056                 }
10057                 else
10058                     // Already on previous page.
10059                     break;
10060             }
10061             if(bufferImageGranularityConflict)
10062             {
10063                 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
10064             }
10065         }
10066 
10067         pAllocationRequest->itemsToMakeLostCount = 0;
10068         pAllocationRequest->sumItemSize = 0;
10069         size_t index1st = m_1stNullItemsBeginCount;
10070 
10071         if(canMakeOtherLost)
10072         {
10073             VMA_ASSERT(!IsVirtual());
10074             while(index1st < suballocations1st.size() &&
10075                 resultOffset + allocSize + debugMargin > suballocations1st[index1st].offset)
10076             {
10077                 // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
10078                 const VmaSuballocation& suballoc = suballocations1st[index1st];
10079                 if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
10080                 {
10081                     // No problem.
10082                 }
10083                 else
10084                 {
10085                     VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
10086                     VMA_ASSERT(alloc != VK_NULL_HANDLE);
10087                     if(alloc->CanBecomeLost() &&
10088                         alloc->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
10089                     {
10090                         ++pAllocationRequest->itemsToMakeLostCount;
10091                         pAllocationRequest->sumItemSize += suballoc.size;
10092                     }
10093                     else
10094                     {
10095                         return false;
10096                     }
10097                 }
10098                 ++index1st;
10099             }
10100 
10101             // Check next suballocations for BufferImageGranularity conflicts.
10102             // If conflict exists, we must mark more allocations lost or fail.
10103             if(allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity)
10104             {
10105                 while(index1st < suballocations1st.size())
10106                 {
10107                     const VmaSuballocation& suballoc = suballocations1st[index1st];
10108                     if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))
10109                     {
10110                         VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
10111                         if (alloc != VK_NULL_HANDLE)
10112                         {
10113                             // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
10114                             if(alloc->CanBecomeLost() &&
10115                                 alloc->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
10116                             {
10117                                 ++pAllocationRequest->itemsToMakeLostCount;
10118                                 pAllocationRequest->sumItemSize += suballoc.size;
10119                             }
10120                             else
10121                             {
10122                                 return false;
10123                             }
10124                         }
10125                     }
10126                     else
10127                     {
10128                         // Already on next page.
10129                         break;
10130                     }
10131                     ++index1st;
10132                 }
10133             }
10134 
10135             // Special case: There is not enough room at the end for this allocation, even after making all from the 1st lost.
10136             if(index1st == suballocations1st.size() &&
10137                 resultOffset + allocSize + debugMargin > blockSize)
10138             {
10139                 // TODO: This is a known bug that it's not yet implemented and the allocation is failing.
10140                 VMA_DEBUG_LOG("Unsupported special case in custom pool with linear allocation algorithm used as ring buffer with allocations that can be lost.");
10141             }
10142         }
10143 
10144         // There is enough free space at the end after alignment.
10145         if((index1st == suballocations1st.size() && resultOffset + allocSize + debugMargin <= blockSize) ||
10146             (index1st < suballocations1st.size() && resultOffset + allocSize + debugMargin <= suballocations1st[index1st].offset))
10147         {
10148             // Check next suballocations for BufferImageGranularity conflicts.
10149             // If conflict exists, allocation cannot be made here.
10150             if(allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity)
10151             {
10152                 for(size_t nextSuballocIndex = index1st;
10153                     nextSuballocIndex < suballocations1st.size();
10154                     nextSuballocIndex++)
10155                 {
10156                     const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
10157                     if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
10158                     {
10159                         if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
10160                         {
10161                             return false;
10162                         }
10163                     }
10164                     else
10165                     {
10166                         // Already on next page.
10167                         break;
10168                     }
10169                 }
10170             }
10171 
10172             // All tests passed: Success.
10173             pAllocationRequest->offset = resultOffset;
10174             pAllocationRequest->sumFreeSize =
10175                 (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : blockSize)
10176                 - resultBaseOffset
10177                 - pAllocationRequest->sumItemSize;
10178             pAllocationRequest->type = VmaAllocationRequestType::EndOf2nd;
10179             // pAllocationRequest->item, customData unused.
10180             return true;
10181         }
10182     }
10183 
10184     return false;
10185 }
10186 
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)10187 bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
10188     uint32_t currentFrameIndex,
10189     uint32_t frameInUseCount,
10190     VmaAllocationRequest* pAllocationRequest)
10191 {
10192     VMA_ASSERT(!IsVirtual());
10193 
10194     if(pAllocationRequest->itemsToMakeLostCount == 0)
10195     {
10196         return true;
10197     }
10198 
10199     VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
10200 
10201     // We always start from 1st.
10202     SuballocationVectorType* suballocations = &AccessSuballocations1st();
10203     size_t index = m_1stNullItemsBeginCount;
10204     size_t madeLostCount = 0;
10205     while(madeLostCount < pAllocationRequest->itemsToMakeLostCount)
10206     {
10207         if(index == suballocations->size())
10208         {
10209             index = 0;
10210             // If we get to the end of 1st, we wrap around to beginning of 2nd of 1st.
10211             if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10212             {
10213                 suballocations = &AccessSuballocations2nd();
10214             }
10215             // else: m_2ndVectorMode == SECOND_VECTOR_EMPTY:
10216             // suballocations continues pointing at AccessSuballocations1st().
10217             VMA_ASSERT(!suballocations->empty());
10218         }
10219         VmaSuballocation& suballoc = (*suballocations)[index];
10220         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10221         {
10222             VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
10223             VMA_ASSERT(alloc != VK_NULL_HANDLE && alloc->CanBecomeLost());
10224             if(alloc->MakeLost(currentFrameIndex, frameInUseCount))
10225             {
10226                 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10227                 suballoc.userData = VMA_NULL;
10228                 m_SumFreeSize += suballoc.size;
10229                 if(suballocations == &AccessSuballocations1st())
10230                 {
10231                     ++m_1stNullItemsMiddleCount;
10232                 }
10233                 else
10234                 {
10235                     ++m_2ndNullItemsCount;
10236                 }
10237                 ++madeLostCount;
10238             }
10239             else
10240             {
10241                 return false;
10242             }
10243         }
10244         ++index;
10245     }
10246 
10247     CleanupAfterFree();
10248     //VMA_HEAVY_ASSERT(Validate()); // Already called by CleanupAfterFree().
10249 
10250     return true;
10251 }
10252 
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)10253 uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
10254 {
10255     VMA_ASSERT(!IsVirtual());
10256 
10257     uint32_t lostAllocationCount = 0;
10258 
10259     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10260     for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
10261     {
10262         VmaSuballocation& suballoc = suballocations1st[i];
10263         VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
10264         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
10265             alloc->CanBecomeLost() &&
10266             alloc->MakeLost(currentFrameIndex, frameInUseCount))
10267         {
10268             suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10269             suballoc.userData = VMA_NULL;
10270             ++m_1stNullItemsMiddleCount;
10271             m_SumFreeSize += suballoc.size;
10272             ++lostAllocationCount;
10273         }
10274     }
10275 
10276     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10277     for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
10278     {
10279         VmaSuballocation& suballoc = suballocations2nd[i];
10280         VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
10281         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
10282             alloc->CanBecomeLost() &&
10283             alloc->MakeLost(currentFrameIndex, frameInUseCount))
10284         {
10285             suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10286             suballoc.userData = VMA_NULL;
10287             ++m_2ndNullItemsCount;
10288             m_SumFreeSize += suballoc.size;
10289             ++lostAllocationCount;
10290         }
10291     }
10292 
10293     if(lostAllocationCount)
10294     {
10295         CleanupAfterFree();
10296     }
10297 
10298     return lostAllocationCount;
10299 }
10300 
CheckCorruption(const void * pBlockData)10301 VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
10302 {
10303     VMA_ASSERT(!IsVirtual());
10304     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10305     for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
10306     {
10307         const VmaSuballocation& suballoc = suballocations1st[i];
10308         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10309         {
10310             if(!VmaValidateMagicValue(pBlockData, suballoc.offset - GetDebugMargin()))
10311             {
10312                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
10313                 return VK_ERROR_UNKNOWN;
10314             }
10315             if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
10316             {
10317                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
10318                 return VK_ERROR_UNKNOWN;
10319             }
10320         }
10321     }
10322 
10323     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10324     for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
10325     {
10326         const VmaSuballocation& suballoc = suballocations2nd[i];
10327         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10328         {
10329             if(!VmaValidateMagicValue(pBlockData, suballoc.offset - GetDebugMargin()))
10330             {
10331                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
10332                 return VK_ERROR_UNKNOWN;
10333             }
10334             if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
10335             {
10336                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
10337                 return VK_ERROR_UNKNOWN;
10338             }
10339         }
10340     }
10341 
10342     return VK_SUCCESS;
10343 }
10344 
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,void * userData)10345 void VmaBlockMetadata_Linear::Alloc(
10346     const VmaAllocationRequest& request,
10347     VmaSuballocationType type,
10348     void* userData)
10349 {
10350     const VmaSuballocation newSuballoc = { request.offset, request.size, userData, type };
10351 
10352     switch(request.type)
10353     {
10354     case VmaAllocationRequestType::UpperAddress:
10355         {
10356             VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
10357                 "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
10358             SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10359             suballocations2nd.push_back(newSuballoc);
10360             m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
10361         }
10362         break;
10363     case VmaAllocationRequestType::EndOf1st:
10364         {
10365             SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10366 
10367             VMA_ASSERT(suballocations1st.empty() ||
10368                 request.offset >= suballocations1st.back().offset + suballocations1st.back().size);
10369             // Check if it fits before the end of the block.
10370             VMA_ASSERT(request.offset + request.size <= GetSize());
10371 
10372             suballocations1st.push_back(newSuballoc);
10373         }
10374         break;
10375     case VmaAllocationRequestType::EndOf2nd:
10376         {
10377             SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10378             // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
10379             VMA_ASSERT(!suballocations1st.empty() &&
10380                 request.offset + request.size <= suballocations1st[m_1stNullItemsBeginCount].offset);
10381             SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10382 
10383             switch(m_2ndVectorMode)
10384             {
10385             case SECOND_VECTOR_EMPTY:
10386                 // First allocation from second part ring buffer.
10387                 VMA_ASSERT(suballocations2nd.empty());
10388                 m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
10389                 break;
10390             case SECOND_VECTOR_RING_BUFFER:
10391                 // 2-part ring buffer is already started.
10392                 VMA_ASSERT(!suballocations2nd.empty());
10393                 break;
10394             case SECOND_VECTOR_DOUBLE_STACK:
10395                 VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
10396                 break;
10397             default:
10398                 VMA_ASSERT(0);
10399             }
10400 
10401             suballocations2nd.push_back(newSuballoc);
10402         }
10403         break;
10404     default:
10405         VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
10406     }
10407 
10408     m_SumFreeSize -= newSuballoc.size;
10409 }
10410 
FreeAtOffset(VkDeviceSize offset)10411 void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
10412 {
10413     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10414     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10415 
10416     if(!suballocations1st.empty())
10417     {
10418         // First allocation: Mark it as next empty at the beginning.
10419         VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
10420         if(firstSuballoc.offset == offset)
10421         {
10422             firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10423             firstSuballoc.userData = VMA_NULL;
10424             m_SumFreeSize += firstSuballoc.size;
10425             ++m_1stNullItemsBeginCount;
10426             CleanupAfterFree();
10427             return;
10428         }
10429     }
10430 
10431     // Last allocation in 2-part ring buffer or top of upper stack (same logic).
10432     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
10433         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10434     {
10435         VmaSuballocation& lastSuballoc = suballocations2nd.back();
10436         if(lastSuballoc.offset == offset)
10437         {
10438             m_SumFreeSize += lastSuballoc.size;
10439             suballocations2nd.pop_back();
10440             CleanupAfterFree();
10441             return;
10442         }
10443     }
10444     // Last allocation in 1st vector.
10445     else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY)
10446     {
10447         VmaSuballocation& lastSuballoc = suballocations1st.back();
10448         if(lastSuballoc.offset == offset)
10449         {
10450             m_SumFreeSize += lastSuballoc.size;
10451             suballocations1st.pop_back();
10452             CleanupAfterFree();
10453             return;
10454         }
10455     }
10456 
10457     VmaSuballocation refSuballoc;
10458     refSuballoc.offset = offset;
10459     // Rest of members stays uninitialized intentionally for better performance.
10460 
10461     // Item from the middle of 1st vector.
10462     {
10463         const SuballocationVectorType::iterator it = VmaBinaryFindSorted(
10464             suballocations1st.begin() + m_1stNullItemsBeginCount,
10465             suballocations1st.end(),
10466             refSuballoc,
10467             VmaSuballocationOffsetLess());
10468         if(it != suballocations1st.end())
10469         {
10470             it->type = VMA_SUBALLOCATION_TYPE_FREE;
10471             it->userData = VMA_NULL;
10472             ++m_1stNullItemsMiddleCount;
10473             m_SumFreeSize += it->size;
10474             CleanupAfterFree();
10475             return;
10476         }
10477     }
10478 
10479     if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
10480     {
10481         // Item from the middle of 2nd vector.
10482         const SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
10483             VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :
10484             VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());
10485         if(it != suballocations2nd.end())
10486         {
10487             it->type = VMA_SUBALLOCATION_TYPE_FREE;
10488             it->userData = VMA_NULL;
10489             ++m_2ndNullItemsCount;
10490             m_SumFreeSize += it->size;
10491             CleanupAfterFree();
10492             return;
10493         }
10494     }
10495 
10496     VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
10497 }
10498 
GetAllocationInfo(VkDeviceSize offset,VmaVirtualAllocationInfo & outInfo)10499 void VmaBlockMetadata_Linear::GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo)
10500 {
10501     VmaSuballocation& suballoc = FindSuballocation(offset);
10502     outInfo.size = suballoc.size;
10503     outInfo.pUserData = suballoc.userData;
10504 }
10505 
Clear()10506 void VmaBlockMetadata_Linear::Clear()
10507 {
10508     m_SumFreeSize = GetSize();
10509     m_Suballocations0.clear();
10510     m_Suballocations1.clear();
10511     // Leaving m_1stVectorIndex unchanged - it doesn't matter.
10512     m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10513     m_1stNullItemsBeginCount = 0;
10514     m_1stNullItemsMiddleCount = 0;
10515     m_2ndNullItemsCount = 0;
10516 }
10517 
SetAllocationUserData(VkDeviceSize offset,void * userData)10518 void VmaBlockMetadata_Linear::SetAllocationUserData(VkDeviceSize offset, void* userData)
10519 {
10520     VmaSuballocation& suballoc = FindSuballocation(offset);
10521     suballoc.userData = userData;
10522 }
10523 
FindSuballocation(VkDeviceSize offset)10524 VmaSuballocation& VmaBlockMetadata_Linear::FindSuballocation(VkDeviceSize offset)
10525 {
10526     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10527     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10528 
10529     VmaSuballocation refSuballoc;
10530     refSuballoc.offset = offset;
10531     // Rest of members stays uninitialized intentionally for better performance.
10532 
10533     // Item from the 1st vector.
10534     {
10535         const SuballocationVectorType::iterator it = VmaBinaryFindSorted(
10536             suballocations1st.begin() + m_1stNullItemsBeginCount,
10537             suballocations1st.end(),
10538             refSuballoc,
10539             VmaSuballocationOffsetLess());
10540         if(it != suballocations1st.end())
10541         {
10542             return *it;
10543         }
10544     }
10545 
10546     if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
10547     {
10548         // Rest of members stays uninitialized intentionally for better performance.
10549         const SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
10550             VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :
10551             VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());
10552         if(it != suballocations2nd.end())
10553         {
10554             return *it;
10555         }
10556     }
10557 
10558     VMA_ASSERT(0 && "Allocation not found in linear allocator!");
10559     return suballocations1st.back(); // Should never occur.
10560 }
10561 
ShouldCompact1st()10562 bool VmaBlockMetadata_Linear::ShouldCompact1st() const
10563 {
10564     const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
10565     const size_t suballocCount = AccessSuballocations1st().size();
10566     return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
10567 }
10568 
CleanupAfterFree()10569 void VmaBlockMetadata_Linear::CleanupAfterFree()
10570 {
10571     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10572     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10573 
10574     if(IsEmpty())
10575     {
10576         suballocations1st.clear();
10577         suballocations2nd.clear();
10578         m_1stNullItemsBeginCount = 0;
10579         m_1stNullItemsMiddleCount = 0;
10580         m_2ndNullItemsCount = 0;
10581         m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10582     }
10583     else
10584     {
10585         const size_t suballoc1stCount = suballocations1st.size();
10586         const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
10587         VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
10588 
10589         // Find more null items at the beginning of 1st vector.
10590         while(m_1stNullItemsBeginCount < suballoc1stCount &&
10591             suballocations1st[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE)
10592         {
10593             ++m_1stNullItemsBeginCount;
10594             --m_1stNullItemsMiddleCount;
10595         }
10596 
10597         // Find more null items at the end of 1st vector.
10598         while(m_1stNullItemsMiddleCount > 0 &&
10599             suballocations1st.back().type == VMA_SUBALLOCATION_TYPE_FREE)
10600         {
10601             --m_1stNullItemsMiddleCount;
10602             suballocations1st.pop_back();
10603         }
10604 
10605         // Find more null items at the end of 2nd vector.
10606         while(m_2ndNullItemsCount > 0 &&
10607             suballocations2nd.back().type == VMA_SUBALLOCATION_TYPE_FREE)
10608         {
10609             --m_2ndNullItemsCount;
10610             suballocations2nd.pop_back();
10611         }
10612 
10613         // Find more null items at the beginning of 2nd vector.
10614         while(m_2ndNullItemsCount > 0 &&
10615             suballocations2nd[0].type == VMA_SUBALLOCATION_TYPE_FREE)
10616         {
10617             --m_2ndNullItemsCount;
10618             VmaVectorRemove(suballocations2nd, 0);
10619         }
10620 
10621         if(ShouldCompact1st())
10622         {
10623             const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
10624             size_t srcIndex = m_1stNullItemsBeginCount;
10625             for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
10626             {
10627                 while(suballocations1st[srcIndex].type == VMA_SUBALLOCATION_TYPE_FREE)
10628                 {
10629                     ++srcIndex;
10630                 }
10631                 if(dstIndex != srcIndex)
10632                 {
10633                     suballocations1st[dstIndex] = suballocations1st[srcIndex];
10634                 }
10635                 ++srcIndex;
10636             }
10637             suballocations1st.resize(nonNullItemCount);
10638             m_1stNullItemsBeginCount = 0;
10639             m_1stNullItemsMiddleCount = 0;
10640         }
10641 
10642         // 2nd vector became empty.
10643         if(suballocations2nd.empty())
10644         {
10645             m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10646         }
10647 
10648         // 1st vector became empty.
10649         if(suballocations1st.size() - m_1stNullItemsBeginCount == 0)
10650         {
10651             suballocations1st.clear();
10652             m_1stNullItemsBeginCount = 0;
10653 
10654             if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10655             {
10656                 // Swap 1st with 2nd. Now 2nd is empty.
10657                 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10658                 m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
10659                 while(m_1stNullItemsBeginCount < suballocations2nd.size() &&
10660                     suballocations2nd[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE)
10661                 {
10662                     ++m_1stNullItemsBeginCount;
10663                     --m_1stNullItemsMiddleCount;
10664                 }
10665                 m_2ndNullItemsCount = 0;
10666                 m_1stVectorIndex ^= 1;
10667             }
10668         }
10669     }
10670 
10671     VMA_HEAVY_ASSERT(Validate());
10672 }
10673 
10674 
10675 ////////////////////////////////////////////////////////////////////////////////
10676 // class VmaBlockMetadata_Buddy
10677 
VmaBlockMetadata_Buddy(const VkAllocationCallbacks * pAllocationCallbacks,bool isVirtual)10678 VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(const VkAllocationCallbacks* pAllocationCallbacks, bool isVirtual) :
10679     VmaBlockMetadata(pAllocationCallbacks, isVirtual),
10680     m_NodeAllocator(pAllocationCallbacks,
10681         32), // firstBlockCapacity
10682     m_Root(VMA_NULL),
10683     m_AllocationCount(0),
10684     m_FreeCount(1),
10685     m_SumFreeSize(0)
10686 {
10687     memset(m_FreeList, 0, sizeof(m_FreeList));
10688 }
10689 
~VmaBlockMetadata_Buddy()10690 VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
10691 {
10692     DeleteNodeChildren(m_Root);
10693     m_NodeAllocator.Free(m_Root);
10694 }
10695 
Init(VkDeviceSize size)10696 void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
10697 {
10698     VmaBlockMetadata::Init(size);
10699 
10700     m_UsableSize = VmaPrevPow2(size);
10701     m_SumFreeSize = m_UsableSize;
10702 
10703     // Calculate m_LevelCount.
10704     const VkDeviceSize minNodeSize = IsVirtual() ? 1 : 16;
10705     m_LevelCount = 1;
10706     while(m_LevelCount < MAX_LEVELS &&
10707         LevelToNodeSize(m_LevelCount) >= minNodeSize)
10708     {
10709         ++m_LevelCount;
10710     }
10711 
10712     Node* rootNode = m_NodeAllocator.Alloc();
10713     rootNode->offset = 0;
10714     rootNode->type = Node::TYPE_FREE;
10715     rootNode->parent = VMA_NULL;
10716     rootNode->buddy = VMA_NULL;
10717 
10718     m_Root = rootNode;
10719     AddToFreeListFront(0, rootNode);
10720 }
10721 
Validate()10722 bool VmaBlockMetadata_Buddy::Validate() const
10723 {
10724     // Validate tree.
10725     ValidationContext ctx;
10726     if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
10727     {
10728         VMA_VALIDATE(false && "ValidateNode failed.");
10729     }
10730     VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
10731     VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
10732 
10733     // Validate free node lists.
10734     for(uint32_t level = 0; level < m_LevelCount; ++level)
10735     {
10736         VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
10737             m_FreeList[level].front->free.prev == VMA_NULL);
10738 
10739         for(Node* node = m_FreeList[level].front;
10740             node != VMA_NULL;
10741             node = node->free.next)
10742         {
10743             VMA_VALIDATE(node->type == Node::TYPE_FREE);
10744 
10745             if(node->free.next == VMA_NULL)
10746             {
10747                 VMA_VALIDATE(m_FreeList[level].back == node);
10748             }
10749             else
10750             {
10751                 VMA_VALIDATE(node->free.next->free.prev == node);
10752             }
10753         }
10754     }
10755 
10756     // Validate that free lists ar higher levels are empty.
10757     for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
10758     {
10759         VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
10760     }
10761 
10762     return true;
10763 }
10764 
GetUnusedRangeSizeMax()10765 VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
10766 {
10767     for(uint32_t level = 0; level < m_LevelCount; ++level)
10768     {
10769         if(m_FreeList[level].front != VMA_NULL)
10770         {
10771             return LevelToNodeSize(level);
10772         }
10773     }
10774     return 0;
10775 }
10776 
CalcAllocationStatInfo(VmaStatInfo & outInfo)10777 void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
10778 {
10779     VmaInitStatInfo(outInfo);
10780     outInfo.blockCount = 1;
10781 
10782     CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0));
10783 
10784     const VkDeviceSize unusableSize = GetUnusableSize();
10785     if(unusableSize > 0)
10786     {
10787         VmaAddStatInfoUnusedRange(outInfo, unusableSize);
10788     }
10789 }
10790 
AddPoolStats(VmaPoolStats & inoutStats)10791 void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
10792 {
10793     const VkDeviceSize unusableSize = GetUnusableSize();
10794 
10795     inoutStats.size += GetSize();
10796     inoutStats.unusedSize += m_SumFreeSize + unusableSize;
10797     inoutStats.allocationCount += m_AllocationCount;
10798     inoutStats.unusedRangeCount += m_FreeCount;
10799     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
10800 
10801     if(unusableSize > 0)
10802     {
10803         ++inoutStats.unusedRangeCount;
10804         // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
10805     }
10806 }
10807 
10808 #if VMA_STATS_STRING_ENABLED
10809 
PrintDetailedMap(class VmaJsonWriter & json)10810 void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
10811 {
10812     VmaStatInfo stat;
10813     CalcAllocationStatInfo(stat);
10814 
10815     PrintDetailedMap_Begin(
10816         json,
10817         stat.unusedBytes,
10818         stat.allocationCount,
10819         stat.unusedRangeCount);
10820 
10821     PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
10822 
10823     const VkDeviceSize unusableSize = GetUnusableSize();
10824     if(unusableSize > 0)
10825     {
10826         PrintDetailedMap_UnusedRange(json,
10827             m_UsableSize, // offset
10828             unusableSize); // size
10829     }
10830 
10831     PrintDetailedMap_End(json);
10832 }
10833 
10834 #endif // #if VMA_STATS_STRING_ENABLED
10835 
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)10836 bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
10837     uint32_t currentFrameIndex,
10838     uint32_t frameInUseCount,
10839     VkDeviceSize bufferImageGranularity,
10840     VkDeviceSize allocSize,
10841     VkDeviceSize allocAlignment,
10842     bool upperAddress,
10843     VmaSuballocationType allocType,
10844     bool canMakeOtherLost,
10845     uint32_t strategy,
10846     VmaAllocationRequest* pAllocationRequest)
10847 {
10848     VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
10849 
10850     allocSize = AlignAllocationSize(allocSize);
10851 
10852     // Simple way to respect bufferImageGranularity. May be optimized some day.
10853     // Whenever it might be an OPTIMAL image...
10854     if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
10855         allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
10856         allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
10857     {
10858         allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity);
10859         allocSize = VMA_MAX(allocSize, bufferImageGranularity);
10860     }
10861 
10862     if(allocSize > m_UsableSize)
10863     {
10864         return false;
10865     }
10866 
10867     const uint32_t targetLevel = AllocSizeToLevel(allocSize);
10868     for(uint32_t level = targetLevel; level--; )
10869     {
10870         for(Node* freeNode = m_FreeList[level].front;
10871             freeNode != VMA_NULL;
10872             freeNode = freeNode->free.next)
10873         {
10874             if(freeNode->offset % allocAlignment == 0)
10875             {
10876                 pAllocationRequest->type = VmaAllocationRequestType::Normal;
10877                 pAllocationRequest->offset = freeNode->offset;
10878                 pAllocationRequest->size = allocSize;
10879                 pAllocationRequest->sumFreeSize = LevelToNodeSize(level);
10880                 pAllocationRequest->sumItemSize = 0;
10881                 pAllocationRequest->itemsToMakeLostCount = 0;
10882                 pAllocationRequest->customData = (void*)(uintptr_t)level;
10883                 return true;
10884             }
10885         }
10886     }
10887 
10888     return false;
10889 }
10890 
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)10891 bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
10892     uint32_t currentFrameIndex,
10893     uint32_t frameInUseCount,
10894     VmaAllocationRequest* pAllocationRequest)
10895 {
10896     /*
10897     Lost allocations are not supported in buddy allocator at the moment.
10898     Support might be added in the future.
10899     */
10900     return pAllocationRequest->itemsToMakeLostCount == 0;
10901 }
10902 
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)10903 uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
10904 {
10905     /*
10906     Lost allocations are not supported in buddy allocator at the moment.
10907     Support might be added in the future.
10908     */
10909     return 0;
10910 }
10911 
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,void * userData)10912 void VmaBlockMetadata_Buddy::Alloc(
10913     const VmaAllocationRequest& request,
10914     VmaSuballocationType type,
10915     void* userData)
10916 {
10917     VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
10918 
10919     const uint32_t targetLevel = AllocSizeToLevel(request.size);
10920     uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
10921 
10922     Node* currNode = m_FreeList[currLevel].front;
10923     VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
10924     while(currNode->offset != request.offset)
10925     {
10926         currNode = currNode->free.next;
10927         VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
10928     }
10929 
10930     // Go down, splitting free nodes.
10931     while(currLevel < targetLevel)
10932     {
10933         // currNode is already first free node at currLevel.
10934         // Remove it from list of free nodes at this currLevel.
10935         RemoveFromFreeList(currLevel, currNode);
10936 
10937         const uint32_t childrenLevel = currLevel + 1;
10938 
10939         // Create two free sub-nodes.
10940         Node* leftChild = m_NodeAllocator.Alloc();
10941         Node* rightChild = m_NodeAllocator.Alloc();
10942 
10943         leftChild->offset = currNode->offset;
10944         leftChild->type = Node::TYPE_FREE;
10945         leftChild->parent = currNode;
10946         leftChild->buddy = rightChild;
10947 
10948         rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
10949         rightChild->type = Node::TYPE_FREE;
10950         rightChild->parent = currNode;
10951         rightChild->buddy = leftChild;
10952 
10953         // Convert current currNode to split type.
10954         currNode->type = Node::TYPE_SPLIT;
10955         currNode->split.leftChild = leftChild;
10956 
10957         // Add child nodes to free list. Order is important!
10958         AddToFreeListFront(childrenLevel, rightChild);
10959         AddToFreeListFront(childrenLevel, leftChild);
10960 
10961         ++m_FreeCount;
10962         ++currLevel;
10963         currNode = m_FreeList[currLevel].front;
10964 
10965         /*
10966         We can be sure that currNode, as left child of node previously split,
10967         also fulfills the alignment requirement.
10968         */
10969     }
10970 
10971     // Remove from free list.
10972     VMA_ASSERT(currLevel == targetLevel &&
10973         currNode != VMA_NULL &&
10974         currNode->type == Node::TYPE_FREE);
10975     RemoveFromFreeList(currLevel, currNode);
10976 
10977     // Convert to allocation node.
10978     currNode->type = Node::TYPE_ALLOCATION;
10979     currNode->allocation.userData = userData;
10980 
10981     ++m_AllocationCount;
10982     --m_FreeCount;
10983     m_SumFreeSize -= request.size;
10984 }
10985 
GetAllocationInfo(VkDeviceSize offset,VmaVirtualAllocationInfo & outInfo)10986 void VmaBlockMetadata_Buddy::GetAllocationInfo(VkDeviceSize offset, VmaVirtualAllocationInfo& outInfo)
10987 {
10988     uint32_t level = 0;
10989     const Node* const node = FindAllocationNode(offset, level);
10990     outInfo.size = LevelToNodeSize(level);
10991     outInfo.pUserData = node->allocation.userData;
10992 }
10993 
DeleteNodeChildren(Node * node)10994 void VmaBlockMetadata_Buddy::DeleteNodeChildren(Node* node)
10995 {
10996     if(node->type == Node::TYPE_SPLIT)
10997     {
10998         DeleteNodeChildren(node->split.leftChild->buddy);
10999         DeleteNodeChildren(node->split.leftChild);
11000         const VkAllocationCallbacks* allocationCallbacks = GetAllocationCallbacks();
11001         m_NodeAllocator.Free(node->split.leftChild->buddy);
11002         m_NodeAllocator.Free(node->split.leftChild);
11003     }
11004 }
11005 
Clear()11006 void VmaBlockMetadata_Buddy::Clear()
11007 {
11008     DeleteNodeChildren(m_Root);
11009     m_Root->type = Node::TYPE_FREE;
11010     m_AllocationCount = 0;
11011     m_FreeCount = 1;
11012     m_SumFreeSize = m_UsableSize;
11013 }
11014 
SetAllocationUserData(VkDeviceSize offset,void * userData)11015 void VmaBlockMetadata_Buddy::SetAllocationUserData(VkDeviceSize offset, void* userData)
11016 {
11017     uint32_t level = 0;
11018     Node* const node = FindAllocationNode(offset, level);
11019     node->allocation.userData = userData;
11020 }
11021 
FindAllocationNode(VkDeviceSize offset,uint32_t & outLevel)11022 VmaBlockMetadata_Buddy::Node* VmaBlockMetadata_Buddy::FindAllocationNode(VkDeviceSize offset, uint32_t& outLevel)
11023 {
11024     Node* node = m_Root;
11025     VkDeviceSize nodeOffset = 0;
11026     outLevel = 0;
11027     VkDeviceSize levelNodeSize = LevelToNodeSize(0);
11028     while(node->type == Node::TYPE_SPLIT)
11029     {
11030         const VkDeviceSize nextLevelNodeSize = levelNodeSize >> 1;
11031         if(offset < nodeOffset + nextLevelNodeSize)
11032         {
11033             node = node->split.leftChild;
11034         }
11035         else
11036         {
11037             node = node->split.leftChild->buddy;
11038             nodeOffset += nextLevelNodeSize;
11039         }
11040         ++outLevel;
11041         levelNodeSize = nextLevelNodeSize;
11042     }
11043 
11044     VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
11045     return node;
11046 }
11047 
ValidateNode(ValidationContext & ctx,const Node * parent,const Node * curr,uint32_t level,VkDeviceSize levelNodeSize)11048 bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
11049 {
11050     VMA_VALIDATE(level < m_LevelCount);
11051     VMA_VALIDATE(curr->parent == parent);
11052     VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
11053     VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
11054     switch(curr->type)
11055     {
11056     case Node::TYPE_FREE:
11057         // curr->free.prev, next are validated separately.
11058         ctx.calculatedSumFreeSize += levelNodeSize;
11059         ++ctx.calculatedFreeCount;
11060         break;
11061     case Node::TYPE_ALLOCATION:
11062         ++ctx.calculatedAllocationCount;
11063         if(!IsVirtual())
11064         {
11065             VMA_VALIDATE(curr->allocation.userData != VMA_NULL);
11066         }
11067         break;
11068     case Node::TYPE_SPLIT:
11069         {
11070             const uint32_t childrenLevel = level + 1;
11071             const VkDeviceSize childrenLevelNodeSize = levelNodeSize >> 1;
11072             const Node* const leftChild = curr->split.leftChild;
11073             VMA_VALIDATE(leftChild != VMA_NULL);
11074             VMA_VALIDATE(leftChild->offset == curr->offset);
11075             if(!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
11076             {
11077                 VMA_VALIDATE(false && "ValidateNode for left child failed.");
11078             }
11079             const Node* const rightChild = leftChild->buddy;
11080             VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
11081             if(!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
11082             {
11083                 VMA_VALIDATE(false && "ValidateNode for right child failed.");
11084             }
11085         }
11086         break;
11087     default:
11088         return false;
11089     }
11090 
11091     return true;
11092 }
11093 
AllocSizeToLevel(VkDeviceSize allocSize)11094 uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
11095 {
11096     // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
11097     uint32_t level = 0;
11098     VkDeviceSize currLevelNodeSize = m_UsableSize;
11099     VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
11100     while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
11101     {
11102         ++level;
11103         currLevelNodeSize >>= 1;
11104         nextLevelNodeSize >>= 1;
11105     }
11106     return level;
11107 }
11108 
FreeAtOffset(VkDeviceSize offset)11109 void VmaBlockMetadata_Buddy::FreeAtOffset(VkDeviceSize offset)
11110 {
11111     uint32_t level = 0;
11112     Node* node = FindAllocationNode(offset, level);
11113 
11114     ++m_FreeCount;
11115     --m_AllocationCount;
11116     m_SumFreeSize += LevelToNodeSize(level);
11117 
11118     node->type = Node::TYPE_FREE;
11119 
11120     // Join free nodes if possible.
11121     while(level > 0 && node->buddy->type == Node::TYPE_FREE)
11122     {
11123         RemoveFromFreeList(level, node->buddy);
11124         Node* const parent = node->parent;
11125 
11126         m_NodeAllocator.Free(node->buddy);
11127         m_NodeAllocator.Free(node);
11128         parent->type = Node::TYPE_FREE;
11129 
11130         node = parent;
11131         --level;
11132         --m_FreeCount;
11133     }
11134 
11135     AddToFreeListFront(level, node);
11136 }
11137 
CalcAllocationStatInfoNode(VmaStatInfo & inoutInfo,const Node * node,VkDeviceSize levelNodeSize)11138 void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& inoutInfo, const Node* node, VkDeviceSize levelNodeSize) const
11139 {
11140     switch(node->type)
11141     {
11142     case Node::TYPE_FREE:
11143         VmaAddStatInfoUnusedRange(inoutInfo, levelNodeSize);
11144         break;
11145     case Node::TYPE_ALLOCATION:
11146         VmaAddStatInfoAllocation(inoutInfo, levelNodeSize);
11147         break;
11148     case Node::TYPE_SPLIT:
11149         {
11150             const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
11151             const Node* const leftChild = node->split.leftChild;
11152             CalcAllocationStatInfoNode(inoutInfo, leftChild, childrenNodeSize);
11153             const Node* const rightChild = leftChild->buddy;
11154             CalcAllocationStatInfoNode(inoutInfo, rightChild, childrenNodeSize);
11155         }
11156         break;
11157     default:
11158         VMA_ASSERT(0);
11159     }
11160 }
11161 
AddToFreeListFront(uint32_t level,Node * node)11162 void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
11163 {
11164     VMA_ASSERT(node->type == Node::TYPE_FREE);
11165 
11166     // List is empty.
11167     Node* const frontNode = m_FreeList[level].front;
11168     if(frontNode == VMA_NULL)
11169     {
11170         VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
11171         node->free.prev = node->free.next = VMA_NULL;
11172         m_FreeList[level].front = m_FreeList[level].back = node;
11173     }
11174     else
11175     {
11176         VMA_ASSERT(frontNode->free.prev == VMA_NULL);
11177         node->free.prev = VMA_NULL;
11178         node->free.next = frontNode;
11179         frontNode->free.prev = node;
11180         m_FreeList[level].front = node;
11181     }
11182 }
11183 
RemoveFromFreeList(uint32_t level,Node * node)11184 void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
11185 {
11186     VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
11187 
11188     // It is at the front.
11189     if(node->free.prev == VMA_NULL)
11190     {
11191         VMA_ASSERT(m_FreeList[level].front == node);
11192         m_FreeList[level].front = node->free.next;
11193     }
11194     else
11195     {
11196         Node* const prevFreeNode = node->free.prev;
11197         VMA_ASSERT(prevFreeNode->free.next == node);
11198         prevFreeNode->free.next = node->free.next;
11199     }
11200 
11201     // It is at the back.
11202     if(node->free.next == VMA_NULL)
11203     {
11204         VMA_ASSERT(m_FreeList[level].back == node);
11205         m_FreeList[level].back = node->free.prev;
11206     }
11207     else
11208     {
11209         Node* const nextFreeNode = node->free.next;
11210         VMA_ASSERT(nextFreeNode->free.prev == node);
11211         nextFreeNode->free.prev = node->free.prev;
11212     }
11213 }
11214 
11215 #if VMA_STATS_STRING_ENABLED
PrintDetailedMapNode(class VmaJsonWriter & json,const Node * node,VkDeviceSize levelNodeSize)11216 void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
11217 {
11218     switch(node->type)
11219     {
11220     case Node::TYPE_FREE:
11221         PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
11222         break;
11223     case Node::TYPE_ALLOCATION:
11224         PrintDetailedMap_Allocation(json, node->offset, levelNodeSize, node->allocation.userData);
11225         break;
11226     case Node::TYPE_SPLIT:
11227         {
11228             const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
11229             const Node* const leftChild = node->split.leftChild;
11230             PrintDetailedMapNode(json, leftChild, childrenNodeSize);
11231             const Node* const rightChild = leftChild->buddy;
11232             PrintDetailedMapNode(json, rightChild, childrenNodeSize);
11233         }
11234         break;
11235     default:
11236         VMA_ASSERT(0);
11237     }
11238 }
11239 #endif // #if VMA_STATS_STRING_ENABLED
11240 
11241 
11242 ////////////////////////////////////////////////////////////////////////////////
11243 // class VmaDeviceMemoryBlock
11244 
VmaDeviceMemoryBlock(VmaAllocator hAllocator)11245 VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) :
11246     m_pMetadata(VMA_NULL),
11247     m_MemoryTypeIndex(UINT32_MAX),
11248     m_Id(0),
11249     m_hMemory(VK_NULL_HANDLE),
11250     m_MapCount(0),
11251     m_pMappedData(VMA_NULL)
11252 {
11253 }
11254 
Init(VmaAllocator hAllocator,VmaPool hParentPool,uint32_t newMemoryTypeIndex,VkDeviceMemory newMemory,VkDeviceSize newSize,uint32_t id,uint32_t algorithm)11255 void VmaDeviceMemoryBlock::Init(
11256     VmaAllocator hAllocator,
11257     VmaPool hParentPool,
11258     uint32_t newMemoryTypeIndex,
11259     VkDeviceMemory newMemory,
11260     VkDeviceSize newSize,
11261     uint32_t id,
11262     uint32_t algorithm)
11263 {
11264     VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
11265 
11266     m_hParentPool = hParentPool;
11267     m_MemoryTypeIndex = newMemoryTypeIndex;
11268     m_Id = id;
11269     m_hMemory = newMemory;
11270 
11271     switch(algorithm)
11272     {
11273     case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
11274         m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator->GetAllocationCallbacks(),
11275             false); // isVirtual
11276         break;
11277     case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
11278         m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator->GetAllocationCallbacks(),
11279             false); // isVirtual
11280         break;
11281     default:
11282         VMA_ASSERT(0);
11283     case 0:
11284         m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator->GetAllocationCallbacks(),
11285             false); // isVirtual
11286     }
11287     m_pMetadata->Init(newSize);
11288 }
11289 
Destroy(VmaAllocator allocator)11290 void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
11291 {
11292     // This is the most important assert in the entire library.
11293     // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
11294     VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
11295 
11296     VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
11297     allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
11298     m_hMemory = VK_NULL_HANDLE;
11299 
11300     vma_delete(allocator, m_pMetadata);
11301     m_pMetadata = VMA_NULL;
11302 }
11303 
Validate()11304 bool VmaDeviceMemoryBlock::Validate() const
11305 {
11306     VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
11307         (m_pMetadata->GetSize() != 0));
11308 
11309     return m_pMetadata->Validate();
11310 }
11311 
CheckCorruption(VmaAllocator hAllocator)11312 VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
11313 {
11314     void* pData = nullptr;
11315     VkResult res = Map(hAllocator, 1, &pData);
11316     if(res != VK_SUCCESS)
11317     {
11318         return res;
11319     }
11320 
11321     res = m_pMetadata->CheckCorruption(pData);
11322 
11323     Unmap(hAllocator, 1);
11324 
11325     return res;
11326 }
11327 
Map(VmaAllocator hAllocator,uint32_t count,void ** ppData)11328 VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
11329 {
11330     if(count == 0)
11331     {
11332         return VK_SUCCESS;
11333     }
11334 
11335     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11336     if(m_MapCount != 0)
11337     {
11338         m_MapCount += count;
11339         VMA_ASSERT(m_pMappedData != VMA_NULL);
11340         if(ppData != VMA_NULL)
11341         {
11342             *ppData = m_pMappedData;
11343         }
11344         return VK_SUCCESS;
11345     }
11346     else
11347     {
11348         VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
11349             hAllocator->m_hDevice,
11350             m_hMemory,
11351             0, // offset
11352             VK_WHOLE_SIZE,
11353             0, // flags
11354             &m_pMappedData);
11355         if(result == VK_SUCCESS)
11356         {
11357             if(ppData != VMA_NULL)
11358             {
11359                 *ppData = m_pMappedData;
11360             }
11361             m_MapCount = count;
11362         }
11363         return result;
11364     }
11365 }
11366 
Unmap(VmaAllocator hAllocator,uint32_t count)11367 void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
11368 {
11369     if(count == 0)
11370     {
11371         return;
11372     }
11373 
11374     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11375     if(m_MapCount >= count)
11376     {
11377         m_MapCount -= count;
11378         if(m_MapCount == 0)
11379         {
11380             m_pMappedData = VMA_NULL;
11381             (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
11382         }
11383     }
11384     else
11385     {
11386         VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
11387     }
11388 }
11389 
WriteMagicValueAroundAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)11390 VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
11391 {
11392     VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
11393     VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
11394 
11395     void* pData;
11396     VkResult res = Map(hAllocator, 1, &pData);
11397     if(res != VK_SUCCESS)
11398     {
11399         return res;
11400     }
11401 
11402     VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN);
11403     VmaWriteMagicValue(pData, allocOffset + allocSize);
11404 
11405     Unmap(hAllocator, 1);
11406 
11407     return VK_SUCCESS;
11408 }
11409 
ValidateMagicValueAroundAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)11410 VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
11411 {
11412     VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
11413     VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
11414 
11415     void* pData;
11416     VkResult res = Map(hAllocator, 1, &pData);
11417     if(res != VK_SUCCESS)
11418     {
11419         return res;
11420     }
11421 
11422     if(!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN))
11423     {
11424         VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
11425     }
11426     else if(!VmaValidateMagicValue(pData, allocOffset + allocSize))
11427     {
11428         VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
11429     }
11430 
11431     Unmap(hAllocator, 1);
11432 
11433     return VK_SUCCESS;
11434 }
11435 
BindBufferMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkBuffer hBuffer,const void * pNext)11436 VkResult VmaDeviceMemoryBlock::BindBufferMemory(
11437     const VmaAllocator hAllocator,
11438     const VmaAllocation hAllocation,
11439     VkDeviceSize allocationLocalOffset,
11440     VkBuffer hBuffer,
11441     const void* pNext)
11442 {
11443     VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
11444         hAllocation->GetBlock() == this);
11445     VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
11446         "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
11447     const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
11448     // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
11449     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11450     return hAllocator->BindVulkanBuffer(m_hMemory, memoryOffset, hBuffer, pNext);
11451 }
11452 
BindImageMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkImage hImage,const void * pNext)11453 VkResult VmaDeviceMemoryBlock::BindImageMemory(
11454     const VmaAllocator hAllocator,
11455     const VmaAllocation hAllocation,
11456     VkDeviceSize allocationLocalOffset,
11457     VkImage hImage,
11458     const void* pNext)
11459 {
11460     VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
11461         hAllocation->GetBlock() == this);
11462     VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
11463         "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
11464     const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
11465     // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
11466     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11467     return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext);
11468 }
11469 
VmaPool_T(VmaAllocator hAllocator,const VmaPoolCreateInfo & createInfo,VkDeviceSize preferredBlockSize)11470 VmaPool_T::VmaPool_T(
11471     VmaAllocator hAllocator,
11472     const VmaPoolCreateInfo& createInfo,
11473     VkDeviceSize preferredBlockSize) :
11474     m_BlockVector(
11475         hAllocator,
11476         this, // hParentPool
11477         createInfo.memoryTypeIndex,
11478         createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
11479         createInfo.minBlockCount,
11480         createInfo.maxBlockCount,
11481         (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
11482         createInfo.frameInUseCount,
11483         createInfo.blockSize != 0, // explicitBlockSize
11484         createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK, // algorithm
11485         createInfo.priority,
11486         VMA_MAX(hAllocator->GetMemoryTypeMinAlignment(createInfo.memoryTypeIndex), createInfo.minAllocationAlignment),
11487         createInfo.pMemoryAllocateNext),
11488     m_Id(0),
11489     m_Name(VMA_NULL)
11490 {
11491 }
11492 
~VmaPool_T()11493 VmaPool_T::~VmaPool_T()
11494 {
11495     VMA_ASSERT(m_PrevPool == VMA_NULL && m_NextPool == VMA_NULL);
11496 }
11497 
SetName(const char * pName)11498 void VmaPool_T::SetName(const char* pName)
11499 {
11500     const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks();
11501     VmaFreeString(allocs, m_Name);
11502 
11503     if(pName != VMA_NULL)
11504     {
11505         m_Name = VmaCreateStringCopy(allocs, pName);
11506     }
11507     else
11508     {
11509         m_Name = VMA_NULL;
11510     }
11511 }
11512 
11513 #if VMA_STATS_STRING_ENABLED
11514 
11515 #endif // #if VMA_STATS_STRING_ENABLED
11516 
VmaBlockVector(VmaAllocator hAllocator,VmaPool hParentPool,uint32_t memoryTypeIndex,VkDeviceSize preferredBlockSize,size_t minBlockCount,size_t maxBlockCount,VkDeviceSize bufferImageGranularity,uint32_t frameInUseCount,bool explicitBlockSize,uint32_t algorithm,float priority,VkDeviceSize minAllocationAlignment,void * pMemoryAllocateNext)11517 VmaBlockVector::VmaBlockVector(
11518     VmaAllocator hAllocator,
11519     VmaPool hParentPool,
11520     uint32_t memoryTypeIndex,
11521     VkDeviceSize preferredBlockSize,
11522     size_t minBlockCount,
11523     size_t maxBlockCount,
11524     VkDeviceSize bufferImageGranularity,
11525     uint32_t frameInUseCount,
11526     bool explicitBlockSize,
11527     uint32_t algorithm,
11528     float priority,
11529     VkDeviceSize minAllocationAlignment,
11530     void* pMemoryAllocateNext) :
11531     m_hAllocator(hAllocator),
11532     m_hParentPool(hParentPool),
11533     m_MemoryTypeIndex(memoryTypeIndex),
11534     m_PreferredBlockSize(preferredBlockSize),
11535     m_MinBlockCount(minBlockCount),
11536     m_MaxBlockCount(maxBlockCount),
11537     m_BufferImageGranularity(bufferImageGranularity),
11538     m_FrameInUseCount(frameInUseCount),
11539     m_ExplicitBlockSize(explicitBlockSize),
11540     m_Algorithm(algorithm),
11541     m_Priority(priority),
11542     m_MinAllocationAlignment(minAllocationAlignment),
11543     m_pMemoryAllocateNext(pMemoryAllocateNext),
11544     m_HasEmptyBlock(false),
11545     m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
11546     m_NextBlockId(0)
11547 {
11548 }
11549 
~VmaBlockVector()11550 VmaBlockVector::~VmaBlockVector()
11551 {
11552     for(size_t i = m_Blocks.size(); i--; )
11553     {
11554         m_Blocks[i]->Destroy(m_hAllocator);
11555         vma_delete(m_hAllocator, m_Blocks[i]);
11556     }
11557 }
11558 
CreateMinBlocks()11559 VkResult VmaBlockVector::CreateMinBlocks()
11560 {
11561     for(size_t i = 0; i < m_MinBlockCount; ++i)
11562     {
11563         VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
11564         if(res != VK_SUCCESS)
11565         {
11566             return res;
11567         }
11568     }
11569     return VK_SUCCESS;
11570 }
11571 
GetPoolStats(VmaPoolStats * pStats)11572 void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
11573 {
11574     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
11575 
11576     const size_t blockCount = m_Blocks.size();
11577 
11578     pStats->size = 0;
11579     pStats->unusedSize = 0;
11580     pStats->allocationCount = 0;
11581     pStats->unusedRangeCount = 0;
11582     pStats->unusedRangeSizeMax = 0;
11583     pStats->blockCount = blockCount;
11584 
11585     for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
11586     {
11587         const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
11588         VMA_ASSERT(pBlock);
11589         VMA_HEAVY_ASSERT(pBlock->Validate());
11590         pBlock->m_pMetadata->AddPoolStats(*pStats);
11591     }
11592 }
11593 
IsEmpty()11594 bool VmaBlockVector::IsEmpty()
11595 {
11596     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
11597     return m_Blocks.empty();
11598 }
11599 
IsCorruptionDetectionEnabled()11600 bool VmaBlockVector::IsCorruptionDetectionEnabled() const
11601 {
11602     const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
11603     return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
11604         (VMA_DEBUG_MARGIN > 0) &&
11605         (m_Algorithm == 0 || m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) &&
11606         (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
11607 }
11608 
11609 static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
11610 
Allocate(uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)11611 VkResult VmaBlockVector::Allocate(
11612     uint32_t currentFrameIndex,
11613     VkDeviceSize size,
11614     VkDeviceSize alignment,
11615     const VmaAllocationCreateInfo& createInfo,
11616     VmaSuballocationType suballocType,
11617     size_t allocationCount,
11618     VmaAllocation* pAllocations)
11619 {
11620     size_t allocIndex;
11621     VkResult res = VK_SUCCESS;
11622 
11623     alignment = VMA_MAX(alignment, m_MinAllocationAlignment);
11624 
11625     if(IsCorruptionDetectionEnabled())
11626     {
11627         size = VmaAlignUp<VkDeviceSize>(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
11628         alignment = VmaAlignUp<VkDeviceSize>(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
11629     }
11630 
11631     {
11632         VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
11633         for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
11634         {
11635             res = AllocatePage(
11636                 currentFrameIndex,
11637                 size,
11638                 alignment,
11639                 createInfo,
11640                 suballocType,
11641                 pAllocations + allocIndex);
11642             if(res != VK_SUCCESS)
11643             {
11644                 break;
11645             }
11646         }
11647     }
11648 
11649     if(res != VK_SUCCESS)
11650     {
11651         // Free all already created allocations.
11652         const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
11653         while(allocIndex--)
11654         {
11655             VmaAllocation_T* const alloc = pAllocations[allocIndex];
11656             const VkDeviceSize allocSize = alloc->GetSize();
11657             Free(alloc);
11658             m_hAllocator->m_Budget.RemoveAllocation(heapIndex, allocSize);
11659         }
11660         memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
11661     }
11662 
11663     return res;
11664 }
11665 
AllocatePage(uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)11666 VkResult VmaBlockVector::AllocatePage(
11667     uint32_t currentFrameIndex,
11668     VkDeviceSize size,
11669     VkDeviceSize alignment,
11670     const VmaAllocationCreateInfo& createInfo,
11671     VmaSuballocationType suballocType,
11672     VmaAllocation* pAllocation)
11673 {
11674     const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
11675     bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
11676     const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
11677     const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
11678 
11679     VkDeviceSize freeMemory;
11680     {
11681         const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
11682         VmaBudget heapBudget = {};
11683         m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
11684         freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0;
11685     }
11686 
11687     const bool canFallbackToDedicated = !IsCustomPool();
11688     const bool canCreateNewBlock =
11689         ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
11690         (m_Blocks.size() < m_MaxBlockCount) &&
11691         (freeMemory >= size || !canFallbackToDedicated);
11692     uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
11693 
11694     // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
11695     // Which in turn is available only when maxBlockCount = 1.
11696     if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
11697     {
11698         canMakeOtherLost = false;
11699     }
11700 
11701     // Upper address can only be used with linear allocator and within single memory block.
11702     if(isUpperAddress &&
11703         (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
11704     {
11705         return VK_ERROR_FEATURE_NOT_PRESENT;
11706     }
11707 
11708     // Validate strategy.
11709     switch(strategy)
11710     {
11711     case 0:
11712         strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
11713         break;
11714     case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
11715     case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
11716     case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
11717         break;
11718     default:
11719         return VK_ERROR_FEATURE_NOT_PRESENT;
11720     }
11721 
11722     // Early reject: requested allocation size is larger that maximum block size for this block vector.
11723     if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
11724     {
11725         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11726     }
11727 
11728     /*
11729     Under certain condition, this whole section can be skipped for optimization, so
11730     we move on directly to trying to allocate with canMakeOtherLost. That is the case
11731     e.g. for custom pools with linear algorithm.
11732     */
11733     if(!canMakeOtherLost || canCreateNewBlock)
11734     {
11735         // 1. Search existing allocations. Try to allocate without making other allocations lost.
11736         VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
11737         allocFlagsCopy &= ~VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
11738 
11739         if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
11740         {
11741             // Use only last block.
11742             if(!m_Blocks.empty())
11743             {
11744                 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
11745                 VMA_ASSERT(pCurrBlock);
11746                 VkResult res = AllocateFromBlock(
11747                     pCurrBlock,
11748                     currentFrameIndex,
11749                     size,
11750                     alignment,
11751                     allocFlagsCopy,
11752                     createInfo.pUserData,
11753                     suballocType,
11754                     strategy,
11755                     pAllocation);
11756                 if(res == VK_SUCCESS)
11757                 {
11758                     VMA_DEBUG_LOG("    Returned from last block #%u", pCurrBlock->GetId());
11759                     return VK_SUCCESS;
11760                 }
11761             }
11762         }
11763         else
11764         {
11765             if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
11766             {
11767                 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
11768                 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
11769                 {
11770                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11771                     VMA_ASSERT(pCurrBlock);
11772                     VkResult res = AllocateFromBlock(
11773                         pCurrBlock,
11774                         currentFrameIndex,
11775                         size,
11776                         alignment,
11777                         allocFlagsCopy,
11778                         createInfo.pUserData,
11779                         suballocType,
11780                         strategy,
11781                         pAllocation);
11782                     if(res == VK_SUCCESS)
11783                     {
11784                         VMA_DEBUG_LOG("    Returned from existing block #%u", pCurrBlock->GetId());
11785                         return VK_SUCCESS;
11786                     }
11787                 }
11788             }
11789             else // WORST_FIT, FIRST_FIT
11790             {
11791                 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
11792                 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
11793                 {
11794                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11795                     VMA_ASSERT(pCurrBlock);
11796                     VkResult res = AllocateFromBlock(
11797                         pCurrBlock,
11798                         currentFrameIndex,
11799                         size,
11800                         alignment,
11801                         allocFlagsCopy,
11802                         createInfo.pUserData,
11803                         suballocType,
11804                         strategy,
11805                         pAllocation);
11806                     if(res == VK_SUCCESS)
11807                     {
11808                         VMA_DEBUG_LOG("    Returned from existing block #%u", pCurrBlock->GetId());
11809                         return VK_SUCCESS;
11810                     }
11811                 }
11812             }
11813         }
11814 
11815         // 2. Try to create new block.
11816         if(canCreateNewBlock)
11817         {
11818             // Calculate optimal size for new block.
11819             VkDeviceSize newBlockSize = m_PreferredBlockSize;
11820             uint32_t newBlockSizeShift = 0;
11821             const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
11822 
11823             if(!m_ExplicitBlockSize)
11824             {
11825                 // Allocate 1/8, 1/4, 1/2 as first blocks.
11826                 const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
11827                 for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
11828                 {
11829                     const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
11830                     if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
11831                     {
11832                         newBlockSize = smallerNewBlockSize;
11833                         ++newBlockSizeShift;
11834                     }
11835                     else
11836                     {
11837                         break;
11838                     }
11839                 }
11840             }
11841 
11842             size_t newBlockIndex = 0;
11843             VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
11844                 CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
11845             // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
11846             if(!m_ExplicitBlockSize)
11847             {
11848                 while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
11849                 {
11850                     const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
11851                     if(smallerNewBlockSize >= size)
11852                     {
11853                         newBlockSize = smallerNewBlockSize;
11854                         ++newBlockSizeShift;
11855                         res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
11856                             CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
11857                     }
11858                     else
11859                     {
11860                         break;
11861                     }
11862                 }
11863             }
11864 
11865             if(res == VK_SUCCESS)
11866             {
11867                 VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
11868                 VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
11869 
11870                 res = AllocateFromBlock(
11871                     pBlock,
11872                     currentFrameIndex,
11873                     size,
11874                     alignment,
11875                     allocFlagsCopy,
11876                     createInfo.pUserData,
11877                     suballocType,
11878                     strategy,
11879                     pAllocation);
11880                 if(res == VK_SUCCESS)
11881                 {
11882                     VMA_DEBUG_LOG("    Created new block #%u Size=%llu", pBlock->GetId(), newBlockSize);
11883                     return VK_SUCCESS;
11884                 }
11885                 else
11886                 {
11887                     // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
11888                     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11889                 }
11890             }
11891         }
11892     }
11893 
11894     // 3. Try to allocate from existing blocks with making other allocations lost.
11895     if(canMakeOtherLost)
11896     {
11897         uint32_t tryIndex = 0;
11898         for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
11899         {
11900             VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
11901             VmaAllocationRequest bestRequest = {};
11902             VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
11903 
11904             // 1. Search existing allocations.
11905             if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
11906             {
11907                 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
11908                 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
11909                 {
11910                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11911                     VMA_ASSERT(pCurrBlock);
11912                     VmaAllocationRequest currRequest = {};
11913                     if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
11914                         currentFrameIndex,
11915                         m_FrameInUseCount,
11916                         m_BufferImageGranularity,
11917                         size,
11918                         alignment,
11919                         (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
11920                         suballocType,
11921                         canMakeOtherLost,
11922                         strategy,
11923                         &currRequest))
11924                     {
11925                         const VkDeviceSize currRequestCost = currRequest.CalcCost();
11926                         if(pBestRequestBlock == VMA_NULL ||
11927                             currRequestCost < bestRequestCost)
11928                         {
11929                             pBestRequestBlock = pCurrBlock;
11930                             bestRequest = currRequest;
11931                             bestRequestCost = currRequestCost;
11932 
11933                             if(bestRequestCost == 0)
11934                             {
11935                                 break;
11936                             }
11937                         }
11938                     }
11939                 }
11940             }
11941             else // WORST_FIT, FIRST_FIT
11942             {
11943                 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
11944                 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
11945                 {
11946                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11947                     VMA_ASSERT(pCurrBlock);
11948                     VmaAllocationRequest currRequest = {};
11949                     if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
11950                         currentFrameIndex,
11951                         m_FrameInUseCount,
11952                         m_BufferImageGranularity,
11953                         size,
11954                         alignment,
11955                         (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
11956                         suballocType,
11957                         canMakeOtherLost,
11958                         strategy,
11959                         &currRequest))
11960                     {
11961                         const VkDeviceSize currRequestCost = currRequest.CalcCost();
11962                         if(pBestRequestBlock == VMA_NULL ||
11963                             currRequestCost < bestRequestCost ||
11964                             strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
11965                         {
11966                             pBestRequestBlock = pCurrBlock;
11967                             bestRequest = currRequest;
11968                             bestRequestCost = currRequestCost;
11969 
11970                             if(bestRequestCost == 0 ||
11971                                 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
11972                             {
11973                                 break;
11974                             }
11975                         }
11976                     }
11977                 }
11978             }
11979 
11980             if(pBestRequestBlock != VMA_NULL)
11981             {
11982                 if(mapped)
11983                 {
11984                     VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
11985                     if(res != VK_SUCCESS)
11986                     {
11987                         return res;
11988                     }
11989                 }
11990 
11991                 if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
11992                     currentFrameIndex,
11993                     m_FrameInUseCount,
11994                     &bestRequest))
11995                 {
11996                     // Allocate from this pBlock.
11997                     *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
11998                     pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, *pAllocation);
11999                     UpdateHasEmptyBlock();
12000                     (*pAllocation)->InitBlockAllocation(
12001                         pBestRequestBlock,
12002                         bestRequest.offset,
12003                         alignment,
12004                         bestRequest.size, // Not size, as actual allocation size may be larger than requested!
12005                         m_MemoryTypeIndex,
12006                         suballocType,
12007                         mapped,
12008                         (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
12009                     VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
12010                     VMA_DEBUG_LOG("    Returned from existing block #%u", pBestRequestBlock->GetId());
12011                     (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
12012                     m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), bestRequest.size);
12013                     if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
12014                     {
12015                         m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
12016                     }
12017                     if(IsCorruptionDetectionEnabled())
12018                     {
12019                         VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, bestRequest.size);
12020                         VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
12021                     }
12022                     return VK_SUCCESS;
12023                 }
12024                 // else: Some allocations must have been touched while we are here. Next try.
12025             }
12026             else
12027             {
12028                 // Could not find place in any of the blocks - break outer loop.
12029                 break;
12030             }
12031         }
12032         /* Maximum number of tries exceeded - a very unlike event when many other
12033         threads are simultaneously touching allocations making it impossible to make
12034         lost at the same time as we try to allocate. */
12035         if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
12036         {
12037             return VK_ERROR_TOO_MANY_OBJECTS;
12038         }
12039     }
12040 
12041     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12042 }
12043 
Free(const VmaAllocation hAllocation)12044 void VmaBlockVector::Free(
12045     const VmaAllocation hAllocation)
12046 {
12047     VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
12048 
12049     bool budgetExceeded = false;
12050     {
12051         const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
12052         VmaBudget heapBudget = {};
12053         m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
12054         budgetExceeded = heapBudget.usage >= heapBudget.budget;
12055     }
12056 
12057     // Scope for lock.
12058     {
12059         VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12060 
12061         VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
12062 
12063         if(IsCorruptionDetectionEnabled())
12064         {
12065             VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
12066             VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
12067         }
12068 
12069         if(hAllocation->IsPersistentMap())
12070         {
12071             pBlock->Unmap(m_hAllocator, 1);
12072         }
12073 
12074         pBlock->m_pMetadata->FreeAtOffset(hAllocation->GetOffset());
12075         VMA_HEAVY_ASSERT(pBlock->Validate());
12076 
12077         VMA_DEBUG_LOG("  Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
12078 
12079         const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount;
12080         // pBlock became empty after this deallocation.
12081         if(pBlock->m_pMetadata->IsEmpty())
12082         {
12083             // Already has empty block. We don't want to have two, so delete this one.
12084             if((m_HasEmptyBlock || budgetExceeded) && canDeleteBlock)
12085             {
12086                 pBlockToDelete = pBlock;
12087                 Remove(pBlock);
12088             }
12089             // else: We now have an empty block - leave it.
12090         }
12091         // pBlock didn't become empty, but we have another empty block - find and free that one.
12092         // (This is optional, heuristics.)
12093         else if(m_HasEmptyBlock && canDeleteBlock)
12094         {
12095             VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
12096             if(pLastBlock->m_pMetadata->IsEmpty())
12097             {
12098                 pBlockToDelete = pLastBlock;
12099                 m_Blocks.pop_back();
12100             }
12101         }
12102 
12103         UpdateHasEmptyBlock();
12104         IncrementallySortBlocks();
12105     }
12106 
12107     // Destruction of a free block. Deferred until this point, outside of mutex
12108     // lock, for performance reason.
12109     if(pBlockToDelete != VMA_NULL)
12110     {
12111         VMA_DEBUG_LOG("    Deleted empty block #%u", pBlockToDelete->GetId());
12112         pBlockToDelete->Destroy(m_hAllocator);
12113         vma_delete(m_hAllocator, pBlockToDelete);
12114     }
12115 }
12116 
CalcMaxBlockSize()12117 VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
12118 {
12119     VkDeviceSize result = 0;
12120     for(size_t i = m_Blocks.size(); i--; )
12121     {
12122         result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
12123         if(result >= m_PreferredBlockSize)
12124         {
12125             break;
12126         }
12127     }
12128     return result;
12129 }
12130 
Remove(VmaDeviceMemoryBlock * pBlock)12131 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
12132 {
12133     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12134     {
12135         if(m_Blocks[blockIndex] == pBlock)
12136         {
12137             VmaVectorRemove(m_Blocks, blockIndex);
12138             return;
12139         }
12140     }
12141     VMA_ASSERT(0);
12142 }
12143 
IncrementallySortBlocks()12144 void VmaBlockVector::IncrementallySortBlocks()
12145 {
12146     if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
12147     {
12148         // Bubble sort only until first swap.
12149         for(size_t i = 1; i < m_Blocks.size(); ++i)
12150         {
12151             if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
12152             {
12153                 VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
12154                 return;
12155             }
12156         }
12157     }
12158 }
12159 
AllocateFromBlock(VmaDeviceMemoryBlock * pBlock,uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,VmaAllocationCreateFlags allocFlags,void * pUserData,VmaSuballocationType suballocType,uint32_t strategy,VmaAllocation * pAllocation)12160 VkResult VmaBlockVector::AllocateFromBlock(
12161     VmaDeviceMemoryBlock* pBlock,
12162     uint32_t currentFrameIndex,
12163     VkDeviceSize size,
12164     VkDeviceSize alignment,
12165     VmaAllocationCreateFlags allocFlags,
12166     void* pUserData,
12167     VmaSuballocationType suballocType,
12168     uint32_t strategy,
12169     VmaAllocation* pAllocation)
12170 {
12171     VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
12172     const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
12173     const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
12174     const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
12175 
12176     VmaAllocationRequest currRequest = {};
12177     if(pBlock->m_pMetadata->CreateAllocationRequest(
12178         currentFrameIndex,
12179         m_FrameInUseCount,
12180         m_BufferImageGranularity,
12181         size,
12182         alignment,
12183         isUpperAddress,
12184         suballocType,
12185         false, // canMakeOtherLost
12186         strategy,
12187         &currRequest))
12188     {
12189         // Allocate from pCurrBlock.
12190         VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
12191 
12192         if(mapped)
12193         {
12194             VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
12195             if(res != VK_SUCCESS)
12196             {
12197                 return res;
12198             }
12199         }
12200 
12201         *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
12202         pBlock->m_pMetadata->Alloc(currRequest, suballocType, *pAllocation);
12203         UpdateHasEmptyBlock();
12204         (*pAllocation)->InitBlockAllocation(
12205             pBlock,
12206             currRequest.offset,
12207             alignment,
12208             currRequest.size, // Not size, as actual allocation size may be larger than requested!
12209             m_MemoryTypeIndex,
12210             suballocType,
12211             mapped,
12212             (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
12213         VMA_HEAVY_ASSERT(pBlock->Validate());
12214         (*pAllocation)->SetUserData(m_hAllocator, pUserData);
12215         m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), currRequest.size);
12216         if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
12217         {
12218             m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
12219         }
12220         if(IsCorruptionDetectionEnabled())
12221         {
12222             VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, currRequest.size);
12223             VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
12224         }
12225         return VK_SUCCESS;
12226     }
12227     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12228 }
12229 
CreateBlock(VkDeviceSize blockSize,size_t * pNewBlockIndex)12230 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
12231 {
12232     VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
12233     allocInfo.pNext = m_pMemoryAllocateNext;
12234     allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
12235     allocInfo.allocationSize = blockSize;
12236 
12237 #if VMA_BUFFER_DEVICE_ADDRESS
12238     // Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature.
12239     VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
12240     if(m_hAllocator->m_UseKhrBufferDeviceAddress)
12241     {
12242         allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
12243         VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
12244     }
12245 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
12246 
12247 #if VMA_MEMORY_PRIORITY
12248     VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };
12249     if(m_hAllocator->m_UseExtMemoryPriority)
12250     {
12251         priorityInfo.priority = m_Priority;
12252         VmaPnextChainPushFront(&allocInfo, &priorityInfo);
12253     }
12254 #endif // #if VMA_MEMORY_PRIORITY
12255 
12256 #if VMA_EXTERNAL_MEMORY
12257     // Attach VkExportMemoryAllocateInfoKHR if necessary.
12258     VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR };
12259     exportMemoryAllocInfo.handleTypes = m_hAllocator->GetExternalMemoryHandleTypeFlags(m_MemoryTypeIndex);
12260     if(exportMemoryAllocInfo.handleTypes != 0)
12261     {
12262         VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo);
12263     }
12264 #endif // #if VMA_EXTERNAL_MEMORY
12265 
12266     VkDeviceMemory mem = VK_NULL_HANDLE;
12267     VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
12268     if(res < 0)
12269     {
12270         return res;
12271     }
12272 
12273     // New VkDeviceMemory successfully created.
12274 
12275     // Create new Allocation for it.
12276     VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
12277     pBlock->Init(
12278         m_hAllocator,
12279         m_hParentPool,
12280         m_MemoryTypeIndex,
12281         mem,
12282         allocInfo.allocationSize,
12283         m_NextBlockId++,
12284         m_Algorithm);
12285 
12286     m_Blocks.push_back(pBlock);
12287     if(pNewBlockIndex != VMA_NULL)
12288     {
12289         *pNewBlockIndex = m_Blocks.size() - 1;
12290     }
12291 
12292     return VK_SUCCESS;
12293 }
12294 
ApplyDefragmentationMovesCpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,const VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves)12295 void VmaBlockVector::ApplyDefragmentationMovesCpu(
12296     class VmaBlockVectorDefragmentationContext* pDefragCtx,
12297     const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
12298 {
12299     const size_t blockCount = m_Blocks.size();
12300     const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
12301 
12302     enum BLOCK_FLAG
12303     {
12304         BLOCK_FLAG_USED = 0x00000001,
12305         BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
12306     };
12307 
12308     struct BlockInfo
12309     {
12310         uint32_t flags;
12311         void* pMappedData;
12312     };
12313     VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
12314         blockInfo(blockCount, BlockInfo(), VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
12315     memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
12316 
12317     // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
12318     const size_t moveCount = moves.size();
12319     for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12320     {
12321         const VmaDefragmentationMove& move = moves[moveIndex];
12322         blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
12323         blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
12324     }
12325 
12326     VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
12327 
12328     // Go over all blocks. Get mapped pointer or map if necessary.
12329     for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
12330     {
12331         BlockInfo& currBlockInfo = blockInfo[blockIndex];
12332         VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12333         if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
12334         {
12335             currBlockInfo.pMappedData = pBlock->GetMappedData();
12336             // It is not originally mapped - map it.
12337             if(currBlockInfo.pMappedData == VMA_NULL)
12338             {
12339                 pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
12340                 if(pDefragCtx->res == VK_SUCCESS)
12341                 {
12342                     currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
12343                 }
12344             }
12345         }
12346     }
12347 
12348     // Go over all moves. Do actual data transfer.
12349     if(pDefragCtx->res == VK_SUCCESS)
12350     {
12351         const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
12352         VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
12353 
12354         for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12355         {
12356             const VmaDefragmentationMove& move = moves[moveIndex];
12357 
12358             const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
12359             const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
12360 
12361             VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
12362 
12363             // Invalidate source.
12364             if(isNonCoherent)
12365             {
12366                 VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
12367                 memRange.memory = pSrcBlock->GetDeviceMemory();
12368                 memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
12369                 memRange.size = VMA_MIN(
12370                     VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
12371                     pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
12372                 (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
12373             }
12374 
12375             // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
12376             memmove(
12377                 reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
12378                 reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
12379                 static_cast<size_t>(move.size));
12380 
12381             if(IsCorruptionDetectionEnabled())
12382             {
12383                 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
12384                 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
12385             }
12386 
12387             // Flush destination.
12388             if(isNonCoherent)
12389             {
12390                 VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
12391                 memRange.memory = pDstBlock->GetDeviceMemory();
12392                 memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
12393                 memRange.size = VMA_MIN(
12394                     VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
12395                     pDstBlock->m_pMetadata->GetSize() - memRange.offset);
12396                 (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
12397             }
12398         }
12399     }
12400 
12401     // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
12402     // Regardless of pCtx->res == VK_SUCCESS.
12403     for(size_t blockIndex = blockCount; blockIndex--; )
12404     {
12405         const BlockInfo& currBlockInfo = blockInfo[blockIndex];
12406         if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
12407         {
12408             VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12409             pBlock->Unmap(m_hAllocator, 1);
12410         }
12411     }
12412 }
12413 
ApplyDefragmentationMovesGpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkCommandBuffer commandBuffer)12414 void VmaBlockVector::ApplyDefragmentationMovesGpu(
12415     class VmaBlockVectorDefragmentationContext* pDefragCtx,
12416     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12417     VkCommandBuffer commandBuffer)
12418 {
12419     const size_t blockCount = m_Blocks.size();
12420 
12421     pDefragCtx->blockContexts.resize(blockCount);
12422     memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext));
12423 
12424     // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
12425     const size_t moveCount = moves.size();
12426     for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12427     {
12428         const VmaDefragmentationMove& move = moves[moveIndex];
12429 
12430         //if(move.type == VMA_ALLOCATION_TYPE_UNKNOWN)
12431         {
12432             // Old school move still require us to map the whole block
12433             pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
12434             pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
12435         }
12436     }
12437 
12438     VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
12439 
12440     // Go over all blocks. Create and bind buffer for whole block if necessary.
12441     {
12442         VkBufferCreateInfo bufCreateInfo;
12443         VmaFillGpuDefragmentationBufferCreateInfo(bufCreateInfo);
12444 
12445         for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
12446         {
12447             VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
12448             VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12449             if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
12450             {
12451                 bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
12452                 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
12453                     m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
12454                 if(pDefragCtx->res == VK_SUCCESS)
12455                 {
12456                     pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
12457                         m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
12458                 }
12459             }
12460         }
12461     }
12462 
12463     // Go over all moves. Post data transfer commands to command buffer.
12464     if(pDefragCtx->res == VK_SUCCESS)
12465     {
12466         for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12467         {
12468             const VmaDefragmentationMove& move = moves[moveIndex];
12469 
12470             const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
12471             const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
12472 
12473             VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
12474 
12475             VkBufferCopy region = {
12476                 move.srcOffset,
12477                 move.dstOffset,
12478                 move.size };
12479             (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
12480                 commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, &region);
12481         }
12482     }
12483 
12484     // Save buffers to defrag context for later destruction.
12485     if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
12486     {
12487         pDefragCtx->res = VK_NOT_READY;
12488     }
12489 }
12490 
FreeEmptyBlocks(VmaDefragmentationStats * pDefragmentationStats)12491 void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
12492 {
12493     for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12494     {
12495         VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12496         if(pBlock->m_pMetadata->IsEmpty())
12497         {
12498             if(m_Blocks.size() > m_MinBlockCount)
12499             {
12500                 if(pDefragmentationStats != VMA_NULL)
12501                 {
12502                     ++pDefragmentationStats->deviceMemoryBlocksFreed;
12503                     pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
12504                 }
12505 
12506                 VmaVectorRemove(m_Blocks, blockIndex);
12507                 pBlock->Destroy(m_hAllocator);
12508                 vma_delete(m_hAllocator, pBlock);
12509             }
12510             else
12511             {
12512                 break;
12513             }
12514         }
12515     }
12516     UpdateHasEmptyBlock();
12517 }
12518 
UpdateHasEmptyBlock()12519 void VmaBlockVector::UpdateHasEmptyBlock()
12520 {
12521     m_HasEmptyBlock = false;
12522     for(size_t index = 0, count = m_Blocks.size(); index < count; ++index)
12523     {
12524         VmaDeviceMemoryBlock* const pBlock = m_Blocks[index];
12525         if(pBlock->m_pMetadata->IsEmpty())
12526         {
12527             m_HasEmptyBlock = true;
12528             break;
12529         }
12530     }
12531 }
12532 
12533 #if VMA_STATS_STRING_ENABLED
12534 
PrintDetailedMap(class VmaJsonWriter & json)12535 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
12536 {
12537     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12538 
12539     json.BeginObject();
12540 
12541     if(IsCustomPool())
12542     {
12543         const char* poolName = m_hParentPool->GetName();
12544         if(poolName != VMA_NULL && poolName[0] != '\0')
12545         {
12546             json.WriteString("Name");
12547             json.WriteString(poolName);
12548         }
12549 
12550         json.WriteString("MemoryTypeIndex");
12551         json.WriteNumber(m_MemoryTypeIndex);
12552 
12553         json.WriteString("BlockSize");
12554         json.WriteNumber(m_PreferredBlockSize);
12555 
12556         json.WriteString("BlockCount");
12557         json.BeginObject(true);
12558         if(m_MinBlockCount > 0)
12559         {
12560             json.WriteString("Min");
12561             json.WriteNumber((uint64_t)m_MinBlockCount);
12562         }
12563         if(m_MaxBlockCount < SIZE_MAX)
12564         {
12565             json.WriteString("Max");
12566             json.WriteNumber((uint64_t)m_MaxBlockCount);
12567         }
12568         json.WriteString("Cur");
12569         json.WriteNumber((uint64_t)m_Blocks.size());
12570         json.EndObject();
12571 
12572         if(m_FrameInUseCount > 0)
12573         {
12574             json.WriteString("FrameInUseCount");
12575             json.WriteNumber(m_FrameInUseCount);
12576         }
12577 
12578         if(m_Algorithm != 0)
12579         {
12580             json.WriteString("Algorithm");
12581             json.WriteString(VmaAlgorithmToStr(m_Algorithm));
12582         }
12583     }
12584     else
12585     {
12586         json.WriteString("PreferredBlockSize");
12587         json.WriteNumber(m_PreferredBlockSize);
12588     }
12589 
12590     json.WriteString("Blocks");
12591     json.BeginObject();
12592     for(size_t i = 0; i < m_Blocks.size(); ++i)
12593     {
12594         json.BeginString();
12595         json.ContinueString(m_Blocks[i]->GetId());
12596         json.EndString();
12597 
12598         m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
12599     }
12600     json.EndObject();
12601 
12602     json.EndObject();
12603 }
12604 
12605 #endif // #if VMA_STATS_STRING_ENABLED
12606 
Defragment(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats,VmaDefragmentationFlags flags,VkDeviceSize & maxCpuBytesToMove,uint32_t & maxCpuAllocationsToMove,VkDeviceSize & maxGpuBytesToMove,uint32_t & maxGpuAllocationsToMove,VkCommandBuffer commandBuffer)12607 void VmaBlockVector::Defragment(
12608     class VmaBlockVectorDefragmentationContext* pCtx,
12609     VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags,
12610     VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
12611     VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
12612     VkCommandBuffer commandBuffer)
12613 {
12614     pCtx->res = VK_SUCCESS;
12615 
12616     const VkMemoryPropertyFlags memPropFlags =
12617         m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
12618     const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
12619 
12620     const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
12621         isHostVisible;
12622     const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
12623         !IsCorruptionDetectionEnabled() &&
12624         ((1u << m_MemoryTypeIndex) & m_hAllocator->GetGpuDefragmentationMemoryTypeBits()) != 0;
12625 
12626     // There are options to defragment this memory type.
12627     if(canDefragmentOnCpu || canDefragmentOnGpu)
12628     {
12629         bool defragmentOnGpu;
12630         // There is only one option to defragment this memory type.
12631         if(canDefragmentOnGpu != canDefragmentOnCpu)
12632         {
12633             defragmentOnGpu = canDefragmentOnGpu;
12634         }
12635         // Both options are available: Heuristics to choose the best one.
12636         else
12637         {
12638             defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
12639                 m_hAllocator->IsIntegratedGpu();
12640         }
12641 
12642         bool overlappingMoveSupported = !defragmentOnGpu;
12643 
12644         if(m_hAllocator->m_UseMutex)
12645         {
12646             if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
12647             {
12648                 if(!m_Mutex.TryLockWrite())
12649                 {
12650                     pCtx->res = VK_ERROR_INITIALIZATION_FAILED;
12651                     return;
12652                 }
12653             }
12654             else
12655             {
12656                 m_Mutex.LockWrite();
12657                 pCtx->mutexLocked = true;
12658             }
12659         }
12660 
12661         pCtx->Begin(overlappingMoveSupported, flags);
12662 
12663         // Defragment.
12664 
12665         const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
12666         const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
12667         VmaDefragmentationAlgorithm* algo = pCtx->GetAlgorithm();
12668         pCtx->res = algo->Defragment(pCtx->defragmentationMoves, maxBytesToMove, maxAllocationsToMove, flags);
12669 
12670         // Accumulate statistics.
12671         if(pStats != VMA_NULL)
12672         {
12673             const VkDeviceSize bytesMoved = algo->GetBytesMoved();
12674             const uint32_t allocationsMoved = algo->GetAllocationsMoved();
12675             pStats->bytesMoved += bytesMoved;
12676             pStats->allocationsMoved += allocationsMoved;
12677             VMA_ASSERT(bytesMoved <= maxBytesToMove);
12678             VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
12679             if(defragmentOnGpu)
12680             {
12681                 maxGpuBytesToMove -= bytesMoved;
12682                 maxGpuAllocationsToMove -= allocationsMoved;
12683             }
12684             else
12685             {
12686                 maxCpuBytesToMove -= bytesMoved;
12687                 maxCpuAllocationsToMove -= allocationsMoved;
12688             }
12689         }
12690 
12691         if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
12692         {
12693             if(m_hAllocator->m_UseMutex)
12694                 m_Mutex.UnlockWrite();
12695 
12696             if(pCtx->res >= VK_SUCCESS && !pCtx->defragmentationMoves.empty())
12697                 pCtx->res = VK_NOT_READY;
12698 
12699             return;
12700         }
12701 
12702         if(pCtx->res >= VK_SUCCESS)
12703         {
12704             if(defragmentOnGpu)
12705             {
12706                 ApplyDefragmentationMovesGpu(pCtx, pCtx->defragmentationMoves, commandBuffer);
12707             }
12708             else
12709             {
12710                 ApplyDefragmentationMovesCpu(pCtx, pCtx->defragmentationMoves);
12711             }
12712         }
12713     }
12714 }
12715 
DefragmentationEnd(class VmaBlockVectorDefragmentationContext * pCtx,uint32_t flags,VmaDefragmentationStats * pStats)12716 void VmaBlockVector::DefragmentationEnd(
12717     class VmaBlockVectorDefragmentationContext* pCtx,
12718     uint32_t flags,
12719     VmaDefragmentationStats* pStats)
12720 {
12721     if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL && m_hAllocator->m_UseMutex)
12722     {
12723         VMA_ASSERT(pCtx->mutexLocked == false);
12724 
12725         // Incremental defragmentation doesn't hold the lock, so when we enter here we don't actually have any
12726         // lock protecting us. Since we mutate state here, we have to take the lock out now
12727         m_Mutex.LockWrite();
12728         pCtx->mutexLocked = true;
12729     }
12730 
12731     // If the mutex isn't locked we didn't do any work and there is nothing to delete.
12732     if(pCtx->mutexLocked || !m_hAllocator->m_UseMutex)
12733     {
12734         // Destroy buffers.
12735         for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--;)
12736         {
12737             VmaBlockDefragmentationContext &blockCtx = pCtx->blockContexts[blockIndex];
12738             if(blockCtx.hBuffer)
12739             {
12740                 (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
12741             }
12742         }
12743 
12744         if(pCtx->res >= VK_SUCCESS)
12745         {
12746             FreeEmptyBlocks(pStats);
12747         }
12748     }
12749 
12750     if(pCtx->mutexLocked)
12751     {
12752         VMA_ASSERT(m_hAllocator->m_UseMutex);
12753         m_Mutex.UnlockWrite();
12754     }
12755 }
12756 
ProcessDefragmentations(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationPassMoveInfo * pMove,uint32_t maxMoves)12757 uint32_t VmaBlockVector::ProcessDefragmentations(
12758     class VmaBlockVectorDefragmentationContext *pCtx,
12759     VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves)
12760 {
12761     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12762 
12763     const uint32_t moveCount = VMA_MIN(uint32_t(pCtx->defragmentationMoves.size()) - pCtx->defragmentationMovesProcessed, maxMoves);
12764 
12765     for(uint32_t i = 0; i < moveCount; ++ i)
12766     {
12767         VmaDefragmentationMove& move = pCtx->defragmentationMoves[pCtx->defragmentationMovesProcessed + i];
12768 
12769         pMove->allocation = move.hAllocation;
12770         pMove->memory = move.pDstBlock->GetDeviceMemory();
12771         pMove->offset = move.dstOffset;
12772 
12773         ++ pMove;
12774     }
12775 
12776     pCtx->defragmentationMovesProcessed += moveCount;
12777 
12778     return moveCount;
12779 }
12780 
CommitDefragmentations(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats)12781 void VmaBlockVector::CommitDefragmentations(
12782     class VmaBlockVectorDefragmentationContext *pCtx,
12783     VmaDefragmentationStats* pStats)
12784 {
12785     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12786 
12787     for(uint32_t i = pCtx->defragmentationMovesCommitted; i < pCtx->defragmentationMovesProcessed; ++ i)
12788     {
12789         const VmaDefragmentationMove &move = pCtx->defragmentationMoves[i];
12790 
12791         move.pSrcBlock->m_pMetadata->FreeAtOffset(move.srcOffset);
12792         move.hAllocation->ChangeBlockAllocation(m_hAllocator, move.pDstBlock, move.dstOffset);
12793     }
12794 
12795     pCtx->defragmentationMovesCommitted = pCtx->defragmentationMovesProcessed;
12796     FreeEmptyBlocks(pStats);
12797 }
12798 
CalcAllocationCount()12799 size_t VmaBlockVector::CalcAllocationCount() const
12800 {
12801     size_t result = 0;
12802     for(size_t i = 0; i < m_Blocks.size(); ++i)
12803     {
12804         result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
12805     }
12806     return result;
12807 }
12808 
IsBufferImageGranularityConflictPossible()12809 bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
12810 {
12811     if(m_BufferImageGranularity == 1)
12812     {
12813         return false;
12814     }
12815     VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
12816     for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
12817     {
12818         VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
12819         VMA_ASSERT(m_Algorithm == 0);
12820         VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
12821         if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
12822         {
12823             return true;
12824         }
12825     }
12826     return false;
12827 }
12828 
MakePoolAllocationsLost(uint32_t currentFrameIndex,size_t * pLostAllocationCount)12829 void VmaBlockVector::MakePoolAllocationsLost(
12830     uint32_t currentFrameIndex,
12831     size_t* pLostAllocationCount)
12832 {
12833     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12834     size_t lostAllocationCount = 0;
12835     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12836     {
12837         VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12838         VMA_ASSERT(pBlock);
12839         lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
12840     }
12841     if(pLostAllocationCount != VMA_NULL)
12842     {
12843         *pLostAllocationCount = lostAllocationCount;
12844     }
12845 }
12846 
CheckCorruption()12847 VkResult VmaBlockVector::CheckCorruption()
12848 {
12849     if(!IsCorruptionDetectionEnabled())
12850     {
12851         return VK_ERROR_FEATURE_NOT_PRESENT;
12852     }
12853 
12854     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12855     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12856     {
12857         VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12858         VMA_ASSERT(pBlock);
12859         VkResult res = pBlock->CheckCorruption(m_hAllocator);
12860         if(res != VK_SUCCESS)
12861         {
12862             return res;
12863         }
12864     }
12865     return VK_SUCCESS;
12866 }
12867 
AddStats(VmaStats * pStats)12868 void VmaBlockVector::AddStats(VmaStats* pStats)
12869 {
12870     const uint32_t memTypeIndex = m_MemoryTypeIndex;
12871     const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
12872 
12873     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12874 
12875     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12876     {
12877         const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12878         VMA_ASSERT(pBlock);
12879         VMA_HEAVY_ASSERT(pBlock->Validate());
12880         VmaStatInfo allocationStatInfo;
12881         pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
12882         VmaAddStatInfo(pStats->total, allocationStatInfo);
12883         VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
12884         VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
12885     }
12886 }
12887 
12888 ////////////////////////////////////////////////////////////////////////////////
12889 // VmaDefragmentationAlgorithm_Generic members definition
12890 
VmaDefragmentationAlgorithm_Generic(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)12891 VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
12892     VmaAllocator hAllocator,
12893     VmaBlockVector* pBlockVector,
12894     uint32_t currentFrameIndex,
12895     bool overlappingMoveSupported) :
12896     VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
12897     m_AllocationCount(0),
12898     m_AllAllocations(false),
12899     m_BytesMoved(0),
12900     m_AllocationsMoved(0),
12901     m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
12902 {
12903     // Create block info for each block.
12904     const size_t blockCount = m_pBlockVector->m_Blocks.size();
12905     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12906     {
12907         BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
12908         pBlockInfo->m_OriginalBlockIndex = blockIndex;
12909         pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
12910         m_Blocks.push_back(pBlockInfo);
12911     }
12912 
12913     // Sort them by m_pBlock pointer value.
12914     VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
12915 }
12916 
~VmaDefragmentationAlgorithm_Generic()12917 VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
12918 {
12919     for(size_t i = m_Blocks.size(); i--; )
12920     {
12921         vma_delete(m_hAllocator, m_Blocks[i]);
12922     }
12923 }
12924 
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)12925 void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
12926 {
12927     // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
12928     if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
12929     {
12930         VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
12931         BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
12932         if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
12933         {
12934             AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
12935             (*it)->m_Allocations.push_back(allocInfo);
12936         }
12937         else
12938         {
12939             VMA_ASSERT(0);
12940         }
12941 
12942         ++m_AllocationCount;
12943     }
12944 }
12945 
DefragmentRound(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,bool freeOldAllocations)12946 VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
12947     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12948     VkDeviceSize maxBytesToMove,
12949     uint32_t maxAllocationsToMove,
12950     bool freeOldAllocations)
12951 {
12952     if(m_Blocks.empty())
12953     {
12954         return VK_SUCCESS;
12955     }
12956 
12957     // This is a choice based on research.
12958     // Option 1:
12959     uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
12960     // Option 2:
12961     //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
12962     // Option 3:
12963     //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
12964 
12965     size_t srcBlockMinIndex = 0;
12966     // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
12967     /*
12968     if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
12969     {
12970         const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
12971         if(blocksWithNonMovableCount > 0)
12972         {
12973             srcBlockMinIndex = blocksWithNonMovableCount - 1;
12974         }
12975     }
12976     */
12977 
12978     size_t srcBlockIndex = m_Blocks.size() - 1;
12979     size_t srcAllocIndex = SIZE_MAX;
12980     for(;;)
12981     {
12982         // 1. Find next allocation to move.
12983         // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
12984         // 1.2. Then start from last to first m_Allocations.
12985         while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
12986         {
12987             if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
12988             {
12989                 // Finished: no more allocations to process.
12990                 if(srcBlockIndex == srcBlockMinIndex)
12991                 {
12992                     return VK_SUCCESS;
12993                 }
12994                 else
12995                 {
12996                     --srcBlockIndex;
12997                     srcAllocIndex = SIZE_MAX;
12998                 }
12999             }
13000             else
13001             {
13002                 srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
13003             }
13004         }
13005 
13006         BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
13007         AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
13008 
13009         const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
13010         const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
13011         const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
13012         const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
13013 
13014         // 2. Try to find new place for this allocation in preceding or current block.
13015         for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
13016         {
13017             BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
13018             VmaAllocationRequest dstAllocRequest;
13019             if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
13020                 m_CurrentFrameIndex,
13021                 m_pBlockVector->GetFrameInUseCount(),
13022                 m_pBlockVector->GetBufferImageGranularity(),
13023                 size,
13024                 alignment,
13025                 false, // upperAddress
13026                 suballocType,
13027                 false, // canMakeOtherLost
13028                 strategy,
13029                 &dstAllocRequest) &&
13030             MoveMakesSense(
13031                 dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
13032             {
13033                 VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
13034 
13035                 // Reached limit on number of allocations or bytes to move.
13036                 if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
13037                     (m_BytesMoved + size > maxBytesToMove))
13038                 {
13039                     return VK_SUCCESS;
13040                 }
13041 
13042                 VmaDefragmentationMove move = {};
13043                 move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
13044                 move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
13045                 move.srcOffset = srcOffset;
13046                 move.dstOffset = dstAllocRequest.offset;
13047                 move.size = size;
13048                 move.hAllocation = allocInfo.m_hAllocation;
13049                 move.pSrcBlock = pSrcBlockInfo->m_pBlock;
13050                 move.pDstBlock = pDstBlockInfo->m_pBlock;
13051 
13052                 moves.push_back(move);
13053 
13054                 pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(dstAllocRequest, suballocType, allocInfo.m_hAllocation);
13055 
13056                 if(freeOldAllocations)
13057                 {
13058                     pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
13059                     allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
13060                 }
13061 
13062                 if(allocInfo.m_pChanged != VMA_NULL)
13063                 {
13064                     *allocInfo.m_pChanged = VK_TRUE;
13065                 }
13066 
13067                 ++m_AllocationsMoved;
13068                 m_BytesMoved += size;
13069 
13070                 VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
13071 
13072                 break;
13073             }
13074         }
13075 
13076         // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
13077 
13078         if(srcAllocIndex > 0)
13079         {
13080             --srcAllocIndex;
13081         }
13082         else
13083         {
13084             if(srcBlockIndex > 0)
13085             {
13086                 --srcBlockIndex;
13087                 srcAllocIndex = SIZE_MAX;
13088             }
13089             else
13090             {
13091                 return VK_SUCCESS;
13092             }
13093         }
13094     }
13095 }
13096 
CalcBlocksWithNonMovableCount()13097 size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
13098 {
13099     size_t result = 0;
13100     for(size_t i = 0; i < m_Blocks.size(); ++i)
13101     {
13102         if(m_Blocks[i]->m_HasNonMovableAllocations)
13103         {
13104             ++result;
13105         }
13106     }
13107     return result;
13108 }
13109 
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,VmaDefragmentationFlags flags)13110 VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
13111     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13112     VkDeviceSize maxBytesToMove,
13113     uint32_t maxAllocationsToMove,
13114     VmaDefragmentationFlags flags)
13115 {
13116     if(!m_AllAllocations && m_AllocationCount == 0)
13117     {
13118         return VK_SUCCESS;
13119     }
13120 
13121     const size_t blockCount = m_Blocks.size();
13122     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13123     {
13124         BlockInfo* pBlockInfo = m_Blocks[blockIndex];
13125 
13126         if(m_AllAllocations)
13127         {
13128             VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
13129             VMA_ASSERT(!pMetadata->IsVirtual());
13130             for (VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
13131                 it != pMetadata->m_Suballocations.end();
13132                 ++it)
13133             {
13134                 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
13135                 {
13136                     AllocationInfo allocInfo = AllocationInfo((VmaAllocation)it->userData, VMA_NULL);
13137                     pBlockInfo->m_Allocations.push_back(allocInfo);
13138                 }
13139             }
13140         }
13141 
13142         pBlockInfo->CalcHasNonMovableAllocations();
13143 
13144         // This is a choice based on research.
13145         // Option 1:
13146         pBlockInfo->SortAllocationsByOffsetDescending();
13147         // Option 2:
13148         //pBlockInfo->SortAllocationsBySizeDescending();
13149     }
13150 
13151     // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
13152     VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
13153 
13154     // This is a choice based on research.
13155     const uint32_t roundCount = 2;
13156 
13157     // Execute defragmentation rounds (the main part).
13158     VkResult result = VK_SUCCESS;
13159     for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
13160     {
13161         result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove, !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL));
13162     }
13163 
13164     return result;
13165 }
13166 
MoveMakesSense(size_t dstBlockIndex,VkDeviceSize dstOffset,size_t srcBlockIndex,VkDeviceSize srcOffset)13167 bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
13168         size_t dstBlockIndex, VkDeviceSize dstOffset,
13169         size_t srcBlockIndex, VkDeviceSize srcOffset)
13170 {
13171     if(dstBlockIndex < srcBlockIndex)
13172     {
13173         return true;
13174     }
13175     if(dstBlockIndex > srcBlockIndex)
13176     {
13177         return false;
13178     }
13179     if(dstOffset < srcOffset)
13180     {
13181         return true;
13182     }
13183     return false;
13184 }
13185 
13186 ////////////////////////////////////////////////////////////////////////////////
13187 // VmaDefragmentationAlgorithm_Fast
13188 
VmaDefragmentationAlgorithm_Fast(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)13189 VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
13190     VmaAllocator hAllocator,
13191     VmaBlockVector* pBlockVector,
13192     uint32_t currentFrameIndex,
13193     bool overlappingMoveSupported) :
13194     VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
13195     m_OverlappingMoveSupported(overlappingMoveSupported),
13196     m_AllocationCount(0),
13197     m_AllAllocations(false),
13198     m_BytesMoved(0),
13199     m_AllocationsMoved(0),
13200     m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
13201 {
13202     VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
13203 
13204 }
13205 
~VmaDefragmentationAlgorithm_Fast()13206 VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
13207 {
13208 }
13209 
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,VmaDefragmentationFlags flags)13210 VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
13211     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13212     VkDeviceSize maxBytesToMove,
13213     uint32_t maxAllocationsToMove,
13214     VmaDefragmentationFlags flags)
13215 {
13216     VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
13217 
13218     const size_t blockCount = m_pBlockVector->GetBlockCount();
13219     if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
13220     {
13221         return VK_SUCCESS;
13222     }
13223 
13224     PreprocessMetadata();
13225 
13226     // Sort blocks in order from most destination.
13227 
13228     m_BlockInfos.resize(blockCount);
13229     for(size_t i = 0; i < blockCount; ++i)
13230     {
13231         m_BlockInfos[i].origBlockIndex = i;
13232     }
13233 
13234     VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
13235         return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
13236             m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
13237     });
13238 
13239     // THE MAIN ALGORITHM
13240 
13241     FreeSpaceDatabase freeSpaceDb;
13242 
13243     size_t dstBlockInfoIndex = 0;
13244     size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
13245     VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
13246     VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
13247     VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
13248     VkDeviceSize dstOffset = 0;
13249 
13250     bool end = false;
13251     for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
13252     {
13253         const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
13254         VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
13255         VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
13256         for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
13257             !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
13258         {
13259             VmaAllocation const pAlloc = (VmaAllocation)srcSuballocIt->userData;
13260             const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
13261             const VkDeviceSize srcAllocSize = srcSuballocIt->size;
13262             if(m_AllocationsMoved == maxAllocationsToMove ||
13263                 m_BytesMoved + srcAllocSize > maxBytesToMove)
13264             {
13265                 end = true;
13266                 break;
13267             }
13268             const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
13269 
13270             VmaDefragmentationMove move = {};
13271             // Try to place it in one of free spaces from the database.
13272             size_t freeSpaceInfoIndex;
13273             VkDeviceSize dstAllocOffset;
13274             if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
13275                 freeSpaceInfoIndex, dstAllocOffset))
13276             {
13277                 size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
13278                 VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
13279                 VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
13280 
13281                 // Same block
13282                 if(freeSpaceInfoIndex == srcBlockInfoIndex)
13283                 {
13284                     VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
13285 
13286                     // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
13287 
13288                     VmaSuballocation suballoc = *srcSuballocIt;
13289                     suballoc.offset = dstAllocOffset;
13290                     ((VmaAllocation)(suballoc.userData))->ChangeOffset(dstAllocOffset);
13291                     m_BytesMoved += srcAllocSize;
13292                     ++m_AllocationsMoved;
13293 
13294                     VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
13295                     ++nextSuballocIt;
13296                     pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
13297                     srcSuballocIt = nextSuballocIt;
13298 
13299                     InsertSuballoc(pFreeSpaceMetadata, suballoc);
13300 
13301                     move.srcBlockIndex = srcOrigBlockIndex;
13302                     move.dstBlockIndex = freeSpaceOrigBlockIndex;
13303                     move.srcOffset = srcAllocOffset;
13304                     move.dstOffset = dstAllocOffset;
13305                     move.size = srcAllocSize;
13306 
13307                     moves.push_back(move);
13308                 }
13309                 // Different block
13310                 else
13311                 {
13312                     // MOVE OPTION 2: Move the allocation to a different block.
13313 
13314                     VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
13315 
13316                     VmaSuballocation suballoc = *srcSuballocIt;
13317                     suballoc.offset = dstAllocOffset;
13318                     ((VmaAllocation)(suballoc.userData))->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
13319                     m_BytesMoved += srcAllocSize;
13320                     ++m_AllocationsMoved;
13321 
13322                     VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
13323                     ++nextSuballocIt;
13324                     pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
13325                     srcSuballocIt = nextSuballocIt;
13326 
13327                     InsertSuballoc(pFreeSpaceMetadata, suballoc);
13328 
13329                     move.srcBlockIndex = srcOrigBlockIndex;
13330                     move.dstBlockIndex = freeSpaceOrigBlockIndex;
13331                     move.srcOffset = srcAllocOffset;
13332                     move.dstOffset = dstAllocOffset;
13333                     move.size = srcAllocSize;
13334 
13335                     moves.push_back(move);
13336                 }
13337             }
13338             else
13339             {
13340                 dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
13341 
13342                 // If the allocation doesn't fit before the end of dstBlock, forward to next block.
13343                 while(dstBlockInfoIndex < srcBlockInfoIndex &&
13344                     dstAllocOffset + srcAllocSize > dstBlockSize)
13345                 {
13346                     // But before that, register remaining free space at the end of dst block.
13347                     freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
13348 
13349                     ++dstBlockInfoIndex;
13350                     dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
13351                     pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
13352                     pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
13353                     dstBlockSize = pDstMetadata->GetSize();
13354                     dstOffset = 0;
13355                     dstAllocOffset = 0;
13356                 }
13357 
13358                 // Same block
13359                 if(dstBlockInfoIndex == srcBlockInfoIndex)
13360                 {
13361                     VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
13362 
13363                     const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
13364 
13365                     bool skipOver = overlap;
13366                     if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
13367                     {
13368                         // If destination and source place overlap, skip if it would move it
13369                         // by only < 1/64 of its size.
13370                         skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
13371                     }
13372 
13373                     if(skipOver)
13374                     {
13375                         freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
13376 
13377                         dstOffset = srcAllocOffset + srcAllocSize;
13378                         ++srcSuballocIt;
13379                     }
13380                     // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
13381                     else
13382                     {
13383                         srcSuballocIt->offset = dstAllocOffset;
13384                         ((VmaAllocation)(srcSuballocIt->userData))->ChangeOffset(dstAllocOffset);
13385                         dstOffset = dstAllocOffset + srcAllocSize;
13386                         m_BytesMoved += srcAllocSize;
13387                         ++m_AllocationsMoved;
13388                         ++srcSuballocIt;
13389 
13390                         move.srcBlockIndex = srcOrigBlockIndex;
13391                         move.dstBlockIndex = dstOrigBlockIndex;
13392                         move.srcOffset = srcAllocOffset;
13393                         move.dstOffset = dstAllocOffset;
13394                         move.size = srcAllocSize;
13395 
13396                         moves.push_back(move);
13397                     }
13398                 }
13399                 // Different block
13400                 else
13401                 {
13402                     // MOVE OPTION 2: Move the allocation to a different block.
13403 
13404                     VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
13405                     VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
13406 
13407                     VmaSuballocation suballoc = *srcSuballocIt;
13408                     suballoc.offset = dstAllocOffset;
13409                     ((VmaAllocation)(suballoc.userData))->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
13410                     dstOffset = dstAllocOffset + srcAllocSize;
13411                     m_BytesMoved += srcAllocSize;
13412                     ++m_AllocationsMoved;
13413 
13414                     VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
13415                     ++nextSuballocIt;
13416                     pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
13417                     srcSuballocIt = nextSuballocIt;
13418 
13419                     pDstMetadata->m_Suballocations.push_back(suballoc);
13420 
13421                     move.srcBlockIndex = srcOrigBlockIndex;
13422                     move.dstBlockIndex = dstOrigBlockIndex;
13423                     move.srcOffset = srcAllocOffset;
13424                     move.dstOffset = dstAllocOffset;
13425                     move.size = srcAllocSize;
13426 
13427                     moves.push_back(move);
13428                 }
13429             }
13430         }
13431     }
13432 
13433     m_BlockInfos.clear();
13434 
13435     PostprocessMetadata();
13436 
13437     return VK_SUCCESS;
13438 }
13439 
PreprocessMetadata()13440 void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
13441 {
13442     const size_t blockCount = m_pBlockVector->GetBlockCount();
13443     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13444     {
13445         VmaBlockMetadata_Generic* const pMetadata =
13446             (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
13447         pMetadata->m_FreeCount = 0;
13448         pMetadata->m_SumFreeSize = pMetadata->GetSize();
13449         pMetadata->m_FreeSuballocationsBySize.clear();
13450         for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
13451             it != pMetadata->m_Suballocations.end(); )
13452         {
13453             if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
13454             {
13455                 VmaSuballocationList::iterator nextIt = it;
13456                 ++nextIt;
13457                 pMetadata->m_Suballocations.erase(it);
13458                 it = nextIt;
13459             }
13460             else
13461             {
13462                 ++it;
13463             }
13464         }
13465     }
13466 }
13467 
PostprocessMetadata()13468 void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
13469 {
13470     const size_t blockCount = m_pBlockVector->GetBlockCount();
13471     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13472     {
13473         VmaBlockMetadata_Generic* const pMetadata =
13474             (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
13475         const VkDeviceSize blockSize = pMetadata->GetSize();
13476 
13477         // No allocations in this block - entire area is free.
13478         if(pMetadata->m_Suballocations.empty())
13479         {
13480             pMetadata->m_FreeCount = 1;
13481             //pMetadata->m_SumFreeSize is already set to blockSize.
13482             VmaSuballocation suballoc = {
13483                 0, // offset
13484                 blockSize, // size
13485                 VMA_NULL, // hAllocation
13486                 VMA_SUBALLOCATION_TYPE_FREE };
13487             pMetadata->m_Suballocations.push_back(suballoc);
13488             pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
13489         }
13490         // There are some allocations in this block.
13491         else
13492         {
13493             VkDeviceSize offset = 0;
13494             VmaSuballocationList::iterator it;
13495             for(it = pMetadata->m_Suballocations.begin();
13496                 it != pMetadata->m_Suballocations.end();
13497                 ++it)
13498             {
13499                 VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
13500                 VMA_ASSERT(it->offset >= offset);
13501 
13502                 // Need to insert preceding free space.
13503                 if(it->offset > offset)
13504                 {
13505                     ++pMetadata->m_FreeCount;
13506                     const VkDeviceSize freeSize = it->offset - offset;
13507                     VmaSuballocation suballoc = {
13508                         offset, // offset
13509                         freeSize, // size
13510                         VMA_NULL, // hAllocation
13511                         VMA_SUBALLOCATION_TYPE_FREE };
13512                     VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
13513                     pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
13514                 }
13515 
13516                 pMetadata->m_SumFreeSize -= it->size;
13517                 offset = it->offset + it->size;
13518             }
13519 
13520             // Need to insert trailing free space.
13521             if(offset < blockSize)
13522             {
13523                 ++pMetadata->m_FreeCount;
13524                 const VkDeviceSize freeSize = blockSize - offset;
13525                 VmaSuballocation suballoc = {
13526                     offset, // offset
13527                     freeSize, // size
13528                     VMA_NULL, // hAllocation
13529                     VMA_SUBALLOCATION_TYPE_FREE };
13530                 VMA_ASSERT(it == pMetadata->m_Suballocations.end());
13531                 VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
13532                 pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
13533             }
13534 
13535             VMA_SORT(
13536                 pMetadata->m_FreeSuballocationsBySize.begin(),
13537                 pMetadata->m_FreeSuballocationsBySize.end(),
13538                 VmaSuballocationItemSizeLess());
13539         }
13540 
13541         VMA_HEAVY_ASSERT(pMetadata->Validate());
13542     }
13543 }
13544 
InsertSuballoc(VmaBlockMetadata_Generic * pMetadata,const VmaSuballocation & suballoc)13545 void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
13546 {
13547     // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
13548     VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
13549     while(it != pMetadata->m_Suballocations.end())
13550     {
13551         if(it->offset < suballoc.offset)
13552         {
13553             ++it;
13554         }
13555     }
13556     pMetadata->m_Suballocations.insert(it, suballoc);
13557 }
13558 
13559 ////////////////////////////////////////////////////////////////////////////////
13560 // VmaBlockVectorDefragmentationContext
13561 
VmaBlockVectorDefragmentationContext(VmaAllocator hAllocator,VmaPool hCustomPool,VmaBlockVector * pBlockVector,uint32_t currFrameIndex)13562 VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
13563     VmaAllocator hAllocator,
13564     VmaPool hCustomPool,
13565     VmaBlockVector* pBlockVector,
13566     uint32_t currFrameIndex) :
13567     res(VK_SUCCESS),
13568     mutexLocked(false),
13569     blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
13570     defragmentationMoves(VmaStlAllocator<VmaDefragmentationMove>(hAllocator->GetAllocationCallbacks())),
13571     defragmentationMovesProcessed(0),
13572     defragmentationMovesCommitted(0),
13573     hasDefragmentationPlan(0),
13574     m_hAllocator(hAllocator),
13575     m_hCustomPool(hCustomPool),
13576     m_pBlockVector(pBlockVector),
13577     m_CurrFrameIndex(currFrameIndex),
13578     m_pAlgorithm(VMA_NULL),
13579     m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
13580     m_AllAllocations(false)
13581 {
13582 }
13583 
~VmaBlockVectorDefragmentationContext()13584 VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
13585 {
13586     vma_delete(m_hAllocator, m_pAlgorithm);
13587 }
13588 
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)13589 void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
13590 {
13591     AllocInfo info = { hAlloc, pChanged };
13592     m_Allocations.push_back(info);
13593 }
13594 
Begin(bool overlappingMoveSupported,VmaDefragmentationFlags flags)13595 void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags)
13596 {
13597     const bool allAllocations = m_AllAllocations ||
13598         m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
13599 
13600     /********************************
13601     HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
13602     ********************************/
13603 
13604     /*
13605     Fast algorithm is supported only when certain criteria are met:
13606     - VMA_DEBUG_MARGIN is 0.
13607     - All allocations in this block vector are movable.
13608     - There is no possibility of image/buffer granularity conflict.
13609     - The defragmentation is not incremental
13610     */
13611     if(VMA_DEBUG_MARGIN == 0 &&
13612         allAllocations &&
13613         !m_pBlockVector->IsBufferImageGranularityConflictPossible() &&
13614         !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL))
13615     {
13616         m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
13617             m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
13618     }
13619     else
13620     {
13621         m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
13622             m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
13623     }
13624 
13625     if(allAllocations)
13626     {
13627         m_pAlgorithm->AddAll();
13628     }
13629     else
13630     {
13631         for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
13632         {
13633             m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
13634         }
13635     }
13636 }
13637 
13638 ////////////////////////////////////////////////////////////////////////////////
13639 // VmaDefragmentationContext
13640 
VmaDefragmentationContext_T(VmaAllocator hAllocator,uint32_t currFrameIndex,uint32_t flags,VmaDefragmentationStats * pStats)13641 VmaDefragmentationContext_T::VmaDefragmentationContext_T(
13642     VmaAllocator hAllocator,
13643     uint32_t currFrameIndex,
13644     uint32_t flags,
13645     VmaDefragmentationStats* pStats) :
13646     m_hAllocator(hAllocator),
13647     m_CurrFrameIndex(currFrameIndex),
13648     m_Flags(flags),
13649     m_pStats(pStats),
13650     m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
13651 {
13652     memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
13653 }
13654 
~VmaDefragmentationContext_T()13655 VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
13656 {
13657     for(size_t i = m_CustomPoolContexts.size(); i--; )
13658     {
13659         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
13660         pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
13661         vma_delete(m_hAllocator, pBlockVectorCtx);
13662     }
13663     for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
13664     {
13665         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
13666         if(pBlockVectorCtx)
13667         {
13668             pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
13669             vma_delete(m_hAllocator, pBlockVectorCtx);
13670         }
13671     }
13672 }
13673 
AddPools(uint32_t poolCount,const VmaPool * pPools)13674 void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, const VmaPool* pPools)
13675 {
13676     for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
13677     {
13678         VmaPool pool = pPools[poolIndex];
13679         VMA_ASSERT(pool);
13680         // Pools with algorithm other than default are not defragmented.
13681         if(pool->m_BlockVector.GetAlgorithm() == 0)
13682         {
13683             VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
13684 
13685             for(size_t i = m_CustomPoolContexts.size(); i--; )
13686             {
13687                 if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
13688                 {
13689                     pBlockVectorDefragCtx = m_CustomPoolContexts[i];
13690                     break;
13691                 }
13692             }
13693 
13694             if(!pBlockVectorDefragCtx)
13695             {
13696                 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13697                     m_hAllocator,
13698                     pool,
13699                     &pool->m_BlockVector,
13700                     m_CurrFrameIndex);
13701                 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
13702             }
13703 
13704             pBlockVectorDefragCtx->AddAll();
13705         }
13706     }
13707 }
13708 
AddAllocations(uint32_t allocationCount,const VmaAllocation * pAllocations,VkBool32 * pAllocationsChanged)13709 void VmaDefragmentationContext_T::AddAllocations(
13710     uint32_t allocationCount,
13711     const VmaAllocation* pAllocations,
13712     VkBool32* pAllocationsChanged)
13713 {
13714     // Dispatch pAllocations among defragmentators. Create them when necessary.
13715     for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
13716     {
13717         const VmaAllocation hAlloc = pAllocations[allocIndex];
13718         VMA_ASSERT(hAlloc);
13719         // DedicatedAlloc cannot be defragmented.
13720         if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
13721             // Lost allocation cannot be defragmented.
13722             (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
13723         {
13724             VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
13725 
13726             const VmaPool hAllocPool = hAlloc->GetBlock()->GetParentPool();
13727             // This allocation belongs to custom pool.
13728             if(hAllocPool != VK_NULL_HANDLE)
13729             {
13730                 // Pools with algorithm other than default are not defragmented.
13731                 if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
13732                 {
13733                     for(size_t i = m_CustomPoolContexts.size(); i--; )
13734                     {
13735                         if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
13736                         {
13737                             pBlockVectorDefragCtx = m_CustomPoolContexts[i];
13738                             break;
13739                         }
13740                     }
13741                     if(!pBlockVectorDefragCtx)
13742                     {
13743                         pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13744                             m_hAllocator,
13745                             hAllocPool,
13746                             &hAllocPool->m_BlockVector,
13747                             m_CurrFrameIndex);
13748                         m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
13749                     }
13750                 }
13751             }
13752             // This allocation belongs to default pool.
13753             else
13754             {
13755                 const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
13756                 pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
13757                 if(!pBlockVectorDefragCtx)
13758                 {
13759                     pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13760                         m_hAllocator,
13761                         VMA_NULL, // hCustomPool
13762                         m_hAllocator->m_pBlockVectors[memTypeIndex],
13763                         m_CurrFrameIndex);
13764                     m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
13765                 }
13766             }
13767 
13768             if(pBlockVectorDefragCtx)
13769             {
13770                 VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
13771                     &pAllocationsChanged[allocIndex] : VMA_NULL;
13772                 pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
13773             }
13774         }
13775     }
13776 }
13777 
Defragment(VkDeviceSize maxCpuBytesToMove,uint32_t maxCpuAllocationsToMove,VkDeviceSize maxGpuBytesToMove,uint32_t maxGpuAllocationsToMove,VkCommandBuffer commandBuffer,VmaDefragmentationStats * pStats,VmaDefragmentationFlags flags)13778 VkResult VmaDefragmentationContext_T::Defragment(
13779     VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
13780     VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
13781     VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags)
13782 {
13783     if(pStats)
13784     {
13785         memset(pStats, 0, sizeof(VmaDefragmentationStats));
13786     }
13787 
13788     if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
13789     {
13790         // For incremental defragmetnations, we just earmark how much we can move
13791         // The real meat is in the defragmentation steps
13792         m_MaxCpuBytesToMove = maxCpuBytesToMove;
13793         m_MaxCpuAllocationsToMove = maxCpuAllocationsToMove;
13794 
13795         m_MaxGpuBytesToMove = maxGpuBytesToMove;
13796         m_MaxGpuAllocationsToMove = maxGpuAllocationsToMove;
13797 
13798         if(m_MaxCpuBytesToMove == 0 && m_MaxCpuAllocationsToMove == 0 &&
13799             m_MaxGpuBytesToMove == 0 && m_MaxGpuAllocationsToMove == 0)
13800             return VK_SUCCESS;
13801 
13802         return VK_NOT_READY;
13803     }
13804 
13805     if(commandBuffer == VK_NULL_HANDLE)
13806     {
13807         maxGpuBytesToMove = 0;
13808         maxGpuAllocationsToMove = 0;
13809     }
13810 
13811     VkResult res = VK_SUCCESS;
13812 
13813     // Process default pools.
13814     for(uint32_t memTypeIndex = 0;
13815         memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
13816         ++memTypeIndex)
13817     {
13818         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
13819         if(pBlockVectorCtx)
13820         {
13821             VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
13822             pBlockVectorCtx->GetBlockVector()->Defragment(
13823                 pBlockVectorCtx,
13824                 pStats, flags,
13825                 maxCpuBytesToMove, maxCpuAllocationsToMove,
13826                 maxGpuBytesToMove, maxGpuAllocationsToMove,
13827                 commandBuffer);
13828             if(pBlockVectorCtx->res != VK_SUCCESS)
13829             {
13830                 res = pBlockVectorCtx->res;
13831             }
13832         }
13833     }
13834 
13835     // Process custom pools.
13836     for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
13837         customCtxIndex < customCtxCount && res >= VK_SUCCESS;
13838         ++customCtxIndex)
13839     {
13840         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
13841         VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
13842         pBlockVectorCtx->GetBlockVector()->Defragment(
13843             pBlockVectorCtx,
13844             pStats, flags,
13845             maxCpuBytesToMove, maxCpuAllocationsToMove,
13846             maxGpuBytesToMove, maxGpuAllocationsToMove,
13847             commandBuffer);
13848         if(pBlockVectorCtx->res != VK_SUCCESS)
13849         {
13850             res = pBlockVectorCtx->res;
13851         }
13852     }
13853 
13854     return res;
13855 }
13856 
DefragmentPassBegin(VmaDefragmentationPassInfo * pInfo)13857 VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo)
13858 {
13859     VmaDefragmentationPassMoveInfo* pCurrentMove = pInfo->pMoves;
13860     uint32_t movesLeft = pInfo->moveCount;
13861 
13862     // Process default pools.
13863     for(uint32_t memTypeIndex = 0;
13864         memTypeIndex < m_hAllocator->GetMemoryTypeCount();
13865         ++memTypeIndex)
13866     {
13867         VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
13868         if(pBlockVectorCtx)
13869         {
13870             VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
13871 
13872             if(!pBlockVectorCtx->hasDefragmentationPlan)
13873             {
13874                 pBlockVectorCtx->GetBlockVector()->Defragment(
13875                     pBlockVectorCtx,
13876                     m_pStats, m_Flags,
13877                     m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
13878                     m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
13879                     VK_NULL_HANDLE);
13880 
13881                 if(pBlockVectorCtx->res < VK_SUCCESS)
13882                     continue;
13883 
13884                 pBlockVectorCtx->hasDefragmentationPlan = true;
13885             }
13886 
13887             const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
13888                 pBlockVectorCtx,
13889                 pCurrentMove, movesLeft);
13890 
13891             movesLeft -= processed;
13892             pCurrentMove += processed;
13893         }
13894     }
13895 
13896     // Process custom pools.
13897     for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
13898         customCtxIndex < customCtxCount;
13899         ++customCtxIndex)
13900     {
13901         VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
13902         VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
13903 
13904         if(!pBlockVectorCtx->hasDefragmentationPlan)
13905         {
13906             pBlockVectorCtx->GetBlockVector()->Defragment(
13907                 pBlockVectorCtx,
13908                 m_pStats, m_Flags,
13909                 m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
13910                 m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
13911                 VK_NULL_HANDLE);
13912 
13913             if(pBlockVectorCtx->res < VK_SUCCESS)
13914                 continue;
13915 
13916             pBlockVectorCtx->hasDefragmentationPlan = true;
13917         }
13918 
13919         const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
13920             pBlockVectorCtx,
13921             pCurrentMove, movesLeft);
13922 
13923         movesLeft -= processed;
13924         pCurrentMove += processed;
13925     }
13926 
13927     pInfo->moveCount = pInfo->moveCount - movesLeft;
13928 
13929     return VK_SUCCESS;
13930 }
DefragmentPassEnd()13931 VkResult VmaDefragmentationContext_T::DefragmentPassEnd()
13932 {
13933     VkResult res = VK_SUCCESS;
13934 
13935     // Process default pools.
13936     for(uint32_t memTypeIndex = 0;
13937         memTypeIndex < m_hAllocator->GetMemoryTypeCount();
13938         ++memTypeIndex)
13939     {
13940         VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
13941         if(pBlockVectorCtx)
13942         {
13943             VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
13944 
13945             if(!pBlockVectorCtx->hasDefragmentationPlan)
13946             {
13947                 res = VK_NOT_READY;
13948                 continue;
13949             }
13950 
13951             pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
13952                 pBlockVectorCtx, m_pStats);
13953 
13954             if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
13955                 res = VK_NOT_READY;
13956         }
13957     }
13958 
13959     // Process custom pools.
13960     for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
13961         customCtxIndex < customCtxCount;
13962         ++customCtxIndex)
13963     {
13964         VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
13965         VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
13966 
13967         if(!pBlockVectorCtx->hasDefragmentationPlan)
13968         {
13969             res = VK_NOT_READY;
13970             continue;
13971         }
13972 
13973         pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
13974             pBlockVectorCtx, m_pStats);
13975 
13976         if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
13977             res = VK_NOT_READY;
13978     }
13979 
13980     return res;
13981 }
13982 
13983 ////////////////////////////////////////////////////////////////////////////////
13984 // VmaRecorder
13985 
13986 #if VMA_RECORDING_ENABLED
13987 
VmaRecorder()13988 VmaRecorder::VmaRecorder() :
13989     m_UseMutex(true),
13990     m_Flags(0),
13991     m_File(VMA_NULL),
13992     m_RecordingStartTime(std::chrono::high_resolution_clock::now())
13993 {
13994 }
13995 
Init(const VmaRecordSettings & settings,bool useMutex)13996 VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
13997 {
13998     m_UseMutex = useMutex;
13999     m_Flags = settings.flags;
14000 
14001 #if defined(_WIN32)
14002     // Open file for writing.
14003     errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
14004 
14005     if(err != 0)
14006     {
14007         return VK_ERROR_INITIALIZATION_FAILED;
14008     }
14009 #else
14010     // Open file for writing.
14011     m_File = fopen(settings.pFilePath, "wb");
14012 
14013     if(m_File == 0)
14014     {
14015         return VK_ERROR_INITIALIZATION_FAILED;
14016     }
14017 #endif
14018 
14019     // Write header.
14020     fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
14021     fprintf(m_File, "%s\n", "1,8");
14022 
14023     return VK_SUCCESS;
14024 }
14025 
~VmaRecorder()14026 VmaRecorder::~VmaRecorder()
14027 {
14028     if(m_File != VMA_NULL)
14029     {
14030         fclose(m_File);
14031     }
14032 }
14033 
RecordCreateAllocator(uint32_t frameIndex)14034 void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
14035 {
14036     CallParams callParams;
14037     GetBasicParams(callParams);
14038 
14039     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14040     fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
14041     Flush();
14042 }
14043 
RecordDestroyAllocator(uint32_t frameIndex)14044 void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
14045 {
14046     CallParams callParams;
14047     GetBasicParams(callParams);
14048 
14049     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14050     fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
14051     Flush();
14052 }
14053 
RecordCreatePool(uint32_t frameIndex,const VmaPoolCreateInfo & createInfo,VmaPool pool)14054 void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
14055 {
14056     CallParams callParams;
14057     GetBasicParams(callParams);
14058 
14059     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14060     fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
14061         createInfo.memoryTypeIndex,
14062         createInfo.flags,
14063         createInfo.blockSize,
14064         (uint64_t)createInfo.minBlockCount,
14065         (uint64_t)createInfo.maxBlockCount,
14066         createInfo.frameInUseCount,
14067         pool);
14068     Flush();
14069 }
14070 
RecordDestroyPool(uint32_t frameIndex,VmaPool pool)14071 void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
14072 {
14073     CallParams callParams;
14074     GetBasicParams(callParams);
14075 
14076     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14077     fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
14078         pool);
14079     Flush();
14080 }
14081 
RecordAllocateMemory(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)14082 void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
14083         const VkMemoryRequirements& vkMemReq,
14084         const VmaAllocationCreateInfo& createInfo,
14085         VmaAllocation allocation)
14086 {
14087     CallParams callParams;
14088     GetBasicParams(callParams);
14089 
14090     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14091     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
14092     fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
14093         vkMemReq.size,
14094         vkMemReq.alignment,
14095         vkMemReq.memoryTypeBits,
14096         createInfo.flags,
14097         createInfo.usage,
14098         createInfo.requiredFlags,
14099         createInfo.preferredFlags,
14100         createInfo.memoryTypeBits,
14101         createInfo.pool,
14102         allocation,
14103         userDataStr.GetString());
14104     Flush();
14105 }
14106 
RecordAllocateMemoryPages(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,uint64_t allocationCount,const VmaAllocation * pAllocations)14107 void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
14108     const VkMemoryRequirements& vkMemReq,
14109     const VmaAllocationCreateInfo& createInfo,
14110     uint64_t allocationCount,
14111     const VmaAllocation* pAllocations)
14112 {
14113     CallParams callParams;
14114     GetBasicParams(callParams);
14115 
14116     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14117     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
14118     fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
14119         vkMemReq.size,
14120         vkMemReq.alignment,
14121         vkMemReq.memoryTypeBits,
14122         createInfo.flags,
14123         createInfo.usage,
14124         createInfo.requiredFlags,
14125         createInfo.preferredFlags,
14126         createInfo.memoryTypeBits,
14127         createInfo.pool);
14128     PrintPointerList(allocationCount, pAllocations);
14129     fprintf(m_File, ",%s\n", userDataStr.GetString());
14130     Flush();
14131 }
14132 
RecordAllocateMemoryForBuffer(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)14133 void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
14134     const VkMemoryRequirements& vkMemReq,
14135     bool requiresDedicatedAllocation,
14136     bool prefersDedicatedAllocation,
14137     const VmaAllocationCreateInfo& createInfo,
14138     VmaAllocation allocation)
14139 {
14140     CallParams callParams;
14141     GetBasicParams(callParams);
14142 
14143     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14144     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
14145     fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryForBuffer,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
14146         vkMemReq.size,
14147         vkMemReq.alignment,
14148         vkMemReq.memoryTypeBits,
14149         requiresDedicatedAllocation ? 1 : 0,
14150         prefersDedicatedAllocation ? 1 : 0,
14151         createInfo.flags,
14152         createInfo.usage,
14153         createInfo.requiredFlags,
14154         createInfo.preferredFlags,
14155         createInfo.memoryTypeBits,
14156         createInfo.pool,
14157         allocation,
14158         userDataStr.GetString());
14159     Flush();
14160 }
14161 
RecordAllocateMemoryForImage(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)14162 void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
14163     const VkMemoryRequirements& vkMemReq,
14164     bool requiresDedicatedAllocation,
14165     bool prefersDedicatedAllocation,
14166     const VmaAllocationCreateInfo& createInfo,
14167     VmaAllocation allocation)
14168 {
14169     CallParams callParams;
14170     GetBasicParams(callParams);
14171 
14172     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14173     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
14174     fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryForImage,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
14175         vkMemReq.size,
14176         vkMemReq.alignment,
14177         vkMemReq.memoryTypeBits,
14178         requiresDedicatedAllocation ? 1 : 0,
14179         prefersDedicatedAllocation ? 1 : 0,
14180         createInfo.flags,
14181         createInfo.usage,
14182         createInfo.requiredFlags,
14183         createInfo.preferredFlags,
14184         createInfo.memoryTypeBits,
14185         createInfo.pool,
14186         allocation,
14187         userDataStr.GetString());
14188     Flush();
14189 }
14190 
RecordFreeMemory(uint32_t frameIndex,VmaAllocation allocation)14191 void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
14192     VmaAllocation allocation)
14193 {
14194     CallParams callParams;
14195     GetBasicParams(callParams);
14196 
14197     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14198     fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
14199         allocation);
14200     Flush();
14201 }
14202 
RecordFreeMemoryPages(uint32_t frameIndex,uint64_t allocationCount,const VmaAllocation * pAllocations)14203 void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
14204     uint64_t allocationCount,
14205     const VmaAllocation* pAllocations)
14206 {
14207     CallParams callParams;
14208     GetBasicParams(callParams);
14209 
14210     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14211     fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
14212     PrintPointerList(allocationCount, pAllocations);
14213     fprintf(m_File, "\n");
14214     Flush();
14215 }
14216 
RecordSetAllocationUserData(uint32_t frameIndex,VmaAllocation allocation,const void * pUserData)14217 void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
14218     VmaAllocation allocation,
14219     const void* pUserData)
14220 {
14221     CallParams callParams;
14222     GetBasicParams(callParams);
14223 
14224     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14225     UserDataString userDataStr(
14226         allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
14227         pUserData);
14228     fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
14229         allocation,
14230         userDataStr.GetString());
14231     Flush();
14232 }
14233 
RecordCreateLostAllocation(uint32_t frameIndex,VmaAllocation allocation)14234 void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
14235     VmaAllocation allocation)
14236 {
14237     CallParams callParams;
14238     GetBasicParams(callParams);
14239 
14240     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14241     fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
14242         allocation);
14243     Flush();
14244 }
14245 
RecordMapMemory(uint32_t frameIndex,VmaAllocation allocation)14246 void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
14247     VmaAllocation allocation)
14248 {
14249     CallParams callParams;
14250     GetBasicParams(callParams);
14251 
14252     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14253     fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
14254         allocation);
14255     Flush();
14256 }
14257 
RecordUnmapMemory(uint32_t frameIndex,VmaAllocation allocation)14258 void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
14259     VmaAllocation allocation)
14260 {
14261     CallParams callParams;
14262     GetBasicParams(callParams);
14263 
14264     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14265     fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
14266         allocation);
14267     Flush();
14268 }
14269 
RecordFlushAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)14270 void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
14271     VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
14272 {
14273     CallParams callParams;
14274     GetBasicParams(callParams);
14275 
14276     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14277     fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
14278         allocation,
14279         offset,
14280         size);
14281     Flush();
14282 }
14283 
RecordInvalidateAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)14284 void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
14285     VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
14286 {
14287     CallParams callParams;
14288     GetBasicParams(callParams);
14289 
14290     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14291     fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
14292         allocation,
14293         offset,
14294         size);
14295     Flush();
14296 }
14297 
RecordCreateBuffer(uint32_t frameIndex,const VkBufferCreateInfo & bufCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)14298 void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
14299     const VkBufferCreateInfo& bufCreateInfo,
14300     const VmaAllocationCreateInfo& allocCreateInfo,
14301     VmaAllocation allocation)
14302 {
14303     CallParams callParams;
14304     GetBasicParams(callParams);
14305 
14306     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14307     UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
14308     fprintf(m_File, "%u,%.3f,%u,vmaCreateBuffer,%u,%llu,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
14309         bufCreateInfo.flags,
14310         bufCreateInfo.size,
14311         bufCreateInfo.usage,
14312         bufCreateInfo.sharingMode,
14313         allocCreateInfo.flags,
14314         allocCreateInfo.usage,
14315         allocCreateInfo.requiredFlags,
14316         allocCreateInfo.preferredFlags,
14317         allocCreateInfo.memoryTypeBits,
14318         allocCreateInfo.pool,
14319         allocation,
14320         userDataStr.GetString());
14321     Flush();
14322 }
14323 
RecordCreateImage(uint32_t frameIndex,const VkImageCreateInfo & imageCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)14324 void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
14325     const VkImageCreateInfo& imageCreateInfo,
14326     const VmaAllocationCreateInfo& allocCreateInfo,
14327     VmaAllocation allocation)
14328 {
14329     CallParams callParams;
14330     GetBasicParams(callParams);
14331 
14332     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14333     UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
14334     fprintf(m_File, "%u,%.3f,%u,vmaCreateImage,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
14335         imageCreateInfo.flags,
14336         imageCreateInfo.imageType,
14337         imageCreateInfo.format,
14338         imageCreateInfo.extent.width,
14339         imageCreateInfo.extent.height,
14340         imageCreateInfo.extent.depth,
14341         imageCreateInfo.mipLevels,
14342         imageCreateInfo.arrayLayers,
14343         imageCreateInfo.samples,
14344         imageCreateInfo.tiling,
14345         imageCreateInfo.usage,
14346         imageCreateInfo.sharingMode,
14347         imageCreateInfo.initialLayout,
14348         allocCreateInfo.flags,
14349         allocCreateInfo.usage,
14350         allocCreateInfo.requiredFlags,
14351         allocCreateInfo.preferredFlags,
14352         allocCreateInfo.memoryTypeBits,
14353         allocCreateInfo.pool,
14354         allocation,
14355         userDataStr.GetString());
14356     Flush();
14357 }
14358 
RecordDestroyBuffer(uint32_t frameIndex,VmaAllocation allocation)14359 void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
14360     VmaAllocation allocation)
14361 {
14362     CallParams callParams;
14363     GetBasicParams(callParams);
14364 
14365     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14366     fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
14367         allocation);
14368     Flush();
14369 }
14370 
RecordDestroyImage(uint32_t frameIndex,VmaAllocation allocation)14371 void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
14372     VmaAllocation allocation)
14373 {
14374     CallParams callParams;
14375     GetBasicParams(callParams);
14376 
14377     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14378     fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
14379         allocation);
14380     Flush();
14381 }
14382 
RecordTouchAllocation(uint32_t frameIndex,VmaAllocation allocation)14383 void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
14384     VmaAllocation allocation)
14385 {
14386     CallParams callParams;
14387     GetBasicParams(callParams);
14388 
14389     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14390     fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
14391         allocation);
14392     Flush();
14393 }
14394 
RecordGetAllocationInfo(uint32_t frameIndex,VmaAllocation allocation)14395 void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
14396     VmaAllocation allocation)
14397 {
14398     CallParams callParams;
14399     GetBasicParams(callParams);
14400 
14401     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14402     fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
14403         allocation);
14404     Flush();
14405 }
14406 
RecordMakePoolAllocationsLost(uint32_t frameIndex,VmaPool pool)14407 void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
14408     VmaPool pool)
14409 {
14410     CallParams callParams;
14411     GetBasicParams(callParams);
14412 
14413     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14414     fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
14415         pool);
14416     Flush();
14417 }
14418 
RecordDefragmentationBegin(uint32_t frameIndex,const VmaDefragmentationInfo2 & info,VmaDefragmentationContext ctx)14419 void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
14420     const VmaDefragmentationInfo2& info,
14421     VmaDefragmentationContext ctx)
14422 {
14423     CallParams callParams;
14424     GetBasicParams(callParams);
14425 
14426     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14427     fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
14428         info.flags);
14429     PrintPointerList(info.allocationCount, info.pAllocations);
14430     fprintf(m_File, ",");
14431     PrintPointerList(info.poolCount, info.pPools);
14432     fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
14433         info.maxCpuBytesToMove,
14434         info.maxCpuAllocationsToMove,
14435         info.maxGpuBytesToMove,
14436         info.maxGpuAllocationsToMove,
14437         info.commandBuffer,
14438         ctx);
14439     Flush();
14440 }
14441 
RecordDefragmentationEnd(uint32_t frameIndex,VmaDefragmentationContext ctx)14442 void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
14443     VmaDefragmentationContext ctx)
14444 {
14445     CallParams callParams;
14446     GetBasicParams(callParams);
14447 
14448     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14449     fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
14450         ctx);
14451     Flush();
14452 }
14453 
RecordSetPoolName(uint32_t frameIndex,VmaPool pool,const char * name)14454 void VmaRecorder::RecordSetPoolName(uint32_t frameIndex,
14455     VmaPool pool,
14456     const char* name)
14457 {
14458     CallParams callParams;
14459     GetBasicParams(callParams);
14460 
14461     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14462     fprintf(m_File, "%u,%.3f,%u,vmaSetPoolName,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
14463         pool, name != VMA_NULL ? name : "");
14464     Flush();
14465 }
14466 
UserDataString(VmaAllocationCreateFlags allocFlags,const void * pUserData)14467 VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
14468 {
14469     if(pUserData != VMA_NULL)
14470     {
14471         if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
14472         {
14473             m_Str = (const char*)pUserData;
14474         }
14475         else
14476         {
14477             // If VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is not specified, convert the string's memory address to a string and store it.
14478             snprintf(m_PtrStr, 17, "%p", pUserData);
14479             m_Str = m_PtrStr;
14480         }
14481     }
14482     else
14483     {
14484         m_Str = "";
14485     }
14486 }
14487 
WriteConfiguration(const VkPhysicalDeviceProperties & devProps,const VkPhysicalDeviceMemoryProperties & memProps,uint32_t vulkanApiVersion,bool dedicatedAllocationExtensionEnabled,bool bindMemory2ExtensionEnabled,bool memoryBudgetExtensionEnabled,bool deviceCoherentMemoryExtensionEnabled)14488 void VmaRecorder::WriteConfiguration(
14489     const VkPhysicalDeviceProperties& devProps,
14490     const VkPhysicalDeviceMemoryProperties& memProps,
14491     uint32_t vulkanApiVersion,
14492     bool dedicatedAllocationExtensionEnabled,
14493     bool bindMemory2ExtensionEnabled,
14494     bool memoryBudgetExtensionEnabled,
14495     bool deviceCoherentMemoryExtensionEnabled)
14496 {
14497     fprintf(m_File, "Config,Begin\n");
14498 
14499     fprintf(m_File, "VulkanApiVersion,%u,%u\n", VK_VERSION_MAJOR(vulkanApiVersion), VK_VERSION_MINOR(vulkanApiVersion));
14500 
14501     fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
14502     fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
14503     fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
14504     fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
14505     fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
14506     fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
14507 
14508     fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
14509     fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
14510     fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
14511 
14512     fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
14513     for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
14514     {
14515         fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
14516         fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
14517     }
14518     fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
14519     for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
14520     {
14521         fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
14522         fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
14523     }
14524 
14525     fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
14526     fprintf(m_File, "Extension,VK_KHR_bind_memory2,%u\n", bindMemory2ExtensionEnabled ? 1 : 0);
14527     fprintf(m_File, "Extension,VK_EXT_memory_budget,%u\n", memoryBudgetExtensionEnabled ? 1 : 0);
14528     fprintf(m_File, "Extension,VK_AMD_device_coherent_memory,%u\n", deviceCoherentMemoryExtensionEnabled ? 1 : 0);
14529 
14530     fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
14531     fprintf(m_File, "Macro,VMA_MIN_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_MIN_ALIGNMENT);
14532     fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
14533     fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
14534     fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
14535     fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
14536     fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
14537     fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
14538     fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
14539 
14540     fprintf(m_File, "Config,End\n");
14541 }
14542 
GetBasicParams(CallParams & outParams)14543 void VmaRecorder::GetBasicParams(CallParams& outParams)
14544 {
14545     #if defined(_WIN32)
14546         outParams.threadId = GetCurrentThreadId();
14547     #else
14548         // Use C++11 features to get thread id and convert it to uint32_t.
14549         // There is room for optimization since sstream is quite slow.
14550         // Is there a better way to convert std::this_thread::get_id() to uint32_t?
14551         std::thread::id thread_id = std::this_thread::get_id();
14552         std::stringstream thread_id_to_string_converter;
14553         thread_id_to_string_converter << thread_id;
14554         std::string thread_id_as_string = thread_id_to_string_converter.str();
14555         outParams.threadId = static_cast<uint32_t>(std::stoi(thread_id_as_string.c_str()));
14556     #endif
14557 
14558     auto current_time = std::chrono::high_resolution_clock::now();
14559 
14560     outParams.time = std::chrono::duration<double, std::chrono::seconds::period>(current_time - m_RecordingStartTime).count();
14561 }
14562 
PrintPointerList(uint64_t count,const VmaAllocation * pItems)14563 void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
14564 {
14565     if(count)
14566     {
14567         fprintf(m_File, "%p", pItems[0]);
14568         for(uint64_t i = 1; i < count; ++i)
14569         {
14570             fprintf(m_File, " %p", pItems[i]);
14571         }
14572     }
14573 }
14574 
Flush()14575 void VmaRecorder::Flush()
14576 {
14577     if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
14578     {
14579         fflush(m_File);
14580     }
14581 }
14582 
14583 #endif // #if VMA_RECORDING_ENABLED
14584 
14585 ////////////////////////////////////////////////////////////////////////////////
14586 // VmaAllocationObjectAllocator
14587 
VmaAllocationObjectAllocator(const VkAllocationCallbacks * pAllocationCallbacks)14588 VmaAllocationObjectAllocator::VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks) :
14589     m_Allocator(pAllocationCallbacks, 1024)
14590 {
14591 }
14592 
Allocate(Types &&...args)14593 template<typename... Types> VmaAllocation VmaAllocationObjectAllocator::Allocate(Types&&... args)
14594 {
14595     VmaMutexLock mutexLock(m_Mutex);
14596     return m_Allocator.Alloc<Types...>(std::forward<Types>(args)...);
14597 }
14598 
Free(VmaAllocation hAlloc)14599 void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc)
14600 {
14601     VmaMutexLock mutexLock(m_Mutex);
14602     m_Allocator.Free(hAlloc);
14603 }
14604 
14605 ////////////////////////////////////////////////////////////////////////////////
14606 // VmaAllocator_T
14607 
VmaAllocator_T(const VmaAllocatorCreateInfo * pCreateInfo)14608 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
14609     m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
14610     m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0),
14611     m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
14612     m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0),
14613     m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0),
14614     m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0),
14615     m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0),
14616     m_UseExtMemoryPriority((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT) != 0),
14617     m_hDevice(pCreateInfo->device),
14618     m_hInstance(pCreateInfo->instance),
14619     m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
14620     m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
14621         *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
14622     m_AllocationObjectAllocator(&m_AllocationCallbacks),
14623     m_HeapSizeLimitMask(0),
14624     m_DeviceMemoryCount(0),
14625     m_PreferredLargeHeapBlockSize(0),
14626     m_PhysicalDevice(pCreateInfo->physicalDevice),
14627     m_CurrentFrameIndex(0),
14628     m_GpuDefragmentationMemoryTypeBits(UINT32_MAX),
14629     m_NextPoolId(0),
14630     m_GlobalMemoryTypeBits(UINT32_MAX)
14631 #if VMA_RECORDING_ENABLED
14632     ,m_pRecorder(VMA_NULL)
14633 #endif
14634 {
14635     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
14636     {
14637         m_UseKhrDedicatedAllocation = false;
14638         m_UseKhrBindMemory2 = false;
14639     }
14640 
14641     if(VMA_DEBUG_DETECT_CORRUPTION)
14642     {
14643         // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
14644         VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
14645     }
14646 
14647     VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance);
14648 
14649     if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
14650     {
14651 #if !(VMA_DEDICATED_ALLOCATION)
14652         if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
14653         {
14654             VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
14655         }
14656 #endif
14657 #if !(VMA_BIND_MEMORY2)
14658         if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0)
14659         {
14660             VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros.");
14661         }
14662 #endif
14663     }
14664 #if !(VMA_MEMORY_BUDGET)
14665     if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0)
14666     {
14667         VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros.");
14668     }
14669 #endif
14670 #if !(VMA_BUFFER_DEVICE_ADDRESS)
14671     if(m_UseKhrBufferDeviceAddress)
14672     {
14673         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.");
14674     }
14675 #endif
14676 #if VMA_VULKAN_VERSION < 1002000
14677     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0))
14678     {
14679         VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros.");
14680     }
14681 #endif
14682 #if VMA_VULKAN_VERSION < 1001000
14683     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
14684     {
14685         VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros.");
14686     }
14687 #endif
14688 #if !(VMA_MEMORY_PRIORITY)
14689     if(m_UseExtMemoryPriority)
14690     {
14691         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.");
14692     }
14693 #endif
14694 
14695     memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
14696     memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
14697     memset(&m_MemProps, 0, sizeof(m_MemProps));
14698 
14699     memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
14700     memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions));
14701 
14702 #if VMA_EXTERNAL_MEMORY
14703     memset(&m_TypeExternalMemoryHandleTypes, 0, sizeof(m_TypeExternalMemoryHandleTypes));
14704 #endif // #if VMA_EXTERNAL_MEMORY
14705 
14706     if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
14707     {
14708         m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData;
14709         m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
14710         m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
14711     }
14712 
14713     ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
14714 
14715     (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
14716     (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
14717 
14718     VMA_ASSERT(VmaIsPow2(VMA_MIN_ALIGNMENT));
14719     VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
14720     VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
14721     VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
14722 
14723     m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
14724         pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
14725 
14726     m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits();
14727 
14728 #if VMA_EXTERNAL_MEMORY
14729     if(pCreateInfo->pTypeExternalMemoryHandleTypes != VMA_NULL)
14730     {
14731         memcpy(m_TypeExternalMemoryHandleTypes, pCreateInfo->pTypeExternalMemoryHandleTypes,
14732             sizeof(VkExternalMemoryHandleTypeFlagsKHR) * GetMemoryTypeCount());
14733     }
14734 #endif // #if VMA_EXTERNAL_MEMORY
14735 
14736     if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
14737     {
14738         for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
14739         {
14740             const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
14741             if(limit != VK_WHOLE_SIZE)
14742             {
14743                 m_HeapSizeLimitMask |= 1u << heapIndex;
14744                 if(limit < m_MemProps.memoryHeaps[heapIndex].size)
14745                 {
14746                     m_MemProps.memoryHeaps[heapIndex].size = limit;
14747                 }
14748             }
14749         }
14750     }
14751 
14752     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14753     {
14754         const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
14755 
14756         m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
14757             this,
14758             VK_NULL_HANDLE, // hParentPool
14759             memTypeIndex,
14760             preferredBlockSize,
14761             0,
14762             SIZE_MAX,
14763             GetBufferImageGranularity(),
14764             pCreateInfo->frameInUseCount,
14765             false, // explicitBlockSize
14766             false, // linearAlgorithm
14767             0.5f, // priority (0.5 is the default per Vulkan spec)
14768             GetMemoryTypeMinAlignment(memTypeIndex), // minAllocationAlignment
14769             VMA_NULL); // // pMemoryAllocateNext
14770         // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
14771         // becase minBlockCount is 0.
14772     }
14773 }
14774 
Init(const VmaAllocatorCreateInfo * pCreateInfo)14775 VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
14776 {
14777     VkResult res = VK_SUCCESS;
14778 
14779     if(pCreateInfo->pRecordSettings != VMA_NULL &&
14780         !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
14781     {
14782 #if VMA_RECORDING_ENABLED
14783         m_pRecorder = vma_new(this, VmaRecorder)();
14784         res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
14785         if(res != VK_SUCCESS)
14786         {
14787             return res;
14788         }
14789         m_pRecorder->WriteConfiguration(
14790             m_PhysicalDeviceProperties,
14791             m_MemProps,
14792             m_VulkanApiVersion,
14793             m_UseKhrDedicatedAllocation,
14794             m_UseKhrBindMemory2,
14795             m_UseExtMemoryBudget,
14796             m_UseAmdDeviceCoherentMemory);
14797         m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
14798 #else
14799         VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
14800         return VK_ERROR_FEATURE_NOT_PRESENT;
14801 #endif
14802     }
14803 
14804 #if VMA_MEMORY_BUDGET
14805     if(m_UseExtMemoryBudget)
14806     {
14807         UpdateVulkanBudget();
14808     }
14809 #endif // #if VMA_MEMORY_BUDGET
14810 
14811     return res;
14812 }
14813 
~VmaAllocator_T()14814 VmaAllocator_T::~VmaAllocator_T()
14815 {
14816 #if VMA_RECORDING_ENABLED
14817     if(m_pRecorder != VMA_NULL)
14818     {
14819         m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
14820         vma_delete(this, m_pRecorder);
14821     }
14822 #endif
14823 
14824     VMA_ASSERT(m_Pools.IsEmpty());
14825 
14826     for(size_t memTypeIndex = GetMemoryTypeCount(); memTypeIndex--; )
14827     {
14828         if(!m_DedicatedAllocations[memTypeIndex].IsEmpty())
14829         {
14830             VMA_ASSERT(0 && "Unfreed dedicated allocations found.");
14831         }
14832 
14833         vma_delete(this, m_pBlockVectors[memTypeIndex]);
14834     }
14835 }
14836 
ImportVulkanFunctions(const VmaVulkanFunctions * pVulkanFunctions)14837 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
14838 {
14839 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
14840     ImportVulkanFunctions_Static();
14841 #endif
14842 
14843     if(pVulkanFunctions != VMA_NULL)
14844     {
14845         ImportVulkanFunctions_Custom(pVulkanFunctions);
14846     }
14847 
14848 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
14849     ImportVulkanFunctions_Dynamic();
14850 #endif
14851 
14852     ValidateVulkanFunctions();
14853 }
14854 
14855 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
14856 
ImportVulkanFunctions_Static()14857 void VmaAllocator_T::ImportVulkanFunctions_Static()
14858 {
14859     // Vulkan 1.0
14860     m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties;
14861     m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties;
14862     m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
14863     m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory;
14864     m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory;
14865     m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory;
14866     m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges;
14867     m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges;
14868     m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory;
14869     m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory;
14870     m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements;
14871     m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements;
14872     m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer;
14873     m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer;
14874     m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage;
14875     m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage;
14876     m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer;
14877 
14878     // Vulkan 1.1
14879 #if VMA_VULKAN_VERSION >= 1001000
14880     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
14881     {
14882         m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2;
14883         m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2;
14884         m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2;
14885         m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2;
14886         m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2;
14887     }
14888 #endif
14889 }
14890 
14891 #endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
14892 
ImportVulkanFunctions_Custom(const VmaVulkanFunctions * pVulkanFunctions)14893 void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions)
14894 {
14895     VMA_ASSERT(pVulkanFunctions != VMA_NULL);
14896 
14897 #define VMA_COPY_IF_NOT_NULL(funcName) \
14898     if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
14899 
14900     VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
14901     VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
14902     VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
14903     VMA_COPY_IF_NOT_NULL(vkFreeMemory);
14904     VMA_COPY_IF_NOT_NULL(vkMapMemory);
14905     VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
14906     VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
14907     VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
14908     VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
14909     VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
14910     VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
14911     VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
14912     VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
14913     VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
14914     VMA_COPY_IF_NOT_NULL(vkCreateImage);
14915     VMA_COPY_IF_NOT_NULL(vkDestroyImage);
14916     VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
14917 
14918 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
14919     VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
14920     VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
14921 #endif
14922 
14923 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
14924     VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR);
14925     VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR);
14926 #endif
14927 
14928 #if VMA_MEMORY_BUDGET
14929     VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR);
14930 #endif
14931 
14932 #undef VMA_COPY_IF_NOT_NULL
14933 }
14934 
14935 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
14936 
ImportVulkanFunctions_Dynamic()14937 void VmaAllocator_T::ImportVulkanFunctions_Dynamic()
14938 {
14939 #define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \
14940     if(m_VulkanFunctions.memberName == VMA_NULL) \
14941         m_VulkanFunctions.memberName = \
14942             (functionPointerType)vkGetInstanceProcAddr(m_hInstance, functionNameString);
14943 #define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \
14944     if(m_VulkanFunctions.memberName == VMA_NULL) \
14945         m_VulkanFunctions.memberName = \
14946             (functionPointerType)vkGetDeviceProcAddr(m_hDevice, functionNameString);
14947 
14948     VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");
14949     VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties");
14950     VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory");
14951     VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory");
14952     VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory");
14953     VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory");
14954     VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges");
14955     VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges");
14956     VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory");
14957     VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory");
14958     VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements");
14959     VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements");
14960     VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer");
14961     VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer");
14962     VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage");
14963     VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage");
14964     VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer");
14965 
14966 #if VMA_VULKAN_VERSION >= 1001000
14967     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
14968     {
14969         VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2, "vkGetBufferMemoryRequirements2");
14970         VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2, "vkGetImageMemoryRequirements2");
14971         VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2, "vkBindBufferMemory2");
14972         VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2, "vkBindImageMemory2");
14973         VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2, "vkGetPhysicalDeviceMemoryProperties2");
14974     }
14975 #endif
14976 
14977 #if VMA_DEDICATED_ALLOCATION
14978     if(m_UseKhrDedicatedAllocation)
14979     {
14980         VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR");
14981         VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR");
14982     }
14983 #endif
14984 
14985 #if VMA_BIND_MEMORY2
14986     if(m_UseKhrBindMemory2)
14987     {
14988         VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR");
14989         VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR");
14990     }
14991 #endif // #if VMA_BIND_MEMORY2
14992 
14993 #if VMA_MEMORY_BUDGET
14994     if(m_UseExtMemoryBudget)
14995     {
14996         VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");
14997     }
14998 #endif // #if VMA_MEMORY_BUDGET
14999 
15000 #undef VMA_FETCH_DEVICE_FUNC
15001 #undef VMA_FETCH_INSTANCE_FUNC
15002 }
15003 
15004 #endif // #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
15005 
ValidateVulkanFunctions()15006 void VmaAllocator_T::ValidateVulkanFunctions()
15007 {
15008     VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
15009     VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
15010     VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
15011     VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
15012     VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
15013     VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
15014     VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
15015     VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
15016     VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
15017     VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
15018     VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
15019     VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
15020     VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
15021     VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
15022     VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
15023     VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
15024     VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
15025 
15026 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15027     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation)
15028     {
15029         VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
15030         VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
15031     }
15032 #endif
15033 
15034 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
15035     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2)
15036     {
15037         VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL);
15038         VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL);
15039     }
15040 #endif
15041 
15042 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
15043     if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15044     {
15045         VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);
15046     }
15047 #endif
15048 }
15049 
CalcPreferredBlockSize(uint32_t memTypeIndex)15050 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
15051 {
15052     const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
15053     const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
15054     const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
15055     return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32);
15056 }
15057 
AllocateMemoryOfType(VkDeviceSize size,VkDeviceSize alignment,bool dedicatedAllocation,VkBuffer dedicatedBuffer,VkBufferUsageFlags dedicatedBufferUsage,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,uint32_t memTypeIndex,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)15058 VkResult VmaAllocator_T::AllocateMemoryOfType(
15059     VkDeviceSize size,
15060     VkDeviceSize alignment,
15061     bool dedicatedAllocation,
15062     VkBuffer dedicatedBuffer,
15063     VkBufferUsageFlags dedicatedBufferUsage,
15064     VkImage dedicatedImage,
15065     const VmaAllocationCreateInfo& createInfo,
15066     uint32_t memTypeIndex,
15067     VmaSuballocationType suballocType,
15068     size_t allocationCount,
15069     VmaAllocation* pAllocations)
15070 {
15071     VMA_ASSERT(pAllocations != VMA_NULL);
15072     VMA_DEBUG_LOG("  AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size);
15073 
15074     VmaAllocationCreateInfo finalCreateInfo = createInfo;
15075 
15076     // If memory type is not HOST_VISIBLE, disable MAPPED.
15077     if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
15078         (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15079     {
15080         finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
15081     }
15082     // If memory is lazily allocated, it should be always dedicated.
15083     if(finalCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED)
15084     {
15085         finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
15086     }
15087 
15088     VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
15089     VMA_ASSERT(blockVector);
15090 
15091     const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
15092     bool preferDedicatedMemory =
15093         VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
15094         dedicatedAllocation ||
15095         // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
15096         size > preferredBlockSize / 2;
15097 
15098     if(preferDedicatedMemory &&
15099         (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
15100         finalCreateInfo.pool == VK_NULL_HANDLE)
15101     {
15102         finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
15103     }
15104 
15105     if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
15106     {
15107         if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
15108         {
15109             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15110         }
15111         else
15112         {
15113             return AllocateDedicatedMemory(
15114                 size,
15115                 suballocType,
15116                 memTypeIndex,
15117                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
15118                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
15119                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
15120                 finalCreateInfo.pUserData,
15121                 finalCreateInfo.priority,
15122                 dedicatedBuffer,
15123                 dedicatedBufferUsage,
15124                 dedicatedImage,
15125                 allocationCount,
15126                 pAllocations);
15127         }
15128     }
15129     else
15130     {
15131         VkResult res = blockVector->Allocate(
15132             m_CurrentFrameIndex.load(),
15133             size,
15134             alignment,
15135             finalCreateInfo,
15136             suballocType,
15137             allocationCount,
15138             pAllocations);
15139         if(res == VK_SUCCESS)
15140         {
15141             return res;
15142         }
15143 
15144         // 5. Try dedicated memory.
15145         if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
15146         {
15147             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15148         }
15149 
15150         // Protection against creating each allocation as dedicated when we reach or exceed heap size/budget,
15151         // which can quickly deplete maxMemoryAllocationCount: Don't try dedicated allocations when above
15152         // 3/4 of the maximum allocation count.
15153         if(m_DeviceMemoryCount.load() > m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount * 3 / 4)
15154         {
15155             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15156         }
15157 
15158         res = AllocateDedicatedMemory(
15159             size,
15160             suballocType,
15161             memTypeIndex,
15162             (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
15163             (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
15164             (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
15165             finalCreateInfo.pUserData,
15166             finalCreateInfo.priority,
15167             dedicatedBuffer,
15168             dedicatedBufferUsage,
15169             dedicatedImage,
15170             allocationCount,
15171             pAllocations);
15172         if(res == VK_SUCCESS)
15173         {
15174             // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
15175             VMA_DEBUG_LOG("    Allocated as DedicatedMemory");
15176             return VK_SUCCESS;
15177         }
15178         else
15179         {
15180             // Everything failed: Return error code.
15181             VMA_DEBUG_LOG("    vkAllocateMemory FAILED");
15182             return res;
15183         }
15184     }
15185 }
15186 
AllocateDedicatedMemory(VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,bool withinBudget,bool map,bool isUserDataString,void * pUserData,float priority,VkBuffer dedicatedBuffer,VkBufferUsageFlags dedicatedBufferUsage,VkImage dedicatedImage,size_t allocationCount,VmaAllocation * pAllocations)15187 VkResult VmaAllocator_T::AllocateDedicatedMemory(
15188     VkDeviceSize size,
15189     VmaSuballocationType suballocType,
15190     uint32_t memTypeIndex,
15191     bool withinBudget,
15192     bool map,
15193     bool isUserDataString,
15194     void* pUserData,
15195     float priority,
15196     VkBuffer dedicatedBuffer,
15197     VkBufferUsageFlags dedicatedBufferUsage,
15198     VkImage dedicatedImage,
15199     size_t allocationCount,
15200     VmaAllocation* pAllocations)
15201 {
15202     VMA_ASSERT(allocationCount > 0 && pAllocations);
15203 
15204     if(withinBudget)
15205     {
15206         const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
15207         VmaBudget heapBudget = {};
15208         GetBudget(&heapBudget, heapIndex, 1);
15209         if(heapBudget.usage + size * allocationCount > heapBudget.budget)
15210         {
15211             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15212         }
15213     }
15214 
15215     VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
15216     allocInfo.memoryTypeIndex = memTypeIndex;
15217     allocInfo.allocationSize = size;
15218 
15219 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15220     VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
15221     if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15222     {
15223         if(dedicatedBuffer != VK_NULL_HANDLE)
15224         {
15225             VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
15226             dedicatedAllocInfo.buffer = dedicatedBuffer;
15227             VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
15228         }
15229         else if(dedicatedImage != VK_NULL_HANDLE)
15230         {
15231             dedicatedAllocInfo.image = dedicatedImage;
15232             VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
15233         }
15234     }
15235 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15236 
15237 #if VMA_BUFFER_DEVICE_ADDRESS
15238     VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
15239     if(m_UseKhrBufferDeviceAddress)
15240     {
15241         bool canContainBufferWithDeviceAddress = true;
15242         if(dedicatedBuffer != VK_NULL_HANDLE)
15243         {
15244             canContainBufferWithDeviceAddress = dedicatedBufferUsage == UINT32_MAX || // Usage flags unknown
15245                 (dedicatedBufferUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) != 0;
15246         }
15247         else if(dedicatedImage != VK_NULL_HANDLE)
15248         {
15249             canContainBufferWithDeviceAddress = false;
15250         }
15251         if(canContainBufferWithDeviceAddress)
15252         {
15253             allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
15254             VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
15255         }
15256     }
15257 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
15258 
15259 #if VMA_MEMORY_PRIORITY
15260     VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };
15261     if(m_UseExtMemoryPriority)
15262     {
15263         priorityInfo.priority = priority;
15264         VmaPnextChainPushFront(&allocInfo, &priorityInfo);
15265     }
15266 #endif // #if VMA_MEMORY_PRIORITY
15267 
15268 #if VMA_EXTERNAL_MEMORY
15269     // Attach VkExportMemoryAllocateInfoKHR if necessary.
15270     VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR };
15271     exportMemoryAllocInfo.handleTypes = GetExternalMemoryHandleTypeFlags(memTypeIndex);
15272     if(exportMemoryAllocInfo.handleTypes != 0)
15273     {
15274         VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo);
15275     }
15276 #endif // #if VMA_EXTERNAL_MEMORY
15277 
15278     size_t allocIndex;
15279     VkResult res = VK_SUCCESS;
15280     for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
15281     {
15282         res = AllocateDedicatedMemoryPage(
15283             size,
15284             suballocType,
15285             memTypeIndex,
15286             allocInfo,
15287             map,
15288             isUserDataString,
15289             pUserData,
15290             pAllocations + allocIndex);
15291         if(res != VK_SUCCESS)
15292         {
15293             break;
15294         }
15295     }
15296 
15297     if(res == VK_SUCCESS)
15298     {
15299         // Register them in m_DedicatedAllocations.
15300         {
15301             VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
15302             DedicatedAllocationLinkedList& dedicatedAllocations = m_DedicatedAllocations[memTypeIndex];
15303             for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
15304             {
15305                 dedicatedAllocations.PushBack(pAllocations[allocIndex]);
15306             }
15307         }
15308 
15309         VMA_DEBUG_LOG("    Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
15310     }
15311     else
15312     {
15313         // Free all already created allocations.
15314         while(allocIndex--)
15315         {
15316             VmaAllocation currAlloc = pAllocations[allocIndex];
15317             VkDeviceMemory hMemory = currAlloc->GetMemory();
15318 
15319             /*
15320             There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
15321             before vkFreeMemory.
15322 
15323             if(currAlloc->GetMappedData() != VMA_NULL)
15324             {
15325                 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
15326             }
15327             */
15328 
15329             FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
15330             m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize());
15331             currAlloc->SetUserData(this, VMA_NULL);
15332             m_AllocationObjectAllocator.Free(currAlloc);
15333         }
15334 
15335         memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
15336     }
15337 
15338     return res;
15339 }
15340 
AllocateDedicatedMemoryPage(VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,const VkMemoryAllocateInfo & allocInfo,bool map,bool isUserDataString,void * pUserData,VmaAllocation * pAllocation)15341 VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
15342     VkDeviceSize size,
15343     VmaSuballocationType suballocType,
15344     uint32_t memTypeIndex,
15345     const VkMemoryAllocateInfo& allocInfo,
15346     bool map,
15347     bool isUserDataString,
15348     void* pUserData,
15349     VmaAllocation* pAllocation)
15350 {
15351     VkDeviceMemory hMemory = VK_NULL_HANDLE;
15352     VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
15353     if(res < 0)
15354     {
15355         VMA_DEBUG_LOG("    vkAllocateMemory FAILED");
15356         return res;
15357     }
15358 
15359     void* pMappedData = VMA_NULL;
15360     if(map)
15361     {
15362         res = (*m_VulkanFunctions.vkMapMemory)(
15363             m_hDevice,
15364             hMemory,
15365             0,
15366             VK_WHOLE_SIZE,
15367             0,
15368             &pMappedData);
15369         if(res < 0)
15370         {
15371             VMA_DEBUG_LOG("    vkMapMemory FAILED");
15372             FreeVulkanMemory(memTypeIndex, size, hMemory);
15373             return res;
15374         }
15375     }
15376 
15377     *pAllocation = m_AllocationObjectAllocator.Allocate(m_CurrentFrameIndex.load(), isUserDataString);
15378     (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
15379     (*pAllocation)->SetUserData(this, pUserData);
15380     m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size);
15381     if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
15382     {
15383         FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
15384     }
15385 
15386     return VK_SUCCESS;
15387 }
15388 
GetBufferMemoryRequirements(VkBuffer hBuffer,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)15389 void VmaAllocator_T::GetBufferMemoryRequirements(
15390     VkBuffer hBuffer,
15391     VkMemoryRequirements& memReq,
15392     bool& requiresDedicatedAllocation,
15393     bool& prefersDedicatedAllocation) const
15394 {
15395 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15396     if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15397     {
15398         VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
15399         memReqInfo.buffer = hBuffer;
15400 
15401         VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
15402 
15403         VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
15404         VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
15405 
15406         (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
15407 
15408         memReq = memReq2.memoryRequirements;
15409         requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
15410         prefersDedicatedAllocation  = (memDedicatedReq.prefersDedicatedAllocation  != VK_FALSE);
15411     }
15412     else
15413 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15414     {
15415         (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
15416         requiresDedicatedAllocation = false;
15417         prefersDedicatedAllocation  = false;
15418     }
15419 }
15420 
GetImageMemoryRequirements(VkImage hImage,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)15421 void VmaAllocator_T::GetImageMemoryRequirements(
15422     VkImage hImage,
15423     VkMemoryRequirements& memReq,
15424     bool& requiresDedicatedAllocation,
15425     bool& prefersDedicatedAllocation) const
15426 {
15427 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15428     if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15429     {
15430         VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
15431         memReqInfo.image = hImage;
15432 
15433         VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
15434 
15435         VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
15436         VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
15437 
15438         (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
15439 
15440         memReq = memReq2.memoryRequirements;
15441         requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
15442         prefersDedicatedAllocation  = (memDedicatedReq.prefersDedicatedAllocation  != VK_FALSE);
15443     }
15444     else
15445 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15446     {
15447         (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
15448         requiresDedicatedAllocation = false;
15449         prefersDedicatedAllocation  = false;
15450     }
15451 }
15452 
AllocateMemory(const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,VkBuffer dedicatedBuffer,VkBufferUsageFlags dedicatedBufferUsage,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)15453 VkResult VmaAllocator_T::AllocateMemory(
15454     const VkMemoryRequirements& vkMemReq,
15455     bool requiresDedicatedAllocation,
15456     bool prefersDedicatedAllocation,
15457     VkBuffer dedicatedBuffer,
15458     VkBufferUsageFlags dedicatedBufferUsage,
15459     VkImage dedicatedImage,
15460     const VmaAllocationCreateInfo& createInfo,
15461     VmaSuballocationType suballocType,
15462     size_t allocationCount,
15463     VmaAllocation* pAllocations)
15464 {
15465     memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
15466 
15467     VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
15468 
15469     if(vkMemReq.size == 0)
15470     {
15471         return VK_ERROR_INITIALIZATION_FAILED;
15472     }
15473     if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
15474         (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
15475     {
15476         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
15477         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15478     }
15479     if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
15480         (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
15481     {
15482         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
15483         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15484     }
15485     if(requiresDedicatedAllocation)
15486     {
15487         if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
15488         {
15489             VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
15490             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15491         }
15492         if(createInfo.pool != VK_NULL_HANDLE)
15493         {
15494             VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
15495             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15496         }
15497     }
15498     if((createInfo.pool != VK_NULL_HANDLE) &&
15499         ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
15500     {
15501         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
15502         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15503     }
15504 
15505     if(createInfo.pool != VK_NULL_HANDLE)
15506     {
15507         VmaAllocationCreateInfo createInfoForPool = createInfo;
15508         // If memory type is not HOST_VISIBLE, disable MAPPED.
15509         if((createInfoForPool.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
15510             (m_MemProps.memoryTypes[createInfo.pool->m_BlockVector.GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15511         {
15512             createInfoForPool.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
15513         }
15514 
15515         return createInfo.pool->m_BlockVector.Allocate(
15516             m_CurrentFrameIndex.load(),
15517             vkMemReq.size,
15518             vkMemReq.alignment,
15519             createInfoForPool,
15520             suballocType,
15521             allocationCount,
15522             pAllocations);
15523     }
15524     else
15525     {
15526         // Bit mask of memory Vulkan types acceptable for this allocation.
15527         uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
15528         uint32_t memTypeIndex = UINT32_MAX;
15529         VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
15530         if(res == VK_SUCCESS)
15531         {
15532             res = AllocateMemoryOfType(
15533                 vkMemReq.size,
15534                 vkMemReq.alignment,
15535                 requiresDedicatedAllocation || prefersDedicatedAllocation,
15536                 dedicatedBuffer,
15537                 dedicatedBufferUsage,
15538                 dedicatedImage,
15539                 createInfo,
15540                 memTypeIndex,
15541                 suballocType,
15542                 allocationCount,
15543                 pAllocations);
15544             // Succeeded on first try.
15545             if(res == VK_SUCCESS)
15546             {
15547                 return res;
15548             }
15549             // Allocation from this memory type failed. Try other compatible memory types.
15550             else
15551             {
15552                 for(;;)
15553                 {
15554                     // Remove old memTypeIndex from list of possibilities.
15555                     memoryTypeBits &= ~(1u << memTypeIndex);
15556                     // Find alternative memTypeIndex.
15557                     res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
15558                     if(res == VK_SUCCESS)
15559                     {
15560                         res = AllocateMemoryOfType(
15561                             vkMemReq.size,
15562                             vkMemReq.alignment,
15563                             requiresDedicatedAllocation || prefersDedicatedAllocation,
15564                             dedicatedBuffer,
15565                             dedicatedBufferUsage,
15566                             dedicatedImage,
15567                             createInfo,
15568                             memTypeIndex,
15569                             suballocType,
15570                             allocationCount,
15571                             pAllocations);
15572                         // Allocation from this alternative memory type succeeded.
15573                         if(res == VK_SUCCESS)
15574                         {
15575                             return res;
15576                         }
15577                         // else: Allocation from this memory type failed. Try next one - next loop iteration.
15578                     }
15579                     // No other matching memory type index could be found.
15580                     else
15581                     {
15582                         // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
15583                         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15584                     }
15585                 }
15586             }
15587         }
15588         // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
15589         else
15590             return res;
15591     }
15592 }
15593 
FreeMemory(size_t allocationCount,const VmaAllocation * pAllocations)15594 void VmaAllocator_T::FreeMemory(
15595     size_t allocationCount,
15596     const VmaAllocation* pAllocations)
15597 {
15598     VMA_ASSERT(pAllocations);
15599 
15600     for(size_t allocIndex = allocationCount; allocIndex--; )
15601     {
15602         VmaAllocation allocation = pAllocations[allocIndex];
15603 
15604         if(allocation != VK_NULL_HANDLE)
15605         {
15606             if(TouchAllocation(allocation))
15607             {
15608                 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
15609                 {
15610                     FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
15611                 }
15612 
15613                 switch(allocation->GetType())
15614                 {
15615                 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15616                     {
15617                         VmaBlockVector* pBlockVector = VMA_NULL;
15618                         VmaPool hPool = allocation->GetBlock()->GetParentPool();
15619                         if(hPool != VK_NULL_HANDLE)
15620                         {
15621                             pBlockVector = &hPool->m_BlockVector;
15622                         }
15623                         else
15624                         {
15625                             const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
15626                             pBlockVector = m_pBlockVectors[memTypeIndex];
15627                         }
15628                         pBlockVector->Free(allocation);
15629                     }
15630                     break;
15631                 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15632                     FreeDedicatedMemory(allocation);
15633                     break;
15634                 default:
15635                     VMA_ASSERT(0);
15636                 }
15637             }
15638 
15639             // Do this regardless of whether the allocation is lost. Lost allocations still account to Budget.AllocationBytes.
15640             m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
15641             allocation->SetUserData(this, VMA_NULL);
15642             m_AllocationObjectAllocator.Free(allocation);
15643         }
15644     }
15645 }
15646 
CalculateStats(VmaStats * pStats)15647 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
15648 {
15649     // Initialize.
15650     VmaInitStatInfo(pStats->total);
15651     for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
15652         VmaInitStatInfo(pStats->memoryType[i]);
15653     for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
15654         VmaInitStatInfo(pStats->memoryHeap[i]);
15655 
15656     // Process default pools.
15657     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15658     {
15659         VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
15660         VMA_ASSERT(pBlockVector);
15661         pBlockVector->AddStats(pStats);
15662     }
15663 
15664     // Process custom pools.
15665     {
15666         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15667         for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
15668         {
15669             pool->m_BlockVector.AddStats(pStats);
15670         }
15671     }
15672 
15673     // Process dedicated allocations.
15674     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15675     {
15676         const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
15677         VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
15678         DedicatedAllocationLinkedList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex];
15679         for(VmaAllocation alloc = dedicatedAllocList.Front();
15680             alloc != VMA_NULL; alloc = dedicatedAllocList.GetNext(alloc))
15681         {
15682             VmaStatInfo allocationStatInfo;
15683             alloc->DedicatedAllocCalcStatsInfo(allocationStatInfo);
15684             VmaAddStatInfo(pStats->total, allocationStatInfo);
15685             VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
15686             VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
15687         }
15688     }
15689 
15690     // Postprocess.
15691     VmaPostprocessCalcStatInfo(pStats->total);
15692     for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
15693         VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
15694     for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
15695         VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
15696 }
15697 
GetBudget(VmaBudget * outBudget,uint32_t firstHeap,uint32_t heapCount)15698 void VmaAllocator_T::GetBudget(VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount)
15699 {
15700 #if VMA_MEMORY_BUDGET
15701     if(m_UseExtMemoryBudget)
15702     {
15703         if(m_Budget.m_OperationsSinceBudgetFetch < 30)
15704         {
15705             VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex);
15706             for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
15707             {
15708                 const uint32_t heapIndex = firstHeap + i;
15709 
15710                 outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
15711                 outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
15712 
15713                 if(m_Budget.m_VulkanUsage[heapIndex] + outBudget->blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex])
15714                 {
15715                     outBudget->usage = m_Budget.m_VulkanUsage[heapIndex] +
15716                         outBudget->blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
15717                 }
15718                 else
15719                 {
15720                     outBudget->usage = 0;
15721                 }
15722 
15723                 // Have to take MIN with heap size because explicit HeapSizeLimit is included in it.
15724                 outBudget->budget = VMA_MIN(
15725                     m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size);
15726             }
15727         }
15728         else
15729         {
15730             UpdateVulkanBudget(); // Outside of mutex lock
15731             GetBudget(outBudget, firstHeap, heapCount); // Recursion
15732         }
15733     }
15734     else
15735 #endif
15736     {
15737         for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
15738         {
15739             const uint32_t heapIndex = firstHeap + i;
15740 
15741             outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
15742             outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
15743 
15744             outBudget->usage = outBudget->blockBytes;
15745             outBudget->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
15746         }
15747     }
15748 }
15749 
15750 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
15751 
DefragmentationBegin(const VmaDefragmentationInfo2 & info,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)15752 VkResult VmaAllocator_T::DefragmentationBegin(
15753     const VmaDefragmentationInfo2& info,
15754     VmaDefragmentationStats* pStats,
15755     VmaDefragmentationContext* pContext)
15756 {
15757     if(info.pAllocationsChanged != VMA_NULL)
15758     {
15759         memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
15760     }
15761 
15762     *pContext = vma_new(this, VmaDefragmentationContext_T)(
15763         this, m_CurrentFrameIndex.load(), info.flags, pStats);
15764 
15765     (*pContext)->AddPools(info.poolCount, info.pPools);
15766     (*pContext)->AddAllocations(
15767         info.allocationCount, info.pAllocations, info.pAllocationsChanged);
15768 
15769     VkResult res = (*pContext)->Defragment(
15770         info.maxCpuBytesToMove, info.maxCpuAllocationsToMove,
15771         info.maxGpuBytesToMove, info.maxGpuAllocationsToMove,
15772         info.commandBuffer, pStats, info.flags);
15773 
15774     if(res != VK_NOT_READY)
15775     {
15776         vma_delete(this, *pContext);
15777         *pContext = VMA_NULL;
15778     }
15779 
15780     return res;
15781 }
15782 
DefragmentationEnd(VmaDefragmentationContext context)15783 VkResult VmaAllocator_T::DefragmentationEnd(
15784     VmaDefragmentationContext context)
15785 {
15786     vma_delete(this, context);
15787     return VK_SUCCESS;
15788 }
15789 
DefragmentationPassBegin(VmaDefragmentationPassInfo * pInfo,VmaDefragmentationContext context)15790 VkResult VmaAllocator_T::DefragmentationPassBegin(
15791     VmaDefragmentationPassInfo* pInfo,
15792     VmaDefragmentationContext context)
15793 {
15794     return context->DefragmentPassBegin(pInfo);
15795 }
DefragmentationPassEnd(VmaDefragmentationContext context)15796 VkResult VmaAllocator_T::DefragmentationPassEnd(
15797     VmaDefragmentationContext context)
15798 {
15799     return context->DefragmentPassEnd();
15800 
15801 }
15802 
GetAllocationInfo(VmaAllocation hAllocation,VmaAllocationInfo * pAllocationInfo)15803 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
15804 {
15805     if(hAllocation->CanBecomeLost())
15806     {
15807         /*
15808         Warning: This is a carefully designed algorithm.
15809         Do not modify unless you really know what you are doing :)
15810         */
15811         const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
15812         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
15813         for(;;)
15814         {
15815             if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
15816             {
15817                 pAllocationInfo->memoryType = UINT32_MAX;
15818                 pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
15819                 pAllocationInfo->offset = 0;
15820                 pAllocationInfo->size = hAllocation->GetSize();
15821                 pAllocationInfo->pMappedData = VMA_NULL;
15822                 pAllocationInfo->pUserData = hAllocation->GetUserData();
15823                 return;
15824             }
15825             else if(localLastUseFrameIndex == localCurrFrameIndex)
15826             {
15827                 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
15828                 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
15829                 pAllocationInfo->offset = hAllocation->GetOffset();
15830                 pAllocationInfo->size = hAllocation->GetSize();
15831                 pAllocationInfo->pMappedData = VMA_NULL;
15832                 pAllocationInfo->pUserData = hAllocation->GetUserData();
15833                 return;
15834             }
15835             else // Last use time earlier than current time.
15836             {
15837                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
15838                 {
15839                     localLastUseFrameIndex = localCurrFrameIndex;
15840                 }
15841             }
15842         }
15843     }
15844     else
15845     {
15846 #if VMA_STATS_STRING_ENABLED
15847         uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
15848         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
15849         for(;;)
15850         {
15851             VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
15852             if(localLastUseFrameIndex == localCurrFrameIndex)
15853             {
15854                 break;
15855             }
15856             else // Last use time earlier than current time.
15857             {
15858                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
15859                 {
15860                     localLastUseFrameIndex = localCurrFrameIndex;
15861                 }
15862             }
15863         }
15864 #endif
15865 
15866         pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
15867         pAllocationInfo->deviceMemory = hAllocation->GetMemory();
15868         pAllocationInfo->offset = hAllocation->GetOffset();
15869         pAllocationInfo->size = hAllocation->GetSize();
15870         pAllocationInfo->pMappedData = hAllocation->GetMappedData();
15871         pAllocationInfo->pUserData = hAllocation->GetUserData();
15872     }
15873 }
15874 
TouchAllocation(VmaAllocation hAllocation)15875 bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
15876 {
15877     // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
15878     if(hAllocation->CanBecomeLost())
15879     {
15880         uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
15881         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
15882         for(;;)
15883         {
15884             if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
15885             {
15886                 return false;
15887             }
15888             else if(localLastUseFrameIndex == localCurrFrameIndex)
15889             {
15890                 return true;
15891             }
15892             else // Last use time earlier than current time.
15893             {
15894                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
15895                 {
15896                     localLastUseFrameIndex = localCurrFrameIndex;
15897                 }
15898             }
15899         }
15900     }
15901     else
15902     {
15903 #if VMA_STATS_STRING_ENABLED
15904         uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
15905         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
15906         for(;;)
15907         {
15908             VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
15909             if(localLastUseFrameIndex == localCurrFrameIndex)
15910             {
15911                 break;
15912             }
15913             else // Last use time earlier than current time.
15914             {
15915                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
15916                 {
15917                     localLastUseFrameIndex = localCurrFrameIndex;
15918                 }
15919             }
15920         }
15921 #endif
15922 
15923         return true;
15924     }
15925 }
15926 
CreatePool(const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)15927 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
15928 {
15929     VMA_DEBUG_LOG("  CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
15930 
15931     VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
15932 
15933     // Protection against uninitialized new structure member. If garbage data are left there, this pointer dereference would crash.
15934     if(pCreateInfo->pMemoryAllocateNext)
15935     {
15936         VMA_ASSERT(((const VkBaseInStructure*)pCreateInfo->pMemoryAllocateNext)->sType != 0);
15937     }
15938 
15939     if(newCreateInfo.maxBlockCount == 0)
15940     {
15941         newCreateInfo.maxBlockCount = SIZE_MAX;
15942     }
15943     if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
15944     {
15945         return VK_ERROR_INITIALIZATION_FAILED;
15946     }
15947     // Memory type index out of range or forbidden.
15948     if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() ||
15949         ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0)
15950     {
15951         return VK_ERROR_FEATURE_NOT_PRESENT;
15952     }
15953     if(newCreateInfo.minAllocationAlignment > 0)
15954     {
15955         VMA_ASSERT(VmaIsPow2(newCreateInfo.minAllocationAlignment));
15956     }
15957 
15958     const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
15959 
15960     *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
15961 
15962     VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
15963     if(res != VK_SUCCESS)
15964     {
15965         vma_delete(this, *pPool);
15966         *pPool = VMA_NULL;
15967         return res;
15968     }
15969 
15970     // Add to m_Pools.
15971     {
15972         VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15973         (*pPool)->SetId(m_NextPoolId++);
15974         m_Pools.PushBack(*pPool);
15975     }
15976 
15977     return VK_SUCCESS;
15978 }
15979 
DestroyPool(VmaPool pool)15980 void VmaAllocator_T::DestroyPool(VmaPool pool)
15981 {
15982     // Remove from m_Pools.
15983     {
15984         VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15985         m_Pools.Remove(pool);
15986     }
15987 
15988     vma_delete(this, pool);
15989 }
15990 
GetPoolStats(VmaPool pool,VmaPoolStats * pPoolStats)15991 void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
15992 {
15993     pool->m_BlockVector.GetPoolStats(pPoolStats);
15994 }
15995 
SetCurrentFrameIndex(uint32_t frameIndex)15996 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
15997 {
15998     m_CurrentFrameIndex.store(frameIndex);
15999 
16000 #if VMA_MEMORY_BUDGET
16001     if(m_UseExtMemoryBudget)
16002     {
16003         UpdateVulkanBudget();
16004     }
16005 #endif // #if VMA_MEMORY_BUDGET
16006 }
16007 
MakePoolAllocationsLost(VmaPool hPool,size_t * pLostAllocationCount)16008 void VmaAllocator_T::MakePoolAllocationsLost(
16009     VmaPool hPool,
16010     size_t* pLostAllocationCount)
16011 {
16012     hPool->m_BlockVector.MakePoolAllocationsLost(
16013         m_CurrentFrameIndex.load(),
16014         pLostAllocationCount);
16015 }
16016 
CheckPoolCorruption(VmaPool hPool)16017 VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
16018 {
16019     return hPool->m_BlockVector.CheckCorruption();
16020 }
16021 
CheckCorruption(uint32_t memoryTypeBits)16022 VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
16023 {
16024     VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
16025 
16026     // Process default pools.
16027     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16028     {
16029         if(((1u << memTypeIndex) & memoryTypeBits) != 0)
16030         {
16031             VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
16032             VMA_ASSERT(pBlockVector);
16033             VkResult localRes = pBlockVector->CheckCorruption();
16034             switch(localRes)
16035             {
16036             case VK_ERROR_FEATURE_NOT_PRESENT:
16037                 break;
16038             case VK_SUCCESS:
16039                 finalRes = VK_SUCCESS;
16040                 break;
16041             default:
16042                 return localRes;
16043             }
16044         }
16045     }
16046 
16047     // Process custom pools.
16048     {
16049         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
16050         for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
16051         {
16052             if(((1u << pool->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
16053             {
16054                 VkResult localRes = pool->m_BlockVector.CheckCorruption();
16055                 switch(localRes)
16056                 {
16057                 case VK_ERROR_FEATURE_NOT_PRESENT:
16058                     break;
16059                 case VK_SUCCESS:
16060                     finalRes = VK_SUCCESS;
16061                     break;
16062                 default:
16063                     return localRes;
16064                 }
16065             }
16066         }
16067     }
16068 
16069     return finalRes;
16070 }
16071 
CreateLostAllocation(VmaAllocation * pAllocation)16072 void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
16073 {
16074     *pAllocation = m_AllocationObjectAllocator.Allocate(VMA_FRAME_INDEX_LOST, false);
16075     (*pAllocation)->InitLost();
16076 }
16077 
16078 // An object that increments given atomic but decrements it back in the destructor unless Commit() is called.
16079 template<typename T>
16080 struct AtomicTransactionalIncrement
16081 {
16082 public:
16083     typedef std::atomic<T> AtomicT;
~AtomicTransactionalIncrementAtomicTransactionalIncrement16084     ~AtomicTransactionalIncrement()
16085     {
16086         if(m_Atomic)
16087             --(*m_Atomic);
16088     }
IncrementAtomicTransactionalIncrement16089     T Increment(AtomicT* atomic)
16090     {
16091         m_Atomic = atomic;
16092         return m_Atomic->fetch_add(1);
16093     }
CommitAtomicTransactionalIncrement16094     void Commit()
16095     {
16096         m_Atomic = nullptr;
16097     }
16098 
16099 private:
16100     AtomicT* m_Atomic = nullptr;
16101 };
16102 
AllocateVulkanMemory(const VkMemoryAllocateInfo * pAllocateInfo,VkDeviceMemory * pMemory)16103 VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
16104 {
16105     AtomicTransactionalIncrement<uint32_t> deviceMemoryCountIncrement;
16106     const uint64_t prevDeviceMemoryCount = deviceMemoryCountIncrement.Increment(&m_DeviceMemoryCount);
16107 #if VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT
16108     if(prevDeviceMemoryCount >= m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount)
16109     {
16110         return VK_ERROR_TOO_MANY_OBJECTS;
16111     }
16112 #endif
16113 
16114     const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
16115 
16116     // HeapSizeLimit is in effect for this heap.
16117     if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0)
16118     {
16119         const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
16120         VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex];
16121         for(;;)
16122         {
16123             const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize;
16124             if(blockBytesAfterAllocation > heapSize)
16125             {
16126                 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16127             }
16128             if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation))
16129             {
16130                 break;
16131             }
16132         }
16133     }
16134     else
16135     {
16136         m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize;
16137     }
16138 
16139     // VULKAN CALL vkAllocateMemory.
16140     VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
16141 
16142     if(res == VK_SUCCESS)
16143     {
16144 #if VMA_MEMORY_BUDGET
16145         ++m_Budget.m_OperationsSinceBudgetFetch;
16146 #endif
16147 
16148         // Informative callback.
16149         if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
16150         {
16151             (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData);
16152         }
16153 
16154         deviceMemoryCountIncrement.Commit();
16155     }
16156     else
16157     {
16158         m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize;
16159     }
16160 
16161     return res;
16162 }
16163 
FreeVulkanMemory(uint32_t memoryType,VkDeviceSize size,VkDeviceMemory hMemory)16164 void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
16165 {
16166     // Informative callback.
16167     if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
16168     {
16169         (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData);
16170     }
16171 
16172     // VULKAN CALL vkFreeMemory.
16173     (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
16174 
16175     m_Budget.m_BlockBytes[MemoryTypeIndexToHeapIndex(memoryType)] -= size;
16176 
16177     --m_DeviceMemoryCount;
16178 }
16179 
BindVulkanBuffer(VkDeviceMemory memory,VkDeviceSize memoryOffset,VkBuffer buffer,const void * pNext)16180 VkResult VmaAllocator_T::BindVulkanBuffer(
16181     VkDeviceMemory memory,
16182     VkDeviceSize memoryOffset,
16183     VkBuffer buffer,
16184     const void* pNext)
16185 {
16186     if(pNext != VMA_NULL)
16187     {
16188 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
16189         if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
16190             m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL)
16191         {
16192             VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR };
16193             bindBufferMemoryInfo.pNext = pNext;
16194             bindBufferMemoryInfo.buffer = buffer;
16195             bindBufferMemoryInfo.memory = memory;
16196             bindBufferMemoryInfo.memoryOffset = memoryOffset;
16197             return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
16198         }
16199         else
16200 #endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
16201         {
16202             return VK_ERROR_EXTENSION_NOT_PRESENT;
16203         }
16204     }
16205     else
16206     {
16207         return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset);
16208     }
16209 }
16210 
BindVulkanImage(VkDeviceMemory memory,VkDeviceSize memoryOffset,VkImage image,const void * pNext)16211 VkResult VmaAllocator_T::BindVulkanImage(
16212     VkDeviceMemory memory,
16213     VkDeviceSize memoryOffset,
16214     VkImage image,
16215     const void* pNext)
16216 {
16217     if(pNext != VMA_NULL)
16218     {
16219 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
16220         if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
16221             m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL)
16222         {
16223             VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR };
16224             bindBufferMemoryInfo.pNext = pNext;
16225             bindBufferMemoryInfo.image = image;
16226             bindBufferMemoryInfo.memory = memory;
16227             bindBufferMemoryInfo.memoryOffset = memoryOffset;
16228             return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
16229         }
16230         else
16231 #endif // #if VMA_BIND_MEMORY2
16232         {
16233             return VK_ERROR_EXTENSION_NOT_PRESENT;
16234         }
16235     }
16236     else
16237     {
16238         return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset);
16239     }
16240 }
16241 
Map(VmaAllocation hAllocation,void ** ppData)16242 VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
16243 {
16244     if(hAllocation->CanBecomeLost())
16245     {
16246         return VK_ERROR_MEMORY_MAP_FAILED;
16247     }
16248 
16249     switch(hAllocation->GetType())
16250     {
16251     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16252         {
16253             VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
16254             char *pBytes = VMA_NULL;
16255             VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
16256             if(res == VK_SUCCESS)
16257             {
16258                 *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
16259                 hAllocation->BlockAllocMap();
16260             }
16261             return res;
16262         }
16263     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
16264         return hAllocation->DedicatedAllocMap(this, ppData);
16265     default:
16266         VMA_ASSERT(0);
16267         return VK_ERROR_MEMORY_MAP_FAILED;
16268     }
16269 }
16270 
Unmap(VmaAllocation hAllocation)16271 void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
16272 {
16273     switch(hAllocation->GetType())
16274     {
16275     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16276         {
16277             VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
16278             hAllocation->BlockAllocUnmap();
16279             pBlock->Unmap(this, 1);
16280         }
16281         break;
16282     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
16283         hAllocation->DedicatedAllocUnmap(this);
16284         break;
16285     default:
16286         VMA_ASSERT(0);
16287     }
16288 }
16289 
BindBufferMemory(VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkBuffer hBuffer,const void * pNext)16290 VkResult VmaAllocator_T::BindBufferMemory(
16291     VmaAllocation hAllocation,
16292     VkDeviceSize allocationLocalOffset,
16293     VkBuffer hBuffer,
16294     const void* pNext)
16295 {
16296     VkResult res = VK_SUCCESS;
16297     switch(hAllocation->GetType())
16298     {
16299     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
16300         res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext);
16301         break;
16302     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16303     {
16304         VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
16305         VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
16306         res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext);
16307         break;
16308     }
16309     default:
16310         VMA_ASSERT(0);
16311     }
16312     return res;
16313 }
16314 
BindImageMemory(VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkImage hImage,const void * pNext)16315 VkResult VmaAllocator_T::BindImageMemory(
16316     VmaAllocation hAllocation,
16317     VkDeviceSize allocationLocalOffset,
16318     VkImage hImage,
16319     const void* pNext)
16320 {
16321     VkResult res = VK_SUCCESS;
16322     switch(hAllocation->GetType())
16323     {
16324     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
16325         res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext);
16326         break;
16327     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16328     {
16329         VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
16330         VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
16331         res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext);
16332         break;
16333     }
16334     default:
16335         VMA_ASSERT(0);
16336     }
16337     return res;
16338 }
16339 
FlushOrInvalidateAllocation(VmaAllocation hAllocation,VkDeviceSize offset,VkDeviceSize size,VMA_CACHE_OPERATION op)16340 VkResult VmaAllocator_T::FlushOrInvalidateAllocation(
16341     VmaAllocation hAllocation,
16342     VkDeviceSize offset, VkDeviceSize size,
16343     VMA_CACHE_OPERATION op)
16344 {
16345     VkResult res = VK_SUCCESS;
16346 
16347     VkMappedMemoryRange memRange = {};
16348     if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange))
16349     {
16350         switch(op)
16351         {
16352         case VMA_CACHE_FLUSH:
16353             res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
16354             break;
16355         case VMA_CACHE_INVALIDATE:
16356             res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
16357             break;
16358         default:
16359             VMA_ASSERT(0);
16360         }
16361     }
16362     // else: Just ignore this call.
16363     return res;
16364 }
16365 
FlushOrInvalidateAllocations(uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes,VMA_CACHE_OPERATION op)16366 VkResult VmaAllocator_T::FlushOrInvalidateAllocations(
16367     uint32_t allocationCount,
16368     const VmaAllocation* allocations,
16369     const VkDeviceSize* offsets, const VkDeviceSize* sizes,
16370     VMA_CACHE_OPERATION op)
16371 {
16372     typedef VmaStlAllocator<VkMappedMemoryRange> RangeAllocator;
16373     typedef VmaSmallVector<VkMappedMemoryRange, RangeAllocator, 16> RangeVector;
16374     RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks()));
16375 
16376     for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16377     {
16378         const VmaAllocation alloc = allocations[allocIndex];
16379         const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0;
16380         const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE;
16381         VkMappedMemoryRange newRange;
16382         if(GetFlushOrInvalidateRange(alloc, offset, size, newRange))
16383         {
16384             ranges.push_back(newRange);
16385         }
16386     }
16387 
16388     VkResult res = VK_SUCCESS;
16389     if(!ranges.empty())
16390     {
16391         switch(op)
16392         {
16393         case VMA_CACHE_FLUSH:
16394             res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
16395             break;
16396         case VMA_CACHE_INVALIDATE:
16397             res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
16398             break;
16399         default:
16400             VMA_ASSERT(0);
16401         }
16402     }
16403     // else: Just ignore this call.
16404     return res;
16405 }
16406 
FreeDedicatedMemory(const VmaAllocation allocation)16407 void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
16408 {
16409     VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
16410 
16411     const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16412     {
16413         VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16414         DedicatedAllocationLinkedList& dedicatedAllocations = m_DedicatedAllocations[memTypeIndex];
16415         dedicatedAllocations.Remove(allocation);
16416     }
16417 
16418     VkDeviceMemory hMemory = allocation->GetMemory();
16419 
16420     /*
16421     There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
16422     before vkFreeMemory.
16423 
16424     if(allocation->GetMappedData() != VMA_NULL)
16425     {
16426         (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
16427     }
16428     */
16429 
16430     FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
16431 
16432     VMA_DEBUG_LOG("    Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
16433 }
16434 
CalculateGpuDefragmentationMemoryTypeBits()16435 uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const
16436 {
16437     VkBufferCreateInfo dummyBufCreateInfo;
16438     VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo);
16439 
16440     uint32_t memoryTypeBits = 0;
16441 
16442     // Create buffer.
16443     VkBuffer buf = VK_NULL_HANDLE;
16444     VkResult res = (*GetVulkanFunctions().vkCreateBuffer)(
16445         m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf);
16446     if(res == VK_SUCCESS)
16447     {
16448         // Query for supported memory types.
16449         VkMemoryRequirements memReq;
16450         (*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq);
16451         memoryTypeBits = memReq.memoryTypeBits;
16452 
16453         // Destroy buffer.
16454         (*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks());
16455     }
16456 
16457     return memoryTypeBits;
16458 }
16459 
CalculateGlobalMemoryTypeBits()16460 uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const
16461 {
16462     // Make sure memory information is already fetched.
16463     VMA_ASSERT(GetMemoryTypeCount() > 0);
16464 
16465     uint32_t memoryTypeBits = UINT32_MAX;
16466 
16467     if(!m_UseAmdDeviceCoherentMemory)
16468     {
16469         // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD.
16470         for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16471         {
16472             if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
16473             {
16474                 memoryTypeBits &= ~(1u << memTypeIndex);
16475             }
16476         }
16477     }
16478 
16479     return memoryTypeBits;
16480 }
16481 
GetFlushOrInvalidateRange(VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size,VkMappedMemoryRange & outRange)16482 bool VmaAllocator_T::GetFlushOrInvalidateRange(
16483     VmaAllocation allocation,
16484     VkDeviceSize offset, VkDeviceSize size,
16485     VkMappedMemoryRange& outRange) const
16486 {
16487     const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16488     if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
16489     {
16490         const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
16491         const VkDeviceSize allocationSize = allocation->GetSize();
16492         VMA_ASSERT(offset <= allocationSize);
16493 
16494         outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
16495         outRange.pNext = VMA_NULL;
16496         outRange.memory = allocation->GetMemory();
16497 
16498         switch(allocation->GetType())
16499         {
16500         case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
16501             outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
16502             if(size == VK_WHOLE_SIZE)
16503             {
16504                 outRange.size = allocationSize - outRange.offset;
16505             }
16506             else
16507             {
16508                 VMA_ASSERT(offset + size <= allocationSize);
16509                 outRange.size = VMA_MIN(
16510                     VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize),
16511                     allocationSize - outRange.offset);
16512             }
16513             break;
16514         case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16515         {
16516             // 1. Still within this allocation.
16517             outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
16518             if(size == VK_WHOLE_SIZE)
16519             {
16520                 size = allocationSize - offset;
16521             }
16522             else
16523             {
16524                 VMA_ASSERT(offset + size <= allocationSize);
16525             }
16526             outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize);
16527 
16528             // 2. Adjust to whole block.
16529             const VkDeviceSize allocationOffset = allocation->GetOffset();
16530             VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
16531             const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize();
16532             outRange.offset += allocationOffset;
16533             outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset);
16534 
16535             break;
16536         }
16537         default:
16538             VMA_ASSERT(0);
16539         }
16540         return true;
16541     }
16542     return false;
16543 }
16544 
16545 #if VMA_MEMORY_BUDGET
16546 
UpdateVulkanBudget()16547 void VmaAllocator_T::UpdateVulkanBudget()
16548 {
16549     VMA_ASSERT(m_UseExtMemoryBudget);
16550 
16551     VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR };
16552 
16553     VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };
16554     VmaPnextChainPushFront(&memProps, &budgetProps);
16555 
16556     GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps);
16557 
16558     {
16559         VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex);
16560 
16561         for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
16562         {
16563             m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex];
16564             m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex];
16565             m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load();
16566 
16567             // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size.
16568             if(m_Budget.m_VulkanBudget[heapIndex] == 0)
16569             {
16570                 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
16571             }
16572             else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size)
16573             {
16574                 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size;
16575             }
16576             if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0)
16577             {
16578                 m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
16579             }
16580         }
16581         m_Budget.m_OperationsSinceBudgetFetch = 0;
16582     }
16583 }
16584 
16585 #endif // #if VMA_MEMORY_BUDGET
16586 
FillAllocation(const VmaAllocation hAllocation,uint8_t pattern)16587 void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
16588 {
16589     if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
16590         !hAllocation->CanBecomeLost() &&
16591         (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
16592     {
16593         void* pData = VMA_NULL;
16594         VkResult res = Map(hAllocation, &pData);
16595         if(res == VK_SUCCESS)
16596         {
16597             memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
16598             FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
16599             Unmap(hAllocation);
16600         }
16601         else
16602         {
16603             VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
16604         }
16605     }
16606 }
16607 
GetGpuDefragmentationMemoryTypeBits()16608 uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits()
16609 {
16610     uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load();
16611     if(memoryTypeBits == UINT32_MAX)
16612     {
16613         memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits();
16614         m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits);
16615     }
16616     return memoryTypeBits;
16617 }
16618 
16619 #if VMA_STATS_STRING_ENABLED
16620 
PrintDetailedMap(VmaJsonWriter & json)16621 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
16622 {
16623     bool dedicatedAllocationsStarted = false;
16624     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16625     {
16626         VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16627         DedicatedAllocationLinkedList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex];
16628         if(!dedicatedAllocList.IsEmpty())
16629         {
16630             if(dedicatedAllocationsStarted == false)
16631             {
16632                 dedicatedAllocationsStarted = true;
16633                 json.WriteString("DedicatedAllocations");
16634                 json.BeginObject();
16635             }
16636 
16637             json.BeginString("Type ");
16638             json.ContinueString(memTypeIndex);
16639             json.EndString();
16640 
16641             json.BeginArray();
16642 
16643             for(VmaAllocation alloc = dedicatedAllocList.Front();
16644                 alloc != VMA_NULL; alloc = dedicatedAllocList.GetNext(alloc))
16645             {
16646                 json.BeginObject(true);
16647                 alloc->PrintParameters(json);
16648                 json.EndObject();
16649             }
16650 
16651             json.EndArray();
16652         }
16653     }
16654     if(dedicatedAllocationsStarted)
16655     {
16656         json.EndObject();
16657     }
16658 
16659     {
16660         bool allocationsStarted = false;
16661         for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16662         {
16663             if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
16664             {
16665                 if(allocationsStarted == false)
16666                 {
16667                     allocationsStarted = true;
16668                     json.WriteString("DefaultPools");
16669                     json.BeginObject();
16670                 }
16671 
16672                 json.BeginString("Type ");
16673                 json.ContinueString(memTypeIndex);
16674                 json.EndString();
16675 
16676                 m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
16677             }
16678         }
16679         if(allocationsStarted)
16680         {
16681             json.EndObject();
16682         }
16683     }
16684 
16685     // Custom pools
16686     {
16687         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
16688         if(!m_Pools.IsEmpty())
16689         {
16690             json.WriteString("Pools");
16691             json.BeginObject();
16692             for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
16693             {
16694                 json.BeginString();
16695                 json.ContinueString(pool->GetId());
16696                 json.EndString();
16697 
16698                 pool->m_BlockVector.PrintDetailedMap(json);
16699             }
16700             json.EndObject();
16701         }
16702     }
16703 }
16704 
16705 #endif // #if VMA_STATS_STRING_ENABLED
16706 
16707 ////////////////////////////////////////////////////////////////////////////////
16708 // VmaVirtualBlock_T
16709 
VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo & createInfo)16710 VmaVirtualBlock_T::VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo) :
16711     m_AllocationCallbacksSpecified(createInfo.pAllocationCallbacks != VMA_NULL),
16712     m_AllocationCallbacks(createInfo.pAllocationCallbacks != VMA_NULL ? *createInfo.pAllocationCallbacks : VmaEmptyAllocationCallbacks)
16713 {
16714     const uint32_t algorithm = createInfo.flags & VMA_VIRTUAL_BLOCK_CREATE_ALGORITHM_MASK;
16715     switch(algorithm)
16716     {
16717     case 0:
16718         m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_Generic)(VK_NULL_HANDLE, true);
16719         break;
16720     case VMA_VIRTUAL_BLOCK_CREATE_BUDDY_ALGORITHM_BIT:
16721         m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_Buddy)(VK_NULL_HANDLE, true);
16722         break;
16723     case VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT:
16724         m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_Linear)(VK_NULL_HANDLE, true);
16725         break;
16726     default:
16727         VMA_ASSERT(0);
16728     }
16729 
16730     m_Metadata->Init(createInfo.size);
16731 }
16732 
~VmaVirtualBlock_T()16733 VmaVirtualBlock_T::~VmaVirtualBlock_T()
16734 {
16735     // This is an important assert!!!
16736     // Hitting it means you have some memory leak - unreleased virtual allocations.
16737     VMA_ASSERT(m_Metadata->IsEmpty() && "Some virtual allocations were not freed before destruction of this virtual block!");
16738 
16739     vma_delete(GetAllocationCallbacks(), m_Metadata);
16740 }
16741 
Allocate(const VmaVirtualAllocationCreateInfo & createInfo,VkDeviceSize & outOffset)16742 VkResult VmaVirtualBlock_T::Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VkDeviceSize& outOffset)
16743 {
16744     outOffset = VK_WHOLE_SIZE;
16745     VmaAllocationRequest request = {};
16746     if(m_Metadata->CreateAllocationRequest(
16747         0, // currentFrameIndex - unimportant
16748         0, // frameInUseCount - unimportant
16749         1, // bufferImageGranularity
16750         createInfo.size, // allocSize
16751         VMA_MAX(createInfo.alignment, (VkDeviceSize)1), // allocAlignment
16752         (createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0, // upperAddress
16753         VMA_SUBALLOCATION_TYPE_UNKNOWN, // allocType - unimportant
16754         false, // canMakeOthersLost
16755         createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK, // strategy
16756         &request))
16757     {
16758         m_Metadata->Alloc(request,
16759             VMA_SUBALLOCATION_TYPE_UNKNOWN, // type - unimportant
16760             createInfo.pUserData);
16761         outOffset = request.offset;
16762         return VK_SUCCESS;
16763     }
16764     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16765 }
16766 
16767 #if VMA_STATS_STRING_ENABLED
BuildStatsString(bool detailedMap,VmaStringBuilder & sb)16768 void VmaVirtualBlock_T::BuildStatsString(bool detailedMap, VmaStringBuilder& sb) const
16769 {
16770     VmaJsonWriter json(GetAllocationCallbacks(), sb);
16771     json.BeginObject();
16772 
16773     VmaStatInfo stat = {};
16774     CalculateStats(stat);
16775 
16776     json.WriteString("Stats");
16777     VmaPrintStatInfo(json, stat);
16778 
16779     if(detailedMap)
16780     {
16781         json.WriteString("Details");
16782         m_Metadata->PrintDetailedMap(json);
16783     }
16784 
16785     json.EndObject();
16786 }
16787 #endif // #if VMA_STATS_STRING_ENABLED
16788 
16789 ////////////////////////////////////////////////////////////////////////////////
16790 // Public interface
16791 
vmaCreateAllocator(const VmaAllocatorCreateInfo * pCreateInfo,VmaAllocator * pAllocator)16792 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
16793     const VmaAllocatorCreateInfo* pCreateInfo,
16794     VmaAllocator* pAllocator)
16795 {
16796     VMA_ASSERT(pCreateInfo && pAllocator);
16797     VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 ||
16798         (VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 3));
16799     VMA_DEBUG_LOG("vmaCreateAllocator");
16800     *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
16801     VkResult result = (*pAllocator)->Init(pCreateInfo);
16802     if(result < 0)
16803     {
16804         vma_delete(pCreateInfo->pAllocationCallbacks, *pAllocator);
16805         *pAllocator = VK_NULL_HANDLE;
16806     }
16807     return result;
16808 }
16809 
vmaDestroyAllocator(VmaAllocator allocator)16810 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
16811     VmaAllocator allocator)
16812 {
16813     if(allocator != VK_NULL_HANDLE)
16814     {
16815         VMA_DEBUG_LOG("vmaDestroyAllocator");
16816         VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks; // Have to copy the callbacks when destroying.
16817         vma_delete(&allocationCallbacks, allocator);
16818     }
16819 }
16820 
vmaGetAllocatorInfo(VmaAllocator allocator,VmaAllocatorInfo * pAllocatorInfo)16821 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo)
16822 {
16823     VMA_ASSERT(allocator && pAllocatorInfo);
16824     pAllocatorInfo->instance = allocator->m_hInstance;
16825     pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice();
16826     pAllocatorInfo->device = allocator->m_hDevice;
16827 }
16828 
vmaGetPhysicalDeviceProperties(VmaAllocator allocator,const VkPhysicalDeviceProperties ** ppPhysicalDeviceProperties)16829 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
16830     VmaAllocator allocator,
16831     const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
16832 {
16833     VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
16834     *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
16835 }
16836 
vmaGetMemoryProperties(VmaAllocator allocator,const VkPhysicalDeviceMemoryProperties ** ppPhysicalDeviceMemoryProperties)16837 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
16838     VmaAllocator allocator,
16839     const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
16840 {
16841     VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
16842     *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
16843 }
16844 
vmaGetMemoryTypeProperties(VmaAllocator allocator,uint32_t memoryTypeIndex,VkMemoryPropertyFlags * pFlags)16845 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
16846     VmaAllocator allocator,
16847     uint32_t memoryTypeIndex,
16848     VkMemoryPropertyFlags* pFlags)
16849 {
16850     VMA_ASSERT(allocator && pFlags);
16851     VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
16852     *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
16853 }
16854 
vmaSetCurrentFrameIndex(VmaAllocator allocator,uint32_t frameIndex)16855 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
16856     VmaAllocator allocator,
16857     uint32_t frameIndex)
16858 {
16859     VMA_ASSERT(allocator);
16860     VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
16861 
16862     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16863 
16864     allocator->SetCurrentFrameIndex(frameIndex);
16865 }
16866 
vmaCalculateStats(VmaAllocator allocator,VmaStats * pStats)16867 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
16868     VmaAllocator allocator,
16869     VmaStats* pStats)
16870 {
16871     VMA_ASSERT(allocator && pStats);
16872     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16873     allocator->CalculateStats(pStats);
16874 }
16875 
vmaGetBudget(VmaAllocator allocator,VmaBudget * pBudget)16876 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
16877     VmaAllocator allocator,
16878     VmaBudget* pBudget)
16879 {
16880     VMA_ASSERT(allocator && pBudget);
16881     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16882     allocator->GetBudget(pBudget, 0, allocator->GetMemoryHeapCount());
16883 }
16884 
16885 #if VMA_STATS_STRING_ENABLED
16886 
vmaBuildStatsString(VmaAllocator allocator,char ** ppStatsString,VkBool32 detailedMap)16887 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
16888     VmaAllocator allocator,
16889     char** ppStatsString,
16890     VkBool32 detailedMap)
16891 {
16892     VMA_ASSERT(allocator && ppStatsString);
16893     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16894 
16895     VmaStringBuilder sb(allocator->GetAllocationCallbacks());
16896     {
16897         VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
16898         json.BeginObject();
16899 
16900         VmaBudget budget[VK_MAX_MEMORY_HEAPS];
16901         allocator->GetBudget(budget, 0, allocator->GetMemoryHeapCount());
16902 
16903         VmaStats stats;
16904         allocator->CalculateStats(&stats);
16905 
16906         json.WriteString("Total");
16907         VmaPrintStatInfo(json, stats.total);
16908 
16909         for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
16910         {
16911             json.BeginString("Heap ");
16912             json.ContinueString(heapIndex);
16913             json.EndString();
16914             json.BeginObject();
16915 
16916             json.WriteString("Size");
16917             json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
16918 
16919             json.WriteString("Flags");
16920             json.BeginArray(true);
16921             if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
16922             {
16923                 json.WriteString("DEVICE_LOCAL");
16924             }
16925             json.EndArray();
16926 
16927             json.WriteString("Budget");
16928             json.BeginObject();
16929             {
16930                 json.WriteString("BlockBytes");
16931                 json.WriteNumber(budget[heapIndex].blockBytes);
16932                 json.WriteString("AllocationBytes");
16933                 json.WriteNumber(budget[heapIndex].allocationBytes);
16934                 json.WriteString("Usage");
16935                 json.WriteNumber(budget[heapIndex].usage);
16936                 json.WriteString("Budget");
16937                 json.WriteNumber(budget[heapIndex].budget);
16938             }
16939             json.EndObject();
16940 
16941             if(stats.memoryHeap[heapIndex].blockCount > 0)
16942             {
16943                 json.WriteString("Stats");
16944                 VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
16945             }
16946 
16947             for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
16948             {
16949                 if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
16950                 {
16951                     json.BeginString("Type ");
16952                     json.ContinueString(typeIndex);
16953                     json.EndString();
16954 
16955                     json.BeginObject();
16956 
16957                     json.WriteString("Flags");
16958                     json.BeginArray(true);
16959                     VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
16960                     if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
16961                     {
16962                         json.WriteString("DEVICE_LOCAL");
16963                     }
16964                     if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
16965                     {
16966                         json.WriteString("HOST_VISIBLE");
16967                     }
16968                     if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
16969                     {
16970                         json.WriteString("HOST_COHERENT");
16971                     }
16972                     if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
16973                     {
16974                         json.WriteString("HOST_CACHED");
16975                     }
16976                     if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
16977                     {
16978                         json.WriteString("LAZILY_ALLOCATED");
16979                     }
16980 #if VMA_VULKAN_VERSION >= 1001000
16981                     if((flags & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0)
16982                     {
16983                         json.WriteString("PROTECTED");
16984                     }
16985 #endif // #if VMA_VULKAN_VERSION >= 1001000
16986 #if VK_AMD_device_coherent_memory
16987                     if((flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
16988                     {
16989                         json.WriteString("DEVICE_COHERENT");
16990                     }
16991                     if((flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY) != 0)
16992                     {
16993                         json.WriteString("DEVICE_UNCACHED");
16994                     }
16995 #endif // #if VK_AMD_device_coherent_memory
16996                     json.EndArray();
16997 
16998                     if(stats.memoryType[typeIndex].blockCount > 0)
16999                     {
17000                         json.WriteString("Stats");
17001                         VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
17002                     }
17003 
17004                     json.EndObject();
17005                 }
17006             }
17007 
17008             json.EndObject();
17009         }
17010         if(detailedMap == VK_TRUE)
17011         {
17012             allocator->PrintDetailedMap(json);
17013         }
17014 
17015         json.EndObject();
17016     }
17017 
17018     *ppStatsString = VmaCreateStringCopy(allocator->GetAllocationCallbacks(), sb.GetData(), sb.GetLength());
17019 }
17020 
vmaFreeStatsString(VmaAllocator allocator,char * pStatsString)17021 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
17022     VmaAllocator allocator,
17023     char* pStatsString)
17024 {
17025     if(pStatsString != VMA_NULL)
17026     {
17027         VMA_ASSERT(allocator);
17028         VmaFreeString(allocator->GetAllocationCallbacks(), pStatsString);
17029     }
17030 }
17031 
17032 #endif // #if VMA_STATS_STRING_ENABLED
17033 
17034 /*
17035 This function is not protected by any mutex because it just reads immutable data.
17036 */
vmaFindMemoryTypeIndex(VmaAllocator allocator,uint32_t memoryTypeBits,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)17037 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
17038     VmaAllocator allocator,
17039     uint32_t memoryTypeBits,
17040     const VmaAllocationCreateInfo* pAllocationCreateInfo,
17041     uint32_t* pMemoryTypeIndex)
17042 {
17043     VMA_ASSERT(allocator != VK_NULL_HANDLE);
17044     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
17045     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
17046 
17047     memoryTypeBits &= allocator->GetGlobalMemoryTypeBits();
17048 
17049     if(pAllocationCreateInfo->memoryTypeBits != 0)
17050     {
17051         memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
17052     }
17053 
17054     uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
17055     uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
17056     uint32_t notPreferredFlags = 0;
17057 
17058     // Convert usage to requiredFlags and preferredFlags.
17059     switch(pAllocationCreateInfo->usage)
17060     {
17061     case VMA_MEMORY_USAGE_UNKNOWN:
17062         break;
17063     case VMA_MEMORY_USAGE_GPU_ONLY:
17064         if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
17065         {
17066             preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
17067         }
17068         break;
17069     case VMA_MEMORY_USAGE_CPU_ONLY:
17070         requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
17071         break;
17072     case VMA_MEMORY_USAGE_CPU_TO_GPU:
17073         requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
17074         if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
17075         {
17076             preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
17077         }
17078         break;
17079     case VMA_MEMORY_USAGE_GPU_TO_CPU:
17080         requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
17081         preferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
17082         break;
17083     case VMA_MEMORY_USAGE_CPU_COPY:
17084         notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
17085         break;
17086     case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED:
17087         requiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
17088         break;
17089     default:
17090         VMA_ASSERT(0);
17091         break;
17092     }
17093 
17094     // Avoid DEVICE_COHERENT unless explicitly requested.
17095     if(((pAllocationCreateInfo->requiredFlags | pAllocationCreateInfo->preferredFlags) &
17096         (VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0)
17097     {
17098         notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY;
17099     }
17100 
17101     *pMemoryTypeIndex = UINT32_MAX;
17102     uint32_t minCost = UINT32_MAX;
17103     for(uint32_t memTypeIndex = 0, memTypeBit = 1;
17104         memTypeIndex < allocator->GetMemoryTypeCount();
17105         ++memTypeIndex, memTypeBit <<= 1)
17106     {
17107         // This memory type is acceptable according to memoryTypeBits bitmask.
17108         if((memTypeBit & memoryTypeBits) != 0)
17109         {
17110             const VkMemoryPropertyFlags currFlags =
17111                 allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
17112             // This memory type contains requiredFlags.
17113             if((requiredFlags & ~currFlags) == 0)
17114             {
17115                 // Calculate cost as number of bits from preferredFlags not present in this memory type.
17116                 uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags) +
17117                     VmaCountBitsSet(currFlags & notPreferredFlags);
17118                 // Remember memory type with lowest cost.
17119                 if(currCost < minCost)
17120                 {
17121                     *pMemoryTypeIndex = memTypeIndex;
17122                     if(currCost == 0)
17123                     {
17124                         return VK_SUCCESS;
17125                     }
17126                     minCost = currCost;
17127                 }
17128             }
17129         }
17130     }
17131     return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
17132 }
17133 
vmaFindMemoryTypeIndexForBufferInfo(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)17134 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
17135     VmaAllocator allocator,
17136     const VkBufferCreateInfo* pBufferCreateInfo,
17137     const VmaAllocationCreateInfo* pAllocationCreateInfo,
17138     uint32_t* pMemoryTypeIndex)
17139 {
17140     VMA_ASSERT(allocator != VK_NULL_HANDLE);
17141     VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
17142     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
17143     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
17144 
17145     const VkDevice hDev = allocator->m_hDevice;
17146     VkBuffer hBuffer = VK_NULL_HANDLE;
17147     const VmaVulkanFunctions* funcs = &allocator->GetVulkanFunctions();
17148     VkResult res = funcs->vkCreateBuffer(
17149         hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
17150     if(res == VK_SUCCESS)
17151     {
17152         VkMemoryRequirements memReq = {};
17153         funcs->vkGetBufferMemoryRequirements(
17154             hDev, hBuffer, &memReq);
17155 
17156         res = vmaFindMemoryTypeIndex(
17157             allocator,
17158             memReq.memoryTypeBits,
17159             pAllocationCreateInfo,
17160             pMemoryTypeIndex);
17161 
17162         funcs->vkDestroyBuffer(
17163             hDev, hBuffer, allocator->GetAllocationCallbacks());
17164     }
17165     return res;
17166 }
17167 
vmaFindMemoryTypeIndexForImageInfo(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)17168 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
17169     VmaAllocator allocator,
17170     const VkImageCreateInfo* pImageCreateInfo,
17171     const VmaAllocationCreateInfo* pAllocationCreateInfo,
17172     uint32_t* pMemoryTypeIndex)
17173 {
17174     VMA_ASSERT(allocator != VK_NULL_HANDLE);
17175     VMA_ASSERT(pImageCreateInfo != VMA_NULL);
17176     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
17177     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
17178 
17179     const VkDevice hDev = allocator->m_hDevice;
17180     VkImage hImage = VK_NULL_HANDLE;
17181     const VmaVulkanFunctions* funcs = &allocator->GetVulkanFunctions();
17182     VkResult res = funcs->vkCreateImage(
17183         hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
17184     if(res == VK_SUCCESS)
17185     {
17186         VkMemoryRequirements memReq = {};
17187         funcs->vkGetImageMemoryRequirements(
17188             hDev, hImage, &memReq);
17189 
17190         res = vmaFindMemoryTypeIndex(
17191             allocator,
17192             memReq.memoryTypeBits,
17193             pAllocationCreateInfo,
17194             pMemoryTypeIndex);
17195 
17196         funcs->vkDestroyImage(
17197             hDev, hImage, allocator->GetAllocationCallbacks());
17198     }
17199     return res;
17200 }
17201 
vmaCreatePool(VmaAllocator allocator,const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)17202 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
17203     VmaAllocator allocator,
17204     const VmaPoolCreateInfo* pCreateInfo,
17205     VmaPool* pPool)
17206 {
17207     VMA_ASSERT(allocator && pCreateInfo && pPool);
17208 
17209     VMA_DEBUG_LOG("vmaCreatePool");
17210 
17211     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17212 
17213     VkResult res = allocator->CreatePool(pCreateInfo, pPool);
17214 
17215 #if VMA_RECORDING_ENABLED
17216     if(allocator->GetRecorder() != VMA_NULL)
17217     {
17218         allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
17219     }
17220 #endif
17221 
17222     return res;
17223 }
17224 
vmaDestroyPool(VmaAllocator allocator,VmaPool pool)17225 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
17226     VmaAllocator allocator,
17227     VmaPool pool)
17228 {
17229     VMA_ASSERT(allocator);
17230 
17231     if(pool == VK_NULL_HANDLE)
17232     {
17233         return;
17234     }
17235 
17236     VMA_DEBUG_LOG("vmaDestroyPool");
17237 
17238     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17239 
17240 #if VMA_RECORDING_ENABLED
17241     if(allocator->GetRecorder() != VMA_NULL)
17242     {
17243         allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
17244     }
17245 #endif
17246 
17247     allocator->DestroyPool(pool);
17248 }
17249 
vmaGetPoolStats(VmaAllocator allocator,VmaPool pool,VmaPoolStats * pPoolStats)17250 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
17251     VmaAllocator allocator,
17252     VmaPool pool,
17253     VmaPoolStats* pPoolStats)
17254 {
17255     VMA_ASSERT(allocator && pool && pPoolStats);
17256 
17257     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17258 
17259     allocator->GetPoolStats(pool, pPoolStats);
17260 }
17261 
vmaMakePoolAllocationsLost(VmaAllocator allocator,VmaPool pool,size_t * pLostAllocationCount)17262 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
17263     VmaAllocator allocator,
17264     VmaPool pool,
17265     size_t* pLostAllocationCount)
17266 {
17267     VMA_ASSERT(allocator && pool);
17268 
17269     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17270 
17271 #if VMA_RECORDING_ENABLED
17272     if(allocator->GetRecorder() != VMA_NULL)
17273     {
17274         allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
17275     }
17276 #endif
17277 
17278     allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
17279 }
17280 
vmaCheckPoolCorruption(VmaAllocator allocator,VmaPool pool)17281 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
17282 {
17283     VMA_ASSERT(allocator && pool);
17284 
17285     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17286 
17287     VMA_DEBUG_LOG("vmaCheckPoolCorruption");
17288 
17289     return allocator->CheckPoolCorruption(pool);
17290 }
17291 
vmaGetPoolName(VmaAllocator allocator,VmaPool pool,const char ** ppName)17292 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
17293     VmaAllocator allocator,
17294     VmaPool pool,
17295     const char** ppName)
17296 {
17297     VMA_ASSERT(allocator && pool && ppName);
17298 
17299     VMA_DEBUG_LOG("vmaGetPoolName");
17300 
17301     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17302 
17303     *ppName = pool->GetName();
17304 }
17305 
vmaSetPoolName(VmaAllocator allocator,VmaPool pool,const char * pName)17306 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
17307     VmaAllocator allocator,
17308     VmaPool pool,
17309     const char* pName)
17310 {
17311     VMA_ASSERT(allocator && pool);
17312 
17313     VMA_DEBUG_LOG("vmaSetPoolName");
17314 
17315     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17316 
17317     pool->SetName(pName);
17318 
17319 #if VMA_RECORDING_ENABLED
17320     if(allocator->GetRecorder() != VMA_NULL)
17321     {
17322         allocator->GetRecorder()->RecordSetPoolName(allocator->GetCurrentFrameIndex(), pool, pName);
17323     }
17324 #endif
17325 }
17326 
vmaAllocateMemory(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)17327 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
17328     VmaAllocator allocator,
17329     const VkMemoryRequirements* pVkMemoryRequirements,
17330     const VmaAllocationCreateInfo* pCreateInfo,
17331     VmaAllocation* pAllocation,
17332     VmaAllocationInfo* pAllocationInfo)
17333 {
17334     VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
17335 
17336     VMA_DEBUG_LOG("vmaAllocateMemory");
17337 
17338     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17339 
17340     VkResult result = allocator->AllocateMemory(
17341         *pVkMemoryRequirements,
17342         false, // requiresDedicatedAllocation
17343         false, // prefersDedicatedAllocation
17344         VK_NULL_HANDLE, // dedicatedBuffer
17345         UINT32_MAX, // dedicatedBufferUsage
17346         VK_NULL_HANDLE, // dedicatedImage
17347         *pCreateInfo,
17348         VMA_SUBALLOCATION_TYPE_UNKNOWN,
17349         1, // allocationCount
17350         pAllocation);
17351 
17352 #if VMA_RECORDING_ENABLED
17353     if(allocator->GetRecorder() != VMA_NULL)
17354     {
17355         allocator->GetRecorder()->RecordAllocateMemory(
17356             allocator->GetCurrentFrameIndex(),
17357             *pVkMemoryRequirements,
17358             *pCreateInfo,
17359             *pAllocation);
17360     }
17361 #endif
17362 
17363     if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
17364     {
17365         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
17366     }
17367 
17368     return result;
17369 }
17370 
vmaAllocateMemoryPages(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,size_t allocationCount,VmaAllocation * pAllocations,VmaAllocationInfo * pAllocationInfo)17371 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
17372     VmaAllocator allocator,
17373     const VkMemoryRequirements* pVkMemoryRequirements,
17374     const VmaAllocationCreateInfo* pCreateInfo,
17375     size_t allocationCount,
17376     VmaAllocation* pAllocations,
17377     VmaAllocationInfo* pAllocationInfo)
17378 {
17379     if(allocationCount == 0)
17380     {
17381         return VK_SUCCESS;
17382     }
17383 
17384     VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
17385 
17386     VMA_DEBUG_LOG("vmaAllocateMemoryPages");
17387 
17388     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17389 
17390     VkResult result = allocator->AllocateMemory(
17391         *pVkMemoryRequirements,
17392         false, // requiresDedicatedAllocation
17393         false, // prefersDedicatedAllocation
17394         VK_NULL_HANDLE, // dedicatedBuffer
17395         UINT32_MAX, // dedicatedBufferUsage
17396         VK_NULL_HANDLE, // dedicatedImage
17397         *pCreateInfo,
17398         VMA_SUBALLOCATION_TYPE_UNKNOWN,
17399         allocationCount,
17400         pAllocations);
17401 
17402 #if VMA_RECORDING_ENABLED
17403     if(allocator->GetRecorder() != VMA_NULL)
17404     {
17405         allocator->GetRecorder()->RecordAllocateMemoryPages(
17406             allocator->GetCurrentFrameIndex(),
17407             *pVkMemoryRequirements,
17408             *pCreateInfo,
17409             (uint64_t)allocationCount,
17410             pAllocations);
17411     }
17412 #endif
17413 
17414     if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
17415     {
17416         for(size_t i = 0; i < allocationCount; ++i)
17417         {
17418             allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
17419         }
17420     }
17421 
17422     return result;
17423 }
17424 
vmaAllocateMemoryForBuffer(VmaAllocator allocator,VkBuffer buffer,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)17425 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
17426     VmaAllocator allocator,
17427     VkBuffer buffer,
17428     const VmaAllocationCreateInfo* pCreateInfo,
17429     VmaAllocation* pAllocation,
17430     VmaAllocationInfo* pAllocationInfo)
17431 {
17432     VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
17433 
17434     VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
17435 
17436     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17437 
17438     VkMemoryRequirements vkMemReq = {};
17439     bool requiresDedicatedAllocation = false;
17440     bool prefersDedicatedAllocation = false;
17441     allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
17442         requiresDedicatedAllocation,
17443         prefersDedicatedAllocation);
17444 
17445     VkResult result = allocator->AllocateMemory(
17446         vkMemReq,
17447         requiresDedicatedAllocation,
17448         prefersDedicatedAllocation,
17449         buffer, // dedicatedBuffer
17450         UINT32_MAX, // dedicatedBufferUsage
17451         VK_NULL_HANDLE, // dedicatedImage
17452         *pCreateInfo,
17453         VMA_SUBALLOCATION_TYPE_BUFFER,
17454         1, // allocationCount
17455         pAllocation);
17456 
17457 #if VMA_RECORDING_ENABLED
17458     if(allocator->GetRecorder() != VMA_NULL)
17459     {
17460         allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
17461             allocator->GetCurrentFrameIndex(),
17462             vkMemReq,
17463             requiresDedicatedAllocation,
17464             prefersDedicatedAllocation,
17465             *pCreateInfo,
17466             *pAllocation);
17467     }
17468 #endif
17469 
17470     if(pAllocationInfo && result == VK_SUCCESS)
17471     {
17472         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
17473     }
17474 
17475     return result;
17476 }
17477 
vmaAllocateMemoryForImage(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)17478 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
17479     VmaAllocator allocator,
17480     VkImage image,
17481     const VmaAllocationCreateInfo* pCreateInfo,
17482     VmaAllocation* pAllocation,
17483     VmaAllocationInfo* pAllocationInfo)
17484 {
17485     VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
17486 
17487     VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
17488 
17489     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17490 
17491     VkMemoryRequirements vkMemReq = {};
17492     bool requiresDedicatedAllocation = false;
17493     bool prefersDedicatedAllocation  = false;
17494     allocator->GetImageMemoryRequirements(image, vkMemReq,
17495         requiresDedicatedAllocation, prefersDedicatedAllocation);
17496 
17497     VkResult result = allocator->AllocateMemory(
17498         vkMemReq,
17499         requiresDedicatedAllocation,
17500         prefersDedicatedAllocation,
17501         VK_NULL_HANDLE, // dedicatedBuffer
17502         UINT32_MAX, // dedicatedBufferUsage
17503         image, // dedicatedImage
17504         *pCreateInfo,
17505         VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
17506         1, // allocationCount
17507         pAllocation);
17508 
17509 #if VMA_RECORDING_ENABLED
17510     if(allocator->GetRecorder() != VMA_NULL)
17511     {
17512         allocator->GetRecorder()->RecordAllocateMemoryForImage(
17513             allocator->GetCurrentFrameIndex(),
17514             vkMemReq,
17515             requiresDedicatedAllocation,
17516             prefersDedicatedAllocation,
17517             *pCreateInfo,
17518             *pAllocation);
17519     }
17520 #endif
17521 
17522     if(pAllocationInfo && result == VK_SUCCESS)
17523     {
17524         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
17525     }
17526 
17527     return result;
17528 }
17529 
vmaFreeMemory(VmaAllocator allocator,VmaAllocation allocation)17530 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
17531     VmaAllocator allocator,
17532     VmaAllocation allocation)
17533 {
17534     VMA_ASSERT(allocator);
17535 
17536     if(allocation == VK_NULL_HANDLE)
17537     {
17538         return;
17539     }
17540 
17541     VMA_DEBUG_LOG("vmaFreeMemory");
17542 
17543     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17544 
17545 #if VMA_RECORDING_ENABLED
17546     if(allocator->GetRecorder() != VMA_NULL)
17547     {
17548         allocator->GetRecorder()->RecordFreeMemory(
17549             allocator->GetCurrentFrameIndex(),
17550             allocation);
17551     }
17552 #endif
17553 
17554     allocator->FreeMemory(
17555         1, // allocationCount
17556         &allocation);
17557 }
17558 
vmaFreeMemoryPages(VmaAllocator allocator,size_t allocationCount,const VmaAllocation * pAllocations)17559 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
17560     VmaAllocator allocator,
17561     size_t allocationCount,
17562     const VmaAllocation* pAllocations)
17563 {
17564     if(allocationCount == 0)
17565     {
17566         return;
17567     }
17568 
17569     VMA_ASSERT(allocator);
17570 
17571     VMA_DEBUG_LOG("vmaFreeMemoryPages");
17572 
17573     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17574 
17575 #if VMA_RECORDING_ENABLED
17576     if(allocator->GetRecorder() != VMA_NULL)
17577     {
17578         allocator->GetRecorder()->RecordFreeMemoryPages(
17579             allocator->GetCurrentFrameIndex(),
17580             (uint64_t)allocationCount,
17581             pAllocations);
17582     }
17583 #endif
17584 
17585     allocator->FreeMemory(allocationCount, pAllocations);
17586 }
17587 
vmaGetAllocationInfo(VmaAllocator allocator,VmaAllocation allocation,VmaAllocationInfo * pAllocationInfo)17588 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
17589     VmaAllocator allocator,
17590     VmaAllocation allocation,
17591     VmaAllocationInfo* pAllocationInfo)
17592 {
17593     VMA_ASSERT(allocator && allocation && pAllocationInfo);
17594 
17595     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17596 
17597 #if VMA_RECORDING_ENABLED
17598     if(allocator->GetRecorder() != VMA_NULL)
17599     {
17600         allocator->GetRecorder()->RecordGetAllocationInfo(
17601             allocator->GetCurrentFrameIndex(),
17602             allocation);
17603     }
17604 #endif
17605 
17606     allocator->GetAllocationInfo(allocation, pAllocationInfo);
17607 }
17608 
vmaTouchAllocation(VmaAllocator allocator,VmaAllocation allocation)17609 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
17610     VmaAllocator allocator,
17611     VmaAllocation allocation)
17612 {
17613     VMA_ASSERT(allocator && allocation);
17614 
17615     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17616 
17617 #if VMA_RECORDING_ENABLED
17618     if(allocator->GetRecorder() != VMA_NULL)
17619     {
17620         allocator->GetRecorder()->RecordTouchAllocation(
17621             allocator->GetCurrentFrameIndex(),
17622             allocation);
17623     }
17624 #endif
17625 
17626     return allocator->TouchAllocation(allocation);
17627 }
17628 
vmaSetAllocationUserData(VmaAllocator allocator,VmaAllocation allocation,void * pUserData)17629 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
17630     VmaAllocator allocator,
17631     VmaAllocation allocation,
17632     void* pUserData)
17633 {
17634     VMA_ASSERT(allocator && allocation);
17635 
17636     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17637 
17638     allocation->SetUserData(allocator, pUserData);
17639 
17640 #if VMA_RECORDING_ENABLED
17641     if(allocator->GetRecorder() != VMA_NULL)
17642     {
17643         allocator->GetRecorder()->RecordSetAllocationUserData(
17644             allocator->GetCurrentFrameIndex(),
17645             allocation,
17646             pUserData);
17647     }
17648 #endif
17649 }
17650 
vmaCreateLostAllocation(VmaAllocator allocator,VmaAllocation * pAllocation)17651 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
17652     VmaAllocator allocator,
17653     VmaAllocation* pAllocation)
17654 {
17655     VMA_ASSERT(allocator && pAllocation);
17656 
17657     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17658 
17659     allocator->CreateLostAllocation(pAllocation);
17660 
17661 #if VMA_RECORDING_ENABLED
17662     if(allocator->GetRecorder() != VMA_NULL)
17663     {
17664         allocator->GetRecorder()->RecordCreateLostAllocation(
17665             allocator->GetCurrentFrameIndex(),
17666             *pAllocation);
17667     }
17668 #endif
17669 }
17670 
vmaMapMemory(VmaAllocator allocator,VmaAllocation allocation,void ** ppData)17671 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
17672     VmaAllocator allocator,
17673     VmaAllocation allocation,
17674     void** ppData)
17675 {
17676     VMA_ASSERT(allocator && allocation && ppData);
17677 
17678     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17679 
17680     VkResult res = allocator->Map(allocation, ppData);
17681 
17682 #if VMA_RECORDING_ENABLED
17683     if(allocator->GetRecorder() != VMA_NULL)
17684     {
17685         allocator->GetRecorder()->RecordMapMemory(
17686             allocator->GetCurrentFrameIndex(),
17687             allocation);
17688     }
17689 #endif
17690 
17691     return res;
17692 }
17693 
vmaUnmapMemory(VmaAllocator allocator,VmaAllocation allocation)17694 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
17695     VmaAllocator allocator,
17696     VmaAllocation allocation)
17697 {
17698     VMA_ASSERT(allocator && allocation);
17699 
17700     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17701 
17702 #if VMA_RECORDING_ENABLED
17703     if(allocator->GetRecorder() != VMA_NULL)
17704     {
17705         allocator->GetRecorder()->RecordUnmapMemory(
17706             allocator->GetCurrentFrameIndex(),
17707             allocation);
17708     }
17709 #endif
17710 
17711     allocator->Unmap(allocation);
17712 }
17713 
vmaFlushAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)17714 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
17715 {
17716     VMA_ASSERT(allocator && allocation);
17717 
17718     VMA_DEBUG_LOG("vmaFlushAllocation");
17719 
17720     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17721 
17722     const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
17723 
17724 #if VMA_RECORDING_ENABLED
17725     if(allocator->GetRecorder() != VMA_NULL)
17726     {
17727         allocator->GetRecorder()->RecordFlushAllocation(
17728             allocator->GetCurrentFrameIndex(),
17729             allocation, offset, size);
17730     }
17731 #endif
17732 
17733     return res;
17734 }
17735 
vmaInvalidateAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)17736 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
17737 {
17738     VMA_ASSERT(allocator && allocation);
17739 
17740     VMA_DEBUG_LOG("vmaInvalidateAllocation");
17741 
17742     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17743 
17744     const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
17745 
17746 #if VMA_RECORDING_ENABLED
17747     if(allocator->GetRecorder() != VMA_NULL)
17748     {
17749         allocator->GetRecorder()->RecordInvalidateAllocation(
17750             allocator->GetCurrentFrameIndex(),
17751             allocation, offset, size);
17752     }
17753 #endif
17754 
17755     return res;
17756 }
17757 
vmaFlushAllocations(VmaAllocator allocator,uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes)17758 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
17759     VmaAllocator allocator,
17760     uint32_t allocationCount,
17761     const VmaAllocation* allocations,
17762     const VkDeviceSize* offsets,
17763     const VkDeviceSize* sizes)
17764 {
17765     VMA_ASSERT(allocator);
17766 
17767     if(allocationCount == 0)
17768     {
17769         return VK_SUCCESS;
17770     }
17771 
17772     VMA_ASSERT(allocations);
17773 
17774     VMA_DEBUG_LOG("vmaFlushAllocations");
17775 
17776     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17777 
17778     const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH);
17779 
17780 #if VMA_RECORDING_ENABLED
17781     if(allocator->GetRecorder() != VMA_NULL)
17782     {
17783         //TODO
17784     }
17785 #endif
17786 
17787     return res;
17788 }
17789 
vmaInvalidateAllocations(VmaAllocator allocator,uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes)17790 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
17791     VmaAllocator allocator,
17792     uint32_t allocationCount,
17793     const VmaAllocation* allocations,
17794     const VkDeviceSize* offsets,
17795     const VkDeviceSize* sizes)
17796 {
17797     VMA_ASSERT(allocator);
17798 
17799     if(allocationCount == 0)
17800     {
17801         return VK_SUCCESS;
17802     }
17803 
17804     VMA_ASSERT(allocations);
17805 
17806     VMA_DEBUG_LOG("vmaInvalidateAllocations");
17807 
17808     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17809 
17810     const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE);
17811 
17812 #if VMA_RECORDING_ENABLED
17813     if(allocator->GetRecorder() != VMA_NULL)
17814     {
17815         //TODO
17816     }
17817 #endif
17818 
17819     return res;
17820 }
17821 
vmaCheckCorruption(VmaAllocator allocator,uint32_t memoryTypeBits)17822 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
17823 {
17824     VMA_ASSERT(allocator);
17825 
17826     VMA_DEBUG_LOG("vmaCheckCorruption");
17827 
17828     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17829 
17830     return allocator->CheckCorruption(memoryTypeBits);
17831 }
17832 
vmaDefragment(VmaAllocator allocator,const VmaAllocation * pAllocations,size_t allocationCount,VkBool32 * pAllocationsChanged,const VmaDefragmentationInfo * pDefragmentationInfo,VmaDefragmentationStats * pDefragmentationStats)17833 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
17834     VmaAllocator allocator,
17835     const VmaAllocation* pAllocations,
17836     size_t allocationCount,
17837     VkBool32* pAllocationsChanged,
17838     const VmaDefragmentationInfo *pDefragmentationInfo,
17839     VmaDefragmentationStats* pDefragmentationStats)
17840 {
17841     // Deprecated interface, reimplemented using new one.
17842 
17843     VmaDefragmentationInfo2 info2 = {};
17844     info2.allocationCount = (uint32_t)allocationCount;
17845     info2.pAllocations = pAllocations;
17846     info2.pAllocationsChanged = pAllocationsChanged;
17847     if(pDefragmentationInfo != VMA_NULL)
17848     {
17849         info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
17850         info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
17851     }
17852     else
17853     {
17854         info2.maxCpuAllocationsToMove = UINT32_MAX;
17855         info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
17856     }
17857     // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
17858 
17859     VmaDefragmentationContext ctx;
17860     VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
17861     if(res == VK_NOT_READY)
17862     {
17863         res = vmaDefragmentationEnd( allocator, ctx);
17864     }
17865     return res;
17866 }
17867 
vmaDefragmentationBegin(VmaAllocator allocator,const VmaDefragmentationInfo2 * pInfo,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)17868 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
17869     VmaAllocator allocator,
17870     const VmaDefragmentationInfo2* pInfo,
17871     VmaDefragmentationStats* pStats,
17872     VmaDefragmentationContext *pContext)
17873 {
17874     VMA_ASSERT(allocator && pInfo && pContext);
17875 
17876     // Degenerate case: Nothing to defragment.
17877     if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
17878     {
17879         return VK_SUCCESS;
17880     }
17881 
17882     VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
17883     VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
17884     VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
17885     VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
17886 
17887     VMA_DEBUG_LOG("vmaDefragmentationBegin");
17888 
17889     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17890 
17891     VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
17892 
17893 #if VMA_RECORDING_ENABLED
17894     if(allocator->GetRecorder() != VMA_NULL)
17895     {
17896         allocator->GetRecorder()->RecordDefragmentationBegin(
17897             allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
17898     }
17899 #endif
17900 
17901     return res;
17902 }
17903 
vmaDefragmentationEnd(VmaAllocator allocator,VmaDefragmentationContext context)17904 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
17905     VmaAllocator allocator,
17906     VmaDefragmentationContext context)
17907 {
17908     VMA_ASSERT(allocator);
17909 
17910     VMA_DEBUG_LOG("vmaDefragmentationEnd");
17911 
17912     if(context != VK_NULL_HANDLE)
17913     {
17914         VMA_DEBUG_GLOBAL_MUTEX_LOCK
17915 
17916 #if VMA_RECORDING_ENABLED
17917         if(allocator->GetRecorder() != VMA_NULL)
17918         {
17919             allocator->GetRecorder()->RecordDefragmentationEnd(
17920                 allocator->GetCurrentFrameIndex(), context);
17921         }
17922 #endif
17923 
17924         return allocator->DefragmentationEnd(context);
17925     }
17926     else
17927     {
17928         return VK_SUCCESS;
17929     }
17930 }
17931 
vmaBeginDefragmentationPass(VmaAllocator allocator,VmaDefragmentationContext context,VmaDefragmentationPassInfo * pInfo)17932 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
17933     VmaAllocator allocator,
17934     VmaDefragmentationContext context,
17935     VmaDefragmentationPassInfo* pInfo
17936     )
17937 {
17938     VMA_ASSERT(allocator);
17939     VMA_ASSERT(pInfo);
17940 
17941     VMA_DEBUG_LOG("vmaBeginDefragmentationPass");
17942 
17943     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17944 
17945     if(context == VK_NULL_HANDLE)
17946     {
17947         pInfo->moveCount = 0;
17948         return VK_SUCCESS;
17949     }
17950 
17951     return allocator->DefragmentationPassBegin(pInfo, context);
17952 }
vmaEndDefragmentationPass(VmaAllocator allocator,VmaDefragmentationContext context)17953 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
17954     VmaAllocator allocator,
17955     VmaDefragmentationContext context)
17956 {
17957     VMA_ASSERT(allocator);
17958 
17959     VMA_DEBUG_LOG("vmaEndDefragmentationPass");
17960     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17961 
17962     if(context == VK_NULL_HANDLE)
17963         return VK_SUCCESS;
17964 
17965     return allocator->DefragmentationPassEnd(context);
17966 }
17967 
vmaBindBufferMemory(VmaAllocator allocator,VmaAllocation allocation,VkBuffer buffer)17968 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
17969     VmaAllocator allocator,
17970     VmaAllocation allocation,
17971     VkBuffer buffer)
17972 {
17973     VMA_ASSERT(allocator && allocation && buffer);
17974 
17975     VMA_DEBUG_LOG("vmaBindBufferMemory");
17976 
17977     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17978 
17979     return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL);
17980 }
17981 
vmaBindBufferMemory2(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize allocationLocalOffset,VkBuffer buffer,const void * pNext)17982 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
17983     VmaAllocator allocator,
17984     VmaAllocation allocation,
17985     VkDeviceSize allocationLocalOffset,
17986     VkBuffer buffer,
17987     const void* pNext)
17988 {
17989     VMA_ASSERT(allocator && allocation && buffer);
17990 
17991     VMA_DEBUG_LOG("vmaBindBufferMemory2");
17992 
17993     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17994 
17995     return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext);
17996 }
17997 
vmaBindImageMemory(VmaAllocator allocator,VmaAllocation allocation,VkImage image)17998 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
17999     VmaAllocator allocator,
18000     VmaAllocation allocation,
18001     VkImage image)
18002 {
18003     VMA_ASSERT(allocator && allocation && image);
18004 
18005     VMA_DEBUG_LOG("vmaBindImageMemory");
18006 
18007     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18008 
18009     return allocator->BindImageMemory(allocation, 0, image, VMA_NULL);
18010 }
18011 
vmaBindImageMemory2(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize allocationLocalOffset,VkImage image,const void * pNext)18012 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
18013     VmaAllocator allocator,
18014     VmaAllocation allocation,
18015     VkDeviceSize allocationLocalOffset,
18016     VkImage image,
18017     const void* pNext)
18018 {
18019     VMA_ASSERT(allocator && allocation && image);
18020 
18021     VMA_DEBUG_LOG("vmaBindImageMemory2");
18022 
18023     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18024 
18025         return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext);
18026 }
18027 
vmaCreateBuffer(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkBuffer * pBuffer,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18028 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
18029     VmaAllocator allocator,
18030     const VkBufferCreateInfo* pBufferCreateInfo,
18031     const VmaAllocationCreateInfo* pAllocationCreateInfo,
18032     VkBuffer* pBuffer,
18033     VmaAllocation* pAllocation,
18034     VmaAllocationInfo* pAllocationInfo)
18035 {
18036     VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
18037 
18038     if(pBufferCreateInfo->size == 0)
18039     {
18040         return VK_ERROR_INITIALIZATION_FAILED;
18041     }
18042     if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
18043         !allocator->m_UseKhrBufferDeviceAddress)
18044     {
18045         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.");
18046         return VK_ERROR_INITIALIZATION_FAILED;
18047     }
18048 
18049     VMA_DEBUG_LOG("vmaCreateBuffer");
18050 
18051     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18052 
18053     *pBuffer = VK_NULL_HANDLE;
18054     *pAllocation = VK_NULL_HANDLE;
18055 
18056     // 1. Create VkBuffer.
18057     VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
18058         allocator->m_hDevice,
18059         pBufferCreateInfo,
18060         allocator->GetAllocationCallbacks(),
18061         pBuffer);
18062     if(res >= 0)
18063     {
18064         // 2. vkGetBufferMemoryRequirements.
18065         VkMemoryRequirements vkMemReq = {};
18066         bool requiresDedicatedAllocation = false;
18067         bool prefersDedicatedAllocation  = false;
18068         allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
18069             requiresDedicatedAllocation, prefersDedicatedAllocation);
18070 
18071         // 3. Allocate memory using allocator.
18072         res = allocator->AllocateMemory(
18073             vkMemReq,
18074             requiresDedicatedAllocation,
18075             prefersDedicatedAllocation,
18076             *pBuffer, // dedicatedBuffer
18077             pBufferCreateInfo->usage, // dedicatedBufferUsage
18078             VK_NULL_HANDLE, // dedicatedImage
18079             *pAllocationCreateInfo,
18080             VMA_SUBALLOCATION_TYPE_BUFFER,
18081             1, // allocationCount
18082             pAllocation);
18083 
18084 #if VMA_RECORDING_ENABLED
18085         if(allocator->GetRecorder() != VMA_NULL)
18086         {
18087             allocator->GetRecorder()->RecordCreateBuffer(
18088                 allocator->GetCurrentFrameIndex(),
18089                 *pBufferCreateInfo,
18090                 *pAllocationCreateInfo,
18091                 *pAllocation);
18092         }
18093 #endif
18094 
18095         if(res >= 0)
18096         {
18097             // 3. Bind buffer with memory.
18098             if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
18099             {
18100                 res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
18101             }
18102             if(res >= 0)
18103             {
18104                 // All steps succeeded.
18105                 #if VMA_STATS_STRING_ENABLED
18106                     (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
18107                 #endif
18108                 if(pAllocationInfo != VMA_NULL)
18109                 {
18110                     allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18111                 }
18112 
18113                 return VK_SUCCESS;
18114             }
18115             allocator->FreeMemory(
18116                 1, // allocationCount
18117                 pAllocation);
18118             *pAllocation = VK_NULL_HANDLE;
18119             (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
18120             *pBuffer = VK_NULL_HANDLE;
18121             return res;
18122         }
18123         (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
18124         *pBuffer = VK_NULL_HANDLE;
18125         return res;
18126     }
18127     return res;
18128 }
18129 
vmaCreateBufferWithAlignment(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkDeviceSize minAlignment,VkBuffer * pBuffer,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18130 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment(
18131     VmaAllocator allocator,
18132     const VkBufferCreateInfo* pBufferCreateInfo,
18133     const VmaAllocationCreateInfo* pAllocationCreateInfo,
18134     VkDeviceSize minAlignment,
18135     VkBuffer* pBuffer,
18136     VmaAllocation* pAllocation,
18137     VmaAllocationInfo* pAllocationInfo)
18138 {
18139     VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && VmaIsPow2(minAlignment) && pBuffer && pAllocation);
18140 
18141     if(pBufferCreateInfo->size == 0)
18142     {
18143         return VK_ERROR_INITIALIZATION_FAILED;
18144     }
18145     if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
18146         !allocator->m_UseKhrBufferDeviceAddress)
18147     {
18148         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.");
18149         return VK_ERROR_INITIALIZATION_FAILED;
18150     }
18151 
18152     VMA_DEBUG_LOG("vmaCreateBufferWithAlignment");
18153 
18154     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18155 
18156     *pBuffer = VK_NULL_HANDLE;
18157     *pAllocation = VK_NULL_HANDLE;
18158 
18159     // 1. Create VkBuffer.
18160     VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
18161         allocator->m_hDevice,
18162         pBufferCreateInfo,
18163         allocator->GetAllocationCallbacks(),
18164         pBuffer);
18165     if(res >= 0)
18166     {
18167         // 2. vkGetBufferMemoryRequirements.
18168         VkMemoryRequirements vkMemReq = {};
18169         bool requiresDedicatedAllocation = false;
18170         bool prefersDedicatedAllocation  = false;
18171         allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
18172             requiresDedicatedAllocation, prefersDedicatedAllocation);
18173 
18174         // 2a. Include minAlignment
18175         vkMemReq.alignment = VMA_MAX(vkMemReq.alignment, minAlignment);
18176 
18177         // 3. Allocate memory using allocator.
18178         res = allocator->AllocateMemory(
18179             vkMemReq,
18180             requiresDedicatedAllocation,
18181             prefersDedicatedAllocation,
18182             *pBuffer, // dedicatedBuffer
18183             pBufferCreateInfo->usage, // dedicatedBufferUsage
18184             VK_NULL_HANDLE, // dedicatedImage
18185             *pAllocationCreateInfo,
18186             VMA_SUBALLOCATION_TYPE_BUFFER,
18187             1, // allocationCount
18188             pAllocation);
18189 
18190 #if VMA_RECORDING_ENABLED
18191         if(allocator->GetRecorder() != VMA_NULL)
18192         {
18193             VMA_ASSERT(0 && "Not implemented.");
18194         }
18195 #endif
18196 
18197         if(res >= 0)
18198         {
18199             // 3. Bind buffer with memory.
18200             if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
18201             {
18202                 res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
18203             }
18204             if(res >= 0)
18205             {
18206                 // All steps succeeded.
18207                 #if VMA_STATS_STRING_ENABLED
18208                     (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
18209                 #endif
18210                 if(pAllocationInfo != VMA_NULL)
18211                 {
18212                     allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18213                 }
18214 
18215                 return VK_SUCCESS;
18216             }
18217             allocator->FreeMemory(
18218                 1, // allocationCount
18219                 pAllocation);
18220             *pAllocation = VK_NULL_HANDLE;
18221             (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
18222             *pBuffer = VK_NULL_HANDLE;
18223             return res;
18224         }
18225         (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
18226         *pBuffer = VK_NULL_HANDLE;
18227         return res;
18228     }
18229     return res;
18230 }
18231 
vmaDestroyBuffer(VmaAllocator allocator,VkBuffer buffer,VmaAllocation allocation)18232 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
18233     VmaAllocator allocator,
18234     VkBuffer buffer,
18235     VmaAllocation allocation)
18236 {
18237     VMA_ASSERT(allocator);
18238 
18239     if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
18240     {
18241         return;
18242     }
18243 
18244     VMA_DEBUG_LOG("vmaDestroyBuffer");
18245 
18246     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18247 
18248 #if VMA_RECORDING_ENABLED
18249     if(allocator->GetRecorder() != VMA_NULL)
18250     {
18251         allocator->GetRecorder()->RecordDestroyBuffer(
18252             allocator->GetCurrentFrameIndex(),
18253             allocation);
18254     }
18255 #endif
18256 
18257     if(buffer != VK_NULL_HANDLE)
18258     {
18259         (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
18260     }
18261 
18262     if(allocation != VK_NULL_HANDLE)
18263     {
18264         allocator->FreeMemory(
18265             1, // allocationCount
18266             &allocation);
18267     }
18268 }
18269 
vmaCreateImage(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkImage * pImage,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18270 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
18271     VmaAllocator allocator,
18272     const VkImageCreateInfo* pImageCreateInfo,
18273     const VmaAllocationCreateInfo* pAllocationCreateInfo,
18274     VkImage* pImage,
18275     VmaAllocation* pAllocation,
18276     VmaAllocationInfo* pAllocationInfo)
18277 {
18278     VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
18279 
18280     if(pImageCreateInfo->extent.width == 0 ||
18281         pImageCreateInfo->extent.height == 0 ||
18282         pImageCreateInfo->extent.depth == 0 ||
18283         pImageCreateInfo->mipLevels == 0 ||
18284         pImageCreateInfo->arrayLayers == 0)
18285     {
18286         return VK_ERROR_INITIALIZATION_FAILED;
18287     }
18288 
18289     VMA_DEBUG_LOG("vmaCreateImage");
18290 
18291     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18292 
18293     *pImage = VK_NULL_HANDLE;
18294     *pAllocation = VK_NULL_HANDLE;
18295 
18296     // 1. Create VkImage.
18297     VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
18298         allocator->m_hDevice,
18299         pImageCreateInfo,
18300         allocator->GetAllocationCallbacks(),
18301         pImage);
18302     if(res >= 0)
18303     {
18304         VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
18305             VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
18306             VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
18307 
18308         // 2. Allocate memory using allocator.
18309         VkMemoryRequirements vkMemReq = {};
18310         bool requiresDedicatedAllocation = false;
18311         bool prefersDedicatedAllocation  = false;
18312         allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
18313             requiresDedicatedAllocation, prefersDedicatedAllocation);
18314 
18315         res = allocator->AllocateMemory(
18316             vkMemReq,
18317             requiresDedicatedAllocation,
18318             prefersDedicatedAllocation,
18319             VK_NULL_HANDLE, // dedicatedBuffer
18320             UINT32_MAX, // dedicatedBufferUsage
18321             *pImage, // dedicatedImage
18322             *pAllocationCreateInfo,
18323             suballocType,
18324             1, // allocationCount
18325             pAllocation);
18326 
18327 #if VMA_RECORDING_ENABLED
18328         if(allocator->GetRecorder() != VMA_NULL)
18329         {
18330             allocator->GetRecorder()->RecordCreateImage(
18331                 allocator->GetCurrentFrameIndex(),
18332                 *pImageCreateInfo,
18333                 *pAllocationCreateInfo,
18334                 *pAllocation);
18335         }
18336 #endif
18337 
18338         if(res >= 0)
18339         {
18340             // 3. Bind image with memory.
18341             if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
18342             {
18343                 res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);
18344             }
18345             if(res >= 0)
18346             {
18347                 // All steps succeeded.
18348                 #if VMA_STATS_STRING_ENABLED
18349                     (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
18350                 #endif
18351                 if(pAllocationInfo != VMA_NULL)
18352                 {
18353                     allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18354                 }
18355 
18356                 return VK_SUCCESS;
18357             }
18358             allocator->FreeMemory(
18359                 1, // allocationCount
18360                 pAllocation);
18361             *pAllocation = VK_NULL_HANDLE;
18362             (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
18363             *pImage = VK_NULL_HANDLE;
18364             return res;
18365         }
18366         (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
18367         *pImage = VK_NULL_HANDLE;
18368         return res;
18369     }
18370     return res;
18371 }
18372 
vmaDestroyImage(VmaAllocator allocator,VkImage image,VmaAllocation allocation)18373 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
18374     VmaAllocator allocator,
18375     VkImage image,
18376     VmaAllocation allocation)
18377 {
18378     VMA_ASSERT(allocator);
18379 
18380     if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
18381     {
18382         return;
18383     }
18384 
18385     VMA_DEBUG_LOG("vmaDestroyImage");
18386 
18387     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18388 
18389 #if VMA_RECORDING_ENABLED
18390     if(allocator->GetRecorder() != VMA_NULL)
18391     {
18392         allocator->GetRecorder()->RecordDestroyImage(
18393             allocator->GetCurrentFrameIndex(),
18394             allocation);
18395     }
18396 #endif
18397 
18398     if(image != VK_NULL_HANDLE)
18399     {
18400         (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
18401     }
18402     if(allocation != VK_NULL_HANDLE)
18403     {
18404         allocator->FreeMemory(
18405             1, // allocationCount
18406             &allocation);
18407     }
18408 }
18409 
vmaCreateVirtualBlock(const VmaVirtualBlockCreateInfo * VMA_NOT_NULL pCreateInfo,VmaVirtualBlock VMA_NULLABLE * VMA_NOT_NULL pVirtualBlock)18410 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock(
18411     const VmaVirtualBlockCreateInfo* VMA_NOT_NULL pCreateInfo,
18412     VmaVirtualBlock VMA_NULLABLE * VMA_NOT_NULL pVirtualBlock)
18413 {
18414     VMA_ASSERT(pCreateInfo && pVirtualBlock);
18415     VMA_ASSERT(pCreateInfo->size > 0);
18416     VMA_DEBUG_LOG("vmaCreateVirtualBlock");
18417     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18418     *pVirtualBlock = vma_new(pCreateInfo->pAllocationCallbacks, VmaVirtualBlock_T)(*pCreateInfo);
18419     VkResult res = (*pVirtualBlock)->Init();
18420     if(res < 0)
18421     {
18422         vma_delete(pCreateInfo->pAllocationCallbacks, *pVirtualBlock);
18423         *pVirtualBlock = VK_NULL_HANDLE;
18424     }
18425     return res;
18426 }
18427 
vmaDestroyVirtualBlock(VmaVirtualBlock VMA_NULLABLE virtualBlock)18428 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyVirtualBlock(VmaVirtualBlock VMA_NULLABLE virtualBlock)
18429 {
18430     if(virtualBlock != VK_NULL_HANDLE)
18431     {
18432         VMA_DEBUG_LOG("vmaDestroyVirtualBlock");
18433         VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18434         VkAllocationCallbacks allocationCallbacks = virtualBlock->m_AllocationCallbacks; // Have to copy the callbacks when destroying.
18435         vma_delete(&allocationCallbacks, virtualBlock);
18436     }
18437 }
18438 
vmaIsVirtualBlockEmpty(VmaVirtualBlock VMA_NOT_NULL virtualBlock)18439 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaIsVirtualBlockEmpty(VmaVirtualBlock VMA_NOT_NULL virtualBlock)
18440 {
18441     VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
18442     VMA_DEBUG_LOG("vmaIsVirtualBlockEmpty");
18443     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18444     return virtualBlock->IsEmpty() ? VK_TRUE : VK_FALSE;
18445 }
18446 
vmaGetVirtualAllocationInfo(VmaVirtualBlock VMA_NOT_NULL virtualBlock,VkDeviceSize offset,VmaVirtualAllocationInfo * VMA_NOT_NULL pVirtualAllocInfo)18447 VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
18448     VkDeviceSize offset, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo)
18449 {
18450     VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pVirtualAllocInfo != VMA_NULL);
18451     VMA_DEBUG_LOG("vmaGetVirtualAllocationInfo");
18452     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18453     virtualBlock->GetAllocationInfo(offset, *pVirtualAllocInfo);
18454 }
18455 
vmaVirtualAllocate(VmaVirtualBlock VMA_NOT_NULL virtualBlock,const VmaVirtualAllocationCreateInfo * VMA_NOT_NULL pCreateInfo,VkDeviceSize * VMA_NOT_NULL pOffset)18456 VMA_CALL_PRE VkResult VMA_CALL_POST vmaVirtualAllocate(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
18457     const VmaVirtualAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, VkDeviceSize* VMA_NOT_NULL pOffset)
18458 {
18459     VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pCreateInfo != VMA_NULL && pOffset != VMA_NULL);
18460     VMA_DEBUG_LOG("vmaVirtualAllocate");
18461     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18462     return virtualBlock->Allocate(*pCreateInfo, *pOffset);
18463 }
18464 
vmaVirtualFree(VmaVirtualBlock VMA_NOT_NULL virtualBlock,VkDeviceSize offset)18465 VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree(VmaVirtualBlock VMA_NOT_NULL virtualBlock, VkDeviceSize offset)
18466 {
18467     VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
18468     VMA_DEBUG_LOG("vmaVirtualFree");
18469     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18470     virtualBlock->Free(offset);
18471 }
18472 
vmaClearVirtualBlock(VmaVirtualBlock VMA_NOT_NULL virtualBlock)18473 VMA_CALL_PRE void VMA_CALL_POST vmaClearVirtualBlock(VmaVirtualBlock VMA_NOT_NULL virtualBlock)
18474 {
18475     VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
18476     VMA_DEBUG_LOG("vmaClearVirtualBlock");
18477     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18478     virtualBlock->Clear();
18479 }
18480 
vmaSetVirtualAllocationUserData(VmaVirtualBlock VMA_NOT_NULL virtualBlock,VkDeviceSize offset,void * VMA_NULLABLE pUserData)18481 VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
18482     VkDeviceSize offset, void* VMA_NULLABLE pUserData)
18483 {
18484     VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
18485     VMA_DEBUG_LOG("vmaSetVirtualAllocationUserData");
18486     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18487     virtualBlock->SetAllocationUserData(offset, pUserData);
18488 }
18489 
vmaCalculateVirtualBlockStats(VmaVirtualBlock VMA_NOT_NULL virtualBlock,VmaStatInfo * VMA_NOT_NULL pStatInfo)18490 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateVirtualBlockStats(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
18491     VmaStatInfo* VMA_NOT_NULL pStatInfo)
18492 {
18493     VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pStatInfo != VMA_NULL);
18494     VMA_DEBUG_LOG("vmaCalculateVirtualBlockStats");
18495     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18496     virtualBlock->CalculateStats(*pStatInfo);
18497 }
18498 
18499 #if VMA_STATS_STRING_ENABLED
18500 
vmaBuildVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,char * VMA_NULLABLE * VMA_NOT_NULL ppStatsString,VkBool32 detailedMap)18501 VMA_CALL_PRE void VMA_CALL_POST vmaBuildVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
18502     char* VMA_NULLABLE * VMA_NOT_NULL ppStatsString, VkBool32 detailedMap)
18503 {
18504     VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && ppStatsString != VMA_NULL);
18505     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18506     const VkAllocationCallbacks* allocationCallbacks = virtualBlock->GetAllocationCallbacks();
18507     VmaStringBuilder sb(allocationCallbacks);
18508     virtualBlock->BuildStatsString(detailedMap != VK_FALSE, sb);
18509     *ppStatsString = VmaCreateStringCopy(allocationCallbacks, sb.GetData(), sb.GetLength());
18510 }
18511 
18512 #endif // #if VMA_STATS_STRING_ENABLED
18513 
vmaFreeVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,char * VMA_NULLABLE pStatsString)18514 VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
18515     char* VMA_NULLABLE pStatsString)
18516 {
18517     if(pStatsString != VMA_NULL)
18518     {
18519         VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
18520         VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18521         VmaFreeString(virtualBlock->GetAllocationCallbacks(), pStatsString);
18522     }
18523 }
18524 
18525 #endif // #ifdef VMA_IMPLEMENTATION
18526 
18527 /**
18528 \page quick_start Quick start
18529 
18530 \section quick_start_project_setup Project setup
18531 
18532 Vulkan Memory Allocator comes in form of a "stb-style" single header file.
18533 You don't need to build it as a separate library project.
18534 You can add this file directly to your project and submit it to code repository next to your other source files.
18535 
18536 "Single header" doesn't mean that everything is contained in C/C++ declarations,
18537 like it tends to be in case of inline functions or C++ templates.
18538 It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro.
18539 If you don't do it properly, you will get linker errors.
18540 
18541 To do it properly:
18542 
18543 -# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library.
18544    This includes declarations of all members of the library.
18545 -# In exactly one CPP file define following macro before this include.
18546    It enables also internal definitions.
18547 
18548 \code
18549 #define VMA_IMPLEMENTATION
18550 #include "vk_mem_alloc.h"
18551 \endcode
18552 
18553 It may be a good idea to create dedicated CPP file just for this purpose.
18554 
18555 Note on language: This library is written in C++, but has C-compatible interface.
18556 Thus you can include and use vk_mem_alloc.h in C or C++ code, but full
18557 implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C.
18558 
18559 Please note that this library includes header `<vulkan/vulkan.h>`, which in turn
18560 includes `<windows.h>` on Windows. If you need some specific macros defined
18561 before including these headers (like `WIN32_LEAN_AND_MEAN` or
18562 `WINVER` for Windows, `VK_USE_PLATFORM_WIN32_KHR` for Vulkan), you must define
18563 them before every `#include` of this library.
18564 
18565 You may need to configure the way you import Vulkan functions.
18566 
18567 - By default, VMA assumes you you link statically with Vulkan API. If this is not the case,
18568   `#define VMA_STATIC_VULKAN_FUNCTIONS 0` before `#include` of the VMA implementation and use another way.
18569 - You can `#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1` and make sure `vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` globals are defined.
18570   All the remaining Vulkan functions will be fetched automatically.
18571 - Finally, you can provide your own pointers to all Vulkan functions needed by VMA using structure member
18572   VmaAllocatorCreateInfo::pVulkanFunctions, if you fetched them in some custom way e.g. using some loader like [Volk](https://github.com/zeux/volk).
18573 
18574 
18575 \section quick_start_initialization Initialization
18576 
18577 At program startup:
18578 
18579 -# Initialize Vulkan to have `VkPhysicalDevice`, `VkDevice` and `VkInstance` object.
18580 -# Fill VmaAllocatorCreateInfo structure and create #VmaAllocator object by
18581    calling vmaCreateAllocator().
18582 
18583 \code
18584 VmaAllocatorCreateInfo allocatorInfo = {};
18585 allocatorInfo.vulkanApiVersion = VK_API_VERSION_1_2;
18586 allocatorInfo.physicalDevice = physicalDevice;
18587 allocatorInfo.device = device;
18588 allocatorInfo.instance = instance;
18589 
18590 VmaAllocator allocator;
18591 vmaCreateAllocator(&allocatorInfo, &allocator);
18592 \endcode
18593 
18594 Only members `physicalDevice`, `device`, `instance` are required.
18595 However, you should inform the library which Vulkan version do you use by setting
18596 VmaAllocatorCreateInfo::vulkanApiVersion and which extensions did you enable
18597 by setting VmaAllocatorCreateInfo::flags (like #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT for VK_KHR_buffer_device_address).
18598 Otherwise, VMA would use only features of Vulkan 1.0 core with no extensions.
18599 
18600 
18601 \section quick_start_resource_allocation Resource allocation
18602 
18603 When you want to create a buffer or image:
18604 
18605 -# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure.
18606 -# Fill VmaAllocationCreateInfo structure.
18607 -# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory
18608    already allocated and bound to it.
18609 
18610 \code
18611 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
18612 bufferInfo.size = 65536;
18613 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
18614 
18615 VmaAllocationCreateInfo allocInfo = {};
18616 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
18617 
18618 VkBuffer buffer;
18619 VmaAllocation allocation;
18620 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
18621 \endcode
18622 
18623 Don't forget to destroy your objects when no longer needed:
18624 
18625 \code
18626 vmaDestroyBuffer(allocator, buffer, allocation);
18627 vmaDestroyAllocator(allocator);
18628 \endcode
18629 
18630 
18631 \page choosing_memory_type Choosing memory type
18632 
18633 Physical devices in Vulkan support various combinations of memory heaps and
18634 types. Help with choosing correct and optimal memory type for your specific
18635 resource is one of the key features of this library. You can use it by filling
18636 appropriate members of VmaAllocationCreateInfo structure, as described below.
18637 You can also combine multiple methods.
18638 
18639 -# If you just want to find memory type index that meets your requirements, you
18640    can use function: vmaFindMemoryTypeIndex(), vmaFindMemoryTypeIndexForBufferInfo(),
18641    vmaFindMemoryTypeIndexForImageInfo().
18642 -# If you want to allocate a region of device memory without association with any
18643    specific image or buffer, you can use function vmaAllocateMemory(). Usage of
18644    this function is not recommended and usually not needed.
18645    vmaAllocateMemoryPages() function is also provided for creating multiple allocations at once,
18646    which may be useful for sparse binding.
18647 -# If you already have a buffer or an image created, you want to allocate memory
18648    for it and then you will bind it yourself, you can use function
18649    vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage().
18650    For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory()
18651    or their extended versions: vmaBindBufferMemory2(), vmaBindImageMemory2().
18652 -# If you want to create a buffer or an image, allocate memory for it and bind
18653    them together, all in one call, you can use function vmaCreateBuffer(),
18654    vmaCreateImage(). This is the easiest and recommended way to use this library.
18655 
18656 When using 3. or 4., the library internally queries Vulkan for memory types
18657 supported for that buffer or image (function `vkGetBufferMemoryRequirements()`)
18658 and uses only one of these types.
18659 
18660 If no memory type can be found that meets all the requirements, these functions
18661 return `VK_ERROR_FEATURE_NOT_PRESENT`.
18662 
18663 You can leave VmaAllocationCreateInfo structure completely filled with zeros.
18664 It means no requirements are specified for memory type.
18665 It is valid, although not very useful.
18666 
18667 \section choosing_memory_type_usage Usage
18668 
18669 The easiest way to specify memory requirements is to fill member
18670 VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage.
18671 It defines high level, common usage types.
18672 For more details, see description of this enum.
18673 
18674 For example, if you want to create a uniform buffer that will be filled using
18675 transfer only once or infrequently and used for rendering every frame, you can
18676 do it using following code:
18677 
18678 \code
18679 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
18680 bufferInfo.size = 65536;
18681 bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
18682 
18683 VmaAllocationCreateInfo allocInfo = {};
18684 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
18685 
18686 VkBuffer buffer;
18687 VmaAllocation allocation;
18688 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
18689 \endcode
18690 
18691 \section choosing_memory_type_required_preferred_flags Required and preferred flags
18692 
18693 You can specify more detailed requirements by filling members
18694 VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags
18695 with a combination of bits from enum `VkMemoryPropertyFlags`. For example,
18696 if you want to create a buffer that will be persistently mapped on host (so it
18697 must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`,
18698 use following code:
18699 
18700 \code
18701 VmaAllocationCreateInfo allocInfo = {};
18702 allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
18703 allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
18704 allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
18705 
18706 VkBuffer buffer;
18707 VmaAllocation allocation;
18708 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
18709 \endcode
18710 
18711 A memory type is chosen that has all the required flags and as many preferred
18712 flags set as possible.
18713 
18714 If you use VmaAllocationCreateInfo::usage, it is just internally converted to
18715 a set of required and preferred flags.
18716 
18717 \section choosing_memory_type_explicit_memory_types Explicit memory types
18718 
18719 If you inspected memory types available on the physical device and you have
18720 a preference for memory types that you want to use, you can fill member
18721 VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set
18722 means that a memory type with that index is allowed to be used for the
18723 allocation. Special value 0, just like `UINT32_MAX`, means there are no
18724 restrictions to memory type index.
18725 
18726 Please note that this member is NOT just a memory type index.
18727 Still you can use it to choose just one, specific memory type.
18728 For example, if you already determined that your buffer should be created in
18729 memory type 2, use following code:
18730 
18731 \code
18732 uint32_t memoryTypeIndex = 2;
18733 
18734 VmaAllocationCreateInfo allocInfo = {};
18735 allocInfo.memoryTypeBits = 1u << memoryTypeIndex;
18736 
18737 VkBuffer buffer;
18738 VmaAllocation allocation;
18739 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
18740 \endcode
18741 
18742 
18743 \section choosing_memory_type_custom_memory_pools Custom memory pools
18744 
18745 If you allocate from custom memory pool, all the ways of specifying memory
18746 requirements described above are not applicable and the aforementioned members
18747 of VmaAllocationCreateInfo structure are ignored. Memory type is selected
18748 explicitly when creating the pool and then used to make all the allocations from
18749 that pool. For further details, see \ref custom_memory_pools.
18750 
18751 \section choosing_memory_type_dedicated_allocations Dedicated allocations
18752 
18753 Memory for allocations is reserved out of larger block of `VkDeviceMemory`
18754 allocated from Vulkan internally. That is the main feature of this whole library.
18755 You can still request a separate memory block to be created for an allocation,
18756 just like you would do in a trivial solution without using any allocator.
18757 In that case, a buffer or image is always bound to that memory at offset 0.
18758 This is called a "dedicated allocation".
18759 You can explicitly request it by using flag #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
18760 The library can also internally decide to use dedicated allocation in some cases, e.g.:
18761 
18762 - When the size of the allocation is large.
18763 - When [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension is enabled
18764   and it reports that dedicated allocation is required or recommended for the resource.
18765 - When allocation of next big memory block fails due to not enough device memory,
18766   but allocation with the exact requested size succeeds.
18767 
18768 
18769 \page memory_mapping Memory mapping
18770 
18771 To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`,
18772 to be able to read from it or write to it in CPU code.
18773 Mapping is possible only of memory allocated from a memory type that has
18774 `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag.
18775 Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose.
18776 You can use them directly with memory allocated by this library,
18777 but it is not recommended because of following issue:
18778 Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed.
18779 This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan.
18780 Because of this, Vulkan Memory Allocator provides following facilities:
18781 
18782 \section memory_mapping_mapping_functions Mapping functions
18783 
18784 The library provides following functions for mapping of a specific #VmaAllocation: vmaMapMemory(), vmaUnmapMemory().
18785 They are safer and more convenient to use than standard Vulkan functions.
18786 You can map an allocation multiple times simultaneously - mapping is reference-counted internally.
18787 You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block.
18788 The way it is implemented is that the library always maps entire memory block, not just region of the allocation.
18789 For further details, see description of vmaMapMemory() function.
18790 Example:
18791 
18792 \code
18793 // Having these objects initialized:
18794 
18795 struct ConstantBuffer
18796 {
18797     ...
18798 };
18799 ConstantBuffer constantBufferData;
18800 
18801 VmaAllocator allocator;
18802 VkBuffer constantBuffer;
18803 VmaAllocation constantBufferAllocation;
18804 
18805 // You can map and fill your buffer using following code:
18806 
18807 void* mappedData;
18808 vmaMapMemory(allocator, constantBufferAllocation, &mappedData);
18809 memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
18810 vmaUnmapMemory(allocator, constantBufferAllocation);
18811 \endcode
18812 
18813 When mapping, you may see a warning from Vulkan validation layer similar to this one:
18814 
18815 <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>
18816 
18817 It happens because the library maps entire `VkDeviceMemory` block, where different
18818 types of images and buffers may end up together, especially on GPUs with unified memory like Intel.
18819 You can safely ignore it if you are sure you access only memory of the intended
18820 object that you wanted to map.
18821 
18822 
18823 \section memory_mapping_persistently_mapped_memory Persistently mapped memory
18824 
18825 Kepping your memory persistently mapped is generally OK in Vulkan.
18826 You don't need to unmap it before using its data on the GPU.
18827 The library provides a special feature designed for that:
18828 Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in
18829 VmaAllocationCreateInfo::flags stay mapped all the time,
18830 so you can just access CPU pointer to it any time
18831 without a need to call any "map" or "unmap" function.
18832 Example:
18833 
18834 \code
18835 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
18836 bufCreateInfo.size = sizeof(ConstantBuffer);
18837 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
18838 
18839 VmaAllocationCreateInfo allocCreateInfo = {};
18840 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
18841 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
18842 
18843 VkBuffer buf;
18844 VmaAllocation alloc;
18845 VmaAllocationInfo allocInfo;
18846 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
18847 
18848 // Buffer is already mapped. You can access its memory.
18849 memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
18850 \endcode
18851 
18852 There are some exceptions though, when you should consider mapping memory only for a short period of time:
18853 
18854 - When operating system is Windows 7 or 8.x (Windows 10 is not affected because it uses WDDM2),
18855   device is discrete AMD GPU,
18856   and memory type is the special 256 MiB pool of `DEVICE_LOCAL + HOST_VISIBLE` memory
18857   (selected when you use #VMA_MEMORY_USAGE_CPU_TO_GPU),
18858   then whenever a memory block allocated from this memory type stays mapped
18859   for the time of any call to `vkQueueSubmit()` or `vkQueuePresentKHR()`, this
18860   block is migrated by WDDM to system RAM, which degrades performance. It doesn't
18861   matter if that particular memory block is actually used by the command buffer
18862   being submitted.
18863 - Keeping many large memory blocks mapped may impact performance or stability of some debugging tools.
18864 
18865 \section memory_mapping_cache_control Cache flush and invalidate
18866 
18867 Memory in Vulkan doesn't need to be unmapped before using it on GPU,
18868 but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set,
18869 you need to manually **invalidate** cache before reading of mapped pointer
18870 and **flush** cache after writing to mapped pointer.
18871 Map/unmap operations don't do that automatically.
18872 Vulkan provides following functions for this purpose `vkFlushMappedMemoryRanges()`,
18873 `vkInvalidateMappedMemoryRanges()`, but this library provides more convenient
18874 functions that refer to given allocation object: vmaFlushAllocation(),
18875 vmaInvalidateAllocation(),
18876 or multiple objects at once: vmaFlushAllocations(), vmaInvalidateAllocations().
18877 
18878 Regions of memory specified for flush/invalidate must be aligned to
18879 `VkPhysicalDeviceLimits::nonCoherentAtomSize`. This is automatically ensured by the library.
18880 In any memory type that is `HOST_VISIBLE` but not `HOST_COHERENT`, all allocations
18881 within blocks are aligned to this value, so their offsets are always multiply of
18882 `nonCoherentAtomSize` and two different allocations never share same "line" of this size.
18883 
18884 Please note that memory allocated with #VMA_MEMORY_USAGE_CPU_ONLY is guaranteed to be `HOST_COHERENT`.
18885 
18886 Also, Windows drivers from all 3 **PC** GPU vendors (AMD, Intel, NVIDIA)
18887 currently provide `HOST_COHERENT` flag on all memory types that are
18888 `HOST_VISIBLE`, so on this platform you may not need to bother.
18889 
18890 \section memory_mapping_finding_if_memory_mappable Finding out if memory is mappable
18891 
18892 It may happen that your allocation ends up in memory that is `HOST_VISIBLE` (available for mapping)
18893 despite it wasn't explicitly requested.
18894 For example, application may work on integrated graphics with unified memory (like Intel) or
18895 allocation from video memory might have failed, so the library chose system memory as fallback.
18896 
18897 You can detect this case and map such allocation to access its memory on CPU directly,
18898 instead of launching a transfer operation.
18899 In order to do that: inspect `allocInfo.memoryType`, call vmaGetMemoryTypeProperties(),
18900 and look for `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag in properties of that memory type.
18901 
18902 \code
18903 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
18904 bufCreateInfo.size = sizeof(ConstantBuffer);
18905 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
18906 
18907 VmaAllocationCreateInfo allocCreateInfo = {};
18908 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
18909 allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
18910 
18911 VkBuffer buf;
18912 VmaAllocation alloc;
18913 VmaAllocationInfo allocInfo;
18914 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
18915 
18916 VkMemoryPropertyFlags memFlags;
18917 vmaGetMemoryTypeProperties(allocator, allocInfo.memoryType, &memFlags);
18918 if((memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
18919 {
18920     // Allocation ended up in mappable memory. You can map it and access it directly.
18921     void* mappedData;
18922     vmaMapMemory(allocator, alloc, &mappedData);
18923     memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
18924     vmaUnmapMemory(allocator, alloc);
18925 }
18926 else
18927 {
18928     // Allocation ended up in non-mappable memory.
18929     // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
18930 }
18931 \endcode
18932 
18933 You can even use #VMA_ALLOCATION_CREATE_MAPPED_BIT flag while creating allocations
18934 that are not necessarily `HOST_VISIBLE` (e.g. using #VMA_MEMORY_USAGE_GPU_ONLY).
18935 If the allocation ends up in memory type that is `HOST_VISIBLE`, it will be persistently mapped and you can use it directly.
18936 If not, the flag is just ignored.
18937 Example:
18938 
18939 \code
18940 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
18941 bufCreateInfo.size = sizeof(ConstantBuffer);
18942 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
18943 
18944 VmaAllocationCreateInfo allocCreateInfo = {};
18945 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
18946 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
18947 
18948 VkBuffer buf;
18949 VmaAllocation alloc;
18950 VmaAllocationInfo allocInfo;
18951 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
18952 
18953 if(allocInfo.pMappedData != nullptr)
18954 {
18955     // Allocation ended up in mappable memory.
18956     // It is persistently mapped. You can access it directly.
18957     memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
18958 }
18959 else
18960 {
18961     // Allocation ended up in non-mappable memory.
18962     // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
18963 }
18964 \endcode
18965 
18966 
18967 \page staying_within_budget Staying within budget
18968 
18969 When developing a graphics-intensive game or program, it is important to avoid allocating
18970 more GPU memory than it is physically available. When the memory is over-committed,
18971 various bad things can happen, depending on the specific GPU, graphics driver, and
18972 operating system:
18973 
18974 - It may just work without any problems.
18975 - The application may slow down because some memory blocks are moved to system RAM
18976   and the GPU has to access them through PCI Express bus.
18977 - A new allocation may take very long time to complete, even few seconds, and possibly
18978   freeze entire system.
18979 - The new allocation may fail with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
18980 - It may even result in GPU crash (TDR), observed as `VK_ERROR_DEVICE_LOST`
18981   returned somewhere later.
18982 
18983 \section staying_within_budget_querying_for_budget Querying for budget
18984 
18985 To query for current memory usage and available budget, use function vmaGetBudget().
18986 Returned structure #VmaBudget contains quantities expressed in bytes, per Vulkan memory heap.
18987 
18988 Please note that this function returns different information and works faster than
18989 vmaCalculateStats(). vmaGetBudget() can be called every frame or even before every
18990 allocation, while vmaCalculateStats() is intended to be used rarely,
18991 only to obtain statistical information, e.g. for debugging purposes.
18992 
18993 It is recommended to use <b>VK_EXT_memory_budget</b> device extension to obtain information
18994 about the budget from Vulkan device. VMA is able to use this extension automatically.
18995 When not enabled, the allocator behaves same way, but then it estimates current usage
18996 and available budget based on its internal information and Vulkan memory heap sizes,
18997 which may be less precise. In order to use this extension:
18998 
18999 1. Make sure extensions VK_EXT_memory_budget and VK_KHR_get_physical_device_properties2
19000    required by it are available and enable them. Please note that the first is a device
19001    extension and the second is instance extension!
19002 2. Use flag #VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT when creating #VmaAllocator object.
19003 3. Make sure to call vmaSetCurrentFrameIndex() every frame. Budget is queried from
19004    Vulkan inside of it to avoid overhead of querying it with every allocation.
19005 
19006 \section staying_within_budget_controlling_memory_usage Controlling memory usage
19007 
19008 There are many ways in which you can try to stay within the budget.
19009 
19010 First, when making new allocation requires allocating a new memory block, the library
19011 tries not to exceed the budget automatically. If a block with default recommended size
19012 (e.g. 256 MB) would go over budget, a smaller block is allocated, possibly even
19013 dedicated memory for just this resource.
19014 
19015 If the size of the requested resource plus current memory usage is more than the
19016 budget, by default the library still tries to create it, leaving it to the Vulkan
19017 implementation whether the allocation succeeds or fails. You can change this behavior
19018 by using #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag. With it, the allocation is
19019 not made if it would exceed the budget or if the budget is already exceeded.
19020 Some other allocations become lost instead to make room for it, if the mechanism of
19021 [lost allocations](@ref lost_allocations) is used.
19022 If that is not possible, the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
19023 Example usage pattern may be to pass the #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag
19024 when creating resources that are not essential for the application (e.g. the texture
19025 of a specific object) and not to pass it when creating critically important resources
19026 (e.g. render targets).
19027 
19028 Finally, you can also use #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT flag to make sure
19029 a new allocation is created only when it fits inside one of the existing memory blocks.
19030 If it would require to allocate a new block, if fails instead with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
19031 This also ensures that the function call is very fast because it never goes to Vulkan
19032 to obtain a new block.
19033 
19034 Please note that creating \ref custom_memory_pools with VmaPoolCreateInfo::minBlockCount
19035 set to more than 0 will try to allocate memory blocks without checking whether they
19036 fit within budget.
19037 
19038 
19039 \page resource_aliasing Resource aliasing (overlap)
19040 
19041 New explicit graphics APIs (Vulkan and Direct3D 12), thanks to manual memory
19042 management, give an opportunity to alias (overlap) multiple resources in the
19043 same region of memory - a feature not available in the old APIs (Direct3D 11, OpenGL).
19044 It can be useful to save video memory, but it must be used with caution.
19045 
19046 For example, if you know the flow of your whole render frame in advance, you
19047 are going to use some intermediate textures or buffers only during a small range of render passes,
19048 and you know these ranges don't overlap in time, you can bind these resources to
19049 the same place in memory, even if they have completely different parameters (width, height, format etc.).
19050 
19051 ![Resource aliasing (overlap)](../gfx/Aliasing.png)
19052 
19053 Such scenario is possible using VMA, but you need to create your images manually.
19054 Then you need to calculate parameters of an allocation to be made using formula:
19055 
19056 - allocation size = max(size of each image)
19057 - allocation alignment = max(alignment of each image)
19058 - allocation memoryTypeBits = bitwise AND(memoryTypeBits of each image)
19059 
19060 Following example shows two different images bound to the same place in memory,
19061 allocated to fit largest of them.
19062 
19063 \code
19064 // A 512x512 texture to be sampled.
19065 VkImageCreateInfo img1CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
19066 img1CreateInfo.imageType = VK_IMAGE_TYPE_2D;
19067 img1CreateInfo.extent.width = 512;
19068 img1CreateInfo.extent.height = 512;
19069 img1CreateInfo.extent.depth = 1;
19070 img1CreateInfo.mipLevels = 10;
19071 img1CreateInfo.arrayLayers = 1;
19072 img1CreateInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
19073 img1CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
19074 img1CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
19075 img1CreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
19076 img1CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
19077 
19078 // A full screen texture to be used as color attachment.
19079 VkImageCreateInfo img2CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
19080 img2CreateInfo.imageType = VK_IMAGE_TYPE_2D;
19081 img2CreateInfo.extent.width = 1920;
19082 img2CreateInfo.extent.height = 1080;
19083 img2CreateInfo.extent.depth = 1;
19084 img2CreateInfo.mipLevels = 1;
19085 img2CreateInfo.arrayLayers = 1;
19086 img2CreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
19087 img2CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
19088 img2CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
19089 img2CreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
19090 img2CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
19091 
19092 VkImage img1;
19093 res = vkCreateImage(device, &img1CreateInfo, nullptr, &img1);
19094 VkImage img2;
19095 res = vkCreateImage(device, &img2CreateInfo, nullptr, &img2);
19096 
19097 VkMemoryRequirements img1MemReq;
19098 vkGetImageMemoryRequirements(device, img1, &img1MemReq);
19099 VkMemoryRequirements img2MemReq;
19100 vkGetImageMemoryRequirements(device, img2, &img2MemReq);
19101 
19102 VkMemoryRequirements finalMemReq = {};
19103 finalMemReq.size = std::max(img1MemReq.size, img2MemReq.size);
19104 finalMemReq.alignment = std::max(img1MemReq.alignment, img2MemReq.alignment);
19105 finalMemReq.memoryTypeBits = img1MemReq.memoryTypeBits & img2MemReq.memoryTypeBits;
19106 // Validate if(finalMemReq.memoryTypeBits != 0)
19107 
19108 VmaAllocationCreateInfo allocCreateInfo = {};
19109 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
19110 
19111 VmaAllocation alloc;
19112 res = vmaAllocateMemory(allocator, &finalMemReq, &allocCreateInfo, &alloc, nullptr);
19113 
19114 res = vmaBindImageMemory(allocator, alloc, img1);
19115 res = vmaBindImageMemory(allocator, alloc, img2);
19116 
19117 // You can use img1, img2 here, but not at the same time!
19118 
19119 vmaFreeMemory(allocator, alloc);
19120 vkDestroyImage(allocator, img2, nullptr);
19121 vkDestroyImage(allocator, img1, nullptr);
19122 \endcode
19123 
19124 Remember that using resources that alias in memory requires proper synchronization.
19125 You need to issue a memory barrier to make sure commands that use `img1` and `img2`
19126 don't overlap on GPU timeline.
19127 You also need to treat a resource after aliasing as uninitialized - containing garbage data.
19128 For example, if you use `img1` and then want to use `img2`, you need to issue
19129 an image memory barrier for `img2` with `oldLayout` = `VK_IMAGE_LAYOUT_UNDEFINED`.
19130 
19131 Additional considerations:
19132 
19133 - Vulkan also allows to interpret contents of memory between aliasing resources consistently in some cases.
19134 See chapter 11.8. "Memory Aliasing" of Vulkan specification or `VK_IMAGE_CREATE_ALIAS_BIT` flag.
19135 - You can create more complex layout where different images and buffers are bound
19136 at different offsets inside one large allocation. For example, one can imagine
19137 a big texture used in some render passes, aliasing with a set of many small buffers
19138 used between in some further passes. To bind a resource at non-zero offset of an allocation,
19139 use vmaBindBufferMemory2() / vmaBindImageMemory2().
19140 - Before allocating memory for the resources you want to alias, check `memoryTypeBits`
19141 returned in memory requirements of each resource to make sure the bits overlap.
19142 Some GPUs may expose multiple memory types suitable e.g. only for buffers or
19143 images with `COLOR_ATTACHMENT` usage, so the sets of memory types supported by your
19144 resources may be disjoint. Aliasing them is not possible in that case.
19145 
19146 
19147 \page custom_memory_pools Custom memory pools
19148 
19149 A memory pool contains a number of `VkDeviceMemory` blocks.
19150 The library automatically creates and manages default pool for each memory type available on the device.
19151 Default memory pool automatically grows in size.
19152 Size of allocated blocks is also variable and managed automatically.
19153 
19154 You can create custom pool and allocate memory out of it.
19155 It can be useful if you want to:
19156 
19157 - Keep certain kind of allocations separate from others.
19158 - Enforce particular, fixed size of Vulkan memory blocks.
19159 - Limit maximum amount of Vulkan memory allocated for that pool.
19160 - Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool.
19161 - Use extra parameters for a set of your allocations that are available in #VmaPoolCreateInfo but not in
19162   #VmaAllocationCreateInfo - e.g., custom minimum alignment, custom `pNext` chain.
19163 
19164 To use custom memory pools:
19165 
19166 -# Fill VmaPoolCreateInfo structure.
19167 -# Call vmaCreatePool() to obtain #VmaPool handle.
19168 -# When making an allocation, set VmaAllocationCreateInfo::pool to this handle.
19169    You don't need to specify any other parameters of this structure, like `usage`.
19170 
19171 Example:
19172 
19173 \code
19174 // Create a pool that can have at most 2 blocks, 128 MiB each.
19175 VmaPoolCreateInfo poolCreateInfo = {};
19176 poolCreateInfo.memoryTypeIndex = ...
19177 poolCreateInfo.blockSize = 128ull * 1024 * 1024;
19178 poolCreateInfo.maxBlockCount = 2;
19179 
19180 VmaPool pool;
19181 vmaCreatePool(allocator, &poolCreateInfo, &pool);
19182 
19183 // Allocate a buffer out of it.
19184 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
19185 bufCreateInfo.size = 1024;
19186 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
19187 
19188 VmaAllocationCreateInfo allocCreateInfo = {};
19189 allocCreateInfo.pool = pool;
19190 
19191 VkBuffer buf;
19192 VmaAllocation alloc;
19193 VmaAllocationInfo allocInfo;
19194 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
19195 \endcode
19196 
19197 You have to free all allocations made from this pool before destroying it.
19198 
19199 \code
19200 vmaDestroyBuffer(allocator, buf, alloc);
19201 vmaDestroyPool(allocator, pool);
19202 \endcode
19203 
19204 \section custom_memory_pools_MemTypeIndex Choosing memory type index
19205 
19206 When creating a pool, you must explicitly specify memory type index.
19207 To find the one suitable for your buffers or images, you can use helper functions
19208 vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo().
19209 You need to provide structures with example parameters of buffers or images
19210 that you are going to create in that pool.
19211 
19212 \code
19213 VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
19214 exampleBufCreateInfo.size = 1024; // Whatever.
19215 exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; // Change if needed.
19216 
19217 VmaAllocationCreateInfo allocCreateInfo = {};
19218 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; // Change if needed.
19219 
19220 uint32_t memTypeIndex;
19221 vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex);
19222 
19223 VmaPoolCreateInfo poolCreateInfo = {};
19224 poolCreateInfo.memoryTypeIndex = memTypeIndex;
19225 // ...
19226 \endcode
19227 
19228 When creating buffers/images allocated in that pool, provide following parameters:
19229 
19230 - `VkBufferCreateInfo`: Prefer to pass same parameters as above.
19231   Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior.
19232   Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers
19233   or the other way around.
19234 - VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member.
19235   Other members are ignored anyway.
19236 
19237 \section linear_algorithm Linear allocation algorithm
19238 
19239 Each Vulkan memory block managed by this library has accompanying metadata that
19240 keeps track of used and unused regions. By default, the metadata structure and
19241 algorithm tries to find best place for new allocations among free regions to
19242 optimize memory usage. This way you can allocate and free objects in any order.
19243 
19244 ![Default allocation algorithm](../gfx/Linear_allocator_1_algo_default.png)
19245 
19246 Sometimes there is a need to use simpler, linear allocation algorithm. You can
19247 create custom pool that uses such algorithm by adding flag
19248 #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
19249 #VmaPool object. Then an alternative metadata management is used. It always
19250 creates new allocations after last one and doesn't reuse free regions after
19251 allocations freed in the middle. It results in better allocation performance and
19252 less memory consumed by metadata.
19253 
19254 ![Linear allocation algorithm](../gfx/Linear_allocator_2_algo_linear.png)
19255 
19256 With this one flag, you can create a custom pool that can be used in many ways:
19257 free-at-once, stack, double stack, and ring buffer. See below for details.
19258 You don't need to specify explicitly which of these options you are going to use - it is detected automatically.
19259 
19260 \subsection linear_algorithm_free_at_once Free-at-once
19261 
19262 In a pool that uses linear algorithm, you still need to free all the allocations
19263 individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free
19264 them in any order. New allocations are always made after last one - free space
19265 in the middle is not reused. However, when you release all the allocation and
19266 the pool becomes empty, allocation starts from the beginning again. This way you
19267 can use linear algorithm to speed up creation of allocations that you are going
19268 to release all at once.
19269 
19270 ![Free-at-once](../gfx/Linear_allocator_3_free_at_once.png)
19271 
19272 This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
19273 value that allows multiple memory blocks.
19274 
19275 \subsection linear_algorithm_stack Stack
19276 
19277 When you free an allocation that was created last, its space can be reused.
19278 Thanks to this, if you always release allocations in the order opposite to their
19279 creation (LIFO - Last In First Out), you can achieve behavior of a stack.
19280 
19281 ![Stack](../gfx/Linear_allocator_4_stack.png)
19282 
19283 This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
19284 value that allows multiple memory blocks.
19285 
19286 \subsection linear_algorithm_double_stack Double stack
19287 
19288 The space reserved by a custom pool with linear algorithm may be used by two
19289 stacks:
19290 
19291 - First, default one, growing up from offset 0.
19292 - Second, "upper" one, growing down from the end towards lower offsets.
19293 
19294 To make allocation from the upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT
19295 to VmaAllocationCreateInfo::flags.
19296 
19297 ![Double stack](../gfx/Linear_allocator_7_double_stack.png)
19298 
19299 Double stack is available only in pools with one memory block -
19300 VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
19301 
19302 When the two stacks' ends meet so there is not enough space between them for a
19303 new allocation, such allocation fails with usual
19304 `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
19305 
19306 \subsection linear_algorithm_ring_buffer Ring buffer
19307 
19308 When you free some allocations from the beginning and there is not enough free space
19309 for a new one at the end of a pool, allocator's "cursor" wraps around to the
19310 beginning and starts allocation there. Thanks to this, if you always release
19311 allocations in the same order as you created them (FIFO - First In First Out),
19312 you can achieve behavior of a ring buffer / queue.
19313 
19314 ![Ring buffer](../gfx/Linear_allocator_5_ring_buffer.png)
19315 
19316 Pools with linear algorithm support [lost allocations](@ref lost_allocations) when used as ring buffer.
19317 If there is not enough free space for a new allocation, but existing allocations
19318 from the front of the queue can become lost, they become lost and the allocation
19319 succeeds.
19320 
19321 ![Ring buffer with lost allocations](../gfx/Linear_allocator_6_ring_buffer_lost.png)
19322 
19323 Ring buffer is available only in pools with one memory block -
19324 VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
19325 
19326 \section buddy_algorithm Buddy allocation algorithm
19327 
19328 There is another allocation algorithm that can be used with custom pools, called
19329 "buddy". Its internal data structure is based on a binary tree of blocks, each having
19330 size that is a power of two and a half of its parent's size. When you want to
19331 allocate memory of certain size, a free node in the tree is located. If it is too
19332 large, it is recursively split into two halves (called "buddies"). However, if
19333 requested allocation size is not a power of two, the size of the allocation is
19334 aligned up to the nearest power of two and the remaining space is wasted. When
19335 two buddy nodes become free, they are merged back into one larger node.
19336 
19337 ![Buddy allocator](../gfx/Buddy_allocator.png)
19338 
19339 The advantage of buddy allocation algorithm over default algorithm is faster
19340 allocation and deallocation, as well as smaller external fragmentation. The
19341 disadvantage is more wasted space (internal fragmentation).
19342 For more information, please search the Internet for "Buddy memory allocation" -
19343 sources that describe this concept in general.
19344 
19345 To use buddy allocation algorithm with a custom pool, add flag
19346 #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
19347 #VmaPool object.
19348 
19349 Several limitations apply to pools that use buddy algorithm:
19350 
19351 - It is recommended to use VmaPoolCreateInfo::blockSize that is a power of two.
19352   Otherwise, only largest power of two smaller than the size is used for
19353   allocations. The remaining space always stays unused.
19354 - [Margins](@ref debugging_memory_usage_margins) and
19355   [corruption detection](@ref debugging_memory_usage_corruption_detection)
19356   don't work in such pools.
19357 - [Lost allocations](@ref lost_allocations) don't work in such pools. You can
19358   use them, but they never become lost. Support may be added in the future.
19359 - [Defragmentation](@ref defragmentation) doesn't work with allocations made from
19360   such pool.
19361 
19362 \page defragmentation Defragmentation
19363 
19364 Interleaved allocations and deallocations of many objects of varying size can
19365 cause fragmentation over time, which can lead to a situation where the library is unable
19366 to find a continuous range of free memory for a new allocation despite there is
19367 enough free space, just scattered across many small free ranges between existing
19368 allocations.
19369 
19370 To mitigate this problem, you can use defragmentation feature:
19371 structure #VmaDefragmentationInfo2, function vmaDefragmentationBegin(), vmaDefragmentationEnd().
19372 Given set of allocations,
19373 this function can move them to compact used memory, ensure more continuous free
19374 space and possibly also free some `VkDeviceMemory` blocks.
19375 
19376 What the defragmentation does is:
19377 
19378 - Updates #VmaAllocation objects to point to new `VkDeviceMemory` and offset.
19379   After allocation has been moved, its VmaAllocationInfo::deviceMemory and/or
19380   VmaAllocationInfo::offset changes. You must query them again using
19381   vmaGetAllocationInfo() if you need them.
19382 - Moves actual data in memory.
19383 
19384 What it doesn't do, so you need to do it yourself:
19385 
19386 - Recreate buffers and images that were bound to allocations that were defragmented and
19387   bind them with their new places in memory.
19388   You must use `vkDestroyBuffer()`, `vkDestroyImage()`,
19389   `vkCreateBuffer()`, `vkCreateImage()`, vmaBindBufferMemory(), vmaBindImageMemory()
19390   for that purpose and NOT vmaDestroyBuffer(),
19391   vmaDestroyImage(), vmaCreateBuffer(), vmaCreateImage(), because you don't need to
19392   destroy or create allocation objects!
19393 - Recreate views and update descriptors that point to these buffers and images.
19394 
19395 \section defragmentation_cpu Defragmenting CPU memory
19396 
19397 Following example demonstrates how you can run defragmentation on CPU.
19398 Only allocations created in memory types that are `HOST_VISIBLE` can be defragmented.
19399 Others are ignored.
19400 
19401 The way it works is:
19402 
19403 - It temporarily maps entire memory blocks when necessary.
19404 - It moves data using `memmove()` function.
19405 
19406 \code
19407 // Given following variables already initialized:
19408 VkDevice device;
19409 VmaAllocator allocator;
19410 std::vector<VkBuffer> buffers;
19411 std::vector<VmaAllocation> allocations;
19412 
19413 
19414 const uint32_t allocCount = (uint32_t)allocations.size();
19415 std::vector<VkBool32> allocationsChanged(allocCount);
19416 
19417 VmaDefragmentationInfo2 defragInfo = {};
19418 defragInfo.allocationCount = allocCount;
19419 defragInfo.pAllocations = allocations.data();
19420 defragInfo.pAllocationsChanged = allocationsChanged.data();
19421 defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE; // No limit.
19422 defragInfo.maxCpuAllocationsToMove = UINT32_MAX; // No limit.
19423 
19424 VmaDefragmentationContext defragCtx;
19425 vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx);
19426 vmaDefragmentationEnd(allocator, defragCtx);
19427 
19428 for(uint32_t i = 0; i < allocCount; ++i)
19429 {
19430     if(allocationsChanged[i])
19431     {
19432         // Destroy buffer that is immutably bound to memory region which is no longer valid.
19433         vkDestroyBuffer(device, buffers[i], nullptr);
19434 
19435         // Create new buffer with same parameters.
19436         VkBufferCreateInfo bufferInfo = ...;
19437         vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
19438 
19439         // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
19440 
19441         // Bind new buffer to new memory region. Data contained in it is already moved.
19442         VmaAllocationInfo allocInfo;
19443         vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
19444         vmaBindBufferMemory(allocator, allocations[i], buffers[i]);
19445     }
19446 }
19447 \endcode
19448 
19449 Setting VmaDefragmentationInfo2::pAllocationsChanged is optional.
19450 This output array tells whether particular allocation in VmaDefragmentationInfo2::pAllocations at the same index
19451 has been modified during defragmentation.
19452 You can pass null, but you then need to query every allocation passed to defragmentation
19453 for new parameters using vmaGetAllocationInfo() if you might need to recreate and rebind a buffer or image associated with it.
19454 
19455 If you use [Custom memory pools](@ref choosing_memory_type_custom_memory_pools),
19456 you can fill VmaDefragmentationInfo2::poolCount and VmaDefragmentationInfo2::pPools
19457 instead of VmaDefragmentationInfo2::allocationCount and VmaDefragmentationInfo2::pAllocations
19458 to defragment all allocations in given pools.
19459 You cannot use VmaDefragmentationInfo2::pAllocationsChanged in that case.
19460 You can also combine both methods.
19461 
19462 \section defragmentation_gpu Defragmenting GPU memory
19463 
19464 It is also possible to defragment allocations created in memory types that are not `HOST_VISIBLE`.
19465 To do that, you need to pass a command buffer that meets requirements as described in
19466 VmaDefragmentationInfo2::commandBuffer. The way it works is:
19467 
19468 - It creates temporary buffers and binds them to entire memory blocks when necessary.
19469 - It issues `vkCmdCopyBuffer()` to passed command buffer.
19470 
19471 Example:
19472 
19473 \code
19474 // Given following variables already initialized:
19475 VkDevice device;
19476 VmaAllocator allocator;
19477 VkCommandBuffer commandBuffer;
19478 std::vector<VkBuffer> buffers;
19479 std::vector<VmaAllocation> allocations;
19480 
19481 
19482 const uint32_t allocCount = (uint32_t)allocations.size();
19483 std::vector<VkBool32> allocationsChanged(allocCount);
19484 
19485 VkCommandBufferBeginInfo cmdBufBeginInfo = ...;
19486 vkBeginCommandBuffer(commandBuffer, &cmdBufBeginInfo);
19487 
19488 VmaDefragmentationInfo2 defragInfo = {};
19489 defragInfo.allocationCount = allocCount;
19490 defragInfo.pAllocations = allocations.data();
19491 defragInfo.pAllocationsChanged = allocationsChanged.data();
19492 defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE; // Notice it is "GPU" this time.
19493 defragInfo.maxGpuAllocationsToMove = UINT32_MAX; // Notice it is "GPU" this time.
19494 defragInfo.commandBuffer = commandBuffer;
19495 
19496 VmaDefragmentationContext defragCtx;
19497 vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx);
19498 
19499 vkEndCommandBuffer(commandBuffer);
19500 
19501 // Submit commandBuffer.
19502 // Wait for a fence that ensures commandBuffer execution finished.
19503 
19504 vmaDefragmentationEnd(allocator, defragCtx);
19505 
19506 for(uint32_t i = 0; i < allocCount; ++i)
19507 {
19508     if(allocationsChanged[i])
19509     {
19510         // Destroy buffer that is immutably bound to memory region which is no longer valid.
19511         vkDestroyBuffer(device, buffers[i], nullptr);
19512 
19513         // Create new buffer with same parameters.
19514         VkBufferCreateInfo bufferInfo = ...;
19515         vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
19516 
19517         // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
19518 
19519         // Bind new buffer to new memory region. Data contained in it is already moved.
19520         VmaAllocationInfo allocInfo;
19521         vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
19522         vmaBindBufferMemory(allocator, allocations[i], buffers[i]);
19523     }
19524 }
19525 \endcode
19526 
19527 You can combine these two methods by specifying non-zero `maxGpu*` as well as `maxCpu*` parameters.
19528 The library automatically chooses best method to defragment each memory pool.
19529 
19530 You may try not to block your entire program to wait until defragmentation finishes,
19531 but do it in the background, as long as you carefully fullfill requirements described
19532 in function vmaDefragmentationBegin().
19533 
19534 \section defragmentation_additional_notes Additional notes
19535 
19536 It is only legal to defragment allocations bound to:
19537 
19538 - buffers
19539 - images created with `VK_IMAGE_CREATE_ALIAS_BIT`, `VK_IMAGE_TILING_LINEAR`, and
19540   being currently in `VK_IMAGE_LAYOUT_GENERAL` or `VK_IMAGE_LAYOUT_PREINITIALIZED`.
19541 
19542 Defragmentation of images created with `VK_IMAGE_TILING_OPTIMAL` or in any other
19543 layout may give undefined results.
19544 
19545 If you defragment allocations bound to images, new images to be bound to new
19546 memory region after defragmentation should be created with `VK_IMAGE_LAYOUT_PREINITIALIZED`
19547 and then transitioned to their original layout from before defragmentation if
19548 needed using an image memory barrier.
19549 
19550 While using defragmentation, you may experience validation layer warnings, which you just need to ignore.
19551 See [Validation layer warnings](@ref general_considerations_validation_layer_warnings).
19552 
19553 Please don't expect memory to be fully compacted after defragmentation.
19554 Algorithms inside are based on some heuristics that try to maximize number of Vulkan
19555 memory blocks to make totally empty to release them, as well as to maximize continuous
19556 empty space inside remaining blocks, while minimizing the number and size of allocations that
19557 need to be moved. Some fragmentation may still remain - this is normal.
19558 
19559 \section defragmentation_custom_algorithm Writing custom defragmentation algorithm
19560 
19561 If you want to implement your own, custom defragmentation algorithm,
19562 there is infrastructure prepared for that,
19563 but it is not exposed through the library API - you need to hack its source code.
19564 Here are steps needed to do this:
19565 
19566 -# Main thing you need to do is to define your own class derived from base abstract
19567    class `VmaDefragmentationAlgorithm` and implement your version of its pure virtual methods.
19568    See definition and comments of this class for details.
19569 -# Your code needs to interact with device memory block metadata.
19570    If you need more access to its data than it is provided by its public interface,
19571    declare your new class as a friend class e.g. in class `VmaBlockMetadata_Generic`.
19572 -# If you want to create a flag that would enable your algorithm or pass some additional
19573    flags to configure it, add them to `VmaDefragmentationFlagBits` and use them in
19574    VmaDefragmentationInfo2::flags.
19575 -# Modify function `VmaBlockVectorDefragmentationContext::Begin` to create object
19576    of your new class whenever needed.
19577 
19578 
19579 \page lost_allocations Lost allocations
19580 
19581 If your game oversubscribes video memory, if may work OK in previous-generation
19582 graphics APIs (DirectX 9, 10, 11, OpenGL) because resources are automatically
19583 paged to system RAM. In Vulkan you can't do it because when you run out of
19584 memory, an allocation just fails. If you have more data (e.g. textures) that can
19585 fit into VRAM and you don't need it all at once, you may want to upload them to
19586 GPU on demand and "push out" ones that are not used for a long time to make room
19587 for the new ones, effectively using VRAM (or a cartain memory pool) as a form of
19588 cache. Vulkan Memory Allocator can help you with that by supporting a concept of
19589 "lost allocations".
19590 
19591 To create an allocation that can become lost, include #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT
19592 flag in VmaAllocationCreateInfo::flags. Before using a buffer or image bound to
19593 such allocation in every new frame, you need to query it if it is not lost.
19594 To check it, call vmaTouchAllocation().
19595 If the allocation is lost, you should not use it or buffer/image bound to it.
19596 You mustn't forget to destroy this allocation and this buffer/image.
19597 vmaGetAllocationInfo() can also be used for checking status of the allocation.
19598 Allocation is lost when returned VmaAllocationInfo::deviceMemory == `VK_NULL_HANDLE`.
19599 
19600 To create an allocation that can make some other allocations lost to make room
19601 for it, use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag. You will
19602 usually use both flags #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT and
19603 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT at the same time.
19604 
19605 Warning! Current implementation uses quite naive, brute force algorithm,
19606 which can make allocation calls that use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT
19607 flag quite slow. A new, more optimal algorithm and data structure to speed this
19608 up is planned for the future.
19609 
19610 <b>Q: When interleaving creation of new allocations with usage of existing ones,
19611 how do you make sure that an allocation won't become lost while it is used in the
19612 current frame?</b>
19613 
19614 It is ensured because vmaTouchAllocation() / vmaGetAllocationInfo() not only returns allocation
19615 status/parameters and checks whether it is not lost, but when it is not, it also
19616 atomically marks it as used in the current frame, which makes it impossible to
19617 become lost in that frame. It uses lockless algorithm, so it works fast and
19618 doesn't involve locking any internal mutex.
19619 
19620 <b>Q: What if my allocation may still be in use by the GPU when it is rendering a
19621 previous frame while I already submit new frame on the CPU?</b>
19622 
19623 You can make sure that allocations "touched" by vmaTouchAllocation() / vmaGetAllocationInfo() will not
19624 become lost for a number of additional frames back from the current one by
19625 specifying this number as VmaAllocatorCreateInfo::frameInUseCount (for default
19626 memory pool) and VmaPoolCreateInfo::frameInUseCount (for custom pool).
19627 
19628 <b>Q: How do you inform the library when new frame starts?</b>
19629 
19630 You need to call function vmaSetCurrentFrameIndex().
19631 
19632 Example code:
19633 
19634 \code
19635 struct MyBuffer
19636 {
19637     VkBuffer m_Buf = nullptr;
19638     VmaAllocation m_Alloc = nullptr;
19639 
19640     // Called when the buffer is really needed in the current frame.
19641     void EnsureBuffer();
19642 };
19643 
19644 void MyBuffer::EnsureBuffer()
19645 {
19646     // Buffer has been created.
19647     if(m_Buf != VK_NULL_HANDLE)
19648     {
19649         // Check if its allocation is not lost + mark it as used in current frame.
19650         if(vmaTouchAllocation(allocator, m_Alloc))
19651         {
19652             // It is all OK - safe to use m_Buf.
19653             return;
19654         }
19655     }
19656 
19657     // Buffer not yet exists or lost - destroy and recreate it.
19658 
19659     vmaDestroyBuffer(allocator, m_Buf, m_Alloc);
19660 
19661     VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
19662     bufCreateInfo.size = 1024;
19663     bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
19664 
19665     VmaAllocationCreateInfo allocCreateInfo = {};
19666     allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
19667     allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
19668         VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
19669 
19670     vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &m_Buf, &m_Alloc, nullptr);
19671 }
19672 \endcode
19673 
19674 When using lost allocations, you may see some Vulkan validation layer warnings
19675 about overlapping regions of memory bound to different kinds of buffers and
19676 images. This is still valid as long as you implement proper handling of lost
19677 allocations (like in the example above) and don't use them.
19678 
19679 You can create an allocation that is already in lost state from the beginning using function
19680 vmaCreateLostAllocation(). It may be useful if you need a "dummy" allocation that is not null.
19681 
19682 You can call function vmaMakePoolAllocationsLost() to set all eligible allocations
19683 in a specified custom pool to lost state.
19684 Allocations that have been "touched" in current frame or VmaPoolCreateInfo::frameInUseCount frames back
19685 cannot become lost.
19686 
19687 <b>Q: Can I touch allocation that cannot become lost?</b>
19688 
19689 Yes, although it has no visible effect.
19690 Calls to vmaGetAllocationInfo() and vmaTouchAllocation() update last use frame index
19691 also for allocations that cannot become lost, but the only way to observe it is to dump
19692 internal allocator state using vmaBuildStatsString().
19693 You can use this feature for debugging purposes to explicitly mark allocations that you use
19694 in current frame and then analyze JSON dump to see for how long each allocation stays unused.
19695 
19696 
19697 \page statistics Statistics
19698 
19699 This library contains functions that return information about its internal state,
19700 especially the amount of memory allocated from Vulkan.
19701 Please keep in mind that these functions need to traverse all internal data structures
19702 to gather these information, so they may be quite time-consuming.
19703 Don't call them too often.
19704 
19705 \section statistics_numeric_statistics Numeric statistics
19706 
19707 You can query for overall statistics of the allocator using function vmaCalculateStats().
19708 Information are returned using structure #VmaStats.
19709 It contains #VmaStatInfo - number of allocated blocks, number of allocations
19710 (occupied ranges in these blocks), number of unused (free) ranges in these blocks,
19711 number of bytes used and unused (but still allocated from Vulkan) and other information.
19712 They are summed across memory heaps, memory types and total for whole allocator.
19713 
19714 You can query for statistics of a custom pool using function vmaGetPoolStats().
19715 Information are returned using structure #VmaPoolStats.
19716 
19717 You can query for information about specific allocation using function vmaGetAllocationInfo().
19718 It fill structure #VmaAllocationInfo.
19719 
19720 \section statistics_json_dump JSON dump
19721 
19722 You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString().
19723 The result is guaranteed to be correct JSON.
19724 It uses ANSI encoding.
19725 Any strings provided by user (see [Allocation names](@ref allocation_names))
19726 are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding,
19727 this JSON string can be treated as using this encoding.
19728 It must be freed using function vmaFreeStatsString().
19729 
19730 The format of this JSON string is not part of official documentation of the library,
19731 but it will not change in backward-incompatible way without increasing library major version number
19732 and appropriate mention in changelog.
19733 
19734 The JSON string contains all the data that can be obtained using vmaCalculateStats().
19735 It can also contain detailed map of allocated memory blocks and their regions -
19736 free and occupied by allocations.
19737 This allows e.g. to visualize the memory or assess fragmentation.
19738 
19739 
19740 \page allocation_annotation Allocation names and user data
19741 
19742 \section allocation_user_data Allocation user data
19743 
19744 You can annotate allocations with your own information, e.g. for debugging purposes.
19745 To do that, fill VmaAllocationCreateInfo::pUserData field when creating
19746 an allocation. It is an opaque `void*` pointer. You can use it e.g. as a pointer,
19747 some handle, index, key, ordinal number or any other value that would associate
19748 the allocation with your custom metadata.
19749 
19750 \code
19751 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
19752 // Fill bufferInfo...
19753 
19754 MyBufferMetadata* pMetadata = CreateBufferMetadata();
19755 
19756 VmaAllocationCreateInfo allocCreateInfo = {};
19757 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
19758 allocCreateInfo.pUserData = pMetadata;
19759 
19760 VkBuffer buffer;
19761 VmaAllocation allocation;
19762 vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, nullptr);
19763 \endcode
19764 
19765 The pointer may be later retrieved as VmaAllocationInfo::pUserData:
19766 
19767 \code
19768 VmaAllocationInfo allocInfo;
19769 vmaGetAllocationInfo(allocator, allocation, &allocInfo);
19770 MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData;
19771 \endcode
19772 
19773 It can also be changed using function vmaSetAllocationUserData().
19774 
19775 Values of (non-zero) allocations' `pUserData` are printed in JSON report created by
19776 vmaBuildStatsString(), in hexadecimal form.
19777 
19778 \section allocation_names Allocation names
19779 
19780 There is alternative mode available where `pUserData` pointer is used to point to
19781 a null-terminated string, giving a name to the allocation. To use this mode,
19782 set #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT flag in VmaAllocationCreateInfo::flags.
19783 Then `pUserData` passed as VmaAllocationCreateInfo::pUserData or argument to
19784 vmaSetAllocationUserData() must be either null or pointer to a null-terminated string.
19785 The library creates internal copy of the string, so the pointer you pass doesn't need
19786 to be valid for whole lifetime of the allocation. You can free it after the call.
19787 
19788 \code
19789 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
19790 // Fill imageInfo...
19791 
19792 std::string imageName = "Texture: ";
19793 imageName += fileName;
19794 
19795 VmaAllocationCreateInfo allocCreateInfo = {};
19796 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
19797 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
19798 allocCreateInfo.pUserData = imageName.c_str();
19799 
19800 VkImage image;
19801 VmaAllocation allocation;
19802 vmaCreateImage(allocator, &imageInfo, &allocCreateInfo, &image, &allocation, nullptr);
19803 \endcode
19804 
19805 The value of `pUserData` pointer of the allocation will be different than the one
19806 you passed when setting allocation's name - pointing to a buffer managed
19807 internally that holds copy of the string.
19808 
19809 \code
19810 VmaAllocationInfo allocInfo;
19811 vmaGetAllocationInfo(allocator, allocation, &allocInfo);
19812 const char* imageName = (const char*)allocInfo.pUserData;
19813 printf("Image name: %s\n", imageName);
19814 \endcode
19815 
19816 That string is also printed in JSON report created by vmaBuildStatsString().
19817 
19818 \note Passing string name to VMA allocation doesn't automatically set it to the Vulkan buffer or image created with it.
19819 You must do it manually using an extension like VK_EXT_debug_utils, which is independent of this library.
19820 
19821 
19822 \page virtual_allocator Virtual allocator
19823 
19824 As an extra feature, the core allocation algorithm of the library is exposed through a simple and convenient API of "virtual allocator".
19825 It doesn't allocate any real GPU memory. It just keeps track of used and free regions of a "virtual block".
19826 You can use it to allocate your own memory or other objects, even completely unrelated to Vulkan.
19827 A common use case is sub-allocation of pieces of one large GPU buffer.
19828 
19829 \section virtual_allocator_creating_virtual_block Creating virtual block
19830 
19831 To use this functionality, there is no main "allocator" object.
19832 You don't need to have #VmaAllocator object created.
19833 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:
19834 
19835 -# Fill in #VmaVirtualBlockCreateInfo structure.
19836 -# Call vmaCreateVirtualBlock(). Get new #VmaVirtualBlock object.
19837 
19838 Example:
19839 
19840 \code
19841 VmaVirtualBlockCreateInfo blockCreateInfo = {};
19842 blockCreateInfo.size = 1048576; // 1 MB
19843 
19844 VmaVirtualBlock block;
19845 VkResult res = vmaCreateVirtualBlock(&blockCreateInfo, &block);
19846 \endcode
19847 
19848 \section virtual_allocator_making_virtual_allocations Making virtual allocations
19849 
19850 #VmaVirtualBlock object contains internal data structure that keeps track of free and occupied regions
19851 using the same code as the main Vulkan memory allocator.
19852 However, there is no "virtual allocation" object.
19853 When you request a new allocation, a `VkDeviceSize` number is returned.
19854 It is an offset inside the block where the allocation has been placed, but it also uniquely identifies the allocation within this block.
19855 
19856 In order to make an allocation:
19857 
19858 -# Fill in #VmaVirtualAllocationCreateInfo structure.
19859 -# Call vmaVirtualAllocate(). Get new `VkDeviceSize offset` that identifies the allocation.
19860 
19861 Example:
19862 
19863 \code
19864 VmaVirtualAllocationCreateInfo allocCreateInfo = {};
19865 allocCreateInfo.size = 4096; // 4 KB
19866 
19867 VkDeviceSize allocOffset;
19868 res = vmaVirtualAllocate(block, &allocCreateInfo, &allocOffset);
19869 if(res == VK_SUCCESS)
19870 {
19871     // Use the 4 KB of your memory starting at allocOffset.
19872 }
19873 else
19874 {
19875     // Allocation failed - no space for it could be found. Handle this error!
19876 }
19877 \endcode
19878 
19879 \section virtual_allocator_deallocation Deallocation
19880 
19881 When no longer needed, an allocation can be freed by calling vmaVirtualFree().
19882 You can only pass to this function the exact offset that was previously returned by vmaVirtualAllocate()
19883 and not any other location within the memory.
19884 
19885 When whole block is no longer needed, the block object can be released by calling vmaDestroyVirtualBlock().
19886 All allocations must be freed before the block is destroyed, which is checked internally by an assert.
19887 However, if you don't want to call vmaVirtualFree() for each allocation, you can use vmaClearVirtualBlock() to free them all at once -
19888 a feature not available in normal Vulkan memory allocator. Example:
19889 
19890 \code
19891 vmaVirtualFree(block, allocOffset);
19892 vmaDestroyVirtualBlock(block);
19893 \endcode
19894 
19895 \section virtual_allocator_allocation_parameters Allocation parameters
19896 
19897 You can attach a custom pointer to each allocation by using vmaSetVirtualAllocationUserData().
19898 Its default value is null.
19899 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
19900 larger data structure containing more information. Example:
19901 
19902 \code
19903 struct CustomAllocData
19904 {
19905     std::string m_AllocName;
19906 };
19907 CustomAllocData* allocData = new CustomAllocData();
19908 allocData->m_AllocName = "My allocation 1";
19909 vmaSetVirtualAllocationUserData(block, allocOffset, allocData);
19910 \endcode
19911 
19912 The pointer can later be fetched, along with allocation size, by passing the allocation offset to function
19913 vmaGetVirtualAllocationInfo() and inspecting returned structure #VmaVirtualAllocationInfo.
19914 If you allocated a new object to be used as the custom pointer, don't forget to delete that object before freeing the allocation!
19915 Example:
19916 
19917 \code
19918 VmaVirtualAllocationInfo allocInfo;
19919 vmaGetVirtualAllocationInfo(block, allocOffset, &allocInfo);
19920 delete (CustomAllocData*)allocInfo.pUserData;
19921 
19922 vmaVirtualFree(block, allocOffset);
19923 \endcode
19924 
19925 \section virtual_allocator_alignment_and_units Alignment and units
19926 
19927 It feels natural to express sizes and offsets in bytes.
19928 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
19929 VmaVirtualAllocationCreateInfo::alignment to request it. Example:
19930 
19931 \code
19932 VmaVirtualAllocationCreateInfo allocCreateInfo = {};
19933 allocCreateInfo.size = 4096; // 4 KB
19934 allocCreateInfo.alignment = 4; // Returned offset must be a multiply of 4 B
19935 
19936 VkDeviceSize allocOffset;
19937 res = vmaVirtualAllocate(block, &allocCreateInfo, &allocOffset);
19938 \endcode
19939 
19940 Alignments of different allocations made from one block may vary.
19941 However, if all alignments and sizes are always multiply of some size e.g. 4 B or `sizeof(MyDataStruct)`,
19942 you can express all sizes, alignments, and offsets in multiples of that size instead of individual bytes.
19943 It might be more convenient, but you need to make sure to use this new unit consistently in all the places:
19944 
19945 - VmaVirtualBlockCreateInfo::size
19946 - VmaVirtualAllocationCreateInfo::size and VmaVirtualAllocationCreateInfo::alignment
19947 - Using offset returned by vmaVirtualAllocate()
19948 
19949 \section virtual_allocator_statistics Statistics
19950 
19951 You can obtain statistics of a virtual block using vmaCalculateVirtualBlockStats().
19952 The function fills structure #VmaStatInfo - same as used by the normal Vulkan memory allocator.
19953 Example:
19954 
19955 \code
19956 VmaStatInfo statInfo;
19957 vmaCalculateVirtualBlockStats(block, &statInfo);
19958 printf("My virtual block has %llu bytes used by %u virtual allocations\n",
19959     statInfo.usedBytes, statInfo.allocationCount);
19960 \endcode
19961 
19962 You can also request a full list of allocations and free regions as a string in JSON format by calling
19963 vmaBuildVirtualBlockStatsString().
19964 Returned string must be later freed using vmaFreeVirtualBlockStatsString().
19965 The format of this string differs from the one returned by the main Vulkan allocator, but it is similar.
19966 
19967 \section virtual_allocator_additional_considerations Additional considerations
19968 
19969 The "virtual allocator" functionality is implemented on a level of individual memory blocks.
19970 Keeping track of a whole collection of blocks, allocating new ones when out of free space,
19971 deleting empty ones, and deciding which one to try first for a new allocation must be implemented by the user.
19972 
19973 Alternative allocation algorithms are supported, just like in custom pools of the real GPU memory.
19974 See enum #VmaVirtualBlockCreateFlagBits to learn how to specify them (e.g. #VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT).
19975 You can find their description in chapter \ref custom_memory_pools.
19976 Allocation strategies are also supported.
19977 See enum #VmaVirtualAllocationCreateFlagBits to learn how to specify them (e.g. #VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT).
19978 
19979 Following features are supported only by the allocator of the real GPU memory and not by virtual allocations:
19980 buffer-image granularity, \ref lost_allocations, `VMA_DEBUG_MARGIN`, `VMA_MIN_ALIGNMENT`.
19981 
19982 
19983 \page debugging_memory_usage Debugging incorrect memory usage
19984 
19985 If you suspect a bug with memory usage, like usage of uninitialized memory or
19986 memory being overwritten out of bounds of an allocation,
19987 you can use debug features of this library to verify this.
19988 
19989 \section debugging_memory_usage_initialization Memory initialization
19990 
19991 If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used,
19992 you can enable automatic memory initialization to verify this.
19993 To do it, define macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to 1.
19994 
19995 \code
19996 #define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1
19997 #include "vk_mem_alloc.h"
19998 \endcode
19999 
20000 It makes memory of all new allocations initialized to bit pattern `0xDCDCDCDC`.
20001 Before an allocation is destroyed, its memory is filled with bit pattern `0xEFEFEFEF`.
20002 Memory is automatically mapped and unmapped if necessary.
20003 
20004 If you find these values while debugging your program, good chances are that you incorrectly
20005 read Vulkan memory that is allocated but not initialized, or already freed, respectively.
20006 
20007 Memory initialization works only with memory types that are `HOST_VISIBLE`.
20008 It works also with dedicated allocations.
20009 It doesn't work with allocations created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
20010 as they cannot be mapped.
20011 
20012 \section debugging_memory_usage_margins Margins
20013 
20014 By default, allocations are laid out in memory blocks next to each other if possible
20015 (considering required alignment, `bufferImageGranularity`, and `nonCoherentAtomSize`).
20016 
20017 ![Allocations without margin](../gfx/Margins_1.png)
20018 
20019 Define macro `VMA_DEBUG_MARGIN` to some non-zero value (e.g. 16) to enforce specified
20020 number of bytes as a margin before and after every allocation.
20021 
20022 \code
20023 #define VMA_DEBUG_MARGIN 16
20024 #include "vk_mem_alloc.h"
20025 \endcode
20026 
20027 ![Allocations with margin](../gfx/Margins_2.png)
20028 
20029 If your bug goes away after enabling margins, it means it may be caused by memory
20030 being overwritten outside of allocation boundaries. It is not 100% certain though.
20031 Change in application behavior may also be caused by different order and distribution
20032 of allocations across memory blocks after margins are applied.
20033 
20034 The margin is applied also before first and after last allocation in a block.
20035 It may occur only once between two adjacent allocations.
20036 
20037 Margins work with all types of memory.
20038 
20039 Margin is applied only to allocations made out of memory blocks and not to dedicated
20040 allocations, which have their own memory block of specific size.
20041 It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag
20042 or those automatically decided to put into dedicated allocations, e.g. due to its
20043 large size or recommended by VK_KHR_dedicated_allocation extension.
20044 Margins are also not active in custom pools created with #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag.
20045 
20046 Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space.
20047 
20048 Note that enabling margins increases memory usage and fragmentation.
20049 
20050 \section debugging_memory_usage_corruption_detection Corruption detection
20051 
20052 You can additionally define macro `VMA_DEBUG_DETECT_CORRUPTION` to 1 to enable validation
20053 of contents of the margins.
20054 
20055 \code
20056 #define VMA_DEBUG_MARGIN 16
20057 #define VMA_DEBUG_DETECT_CORRUPTION 1
20058 #include "vk_mem_alloc.h"
20059 \endcode
20060 
20061 When this feature is enabled, number of bytes specified as `VMA_DEBUG_MARGIN`
20062 (it must be multiply of 4) before and after every allocation is filled with a magic number.
20063 This idea is also know as "canary".
20064 Memory is automatically mapped and unmapped if necessary.
20065 
20066 This number is validated automatically when the allocation is destroyed.
20067 If it is not equal to the expected value, `VMA_ASSERT()` is executed.
20068 It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation,
20069 which indicates a serious bug.
20070 
20071 You can also explicitly request checking margins of all allocations in all memory blocks
20072 that belong to specified memory types by using function vmaCheckCorruption(),
20073 or in memory blocks that belong to specified custom pool, by using function
20074 vmaCheckPoolCorruption().
20075 
20076 Margin validation (corruption detection) works only for memory types that are
20077 `HOST_VISIBLE` and `HOST_COHERENT`.
20078 
20079 
20080 \page record_and_replay Record and replay
20081 
20082 \section record_and_replay_introduction Introduction
20083 
20084 While using the library, sequence of calls to its functions together with their
20085 parameters can be recorded to a file and later replayed using standalone player
20086 application. It can be useful to:
20087 
20088 - Test correctness - check if same sequence of calls will not cause crash or
20089   failures on a target platform.
20090 - Gather statistics - see number of allocations, peak memory usage, number of
20091   calls etc.
20092 - Benchmark performance - see how much time it takes to replay the whole
20093   sequence.
20094 
20095 \section record_and_replay_usage Usage
20096 
20097 Recording functionality is disabled by default.
20098 To enable it, define following macro before every include of this library:
20099 
20100 \code
20101 #define VMA_RECORDING_ENABLED 1
20102 \endcode
20103 
20104 <b>To record sequence of calls to a file:</b> Fill in
20105 VmaAllocatorCreateInfo::pRecordSettings member while creating #VmaAllocator
20106 object. File is opened and written during whole lifetime of the allocator.
20107 
20108 <b>To replay file:</b> Use VmaReplay - standalone command-line program.
20109 Precompiled binary can be found in "bin" directory.
20110 Its source can be found in "src/VmaReplay" directory.
20111 Its project is generated by Premake.
20112 Command line syntax is printed when the program is launched without parameters.
20113 Basic usage:
20114 
20115     VmaReplay.exe MyRecording.csv
20116 
20117 <b>Documentation of file format</b> can be found in file: "docs/Recording file format.md".
20118 It is a human-readable, text file in CSV format (Comma Separated Values).
20119 
20120 \section record_and_replay_additional_considerations Additional considerations
20121 
20122 - Replaying file that was recorded on a different GPU (with different parameters
20123   like `bufferImageGranularity`, `nonCoherentAtomSize`, and especially different
20124   set of memory heaps and types) may give different performance and memory usage
20125   results, as well as issue some warnings and errors.
20126 - Current implementation of recording in VMA, as well as VmaReplay application, is
20127   coded and tested only on Windows. Inclusion of recording code is driven by
20128   `VMA_RECORDING_ENABLED` macro. Support for other platforms should be easy to
20129   add. Contributions are welcomed.
20130 
20131 
20132 \page opengl_interop OpenGL Interop
20133 
20134 VMA provides some features that help with interoperability with OpenGL.
20135 
20136 \section opengl_interop_exporting_memory Exporting memory
20137 
20138 If you want to attach `VkExportMemoryAllocateInfoKHR` structure to `pNext` chain of memory allocations made by the library:
20139 
20140 It is recommended to create \ref custom_memory_pools for such allocations.
20141 Define and fill in your `VkExportMemoryAllocateInfoKHR` structure and attach it to VmaPoolCreateInfo::pMemoryAllocateNext
20142 while creating the custom pool.
20143 Please note that the structure must remain alive and unchanged for the whole lifetime of the #VmaPool,
20144 not only while creating it, as no copy of the structure is made,
20145 but its original pointer is used for each allocation instead.
20146 
20147 If you want to export all memory allocated by the library from certain memory types,
20148 also dedicated allocations or other allocations made from default pools,
20149 an alternative solution is to fill in VmaAllocatorCreateInfo::pTypeExternalMemoryHandleTypes.
20150 It should point to an array with `VkExternalMemoryHandleTypeFlagsKHR` to be automatically passed by the library
20151 through `VkExportMemoryAllocateInfoKHR` on each allocation made from a specific memory type.
20152 This is currently the only method to use if you need exported dedicated allocations, as they cannot be created out of custom pools.
20153 This will change in future versions of the library though.
20154 
20155 You should not mix these two methods in a way that allows to apply both to the same memory type.
20156 Otherwise, `VkExportMemoryAllocateInfoKHR` structure would be attached twice to the `pNext` chain of `VkMemoryAllocateInfo`.
20157 
20158 
20159 \section opengl_interop_custom_alignment Custom alignment
20160 
20161 Buffers or images exported to a different API like OpenGL may require a different alignment,
20162 higher than the one used by the library automatically, queried from functions like `vkGetBufferMemoryRequirements`.
20163 To impose such alignment:
20164 
20165 It is recommended to create \ref custom_memory_pools for such allocations.
20166 Set VmaPoolCreateInfo::minAllocationAlignment member to the minimum alignment required for each allocation
20167 to be made out of this pool.
20168 The alignment actually used will be the maximum of this member and the alignment returned for the specific buffer or image
20169 from a function like `vkGetBufferMemoryRequirements`, which is called by VMA automatically.
20170 
20171 If you want to create a buffer with a specific minimum alignment out of default pools,
20172 use special function vmaCreateBufferWithAlignment(), which takes additional parameter `minAlignment`.
20173 This is currently the only method to use if you need exported dedicated allocations, as they cannot be created out of custom pools.
20174 This will change in future versions of the library though.
20175 
20176 Note the problem of alignment affects only resources placed inside bigger `VkDeviceMemory` blocks and not dedicated
20177 allocations, as these, by definition, always have alignment = 0 because the resource is bound to the beginning of its dedicated block.
20178 Contrary to Direct3D 12, Vulkan doesn't have a concept of alignment of the entire memory block passed on its allocation.
20179 
20180 
20181 \page usage_patterns Recommended usage patterns
20182 
20183 See also slides from talk:
20184 [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)
20185 
20186 
20187 \section usage_patterns_common_mistakes Common mistakes
20188 
20189 <b>Use of CPU_TO_GPU instead of CPU_ONLY memory</b>
20190 
20191 #VMA_MEMORY_USAGE_CPU_TO_GPU is recommended only for resources that will be
20192 mapped and written by the CPU, as well as read directly by the GPU - like some
20193 buffers or textures updated every frame (dynamic). If you create a staging copy
20194 of a resource to be written by CPU and then used as a source of transfer to
20195 another resource placed in the GPU memory, that staging resource should be
20196 created with #VMA_MEMORY_USAGE_CPU_ONLY. Please read the descriptions of these
20197 enums carefully for details.
20198 
20199 <b>Unnecessary use of custom pools</b>
20200 
20201 \ref custom_memory_pools may be useful for special purposes - when you want to
20202 keep certain type of resources separate e.g. to reserve minimum amount of memory
20203 for them, limit maximum amount of memory they can occupy, or make some of them
20204 push out the other through the mechanism of \ref lost_allocations. For most
20205 resources this is not needed and so it is not recommended to create #VmaPool
20206 objects and allocations out of them. Allocating from the default pool is sufficient.
20207 
20208 \section usage_patterns_simple Simple patterns
20209 
20210 \subsection usage_patterns_simple_render_targets Render targets
20211 
20212 <b>When:</b>
20213 Any resources that you frequently write and read on GPU,
20214 e.g. images used as color attachments (aka "render targets"), depth-stencil attachments,
20215 images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)").
20216 
20217 <b>What to do:</b>
20218 Create them in video memory that is fastest to access from GPU using
20219 #VMA_MEMORY_USAGE_GPU_ONLY.
20220 
20221 Consider using [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension
20222 and/or manually creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
20223 especially if they are large or if you plan to destroy and recreate them e.g. when
20224 display resolution changes.
20225 Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later.
20226 
20227 \subsection usage_patterns_simple_immutable_resources Immutable resources
20228 
20229 <b>When:</b>
20230 Any resources that you fill on CPU only once (aka "immutable") or infrequently
20231 and then read frequently on GPU,
20232 e.g. textures, vertex and index buffers, constant buffers that don't change often.
20233 
20234 <b>What to do:</b>
20235 Create them in video memory that is fastest to access from GPU using
20236 #VMA_MEMORY_USAGE_GPU_ONLY.
20237 
20238 To initialize content of such resource, create a CPU-side (aka "staging") copy of it
20239 in system memory - #VMA_MEMORY_USAGE_CPU_ONLY, map it, fill it,
20240 and submit a transfer from it to the GPU resource.
20241 You can keep the staging copy if you need it for another upload transfer in the future.
20242 If you don't, you can destroy it or reuse this buffer for uploading different resource
20243 after the transfer finishes.
20244 
20245 Prefer to create just buffers in system memory rather than images, even for uploading textures.
20246 Use `vkCmdCopyBufferToImage()`.
20247 Dont use images with `VK_IMAGE_TILING_LINEAR`.
20248 
20249 \subsection usage_patterns_dynamic_resources Dynamic resources
20250 
20251 <b>When:</b>
20252 Any resources that change frequently (aka "dynamic"), e.g. every frame or every draw call,
20253 written on CPU, read on GPU.
20254 
20255 <b>What to do:</b>
20256 Create them using #VMA_MEMORY_USAGE_CPU_TO_GPU.
20257 You can map it and write to it directly on CPU, as well as read from it on GPU.
20258 
20259 This is a more complex situation. Different solutions are possible,
20260 and the best one depends on specific GPU type, but you can use this simple approach for the start.
20261 Prefer to write to such resource sequentially (e.g. using `memcpy`).
20262 Don't perform random access or any reads from it on CPU, as it may be very slow.
20263 Also note that textures written directly from the host through a mapped pointer need to be in LINEAR not OPTIMAL layout.
20264 
20265 \subsection usage_patterns_readback Readback
20266 
20267 <b>When:</b>
20268 Resources that contain data written by GPU that you want to read back on CPU,
20269 e.g. results of some computations.
20270 
20271 <b>What to do:</b>
20272 Create them using #VMA_MEMORY_USAGE_GPU_TO_CPU.
20273 You can write to them directly on GPU, as well as map and read them on CPU.
20274 
20275 \section usage_patterns_advanced Advanced patterns
20276 
20277 \subsection usage_patterns_integrated_graphics Detecting integrated graphics
20278 
20279 You can support integrated graphics (like Intel HD Graphics, AMD APU) better
20280 by detecting it in Vulkan.
20281 To do it, call `vkGetPhysicalDeviceProperties()`, inspect
20282 `VkPhysicalDeviceProperties::deviceType` and look for `VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU`.
20283 When you find it, you can assume that memory is unified and all memory types are comparably fast
20284 to access from GPU, regardless of `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
20285 
20286 You can then sum up sizes of all available memory heaps and treat them as useful for
20287 your GPU resources, instead of only `DEVICE_LOCAL` ones.
20288 You can also prefer to create your resources in memory types that are `HOST_VISIBLE` to map them
20289 directly instead of submitting explicit transfer (see below).
20290 
20291 \subsection usage_patterns_direct_vs_transfer Direct access versus transfer
20292 
20293 For resources that you frequently write on CPU and read on GPU, many solutions are possible:
20294 
20295 -# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
20296    second copy in system memory using #VMA_MEMORY_USAGE_CPU_ONLY and submit explicit transfer each time.
20297 -# Create just a single copy using #VMA_MEMORY_USAGE_CPU_TO_GPU, map it and fill it on CPU,
20298    read it directly on GPU.
20299 -# Create just a single copy using #VMA_MEMORY_USAGE_CPU_ONLY, map it and fill it on CPU,
20300    read it directly on GPU.
20301 
20302 Which solution is the most efficient depends on your resource and especially on the GPU.
20303 It is best to measure it and then make the decision.
20304 Some general recommendations:
20305 
20306 - On integrated graphics use (2) or (3) to avoid unnecessary time and memory overhead
20307   related to using a second copy and making transfer.
20308 - For small resources (e.g. constant buffers) use (2).
20309   Discrete AMD cards have special 256 MiB pool of video memory that is directly mappable.
20310   Even if the resource ends up in system memory, its data may be cached on GPU after first
20311   fetch over PCIe bus.
20312 - For larger resources (e.g. textures), decide between (1) and (2).
20313   You may want to differentiate NVIDIA and AMD, e.g. by looking for memory type that is
20314   both `DEVICE_LOCAL` and `HOST_VISIBLE`. When you find it, use (2), otherwise use (1).
20315 
20316 Similarly, for resources that you frequently write on GPU and read on CPU, multiple
20317 solutions are possible:
20318 
20319 -# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
20320    second copy in system memory using #VMA_MEMORY_USAGE_GPU_TO_CPU and submit explicit tranfer each time.
20321 -# Create just single copy using #VMA_MEMORY_USAGE_GPU_TO_CPU, write to it directly on GPU,
20322    map it and read it on CPU.
20323 
20324 You should take some measurements to decide which option is faster in case of your specific
20325 resource.
20326 
20327 Note that textures accessed directly from the host through a mapped pointer need to be in LINEAR layout,
20328 which may slow down their usage on the device.
20329 Textures accessed only by the device and transfer operations can use OPTIMAL layout.
20330 
20331 If you don't want to specialize your code for specific types of GPUs, you can still make
20332 an simple optimization for cases when your resource ends up in mappable memory to use it
20333 directly in this case instead of creating CPU-side staging copy.
20334 For details see [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable).
20335 
20336 
20337 \page configuration Configuration
20338 
20339 Please check "CONFIGURATION SECTION" in the code to find macros that you can define
20340 before each include of this file or change directly in this file to provide
20341 your own implementation of basic facilities like assert, `min()` and `max()` functions,
20342 mutex, atomic etc.
20343 The library uses its own implementation of containers by default, but you can switch to using
20344 STL containers instead.
20345 
20346 For example, define `VMA_ASSERT(expr)` before including the library to provide
20347 custom implementation of the assertion, compatible with your project.
20348 By default it is defined to standard C `assert(expr)` in `_DEBUG` configuration
20349 and empty otherwise.
20350 
20351 \section config_Vulkan_functions Pointers to Vulkan functions
20352 
20353 There are multiple ways to import pointers to Vulkan functions in the library.
20354 In the simplest case you don't need to do anything.
20355 If the compilation or linking of your program or the initialization of the #VmaAllocator
20356 doesn't work for you, you can try to reconfigure it.
20357 
20358 First, the allocator tries to fetch pointers to Vulkan functions linked statically,
20359 like this:
20360 
20361 \code
20362 m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
20363 \endcode
20364 
20365 If you want to disable this feature, set configuration macro: `#define VMA_STATIC_VULKAN_FUNCTIONS 0`.
20366 
20367 Second, you can provide the pointers yourself by setting member VmaAllocatorCreateInfo::pVulkanFunctions.
20368 You can fetch them e.g. using functions `vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` or
20369 by using a helper library like [volk](https://github.com/zeux/volk).
20370 
20371 Third, VMA tries to fetch remaining pointers that are still null by calling
20372 `vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` on its own.
20373 If you want to disable this feature, set configuration macro: `#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0`.
20374 
20375 Finally, all the function pointers required by the library (considering selected
20376 Vulkan version and enabled extensions) are checked with `VMA_ASSERT` if they are not null.
20377 
20378 
20379 \section custom_memory_allocator Custom host memory allocator
20380 
20381 If you use custom allocator for CPU memory rather than default operator `new`
20382 and `delete` from C++, you can make this library using your allocator as well
20383 by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These
20384 functions will be passed to Vulkan, as well as used by the library itself to
20385 make any CPU-side allocations.
20386 
20387 \section allocation_callbacks Device memory allocation callbacks
20388 
20389 The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally.
20390 You can setup callbacks to be informed about these calls, e.g. for the purpose
20391 of gathering some statistics. To do it, fill optional member
20392 VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
20393 
20394 \section heap_memory_limit Device heap memory limit
20395 
20396 When device memory of certain heap runs out of free space, new allocations may
20397 fail (returning error code) or they may succeed, silently pushing some existing
20398 memory blocks from GPU VRAM to system RAM (which degrades performance). This
20399 behavior is implementation-dependent - it depends on GPU vendor and graphics
20400 driver.
20401 
20402 On AMD cards it can be controlled while creating Vulkan device object by using
20403 VK_AMD_memory_overallocation_behavior extension, if available.
20404 
20405 Alternatively, if you want to test how your program behaves with limited amount of Vulkan device
20406 memory available without switching your graphics card to one that really has
20407 smaller VRAM, you can use a feature of this library intended for this purpose.
20408 To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit.
20409 
20410 
20411 
20412 \page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation
20413 
20414 VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve
20415 performance on some GPUs. It augments Vulkan API with possibility to query
20416 driver whether it prefers particular buffer or image to have its own, dedicated
20417 allocation (separate `VkDeviceMemory` block) for better efficiency - to be able
20418 to do some internal optimizations.
20419 
20420 The extension is supported by this library. It will be used automatically when
20421 enabled. To enable it:
20422 
20423 1 . When creating Vulkan device, check if following 2 device extensions are
20424 supported (call `vkEnumerateDeviceExtensionProperties()`).
20425 If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`).
20426 
20427 - VK_KHR_get_memory_requirements2
20428 - VK_KHR_dedicated_allocation
20429 
20430 If you enabled these extensions:
20431 
20432 2 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating
20433 your #VmaAllocator`to inform the library that you enabled required extensions
20434 and you want the library to use them.
20435 
20436 \code
20437 allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
20438 
20439 vmaCreateAllocator(&allocatorInfo, &allocator);
20440 \endcode
20441 
20442 That is all. The extension will be automatically used whenever you create a
20443 buffer using vmaCreateBuffer() or image using vmaCreateImage().
20444 
20445 When using the extension together with Vulkan Validation Layer, you will receive
20446 warnings like this:
20447 
20448     vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer.
20449 
20450 It is OK, you should just ignore it. It happens because you use function
20451 `vkGetBufferMemoryRequirements2KHR()` instead of standard
20452 `vkGetBufferMemoryRequirements()`, while the validation layer seems to be
20453 unaware of it.
20454 
20455 To learn more about this extension, see:
20456 
20457 - [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap50.html#VK_KHR_dedicated_allocation)
20458 - [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5)
20459 
20460 
20461 
20462 \page vk_amd_device_coherent_memory VK_AMD_device_coherent_memory
20463 
20464 VK_AMD_device_coherent_memory is a device extension that enables access to
20465 additional memory types with `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and
20466 `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flag. It is useful mostly for
20467 allocation of buffers intended for writing "breadcrumb markers" in between passes
20468 or draw calls, which in turn are useful for debugging GPU crash/hang/TDR cases.
20469 
20470 When the extension is available but has not been enabled, Vulkan physical device
20471 still exposes those memory types, but their usage is forbidden. VMA automatically
20472 takes care of that - it returns `VK_ERROR_FEATURE_NOT_PRESENT` when an attempt
20473 to allocate memory of such type is made.
20474 
20475 If you want to use this extension in connection with VMA, follow these steps:
20476 
20477 \section vk_amd_device_coherent_memory_initialization Initialization
20478 
20479 1) Call `vkEnumerateDeviceExtensionProperties` for the physical device.
20480 Check if the extension is supported - if returned array of `VkExtensionProperties` contains "VK_AMD_device_coherent_memory".
20481 
20482 2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`.
20483 Attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to `VkPhysicalDeviceFeatures2::pNext` to be returned.
20484 Check if the device feature is really supported - check if `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true.
20485 
20486 3) While creating device with `vkCreateDevice`, enable this extension - add "VK_AMD_device_coherent_memory"
20487 to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`.
20488 
20489 4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`.
20490 Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`.
20491 Enable this device feature - attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to
20492 `VkPhysicalDeviceFeatures2::pNext` and set its member `deviceCoherentMemory` to `VK_TRUE`.
20493 
20494 5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you
20495 have enabled this extension and feature - add #VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT
20496 to VmaAllocatorCreateInfo::flags.
20497 
20498 \section vk_amd_device_coherent_memory_usage Usage
20499 
20500 After following steps described above, you can create VMA allocations and custom pools
20501 out of the special `DEVICE_COHERENT` and `DEVICE_UNCACHED` memory types on eligible
20502 devices. There are multiple ways to do it, for example:
20503 
20504 - You can request or prefer to allocate out of such memory types by adding
20505   `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` to VmaAllocationCreateInfo::requiredFlags
20506   or VmaAllocationCreateInfo::preferredFlags. Those flags can be freely mixed with
20507   other ways of \ref choosing_memory_type, like setting VmaAllocationCreateInfo::usage.
20508 - If you manually found memory type index to use for this purpose, force allocation
20509   from this specific index by setting VmaAllocationCreateInfo::memoryTypeBits `= 1u << index`.
20510 
20511 \section vk_amd_device_coherent_memory_more_information More information
20512 
20513 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)
20514 
20515 Example use of this extension can be found in the code of the sample and test suite
20516 accompanying this library.
20517 
20518 
20519 \page enabling_buffer_device_address Enabling buffer device address
20520 
20521 Device extension VK_KHR_buffer_device_address
20522 allow to fetch raw GPU pointer to a buffer and pass it for usage in a shader code.
20523 It is promoted to core Vulkan 1.2.
20524 
20525 If you want to use this feature in connection with VMA, follow these steps:
20526 
20527 \section enabling_buffer_device_address_initialization Initialization
20528 
20529 1) (For Vulkan version < 1.2) Call `vkEnumerateDeviceExtensionProperties` for the physical device.
20530 Check if the extension is supported - if returned array of `VkExtensionProperties` contains
20531 "VK_KHR_buffer_device_address".
20532 
20533 2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`.
20534 Attach additional structure `VkPhysicalDeviceBufferDeviceAddressFeatures*` to `VkPhysicalDeviceFeatures2::pNext` to be returned.
20535 Check if the device feature is really supported - check if `VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress` is true.
20536 
20537 3) (For Vulkan version < 1.2) While creating device with `vkCreateDevice`, enable this extension - add
20538 "VK_KHR_buffer_device_address" to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`.
20539 
20540 4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`.
20541 Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`.
20542 Enable this device feature - attach additional structure `VkPhysicalDeviceBufferDeviceAddressFeatures*` to
20543 `VkPhysicalDeviceFeatures2::pNext` and set its member `bufferDeviceAddress` to `VK_TRUE`.
20544 
20545 5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you
20546 have enabled this feature - add #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT
20547 to VmaAllocatorCreateInfo::flags.
20548 
20549 \section enabling_buffer_device_address_usage Usage
20550 
20551 After following steps described above, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*` using VMA.
20552 The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT*` to
20553 allocated memory blocks wherever it might be needed.
20554 
20555 Please note that the library supports only `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*`.
20556 The second part of this functionality related to "capture and replay" is not supported,
20557 as it is intended for usage in debugging tools like RenderDoc, not in everyday Vulkan usage.
20558 
20559 \section enabling_buffer_device_address_more_information More information
20560 
20561 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)
20562 
20563 Example use of this extension can be found in the code of the sample and test suite
20564 accompanying this library.
20565 
20566 \page general_considerations General considerations
20567 
20568 \section general_considerations_thread_safety Thread safety
20569 
20570 - The library has no global state, so separate #VmaAllocator objects can be used
20571   independently.
20572   There should be no need to create multiple such objects though - one per `VkDevice` is enough.
20573 - By default, all calls to functions that take #VmaAllocator as first parameter
20574   are safe to call from multiple threads simultaneously because they are
20575   synchronized internally when needed.
20576 - When the allocator is created with #VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT
20577   flag, calls to functions that take such #VmaAllocator object must be
20578   synchronized externally.
20579 - Access to a #VmaAllocation object must be externally synchronized. For example,
20580   you must not call vmaGetAllocationInfo() and vmaMapMemory() from different
20581   threads at the same time if you pass the same #VmaAllocation object to these
20582   functions.
20583 
20584 \section general_considerations_validation_layer_warnings Validation layer warnings
20585 
20586 When using this library, you can meet following types of warnings issued by
20587 Vulkan validation layer. They don't necessarily indicate a bug, so you may need
20588 to just ignore them.
20589 
20590 - *vkBindBufferMemory(): Binding memory to buffer 0xeb8e4 but vkGetBufferMemoryRequirements() has not been called on that buffer.*
20591   - It happens when VK_KHR_dedicated_allocation extension is enabled.
20592     `vkGetBufferMemoryRequirements2KHR` function is used instead, while validation layer seems to be unaware of it.
20593 - *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.*
20594   - It happens when you map a buffer or image, because the library maps entire
20595     `VkDeviceMemory` block, where different types of images and buffers may end
20596     up together, especially on GPUs with unified memory like Intel.
20597 - *Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug.*
20598   - It happens when you use lost allocations, and a new image or buffer is
20599     created in place of an existing object that became lost.
20600   - It may happen also when you use [defragmentation](@ref defragmentation).
20601 
20602 \section general_considerations_allocation_algorithm Allocation algorithm
20603 
20604 The library uses following algorithm for allocation, in order:
20605 
20606 -# Try to find free range of memory in existing blocks.
20607 -# If failed, try to create a new block of `VkDeviceMemory`, with preferred block size.
20608 -# If failed, try to create such block with size/2, size/4, size/8.
20609 -# If failed and #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag was
20610    specified, try to find space in existing blocks, possilby making some other
20611    allocations lost.
20612 -# If failed, try to allocate separate `VkDeviceMemory` for this allocation,
20613    just like when you use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
20614 -# If failed, choose other memory type that meets the requirements specified in
20615    VmaAllocationCreateInfo and go to point 1.
20616 -# If failed, return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
20617 
20618 \section general_considerations_features_not_supported Features not supported
20619 
20620 Features deliberately excluded from the scope of this library:
20621 
20622 - **Data transfer.** Uploading (streaming) and downloading data of buffers and images
20623   between CPU and GPU memory and related synchronization is responsibility of the user.
20624   Defining some "texture" object that would automatically stream its data from a
20625   staging copy in CPU memory to GPU memory would rather be a feature of another,
20626   higher-level library implemented on top of VMA.
20627 - **Recreation of buffers and images.** Although the library has functions for
20628   buffer and image creation (vmaCreateBuffer(), vmaCreateImage()), you need to
20629   recreate these objects yourself after defragmentation. That is because the big
20630   structures `VkBufferCreateInfo`, `VkImageCreateInfo` are not stored in
20631   #VmaAllocation object.
20632 - **Handling CPU memory allocation failures.** When dynamically creating small C++
20633   objects in CPU memory (not Vulkan memory), allocation failures are not checked
20634   and handled gracefully, because that would complicate code significantly and
20635   is usually not needed in desktop PC applications anyway.
20636   Success of an allocation is just checked with an assert.
20637 - **Code free of any compiler warnings.** Maintaining the library to compile and
20638   work correctly on so many different platforms is hard enough. Being free of
20639   any warnings, on any version of any compiler, is simply not feasible.
20640   There are many preprocessor macros that make some variables unused, function parameters unreferenced,
20641   or conditional expressions constant in some configurations.
20642   The code of this library should not be bigger or more complicated just to silence these warnings.
20643   It is recommended to disable such warnings instead.
20644 - This is a C++ library with C interface. **Bindings or ports to any other programming languages** are welcome as external projects but
20645   are not going to be included into this repository.
20646 */
20647