xref: /aosp_15_r20/external/vulkan-validation-layers/layers/vk_mem_alloc.h (revision b7893ccf7851cd6a48cc5a1e965257d8a5cdcc70)
1 //
2 // Copyright (c) 2017-2018 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 // clang-format off
24 //
25 // Source: https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator
26 // THIS FILE HAS BEEN CHANGED FROM THE ORIGINAL VERSION
27 //
28 // Change Log:
29 //    3/27/19 - Make changes to suppress warnings from GCC
30 //    4/18/19 - Make changes to suppress warnings from clang
31 //    6/05/19 - Make changes to suppress warnings from clang 3.8.0
32 //    6/05/19 - Make changes to suppress more warnings from GCC
33 //    8/09/19 - Make changes to suppress dead code warnings (from upstream master branch)
34 //
35 
36 #ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H
37 #define AMD_VULKAN_MEMORY_ALLOCATOR_H
38 
39 #ifdef __cplusplus
40 extern "C" {
41 #endif
42 
43 /** \mainpage Vulkan Memory Allocator
44 
45 <b>Version 2.2.0</b> (2018-12-13)
46 
47 Copyright (c) 2017-2018 Advanced Micro Devices, Inc. All rights reserved. \n
48 License: MIT
49 
50 Documentation of all members: vk_mem_alloc.h
51 
52 \section main_table_of_contents Table of contents
53 
54 - <b>User guide</b>
55   - \subpage quick_start
56     - [Project setup](@ref quick_start_project_setup)
57     - [Initialization](@ref quick_start_initialization)
58     - [Resource allocation](@ref quick_start_resource_allocation)
59   - \subpage choosing_memory_type
60     - [Usage](@ref choosing_memory_type_usage)
61     - [Required and preferred flags](@ref choosing_memory_type_required_preferred_flags)
62     - [Explicit memory types](@ref choosing_memory_type_explicit_memory_types)
63     - [Custom memory pools](@ref choosing_memory_type_custom_memory_pools)
64   - \subpage memory_mapping
65     - [Mapping functions](@ref memory_mapping_mapping_functions)
66     - [Persistently mapped memory](@ref memory_mapping_persistently_mapped_memory)
67     - [Cache control](@ref memory_mapping_cache_control)
68     - [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable)
69   - \subpage custom_memory_pools
70     - [Choosing memory type index](@ref custom_memory_pools_MemTypeIndex)
71     - [Linear allocation algorithm](@ref linear_algorithm)
72       - [Free-at-once](@ref linear_algorithm_free_at_once)
73       - [Stack](@ref linear_algorithm_stack)
74       - [Double stack](@ref linear_algorithm_double_stack)
75       - [Ring buffer](@ref linear_algorithm_ring_buffer)
76     - [Buddy allocation algorithm](@ref buddy_algorithm)
77   - \subpage defragmentation
78   	- [Defragmenting CPU memory](@ref defragmentation_cpu)
79   	- [Defragmenting GPU memory](@ref defragmentation_gpu)
80   	- [Additional notes](@ref defragmentation_additional_notes)
81   	- [Writing custom allocation algorithm](@ref defragmentation_custom_algorithm)
82   - \subpage lost_allocations
83   - \subpage statistics
84     - [Numeric statistics](@ref statistics_numeric_statistics)
85     - [JSON dump](@ref statistics_json_dump)
86   - \subpage allocation_annotation
87     - [Allocation user data](@ref allocation_user_data)
88     - [Allocation names](@ref allocation_names)
89   - \subpage debugging_memory_usage
90     - [Memory initialization](@ref debugging_memory_usage_initialization)
91     - [Margins](@ref debugging_memory_usage_margins)
92     - [Corruption detection](@ref debugging_memory_usage_corruption_detection)
93   - \subpage record_and_replay
94 - \subpage usage_patterns
95   - [Simple patterns](@ref usage_patterns_simple)
96   - [Advanced patterns](@ref usage_patterns_advanced)
97 - \subpage configuration
98   - [Pointers to Vulkan functions](@ref config_Vulkan_functions)
99   - [Custom host memory allocator](@ref custom_memory_allocator)
100   - [Device memory allocation callbacks](@ref allocation_callbacks)
101   - [Device heap memory limit](@ref heap_memory_limit)
102   - \subpage vk_khr_dedicated_allocation
103 - \subpage general_considerations
104   - [Thread safety](@ref general_considerations_thread_safety)
105   - [Validation layer warnings](@ref general_considerations_validation_layer_warnings)
106   - [Allocation algorithm](@ref general_considerations_allocation_algorithm)
107   - [Features not supported](@ref general_considerations_features_not_supported)
108 
109 \section main_see_also See also
110 
111 - [Product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-memory-allocator/)
112 - [Source repository on GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)
113 
114 
115 
116 
117 \page quick_start Quick start
118 
119 \section quick_start_project_setup Project setup
120 
121 Vulkan Memory Allocator comes in form of a single header file.
122 You don't need to build it as a separate library project.
123 You can add this file directly to your project and submit it to code repository next to your other source files.
124 
125 "Single header" doesn't mean that everything is contained in C/C++ declarations,
126 like it tends to be in case of inline functions or C++ templates.
127 It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro.
128 If you don't do it properly, you will get linker errors.
129 
130 To do it properly:
131 
132 -# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library.
133    This includes declarations of all members of the library.
134 -# In exacly one CPP file define following macro before this include.
135    It enables also internal definitions.
136 
137 \code
138 #define VMA_IMPLEMENTATION
139 #include "vk_mem_alloc.h"
140 \endcode
141 
142 It may be a good idea to create dedicated CPP file just for this purpose.
143 
144 Note on language: This library is written in C++, but has C-compatible interface.
145 Thus you can include and use vk_mem_alloc.h in C or C++ code, but full
146 implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C.
147 
148 Please note that this library includes header `<vulkan/vulkan.h>`, which in turn
149 includes `<windows.h>` on Windows. If you need some specific macros defined
150 before including these headers (like `WIN32_LEAN_AND_MEAN` or
151 `WINVER` for Windows, `VK_USE_PLATFORM_WIN32_KHR` for Vulkan), you must define
152 them before every `#include` of this library.
153 
154 
155 \section quick_start_initialization Initialization
156 
157 At program startup:
158 
159 -# Initialize Vulkan to have `VkPhysicalDevice` and `VkDevice` object.
160 -# Fill VmaAllocatorCreateInfo structure and create #VmaAllocator object by
161    calling vmaCreateAllocator().
162 
163 \code
164 VmaAllocatorCreateInfo allocatorInfo = {};
165 allocatorInfo.physicalDevice = physicalDevice;
166 allocatorInfo.device = device;
167 
168 VmaAllocator allocator;
169 vmaCreateAllocator(&allocatorInfo, &allocator);
170 \endcode
171 
172 \section quick_start_resource_allocation Resource allocation
173 
174 When you want to create a buffer or image:
175 
176 -# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure.
177 -# Fill VmaAllocationCreateInfo structure.
178 -# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory
179    already allocated and bound to it.
180 
181 \code
182 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
183 bufferInfo.size = 65536;
184 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
185 
186 VmaAllocationCreateInfo allocInfo = {};
187 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
188 
189 VkBuffer buffer;
190 VmaAllocation allocation;
191 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
192 \endcode
193 
194 Don't forget to destroy your objects when no longer needed:
195 
196 \code
197 vmaDestroyBuffer(allocator, buffer, allocation);
198 vmaDestroyAllocator(allocator);
199 \endcode
200 
201 
202 \page choosing_memory_type Choosing memory type
203 
204 Physical devices in Vulkan support various combinations of memory heaps and
205 types. Help with choosing correct and optimal memory type for your specific
206 resource is one of the key features of this library. You can use it by filling
207 appropriate members of VmaAllocationCreateInfo structure, as described below.
208 You can also combine multiple methods.
209 
210 -# If you just want to find memory type index that meets your requirements, you
211    can use function vmaFindMemoryTypeIndex().
212 -# If you want to allocate a region of device memory without association with any
213    specific image or buffer, you can use function vmaAllocateMemory(). Usage of
214    this function is not recommended and usually not needed.
215 -# If you already have a buffer or an image created, you want to allocate memory
216    for it and then you will bind it yourself, you can use function
217    vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage().
218    For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory().
219 -# If you want to create a buffer or an image, allocate memory for it and bind
220    them together, all in one call, you can use function vmaCreateBuffer(),
221    vmaCreateImage(). This is the recommended way to use this library.
222 
223 When using 3. or 4., the library internally queries Vulkan for memory types
224 supported for that buffer or image (function `vkGetBufferMemoryRequirements()`)
225 and uses only one of these types.
226 
227 If no memory type can be found that meets all the requirements, these functions
228 return `VK_ERROR_FEATURE_NOT_PRESENT`.
229 
230 You can leave VmaAllocationCreateInfo structure completely filled with zeros.
231 It means no requirements are specified for memory type.
232 It is valid, although not very useful.
233 
234 \section choosing_memory_type_usage Usage
235 
236 The easiest way to specify memory requirements is to fill member
237 VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage.
238 It defines high level, common usage types.
239 For more details, see description of this enum.
240 
241 For example, if you want to create a uniform buffer that will be filled using
242 transfer only once or infrequently and used for rendering every frame, you can
243 do it using following code:
244 
245 \code
246 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
247 bufferInfo.size = 65536;
248 bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
249 
250 VmaAllocationCreateInfo allocInfo = {};
251 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
252 
253 VkBuffer buffer;
254 VmaAllocation allocation;
255 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
256 \endcode
257 
258 \section choosing_memory_type_required_preferred_flags Required and preferred flags
259 
260 You can specify more detailed requirements by filling members
261 VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags
262 with a combination of bits from enum `VkMemoryPropertyFlags`. For example,
263 if you want to create a buffer that will be persistently mapped on host (so it
264 must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`,
265 use following code:
266 
267 \code
268 VmaAllocationCreateInfo allocInfo = {};
269 allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
270 allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
271 allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
272 
273 VkBuffer buffer;
274 VmaAllocation allocation;
275 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
276 \endcode
277 
278 A memory type is chosen that has all the required flags and as many preferred
279 flags set as possible.
280 
281 If you use VmaAllocationCreateInfo::usage, it is just internally converted to
282 a set of required and preferred flags.
283 
284 \section choosing_memory_type_explicit_memory_types Explicit memory types
285 
286 If you inspected memory types available on the physical device and you have
287 a preference for memory types that you want to use, you can fill member
288 VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set
289 means that a memory type with that index is allowed to be used for the
290 allocation. Special value 0, just like `UINT32_MAX`, means there are no
291 restrictions to memory type index.
292 
293 Please note that this member is NOT just a memory type index.
294 Still you can use it to choose just one, specific memory type.
295 For example, if you already determined that your buffer should be created in
296 memory type 2, use following code:
297 
298 \code
299 uint32_t memoryTypeIndex = 2;
300 
301 VmaAllocationCreateInfo allocInfo = {};
302 allocInfo.memoryTypeBits = 1u << memoryTypeIndex;
303 
304 VkBuffer buffer;
305 VmaAllocation allocation;
306 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
307 \endcode
308 
309 \section choosing_memory_type_custom_memory_pools Custom memory pools
310 
311 If you allocate from custom memory pool, all the ways of specifying memory
312 requirements described above are not applicable and the aforementioned members
313 of VmaAllocationCreateInfo structure are ignored. Memory type is selected
314 explicitly when creating the pool and then used to make all the allocations from
315 that pool. For further details, see \ref custom_memory_pools.
316 
317 
318 \page memory_mapping Memory mapping
319 
320 To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`,
321 to be able to read from it or write to it in CPU code.
322 Mapping is possible only of memory allocated from a memory type that has
323 `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag.
324 Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose.
325 You can use them directly with memory allocated by this library,
326 but it is not recommended because of following issue:
327 Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed.
328 This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan.
329 Because of this, Vulkan Memory Allocator provides following facilities:
330 
331 \section memory_mapping_mapping_functions Mapping functions
332 
333 The library provides following functions for mapping of a specific #VmaAllocation: vmaMapMemory(), vmaUnmapMemory().
334 They are safer and more convenient to use than standard Vulkan functions.
335 You can map an allocation multiple times simultaneously - mapping is reference-counted internally.
336 You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block.
337 The way it's implemented is that the library always maps entire memory block, not just region of the allocation.
338 For further details, see description of vmaMapMemory() function.
339 Example:
340 
341 \code
342 // Having these objects initialized:
343 
344 struct ConstantBuffer
345 {
346     ...
347 };
348 ConstantBuffer constantBufferData;
349 
350 VmaAllocator allocator;
351 VkBuffer constantBuffer;
352 VmaAllocation constantBufferAllocation;
353 
354 // You can map and fill your buffer using following code:
355 
356 void* mappedData;
357 vmaMapMemory(allocator, constantBufferAllocation, &mappedData);
358 memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
359 vmaUnmapMemory(allocator, constantBufferAllocation);
360 \endcode
361 
362 When mapping, you may see a warning from Vulkan validation layer similar to this one:
363 
364 <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>
365 
366 It happens because the library maps entire `VkDeviceMemory` block, where different
367 types of images and buffers may end up together, especially on GPUs with unified memory like Intel.
368 You can safely ignore it if you are sure you access only memory of the intended
369 object that you wanted to map.
370 
371 
372 \section memory_mapping_persistently_mapped_memory Persistently mapped memory
373 
374 Kepping your memory persistently mapped is generally OK in Vulkan.
375 You don't need to unmap it before using its data on the GPU.
376 The library provides a special feature designed for that:
377 Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in
378 VmaAllocationCreateInfo::flags stay mapped all the time,
379 so you can just access CPU pointer to it any time
380 without a need to call any "map" or "unmap" function.
381 Example:
382 
383 \code
384 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
385 bufCreateInfo.size = sizeof(ConstantBuffer);
386 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
387 
388 VmaAllocationCreateInfo allocCreateInfo = {};
389 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
390 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
391 
392 VkBuffer buf;
393 VmaAllocation alloc;
394 VmaAllocationInfo allocInfo;
395 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
396 
397 // Buffer is already mapped. You can access its memory.
398 memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
399 \endcode
400 
401 There are some exceptions though, when you should consider mapping memory only for a short period of time:
402 
403 - When operating system is Windows 7 or 8.x (Windows 10 is not affected because it uses WDDM2),
404   device is discrete AMD GPU,
405   and memory type is the special 256 MiB pool of `DEVICE_LOCAL + HOST_VISIBLE` memory
406   (selected when you use #VMA_MEMORY_USAGE_CPU_TO_GPU),
407   then whenever a memory block allocated from this memory type stays mapped
408   for the time of any call to `vkQueueSubmit()` or `vkQueuePresentKHR()`, this
409   block is migrated by WDDM to system RAM, which degrades performance. It doesn't
410   matter if that particular memory block is actually used by the command buffer
411   being submitted.
412 - On Mac/MoltenVK there is a known bug - [Issue #175](https://github.com/KhronosGroup/MoltenVK/issues/175)
413   which requires unmapping before GPU can see updated texture.
414 - Keeping many large memory blocks mapped may impact performance or stability of some debugging tools.
415 
416 \section memory_mapping_cache_control Cache control
417 
418 Memory in Vulkan doesn't need to be unmapped before using it on GPU,
419 but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set,
420 you need to manually invalidate cache before reading of mapped pointer
421 and flush cache after writing to mapped pointer.
422 Vulkan provides following functions for this purpose `vkFlushMappedMemoryRanges()`,
423 `vkInvalidateMappedMemoryRanges()`, but this library provides more convenient
424 functions that refer to given allocation object: vmaFlushAllocation(),
425 vmaInvalidateAllocation().
426 
427 Regions of memory specified for flush/invalidate must be aligned to
428 `VkPhysicalDeviceLimits::nonCoherentAtomSize`. This is automatically ensured by the library.
429 In any memory type that is `HOST_VISIBLE` but not `HOST_COHERENT`, all allocations
430 within blocks are aligned to this value, so their offsets are always multiply of
431 `nonCoherentAtomSize` and two different allocations never share same "line" of this size.
432 
433 Please note that memory allocated with #VMA_MEMORY_USAGE_CPU_ONLY is guaranteed to be `HOST_COHERENT`.
434 
435 Also, Windows drivers from all 3 PC GPU vendors (AMD, Intel, NVIDIA)
436 currently provide `HOST_COHERENT` flag on all memory types that are
437 `HOST_VISIBLE`, so on this platform you may not need to bother.
438 
439 \section memory_mapping_finding_if_memory_mappable Finding out if memory is mappable
440 
441 It may happen that your allocation ends up in memory that is `HOST_VISIBLE` (available for mapping)
442 despite it wasn't explicitly requested.
443 For example, application may work on integrated graphics with unified memory (like Intel) or
444 allocation from video memory might have failed, so the library chose system memory as fallback.
445 
446 You can detect this case and map such allocation to access its memory on CPU directly,
447 instead of launching a transfer operation.
448 In order to do that: inspect `allocInfo.memoryType`, call vmaGetMemoryTypeProperties(),
449 and look for `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag in properties of that memory type.
450 
451 \code
452 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
453 bufCreateInfo.size = sizeof(ConstantBuffer);
454 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
455 
456 VmaAllocationCreateInfo allocCreateInfo = {};
457 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
458 allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
459 
460 VkBuffer buf;
461 VmaAllocation alloc;
462 VmaAllocationInfo allocInfo;
463 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
464 
465 VkMemoryPropertyFlags memFlags;
466 vmaGetMemoryTypeProperties(allocator, allocInfo.memoryType, &memFlags);
467 if((memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
468 {
469     // Allocation ended up in mappable memory. You can map it and access it directly.
470     void* mappedData;
471     vmaMapMemory(allocator, alloc, &mappedData);
472     memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
473     vmaUnmapMemory(allocator, alloc);
474 }
475 else
476 {
477     // Allocation ended up in non-mappable memory.
478     // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
479 }
480 \endcode
481 
482 You can even use #VMA_ALLOCATION_CREATE_MAPPED_BIT flag while creating allocations
483 that are not necessarily `HOST_VISIBLE` (e.g. using #VMA_MEMORY_USAGE_GPU_ONLY).
484 If the allocation ends up in memory type that is `HOST_VISIBLE`, it will be persistently mapped and you can use it directly.
485 If not, the flag is just ignored.
486 Example:
487 
488 \code
489 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
490 bufCreateInfo.size = sizeof(ConstantBuffer);
491 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
492 
493 VmaAllocationCreateInfo allocCreateInfo = {};
494 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
495 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
496 
497 VkBuffer buf;
498 VmaAllocation alloc;
499 VmaAllocationInfo allocInfo;
500 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
501 
502 if(allocInfo.pUserData != nullptr)
503 {
504     // Allocation ended up in mappable memory.
505     // It's persistently mapped. You can access it directly.
506     memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
507 }
508 else
509 {
510     // Allocation ended up in non-mappable memory.
511     // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
512 }
513 \endcode
514 
515 
516 \page custom_memory_pools Custom memory pools
517 
518 A memory pool contains a number of `VkDeviceMemory` blocks.
519 The library automatically creates and manages default pool for each memory type available on the device.
520 Default memory pool automatically grows in size.
521 Size of allocated blocks is also variable and managed automatically.
522 
523 You can create custom pool and allocate memory out of it.
524 It can be useful if you want to:
525 
526 - Keep certain kind of allocations separate from others.
527 - Enforce particular, fixed size of Vulkan memory blocks.
528 - Limit maximum amount of Vulkan memory allocated for that pool.
529 - Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool.
530 
531 To use custom memory pools:
532 
533 -# Fill VmaPoolCreateInfo structure.
534 -# Call vmaCreatePool() to obtain #VmaPool handle.
535 -# When making an allocation, set VmaAllocationCreateInfo::pool to this handle.
536    You don't need to specify any other parameters of this structure, like `usage`.
537 
538 Example:
539 
540 \code
541 // Create a pool that can have at most 2 blocks, 128 MiB each.
542 VmaPoolCreateInfo poolCreateInfo = {};
543 poolCreateInfo.memoryTypeIndex = ...
544 poolCreateInfo.blockSize = 128ull * 1024 * 1024;
545 poolCreateInfo.maxBlockCount = 2;
546 
547 VmaPool pool;
548 vmaCreatePool(allocator, &poolCreateInfo, &pool);
549 
550 // Allocate a buffer out of it.
551 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
552 bufCreateInfo.size = 1024;
553 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
554 
555 VmaAllocationCreateInfo allocCreateInfo = {};
556 allocCreateInfo.pool = pool;
557 
558 VkBuffer buf;
559 VmaAllocation alloc;
560 VmaAllocationInfo allocInfo;
561 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
562 \endcode
563 
564 You have to free all allocations made from this pool before destroying it.
565 
566 \code
567 vmaDestroyBuffer(allocator, buf, alloc);
568 vmaDestroyPool(allocator, pool);
569 \endcode
570 
571 \section custom_memory_pools_MemTypeIndex Choosing memory type index
572 
573 When creating a pool, you must explicitly specify memory type index.
574 To find the one suitable for your buffers or images, you can use helper functions
575 vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo().
576 You need to provide structures with example parameters of buffers or images
577 that you are going to create in that pool.
578 
579 \code
580 VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
581 exampleBufCreateInfo.size = 1024; // Whatever.
582 exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; // Change if needed.
583 
584 VmaAllocationCreateInfo allocCreateInfo = {};
585 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; // Change if needed.
586 
587 uint32_t memTypeIndex;
588 vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex);
589 
590 VmaPoolCreateInfo poolCreateInfo = {};
591 poolCreateInfo.memoryTypeIndex = memTypeIndex;
592 // ...
593 \endcode
594 
595 When creating buffers/images allocated in that pool, provide following parameters:
596 
597 - `VkBufferCreateInfo`: Prefer to pass same parameters as above.
598   Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior.
599   Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers
600   or the other way around.
601 - VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member.
602   Other members are ignored anyway.
603 
604 \section linear_algorithm Linear allocation algorithm
605 
606 Each Vulkan memory block managed by this library has accompanying metadata that
607 keeps track of used and unused regions. By default, the metadata structure and
608 algorithm tries to find best place for new allocations among free regions to
609 optimize memory usage. This way you can allocate and free objects in any order.
610 
611 ![Default allocation algorithm](../gfx/Linear_allocator_1_algo_default.png)
612 
613 Sometimes there is a need to use simpler, linear allocation algorithm. You can
614 create custom pool that uses such algorithm by adding flag
615 #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
616 #VmaPool object. Then an alternative metadata management is used. It always
617 creates new allocations after last one and doesn't reuse free regions after
618 allocations freed in the middle. It results in better allocation performance and
619 less memory consumed by metadata.
620 
621 ![Linear allocation algorithm](../gfx/Linear_allocator_2_algo_linear.png)
622 
623 With this one flag, you can create a custom pool that can be used in many ways:
624 free-at-once, stack, double stack, and ring buffer. See below for details.
625 
626 \subsection linear_algorithm_free_at_once Free-at-once
627 
628 In a pool that uses linear algorithm, you still need to free all the allocations
629 individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free
630 them in any order. New allocations are always made after last one - free space
631 in the middle is not reused. However, when you release all the allocation and
632 the pool becomes empty, allocation starts from the beginning again. This way you
633 can use linear algorithm to speed up creation of allocations that you are going
634 to release all at once.
635 
636 ![Free-at-once](../gfx/Linear_allocator_3_free_at_once.png)
637 
638 This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
639 value that allows multiple memory blocks.
640 
641 \subsection linear_algorithm_stack Stack
642 
643 When you free an allocation that was created last, its space can be reused.
644 Thanks to this, if you always release allocations in the order opposite to their
645 creation (LIFO - Last In First Out), you can achieve behavior of a stack.
646 
647 ![Stack](../gfx/Linear_allocator_4_stack.png)
648 
649 This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
650 value that allows multiple memory blocks.
651 
652 \subsection linear_algorithm_double_stack Double stack
653 
654 The space reserved by a custom pool with linear algorithm may be used by two
655 stacks:
656 
657 - First, default one, growing up from offset 0.
658 - Second, "upper" one, growing down from the end towards lower offsets.
659 
660 To make allocation from upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT
661 to VmaAllocationCreateInfo::flags.
662 
663 ![Double stack](../gfx/Linear_allocator_7_double_stack.png)
664 
665 Double stack is available only in pools with one memory block -
666 VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
667 
668 When the two stacks' ends meet so there is not enough space between them for a
669 new allocation, such allocation fails with usual
670 `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
671 
672 \subsection linear_algorithm_ring_buffer Ring buffer
673 
674 When you free some allocations from the beginning and there is not enough free space
675 for a new one at the end of a pool, allocator's "cursor" wraps around to the
676 beginning and starts allocation there. Thanks to this, if you always release
677 allocations in the same order as you created them (FIFO - First In First Out),
678 you can achieve behavior of a ring buffer / queue.
679 
680 ![Ring buffer](../gfx/Linear_allocator_5_ring_buffer.png)
681 
682 Pools with linear algorithm support [lost allocations](@ref lost_allocations) when used as ring buffer.
683 If there is not enough free space for a new allocation, but existing allocations
684 from the front of the queue can become lost, they become lost and the allocation
685 succeeds.
686 
687 ![Ring buffer with lost allocations](../gfx/Linear_allocator_6_ring_buffer_lost.png)
688 
689 Ring buffer is available only in pools with one memory block -
690 VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
691 
692 \section buddy_algorithm Buddy allocation algorithm
693 
694 There is another allocation algorithm that can be used with custom pools, called
695 "buddy". Its internal data structure is based on a tree of blocks, each having
696 size that is a power of two and a half of its parent's size. When you want to
697 allocate memory of certain size, a free node in the tree is located. If it's too
698 large, it is recursively split into two halves (called "buddies"). However, if
699 requested allocation size is not a power of two, the size of a tree node is
700 aligned up to the nearest power of two and the remaining space is wasted. When
701 two buddy nodes become free, they are merged back into one larger node.
702 
703 ![Buddy allocator](../gfx/Buddy_allocator.png)
704 
705 The advantage of buddy allocation algorithm over default algorithm is faster
706 allocation and deallocation, as well as smaller external fragmentation. The
707 disadvantage is more wasted space (internal fragmentation).
708 
709 For more information, please read ["Buddy memory allocation" on Wikipedia](https://en.wikipedia.org/wiki/Buddy_memory_allocation)
710 or other sources that describe this concept in general.
711 
712 To use buddy allocation algorithm with a custom pool, add flag
713 #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
714 #VmaPool object.
715 
716 Several limitations apply to pools that use buddy algorithm:
717 
718 - It is recommended to use VmaPoolCreateInfo::blockSize that is a power of two.
719   Otherwise, only largest power of two smaller than the size is used for
720   allocations. The remaining space always stays unused.
721 - [Margins](@ref debugging_memory_usage_margins) and
722   [corruption detection](@ref debugging_memory_usage_corruption_detection)
723   don't work in such pools.
724 - [Lost allocations](@ref lost_allocations) don't work in such pools. You can
725   use them, but they never become lost. Support may be added in the future.
726 - [Defragmentation](@ref defragmentation) doesn't work with allocations made from
727   such pool.
728 
729 \page defragmentation Defragmentation
730 
731 Interleaved allocations and deallocations of many objects of varying size can
732 cause fragmentation over time, which can lead to a situation where the library is unable
733 to find a continuous range of free memory for a new allocation despite there is
734 enough free space, just scattered across many small free ranges between existing
735 allocations.
736 
737 To mitigate this problem, you can use defragmentation feature:
738 structure #VmaDefragmentationInfo2, function vmaDefragmentationBegin(), vmaDefragmentationEnd().
739 Given set of allocations,
740 this function can move them to compact used memory, ensure more continuous free
741 space and possibly also free some `VkDeviceMemory` blocks.
742 
743 What the defragmentation does is:
744 
745 - Updates #VmaAllocation objects to point to new `VkDeviceMemory` and offset.
746   After allocation has been moved, its VmaAllocationInfo::deviceMemory and/or
747   VmaAllocationInfo::offset changes. You must query them again using
748   vmaGetAllocationInfo() if you need them.
749 - Moves actual data in memory.
750 
751 What it doesn't do, so you need to do it yourself:
752 
753 - Recreate buffers and images that were bound to allocations that were defragmented and
754   bind them with their new places in memory.
755   You must use `vkDestroyBuffer()`, `vkDestroyImage()`,
756   `vkCreateBuffer()`, `vkCreateImage()` for that purpose and NOT vmaDestroyBuffer(),
757   vmaDestroyImage(), vmaCreateBuffer(), vmaCreateImage(), because you don't need to
758   destroy or create allocation objects!
759 - Recreate views and update descriptors that point to these buffers and images.
760 
761 \section defragmentation_cpu Defragmenting CPU memory
762 
763 Following example demonstrates how you can run defragmentation on CPU.
764 Only allocations created in memory types that are `HOST_VISIBLE` can be defragmented.
765 Others are ignored.
766 
767 The way it works is:
768 
769 - It temporarily maps entire memory blocks when necessary.
770 - It moves data using `memmove()` function.
771 
772 \code
773 // Given following variables already initialized:
774 VkDevice device;
775 VmaAllocator allocator;
776 std::vector<VkBuffer> buffers;
777 std::vector<VmaAllocation> allocations;
778 
779 
780 const uint32_t allocCount = (uint32_t)allocations.size();
781 std::vector<VkBool32> allocationsChanged(allocCount);
782 
783 VmaDefragmentationInfo2 defragInfo = {};
784 defragInfo.allocationCount = allocCount;
785 defragInfo.pAllocations = allocations.data();
786 defragInfo.pAllocationsChanged = allocationsChanged.data();
787 defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE; // No limit.
788 defragInfo.maxCpuAllocationsToMove = UINT32_MAX; // No limit.
789 
790 VmaDefragmentationContext defragCtx;
791 vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx);
792 vmaDefragmentationEnd(allocator, defragCtx);
793 
794 for(uint32_t i = 0; i < allocCount; ++i)
795 {
796     if(allocationsChanged[i])
797     {
798         // Destroy buffer that is immutably bound to memory region which is no longer valid.
799         vkDestroyBuffer(device, buffers[i], nullptr);
800 
801         // Create new buffer with same parameters.
802         VkBufferCreateInfo bufferInfo = ...;
803         vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
804 
805         // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
806 
807         // Bind new buffer to new memory region. Data contained in it is already moved.
808         VmaAllocationInfo allocInfo;
809         vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
810         vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset);
811     }
812 }
813 \endcode
814 
815 Setting VmaDefragmentationInfo2::pAllocationsChanged is optional.
816 This output array tells whether particular allocation in VmaDefragmentationInfo2::pAllocations at the same index
817 has been modified during defragmentation.
818 You can pass null, but you then need to query every allocation passed to defragmentation
819 for new parameters using vmaGetAllocationInfo() if you might need to recreate and rebind a buffer or image associated with it.
820 
821 If you use [Custom memory pools](@ref choosing_memory_type_custom_memory_pools),
822 you can fill VmaDefragmentationInfo2::poolCount and VmaDefragmentationInfo2::pPools
823 instead of VmaDefragmentationInfo2::allocationCount and VmaDefragmentationInfo2::pAllocations
824 to defragment all allocations in given pools.
825 You cannot use VmaDefragmentationInfo2::pAllocationsChanged in that case.
826 You can also combine both methods.
827 
828 \section defragmentation_gpu Defragmenting GPU memory
829 
830 It is also possible to defragment allocations created in memory types that are not `HOST_VISIBLE`.
831 To do that, you need to pass a command buffer that meets requirements as described in
832 VmaDefragmentationInfo2::commandBuffer. The way it works is:
833 
834 - It creates temporary buffers and binds them to entire memory blocks when necessary.
835 - It issues `vkCmdCopyBuffer()` to passed command buffer.
836 
837 Example:
838 
839 \code
840 // Given following variables already initialized:
841 VkDevice device;
842 VmaAllocator allocator;
843 VkCommandBuffer commandBuffer;
844 std::vector<VkBuffer> buffers;
845 std::vector<VmaAllocation> allocations;
846 
847 
848 const uint32_t allocCount = (uint32_t)allocations.size();
849 std::vector<VkBool32> allocationsChanged(allocCount);
850 
851 VkCommandBufferBeginInfo cmdBufBeginInfo = ...;
852 vkBeginCommandBuffer(commandBuffer, &cmdBufBeginInfo);
853 
854 VmaDefragmentationInfo2 defragInfo = {};
855 defragInfo.allocationCount = allocCount;
856 defragInfo.pAllocations = allocations.data();
857 defragInfo.pAllocationsChanged = allocationsChanged.data();
858 defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE; // Notice it's "GPU" this time.
859 defragInfo.maxGpuAllocationsToMove = UINT32_MAX; // Notice it's "GPU" this time.
860 defragInfo.commandBuffer = commandBuffer;
861 
862 VmaDefragmentationContext defragCtx;
863 vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx);
864 
865 vkEndCommandBuffer(commandBuffer);
866 
867 // Submit commandBuffer.
868 // Wait for a fence that ensures commandBuffer execution finished.
869 
870 vmaDefragmentationEnd(allocator, defragCtx);
871 
872 for(uint32_t i = 0; i < allocCount; ++i)
873 {
874     if(allocationsChanged[i])
875     {
876         // Destroy buffer that is immutably bound to memory region which is no longer valid.
877         vkDestroyBuffer(device, buffers[i], nullptr);
878 
879         // Create new buffer with same parameters.
880         VkBufferCreateInfo bufferInfo = ...;
881         vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
882 
883         // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
884 
885         // Bind new buffer to new memory region. Data contained in it is already moved.
886         VmaAllocationInfo allocInfo;
887         vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
888         vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset);
889     }
890 }
891 \endcode
892 
893 You can combine these two methods by specifying non-zero `maxGpu*` as well as `maxCpu*` parameters.
894 The library automatically chooses best method to defragment each memory pool.
895 
896 You may try not to block your entire program to wait until defragmentation finishes,
897 but do it in the background, as long as you carefully fullfill requirements described
898 in function vmaDefragmentationBegin().
899 
900 \section defragmentation_additional_notes Additional notes
901 
902 While using defragmentation, you may experience validation layer warnings, which you just need to ignore.
903 See [Validation layer warnings](@ref general_considerations_validation_layer_warnings).
904 
905 If you defragment allocations bound to images, these images should be created with
906 `VK_IMAGE_CREATE_ALIAS_BIT` flag, to make sure that new image created with same
907 parameters and pointing to data copied to another memory region will interpret
908 its contents consistently. Otherwise you may experience corrupted data on some
909 implementations, e.g. due to different pixel swizzling used internally by the graphics driver.
910 
911 If you defragment allocations bound to images, new images to be bound to new
912 memory region after defragmentation should be created with `VK_IMAGE_LAYOUT_PREINITIALIZED`
913 and then transitioned to their original layout from before defragmentation using
914 an image memory barrier.
915 
916 Please don't expect memory to be fully compacted after defragmentation.
917 Algorithms inside are based on some heuristics that try to maximize number of Vulkan
918 memory blocks to make totally empty to release them, as well as to maximimze continuous
919 empty space inside remaining blocks, while minimizing the number and size of allocations that
920 need to be moved. Some fragmentation may still remain - this is normal.
921 
922 \section defragmentation_custom_algorithm Writing custom defragmentation algorithm
923 
924 If you want to implement your own, custom defragmentation algorithm,
925 there is infrastructure prepared for that,
926 but it is not exposed through the library API - you need to hack its source code.
927 Here are steps needed to do this:
928 
929 -# Main thing you need to do is to define your own class derived from base abstract
930    class `VmaDefragmentationAlgorithm` and implement your version of its pure virtual methods.
931    See definition and comments of this class for details.
932 -# Your code needs to interact with device memory block metadata.
933    If you need more access to its data than it's provided by its public interface,
934    declare your new class as a friend class e.g. in class `VmaBlockMetadata_Generic`.
935 -# If you want to create a flag that would enable your algorithm or pass some additional
936    flags to configure it, add them to `VmaDefragmentationFlagBits` and use them in
937    VmaDefragmentationInfo2::flags.
938 -# Modify function `VmaBlockVectorDefragmentationContext::Begin` to create object
939    of your new class whenever needed.
940 
941 
942 \page lost_allocations Lost allocations
943 
944 If your game oversubscribes video memory, if may work OK in previous-generation
945 graphics APIs (DirectX 9, 10, 11, OpenGL) because resources are automatically
946 paged to system RAM. In Vulkan you can't do it because when you run out of
947 memory, an allocation just fails. If you have more data (e.g. textures) that can
948 fit into VRAM and you don't need it all at once, you may want to upload them to
949 GPU on demand and "push out" ones that are not used for a long time to make room
950 for the new ones, effectively using VRAM (or a cartain memory pool) as a form of
951 cache. Vulkan Memory Allocator can help you with that by supporting a concept of
952 "lost allocations".
953 
954 To create an allocation that can become lost, include #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT
955 flag in VmaAllocationCreateInfo::flags. Before using a buffer or image bound to
956 such allocation in every new frame, you need to query it if it's not lost.
957 To check it, call vmaTouchAllocation().
958 If the allocation is lost, you should not use it or buffer/image bound to it.
959 You mustn't forget to destroy this allocation and this buffer/image.
960 vmaGetAllocationInfo() can also be used for checking status of the allocation.
961 Allocation is lost when returned VmaAllocationInfo::deviceMemory == `VK_NULL_HANDLE`.
962 
963 To create an allocation that can make some other allocations lost to make room
964 for it, use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag. You will
965 usually use both flags #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT and
966 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT at the same time.
967 
968 Warning! Current implementation uses quite naive, brute force algorithm,
969 which can make allocation calls that use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT
970 flag quite slow. A new, more optimal algorithm and data structure to speed this
971 up is planned for the future.
972 
973 <b>Q: When interleaving creation of new allocations with usage of existing ones,
974 how do you make sure that an allocation won't become lost while it's used in the
975 current frame?</b>
976 
977 It is ensured because vmaTouchAllocation() / vmaGetAllocationInfo() not only returns allocation
978 status/parameters and checks whether it's not lost, but when it's not, it also
979 atomically marks it as used in the current frame, which makes it impossible to
980 become lost in that frame. It uses lockless algorithm, so it works fast and
981 doesn't involve locking any internal mutex.
982 
983 <b>Q: What if my allocation may still be in use by the GPU when it's rendering a
984 previous frame while I already submit new frame on the CPU?</b>
985 
986 You can make sure that allocations "touched" by vmaTouchAllocation() / vmaGetAllocationInfo() will not
987 become lost for a number of additional frames back from the current one by
988 specifying this number as VmaAllocatorCreateInfo::frameInUseCount (for default
989 memory pool) and VmaPoolCreateInfo::frameInUseCount (for custom pool).
990 
991 <b>Q: How do you inform the library when new frame starts?</b>
992 
993 You need to call function vmaSetCurrentFrameIndex().
994 
995 Example code:
996 
997 \code
998 struct MyBuffer
999 {
1000     VkBuffer m_Buf = nullptr;
1001     VmaAllocation m_Alloc = nullptr;
1002 
1003     // Called when the buffer is really needed in the current frame.
1004     void EnsureBuffer();
1005 };
1006 
1007 void MyBuffer::EnsureBuffer()
1008 {
1009     // Buffer has been created.
1010     if(m_Buf != VK_NULL_HANDLE)
1011     {
1012         // Check if its allocation is not lost + mark it as used in current frame.
1013         if(vmaTouchAllocation(allocator, m_Alloc))
1014         {
1015             // It's all OK - safe to use m_Buf.
1016             return;
1017         }
1018     }
1019 
1020     // Buffer not yet exists or lost - destroy and recreate it.
1021 
1022     vmaDestroyBuffer(allocator, m_Buf, m_Alloc);
1023 
1024     VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1025     bufCreateInfo.size = 1024;
1026     bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
1027 
1028     VmaAllocationCreateInfo allocCreateInfo = {};
1029     allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1030     allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
1031         VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
1032 
1033     vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &m_Buf, &m_Alloc, nullptr);
1034 }
1035 \endcode
1036 
1037 When using lost allocations, you may see some Vulkan validation layer warnings
1038 about overlapping regions of memory bound to different kinds of buffers and
1039 images. This is still valid as long as you implement proper handling of lost
1040 allocations (like in the example above) and don't use them.
1041 
1042 You can create an allocation that is already in lost state from the beginning using function
1043 vmaCreateLostAllocation(). It may be useful if you need a "dummy" allocation that is not null.
1044 
1045 You can call function vmaMakePoolAllocationsLost() to set all eligible allocations
1046 in a specified custom pool to lost state.
1047 Allocations that have been "touched" in current frame or VmaPoolCreateInfo::frameInUseCount frames back
1048 cannot become lost.
1049 
1050 <b>Q: Can I touch allocation that cannot become lost?</b>
1051 
1052 Yes, although it has no visible effect.
1053 Calls to vmaGetAllocationInfo() and vmaTouchAllocation() update last use frame index
1054 also for allocations that cannot become lost, but the only way to observe it is to dump
1055 internal allocator state using vmaBuildStatsString().
1056 You can use this feature for debugging purposes to explicitly mark allocations that you use
1057 in current frame and then analyze JSON dump to see for how long each allocation stays unused.
1058 
1059 
1060 \page statistics Statistics
1061 
1062 This library contains functions that return information about its internal state,
1063 especially the amount of memory allocated from Vulkan.
1064 Please keep in mind that these functions need to traverse all internal data structures
1065 to gather these information, so they may be quite time-consuming.
1066 Don't call them too often.
1067 
1068 \section statistics_numeric_statistics Numeric statistics
1069 
1070 You can query for overall statistics of the allocator using function vmaCalculateStats().
1071 Information are returned using structure #VmaStats.
1072 It contains #VmaStatInfo - number of allocated blocks, number of allocations
1073 (occupied ranges in these blocks), number of unused (free) ranges in these blocks,
1074 number of bytes used and unused (but still allocated from Vulkan) and other information.
1075 They are summed across memory heaps, memory types and total for whole allocator.
1076 
1077 You can query for statistics of a custom pool using function vmaGetPoolStats().
1078 Information are returned using structure #VmaPoolStats.
1079 
1080 You can query for information about specific allocation using function vmaGetAllocationInfo().
1081 It fill structure #VmaAllocationInfo.
1082 
1083 \section statistics_json_dump JSON dump
1084 
1085 You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString().
1086 The result is guaranteed to be correct JSON.
1087 It uses ANSI encoding.
1088 Any strings provided by user (see [Allocation names](@ref allocation_names))
1089 are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding,
1090 this JSON string can be treated as using this encoding.
1091 It must be freed using function vmaFreeStatsString().
1092 
1093 The format of this JSON string is not part of official documentation of the library,
1094 but it will not change in backward-incompatible way without increasing library major version number
1095 and appropriate mention in changelog.
1096 
1097 The JSON string contains all the data that can be obtained using vmaCalculateStats().
1098 It can also contain detailed map of allocated memory blocks and their regions -
1099 free and occupied by allocations.
1100 This allows e.g. to visualize the memory or assess fragmentation.
1101 
1102 
1103 \page allocation_annotation Allocation names and user data
1104 
1105 \section allocation_user_data Allocation user data
1106 
1107 You can annotate allocations with your own information, e.g. for debugging purposes.
1108 To do that, fill VmaAllocationCreateInfo::pUserData field when creating
1109 an allocation. It's an opaque `void*` pointer. You can use it e.g. as a pointer,
1110 some handle, index, key, ordinal number or any other value that would associate
1111 the allocation with your custom metadata.
1112 
1113 \code
1114 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1115 // Fill bufferInfo...
1116 
1117 MyBufferMetadata* pMetadata = CreateBufferMetadata();
1118 
1119 VmaAllocationCreateInfo allocCreateInfo = {};
1120 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1121 allocCreateInfo.pUserData = pMetadata;
1122 
1123 VkBuffer buffer;
1124 VmaAllocation allocation;
1125 vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, nullptr);
1126 \endcode
1127 
1128 The pointer may be later retrieved as VmaAllocationInfo::pUserData:
1129 
1130 \code
1131 VmaAllocationInfo allocInfo;
1132 vmaGetAllocationInfo(allocator, allocation, &allocInfo);
1133 MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData;
1134 \endcode
1135 
1136 It can also be changed using function vmaSetAllocationUserData().
1137 
1138 Values of (non-zero) allocations' `pUserData` are printed in JSON report created by
1139 vmaBuildStatsString(), in hexadecimal form.
1140 
1141 \section allocation_names Allocation names
1142 
1143 There is alternative mode available where `pUserData` pointer is used to point to
1144 a null-terminated string, giving a name to the allocation. To use this mode,
1145 set #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT flag in VmaAllocationCreateInfo::flags.
1146 Then `pUserData` passed as VmaAllocationCreateInfo::pUserData or argument to
1147 vmaSetAllocationUserData() must be either null or pointer to a null-terminated string.
1148 The library creates internal copy of the string, so the pointer you pass doesn't need
1149 to be valid for whole lifetime of the allocation. You can free it after the call.
1150 
1151 \code
1152 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
1153 // Fill imageInfo...
1154 
1155 std::string imageName = "Texture: ";
1156 imageName += fileName;
1157 
1158 VmaAllocationCreateInfo allocCreateInfo = {};
1159 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1160 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
1161 allocCreateInfo.pUserData = imageName.c_str();
1162 
1163 VkImage image;
1164 VmaAllocation allocation;
1165 vmaCreateImage(allocator, &imageInfo, &allocCreateInfo, &image, &allocation, nullptr);
1166 \endcode
1167 
1168 The value of `pUserData` pointer of the allocation will be different than the one
1169 you passed when setting allocation's name - pointing to a buffer managed
1170 internally that holds copy of the string.
1171 
1172 \code
1173 VmaAllocationInfo allocInfo;
1174 vmaGetAllocationInfo(allocator, allocation, &allocInfo);
1175 const char* imageName = (const char*)allocInfo.pUserData;
1176 printf("Image name: %s\n", imageName);
1177 \endcode
1178 
1179 That string is also printed in JSON report created by vmaBuildStatsString().
1180 
1181 
1182 \page debugging_memory_usage Debugging incorrect memory usage
1183 
1184 If you suspect a bug with memory usage, like usage of uninitialized memory or
1185 memory being overwritten out of bounds of an allocation,
1186 you can use debug features of this library to verify this.
1187 
1188 \section debugging_memory_usage_initialization Memory initialization
1189 
1190 If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used,
1191 you can enable automatic memory initialization to verify this.
1192 To do it, define macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to 1.
1193 
1194 \code
1195 #define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1
1196 #include "vk_mem_alloc.h"
1197 \endcode
1198 
1199 It makes memory of all new allocations initialized to bit pattern `0xDCDCDCDC`.
1200 Before an allocation is destroyed, its memory is filled with bit pattern `0xEFEFEFEF`.
1201 Memory is automatically mapped and unmapped if necessary.
1202 
1203 If you find these values while debugging your program, good chances are that you incorrectly
1204 read Vulkan memory that is allocated but not initialized, or already freed, respectively.
1205 
1206 Memory initialization works only with memory types that are `HOST_VISIBLE`.
1207 It works also with dedicated allocations.
1208 It doesn't work with allocations created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
1209 as they cannot be mapped.
1210 
1211 \section debugging_memory_usage_margins Margins
1212 
1213 By default, allocations are laid out in memory blocks next to each other if possible
1214 (considering required alignment, `bufferImageGranularity`, and `nonCoherentAtomSize`).
1215 
1216 ![Allocations without margin](../gfx/Margins_1.png)
1217 
1218 Define macro `VMA_DEBUG_MARGIN` to some non-zero value (e.g. 16) to enforce specified
1219 number of bytes as a margin before and after every allocation.
1220 
1221 \code
1222 #define VMA_DEBUG_MARGIN 16
1223 #include "vk_mem_alloc.h"
1224 \endcode
1225 
1226 ![Allocations with margin](../gfx/Margins_2.png)
1227 
1228 If your bug goes away after enabling margins, it means it may be caused by memory
1229 being overwritten outside of allocation boundaries. It is not 100% certain though.
1230 Change in application behavior may also be caused by different order and distribution
1231 of allocations across memory blocks after margins are applied.
1232 
1233 The margin is applied also before first and after last allocation in a block.
1234 It may occur only once between two adjacent allocations.
1235 
1236 Margins work with all types of memory.
1237 
1238 Margin is applied only to allocations made out of memory blocks and not to dedicated
1239 allocations, which have their own memory block of specific size.
1240 It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag
1241 or those automatically decided to put into dedicated allocations, e.g. due to its
1242 large size or recommended by VK_KHR_dedicated_allocation extension.
1243 Margins are also not active in custom pools created with #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag.
1244 
1245 Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space.
1246 
1247 Note that enabling margins increases memory usage and fragmentation.
1248 
1249 \section debugging_memory_usage_corruption_detection Corruption detection
1250 
1251 You can additionally define macro `VMA_DEBUG_DETECT_CORRUPTION` to 1 to enable validation
1252 of contents of the margins.
1253 
1254 \code
1255 #define VMA_DEBUG_MARGIN 16
1256 #define VMA_DEBUG_DETECT_CORRUPTION 1
1257 #include "vk_mem_alloc.h"
1258 \endcode
1259 
1260 When this feature is enabled, number of bytes specified as `VMA_DEBUG_MARGIN`
1261 (it must be multiply of 4) before and after every allocation is filled with a magic number.
1262 This idea is also know as "canary".
1263 Memory is automatically mapped and unmapped if necessary.
1264 
1265 This number is validated automatically when the allocation is destroyed.
1266 If it's not equal to the expected value, `VMA_ASSERT()` is executed.
1267 It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation,
1268 which indicates a serious bug.
1269 
1270 You can also explicitly request checking margins of all allocations in all memory blocks
1271 that belong to specified memory types by using function vmaCheckCorruption(),
1272 or in memory blocks that belong to specified custom pool, by using function
1273 vmaCheckPoolCorruption().
1274 
1275 Margin validation (corruption detection) works only for memory types that are
1276 `HOST_VISIBLE` and `HOST_COHERENT`.
1277 
1278 
1279 \page record_and_replay Record and replay
1280 
1281 \section record_and_replay_introduction Introduction
1282 
1283 While using the library, sequence of calls to its functions together with their
1284 parameters can be recorded to a file and later replayed using standalone player
1285 application. It can be useful to:
1286 
1287 - Test correctness - check if same sequence of calls will not cause crash or
1288   failures on a target platform.
1289 - Gather statistics - see number of allocations, peak memory usage, number of
1290   calls etc.
1291 - Benchmark performance - see how much time it takes to replay the whole
1292   sequence.
1293 
1294 \section record_and_replay_usage Usage
1295 
1296 <b>To record sequence of calls to a file:</b> Fill in
1297 VmaAllocatorCreateInfo::pRecordSettings member while creating #VmaAllocator
1298 object. File is opened and written during whole lifetime of the allocator.
1299 
1300 <b>To replay file:</b> Use VmaReplay - standalone command-line program.
1301 Precompiled binary can be found in "bin" directory.
1302 Its source can be found in "src/VmaReplay" directory.
1303 Its project is generated by Premake.
1304 Command line syntax is printed when the program is launched without parameters.
1305 Basic usage:
1306 
1307     VmaReplay.exe MyRecording.csv
1308 
1309 <b>Documentation of file format</b> can be found in file: "docs/Recording file format.md".
1310 It's a human-readable, text file in CSV format (Comma Separated Values).
1311 
1312 \section record_and_replay_additional_considerations Additional considerations
1313 
1314 - Replaying file that was recorded on a different GPU (with different parameters
1315   like `bufferImageGranularity`, `nonCoherentAtomSize`, and especially different
1316   set of memory heaps and types) may give different performance and memory usage
1317   results, as well as issue some warnings and errors.
1318 - Current implementation of recording in VMA, as well as VmaReplay application, is
1319   coded and tested only on Windows. Inclusion of recording code is driven by
1320   `VMA_RECORDING_ENABLED` macro. Support for other platforms should be easy to
1321   add. Contributions are welcomed.
1322 - Currently calls to vmaDefragment() function are not recorded.
1323 
1324 
1325 \page usage_patterns Recommended usage patterns
1326 
1327 See also slides from talk:
1328 [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)
1329 
1330 
1331 \section usage_patterns_simple Simple patterns
1332 
1333 \subsection usage_patterns_simple_render_targets Render targets
1334 
1335 <b>When:</b>
1336 Any resources that you frequently write and read on GPU,
1337 e.g. images used as color attachments (aka "render targets"), depth-stencil attachments,
1338 images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)").
1339 
1340 <b>What to do:</b>
1341 Create them in video memory that is fastest to access from GPU using
1342 #VMA_MEMORY_USAGE_GPU_ONLY.
1343 
1344 Consider using [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension
1345 and/or manually creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
1346 especially if they are large or if you plan to destroy and recreate them e.g. when
1347 display resolution changes.
1348 Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later.
1349 
1350 \subsection usage_patterns_simple_immutable_resources Immutable resources
1351 
1352 <b>When:</b>
1353 Any resources that you fill on CPU only once (aka "immutable") or infrequently
1354 and then read frequently on GPU,
1355 e.g. textures, vertex and index buffers, constant buffers that don't change often.
1356 
1357 <b>What to do:</b>
1358 Create them in video memory that is fastest to access from GPU using
1359 #VMA_MEMORY_USAGE_GPU_ONLY.
1360 
1361 To initialize content of such resource, create a CPU-side (aka "staging") copy of it
1362 in system memory - #VMA_MEMORY_USAGE_CPU_ONLY, map it, fill it,
1363 and submit a transfer from it to the GPU resource.
1364 You can keep the staging copy if you need it for another upload transfer in the future.
1365 If you don't, you can destroy it or reuse this buffer for uploading different resource
1366 after the transfer finishes.
1367 
1368 Prefer to create just buffers in system memory rather than images, even for uploading textures.
1369 Use `vkCmdCopyBufferToImage()`.
1370 Dont use images with `VK_IMAGE_TILING_LINEAR`.
1371 
1372 \subsection usage_patterns_dynamic_resources Dynamic resources
1373 
1374 <b>When:</b>
1375 Any resources that change frequently (aka "dynamic"), e.g. every frame or every draw call,
1376 written on CPU, read on GPU.
1377 
1378 <b>What to do:</b>
1379 Create them using #VMA_MEMORY_USAGE_CPU_TO_GPU.
1380 You can map it and write to it directly on CPU, as well as read from it on GPU.
1381 
1382 This is a more complex situation. Different solutions are possible,
1383 and the best one depends on specific GPU type, but you can use this simple approach for the start.
1384 Prefer to write to such resource sequentially (e.g. using `memcpy`).
1385 Don't perform random access or any reads from it on CPU, as it may be very slow.
1386 
1387 \subsection usage_patterns_readback Readback
1388 
1389 <b>When:</b>
1390 Resources that contain data written by GPU that you want to read back on CPU,
1391 e.g. results of some computations.
1392 
1393 <b>What to do:</b>
1394 Create them using #VMA_MEMORY_USAGE_GPU_TO_CPU.
1395 You can write to them directly on GPU, as well as map and read them on CPU.
1396 
1397 \section usage_patterns_advanced Advanced patterns
1398 
1399 \subsection usage_patterns_integrated_graphics Detecting integrated graphics
1400 
1401 You can support integrated graphics (like Intel HD Graphics, AMD APU) better
1402 by detecting it in Vulkan.
1403 To do it, call `vkGetPhysicalDeviceProperties()`, inspect
1404 `VkPhysicalDeviceProperties::deviceType` and look for `VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU`.
1405 When you find it, you can assume that memory is unified and all memory types are comparably fast
1406 to access from GPU, regardless of `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
1407 
1408 You can then sum up sizes of all available memory heaps and treat them as useful for
1409 your GPU resources, instead of only `DEVICE_LOCAL` ones.
1410 You can also prefer to create your resources in memory types that are `HOST_VISIBLE` to map them
1411 directly instead of submitting explicit transfer (see below).
1412 
1413 \subsection usage_patterns_direct_vs_transfer Direct access versus transfer
1414 
1415 For resources that you frequently write on CPU and read on GPU, many solutions are possible:
1416 
1417 -# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
1418    second copy in system memory using #VMA_MEMORY_USAGE_CPU_ONLY and submit explicit tranfer each time.
1419 -# Create just single copy using #VMA_MEMORY_USAGE_CPU_TO_GPU, map it and fill it on CPU,
1420    read it directly on GPU.
1421 -# Create just single copy using #VMA_MEMORY_USAGE_CPU_ONLY, map it and fill it on CPU,
1422    read it directly on GPU.
1423 
1424 Which solution is the most efficient depends on your resource and especially on the GPU.
1425 It is best to measure it and then make the decision.
1426 Some general recommendations:
1427 
1428 - On integrated graphics use (2) or (3) to avoid unnecesary time and memory overhead
1429   related to using a second copy and making transfer.
1430 - For small resources (e.g. constant buffers) use (2).
1431   Discrete AMD cards have special 256 MiB pool of video memory that is directly mappable.
1432   Even if the resource ends up in system memory, its data may be cached on GPU after first
1433   fetch over PCIe bus.
1434 - For larger resources (e.g. textures), decide between (1) and (2).
1435   You may want to differentiate NVIDIA and AMD, e.g. by looking for memory type that is
1436   both `DEVICE_LOCAL` and `HOST_VISIBLE`. When you find it, use (2), otherwise use (1).
1437 
1438 Similarly, for resources that you frequently write on GPU and read on CPU, multiple
1439 solutions are possible:
1440 
1441 -# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
1442    second copy in system memory using #VMA_MEMORY_USAGE_GPU_TO_CPU and submit explicit tranfer each time.
1443 -# Create just single copy using #VMA_MEMORY_USAGE_GPU_TO_CPU, write to it directly on GPU,
1444    map it and read it on CPU.
1445 
1446 You should take some measurements to decide which option is faster in case of your specific
1447 resource.
1448 
1449 If you don't want to specialize your code for specific types of GPUs, you can still make
1450 an simple optimization for cases when your resource ends up in mappable memory to use it
1451 directly in this case instead of creating CPU-side staging copy.
1452 For details see [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable).
1453 
1454 
1455 \page configuration Configuration
1456 
1457 Please check "CONFIGURATION SECTION" in the code to find macros that you can define
1458 before each include of this file or change directly in this file to provide
1459 your own implementation of basic facilities like assert, `min()` and `max()` functions,
1460 mutex, atomic etc.
1461 The library uses its own implementation of containers by default, but you can switch to using
1462 STL containers instead.
1463 
1464 \section config_Vulkan_functions Pointers to Vulkan functions
1465 
1466 The library uses Vulkan functions straight from the `vulkan.h` header by default.
1467 If you want to provide your own pointers to these functions, e.g. fetched using
1468 `vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`:
1469 
1470 -# Define `VMA_STATIC_VULKAN_FUNCTIONS 0`.
1471 -# Provide valid pointers through VmaAllocatorCreateInfo::pVulkanFunctions.
1472 
1473 \section custom_memory_allocator Custom host memory allocator
1474 
1475 If you use custom allocator for CPU memory rather than default operator `new`
1476 and `delete` from C++, you can make this library using your allocator as well
1477 by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These
1478 functions will be passed to Vulkan, as well as used by the library itself to
1479 make any CPU-side allocations.
1480 
1481 \section allocation_callbacks Device memory allocation callbacks
1482 
1483 The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally.
1484 You can setup callbacks to be informed about these calls, e.g. for the purpose
1485 of gathering some statistics. To do it, fill optional member
1486 VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
1487 
1488 \section heap_memory_limit Device heap memory limit
1489 
1490 If you want to test how your program behaves with limited amount of Vulkan device
1491 memory available without switching your graphics card to one that really has
1492 smaller VRAM, you can use a feature of this library intended for this purpose.
1493 To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit.
1494 
1495 
1496 
1497 \page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation
1498 
1499 VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve
1500 performance on some GPUs. It augments Vulkan API with possibility to query
1501 driver whether it prefers particular buffer or image to have its own, dedicated
1502 allocation (separate `VkDeviceMemory` block) for better efficiency - to be able
1503 to do some internal optimizations.
1504 
1505 The extension is supported by this library. It will be used automatically when
1506 enabled. To enable it:
1507 
1508 1 . When creating Vulkan device, check if following 2 device extensions are
1509 supported (call `vkEnumerateDeviceExtensionProperties()`).
1510 If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`).
1511 
1512 - VK_KHR_get_memory_requirements2
1513 - VK_KHR_dedicated_allocation
1514 
1515 If you enabled these extensions:
1516 
1517 2 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating
1518 your #VmaAllocator`to inform the library that you enabled required extensions
1519 and you want the library to use them.
1520 
1521 \code
1522 allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
1523 
1524 vmaCreateAllocator(&allocatorInfo, &allocator);
1525 \endcode
1526 
1527 That's all. The extension will be automatically used whenever you create a
1528 buffer using vmaCreateBuffer() or image using vmaCreateImage().
1529 
1530 When using the extension together with Vulkan Validation Layer, you will receive
1531 warnings like this:
1532 
1533     vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer.
1534 
1535 It is OK, you should just ignore it. It happens because you use function
1536 `vkGetBufferMemoryRequirements2KHR()` instead of standard
1537 `vkGetBufferMemoryRequirements()`, while the validation layer seems to be
1538 unaware of it.
1539 
1540 To learn more about this extension, see:
1541 
1542 - [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VK_KHR_dedicated_allocation)
1543 - [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5)
1544 
1545 
1546 
1547 \page general_considerations General considerations
1548 
1549 \section general_considerations_thread_safety Thread safety
1550 
1551 - The library has no global state, so separate #VmaAllocator objects can be used
1552   independently.
1553   There should be no need to create multiple such objects though - one per `VkDevice` is enough.
1554 - By default, all calls to functions that take #VmaAllocator as first parameter
1555   are safe to call from multiple threads simultaneously because they are
1556   synchronized internally when needed.
1557 - When the allocator is created with #VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT
1558   flag, calls to functions that take such #VmaAllocator object must be
1559   synchronized externally.
1560 - Access to a #VmaAllocation object must be externally synchronized. For example,
1561   you must not call vmaGetAllocationInfo() and vmaMapMemory() from different
1562   threads at the same time if you pass the same #VmaAllocation object to these
1563   functions.
1564 
1565 \section general_considerations_validation_layer_warnings Validation layer warnings
1566 
1567 When using this library, you can meet following types of warnings issued by
1568 Vulkan validation layer. They don't necessarily indicate a bug, so you may need
1569 to just ignore them.
1570 
1571 - *vkBindBufferMemory(): Binding memory to buffer 0xeb8e4 but vkGetBufferMemoryRequirements() has not been called on that buffer.*
1572   - It happens when VK_KHR_dedicated_allocation extension is enabled.
1573     `vkGetBufferMemoryRequirements2KHR` function is used instead, while validation layer seems to be unaware of it.
1574 - *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.*
1575   - It happens when you map a buffer or image, because the library maps entire
1576     `VkDeviceMemory` block, where different types of images and buffers may end
1577     up together, especially on GPUs with unified memory like Intel.
1578 - *Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug.*
1579   - It happens when you use lost allocations, and a new image or buffer is
1580     created in place of an existing object that bacame lost.
1581   - It may happen also when you use [defragmentation](@ref defragmentation).
1582 
1583 \section general_considerations_allocation_algorithm Allocation algorithm
1584 
1585 The library uses following algorithm for allocation, in order:
1586 
1587 -# Try to find free range of memory in existing blocks.
1588 -# If failed, try to create a new block of `VkDeviceMemory`, with preferred block size.
1589 -# If failed, try to create such block with size/2, size/4, size/8.
1590 -# If failed and #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag was
1591    specified, try to find space in existing blocks, possilby making some other
1592    allocations lost.
1593 -# If failed, try to allocate separate `VkDeviceMemory` for this allocation,
1594    just like when you use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
1595 -# If failed, choose other memory type that meets the requirements specified in
1596    VmaAllocationCreateInfo and go to point 1.
1597 -# If failed, return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
1598 
1599 \section general_considerations_features_not_supported Features not supported
1600 
1601 Features deliberately excluded from the scope of this library:
1602 
1603 - Data transfer. Uploading (straming) and downloading data of buffers and images
1604   between CPU and GPU memory and related synchronization is responsibility of the user.
1605 - Allocations for imported/exported external memory. They tend to require
1606   explicit memory type index and dedicated allocation anyway, so they don't
1607   interact with main features of this library. Such special purpose allocations
1608   should be made manually, using `vkCreateBuffer()` and `vkAllocateMemory()`.
1609 - Recreation of buffers and images. Although the library has functions for
1610   buffer and image creation (vmaCreateBuffer(), vmaCreateImage()), you need to
1611   recreate these objects yourself after defragmentation. That's because the big
1612   structures `VkBufferCreateInfo`, `VkImageCreateInfo` are not stored in
1613   #VmaAllocation object.
1614 - Handling CPU memory allocation failures. When dynamically creating small C++
1615   objects in CPU memory (not Vulkan memory), allocation failures are not checked
1616   and handled gracefully, because that would complicate code significantly and
1617   is usually not needed in desktop PC applications anyway.
1618 - Code free of any compiler warnings. Maintaining the library to compile and
1619   work correctly on so many different platforms is hard enough. Being free of
1620   any warnings, on any version of any compiler, is simply not feasible.
1621 - This is a C++ library with C interface.
1622   Bindings or ports to any other programming languages are welcomed as external projects and
1623   are not going to be included into this repository.
1624 
1625 */
1626 
1627 /*
1628 Define this macro to 0/1 to disable/enable support for recording functionality,
1629 available through VmaAllocatorCreateInfo::pRecordSettings.
1630 */
1631 #ifndef VMA_RECORDING_ENABLED
1632     #ifdef _WIN32
1633         #define VMA_RECORDING_ENABLED 1
1634     #else
1635         #define VMA_RECORDING_ENABLED 0
1636     #endif
1637 #endif
1638 
1639 #ifndef NOMINMAX
1640     #define NOMINMAX // For windows.h
1641 #endif
1642 
1643 #ifndef VULKAN_H_
1644     #include <vulkan/vulkan.h>
1645 #endif
1646 
1647 #if VMA_RECORDING_ENABLED
1648     #include <windows.h>
1649 #endif
1650 
1651 #if !defined(VMA_DEDICATED_ALLOCATION)
1652     #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation
1653         #define VMA_DEDICATED_ALLOCATION 1
1654     #else
1655         #define VMA_DEDICATED_ALLOCATION 0
1656     #endif
1657 #endif
1658 
1659 /** \struct VmaAllocator
1660 \brief Represents main object of this library initialized.
1661 
1662 Fill structure #VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it.
1663 Call function vmaDestroyAllocator() to destroy it.
1664 
1665 It is recommended to create just one object of this type per `VkDevice` object,
1666 right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed.
1667 */
1668 VK_DEFINE_HANDLE(VmaAllocator)
1669 
1670 /// Callback function called after successful vkAllocateMemory.
1671 typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)(
1672     VmaAllocator      allocator,
1673     uint32_t          memoryType,
1674     VkDeviceMemory    memory,
1675     VkDeviceSize      size);
1676 /// Callback function called before vkFreeMemory.
1677 typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)(
1678     VmaAllocator      allocator,
1679     uint32_t          memoryType,
1680     VkDeviceMemory    memory,
1681     VkDeviceSize      size);
1682 
1683 /** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`.
1684 
1685 Provided for informative purpose, e.g. to gather statistics about number of
1686 allocations or total amount of memory allocated in Vulkan.
1687 
1688 Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
1689 */
1690 typedef struct VmaDeviceMemoryCallbacks {
1691     /// Optional, can be null.
1692     PFN_vmaAllocateDeviceMemoryFunction pfnAllocate;
1693     /// Optional, can be null.
1694     PFN_vmaFreeDeviceMemoryFunction pfnFree;
1695 } VmaDeviceMemoryCallbacks;
1696 
1697 /// Flags for created #VmaAllocator.
1698 typedef enum VmaAllocatorCreateFlagBits {
1699     /** \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.
1700 
1701     Using this flag may increase performance because internal mutexes are not used.
1702     */
1703     VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001,
1704     /** \brief Enables usage of VK_KHR_dedicated_allocation extension.
1705 
1706     Using this extenion will automatically allocate dedicated blocks of memory for
1707     some buffers and images instead of suballocating place for them out of bigger
1708     memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
1709     flag) when it is recommended by the driver. It may improve performance on some
1710     GPUs.
1711 
1712     You may set this flag only if you found out that following device extensions are
1713     supported, you enabled them while creating Vulkan device passed as
1714     VmaAllocatorCreateInfo::device, and you want them to be used internally by this
1715     library:
1716 
1717     - VK_KHR_get_memory_requirements2
1718     - VK_KHR_dedicated_allocation
1719 
1720 When this flag is set, you can experience following warnings reported by Vulkan
1721 validation layer. You can ignore them.
1722 
1723 > vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer.
1724     */
1725     VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002,
1726 
1727     VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1728 } VmaAllocatorCreateFlagBits;
1729 typedef VkFlags VmaAllocatorCreateFlags;
1730 
1731 /** \brief Pointers to some Vulkan functions - a subset used by the library.
1732 
1733 Used in VmaAllocatorCreateInfo::pVulkanFunctions.
1734 */
1735 typedef struct VmaVulkanFunctions {
1736     PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
1737     PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
1738     PFN_vkAllocateMemory vkAllocateMemory;
1739     PFN_vkFreeMemory vkFreeMemory;
1740     PFN_vkMapMemory vkMapMemory;
1741     PFN_vkUnmapMemory vkUnmapMemory;
1742     PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
1743     PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
1744     PFN_vkBindBufferMemory vkBindBufferMemory;
1745     PFN_vkBindImageMemory vkBindImageMemory;
1746     PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
1747     PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
1748     PFN_vkCreateBuffer vkCreateBuffer;
1749     PFN_vkDestroyBuffer vkDestroyBuffer;
1750     PFN_vkCreateImage vkCreateImage;
1751     PFN_vkDestroyImage vkDestroyImage;
1752     PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
1753 #if VMA_DEDICATED_ALLOCATION
1754     PFN_vkGetBufferMemoryRequirements2KHR vkGetBufferMemoryRequirements2KHR;
1755     PFN_vkGetImageMemoryRequirements2KHR vkGetImageMemoryRequirements2KHR;
1756 #endif
1757 } VmaVulkanFunctions;
1758 
1759 /// Flags to be used in VmaRecordSettings::flags.
1760 typedef enum VmaRecordFlagBits {
1761     /** \brief Enables flush after recording every function call.
1762 
1763     Enable it if you expect your application to crash, which may leave recording file truncated.
1764     It may degrade performance though.
1765     */
1766     VMA_RECORD_FLUSH_AFTER_CALL_BIT = 0x00000001,
1767 
1768     VMA_RECORD_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1769 } VmaRecordFlagBits;
1770 typedef VkFlags VmaRecordFlags;
1771 
1772 /// Parameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSettings.
1773 typedef struct VmaRecordSettings
1774 {
1775     /// Flags for recording. Use #VmaRecordFlagBits enum.
1776     VmaRecordFlags flags;
1777     /** \brief Path to the file that should be written by the recording.
1778 
1779     Suggested extension: "csv".
1780     If the file already exists, it will be overwritten.
1781     It will be opened for the whole time #VmaAllocator object is alive.
1782     If opening this file fails, creation of the whole allocator object fails.
1783     */
1784     const char* pFilePath;
1785 } VmaRecordSettings;
1786 
1787 /// Description of a Allocator to be created.
1788 typedef struct VmaAllocatorCreateInfo
1789 {
1790     /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum.
1791     VmaAllocatorCreateFlags flags;
1792     /// Vulkan physical device.
1793     /** It must be valid throughout whole lifetime of created allocator. */
1794     VkPhysicalDevice physicalDevice;
1795     /// Vulkan device.
1796     /** It must be valid throughout whole lifetime of created allocator. */
1797     VkDevice device;
1798     /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional.
1799     /** Set to 0 to use default, which is currently 256 MiB. */
1800     VkDeviceSize preferredLargeHeapBlockSize;
1801     /// Custom CPU memory allocation callbacks. Optional.
1802     /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */
1803     const VkAllocationCallbacks* pAllocationCallbacks;
1804     /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional.
1805     /** Optional, can be null. */
1806     const VmaDeviceMemoryCallbacks* pDeviceMemoryCallbacks;
1807     /** \brief Maximum number of additional frames that are in use at the same time as current frame.
1808 
1809     This value is used only when you make allocations with
1810     VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
1811     lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
1812 
1813     For example, if you double-buffer your command buffers, so resources used for
1814     rendering in previous frame may still be in use by the GPU at the moment you
1815     allocate resources needed for the current frame, set this value to 1.
1816 
1817     If you want to allow any allocations other than used in the current frame to
1818     become lost, set this value to 0.
1819     */
1820     uint32_t frameInUseCount;
1821     /** \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.
1822 
1823     If not NULL, it must be a pointer to an array of
1824     `VkPhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on
1825     maximum number of bytes that can be allocated out of particular Vulkan memory
1826     heap.
1827 
1828     Any of the elements may be equal to `VK_WHOLE_SIZE`, which means no limit on that
1829     heap. This is also the default in case of `pHeapSizeLimit` = NULL.
1830 
1831     If there is a limit defined for a heap:
1832 
1833     - If user tries to allocate more memory from that heap using this allocator,
1834       the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
1835     - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the
1836       value of this limit will be reported instead when using vmaGetMemoryProperties().
1837 
1838     Warning! Using this feature may not be equivalent to installing a GPU with
1839     smaller amount of memory, because graphics driver doesn't necessary fail new
1840     allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is
1841     exceeded. It may return success and just silently migrate some device memory
1842     blocks to system RAM. This driver behavior can also be controlled using
1843     VK_AMD_memory_overallocation_behavior extension.
1844     */
1845     const VkDeviceSize* pHeapSizeLimit;
1846     /** \brief Pointers to Vulkan functions. Can be null if you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1`.
1847 
1848     If you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1` in configuration section,
1849     you can pass null as this member, because the library will fetch pointers to
1850     Vulkan functions internally in a static way, like:
1851 
1852         vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
1853 
1854     Fill this member if you want to provide your own pointers to Vulkan functions,
1855     e.g. fetched using `vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`.
1856     */
1857     const VmaVulkanFunctions* pVulkanFunctions;
1858     /** \brief Parameters for recording of VMA calls. Can be null.
1859 
1860     If not null, it enables recording of calls to VMA functions to a file.
1861     If support for recording is not enabled using `VMA_RECORDING_ENABLED` macro,
1862     creation of the allocator object fails with `VK_ERROR_FEATURE_NOT_PRESENT`.
1863     */
1864     const VmaRecordSettings* pRecordSettings;
1865 } VmaAllocatorCreateInfo;
1866 
1867 /// Creates Allocator object.
1868 VkResult vmaCreateAllocator(
1869     const VmaAllocatorCreateInfo* pCreateInfo,
1870     VmaAllocator* pAllocator);
1871 
1872 /// Destroys allocator object.
1873 void vmaDestroyAllocator(
1874     VmaAllocator allocator);
1875 
1876 /**
1877 PhysicalDeviceProperties are fetched from physicalDevice by the allocator.
1878 You can access it here, without fetching it again on your own.
1879 */
1880 void vmaGetPhysicalDeviceProperties(
1881     VmaAllocator allocator,
1882     const VkPhysicalDeviceProperties** ppPhysicalDeviceProperties);
1883 
1884 /**
1885 PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator.
1886 You can access it here, without fetching it again on your own.
1887 */
1888 void vmaGetMemoryProperties(
1889     VmaAllocator allocator,
1890     const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties);
1891 
1892 /**
1893 \brief Given Memory Type Index, returns Property Flags of this memory type.
1894 
1895 This is just a convenience function. Same information can be obtained using
1896 vmaGetMemoryProperties().
1897 */
1898 void vmaGetMemoryTypeProperties(
1899     VmaAllocator allocator,
1900     uint32_t memoryTypeIndex,
1901     VkMemoryPropertyFlags* pFlags);
1902 
1903 /** \brief Sets index of the current frame.
1904 
1905 This function must be used if you make allocations with
1906 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT and
1907 #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flags to inform the allocator
1908 when a new frame begins. Allocations queried using vmaGetAllocationInfo() cannot
1909 become lost in the current frame.
1910 */
1911 void vmaSetCurrentFrameIndex(
1912     VmaAllocator allocator,
1913     uint32_t frameIndex);
1914 
1915 /** \brief Calculated statistics of memory usage in entire allocator.
1916 */
1917 typedef struct VmaStatInfo
1918 {
1919     /// Number of `VkDeviceMemory` Vulkan memory blocks allocated.
1920     uint32_t blockCount;
1921     /// Number of #VmaAllocation allocation objects allocated.
1922     uint32_t allocationCount;
1923     /// Number of free ranges of memory between allocations.
1924     uint32_t unusedRangeCount;
1925     /// Total number of bytes occupied by all allocations.
1926     VkDeviceSize usedBytes;
1927     /// Total number of bytes occupied by unused ranges.
1928     VkDeviceSize unusedBytes;
1929     VkDeviceSize allocationSizeMin, allocationSizeAvg, allocationSizeMax;
1930     VkDeviceSize unusedRangeSizeMin, unusedRangeSizeAvg, unusedRangeSizeMax;
1931 } VmaStatInfo;
1932 
1933 /// General statistics from current state of Allocator.
1934 typedef struct VmaStats
1935 {
1936     VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
1937     VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
1938     VmaStatInfo total;
1939 } VmaStats;
1940 
1941 /// Retrieves statistics from current state of the Allocator.
1942 void vmaCalculateStats(
1943     VmaAllocator allocator,
1944     VmaStats* pStats);
1945 
1946 #define VMA_STATS_STRING_ENABLED 1
1947 
1948 #if VMA_STATS_STRING_ENABLED
1949 
1950 /// Builds and returns statistics as string in JSON format.
1951 /** @param[out] ppStatsString Must be freed using vmaFreeStatsString() function.
1952 */
1953 void vmaBuildStatsString(
1954     VmaAllocator allocator,
1955     char** ppStatsString,
1956     VkBool32 detailedMap);
1957 
1958 void vmaFreeStatsString(
1959     VmaAllocator allocator,
1960     char* pStatsString);
1961 
1962 #endif // #if VMA_STATS_STRING_ENABLED
1963 
1964 /** \struct VmaPool
1965 \brief Represents custom memory pool
1966 
1967 Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it.
1968 Call function vmaDestroyPool() to destroy it.
1969 
1970 For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools).
1971 */
1972 VK_DEFINE_HANDLE(VmaPool)
1973 
1974 typedef enum VmaMemoryUsage
1975 {
1976     /** No intended memory usage specified.
1977     Use other members of VmaAllocationCreateInfo to specify your requirements.
1978     */
1979     VMA_MEMORY_USAGE_UNKNOWN = 0,
1980     /** Memory will be used on device only, so fast access from the device is preferred.
1981     It usually means device-local GPU (video) memory.
1982     No need to be mappable on host.
1983     It is roughly equivalent of `D3D12_HEAP_TYPE_DEFAULT`.
1984 
1985     Usage:
1986 
1987     - Resources written and read by device, e.g. images used as attachments.
1988     - Resources transferred from host once (immutable) or infrequently and read by
1989       device multiple times, e.g. textures to be sampled, vertex buffers, uniform
1990       (constant) buffers, and majority of other types of resources used on GPU.
1991 
1992     Allocation may still end up in `HOST_VISIBLE` memory on some implementations.
1993     In such case, you are free to map it.
1994     You can use #VMA_ALLOCATION_CREATE_MAPPED_BIT with this usage type.
1995     */
1996     VMA_MEMORY_USAGE_GPU_ONLY = 1,
1997     /** Memory will be mappable on host.
1998     It usually means CPU (system) memory.
1999     Guarantees to be `HOST_VISIBLE` and `HOST_COHERENT`.
2000     CPU access is typically uncached. Writes may be write-combined.
2001     Resources created in this pool may still be accessible to the device, but access to them can be slow.
2002     It is roughly equivalent of `D3D12_HEAP_TYPE_UPLOAD`.
2003 
2004     Usage: Staging copy of resources used as transfer source.
2005     */
2006     VMA_MEMORY_USAGE_CPU_ONLY = 2,
2007     /**
2008     Memory that is both mappable on host (guarantees to be `HOST_VISIBLE`) and preferably fast to access by GPU.
2009     CPU access is typically uncached. Writes may be write-combined.
2010 
2011     Usage: Resources written frequently by host (dynamic), read by device. E.g. textures, vertex buffers, uniform buffers updated every frame or every draw call.
2012     */
2013     VMA_MEMORY_USAGE_CPU_TO_GPU = 3,
2014     /** Memory mappable on host (guarantees to be `HOST_VISIBLE`) and cached.
2015     It is roughly equivalent of `D3D12_HEAP_TYPE_READBACK`.
2016 
2017     Usage:
2018 
2019     - Resources written by device, read by host - results of some computations, e.g. screen capture, average scene luminance for HDR tone mapping.
2020     - 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.
2021     */
2022     VMA_MEMORY_USAGE_GPU_TO_CPU = 4,
2023     VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
2024 } VmaMemoryUsage;
2025 
2026 /// Flags to be passed as VmaAllocationCreateInfo::flags.
2027 typedef enum VmaAllocationCreateFlagBits {
2028     /** \brief Set this flag if the allocation should have its own memory block.
2029 
2030     Use it for special, big resources, like fullscreen images used as attachments.
2031 
2032     This flag must also be used for host visible resources that you want to map
2033     simultaneously because otherwise they might end up as regions of the same
2034     `VkDeviceMemory`, while mapping same `VkDeviceMemory` multiple times
2035     simultaneously is illegal.
2036 
2037     You should not use this flag if VmaAllocationCreateInfo::pool is not null.
2038     */
2039     VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001,
2040 
2041     /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block.
2042 
2043     If new allocation cannot be placed in any of the existing blocks, allocation
2044     fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
2045 
2046     You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and
2047     #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense.
2048 
2049     If VmaAllocationCreateInfo::pool is not null, this flag is implied and ignored. */
2050     VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002,
2051     /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it.
2052 
2053     Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData.
2054 
2055     Is it valid to use this flag for allocation made from memory type that is not
2056     `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is
2057     useful if you need an allocation that is efficient to use on GPU
2058     (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that
2059     support it (e.g. Intel GPU).
2060 
2061     You should not use this flag together with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT.
2062     */
2063     VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004,
2064     /** Allocation created with this flag can become lost as a result of another
2065     allocation with #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag, so you
2066     must check it before use.
2067 
2068     To check if allocation is not lost, call vmaGetAllocationInfo() and check if
2069     VmaAllocationInfo::deviceMemory is not `VK_NULL_HANDLE`.
2070 
2071     For details about supporting lost allocations, see Lost Allocations
2072     chapter of User Guide on Main Page.
2073 
2074     You should not use this flag together with #VMA_ALLOCATION_CREATE_MAPPED_BIT.
2075     */
2076     VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT = 0x00000008,
2077     /** While creating allocation using this flag, other allocations that were
2078     created with flag #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT can become lost.
2079 
2080     For details about supporting lost allocations, see Lost Allocations
2081     chapter of User Guide on Main Page.
2082     */
2083     VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT = 0x00000010,
2084     /** Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a
2085     null-terminated string. Instead of copying pointer value, a local copy of the
2086     string is made and stored in allocation's `pUserData`. The string is automatically
2087     freed together with the allocation. It is also used in vmaBuildStatsString().
2088     */
2089     VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020,
2090     /** Allocation will be created from upper stack in a double stack pool.
2091 
2092     This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag.
2093     */
2094     VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040,
2095 
2096     /** Allocation strategy that chooses smallest possible free range for the
2097     allocation.
2098     */
2099     VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT  = 0x00010000,
2100     /** Allocation strategy that chooses biggest possible free range for the
2101     allocation.
2102     */
2103     VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT = 0x00020000,
2104     /** Allocation strategy that chooses first suitable free range for the
2105     allocation.
2106 
2107     "First" doesn't necessarily means the one with smallest offset in memory,
2108     but rather the one that is easiest and fastest to find.
2109     */
2110     VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = 0x00040000,
2111 
2112     /** Allocation strategy that tries to minimize memory usage.
2113     */
2114     VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT,
2115     /** Allocation strategy that tries to minimize allocation time.
2116     */
2117     VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
2118     /** Allocation strategy that tries to minimize memory fragmentation.
2119     */
2120     VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT,
2121 
2122     /** A bit mask to extract only `STRATEGY` bits from entire set of flags.
2123     */
2124     VMA_ALLOCATION_CREATE_STRATEGY_MASK =
2125         VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT |
2126         VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT |
2127         VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
2128 
2129     VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2130 } VmaAllocationCreateFlagBits;
2131 typedef VkFlags VmaAllocationCreateFlags;
2132 
2133 typedef struct VmaAllocationCreateInfo
2134 {
2135     /// Use #VmaAllocationCreateFlagBits enum.
2136     VmaAllocationCreateFlags flags;
2137     /** \brief Intended usage of memory.
2138 
2139     You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n
2140     If `pool` is not null, this member is ignored.
2141     */
2142     VmaMemoryUsage usage;
2143     /** \brief Flags that must be set in a Memory Type chosen for an allocation.
2144 
2145     Leave 0 if you specify memory requirements in other way. \n
2146     If `pool` is not null, this member is ignored.*/
2147     VkMemoryPropertyFlags requiredFlags;
2148     /** \brief Flags that preferably should be set in a memory type chosen for an allocation.
2149 
2150     Set to 0 if no additional flags are prefered. \n
2151     If `pool` is not null, this member is ignored. */
2152     VkMemoryPropertyFlags preferredFlags;
2153     /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation.
2154 
2155     Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if
2156     it meets other requirements specified by this structure, with no further
2157     restrictions on memory type index. \n
2158     If `pool` is not null, this member is ignored.
2159     */
2160     uint32_t memoryTypeBits;
2161     /** \brief Pool that this allocation should be created in.
2162 
2163     Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members:
2164     `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.
2165     */
2166     VmaPool pool;
2167     /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().
2168 
2169     If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either
2170     null or pointer to a null-terminated string. The string will be then copied to
2171     internal buffer, so it doesn't need to be valid after allocation call.
2172     */
2173     void* pUserData;
2174 } VmaAllocationCreateInfo;
2175 
2176 /**
2177 \brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.
2178 
2179 This algorithm tries to find a memory type that:
2180 
2181 - Is allowed by memoryTypeBits.
2182 - Contains all the flags from pAllocationCreateInfo->requiredFlags.
2183 - Matches intended usage.
2184 - Has as many flags from pAllocationCreateInfo->preferredFlags as possible.
2185 
2186 \return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result
2187 from this function or any other allocating function probably means that your
2188 device doesn't support any memory type with requested features for the specific
2189 type of resource you want to use it for. Please check parameters of your
2190 resource, like image layout (OPTIMAL versus LINEAR) or mip level count.
2191 */
2192 VkResult vmaFindMemoryTypeIndex(
2193     VmaAllocator allocator,
2194     uint32_t memoryTypeBits,
2195     const VmaAllocationCreateInfo* pAllocationCreateInfo,
2196     uint32_t* pMemoryTypeIndex);
2197 
2198 /**
2199 \brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.
2200 
2201 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
2202 It internally creates a temporary, dummy buffer that never has memory bound.
2203 It is just a convenience function, equivalent to calling:
2204 
2205 - `vkCreateBuffer`
2206 - `vkGetBufferMemoryRequirements`
2207 - `vmaFindMemoryTypeIndex`
2208 - `vkDestroyBuffer`
2209 */
2210 VkResult vmaFindMemoryTypeIndexForBufferInfo(
2211     VmaAllocator allocator,
2212     const VkBufferCreateInfo* pBufferCreateInfo,
2213     const VmaAllocationCreateInfo* pAllocationCreateInfo,
2214     uint32_t* pMemoryTypeIndex);
2215 
2216 /**
2217 \brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.
2218 
2219 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
2220 It internally creates a temporary, dummy image that never has memory bound.
2221 It is just a convenience function, equivalent to calling:
2222 
2223 - `vkCreateImage`
2224 - `vkGetImageMemoryRequirements`
2225 - `vmaFindMemoryTypeIndex`
2226 - `vkDestroyImage`
2227 */
2228 VkResult vmaFindMemoryTypeIndexForImageInfo(
2229     VmaAllocator allocator,
2230     const VkImageCreateInfo* pImageCreateInfo,
2231     const VmaAllocationCreateInfo* pAllocationCreateInfo,
2232     uint32_t* pMemoryTypeIndex);
2233 
2234 /// Flags to be passed as VmaPoolCreateInfo::flags.
2235 typedef enum VmaPoolCreateFlagBits {
2236     /** \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.
2237 
2238     This is an optional optimization flag.
2239 
2240     If you always allocate using vmaCreateBuffer(), vmaCreateImage(),
2241     vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator
2242     knows exact type of your allocations so it can handle Buffer-Image Granularity
2243     in the optimal way.
2244 
2245     If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(),
2246     exact type of such allocations is not known, so allocator must be conservative
2247     in handling Buffer-Image Granularity, which can lead to suboptimal allocation
2248     (wasted memory). In that case, if you can make sure you always allocate only
2249     buffers and linear images or only optimal images out of this pool, use this flag
2250     to make allocator disregard Buffer-Image Granularity and so make allocations
2251     faster and more optimal.
2252     */
2253     VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002,
2254 
2255     /** \brief Enables alternative, linear allocation algorithm in this pool.
2256 
2257     Specify this flag to enable linear allocation algorithm, which always creates
2258     new allocations after last one and doesn't reuse space from allocations freed in
2259     between. It trades memory consumption for simplified algorithm and data
2260     structure, which has better performance and uses less memory for metadata.
2261 
2262     By using this flag, you can achieve behavior of free-at-once, stack,
2263     ring buffer, and double stack. For details, see documentation chapter
2264     \ref linear_algorithm.
2265 
2266     When using this flag, you must specify VmaPoolCreateInfo::maxBlockCount == 1 (or 0 for default).
2267 
2268     For more details, see [Linear allocation algorithm](@ref linear_algorithm).
2269     */
2270     VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004,
2271 
2272     /** \brief Enables alternative, buddy allocation algorithm in this pool.
2273 
2274     It operates on a tree of blocks, each having size that is a power of two and
2275     a half of its parent's size. Comparing to default algorithm, this one provides
2276     faster allocation and deallocation and decreased external fragmentation,
2277     at the expense of more memory wasted (internal fragmentation).
2278 
2279     For more details, see [Buddy allocation algorithm](@ref buddy_algorithm).
2280     */
2281     VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT = 0x00000008,
2282 
2283     /** Bit mask to extract only `ALGORITHM` bits from entire set of flags.
2284     */
2285     VMA_POOL_CREATE_ALGORITHM_MASK =
2286         VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT |
2287         VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT,
2288 
2289     VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2290 } VmaPoolCreateFlagBits;
2291 typedef VkFlags VmaPoolCreateFlags;
2292 
2293 /** \brief Describes parameter of created #VmaPool.
2294 */
2295 typedef struct VmaPoolCreateInfo {
2296     /** \brief Vulkan memory type index to allocate this pool from.
2297     */
2298     uint32_t memoryTypeIndex;
2299     /** \brief Use combination of #VmaPoolCreateFlagBits.
2300     */
2301     VmaPoolCreateFlags flags;
2302     /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. Optional.
2303 
2304     Specify nonzero to set explicit, constant size of memory blocks used by this
2305     pool.
2306 
2307     Leave 0 to use default and let the library manage block sizes automatically.
2308     Sizes of particular blocks may vary.
2309     */
2310     VkDeviceSize blockSize;
2311     /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty.
2312 
2313     Set to 0 to have no preallocated blocks and allow the pool be completely empty.
2314     */
2315     size_t minBlockCount;
2316     /** \brief Maximum number of blocks that can be allocated in this pool. Optional.
2317 
2318     Set to 0 to use default, which is `SIZE_MAX`, which means no limit.
2319 
2320     Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated
2321     throughout whole lifetime of this pool.
2322     */
2323     size_t maxBlockCount;
2324     /** \brief Maximum number of additional frames that are in use at the same time as current frame.
2325 
2326     This value is used only when you make allocations with
2327     #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
2328     lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
2329 
2330     For example, if you double-buffer your command buffers, so resources used for
2331     rendering in previous frame may still be in use by the GPU at the moment you
2332     allocate resources needed for the current frame, set this value to 1.
2333 
2334     If you want to allow any allocations other than used in the current frame to
2335     become lost, set this value to 0.
2336     */
2337     uint32_t frameInUseCount;
2338 } VmaPoolCreateInfo;
2339 
2340 /** \brief Describes parameter of existing #VmaPool.
2341 */
2342 typedef struct VmaPoolStats {
2343     /** \brief Total amount of `VkDeviceMemory` allocated from Vulkan for this pool, in bytes.
2344     */
2345     VkDeviceSize size;
2346     /** \brief Total number of bytes in the pool not used by any #VmaAllocation.
2347     */
2348     VkDeviceSize unusedSize;
2349     /** \brief Number of #VmaAllocation objects created from this pool that were not destroyed or lost.
2350     */
2351     size_t allocationCount;
2352     /** \brief Number of continuous memory ranges in the pool not used by any #VmaAllocation.
2353     */
2354     size_t unusedRangeCount;
2355     /** \brief Size of the largest continuous free memory region available for new allocation.
2356 
2357     Making a new allocation of that size is not guaranteed to succeed because of
2358     possible additional margin required to respect alignment and buffer/image
2359     granularity.
2360     */
2361     VkDeviceSize unusedRangeSizeMax;
2362     /** \brief Number of `VkDeviceMemory` blocks allocated for this pool.
2363     */
2364     size_t blockCount;
2365 } VmaPoolStats;
2366 
2367 /** \brief Allocates Vulkan device memory and creates #VmaPool object.
2368 
2369 @param allocator Allocator object.
2370 @param pCreateInfo Parameters of pool to create.
2371 @param[out] pPool Handle to created pool.
2372 */
2373 VkResult vmaCreatePool(
2374 	VmaAllocator allocator,
2375 	const VmaPoolCreateInfo* pCreateInfo,
2376 	VmaPool* pPool);
2377 
2378 /** \brief Destroys #VmaPool object and frees Vulkan device memory.
2379 */
2380 void vmaDestroyPool(
2381     VmaAllocator allocator,
2382     VmaPool pool);
2383 
2384 /** \brief Retrieves statistics of existing #VmaPool object.
2385 
2386 @param allocator Allocator object.
2387 @param pool Pool object.
2388 @param[out] pPoolStats Statistics of specified pool.
2389 */
2390 void vmaGetPoolStats(
2391     VmaAllocator allocator,
2392     VmaPool pool,
2393     VmaPoolStats* pPoolStats);
2394 
2395 /** \brief Marks all allocations in given pool as lost if they are not used in current frame or VmaPoolCreateInfo::frameInUseCount back from now.
2396 
2397 @param allocator Allocator object.
2398 @param pool Pool.
2399 @param[out] pLostAllocationCount Number of allocations marked as lost. Optional - pass null if you don't need this information.
2400 */
2401 void vmaMakePoolAllocationsLost(
2402     VmaAllocator allocator,
2403     VmaPool pool,
2404     size_t* pLostAllocationCount);
2405 
2406 /** \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions.
2407 
2408 Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
2409 `VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is
2410 `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
2411 
2412 Possible return values:
2413 
2414 - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool.
2415 - `VK_SUCCESS` - corruption detection has been performed and succeeded.
2416 - `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
2417   `VMA_ASSERT` is also fired in that case.
2418 - Other value: Error returned by Vulkan, e.g. memory mapping failure.
2419 */
2420 VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool);
2421 
2422 /** \struct VmaAllocation
2423 \brief Represents single memory allocation.
2424 
2425 It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type
2426 plus unique offset.
2427 
2428 There are multiple ways to create such object.
2429 You need to fill structure VmaAllocationCreateInfo.
2430 For more information see [Choosing memory type](@ref choosing_memory_type).
2431 
2432 Although the library provides convenience functions that create Vulkan buffer or image,
2433 allocate memory for it and bind them together,
2434 binding of the allocation to a buffer or an image is out of scope of the allocation itself.
2435 Allocation object can exist without buffer/image bound,
2436 binding can be done manually by the user, and destruction of it can be done
2437 independently of destruction of the allocation.
2438 
2439 The object also remembers its size and some other information.
2440 To retrieve this information, use function vmaGetAllocationInfo() and inspect
2441 returned structure VmaAllocationInfo.
2442 
2443 Some kinds allocations can be in lost state.
2444 For more information, see [Lost allocations](@ref lost_allocations).
2445 */
2446 VK_DEFINE_HANDLE(VmaAllocation)
2447 
2448 /** \brief Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
2449 */
2450 typedef struct VmaAllocationInfo {
2451     /** \brief Memory type index that this allocation was allocated from.
2452 
2453     It never changes.
2454     */
2455     uint32_t memoryType;
2456     /** \brief Handle to Vulkan memory object.
2457 
2458     Same memory object can be shared by multiple allocations.
2459 
2460     It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
2461 
2462     If the allocation is lost, it is equal to `VK_NULL_HANDLE`.
2463     */
2464     VkDeviceMemory deviceMemory;
2465     /** \brief Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation.
2466 
2467     It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
2468     */
2469     VkDeviceSize offset;
2470     /** \brief Size of this allocation, in bytes.
2471 
2472     It never changes, unless allocation is lost.
2473     */
2474     VkDeviceSize size;
2475     /** \brief Pointer to the beginning of this allocation as mapped data.
2476 
2477     If the allocation hasn't been mapped using vmaMapMemory() and hasn't been
2478     created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value null.
2479 
2480     It can change after call to vmaMapMemory(), vmaUnmapMemory().
2481     It can also change after call to vmaDefragment() if this allocation is passed to the function.
2482     */
2483     void* pMappedData;
2484     /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData().
2485 
2486     It can change after call to vmaSetAllocationUserData() for this allocation.
2487     */
2488     void* pUserData;
2489 } VmaAllocationInfo;
2490 
2491 /** \brief General purpose memory allocation.
2492 
2493 @param[out] pAllocation Handle to allocated memory.
2494 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
2495 
2496 You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
2497 
2498 It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(),
2499 vmaCreateBuffer(), vmaCreateImage() instead whenever possible.
2500 */
2501 VkResult vmaAllocateMemory(
2502     VmaAllocator allocator,
2503     const VkMemoryRequirements* pVkMemoryRequirements,
2504     const VmaAllocationCreateInfo* pCreateInfo,
2505     VmaAllocation* pAllocation,
2506     VmaAllocationInfo* pAllocationInfo);
2507 
2508 /** \brief General purpose memory allocation for multiple allocation objects at once.
2509 
2510 @param allocator Allocator object.
2511 @param pVkMemoryRequirements Memory requirements for each allocation.
2512 @param pCreateInfo Creation parameters for each alloction.
2513 @param allocationCount Number of allocations to make.
2514 @param[out] pAllocations Pointer to array that will be filled with handles to created allocations.
2515 @param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations.
2516 
2517 You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
2518 
2519 Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding.
2520 It is just a general purpose allocation function able to make multiple allocations at once.
2521 It may be internally optimized to be more efficient than calling vmaAllocateMemory() `allocationCount` times.
2522 
2523 All allocations are made using same parameters. All of them are created out of the same memory pool and type.
2524 If any allocation fails, all allocations already made within this function call are also freed, so that when
2525 returned result is not `VK_SUCCESS`, `pAllocation` array is always entirely filled with `VK_NULL_HANDLE`.
2526 */
2527 VkResult vmaAllocateMemoryPages(
2528     VmaAllocator allocator,
2529     const VkMemoryRequirements* pVkMemoryRequirements,
2530     const VmaAllocationCreateInfo* pCreateInfo,
2531     size_t allocationCount,
2532     VmaAllocation* pAllocations,
2533     VmaAllocationInfo* pAllocationInfo);
2534 
2535 /**
2536 @param[out] pAllocation Handle to allocated memory.
2537 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
2538 
2539 You should free the memory using vmaFreeMemory().
2540 */
2541 VkResult vmaAllocateMemoryForBuffer(
2542     VmaAllocator allocator,
2543     VkBuffer buffer,
2544     const VmaAllocationCreateInfo* pCreateInfo,
2545     VmaAllocation* pAllocation,
2546     VmaAllocationInfo* pAllocationInfo);
2547 
2548 /// Function similar to vmaAllocateMemoryForBuffer().
2549 VkResult vmaAllocateMemoryForImage(
2550     VmaAllocator allocator,
2551     VkImage image,
2552     const VmaAllocationCreateInfo* pCreateInfo,
2553     VmaAllocation* pAllocation,
2554     VmaAllocationInfo* pAllocationInfo);
2555 
2556 /** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().
2557 
2558 Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped.
2559 */
2560 void vmaFreeMemory(
2561     VmaAllocator allocator,
2562     VmaAllocation allocation);
2563 
2564 /** \brief Frees memory and destroys multiple allocations.
2565 
2566 Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding.
2567 It is just a general purpose function to free memory and destroy allocations made using e.g. vmaAllocateMemory(),
2568 vmaAllocateMemoryPages() and other functions.
2569 It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times.
2570 
2571 Allocations in `pAllocations` array can come from any memory pools and types.
2572 Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped.
2573 */
2574 void vmaFreeMemoryPages(
2575     VmaAllocator allocator,
2576     size_t allocationCount,
2577     VmaAllocation* pAllocations);
2578 
2579 /** \brief Tries to resize an allocation in place, if there is enough free memory after it.
2580 
2581 Tries to change allocation's size without moving or reallocating it.
2582 You can both shrink and grow allocation size.
2583 When growing, it succeeds only when the allocation belongs to a memory block with enough
2584 free space after it.
2585 
2586 Returns `VK_SUCCESS` if allocation's size has been successfully changed.
2587 Returns `VK_ERROR_OUT_OF_POOL_MEMORY` if allocation's size could not be changed.
2588 
2589 After successful call to this function, VmaAllocationInfo::size of this allocation changes.
2590 All other parameters stay the same: memory pool and type, alignment, offset, mapped pointer.
2591 
2592 - Calling this function on allocation that is in lost state fails with result `VK_ERROR_VALIDATION_FAILED_EXT`.
2593 - Calling this function with `newSize` same as current allocation size does nothing and returns `VK_SUCCESS`.
2594 - Resizing dedicated allocations, as well as allocations created in pools that use linear
2595   or buddy algorithm, is not supported.
2596   The function returns `VK_ERROR_FEATURE_NOT_PRESENT` in such cases.
2597   Support may be added in the future.
2598 */
2599 VkResult vmaResizeAllocation(
2600     VmaAllocator allocator,
2601     VmaAllocation allocation,
2602     VkDeviceSize newSize);
2603 
2604 /** \brief Returns current information about specified allocation and atomically marks it as used in current frame.
2605 
2606 Current paramters of given allocation are returned in `pAllocationInfo`.
2607 
2608 This function also atomically "touches" allocation - marks it as used in current frame,
2609 just like vmaTouchAllocation().
2610 If the allocation is in lost state, `pAllocationInfo->deviceMemory == VK_NULL_HANDLE`.
2611 
2612 Although this function uses atomics and doesn't lock any mutex, so it should be quite efficient,
2613 you can avoid calling it too often.
2614 
2615 - You can retrieve same VmaAllocationInfo structure while creating your resource, from function
2616   vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change
2617   (e.g. due to defragmentation or allocation becoming lost).
2618 - If you just want to check if allocation is not lost, vmaTouchAllocation() will work faster.
2619 */
2620 void vmaGetAllocationInfo(
2621     VmaAllocator allocator,
2622     VmaAllocation allocation,
2623     VmaAllocationInfo* pAllocationInfo);
2624 
2625 /** \brief Returns `VK_TRUE` if allocation is not lost and atomically marks it as used in current frame.
2626 
2627 If the allocation has been created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
2628 this function returns `VK_TRUE` if it's not in lost state, so it can still be used.
2629 It then also atomically "touches" the allocation - marks it as used in current frame,
2630 so that you can be sure it won't become lost in current frame or next `frameInUseCount` frames.
2631 
2632 If the allocation is in lost state, the function returns `VK_FALSE`.
2633 Memory of such allocation, as well as buffer or image bound to it, should not be used.
2634 Lost allocation and the buffer/image still need to be destroyed.
2635 
2636 If the allocation has been created without #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
2637 this function always returns `VK_TRUE`.
2638 */
2639 VkBool32 vmaTouchAllocation(
2640     VmaAllocator allocator,
2641     VmaAllocation allocation);
2642 
2643 /** \brief Sets pUserData in given allocation to new value.
2644 
2645 If the allocation was created with VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT,
2646 pUserData must be either null, or pointer to a null-terminated string. The function
2647 makes local copy of the string and sets it as allocation's `pUserData`. String
2648 passed as pUserData doesn't need to be valid for whole lifetime of the allocation -
2649 you can free it after this call. String previously pointed by allocation's
2650 pUserData is freed from memory.
2651 
2652 If the flag was not used, the value of pointer `pUserData` is just copied to
2653 allocation's `pUserData`. It is opaque, so you can use it however you want - e.g.
2654 as a pointer, ordinal number or some handle to you own data.
2655 */
2656 void vmaSetAllocationUserData(
2657     VmaAllocator allocator,
2658     VmaAllocation allocation,
2659     void* pUserData);
2660 
2661 /** \brief Creates new allocation that is in lost state from the beginning.
2662 
2663 It can be useful if you need a dummy, non-null allocation.
2664 
2665 You still need to destroy created object using vmaFreeMemory().
2666 
2667 Returned allocation is not tied to any specific memory pool or memory type and
2668 not bound to any image or buffer. It has size = 0. It cannot be turned into
2669 a real, non-empty allocation.
2670 */
2671 void vmaCreateLostAllocation(
2672     VmaAllocator allocator,
2673     VmaAllocation* pAllocation);
2674 
2675 /** \brief Maps memory represented by given allocation and returns pointer to it.
2676 
2677 Maps memory represented by given allocation to make it accessible to CPU code.
2678 When succeeded, `*ppData` contains pointer to first byte of this memory.
2679 If the allocation is part of bigger `VkDeviceMemory` block, the pointer is
2680 correctly offseted to the beginning of region assigned to this particular
2681 allocation.
2682 
2683 Mapping is internally reference-counted and synchronized, so despite raw Vulkan
2684 function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory`
2685 multiple times simultaneously, it is safe to call this function on allocations
2686 assigned to the same memory block. Actual Vulkan memory will be mapped on first
2687 mapping and unmapped on last unmapping.
2688 
2689 If the function succeeded, you must call vmaUnmapMemory() to unmap the
2690 allocation when mapping is no longer needed or before freeing the allocation, at
2691 the latest.
2692 
2693 It also safe to call this function multiple times on the same allocation. You
2694 must call vmaUnmapMemory() same number of times as you called vmaMapMemory().
2695 
2696 It is also safe to call this function on allocation created with
2697 #VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time.
2698 You must still call vmaUnmapMemory() same number of times as you called
2699 vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the
2700 "0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag.
2701 
2702 This function fails when used on allocation made in memory type that is not
2703 `HOST_VISIBLE`.
2704 
2705 This function always fails when called for allocation that was created with
2706 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocations cannot be
2707 mapped.
2708 */
2709 VkResult vmaMapMemory(
2710     VmaAllocator allocator,
2711     VmaAllocation allocation,
2712     void** ppData);
2713 
2714 /** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
2715 
2716 For details, see description of vmaMapMemory().
2717 */
2718 void vmaUnmapMemory(
2719     VmaAllocator allocator,
2720     VmaAllocation allocation);
2721 
2722 /** \brief Flushes memory of given allocation.
2723 
2724 Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation.
2725 
2726 - `offset` must be relative to the beginning of allocation.
2727 - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
2728 - `offset` and `size` don't have to be aligned.
2729   They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
2730 - If `size` is 0, this call is ignored.
2731 - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
2732   this call is ignored.
2733 */
2734 void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
2735 
2736 /** \brief Invalidates memory of given allocation.
2737 
2738 Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation.
2739 
2740 - `offset` must be relative to the beginning of allocation.
2741 - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
2742 - `offset` and `size` don't have to be aligned.
2743   They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
2744 - If `size` is 0, this call is ignored.
2745 - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
2746   this call is ignored.
2747 */
2748 void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
2749 
2750 /** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions.
2751 
2752 @param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked.
2753 
2754 Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
2755 `VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are
2756 `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
2757 
2758 Possible return values:
2759 
2760 - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types.
2761 - `VK_SUCCESS` - corruption detection has been performed and succeeded.
2762 - `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
2763   `VMA_ASSERT` is also fired in that case.
2764 - Other value: Error returned by Vulkan, e.g. memory mapping failure.
2765 */
2766 VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits);
2767 
2768 /** \struct VmaDefragmentationContext
2769 \brief Represents Opaque object that represents started defragmentation process.
2770 
2771 Fill structure #VmaDefragmentationInfo2 and call function vmaDefragmentationBegin() to create it.
2772 Call function vmaDefragmentationEnd() to destroy it.
2773 */
2774 VK_DEFINE_HANDLE(VmaDefragmentationContext)
2775 
2776 /// Flags to be used in vmaDefragmentationBegin(). None at the moment. Reserved for future use.
2777 typedef enum VmaDefragmentationFlagBits {
2778     VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2779 } VmaDefragmentationFlagBits;
2780 typedef VkFlags VmaDefragmentationFlags;
2781 
2782 /** \brief Parameters for defragmentation.
2783 
2784 To be used with function vmaDefragmentationBegin().
2785 */
2786 typedef struct VmaDefragmentationInfo2 {
2787     /** \brief Reserved for future use. Should be 0.
2788     */
2789     VmaDefragmentationFlags flags;
2790     /** \brief Number of allocations in `pAllocations` array.
2791     */
2792     uint32_t allocationCount;
2793     /** \brief Pointer to array of allocations that can be defragmented.
2794 
2795     The array should have `allocationCount` elements.
2796     The array should not contain nulls.
2797     Elements in the array should be unique - same allocation cannot occur twice.
2798     It is safe to pass allocations that are in the lost state - they are ignored.
2799     All allocations not present in this array are considered non-moveable during this defragmentation.
2800     */
2801     VmaAllocation* pAllocations;
2802     /** \brief Optional, output. Pointer to array that will be filled with information whether the allocation at certain index has been changed during defragmentation.
2803 
2804     The array should have `allocationCount` elements.
2805     You can pass null if you are not interested in this information.
2806     */
2807     VkBool32* pAllocationsChanged;
2808     /** \brief Numer of pools in `pPools` array.
2809     */
2810     uint32_t poolCount;
2811     /** \brief Either null or pointer to array of pools to be defragmented.
2812 
2813     All the allocations in the specified pools can be moved during defragmentation
2814     and there is no way to check if they were really moved as in `pAllocationsChanged`,
2815     so you must query all the allocations in all these pools for new `VkDeviceMemory`
2816     and offset using vmaGetAllocationInfo() if you might need to recreate buffers
2817     and images bound to them.
2818 
2819     The array should have `poolCount` elements.
2820     The array should not contain nulls.
2821     Elements in the array should be unique - same pool cannot occur twice.
2822 
2823     Using this array is equivalent to specifying all allocations from the pools in `pAllocations`.
2824     It might be more efficient.
2825     */
2826     VmaPool* pPools;
2827     /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on CPU side, like `memcpy()`, `memmove()`.
2828 
2829     `VK_WHOLE_SIZE` means no limit.
2830     */
2831     VkDeviceSize maxCpuBytesToMove;
2832     /** \brief Maximum number of allocations that can be moved to a different place using transfers on CPU side, like `memcpy()`, `memmove()`.
2833 
2834     `UINT32_MAX` means no limit.
2835     */
2836     uint32_t maxCpuAllocationsToMove;
2837     /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on GPU side, posted to `commandBuffer`.
2838 
2839     `VK_WHOLE_SIZE` means no limit.
2840     */
2841     VkDeviceSize maxGpuBytesToMove;
2842     /** \brief Maximum number of allocations that can be moved to a different place using transfers on GPU side, posted to `commandBuffer`.
2843 
2844     `UINT32_MAX` means no limit.
2845     */
2846     uint32_t maxGpuAllocationsToMove;
2847     /** \brief Optional. Command buffer where GPU copy commands will be posted.
2848 
2849     If not null, it must be a valid command buffer handle that supports Transfer queue type.
2850     It must be in the recording state and outside of a render pass instance.
2851     You need to submit it and make sure it finished execution before calling vmaDefragmentationEnd().
2852 
2853     Passing null means that only CPU defragmentation will be performed.
2854     */
2855     VkCommandBuffer commandBuffer;
2856 } VmaDefragmentationInfo2;
2857 
2858 /** \brief Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
2859 
2860 \deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
2861 */
2862 typedef struct VmaDefragmentationInfo {
2863     /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places.
2864 
2865     Default is `VK_WHOLE_SIZE`, which means no limit.
2866     */
2867     VkDeviceSize maxBytesToMove;
2868     /** \brief Maximum number of allocations that can be moved to different place.
2869 
2870     Default is `UINT32_MAX`, which means no limit.
2871     */
2872     uint32_t maxAllocationsToMove;
2873 } VmaDefragmentationInfo;
2874 
2875 /** \brief Statistics returned by function vmaDefragment(). */
2876 typedef struct VmaDefragmentationStats {
2877     /// Total number of bytes that have been copied while moving allocations to different places.
2878     VkDeviceSize bytesMoved;
2879     /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects.
2880     VkDeviceSize bytesFreed;
2881     /// Number of allocations that have been moved to different places.
2882     uint32_t allocationsMoved;
2883     /// Number of empty `VkDeviceMemory` objects that have been released to the system.
2884     uint32_t deviceMemoryBlocksFreed;
2885 } VmaDefragmentationStats;
2886 
2887 /** \brief Begins defragmentation process.
2888 
2889 @param allocator Allocator object.
2890 @param pInfo Structure filled with parameters of defragmentation.
2891 @param[out] pStats Optional. Statistics of defragmentation. You can pass null if you are not interested in this information.
2892 @param[out] pContext Context object that must be passed to vmaDefragmentationEnd() to finish defragmentation.
2893 @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.
2894 
2895 Use this function instead of old, deprecated vmaDefragment().
2896 
2897 Warning! Between the call to vmaDefragmentationBegin() and vmaDefragmentationEnd():
2898 
2899 - You should not use any of allocations passed as `pInfo->pAllocations` or
2900   any allocations that belong to pools passed as `pInfo->pPools`,
2901   including calling vmaGetAllocationInfo(), vmaTouchAllocation(), or access
2902   their data.
2903 - Some mutexes protecting internal data structures may be locked, so trying to
2904   make or free any allocations, bind buffers or images, map memory, or launch
2905   another simultaneous defragmentation in between may cause stall (when done on
2906   another thread) or deadlock (when done on the same thread), unless you are
2907   100% sure that defragmented allocations are in different pools.
2908 - Information returned via `pStats` and `pInfo->pAllocationsChanged` are undefined.
2909   They become valid after call to vmaDefragmentationEnd().
2910 - If `pInfo->commandBuffer` is not null, you must submit that command buffer
2911   and make sure it finished execution before calling vmaDefragmentationEnd().
2912 */
2913 VkResult vmaDefragmentationBegin(
2914     VmaAllocator allocator,
2915     const VmaDefragmentationInfo2* pInfo,
2916     VmaDefragmentationStats* pStats,
2917     VmaDefragmentationContext *pContext);
2918 
2919 /** \brief Ends defragmentation process.
2920 
2921 Use this function to finish defragmentation started by vmaDefragmentationBegin().
2922 It is safe to pass `context == null`. The function then does nothing.
2923 */
2924 VkResult vmaDefragmentationEnd(
2925     VmaAllocator allocator,
2926     VmaDefragmentationContext context);
2927 
2928 /** \brief Deprecated. Compacts memory by moving allocations.
2929 
2930 @param pAllocations Array of allocations that can be moved during this compation.
2931 @param allocationCount Number of elements in pAllocations and pAllocationsChanged arrays.
2932 @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.
2933 @param pDefragmentationInfo Configuration parameters. Optional - pass null to use default values.
2934 @param[out] pDefragmentationStats Statistics returned by the function. Optional - pass null if you don't need this information.
2935 @return `VK_SUCCESS` if completed, negative error code in case of error.
2936 
2937 \deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
2938 
2939 This function works by moving allocations to different places (different
2940 `VkDeviceMemory` objects and/or different offsets) in order to optimize memory
2941 usage. Only allocations that are in `pAllocations` array can be moved. All other
2942 allocations are considered nonmovable in this call. Basic rules:
2943 
2944 - Only allocations made in memory types that have
2945   `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`
2946   flags can be compacted. You may pass other allocations but it makes no sense -
2947   these will never be moved.
2948 - Custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT or
2949   #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag are not defragmented. Allocations
2950   passed to this function that come from such pools are ignored.
2951 - Allocations created with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT or
2952   created as dedicated allocations for any other reason are also ignored.
2953 - Both allocations made with or without #VMA_ALLOCATION_CREATE_MAPPED_BIT
2954   flag can be compacted. If not persistently mapped, memory will be mapped
2955   temporarily inside this function if needed.
2956 - You must not pass same #VmaAllocation object multiple times in `pAllocations` array.
2957 
2958 The function also frees empty `VkDeviceMemory` blocks.
2959 
2960 Warning: This function may be time-consuming, so you shouldn't call it too often
2961 (like after every resource creation/destruction).
2962 You can call it on special occasions (like when reloading a game level or
2963 when you just destroyed a lot of objects). Calling it every frame may be OK, but
2964 you should measure that on your platform.
2965 
2966 For more information, see [Defragmentation](@ref defragmentation) chapter.
2967 */
2968 VkResult vmaDefragment(
2969     VmaAllocator allocator,
2970     VmaAllocation* pAllocations,
2971     size_t allocationCount,
2972     VkBool32* pAllocationsChanged,
2973     const VmaDefragmentationInfo *pDefragmentationInfo,
2974     VmaDefragmentationStats* pDefragmentationStats);
2975 
2976 /** \brief Binds buffer to allocation.
2977 
2978 Binds specified buffer to region of memory represented by specified allocation.
2979 Gets `VkDeviceMemory` handle and offset from the allocation.
2980 If you want to create a buffer, allocate memory for it and bind them together separately,
2981 you should use this function for binding instead of standard `vkBindBufferMemory()`,
2982 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
2983 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
2984 (which is illegal in Vulkan).
2985 
2986 It is recommended to use function vmaCreateBuffer() instead of this one.
2987 */
2988 VkResult vmaBindBufferMemory(
2989     VmaAllocator allocator,
2990     VmaAllocation allocation,
2991     VkBuffer buffer);
2992 
2993 /** \brief Binds image to allocation.
2994 
2995 Binds specified image to region of memory represented by specified allocation.
2996 Gets `VkDeviceMemory` handle and offset from the allocation.
2997 If you want to create an image, allocate memory for it and bind them together separately,
2998 you should use this function for binding instead of standard `vkBindImageMemory()`,
2999 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
3000 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
3001 (which is illegal in Vulkan).
3002 
3003 It is recommended to use function vmaCreateImage() instead of this one.
3004 */
3005 VkResult vmaBindImageMemory(
3006     VmaAllocator allocator,
3007     VmaAllocation allocation,
3008     VkImage image);
3009 
3010 /**
3011 @param[out] pBuffer Buffer that was created.
3012 @param[out] pAllocation Allocation that was created.
3013 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
3014 
3015 This function automatically:
3016 
3017 -# Creates buffer.
3018 -# Allocates appropriate memory for it.
3019 -# Binds the buffer with the memory.
3020 
3021 If any of these operations fail, buffer and allocation are not created,
3022 returned value is negative error code, *pBuffer and *pAllocation are null.
3023 
3024 If the function succeeded, you must destroy both buffer and allocation when you
3025 no longer need them using either convenience function vmaDestroyBuffer() or
3026 separately, using `vkDestroyBuffer()` and vmaFreeMemory().
3027 
3028 If VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used,
3029 VK_KHR_dedicated_allocation extension is used internally to query driver whether
3030 it requires or prefers the new buffer to have dedicated allocation. If yes,
3031 and if dedicated allocation is possible (VmaAllocationCreateInfo::pool is null
3032 and VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated
3033 allocation for this buffer, just like when using
3034 VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
3035 */
3036 VkResult vmaCreateBuffer(
3037     VmaAllocator allocator,
3038     const VkBufferCreateInfo* pBufferCreateInfo,
3039     const VmaAllocationCreateInfo* pAllocationCreateInfo,
3040     VkBuffer* pBuffer,
3041     VmaAllocation* pAllocation,
3042     VmaAllocationInfo* pAllocationInfo);
3043 
3044 /** \brief Destroys Vulkan buffer and frees allocated memory.
3045 
3046 This is just a convenience function equivalent to:
3047 
3048 \code
3049 vkDestroyBuffer(device, buffer, allocationCallbacks);
3050 vmaFreeMemory(allocator, allocation);
3051 \endcode
3052 
3053 It it safe to pass null as buffer and/or allocation.
3054 */
3055 void vmaDestroyBuffer(
3056     VmaAllocator allocator,
3057     VkBuffer buffer,
3058     VmaAllocation allocation);
3059 
3060 /// Function similar to vmaCreateBuffer().
3061 VkResult vmaCreateImage(
3062     VmaAllocator allocator,
3063     const VkImageCreateInfo* pImageCreateInfo,
3064     const VmaAllocationCreateInfo* pAllocationCreateInfo,
3065     VkImage* pImage,
3066     VmaAllocation* pAllocation,
3067     VmaAllocationInfo* pAllocationInfo);
3068 
3069 /** \brief Destroys Vulkan image and frees allocated memory.
3070 
3071 This is just a convenience function equivalent to:
3072 
3073 \code
3074 vkDestroyImage(device, image, allocationCallbacks);
3075 vmaFreeMemory(allocator, allocation);
3076 \endcode
3077 
3078 It it safe to pass null as image and/or allocation.
3079 */
3080 void vmaDestroyImage(
3081     VmaAllocator allocator,
3082     VkImage image,
3083     VmaAllocation allocation);
3084 
3085 #ifdef __cplusplus
3086 }
3087 #endif
3088 
3089 #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
3090 
3091 // For Visual Studio IntelliSense.
3092 #if defined(__cplusplus) && defined(__INTELLISENSE__)
3093 #define VMA_IMPLEMENTATION
3094 #endif
3095 
3096 #ifdef VMA_IMPLEMENTATION
3097 #undef VMA_IMPLEMENTATION
3098 
3099 #include <cstdint>
3100 #include <cstdlib>
3101 #include <cstring>
3102 
3103 /*******************************************************************************
3104 CONFIGURATION SECTION
3105 
3106 Define some of these macros before each #include of this header or change them
3107 here if you need other then default behavior depending on your environment.
3108 */
3109 
3110 /*
3111 Define this macro to 1 to make the library fetch pointers to Vulkan functions
3112 internally, like:
3113 
3114     vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
3115 
3116 Define to 0 if you are going to provide you own pointers to Vulkan functions via
3117 VmaAllocatorCreateInfo::pVulkanFunctions.
3118 */
3119 #if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
3120 #define VMA_STATIC_VULKAN_FUNCTIONS 1
3121 #endif
3122 
3123 // Define this macro to 1 to make the library use STL containers instead of its own implementation.
3124 //#define VMA_USE_STL_CONTAINERS 1
3125 
3126 /* Set this macro to 1 to make the library including and using STL containers:
3127 std::pair, std::vector, std::list, std::unordered_map.
3128 
3129 Set it to 0 or undefined to make the library using its own implementation of
3130 the containers.
3131 */
3132 #if VMA_USE_STL_CONTAINERS
3133    #define VMA_USE_STL_VECTOR 1
3134    #define VMA_USE_STL_UNORDERED_MAP 1
3135    #define VMA_USE_STL_LIST 1
3136 #endif
3137 
3138 #ifndef VMA_USE_STL_SHARED_MUTEX
3139     // Minimum Visual Studio 2015 Update 2
3140     #if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && NTDDI_VERSION > NTDDI_WIN10_RS2
3141         #define VMA_USE_STL_SHARED_MUTEX 1
3142     #endif
3143 #endif
3144 
3145 #if VMA_USE_STL_VECTOR
3146    #include <vector>
3147 #endif
3148 
3149 #if VMA_USE_STL_UNORDERED_MAP
3150    #include <unordered_map>
3151 #endif
3152 
3153 #if VMA_USE_STL_LIST
3154    #include <list>
3155 #endif
3156 
3157 /*
3158 Following headers are used in this CONFIGURATION section only, so feel free to
3159 remove them if not needed.
3160 */
3161 #include <cassert> // for assert
3162 #include <algorithm> // for min, max
3163 #include <mutex>
3164 #include <atomic> // for std::atomic
3165 
3166 #ifndef VMA_NULL
3167    // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
3168    #define VMA_NULL   nullptr
3169 #endif
3170 
3171 #if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
3172 #include <cstdlib>
aligned_alloc(size_t alignment,size_t size)3173 void *aligned_alloc(size_t alignment, size_t size)
3174 {
3175     // alignment must be >= sizeof(void*)
3176     if(alignment < sizeof(void*))
3177     {
3178         alignment = sizeof(void*);
3179     }
3180 
3181     return memalign(alignment, size);
3182 }
3183 #elif defined(__APPLE__) || defined(__ANDROID__)
3184 #  define ALIGNED_ALLOC_WITH_POSIX_MEMALIGN
3185 #elif defined(__GNU_LIBRARY__)
3186 #  if !defined(__GLIBC_PREREQ) || !__GLIBC_PREREQ(2, 16)
3187 // aligned_alloc() is defined in glibc only for version >= 2.16
3188 #    define ALIGNED_ALLOC_WITH_POSIX_MEMALIGN
3189 #  endif
3190 #endif
3191 
3192 #ifdef ALIGNED_ALLOC_WITH_POSIX_MEMALIGN
3193 #include <cstdlib>
aligned_alloc(size_t alignment,size_t size)3194 void *aligned_alloc(size_t alignment, size_t size)
3195 {
3196     // alignment must be >= sizeof(void*)
3197     if(alignment < sizeof(void*))
3198     {
3199         alignment = sizeof(void*);
3200     }
3201 
3202     void *pointer;
3203     if(posix_memalign(&pointer, alignment, size) == 0)
3204         return pointer;
3205     return VMA_NULL;
3206 }
3207 #endif
3208 
3209 // If your compiler is not compatible with C++11 and definition of
3210 // aligned_alloc() function is missing, uncommeting following line may help:
3211 
3212 //#include <malloc.h>
3213 
3214 // Normal assert to check for programmer's errors, especially in Debug configuration.
3215 #ifndef VMA_ASSERT
3216    #ifdef _DEBUG
3217        #define VMA_ASSERT(expr)         assert(expr)
3218    #else
3219        #define VMA_ASSERT(expr)
3220    #endif
3221 #endif
3222 
3223 // Assert that will be called very often, like inside data structures e.g. operator[].
3224 // Making it non-empty can make program slow.
3225 #ifndef VMA_HEAVY_ASSERT
3226    #ifdef _DEBUG
3227        #define VMA_HEAVY_ASSERT(expr)   //VMA_ASSERT(expr)
3228    #else
3229        #define VMA_HEAVY_ASSERT(expr)
3230    #endif
3231 #endif
3232 
3233 #ifndef VMA_ALIGN_OF
3234    #define VMA_ALIGN_OF(type)       (__alignof(type))
3235 #endif
3236 
3237 #ifndef VMA_SYSTEM_ALIGNED_MALLOC
3238    #if defined(_WIN32)
3239        #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment)   (_aligned_malloc((size), (alignment)))
3240    #else
3241        #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment)   (aligned_alloc((alignment), (size) ))
3242    #endif
3243 #endif
3244 
3245 #ifndef VMA_SYSTEM_FREE
3246    #if defined(_WIN32)
3247        #define VMA_SYSTEM_FREE(ptr)   _aligned_free(ptr)
3248    #else
3249        #define VMA_SYSTEM_FREE(ptr)   free(ptr)
3250    #endif
3251 #endif
3252 
3253 #ifndef VMA_MIN
3254    #define VMA_MIN(v1, v2)    (std::min((v1), (v2)))
3255 #endif
3256 
3257 #ifndef VMA_MAX
3258    #define VMA_MAX(v1, v2)    (std::max((v1), (v2)))
3259 #endif
3260 
3261 #ifndef VMA_SWAP
3262    #define VMA_SWAP(v1, v2)   std::swap((v1), (v2))
3263 #endif
3264 
3265 #ifndef VMA_SORT
3266    #define VMA_SORT(beg, end, cmp)  std::sort(beg, end, cmp)
3267 #endif
3268 
3269 #ifndef VMA_DEBUG_LOG
3270    #define VMA_DEBUG_LOG(format, ...)
3271    /*
3272    #define VMA_DEBUG_LOG(format, ...) do { \
3273        printf(format, __VA_ARGS__); \
3274        printf("\n"); \
3275    } while(false)
3276    */
3277 #endif
3278 
3279 // Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
3280 #if VMA_STATS_STRING_ENABLED
VmaUint32ToStr(char * outStr,size_t strLen,uint32_t num)3281     static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num)
3282     {
3283         snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
3284     }
VmaUint64ToStr(char * outStr,size_t strLen,uint64_t num)3285     static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
3286     {
3287         snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
3288     }
VmaPtrToStr(char * outStr,size_t strLen,const void * ptr)3289     static inline void VmaPtrToStr(char* outStr, size_t strLen, const void* ptr)
3290     {
3291         snprintf(outStr, strLen, "%p", ptr);
3292     }
3293 #endif
3294 
3295 #ifndef VMA_MUTEX
3296     class VmaMutex
3297     {
3298     public:
Lock()3299         void Lock() { m_Mutex.lock(); }
Unlock()3300         void Unlock() { m_Mutex.unlock(); }
3301     private:
3302         std::mutex m_Mutex;
3303     };
3304     #define VMA_MUTEX VmaMutex
3305 #endif
3306 
3307 // Read-write mutex, where "read" is shared access, "write" is exclusive access.
3308 #ifndef VMA_RW_MUTEX
3309     #if VMA_USE_STL_SHARED_MUTEX
3310         // Use std::shared_mutex from C++17.
3311         #include <shared_mutex>
3312         class VmaRWMutex
3313         {
3314         public:
LockRead()3315             void LockRead() { m_Mutex.lock_shared(); }
UnlockRead()3316             void UnlockRead() { m_Mutex.unlock_shared(); }
LockWrite()3317             void LockWrite() { m_Mutex.lock(); }
UnlockWrite()3318             void UnlockWrite() { m_Mutex.unlock(); }
3319         private:
3320             std::shared_mutex m_Mutex;
3321         };
3322         #define VMA_RW_MUTEX VmaRWMutex
3323     #elif defined(_WIN32)
3324         // Use SRWLOCK from WinAPI.
3325         class VmaRWMutex
3326         {
3327         public:
VmaRWMutex()3328             VmaRWMutex() { InitializeSRWLock(&m_Lock); }
LockRead()3329             void LockRead() { AcquireSRWLockShared(&m_Lock); }
UnlockRead()3330             void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
LockWrite()3331             void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
UnlockWrite()3332             void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
3333         private:
3334             SRWLOCK m_Lock;
3335         };
3336         #define VMA_RW_MUTEX VmaRWMutex
3337     #else
3338         // Less efficient fallback: Use normal mutex.
3339         class VmaRWMutex
3340         {
3341         public:
LockRead()3342             void LockRead() { m_Mutex.Lock(); }
UnlockRead()3343             void UnlockRead() { m_Mutex.Unlock(); }
LockWrite()3344             void LockWrite() { m_Mutex.Lock(); }
UnlockWrite()3345             void UnlockWrite() { m_Mutex.Unlock(); }
3346         private:
3347             VMA_MUTEX m_Mutex;
3348         };
3349         #define VMA_RW_MUTEX VmaRWMutex
3350     #endif // #if VMA_USE_STL_SHARED_MUTEX
3351 #endif // #ifndef VMA_RW_MUTEX
3352 
3353 /*
3354 If providing your own implementation, you need to implement a subset of std::atomic:
3355 
3356 - Constructor(uint32_t desired)
3357 - uint32_t load() const
3358 - void store(uint32_t desired)
3359 - bool compare_exchange_weak(uint32_t& expected, uint32_t desired)
3360 */
3361 #ifndef VMA_ATOMIC_UINT32
3362    #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
3363 #endif
3364 
3365 #ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
3366     /**
3367     Every allocation will have its own memory block.
3368     Define to 1 for debugging purposes only.
3369     */
3370     #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
3371 #endif
3372 
3373 #ifndef VMA_DEBUG_ALIGNMENT
3374     /**
3375     Minimum alignment of all allocations, in bytes.
3376     Set to more than 1 for debugging purposes only. Must be power of two.
3377     */
3378     #define VMA_DEBUG_ALIGNMENT (1)
3379 #endif
3380 
3381 #ifndef VMA_DEBUG_MARGIN
3382     /**
3383     Minimum margin before and after every allocation, in bytes.
3384     Set nonzero for debugging purposes only.
3385     */
3386     #define VMA_DEBUG_MARGIN (0)
3387 #endif
3388 
3389 #ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
3390     /**
3391     Define this macro to 1 to automatically fill new allocations and destroyed
3392     allocations with some bit pattern.
3393     */
3394     #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)
3395 #endif
3396 
3397 #ifndef VMA_DEBUG_DETECT_CORRUPTION
3398     /**
3399     Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to
3400     enable writing magic value to the margin before and after every allocation and
3401     validating it, so that memory corruptions (out-of-bounds writes) are detected.
3402     */
3403     #define VMA_DEBUG_DETECT_CORRUPTION (0)
3404 #endif
3405 
3406 #ifndef VMA_DEBUG_GLOBAL_MUTEX
3407     /**
3408     Set this to 1 for debugging purposes only, to enable single mutex protecting all
3409     entry calls to the library. Can be useful for debugging multithreading issues.
3410     */
3411     #define VMA_DEBUG_GLOBAL_MUTEX (0)
3412 #endif
3413 
3414 #ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
3415     /**
3416     Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.
3417     Set to more than 1 for debugging purposes only. Must be power of two.
3418     */
3419     #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
3420 #endif
3421 
3422 #ifndef VMA_SMALL_HEAP_MAX_SIZE
3423    /// Maximum size of a memory heap in Vulkan to consider it "small".
3424    #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
3425 #endif
3426 
3427 #ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
3428    /// Default size of a block allocated as single VkDeviceMemory from a "large" heap.
3429    #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
3430 #endif
3431 
3432 #ifndef VMA_CLASS_NO_COPY
3433     #define VMA_CLASS_NO_COPY(className) \
3434         private: \
3435             className(const className&) = delete; \
3436             className& operator=(const className&) = delete;
3437 #endif
3438 
3439 static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX;
3440 
3441 // Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.
3442 static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
3443 
3444 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED   = 0xDC;
3445 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
3446 
3447 /*******************************************************************************
3448 END OF CONFIGURATION
3449 */
3450 
3451 #if defined(__GNUC__)
3452 #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
3453 #pragma GCC diagnostic push
3454 #pragma GCC diagnostic ignored "-Wtype-limits"
3455 #pragma GCC diagnostic ignored "-Wunused-variable"
3456 #if defined(__clang__)
3457 #pragma clang diagnostic push
3458 #pragma clang diagnostic ignored "-Wtautological-compare"
3459 #endif
3460 #if GCC_VERSION >= 80000
3461 #pragma GCC diagnostic ignored "-Wclass-memaccess"
3462 #endif
3463 #if defined(ANDROID)
3464 #pragma GCC diagnostic ignored "-Wunused-private-field"
3465 #endif
3466 #endif
3467 static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
3468 
3469 static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
3470     VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
3471 
3472 // Returns number of bits set to 1 in (v).
VmaCountBitsSet(uint32_t v)3473 static inline uint32_t VmaCountBitsSet(uint32_t v)
3474 {
3475 	uint32_t c = v - ((v >> 1) & 0x55555555);
3476 	c = ((c >>  2) & 0x33333333) + (c & 0x33333333);
3477 	c = ((c >>  4) + c) & 0x0F0F0F0F;
3478 	c = ((c >>  8) + c) & 0x00FF00FF;
3479 	c = ((c >> 16) + c) & 0x0000FFFF;
3480 	return c;
3481 }
3482 
3483 // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
3484 // Use types like uint32_t, uint64_t as T.
3485 template <typename T>
VmaAlignUp(T val,T align)3486 static inline T VmaAlignUp(T val, T align)
3487 {
3488 	return (val + align - 1) / align * align;
3489 }
3490 // Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8.
3491 // Use types like uint32_t, uint64_t as T.
3492 template <typename T>
VmaAlignDown(T val,T align)3493 static inline T VmaAlignDown(T val, T align)
3494 {
3495     return val / align * align;
3496 }
3497 
3498 // Division with mathematical rounding to nearest number.
3499 template <typename T>
VmaRoundDiv(T x,T y)3500 static inline T VmaRoundDiv(T x, T y)
3501 {
3502 	return (x + (y / (T)2)) / y;
3503 }
3504 
3505 /*
3506 Returns true if given number is a power of two.
3507 T must be unsigned integer number or signed integer but always nonnegative.
3508 For 0 returns true.
3509 */
3510 template <typename T>
VmaIsPow2(T x)3511 inline bool VmaIsPow2(T x)
3512 {
3513     return (x & (x-1)) == 0;
3514 }
3515 
3516 // Returns smallest power of 2 greater or equal to v.
VmaNextPow2(uint32_t v)3517 static inline uint32_t VmaNextPow2(uint32_t v)
3518 {
3519 	v--;
3520     v |= v >> 1;
3521     v |= v >> 2;
3522     v |= v >> 4;
3523     v |= v >> 8;
3524     v |= v >> 16;
3525     v++;
3526     return v;
3527 }
VmaNextPow2(uint64_t v)3528 static inline uint64_t VmaNextPow2(uint64_t v)
3529 {
3530 	v--;
3531     v |= v >> 1;
3532     v |= v >> 2;
3533     v |= v >> 4;
3534     v |= v >> 8;
3535     v |= v >> 16;
3536     v |= v >> 32;
3537     v++;
3538     return v;
3539 }
3540 
3541 // Returns largest power of 2 less or equal to v.
VmaPrevPow2(uint32_t v)3542 static inline uint32_t VmaPrevPow2(uint32_t v)
3543 {
3544     v |= v >> 1;
3545     v |= v >> 2;
3546     v |= v >> 4;
3547     v |= v >> 8;
3548     v |= v >> 16;
3549     v = v ^ (v >> 1);
3550     return v;
3551 }
VmaPrevPow2(uint64_t v)3552 static inline uint64_t VmaPrevPow2(uint64_t v)
3553 {
3554     v |= v >> 1;
3555     v |= v >> 2;
3556     v |= v >> 4;
3557     v |= v >> 8;
3558     v |= v >> 16;
3559     v |= v >> 32;
3560     v = v ^ (v >> 1);
3561     return v;
3562 }
3563 
VmaStrIsEmpty(const char * pStr)3564 static inline bool VmaStrIsEmpty(const char* pStr)
3565 {
3566     return pStr == VMA_NULL || *pStr == '\0';
3567 }
3568 
VmaAlgorithmToStr(uint32_t algorithm)3569 static const char* VmaAlgorithmToStr(uint32_t algorithm)
3570 {
3571     switch(algorithm)
3572     {
3573     case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
3574         return "Linear";
3575     case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
3576         return "Buddy";
3577     case 0:
3578         return "Default";
3579     default:
3580         VMA_ASSERT(0);
3581         return "";
3582     }
3583 }
3584 
3585 #ifndef VMA_SORT
3586 
3587 template<typename Iterator, typename Compare>
VmaQuickSortPartition(Iterator beg,Iterator end,Compare cmp)3588 Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
3589 {
3590     Iterator centerValue = end; --centerValue;
3591     Iterator insertIndex = beg;
3592     for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
3593     {
3594         if(cmp(*memTypeIndex, *centerValue))
3595         {
3596             if(insertIndex != memTypeIndex)
3597             {
3598                 VMA_SWAP(*memTypeIndex, *insertIndex);
3599             }
3600             ++insertIndex;
3601         }
3602     }
3603     if(insertIndex != centerValue)
3604     {
3605         VMA_SWAP(*insertIndex, *centerValue);
3606     }
3607     return insertIndex;
3608 }
3609 
3610 template<typename Iterator, typename Compare>
VmaQuickSort(Iterator beg,Iterator end,Compare cmp)3611 void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
3612 {
3613     if(beg < end)
3614     {
3615         Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
3616         VmaQuickSort<Iterator, Compare>(beg, it, cmp);
3617         VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
3618     }
3619 }
3620 
3621 #define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
3622 
3623 #endif // #ifndef VMA_SORT
3624 
3625 /*
3626 Returns true if two memory blocks occupy overlapping pages.
3627 ResourceA must be in less memory offset than ResourceB.
3628 
3629 Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
3630 chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
3631 */
VmaBlocksOnSamePage(VkDeviceSize resourceAOffset,VkDeviceSize resourceASize,VkDeviceSize resourceBOffset,VkDeviceSize pageSize)3632 static inline bool VmaBlocksOnSamePage(
3633     VkDeviceSize resourceAOffset,
3634     VkDeviceSize resourceASize,
3635     VkDeviceSize resourceBOffset,
3636     VkDeviceSize pageSize)
3637 {
3638     VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
3639     VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
3640     VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
3641     VkDeviceSize resourceBStart = resourceBOffset;
3642     VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
3643     return resourceAEndPage == resourceBStartPage;
3644 }
3645 
3646 enum VmaSuballocationType
3647 {
3648     VMA_SUBALLOCATION_TYPE_FREE = 0,
3649     VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
3650     VMA_SUBALLOCATION_TYPE_BUFFER = 2,
3651     VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
3652     VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
3653     VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
3654     VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
3655 };
3656 
3657 /*
3658 Returns true if given suballocation types could conflict and must respect
3659 VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
3660 or linear image and another one is optimal image. If type is unknown, behave
3661 conservatively.
3662 */
VmaIsBufferImageGranularityConflict(VmaSuballocationType suballocType1,VmaSuballocationType suballocType2)3663 static inline bool VmaIsBufferImageGranularityConflict(
3664     VmaSuballocationType suballocType1,
3665     VmaSuballocationType suballocType2)
3666 {
3667     if(suballocType1 > suballocType2)
3668     {
3669         VMA_SWAP(suballocType1, suballocType2);
3670     }
3671 
3672     switch(suballocType1)
3673     {
3674     case VMA_SUBALLOCATION_TYPE_FREE:
3675         return false;
3676     case VMA_SUBALLOCATION_TYPE_UNKNOWN:
3677         return true;
3678     case VMA_SUBALLOCATION_TYPE_BUFFER:
3679         return
3680             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3681             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3682     case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
3683         return
3684             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3685             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
3686             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3687     case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
3688         return
3689             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3690     case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
3691         return false;
3692     default:
3693         VMA_ASSERT(0);
3694         return true;
3695     }
3696 }
3697 
VmaWriteMagicValue(void * pData,VkDeviceSize offset)3698 static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
3699 {
3700 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
3701     uint32_t* pDst = (uint32_t*)((char*)pData + offset);
3702     const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3703     for(size_t i = 0; i < numberCount; ++i, ++pDst)
3704     {
3705         *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
3706     }
3707 #else
3708     // no-op
3709 #endif
3710 }
3711 
VmaValidateMagicValue(const void * pData,VkDeviceSize offset)3712 static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
3713 {
3714 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
3715     const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
3716     const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3717     for(size_t i = 0; i < numberCount; ++i, ++pSrc)
3718     {
3719         if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
3720         {
3721             return false;
3722         }
3723     }
3724 #endif
3725     return true;
3726 }
3727 
3728 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
3729 struct VmaMutexLock
3730 {
VMA_CLASS_NO_COPYVmaMutexLock3731     VMA_CLASS_NO_COPY(VmaMutexLock)
3732 public:
3733     VmaMutexLock(VMA_MUTEX& mutex, bool useMutex) :
3734         m_pMutex(useMutex ? &mutex : VMA_NULL)
3735     { if(m_pMutex) { m_pMutex->Lock(); } }
~VmaMutexLockVmaMutexLock3736     ~VmaMutexLock()
3737     { if(m_pMutex) { m_pMutex->Unlock(); } }
3738 private:
3739     VMA_MUTEX* m_pMutex;
3740 };
3741 
3742 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
3743 struct VmaMutexLockRead
3744 {
VMA_CLASS_NO_COPYVmaMutexLockRead3745     VMA_CLASS_NO_COPY(VmaMutexLockRead)
3746 public:
3747     VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
3748         m_pMutex(useMutex ? &mutex : VMA_NULL)
3749     { if(m_pMutex) { m_pMutex->LockRead(); } }
~VmaMutexLockReadVmaMutexLockRead3750     ~VmaMutexLockRead() { if(m_pMutex) { m_pMutex->UnlockRead(); } }
3751 private:
3752     VMA_RW_MUTEX* m_pMutex;
3753 };
3754 
3755 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
3756 struct VmaMutexLockWrite
3757 {
VMA_CLASS_NO_COPYVmaMutexLockWrite3758     VMA_CLASS_NO_COPY(VmaMutexLockWrite)
3759 public:
3760     VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) :
3761         m_pMutex(useMutex ? &mutex : VMA_NULL)
3762     { if(m_pMutex) { m_pMutex->LockWrite(); } }
~VmaMutexLockWriteVmaMutexLockWrite3763     ~VmaMutexLockWrite() { if(m_pMutex) { m_pMutex->UnlockWrite(); } }
3764 private:
3765     VMA_RW_MUTEX* m_pMutex;
3766 };
3767 
3768 #if VMA_DEBUG_GLOBAL_MUTEX
3769     static VMA_MUTEX gDebugGlobalMutex;
3770     #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
3771 #else
3772     #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
3773 #endif
3774 
3775 // Minimum size of a free suballocation to register it in the free suballocation collection.
3776 static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
3777 
3778 /*
3779 Performs binary search and returns iterator to first element that is greater or
3780 equal to (key), according to comparison (cmp).
3781 
3782 Cmp should return true if first argument is less than second argument.
3783 
3784 Returned value is the found element, if present in the collection or place where
3785 new element with value (key) should be inserted.
3786 */
3787 template <typename CmpLess, typename IterT, typename KeyT>
VmaBinaryFindFirstNotLess(IterT beg,IterT end,const KeyT & key,CmpLess cmp)3788 static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, CmpLess cmp)
3789 {
3790     size_t down = 0, up = (end - beg);
3791     while(down < up)
3792     {
3793         const size_t mid = (down + up) / 2;
3794         if(cmp(*(beg+mid), key))
3795         {
3796             down = mid + 1;
3797         }
3798         else
3799         {
3800             up = mid;
3801         }
3802     }
3803     return beg + down;
3804 }
3805 
3806 /*
3807 Returns true if all pointers in the array are not-null and unique.
3808 Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
3809 T must be pointer type, e.g. VmaAllocation, VmaPool.
3810 */
3811 template<typename T>
VmaValidatePointerArray(uint32_t count,const T * arr)3812 static bool VmaValidatePointerArray(uint32_t count, const T* arr)
3813 {
3814     for(uint32_t i = 0; i < count; ++i)
3815     {
3816         const T iPtr = arr[i];
3817         if(iPtr == VMA_NULL)
3818         {
3819             return false;
3820         }
3821         for(uint32_t j = i + 1; j < count; ++j)
3822         {
3823             if(iPtr == arr[j])
3824             {
3825                 return false;
3826             }
3827         }
3828     }
3829     return true;
3830 }
3831 
3832 ////////////////////////////////////////////////////////////////////////////////
3833 // Memory allocation
3834 
VmaMalloc(const VkAllocationCallbacks * pAllocationCallbacks,size_t size,size_t alignment)3835 static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
3836 {
3837     if((pAllocationCallbacks != VMA_NULL) &&
3838         (pAllocationCallbacks->pfnAllocation != VMA_NULL))
3839     {
3840         return (*pAllocationCallbacks->pfnAllocation)(
3841             pAllocationCallbacks->pUserData,
3842             size,
3843             alignment,
3844             VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
3845     }
3846     else
3847     {
3848         return VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
3849     }
3850 }
3851 
VmaFree(const VkAllocationCallbacks * pAllocationCallbacks,void * ptr)3852 static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
3853 {
3854     if((pAllocationCallbacks != VMA_NULL) &&
3855         (pAllocationCallbacks->pfnFree != VMA_NULL))
3856     {
3857         (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
3858     }
3859     else
3860     {
3861         VMA_SYSTEM_FREE(ptr);
3862     }
3863 }
3864 
3865 template<typename T>
VmaAllocate(const VkAllocationCallbacks * pAllocationCallbacks)3866 static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
3867 {
3868     return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
3869 }
3870 
3871 template<typename T>
VmaAllocateArray(const VkAllocationCallbacks * pAllocationCallbacks,size_t count)3872 static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
3873 {
3874     return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
3875 }
3876 
3877 #define vma_new(allocator, type)   new(VmaAllocate<type>(allocator))(type)
3878 
3879 #define vma_new_array(allocator, type, count)   new(VmaAllocateArray<type>((allocator), (count)))(type)
3880 
3881 template<typename T>
vma_delete(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr)3882 static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
3883 {
3884     ptr->~T();
3885     VmaFree(pAllocationCallbacks, ptr);
3886 }
3887 
3888 template<typename T>
vma_delete_array(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr,size_t count)3889 static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
3890 {
3891     if(ptr != VMA_NULL)
3892     {
3893         for(size_t i = count; i--; )
3894         {
3895             ptr[i].~T();
3896         }
3897         VmaFree(pAllocationCallbacks, ptr);
3898     }
3899 }
3900 
3901 // STL-compatible allocator.
3902 template<typename T>
3903 class VmaStlAllocator
3904 {
3905 public:
3906     const VkAllocationCallbacks* const m_pCallbacks;
3907     typedef T value_type;
3908 
VmaStlAllocator(const VkAllocationCallbacks * pCallbacks)3909     VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
VmaStlAllocator(const VmaStlAllocator<U> & src)3910     template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
3911 
allocate(size_t n)3912     T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
deallocate(T * p,size_t n)3913     void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
3914 
3915     template<typename U>
3916     bool operator==(const VmaStlAllocator<U>& rhs) const
3917     {
3918         return m_pCallbacks == rhs.m_pCallbacks;
3919     }
3920     template<typename U>
3921     bool operator!=(const VmaStlAllocator<U>& rhs) const
3922     {
3923         return m_pCallbacks != rhs.m_pCallbacks;
3924     }
3925 
3926     VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
3927 };
3928 
3929 #if VMA_USE_STL_VECTOR
3930 
3931 #define VmaVector std::vector
3932 
3933 template<typename T, typename allocatorT>
VmaVectorInsert(std::vector<T,allocatorT> & vec,size_t index,const T & item)3934 static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
3935 {
3936     vec.insert(vec.begin() + index, item);
3937 }
3938 
3939 template<typename T, typename allocatorT>
VmaVectorRemove(std::vector<T,allocatorT> & vec,size_t index)3940 static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
3941 {
3942     vec.erase(vec.begin() + index);
3943 }
3944 
3945 #else // #if VMA_USE_STL_VECTOR
3946 
3947 /* Class with interface compatible with subset of std::vector.
3948 T must be POD because constructors and destructors are not called and memcpy is
3949 used for these objects. */
3950 template<typename T, typename AllocatorT>
3951 class VmaVector
3952 {
3953 public:
3954     typedef T value_type;
3955 
VmaVector(const AllocatorT & allocator)3956     VmaVector(const AllocatorT& allocator) :
3957         m_Allocator(allocator),
3958         m_pArray(VMA_NULL),
3959         m_Count(0),
3960         m_Capacity(0)
3961     {
3962     }
3963 
VmaVector(size_t count,const AllocatorT & allocator)3964     VmaVector(size_t count, const AllocatorT& allocator) :
3965         m_Allocator(allocator),
3966         m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
3967         m_Count(count),
3968         m_Capacity(count)
3969     {
3970     }
3971 
VmaVector(const VmaVector<T,AllocatorT> & src)3972     VmaVector(const VmaVector<T, AllocatorT>& src) :
3973         m_Allocator(src.m_Allocator),
3974         m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
3975         m_Count(src.m_Count),
3976         m_Capacity(src.m_Count)
3977     {
3978         if(m_Count != 0)
3979         {
3980             memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
3981         }
3982     }
3983 
~VmaVector()3984     ~VmaVector()
3985     {
3986         VmaFree(m_Allocator.m_pCallbacks, m_pArray);
3987     }
3988 
3989     VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
3990     {
3991         if(&rhs != this)
3992         {
3993             resize(rhs.m_Count);
3994             if(m_Count != 0)
3995             {
3996                 memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
3997             }
3998         }
3999         return *this;
4000     }
4001 
empty()4002     bool empty() const { return m_Count == 0; }
size()4003     size_t size() const { return m_Count; }
data()4004     T* data() { return m_pArray; }
data()4005     const T* data() const { return m_pArray; }
4006 
4007     T& operator[](size_t index)
4008     {
4009         VMA_HEAVY_ASSERT(index < m_Count);
4010         return m_pArray[index];
4011     }
4012     const T& operator[](size_t index) const
4013     {
4014         VMA_HEAVY_ASSERT(index < m_Count);
4015         return m_pArray[index];
4016     }
4017 
front()4018     T& front()
4019     {
4020         VMA_HEAVY_ASSERT(m_Count > 0);
4021         return m_pArray[0];
4022     }
front()4023     const T& front() const
4024     {
4025         VMA_HEAVY_ASSERT(m_Count > 0);
4026         return m_pArray[0];
4027     }
back()4028     T& back()
4029     {
4030         VMA_HEAVY_ASSERT(m_Count > 0);
4031         return m_pArray[m_Count - 1];
4032     }
back()4033     const T& back() const
4034     {
4035         VMA_HEAVY_ASSERT(m_Count > 0);
4036         return m_pArray[m_Count - 1];
4037     }
4038 
4039     void reserve(size_t newCapacity, bool freeMemory = false)
4040     {
4041         newCapacity = VMA_MAX(newCapacity, m_Count);
4042 
4043         if((newCapacity < m_Capacity) && !freeMemory)
4044         {
4045             newCapacity = m_Capacity;
4046         }
4047 
4048         if(newCapacity != m_Capacity)
4049         {
4050             T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
4051             if(m_Count != 0)
4052             {
4053                 memcpy(newArray, m_pArray, m_Count * sizeof(T));
4054             }
4055             VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4056             m_Capacity = newCapacity;
4057             m_pArray = newArray;
4058         }
4059     }
4060 
4061     void resize(size_t newCount, bool freeMemory = false)
4062     {
4063         size_t newCapacity = m_Capacity;
4064         if(newCount > m_Capacity)
4065         {
4066             newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
4067         }
4068         else if(freeMemory)
4069         {
4070             newCapacity = newCount;
4071         }
4072 
4073         if(newCapacity != m_Capacity)
4074         {
4075             T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
4076             const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
4077             if(elementsToCopy != 0)
4078             {
4079                 memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
4080             }
4081             VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4082             m_Capacity = newCapacity;
4083             m_pArray = newArray;
4084         }
4085 
4086         m_Count = newCount;
4087     }
4088 
4089     void clear(bool freeMemory = false)
4090     {
4091         resize(0, freeMemory);
4092     }
4093 
insert(size_t index,const T & src)4094     void insert(size_t index, const T& src)
4095     {
4096         VMA_HEAVY_ASSERT(index <= m_Count);
4097         const size_t oldCount = size();
4098         resize(oldCount + 1);
4099         if(index < oldCount)
4100         {
4101             memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
4102         }
4103         m_pArray[index] = src;
4104     }
4105 
remove(size_t index)4106     void remove(size_t index)
4107     {
4108         VMA_HEAVY_ASSERT(index < m_Count);
4109         const size_t oldCount = size();
4110         if(index < oldCount - 1)
4111         {
4112             memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
4113         }
4114         resize(oldCount - 1);
4115     }
4116 
push_back(const T & src)4117     void push_back(const T& src)
4118     {
4119         const size_t newIndex = size();
4120         resize(newIndex + 1);
4121         m_pArray[newIndex] = src;
4122     }
4123 
pop_back()4124     void pop_back()
4125     {
4126         VMA_HEAVY_ASSERT(m_Count > 0);
4127         resize(size() - 1);
4128     }
4129 
push_front(const T & src)4130     void push_front(const T& src)
4131     {
4132         insert(0, src);
4133     }
4134 
pop_front()4135     void pop_front()
4136     {
4137         VMA_HEAVY_ASSERT(m_Count > 0);
4138         remove(0);
4139     }
4140 
4141     typedef T* iterator;
4142 
begin()4143     iterator begin() { return m_pArray; }
end()4144     iterator end() { return m_pArray + m_Count; }
4145 
4146 private:
4147     AllocatorT m_Allocator;
4148     T* m_pArray;
4149     size_t m_Count;
4150     size_t m_Capacity;
4151 };
4152 
4153 template<typename T, typename allocatorT>
VmaVectorInsert(VmaVector<T,allocatorT> & vec,size_t index,const T & item)4154 static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
4155 {
4156     vec.insert(index, item);
4157 }
4158 
4159 template<typename T, typename allocatorT>
VmaVectorRemove(VmaVector<T,allocatorT> & vec,size_t index)4160 static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
4161 {
4162     vec.remove(index);
4163 }
4164 
4165 #endif // #if VMA_USE_STL_VECTOR
4166 
4167 template<typename CmpLess, typename VectorT>
VmaVectorInsertSorted(VectorT & vector,const typename VectorT::value_type & value)4168 size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
4169 {
4170     const size_t indexToInsert = VmaBinaryFindFirstNotLess(
4171         vector.data(),
4172         vector.data() + vector.size(),
4173         value,
4174         CmpLess()) - vector.data();
4175     VmaVectorInsert(vector, indexToInsert, value);
4176     return indexToInsert;
4177 }
4178 
4179 template<typename CmpLess, typename VectorT>
VmaVectorRemoveSorted(VectorT & vector,const typename VectorT::value_type & value)4180 bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
4181 {
4182     CmpLess comparator;
4183     typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
4184         vector.begin(),
4185         vector.end(),
4186         value,
4187         comparator);
4188     if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
4189     {
4190         size_t indexToRemove = it - vector.begin();
4191         VmaVectorRemove(vector, indexToRemove);
4192         return true;
4193     }
4194     return false;
4195 }
4196 
4197 template<typename CmpLess, typename IterT, typename KeyT>
VmaVectorFindSorted(const IterT & beg,const IterT & end,const KeyT & value)4198 IterT VmaVectorFindSorted(const IterT& beg, const IterT& end, const KeyT& value)
4199 {
4200     CmpLess comparator;
4201     IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
4202         beg, end, value, comparator);
4203     if(it == end ||
4204         (!comparator(*it, value) && !comparator(value, *it)))
4205     {
4206         return it;
4207     }
4208     return end;
4209 }
4210 
4211 ////////////////////////////////////////////////////////////////////////////////
4212 // class VmaPoolAllocator
4213 
4214 /*
4215 Allocator for objects of type T using a list of arrays (pools) to speed up
4216 allocation. Number of elements that can be allocated is not bounded because
4217 allocator can create multiple blocks.
4218 */
4219 template<typename T>
4220 class VmaPoolAllocator
4221 {
4222     VMA_CLASS_NO_COPY(VmaPoolAllocator)
4223 public:
4224     VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock);
4225     ~VmaPoolAllocator();
4226     void Clear();
4227     T* Alloc();
4228     void Free(T* ptr);
4229 
4230 private:
4231     union Item
4232     {
4233         uint32_t NextFreeIndex;
4234         T Value;
4235     };
4236 
4237     struct ItemBlock
4238     {
4239         Item* pItems;
4240         uint32_t FirstFreeIndex;
4241     };
4242 
4243     const VkAllocationCallbacks* m_pAllocationCallbacks;
4244     size_t m_ItemsPerBlock;
4245     VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
4246 
4247     ItemBlock& CreateNewBlock();
4248 };
4249 
4250 template<typename T>
VmaPoolAllocator(const VkAllocationCallbacks * pAllocationCallbacks,size_t itemsPerBlock)4251 VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock) :
4252     m_pAllocationCallbacks(pAllocationCallbacks),
4253     m_ItemsPerBlock(itemsPerBlock),
4254     m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
4255 {
4256     VMA_ASSERT(itemsPerBlock > 0);
4257 }
4258 
4259 template<typename T>
~VmaPoolAllocator()4260 VmaPoolAllocator<T>::~VmaPoolAllocator()
4261 {
4262     Clear();
4263 }
4264 
4265 template<typename T>
Clear()4266 void VmaPoolAllocator<T>::Clear()
4267 {
4268     for(size_t i = m_ItemBlocks.size(); i--; )
4269         vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemsPerBlock);
4270     m_ItemBlocks.clear();
4271 }
4272 
4273 template<typename T>
Alloc()4274 T* VmaPoolAllocator<T>::Alloc()
4275 {
4276     for(size_t i = m_ItemBlocks.size(); i--; )
4277     {
4278         ItemBlock& block = m_ItemBlocks[i];
4279         // This block has some free items: Use first one.
4280         if(block.FirstFreeIndex != UINT32_MAX)
4281         {
4282             Item* const pItem = &block.pItems[block.FirstFreeIndex];
4283             block.FirstFreeIndex = pItem->NextFreeIndex;
4284             return &pItem->Value;
4285         }
4286     }
4287 
4288     // No block has free item: Create new one and use it.
4289     ItemBlock& newBlock = CreateNewBlock();
4290     Item* const pItem = &newBlock.pItems[0];
4291     newBlock.FirstFreeIndex = pItem->NextFreeIndex;
4292     return &pItem->Value;
4293 }
4294 
4295 template<typename T>
Free(T * ptr)4296 void VmaPoolAllocator<T>::Free(T* ptr)
4297 {
4298     // Search all memory blocks to find ptr.
4299     for(size_t i = 0; i < m_ItemBlocks.size(); ++i)
4300     {
4301         ItemBlock& block = m_ItemBlocks[i];
4302 
4303         // Casting to union.
4304         Item* pItemPtr;
4305         memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
4306 
4307         // Check if pItemPtr is in address range of this block.
4308         if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + m_ItemsPerBlock))
4309         {
4310             const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
4311             pItemPtr->NextFreeIndex = block.FirstFreeIndex;
4312             block.FirstFreeIndex = index;
4313             return;
4314         }
4315     }
4316     VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
4317 }
4318 
4319 template<typename T>
CreateNewBlock()4320 typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
4321 {
4322     ItemBlock newBlock = {
4323         vma_new_array(m_pAllocationCallbacks, Item, m_ItemsPerBlock), 0 };
4324 
4325     m_ItemBlocks.push_back(newBlock);
4326 
4327     // Setup singly-linked list of all free items in this block.
4328     for(uint32_t i = 0; i < m_ItemsPerBlock - 1; ++i)
4329         newBlock.pItems[i].NextFreeIndex = i + 1;
4330     newBlock.pItems[m_ItemsPerBlock - 1].NextFreeIndex = UINT32_MAX;
4331     return m_ItemBlocks.back();
4332 }
4333 
4334 ////////////////////////////////////////////////////////////////////////////////
4335 // class VmaRawList, VmaList
4336 
4337 #if VMA_USE_STL_LIST
4338 
4339 #define VmaList std::list
4340 
4341 #else // #if VMA_USE_STL_LIST
4342 
4343 template<typename T>
4344 struct VmaListItem
4345 {
4346     VmaListItem* pPrev;
4347     VmaListItem* pNext;
4348     T Value;
4349 };
4350 
4351 // Doubly linked list.
4352 template<typename T>
4353 class VmaRawList
4354 {
4355     VMA_CLASS_NO_COPY(VmaRawList)
4356 public:
4357     typedef VmaListItem<T> ItemType;
4358 
4359     VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
4360     ~VmaRawList();
4361     void Clear();
4362 
GetCount()4363     size_t GetCount() const { return m_Count; }
IsEmpty()4364     bool IsEmpty() const { return m_Count == 0; }
4365 
Front()4366     ItemType* Front() { return m_pFront; }
Front()4367     const ItemType* Front() const { return m_pFront; }
Back()4368     ItemType* Back() { return m_pBack; }
Back()4369     const ItemType* Back() const { return m_pBack; }
4370 
4371     ItemType* PushBack();
4372     ItemType* PushFront();
4373     ItemType* PushBack(const T& value);
4374     ItemType* PushFront(const T& value);
4375     void PopBack();
4376     void PopFront();
4377 
4378     // Item can be null - it means PushBack.
4379     ItemType* InsertBefore(ItemType* pItem);
4380     // Item can be null - it means PushFront.
4381     ItemType* InsertAfter(ItemType* pItem);
4382 
4383     ItemType* InsertBefore(ItemType* pItem, const T& value);
4384     ItemType* InsertAfter(ItemType* pItem, const T& value);
4385 
4386     void Remove(ItemType* pItem);
4387 
4388 private:
4389     const VkAllocationCallbacks* const m_pAllocationCallbacks;
4390     VmaPoolAllocator<ItemType> m_ItemAllocator;
4391     ItemType* m_pFront;
4392     ItemType* m_pBack;
4393     size_t m_Count;
4394 };
4395 
4396 template<typename T>
VmaRawList(const VkAllocationCallbacks * pAllocationCallbacks)4397 VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
4398     m_pAllocationCallbacks(pAllocationCallbacks),
4399     m_ItemAllocator(pAllocationCallbacks, 128),
4400     m_pFront(VMA_NULL),
4401     m_pBack(VMA_NULL),
4402     m_Count(0)
4403 {
4404 }
4405 
4406 template<typename T>
~VmaRawList()4407 VmaRawList<T>::~VmaRawList()
4408 {
4409     // Intentionally not calling Clear, because that would be unnecessary
4410     // computations to return all items to m_ItemAllocator as free.
4411 }
4412 
4413 template<typename T>
Clear()4414 void VmaRawList<T>::Clear()
4415 {
4416     if(IsEmpty() == false)
4417     {
4418         ItemType* pItem = m_pBack;
4419         while(pItem != VMA_NULL)
4420         {
4421             ItemType* const pPrevItem = pItem->pPrev;
4422             m_ItemAllocator.Free(pItem);
4423             pItem = pPrevItem;
4424         }
4425         m_pFront = VMA_NULL;
4426         m_pBack = VMA_NULL;
4427         m_Count = 0;
4428     }
4429 }
4430 
4431 template<typename T>
PushBack()4432 VmaListItem<T>* VmaRawList<T>::PushBack()
4433 {
4434     ItemType* const pNewItem = m_ItemAllocator.Alloc();
4435     pNewItem->pNext = VMA_NULL;
4436     if(IsEmpty())
4437     {
4438         pNewItem->pPrev = VMA_NULL;
4439         m_pFront = pNewItem;
4440         m_pBack = pNewItem;
4441         m_Count = 1;
4442     }
4443     else
4444     {
4445         pNewItem->pPrev = m_pBack;
4446         m_pBack->pNext = pNewItem;
4447         m_pBack = pNewItem;
4448         ++m_Count;
4449     }
4450     return pNewItem;
4451 }
4452 
4453 template<typename T>
PushFront()4454 VmaListItem<T>* VmaRawList<T>::PushFront()
4455 {
4456     ItemType* const pNewItem = m_ItemAllocator.Alloc();
4457     pNewItem->pPrev = VMA_NULL;
4458     if(IsEmpty())
4459     {
4460         pNewItem->pNext = VMA_NULL;
4461         m_pFront = pNewItem;
4462         m_pBack = pNewItem;
4463         m_Count = 1;
4464     }
4465     else
4466     {
4467         pNewItem->pNext = m_pFront;
4468         m_pFront->pPrev = pNewItem;
4469         m_pFront = pNewItem;
4470         ++m_Count;
4471     }
4472     return pNewItem;
4473 }
4474 
4475 template<typename T>
PushBack(const T & value)4476 VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
4477 {
4478     ItemType* const pNewItem = PushBack();
4479     pNewItem->Value = value;
4480     return pNewItem;
4481 }
4482 
4483 template<typename T>
PushFront(const T & value)4484 VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
4485 {
4486     ItemType* const pNewItem = PushFront();
4487     pNewItem->Value = value;
4488     return pNewItem;
4489 }
4490 
4491 template<typename T>
PopBack()4492 void VmaRawList<T>::PopBack()
4493 {
4494     VMA_HEAVY_ASSERT(m_Count > 0);
4495     ItemType* const pBackItem = m_pBack;
4496     ItemType* const pPrevItem = pBackItem->pPrev;
4497     if(pPrevItem != VMA_NULL)
4498     {
4499         pPrevItem->pNext = VMA_NULL;
4500     }
4501     m_pBack = pPrevItem;
4502     m_ItemAllocator.Free(pBackItem);
4503     --m_Count;
4504 }
4505 
4506 template<typename T>
PopFront()4507 void VmaRawList<T>::PopFront()
4508 {
4509     VMA_HEAVY_ASSERT(m_Count > 0);
4510     ItemType* const pFrontItem = m_pFront;
4511     ItemType* const pNextItem = pFrontItem->pNext;
4512     if(pNextItem != VMA_NULL)
4513     {
4514         pNextItem->pPrev = VMA_NULL;
4515     }
4516     m_pFront = pNextItem;
4517     m_ItemAllocator.Free(pFrontItem);
4518     --m_Count;
4519 }
4520 
4521 template<typename T>
Remove(ItemType * pItem)4522 void VmaRawList<T>::Remove(ItemType* pItem)
4523 {
4524     VMA_HEAVY_ASSERT(pItem != VMA_NULL);
4525     VMA_HEAVY_ASSERT(m_Count > 0);
4526 
4527     if(pItem->pPrev != VMA_NULL)
4528     {
4529         pItem->pPrev->pNext = pItem->pNext;
4530     }
4531     else
4532     {
4533         VMA_HEAVY_ASSERT(m_pFront == pItem);
4534         m_pFront = pItem->pNext;
4535     }
4536 
4537     if(pItem->pNext != VMA_NULL)
4538     {
4539         pItem->pNext->pPrev = pItem->pPrev;
4540     }
4541     else
4542     {
4543         VMA_HEAVY_ASSERT(m_pBack == pItem);
4544         m_pBack = pItem->pPrev;
4545     }
4546 
4547     m_ItemAllocator.Free(pItem);
4548     --m_Count;
4549 }
4550 
4551 template<typename T>
InsertBefore(ItemType * pItem)4552 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
4553 {
4554     if(pItem != VMA_NULL)
4555     {
4556         ItemType* const prevItem = pItem->pPrev;
4557         ItemType* const newItem = m_ItemAllocator.Alloc();
4558         newItem->pPrev = prevItem;
4559         newItem->pNext = pItem;
4560         pItem->pPrev = newItem;
4561         if(prevItem != VMA_NULL)
4562         {
4563             prevItem->pNext = newItem;
4564         }
4565         else
4566         {
4567             VMA_HEAVY_ASSERT(m_pFront == pItem);
4568             m_pFront = newItem;
4569         }
4570         ++m_Count;
4571         return newItem;
4572     }
4573     else
4574         return PushBack();
4575 }
4576 
4577 template<typename T>
InsertAfter(ItemType * pItem)4578 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
4579 {
4580     if(pItem != VMA_NULL)
4581     {
4582         ItemType* const nextItem = pItem->pNext;
4583         ItemType* const newItem = m_ItemAllocator.Alloc();
4584         newItem->pNext = nextItem;
4585         newItem->pPrev = pItem;
4586         pItem->pNext = newItem;
4587         if(nextItem != VMA_NULL)
4588         {
4589             nextItem->pPrev = newItem;
4590         }
4591         else
4592         {
4593             VMA_HEAVY_ASSERT(m_pBack == pItem);
4594             m_pBack = newItem;
4595         }
4596         ++m_Count;
4597         return newItem;
4598     }
4599     else
4600         return PushFront();
4601 }
4602 
4603 template<typename T>
InsertBefore(ItemType * pItem,const T & value)4604 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
4605 {
4606     ItemType* const newItem = InsertBefore(pItem);
4607     newItem->Value = value;
4608     return newItem;
4609 }
4610 
4611 template<typename T>
InsertAfter(ItemType * pItem,const T & value)4612 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
4613 {
4614     ItemType* const newItem = InsertAfter(pItem);
4615     newItem->Value = value;
4616     return newItem;
4617 }
4618 
4619 template<typename T, typename AllocatorT>
4620 class VmaList
4621 {
VMA_CLASS_NO_COPY(VmaList)4622     VMA_CLASS_NO_COPY(VmaList)
4623 public:
4624     class iterator
4625     {
4626     public:
4627         iterator() :
4628             m_pList(VMA_NULL),
4629             m_pItem(VMA_NULL)
4630         {
4631         }
4632 
4633         T& operator*() const
4634         {
4635             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4636             return m_pItem->Value;
4637         }
4638         T* operator->() const
4639         {
4640             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4641             return &m_pItem->Value;
4642         }
4643 
4644         iterator& operator++()
4645         {
4646             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4647             m_pItem = m_pItem->pNext;
4648             return *this;
4649         }
4650         iterator& operator--()
4651         {
4652             if(m_pItem != VMA_NULL)
4653             {
4654                 m_pItem = m_pItem->pPrev;
4655             }
4656             else
4657             {
4658                 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4659                 m_pItem = m_pList->Back();
4660             }
4661             return *this;
4662         }
4663 
4664         iterator operator++(int)
4665         {
4666             iterator result = *this;
4667             ++*this;
4668             return result;
4669         }
4670         iterator operator--(int)
4671         {
4672             iterator result = *this;
4673             --*this;
4674             return result;
4675         }
4676 
4677         bool operator==(const iterator& rhs) const
4678         {
4679             VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4680             return m_pItem == rhs.m_pItem;
4681         }
4682         bool operator!=(const iterator& rhs) const
4683         {
4684             VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4685             return m_pItem != rhs.m_pItem;
4686         }
4687 
4688     private:
4689         VmaRawList<T>* m_pList;
4690         VmaListItem<T>* m_pItem;
4691 
4692         iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
4693             m_pList(pList),
4694             m_pItem(pItem)
4695         {
4696         }
4697 
4698         friend class VmaList<T, AllocatorT>;
4699     };
4700 
4701     class const_iterator
4702     {
4703     public:
const_iterator()4704         const_iterator() :
4705             m_pList(VMA_NULL),
4706             m_pItem(VMA_NULL)
4707         {
4708         }
4709 
const_iterator(const iterator & src)4710         const_iterator(const iterator& src) :
4711             m_pList(src.m_pList),
4712             m_pItem(src.m_pItem)
4713         {
4714         }
4715 
4716         const T& operator*() const
4717         {
4718             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4719             return m_pItem->Value;
4720         }
4721         const T* operator->() const
4722         {
4723             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4724             return &m_pItem->Value;
4725         }
4726 
4727         const_iterator& operator++()
4728         {
4729             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4730             m_pItem = m_pItem->pNext;
4731             return *this;
4732         }
4733         const_iterator& operator--()
4734         {
4735             if(m_pItem != VMA_NULL)
4736             {
4737                 m_pItem = m_pItem->pPrev;
4738             }
4739             else
4740             {
4741                 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4742                 m_pItem = m_pList->Back();
4743             }
4744             return *this;
4745         }
4746 
4747         const_iterator operator++(int)
4748         {
4749             const_iterator result = *this;
4750             ++*this;
4751             return result;
4752         }
4753         const_iterator operator--(int)
4754         {
4755             const_iterator result = *this;
4756             --*this;
4757             return result;
4758         }
4759 
4760         bool operator==(const const_iterator& rhs) const
4761         {
4762             VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4763             return m_pItem == rhs.m_pItem;
4764         }
4765         bool operator!=(const const_iterator& rhs) const
4766         {
4767             VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4768             return m_pItem != rhs.m_pItem;
4769         }
4770 
4771     private:
const_iterator(const VmaRawList<T> * pList,const VmaListItem<T> * pItem)4772         const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
4773             m_pList(pList),
4774             m_pItem(pItem)
4775         {
4776         }
4777 
4778         const VmaRawList<T>* m_pList;
4779         const VmaListItem<T>* m_pItem;
4780 
4781         friend class VmaList<T, AllocatorT>;
4782     };
4783 
VmaList(const AllocatorT & allocator)4784     VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
4785 
empty()4786     bool empty() const { return m_RawList.IsEmpty(); }
size()4787     size_t size() const { return m_RawList.GetCount(); }
4788 
begin()4789     iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
end()4790     iterator end() { return iterator(&m_RawList, VMA_NULL); }
4791 
cbegin()4792     const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
cend()4793     const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
4794 
clear()4795     void clear() { m_RawList.Clear(); }
push_back(const T & value)4796     void push_back(const T& value) { m_RawList.PushBack(value); }
erase(iterator it)4797     void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
insert(iterator it,const T & value)4798     iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
4799 
4800 private:
4801     VmaRawList<T> m_RawList;
4802 };
4803 
4804 #endif // #if VMA_USE_STL_LIST
4805 
4806 ////////////////////////////////////////////////////////////////////////////////
4807 // class VmaMap
4808 
4809 // Unused in this version.
4810 #if 0
4811 
4812 #if VMA_USE_STL_UNORDERED_MAP
4813 
4814 #define VmaPair std::pair
4815 
4816 #define VMA_MAP_TYPE(KeyT, ValueT) \
4817     std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
4818 
4819 #else // #if VMA_USE_STL_UNORDERED_MAP
4820 
4821 template<typename T1, typename T2>
4822 struct VmaPair
4823 {
4824     T1 first;
4825     T2 second;
4826 
4827     VmaPair() : first(), second() { }
4828     VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
4829 };
4830 
4831 /* Class compatible with subset of interface of std::unordered_map.
4832 KeyT, ValueT must be POD because they will be stored in VmaVector.
4833 */
4834 template<typename KeyT, typename ValueT>
4835 class VmaMap
4836 {
4837 public:
4838     typedef VmaPair<KeyT, ValueT> PairType;
4839     typedef PairType* iterator;
4840 
4841     VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
4842 
4843     iterator begin() { return m_Vector.begin(); }
4844     iterator end() { return m_Vector.end(); }
4845 
4846     void insert(const PairType& pair);
4847     iterator find(const KeyT& key);
4848     void erase(iterator it);
4849 
4850 private:
4851     VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
4852 };
4853 
4854 #define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
4855 
4856 template<typename FirstT, typename SecondT>
4857 struct VmaPairFirstLess
4858 {
4859     bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
4860     {
4861         return lhs.first < rhs.first;
4862     }
4863     bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
4864     {
4865         return lhs.first < rhsFirst;
4866     }
4867 };
4868 
4869 template<typename KeyT, typename ValueT>
4870 void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
4871 {
4872     const size_t indexToInsert = VmaBinaryFindFirstNotLess(
4873         m_Vector.data(),
4874         m_Vector.data() + m_Vector.size(),
4875         pair,
4876         VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
4877     VmaVectorInsert(m_Vector, indexToInsert, pair);
4878 }
4879 
4880 template<typename KeyT, typename ValueT>
4881 VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
4882 {
4883     PairType* it = VmaBinaryFindFirstNotLess(
4884         m_Vector.data(),
4885         m_Vector.data() + m_Vector.size(),
4886         key,
4887         VmaPairFirstLess<KeyT, ValueT>());
4888     if((it != m_Vector.end()) && (it->first == key))
4889     {
4890         return it;
4891     }
4892     else
4893     {
4894         return m_Vector.end();
4895     }
4896 }
4897 
4898 template<typename KeyT, typename ValueT>
4899 void VmaMap<KeyT, ValueT>::erase(iterator it)
4900 {
4901     VmaVectorRemove(m_Vector, it - m_Vector.begin());
4902 }
4903 
4904 #endif // #if VMA_USE_STL_UNORDERED_MAP
4905 
4906 #endif // #if 0
4907 
4908 ////////////////////////////////////////////////////////////////////////////////
4909 
4910 class VmaDeviceMemoryBlock;
4911 
4912 enum VMA_CACHE_OPERATION { VMA_CACHE_FLUSH, VMA_CACHE_INVALIDATE };
4913 
4914 struct VmaAllocation_T
4915 {
4916     VMA_CLASS_NO_COPY(VmaAllocation_T)
4917 private:
4918     static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
4919 
4920     enum FLAGS
4921     {
4922         FLAG_USER_DATA_STRING = 0x01,
4923     };
4924 
4925 public:
4926     enum ALLOCATION_TYPE
4927     {
4928         ALLOCATION_TYPE_NONE,
4929         ALLOCATION_TYPE_BLOCK,
4930         ALLOCATION_TYPE_DEDICATED,
4931     };
4932 
VmaAllocation_TVmaAllocation_T4933     VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
4934         m_Alignment(1),
4935         m_Size(0),
4936         m_pUserData(VMA_NULL),
4937         m_LastUseFrameIndex(currentFrameIndex),
4938         m_Type((uint8_t)ALLOCATION_TYPE_NONE),
4939         m_SuballocationType((uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN),
4940         m_MapCount(0),
4941         m_Flags(userDataString ? (uint8_t)FLAG_USER_DATA_STRING : 0)
4942     {
4943 #if VMA_STATS_STRING_ENABLED
4944         m_CreationFrameIndex = currentFrameIndex;
4945         m_BufferImageUsage = 0;
4946 #endif
4947     }
4948 
~VmaAllocation_TVmaAllocation_T4949     ~VmaAllocation_T()
4950     {
4951         VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
4952 
4953         // Check if owned string was freed.
4954         VMA_ASSERT(m_pUserData == VMA_NULL);
4955     }
4956 
InitBlockAllocationVmaAllocation_T4957     void InitBlockAllocation(
4958         VmaPool hPool,
4959         VmaDeviceMemoryBlock* block,
4960         VkDeviceSize offset,
4961         VkDeviceSize alignment,
4962         VkDeviceSize size,
4963         VmaSuballocationType suballocationType,
4964         bool mapped,
4965         bool canBecomeLost)
4966     {
4967         VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4968         VMA_ASSERT(block != VMA_NULL);
4969         m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
4970         m_Alignment = alignment;
4971         m_Size = size;
4972         m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
4973         m_SuballocationType = (uint8_t)suballocationType;
4974         m_BlockAllocation.m_hPool = hPool;
4975         m_BlockAllocation.m_Block = block;
4976         m_BlockAllocation.m_Offset = offset;
4977         m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
4978     }
4979 
InitLostVmaAllocation_T4980     void InitLost()
4981     {
4982         VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4983         VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
4984         m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
4985         m_BlockAllocation.m_hPool = VK_NULL_HANDLE;
4986         m_BlockAllocation.m_Block = VMA_NULL;
4987         m_BlockAllocation.m_Offset = 0;
4988         m_BlockAllocation.m_CanBecomeLost = true;
4989     }
4990 
4991     void ChangeBlockAllocation(
4992         VmaAllocator hAllocator,
4993         VmaDeviceMemoryBlock* block,
4994         VkDeviceSize offset);
4995 
4996     void ChangeSize(VkDeviceSize newSize);
4997     void ChangeOffset(VkDeviceSize newOffset);
4998 
4999     // pMappedData not null means allocation is created with MAPPED flag.
InitDedicatedAllocationVmaAllocation_T5000     void InitDedicatedAllocation(
5001         uint32_t memoryTypeIndex,
5002         VkDeviceMemory hMemory,
5003         VmaSuballocationType suballocationType,
5004         void* pMappedData,
5005         VkDeviceSize size)
5006     {
5007         VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
5008         VMA_ASSERT(hMemory != VK_NULL_HANDLE);
5009         m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
5010         m_Alignment = 0;
5011         m_Size = size;
5012         m_SuballocationType = (uint8_t)suballocationType;
5013         m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
5014         m_DedicatedAllocation.m_MemoryTypeIndex = memoryTypeIndex;
5015         m_DedicatedAllocation.m_hMemory = hMemory;
5016         m_DedicatedAllocation.m_pMappedData = pMappedData;
5017     }
5018 
GetTypeVmaAllocation_T5019     ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
GetAlignmentVmaAllocation_T5020     VkDeviceSize GetAlignment() const { return m_Alignment; }
GetSizeVmaAllocation_T5021     VkDeviceSize GetSize() const { return m_Size; }
IsUserDataStringVmaAllocation_T5022     bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
GetUserDataVmaAllocation_T5023     void* GetUserData() const { return m_pUserData; }
5024     void SetUserData(VmaAllocator hAllocator, void* pUserData);
GetSuballocationTypeVmaAllocation_T5025     VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
5026 
GetBlockVmaAllocation_T5027     VmaDeviceMemoryBlock* GetBlock() const
5028     {
5029         VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
5030         return m_BlockAllocation.m_Block;
5031     }
5032     VkDeviceSize GetOffset() const;
5033     VkDeviceMemory GetMemory() const;
5034     uint32_t GetMemoryTypeIndex() const;
IsPersistentMapVmaAllocation_T5035     bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
5036     void* GetMappedData() const;
5037     bool CanBecomeLost() const;
5038     VmaPool GetPool() const;
5039 
GetLastUseFrameIndexVmaAllocation_T5040     uint32_t GetLastUseFrameIndex() const
5041     {
5042         return m_LastUseFrameIndex.load();
5043     }
CompareExchangeLastUseFrameIndexVmaAllocation_T5044     bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
5045     {
5046         return m_LastUseFrameIndex.compare_exchange_weak(expected, desired);
5047     }
5048     /*
5049     - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
5050       makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
5051     - Else, returns false.
5052 
5053     If hAllocation is already lost, assert - you should not call it then.
5054     If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
5055     */
5056     bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5057 
DedicatedAllocCalcStatsInfoVmaAllocation_T5058     void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
5059     {
5060         VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
5061         outInfo.blockCount = 1;
5062         outInfo.allocationCount = 1;
5063         outInfo.unusedRangeCount = 0;
5064         outInfo.usedBytes = m_Size;
5065         outInfo.unusedBytes = 0;
5066         outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
5067         outInfo.unusedRangeSizeMin = UINT64_MAX;
5068         outInfo.unusedRangeSizeMax = 0;
5069     }
5070 
5071     void BlockAllocMap();
5072     void BlockAllocUnmap();
5073     VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
5074     void DedicatedAllocUnmap(VmaAllocator hAllocator);
5075 
5076 #if VMA_STATS_STRING_ENABLED
GetCreationFrameIndexVmaAllocation_T5077     uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; }
GetBufferImageUsageVmaAllocation_T5078     uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
5079 
InitBufferImageUsageVmaAllocation_T5080     void InitBufferImageUsage(uint32_t bufferImageUsage)
5081     {
5082         VMA_ASSERT(m_BufferImageUsage == 0);
5083         m_BufferImageUsage = bufferImageUsage;
5084     }
5085 
5086     void PrintParameters(class VmaJsonWriter& json) const;
5087 #endif
5088 
5089 private:
5090     VkDeviceSize m_Alignment;
5091     VkDeviceSize m_Size;
5092     void* m_pUserData;
5093     VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
5094     uint8_t m_Type; // ALLOCATION_TYPE
5095     uint8_t m_SuballocationType; // VmaSuballocationType
5096     // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
5097     // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
5098     uint8_t m_MapCount;
5099     uint8_t m_Flags; // enum FLAGS
5100 
5101     // Allocation out of VmaDeviceMemoryBlock.
5102     struct BlockAllocation
5103     {
5104         VmaPool m_hPool; // Null if belongs to general memory.
5105         VmaDeviceMemoryBlock* m_Block;
5106         VkDeviceSize m_Offset;
5107         bool m_CanBecomeLost;
5108     };
5109 
5110     // Allocation for an object that has its own private VkDeviceMemory.
5111     struct DedicatedAllocation
5112     {
5113         uint32_t m_MemoryTypeIndex;
5114         VkDeviceMemory m_hMemory;
5115         void* m_pMappedData; // Not null means memory is mapped.
5116     };
5117 
5118     union
5119     {
5120         // Allocation out of VmaDeviceMemoryBlock.
5121         BlockAllocation m_BlockAllocation;
5122         // Allocation for an object that has its own private VkDeviceMemory.
5123         DedicatedAllocation m_DedicatedAllocation;
5124     };
5125 
5126 #if VMA_STATS_STRING_ENABLED
5127     uint32_t m_CreationFrameIndex;
5128     uint32_t m_BufferImageUsage; // 0 if unknown.
5129 #endif
5130 
5131     void FreeUserDataString(VmaAllocator hAllocator);
5132 };
5133 
5134 /*
5135 Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
5136 allocated memory block or free.
5137 */
5138 struct VmaSuballocation
5139 {
5140     VkDeviceSize offset;
5141     VkDeviceSize size;
5142     VmaAllocation hAllocation;
5143     VmaSuballocationType type;
5144 };
5145 
5146 // Comparator for offsets.
5147 struct VmaSuballocationOffsetLess
5148 {
operatorVmaSuballocationOffsetLess5149     bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
5150     {
5151         return lhs.offset < rhs.offset;
5152     }
5153 };
5154 struct VmaSuballocationOffsetGreater
5155 {
operatorVmaSuballocationOffsetGreater5156     bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
5157     {
5158         return lhs.offset > rhs.offset;
5159     }
5160 };
5161 
5162 typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
5163 
5164 // Cost of one additional allocation lost, as equivalent in bytes.
5165 static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
5166 
5167 /*
5168 Parameters of planned allocation inside a VmaDeviceMemoryBlock.
5169 
5170 If canMakeOtherLost was false:
5171 - item points to a FREE suballocation.
5172 - itemsToMakeLostCount is 0.
5173 
5174 If canMakeOtherLost was true:
5175 - item points to first of sequence of suballocations, which are either FREE,
5176   or point to VmaAllocations that can become lost.
5177 - itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
5178   the requested allocation to succeed.
5179 */
5180 struct VmaAllocationRequest
5181 {
5182     VkDeviceSize offset;
5183     VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
5184     VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
5185     VmaSuballocationList::iterator item;
5186     size_t itemsToMakeLostCount;
5187     void* customData;
5188 
CalcCostVmaAllocationRequest5189     VkDeviceSize CalcCost() const
5190     {
5191         return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
5192     }
5193 };
5194 
5195 /*
5196 Data structure used for bookkeeping of allocations and unused ranges of memory
5197 in a single VkDeviceMemory block.
5198 */
5199 class VmaBlockMetadata
5200 {
5201 public:
5202     VmaBlockMetadata(VmaAllocator hAllocator);
~VmaBlockMetadata()5203     virtual ~VmaBlockMetadata() { }
Init(VkDeviceSize size)5204     virtual void Init(VkDeviceSize size) { m_Size = size; }
5205 
5206     // Validates all data structures inside this object. If not valid, returns false.
5207     virtual bool Validate() const = 0;
GetSize()5208     VkDeviceSize GetSize() const { return m_Size; }
5209     virtual size_t GetAllocationCount() const = 0;
5210     virtual VkDeviceSize GetSumFreeSize() const = 0;
5211     virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0;
5212     // Returns true if this block is empty - contains only single free suballocation.
5213     virtual bool IsEmpty() const = 0;
5214 
5215     virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
5216     // Shouldn't modify blockCount.
5217     virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
5218 
5219 #if VMA_STATS_STRING_ENABLED
5220     virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
5221 #endif
5222 
5223     // Tries to find a place for suballocation with given parameters inside this block.
5224     // If succeeded, fills pAllocationRequest and returns true.
5225     // If failed, returns false.
5226     virtual bool CreateAllocationRequest(
5227         uint32_t currentFrameIndex,
5228         uint32_t frameInUseCount,
5229         VkDeviceSize bufferImageGranularity,
5230         VkDeviceSize allocSize,
5231         VkDeviceSize allocAlignment,
5232         bool upperAddress,
5233         VmaSuballocationType allocType,
5234         bool canMakeOtherLost,
5235         // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
5236         uint32_t strategy,
5237         VmaAllocationRequest* pAllocationRequest) = 0;
5238 
5239     virtual bool MakeRequestedAllocationsLost(
5240         uint32_t currentFrameIndex,
5241         uint32_t frameInUseCount,
5242         VmaAllocationRequest* pAllocationRequest) = 0;
5243 
5244     virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0;
5245 
5246     virtual VkResult CheckCorruption(const void* pBlockData) = 0;
5247 
5248     // Makes actual allocation based on request. Request must already be checked and valid.
5249     virtual void Alloc(
5250         const VmaAllocationRequest& request,
5251         VmaSuballocationType type,
5252         VkDeviceSize allocSize,
5253         bool upperAddress,
5254         VmaAllocation hAllocation) = 0;
5255 
5256     // Frees suballocation assigned to given memory region.
5257     virtual void Free(const VmaAllocation allocation) = 0;
5258     virtual void FreeAtOffset(VkDeviceSize offset) = 0;
5259 
5260     // Tries to resize (grow or shrink) space for given allocation, in place.
ResizeAllocation(const VmaAllocation alloc,VkDeviceSize newSize)5261     virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize) { return false; }
5262 
5263 protected:
GetAllocationCallbacks()5264     const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
5265 
5266 #if VMA_STATS_STRING_ENABLED
5267     void PrintDetailedMap_Begin(class VmaJsonWriter& json,
5268         VkDeviceSize unusedBytes,
5269         size_t allocationCount,
5270         size_t unusedRangeCount) const;
5271     void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
5272         VkDeviceSize offset,
5273         VmaAllocation hAllocation) const;
5274     void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
5275         VkDeviceSize offset,
5276         VkDeviceSize size) const;
5277     void PrintDetailedMap_End(class VmaJsonWriter& json) const;
5278 #endif
5279 
5280 private:
5281     VkDeviceSize m_Size;
5282     const VkAllocationCallbacks* m_pAllocationCallbacks;
5283 };
5284 
5285 #define VMA_VALIDATE(cond) do { if(!(cond)) { \
5286         VMA_ASSERT(0 && "Validation failed: " #cond); \
5287         return false; \
5288     } } while(false)
5289 
5290 class VmaBlockMetadata_Generic : public VmaBlockMetadata
5291 {
5292     VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
5293 public:
5294     VmaBlockMetadata_Generic(VmaAllocator hAllocator);
5295     virtual ~VmaBlockMetadata_Generic();
5296     virtual void Init(VkDeviceSize size);
5297 
5298     virtual bool Validate() const;
GetAllocationCount()5299     virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
GetSumFreeSize()5300     virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
5301     virtual VkDeviceSize GetUnusedRangeSizeMax() const;
5302     virtual bool IsEmpty() const;
5303 
5304     virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5305     virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5306 
5307 #if VMA_STATS_STRING_ENABLED
5308     virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5309 #endif
5310 
5311     virtual bool CreateAllocationRequest(
5312         uint32_t currentFrameIndex,
5313         uint32_t frameInUseCount,
5314         VkDeviceSize bufferImageGranularity,
5315         VkDeviceSize allocSize,
5316         VkDeviceSize allocAlignment,
5317         bool upperAddress,
5318         VmaSuballocationType allocType,
5319         bool canMakeOtherLost,
5320         uint32_t strategy,
5321         VmaAllocationRequest* pAllocationRequest);
5322 
5323     virtual bool MakeRequestedAllocationsLost(
5324         uint32_t currentFrameIndex,
5325         uint32_t frameInUseCount,
5326         VmaAllocationRequest* pAllocationRequest);
5327 
5328     virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5329 
5330     virtual VkResult CheckCorruption(const void* pBlockData);
5331 
5332     virtual void Alloc(
5333         const VmaAllocationRequest& request,
5334         VmaSuballocationType type,
5335         VkDeviceSize allocSize,
5336         bool upperAddress,
5337         VmaAllocation hAllocation);
5338 
5339     virtual void Free(const VmaAllocation allocation);
5340     virtual void FreeAtOffset(VkDeviceSize offset);
5341 
5342     virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize);
5343 
5344     ////////////////////////////////////////////////////////////////////////////////
5345     // For defragmentation
5346 
5347     bool IsBufferImageGranularityConflictPossible(
5348         VkDeviceSize bufferImageGranularity,
5349         VmaSuballocationType& inOutPrevSuballocType) const;
5350 
5351 private:
5352     friend class VmaDefragmentationAlgorithm_Generic;
5353     friend class VmaDefragmentationAlgorithm_Fast;
5354 
5355     uint32_t m_FreeCount;
5356     VkDeviceSize m_SumFreeSize;
5357     VmaSuballocationList m_Suballocations;
5358     // Suballocations that are free and have size greater than certain threshold.
5359     // Sorted by size, ascending.
5360     VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
5361 
5362     bool ValidateFreeSuballocationList() const;
5363 
5364     // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
5365     // If yes, fills pOffset and returns true. If no, returns false.
5366     bool CheckAllocation(
5367         uint32_t currentFrameIndex,
5368         uint32_t frameInUseCount,
5369         VkDeviceSize bufferImageGranularity,
5370         VkDeviceSize allocSize,
5371         VkDeviceSize allocAlignment,
5372         VmaSuballocationType allocType,
5373         VmaSuballocationList::const_iterator suballocItem,
5374         bool canMakeOtherLost,
5375         VkDeviceSize* pOffset,
5376         size_t* itemsToMakeLostCount,
5377         VkDeviceSize* pSumFreeSize,
5378         VkDeviceSize* pSumItemSize) const;
5379     // Given free suballocation, it merges it with following one, which must also be free.
5380     void MergeFreeWithNext(VmaSuballocationList::iterator item);
5381     // Releases given suballocation, making it free.
5382     // Merges it with adjacent free suballocations if applicable.
5383     // Returns iterator to new free suballocation at this place.
5384     VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
5385     // Given free suballocation, it inserts it into sorted list of
5386     // m_FreeSuballocationsBySize if it's suitable.
5387     void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
5388     // Given free suballocation, it removes it from sorted list of
5389     // m_FreeSuballocationsBySize if it's suitable.
5390     void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
5391 };
5392 
5393 /*
5394 Allocations and their references in internal data structure look like this:
5395 
5396 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
5397 
5398         0 +-------+
5399           |       |
5400           |       |
5401           |       |
5402           +-------+
5403           | Alloc |  1st[m_1stNullItemsBeginCount]
5404           +-------+
5405           | Alloc |  1st[m_1stNullItemsBeginCount + 1]
5406           +-------+
5407           |  ...  |
5408           +-------+
5409           | Alloc |  1st[1st.size() - 1]
5410           +-------+
5411           |       |
5412           |       |
5413           |       |
5414 GetSize() +-------+
5415 
5416 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
5417 
5418         0 +-------+
5419           | Alloc |  2nd[0]
5420           +-------+
5421           | Alloc |  2nd[1]
5422           +-------+
5423           |  ...  |
5424           +-------+
5425           | Alloc |  2nd[2nd.size() - 1]
5426           +-------+
5427           |       |
5428           |       |
5429           |       |
5430           +-------+
5431           | Alloc |  1st[m_1stNullItemsBeginCount]
5432           +-------+
5433           | Alloc |  1st[m_1stNullItemsBeginCount + 1]
5434           +-------+
5435           |  ...  |
5436           +-------+
5437           | Alloc |  1st[1st.size() - 1]
5438           +-------+
5439           |       |
5440 GetSize() +-------+
5441 
5442 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
5443 
5444         0 +-------+
5445           |       |
5446           |       |
5447           |       |
5448           +-------+
5449           | Alloc |  1st[m_1stNullItemsBeginCount]
5450           +-------+
5451           | Alloc |  1st[m_1stNullItemsBeginCount + 1]
5452           +-------+
5453           |  ...  |
5454           +-------+
5455           | Alloc |  1st[1st.size() - 1]
5456           +-------+
5457           |       |
5458           |       |
5459           |       |
5460           +-------+
5461           | Alloc |  2nd[2nd.size() - 1]
5462           +-------+
5463           |  ...  |
5464           +-------+
5465           | Alloc |  2nd[1]
5466           +-------+
5467           | Alloc |  2nd[0]
5468 GetSize() +-------+
5469 
5470 */
5471 class VmaBlockMetadata_Linear : public VmaBlockMetadata
5472 {
5473     VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
5474 public:
5475     VmaBlockMetadata_Linear(VmaAllocator hAllocator);
5476     virtual ~VmaBlockMetadata_Linear();
5477     virtual void Init(VkDeviceSize size);
5478 
5479     virtual bool Validate() const;
5480     virtual size_t GetAllocationCount() const;
GetSumFreeSize()5481     virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
5482     virtual VkDeviceSize GetUnusedRangeSizeMax() const;
IsEmpty()5483     virtual bool IsEmpty() const { return GetAllocationCount() == 0; }
5484 
5485     virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5486     virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5487 
5488 #if VMA_STATS_STRING_ENABLED
5489     virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5490 #endif
5491 
5492     virtual bool CreateAllocationRequest(
5493         uint32_t currentFrameIndex,
5494         uint32_t frameInUseCount,
5495         VkDeviceSize bufferImageGranularity,
5496         VkDeviceSize allocSize,
5497         VkDeviceSize allocAlignment,
5498         bool upperAddress,
5499         VmaSuballocationType allocType,
5500         bool canMakeOtherLost,
5501         uint32_t strategy,
5502         VmaAllocationRequest* pAllocationRequest);
5503 
5504     virtual bool MakeRequestedAllocationsLost(
5505         uint32_t currentFrameIndex,
5506         uint32_t frameInUseCount,
5507         VmaAllocationRequest* pAllocationRequest);
5508 
5509     virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5510 
5511     virtual VkResult CheckCorruption(const void* pBlockData);
5512 
5513     virtual void Alloc(
5514         const VmaAllocationRequest& request,
5515         VmaSuballocationType type,
5516         VkDeviceSize allocSize,
5517         bool upperAddress,
5518         VmaAllocation hAllocation);
5519 
5520     virtual void Free(const VmaAllocation allocation);
5521     virtual void FreeAtOffset(VkDeviceSize offset);
5522 
5523 private:
5524     /*
5525     There are two suballocation vectors, used in ping-pong way.
5526     The one with index m_1stVectorIndex is called 1st.
5527     The one with index (m_1stVectorIndex ^ 1) is called 2nd.
5528     2nd can be non-empty only when 1st is not empty.
5529     When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
5530     */
5531     typedef VmaVector< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > SuballocationVectorType;
5532 
5533     enum SECOND_VECTOR_MODE
5534     {
5535         SECOND_VECTOR_EMPTY,
5536         /*
5537         Suballocations in 2nd vector are created later than the ones in 1st, but they
5538         all have smaller offset.
5539         */
5540         SECOND_VECTOR_RING_BUFFER,
5541         /*
5542         Suballocations in 2nd vector are upper side of double stack.
5543         They all have offsets higher than those in 1st vector.
5544         Top of this stack means smaller offsets, but higher indices in this vector.
5545         */
5546         SECOND_VECTOR_DOUBLE_STACK,
5547     };
5548 
5549     VkDeviceSize m_SumFreeSize;
5550     SuballocationVectorType m_Suballocations0, m_Suballocations1;
5551     uint32_t m_1stVectorIndex;
5552     SECOND_VECTOR_MODE m_2ndVectorMode;
5553 
AccessSuballocations1st()5554     SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
AccessSuballocations2nd()5555     SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
AccessSuballocations1st()5556     const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
AccessSuballocations2nd()5557     const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
5558 
5559     // Number of items in 1st vector with hAllocation = null at the beginning.
5560     size_t m_1stNullItemsBeginCount;
5561     // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
5562     size_t m_1stNullItemsMiddleCount;
5563     // Number of items in 2nd vector with hAllocation = null.
5564     size_t m_2ndNullItemsCount;
5565 
5566     bool ShouldCompact1st() const;
5567     void CleanupAfterFree();
5568 };
5569 
5570 /*
5571 - GetSize() is the original size of allocated memory block.
5572 - m_UsableSize is this size aligned down to a power of two.
5573   All allocations and calculations happen relative to m_UsableSize.
5574 - GetUnusableSize() is the difference between them.
5575   It is repoted as separate, unused range, not available for allocations.
5576 
5577 Node at level 0 has size = m_UsableSize.
5578 Each next level contains nodes with size 2 times smaller than current level.
5579 m_LevelCount is the maximum number of levels to use in the current object.
5580 */
5581 class VmaBlockMetadata_Buddy : public VmaBlockMetadata
5582 {
5583     VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
5584 public:
5585     VmaBlockMetadata_Buddy(VmaAllocator hAllocator);
5586     virtual ~VmaBlockMetadata_Buddy();
5587     virtual void Init(VkDeviceSize size);
5588 
5589     virtual bool Validate() const;
GetAllocationCount()5590     virtual size_t GetAllocationCount() const { return m_AllocationCount; }
GetSumFreeSize()5591     virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); }
5592     virtual VkDeviceSize GetUnusedRangeSizeMax() const;
IsEmpty()5593     virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; }
5594 
5595     virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5596     virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5597 
5598 #if VMA_STATS_STRING_ENABLED
5599     virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5600 #endif
5601 
5602     virtual bool CreateAllocationRequest(
5603         uint32_t currentFrameIndex,
5604         uint32_t frameInUseCount,
5605         VkDeviceSize bufferImageGranularity,
5606         VkDeviceSize allocSize,
5607         VkDeviceSize allocAlignment,
5608         bool upperAddress,
5609         VmaSuballocationType allocType,
5610         bool canMakeOtherLost,
5611         uint32_t strategy,
5612         VmaAllocationRequest* pAllocationRequest);
5613 
5614     virtual bool MakeRequestedAllocationsLost(
5615         uint32_t currentFrameIndex,
5616         uint32_t frameInUseCount,
5617         VmaAllocationRequest* pAllocationRequest);
5618 
5619     virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5620 
CheckCorruption(const void * pBlockData)5621     virtual VkResult CheckCorruption(const void* pBlockData) { return VK_ERROR_FEATURE_NOT_PRESENT; }
5622 
5623     virtual void Alloc(
5624         const VmaAllocationRequest& request,
5625         VmaSuballocationType type,
5626         VkDeviceSize allocSize,
5627         bool upperAddress,
5628         VmaAllocation hAllocation);
5629 
Free(const VmaAllocation allocation)5630     virtual void Free(const VmaAllocation allocation) { FreeAtOffset(allocation, allocation->GetOffset()); }
FreeAtOffset(VkDeviceSize offset)5631     virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VMA_NULL, offset); }
5632 
5633 private:
5634     static const VkDeviceSize MIN_NODE_SIZE = 32;
5635     static const size_t MAX_LEVELS = 30;
5636 
5637     struct ValidationContext
5638     {
5639         size_t calculatedAllocationCount;
5640         size_t calculatedFreeCount;
5641         VkDeviceSize calculatedSumFreeSize;
5642 
ValidationContextValidationContext5643         ValidationContext() :
5644             calculatedAllocationCount(0),
5645             calculatedFreeCount(0),
5646             calculatedSumFreeSize(0) { }
5647     };
5648 
5649     struct Node
5650     {
5651         VkDeviceSize offset;
5652         enum TYPE
5653         {
5654             TYPE_FREE,
5655             TYPE_ALLOCATION,
5656             TYPE_SPLIT,
5657             TYPE_COUNT
5658         } type;
5659         Node* parent;
5660         Node* buddy;
5661 
5662         union
5663         {
5664             struct
5665             {
5666                 Node* prev;
5667                 Node* next;
5668             } free;
5669             struct
5670             {
5671                 VmaAllocation alloc;
5672             } allocation;
5673             struct
5674             {
5675                 Node* leftChild;
5676             } split;
5677         };
5678     };
5679 
5680     // Size of the memory block aligned down to a power of two.
5681     VkDeviceSize m_UsableSize;
5682     uint32_t m_LevelCount;
5683 
5684     Node* m_Root;
5685     struct {
5686         Node* front;
5687         Node* back;
5688     } m_FreeList[MAX_LEVELS];
5689     // Number of nodes in the tree with type == TYPE_ALLOCATION.
5690     size_t m_AllocationCount;
5691     // Number of nodes in the tree with type == TYPE_FREE.
5692     size_t m_FreeCount;
5693     // This includes space wasted due to internal fragmentation. Doesn't include unusable size.
5694     VkDeviceSize m_SumFreeSize;
5695 
GetUnusableSize()5696     VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
5697     void DeleteNode(Node* node);
5698     bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
5699     uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
LevelToNodeSize(uint32_t level)5700     inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
5701     // Alloc passed just for validation. Can be null.
5702     void FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset);
5703     void CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const;
5704     // Adds node to the front of FreeList at given level.
5705     // node->type must be FREE.
5706     // node->free.prev, next can be undefined.
5707     void AddToFreeListFront(uint32_t level, Node* node);
5708     // Removes node from FreeList at given level.
5709     // node->type must be FREE.
5710     // node->free.prev, next stay untouched.
5711     void RemoveFromFreeList(uint32_t level, Node* node);
5712 
5713 #if VMA_STATS_STRING_ENABLED
5714     void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
5715 #endif
5716 };
5717 
5718 /*
5719 Represents a single block of device memory (`VkDeviceMemory`) with all the
5720 data about its regions (aka suballocations, #VmaAllocation), assigned and free.
5721 
5722 Thread-safety: This class must be externally synchronized.
5723 */
5724 class VmaDeviceMemoryBlock
5725 {
5726     VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
5727 public:
5728     VmaBlockMetadata* m_pMetadata;
5729 
5730     VmaDeviceMemoryBlock(VmaAllocator hAllocator);
5731 
~VmaDeviceMemoryBlock()5732     ~VmaDeviceMemoryBlock()
5733     {
5734         VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
5735         VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
5736     }
5737 
5738     // Always call after construction.
5739     void Init(
5740         VmaAllocator hAllocator,
5741         uint32_t newMemoryTypeIndex,
5742         VkDeviceMemory newMemory,
5743         VkDeviceSize newSize,
5744         uint32_t id,
5745         uint32_t algorithm);
5746     // Always call before destruction.
5747     void Destroy(VmaAllocator allocator);
5748 
GetDeviceMemory()5749     VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
GetMemoryTypeIndex()5750     uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetId()5751     uint32_t GetId() const { return m_Id; }
GetMappedData()5752     void* GetMappedData() const { return m_pMappedData; }
5753 
5754     // Validates all data structures inside this object. If not valid, returns false.
5755     bool Validate() const;
5756 
5757     VkResult CheckCorruption(VmaAllocator hAllocator);
5758 
5759     // ppData can be null.
5760     VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
5761     void Unmap(VmaAllocator hAllocator, uint32_t count);
5762 
5763     VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5764     VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5765 
5766     VkResult BindBufferMemory(
5767         const VmaAllocator hAllocator,
5768         const VmaAllocation hAllocation,
5769         VkBuffer hBuffer);
5770     VkResult BindImageMemory(
5771         const VmaAllocator hAllocator,
5772         const VmaAllocation hAllocation,
5773         VkImage hImage);
5774 
5775 private:
5776     uint32_t m_MemoryTypeIndex;
5777     uint32_t m_Id;
5778     VkDeviceMemory m_hMemory;
5779 
5780     /*
5781     Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
5782     Also protects m_MapCount, m_pMappedData.
5783     Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
5784     */
5785     VMA_MUTEX m_Mutex;
5786     uint32_t m_MapCount;
5787     void* m_pMappedData;
5788 };
5789 
5790 struct VmaPointerLess
5791 {
operatorVmaPointerLess5792     bool operator()(const void* lhs, const void* rhs) const
5793     {
5794         return lhs < rhs;
5795     }
5796 };
5797 
5798 struct VmaDefragmentationMove
5799 {
5800     size_t srcBlockIndex;
5801     size_t dstBlockIndex;
5802     VkDeviceSize srcOffset;
5803     VkDeviceSize dstOffset;
5804     VkDeviceSize size;
5805 };
5806 
5807 class VmaDefragmentationAlgorithm;
5808 
5809 /*
5810 Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
5811 Vulkan memory type.
5812 
5813 Synchronized internally with a mutex.
5814 */
5815 struct VmaBlockVector
5816 {
5817     VMA_CLASS_NO_COPY(VmaBlockVector)
5818 public:
5819     VmaBlockVector(
5820         VmaAllocator hAllocator,
5821         uint32_t memoryTypeIndex,
5822         VkDeviceSize preferredBlockSize,
5823         size_t minBlockCount,
5824         size_t maxBlockCount,
5825         VkDeviceSize bufferImageGranularity,
5826         uint32_t frameInUseCount,
5827         bool isCustomPool,
5828         bool explicitBlockSize,
5829         uint32_t algorithm);
5830     ~VmaBlockVector();
5831 
5832     VkResult CreateMinBlocks();
5833 
GetMemoryTypeIndexVmaBlockVector5834     uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetPreferredBlockSizeVmaBlockVector5835     VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
GetBufferImageGranularityVmaBlockVector5836     VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
GetFrameInUseCountVmaBlockVector5837     uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
GetAlgorithmVmaBlockVector5838     uint32_t GetAlgorithm() const { return m_Algorithm; }
5839 
5840     void GetPoolStats(VmaPoolStats* pStats);
5841 
IsEmptyVmaBlockVector5842     bool IsEmpty() const { return m_Blocks.empty(); }
5843     bool IsCorruptionDetectionEnabled() const;
5844 
5845     VkResult Allocate(
5846         VmaPool hCurrentPool,
5847         uint32_t currentFrameIndex,
5848         VkDeviceSize size,
5849         VkDeviceSize alignment,
5850         const VmaAllocationCreateInfo& createInfo,
5851         VmaSuballocationType suballocType,
5852         size_t allocationCount,
5853         VmaAllocation* pAllocations);
5854 
5855     void Free(
5856         VmaAllocation hAllocation);
5857 
5858     // Adds statistics of this BlockVector to pStats.
5859     void AddStats(VmaStats* pStats);
5860 
5861 #if VMA_STATS_STRING_ENABLED
5862     void PrintDetailedMap(class VmaJsonWriter& json);
5863 #endif
5864 
5865     void MakePoolAllocationsLost(
5866         uint32_t currentFrameIndex,
5867         size_t* pLostAllocationCount);
5868     VkResult CheckCorruption();
5869 
5870     // Saves results in pCtx->res.
5871     void Defragment(
5872         class VmaBlockVectorDefragmentationContext* pCtx,
5873         VmaDefragmentationStats* pStats,
5874         VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
5875         VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
5876         VkCommandBuffer commandBuffer);
5877     void DefragmentationEnd(
5878         class VmaBlockVectorDefragmentationContext* pCtx,
5879         VmaDefragmentationStats* pStats);
5880 
5881     ////////////////////////////////////////////////////////////////////////////////
5882     // To be used only while the m_Mutex is locked. Used during defragmentation.
5883 
GetBlockCountVmaBlockVector5884     size_t GetBlockCount() const { return m_Blocks.size(); }
GetBlockVmaBlockVector5885     VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
5886     size_t CalcAllocationCount() const;
5887     bool IsBufferImageGranularityConflictPossible() const;
5888 
5889 private:
5890     friend class VmaDefragmentationAlgorithm_Generic;
5891 
5892     const VmaAllocator m_hAllocator;
5893     const uint32_t m_MemoryTypeIndex;
5894     const VkDeviceSize m_PreferredBlockSize;
5895     const size_t m_MinBlockCount;
5896     const size_t m_MaxBlockCount;
5897     const VkDeviceSize m_BufferImageGranularity;
5898     const uint32_t m_FrameInUseCount;
5899     const bool m_IsCustomPool;
5900     const bool m_ExplicitBlockSize;
5901     const uint32_t m_Algorithm;
5902     /* There can be at most one allocation that is completely empty - a
5903     hysteresis to avoid pessimistic case of alternating creation and destruction
5904     of a VkDeviceMemory. */
5905     bool m_HasEmptyBlock;
5906     VMA_RW_MUTEX m_Mutex;
5907     // Incrementally sorted by sumFreeSize, ascending.
5908     VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
5909     uint32_t m_NextBlockId;
5910 
5911     VkDeviceSize CalcMaxBlockSize() const;
5912 
5913     // Finds and removes given block from vector.
5914     void Remove(VmaDeviceMemoryBlock* pBlock);
5915 
5916     // Performs single step in sorting m_Blocks. They may not be fully sorted
5917     // after this call.
5918     void IncrementallySortBlocks();
5919 
5920     VkResult AllocatePage(
5921         VmaPool hCurrentPool,
5922         uint32_t currentFrameIndex,
5923         VkDeviceSize size,
5924         VkDeviceSize alignment,
5925         const VmaAllocationCreateInfo& createInfo,
5926         VmaSuballocationType suballocType,
5927         VmaAllocation* pAllocation);
5928 
5929     // To be used only without CAN_MAKE_OTHER_LOST flag.
5930     VkResult AllocateFromBlock(
5931         VmaDeviceMemoryBlock* pBlock,
5932         VmaPool hCurrentPool,
5933         uint32_t currentFrameIndex,
5934         VkDeviceSize size,
5935         VkDeviceSize alignment,
5936         VmaAllocationCreateFlags allocFlags,
5937         void* pUserData,
5938         VmaSuballocationType suballocType,
5939         uint32_t strategy,
5940         VmaAllocation* pAllocation);
5941 
5942     VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
5943 
5944     // Saves result to pCtx->res.
5945     void ApplyDefragmentationMovesCpu(
5946         class VmaBlockVectorDefragmentationContext* pDefragCtx,
5947         const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
5948     // Saves result to pCtx->res.
5949     void ApplyDefragmentationMovesGpu(
5950         class VmaBlockVectorDefragmentationContext* pDefragCtx,
5951         const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
5952         VkCommandBuffer commandBuffer);
5953 
5954     /*
5955     Used during defragmentation. pDefragmentationStats is optional. It's in/out
5956     - updated with new data.
5957     */
5958     void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
5959 };
5960 
5961 struct VmaPool_T
5962 {
5963     VMA_CLASS_NO_COPY(VmaPool_T)
5964 public:
5965     VmaBlockVector m_BlockVector;
5966 
5967     VmaPool_T(
5968         VmaAllocator hAllocator,
5969         const VmaPoolCreateInfo& createInfo,
5970         VkDeviceSize preferredBlockSize);
5971     ~VmaPool_T();
5972 
GetIdVmaPool_T5973     uint32_t GetId() const { return m_Id; }
SetIdVmaPool_T5974     void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
5975 
5976 #if VMA_STATS_STRING_ENABLED
5977     //void PrintDetailedMap(class VmaStringBuilder& sb);
5978 #endif
5979 
5980 private:
5981     uint32_t m_Id;
5982 };
5983 
5984 /*
5985 Performs defragmentation:
5986 
5987 - Updates `pBlockVector->m_pMetadata`.
5988 - Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
5989 - Does not move actual data, only returns requested moves as `moves`.
5990 */
5991 class VmaDefragmentationAlgorithm
5992 {
VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)5993     VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
5994 public:
5995     VmaDefragmentationAlgorithm(
5996         VmaAllocator hAllocator,
5997         VmaBlockVector* pBlockVector,
5998         uint32_t currentFrameIndex) :
5999         m_hAllocator(hAllocator),
6000         m_pBlockVector(pBlockVector),
6001         m_CurrentFrameIndex(currentFrameIndex)
6002     {
6003     }
~VmaDefragmentationAlgorithm()6004     virtual ~VmaDefragmentationAlgorithm()
6005     {
6006     }
6007 
6008     virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
6009     virtual void AddAll() = 0;
6010 
6011     virtual VkResult Defragment(
6012         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6013         VkDeviceSize maxBytesToMove,
6014         uint32_t maxAllocationsToMove) = 0;
6015 
6016     virtual VkDeviceSize GetBytesMoved() const = 0;
6017     virtual uint32_t GetAllocationsMoved() const = 0;
6018 
6019 protected:
6020     VmaAllocator const m_hAllocator;
6021     VmaBlockVector* const m_pBlockVector;
6022     const uint32_t m_CurrentFrameIndex;
6023 
6024     struct AllocationInfo
6025     {
6026         VmaAllocation m_hAllocation;
6027         VkBool32* m_pChanged;
6028 
AllocationInfoAllocationInfo6029         AllocationInfo() :
6030             m_hAllocation(VK_NULL_HANDLE),
6031             m_pChanged(VMA_NULL)
6032         {
6033         }
AllocationInfoAllocationInfo6034         AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
6035             m_hAllocation(hAlloc),
6036             m_pChanged(pChanged)
6037         {
6038         }
6039     };
6040 };
6041 
6042 class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
6043 {
6044     VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
6045 public:
6046     VmaDefragmentationAlgorithm_Generic(
6047         VmaAllocator hAllocator,
6048         VmaBlockVector* pBlockVector,
6049         uint32_t currentFrameIndex,
6050         bool overlappingMoveSupported);
6051     virtual ~VmaDefragmentationAlgorithm_Generic();
6052 
6053     virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
AddAll()6054     virtual void AddAll() { m_AllAllocations = true; }
6055 
6056     virtual VkResult Defragment(
6057         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6058         VkDeviceSize maxBytesToMove,
6059         uint32_t maxAllocationsToMove);
6060 
GetBytesMoved()6061     virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
GetAllocationsMoved()6062     virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
6063 
6064 private:
6065     uint32_t m_AllocationCount;
6066     bool m_AllAllocations;
6067 
6068     VkDeviceSize m_BytesMoved;
6069     uint32_t m_AllocationsMoved;
6070 
6071     struct AllocationInfoSizeGreater
6072     {
operatorAllocationInfoSizeGreater6073         bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
6074         {
6075             return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
6076         }
6077     };
6078 
6079     struct AllocationInfoOffsetGreater
6080     {
operatorAllocationInfoOffsetGreater6081         bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
6082         {
6083             return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
6084         }
6085     };
6086 
6087     struct BlockInfo
6088     {
6089         size_t m_OriginalBlockIndex;
6090         VmaDeviceMemoryBlock* m_pBlock;
6091         bool m_HasNonMovableAllocations;
6092         VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
6093 
BlockInfoBlockInfo6094         BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
6095             m_OriginalBlockIndex(SIZE_MAX),
6096             m_pBlock(VMA_NULL),
6097             m_HasNonMovableAllocations(true),
6098             m_Allocations(pAllocationCallbacks)
6099         {
6100         }
6101 
CalcHasNonMovableAllocationsBlockInfo6102         void CalcHasNonMovableAllocations()
6103         {
6104             const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
6105             const size_t defragmentAllocCount = m_Allocations.size();
6106             m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
6107         }
6108 
SortAllocationsBySizeDescendingBlockInfo6109         void SortAllocationsBySizeDescending()
6110         {
6111             VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
6112         }
6113 
SortAllocationsByOffsetDescendingBlockInfo6114         void SortAllocationsByOffsetDescending()
6115         {
6116             VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
6117         }
6118     };
6119 
6120     struct BlockPointerLess
6121     {
operatorBlockPointerLess6122         bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
6123         {
6124             return pLhsBlockInfo->m_pBlock < pRhsBlock;
6125         }
operatorBlockPointerLess6126         bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
6127         {
6128             return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
6129         }
6130     };
6131 
6132     // 1. Blocks with some non-movable allocations go first.
6133     // 2. Blocks with smaller sumFreeSize go first.
6134     struct BlockInfoCompareMoveDestination
6135     {
operatorBlockInfoCompareMoveDestination6136         bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
6137         {
6138             if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
6139             {
6140                 return true;
6141             }
6142             if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
6143             {
6144                 return false;
6145             }
6146             if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
6147             {
6148                 return true;
6149             }
6150             return false;
6151         }
6152     };
6153 
6154     typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
6155     BlockInfoVector m_Blocks;
6156 
6157     VkResult DefragmentRound(
6158         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6159         VkDeviceSize maxBytesToMove,
6160         uint32_t maxAllocationsToMove);
6161 
6162     size_t CalcBlocksWithNonMovableCount() const;
6163 
6164     static bool MoveMakesSense(
6165         size_t dstBlockIndex, VkDeviceSize dstOffset,
6166         size_t srcBlockIndex, VkDeviceSize srcOffset);
6167 };
6168 
6169 class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
6170 {
6171     VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
6172 public:
6173     VmaDefragmentationAlgorithm_Fast(
6174         VmaAllocator hAllocator,
6175         VmaBlockVector* pBlockVector,
6176         uint32_t currentFrameIndex,
6177         bool overlappingMoveSupported);
6178     virtual ~VmaDefragmentationAlgorithm_Fast();
6179 
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)6180     virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { ++m_AllocationCount; }
AddAll()6181     virtual void AddAll() { m_AllAllocations = true; }
6182 
6183     virtual VkResult Defragment(
6184         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6185         VkDeviceSize maxBytesToMove,
6186         uint32_t maxAllocationsToMove);
6187 
GetBytesMoved()6188     virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
GetAllocationsMoved()6189     virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
6190 
6191 private:
6192     struct BlockInfo
6193     {
6194         size_t origBlockIndex;
6195     };
6196 
6197     class FreeSpaceDatabase
6198     {
6199     public:
FreeSpaceDatabase()6200         FreeSpaceDatabase()
6201         {
6202             FreeSpace s = {};
6203             s.blockInfoIndex = SIZE_MAX;
6204             for(size_t i = 0; i < MAX_COUNT; ++i)
6205             {
6206                 m_FreeSpaces[i] = s;
6207             }
6208         }
6209 
Register(size_t blockInfoIndex,VkDeviceSize offset,VkDeviceSize size)6210         void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
6211         {
6212             if(size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
6213             {
6214                 return;
6215             }
6216 
6217             // Find first invalid or the smallest structure.
6218             size_t bestIndex = SIZE_MAX;
6219             for(size_t i = 0; i < MAX_COUNT; ++i)
6220             {
6221                 // Empty structure.
6222                 if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
6223                 {
6224                     bestIndex = i;
6225                     break;
6226                 }
6227                 if(m_FreeSpaces[i].size < size &&
6228                     (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
6229                 {
6230                     bestIndex = i;
6231                 }
6232             }
6233 
6234             if(bestIndex != SIZE_MAX)
6235             {
6236                 m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
6237                 m_FreeSpaces[bestIndex].offset = offset;
6238                 m_FreeSpaces[bestIndex].size = size;
6239             }
6240         }
6241 
Fetch(VkDeviceSize alignment,VkDeviceSize size,size_t & outBlockInfoIndex,VkDeviceSize & outDstOffset)6242         bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
6243             size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
6244         {
6245             size_t bestIndex = SIZE_MAX;
6246             VkDeviceSize bestFreeSpaceAfter = 0;
6247             for(size_t i = 0; i < MAX_COUNT; ++i)
6248             {
6249                 // Structure is valid.
6250                 if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
6251                 {
6252                     const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment);
6253                     // Allocation fits into this structure.
6254                     if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
6255                     {
6256                         const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
6257                             (dstOffset + size);
6258                         if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
6259                         {
6260                             bestIndex = i;
6261                             bestFreeSpaceAfter = freeSpaceAfter;
6262                         }
6263                     }
6264                 }
6265             }
6266 
6267             if(bestIndex != SIZE_MAX)
6268             {
6269                 outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
6270                 outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment);
6271 
6272                 if(bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
6273                 {
6274                     // Leave this structure for remaining empty space.
6275                     const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
6276                     m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
6277                     m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
6278                 }
6279                 else
6280                 {
6281                     // This structure becomes invalid.
6282                     m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX;
6283                 }
6284 
6285                 return true;
6286             }
6287 
6288             return false;
6289         }
6290 
6291     private:
6292         static const size_t MAX_COUNT = 4;
6293 
6294         struct FreeSpace
6295         {
6296             size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
6297             VkDeviceSize offset;
6298             VkDeviceSize size;
6299         } m_FreeSpaces[MAX_COUNT];
6300     };
6301 
6302     const bool m_OverlappingMoveSupported;
6303 
6304     uint32_t m_AllocationCount;
6305     bool m_AllAllocations;
6306 
6307     VkDeviceSize m_BytesMoved;
6308     uint32_t m_AllocationsMoved;
6309 
6310     VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
6311 
6312     void PreprocessMetadata();
6313     void PostprocessMetadata();
6314     void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
6315 };
6316 
6317 struct VmaBlockDefragmentationContext
6318 {
6319     enum BLOCK_FLAG
6320     {
6321         BLOCK_FLAG_USED = 0x00000001,
6322     };
6323     uint32_t flags;
6324     VkBuffer hBuffer;
6325 
VmaBlockDefragmentationContextVmaBlockDefragmentationContext6326     VmaBlockDefragmentationContext() :
6327         flags(0),
6328         hBuffer(VK_NULL_HANDLE)
6329     {
6330     }
6331 };
6332 
6333 class VmaBlockVectorDefragmentationContext
6334 {
6335     VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
6336 public:
6337     VkResult res;
6338     bool mutexLocked;
6339     VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
6340 
6341     VmaBlockVectorDefragmentationContext(
6342         VmaAllocator hAllocator,
6343         VmaPool hCustomPool, // Optional.
6344         VmaBlockVector* pBlockVector,
6345         uint32_t currFrameIndex,
6346         uint32_t flags);
6347     ~VmaBlockVectorDefragmentationContext();
6348 
GetCustomPool()6349     VmaPool GetCustomPool() const { return m_hCustomPool; }
GetBlockVector()6350     VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
GetAlgorithm()6351     VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
6352 
6353     void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
AddAll()6354     void AddAll() { m_AllAllocations = true; }
6355 
6356     void Begin(bool overlappingMoveSupported);
6357 
6358 private:
6359     const VmaAllocator m_hAllocator;
6360     // Null if not from custom pool.
6361     const VmaPool m_hCustomPool;
6362     // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
6363     VmaBlockVector* const m_pBlockVector;
6364     const uint32_t m_CurrFrameIndex;
6365     //const uint32_t m_AlgorithmFlags;
6366     // Owner of this object.
6367     VmaDefragmentationAlgorithm* m_pAlgorithm;
6368 
6369     struct AllocInfo
6370     {
6371         VmaAllocation hAlloc;
6372         VkBool32* pChanged;
6373     };
6374     // Used between constructor and Begin.
6375     VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
6376     bool m_AllAllocations;
6377 };
6378 
6379 struct VmaDefragmentationContext_T
6380 {
6381 private:
6382     VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
6383 public:
6384     VmaDefragmentationContext_T(
6385         VmaAllocator hAllocator,
6386         uint32_t currFrameIndex,
6387         uint32_t flags,
6388         VmaDefragmentationStats* pStats);
6389     ~VmaDefragmentationContext_T();
6390 
6391     void AddPools(uint32_t poolCount, VmaPool* pPools);
6392     void AddAllocations(
6393         uint32_t allocationCount,
6394         VmaAllocation* pAllocations,
6395         VkBool32* pAllocationsChanged);
6396 
6397     /*
6398     Returns:
6399     - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
6400     - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
6401     - Negative value if error occured and object can be destroyed immediately.
6402     */
6403     VkResult Defragment(
6404         VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
6405         VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
6406         VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats);
6407 
6408 private:
6409     const VmaAllocator m_hAllocator;
6410     const uint32_t m_CurrFrameIndex;
6411     const uint32_t m_Flags;
6412     VmaDefragmentationStats* const m_pStats;
6413     // Owner of these objects.
6414     VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
6415     // Owner of these objects.
6416     VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
6417 };
6418 
6419 #if VMA_RECORDING_ENABLED
6420 
6421 class VmaRecorder
6422 {
6423 public:
6424     VmaRecorder();
6425     VkResult Init(const VmaRecordSettings& settings, bool useMutex);
6426     void WriteConfiguration(
6427         const VkPhysicalDeviceProperties& devProps,
6428         const VkPhysicalDeviceMemoryProperties& memProps,
6429         bool dedicatedAllocationExtensionEnabled);
6430     ~VmaRecorder();
6431 
6432     void RecordCreateAllocator(uint32_t frameIndex);
6433     void RecordDestroyAllocator(uint32_t frameIndex);
6434     void RecordCreatePool(uint32_t frameIndex,
6435         const VmaPoolCreateInfo& createInfo,
6436         VmaPool pool);
6437     void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
6438     void RecordAllocateMemory(uint32_t frameIndex,
6439         const VkMemoryRequirements& vkMemReq,
6440         const VmaAllocationCreateInfo& createInfo,
6441         VmaAllocation allocation);
6442     void RecordAllocateMemoryPages(uint32_t frameIndex,
6443         const VkMemoryRequirements& vkMemReq,
6444         const VmaAllocationCreateInfo& createInfo,
6445         uint64_t allocationCount,
6446         const VmaAllocation* pAllocations);
6447     void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
6448         const VkMemoryRequirements& vkMemReq,
6449         bool requiresDedicatedAllocation,
6450         bool prefersDedicatedAllocation,
6451         const VmaAllocationCreateInfo& createInfo,
6452         VmaAllocation allocation);
6453     void RecordAllocateMemoryForImage(uint32_t frameIndex,
6454         const VkMemoryRequirements& vkMemReq,
6455         bool requiresDedicatedAllocation,
6456         bool prefersDedicatedAllocation,
6457         const VmaAllocationCreateInfo& createInfo,
6458         VmaAllocation allocation);
6459     void RecordFreeMemory(uint32_t frameIndex,
6460         VmaAllocation allocation);
6461     void RecordFreeMemoryPages(uint32_t frameIndex,
6462         uint64_t allocationCount,
6463         const VmaAllocation* pAllocations);
6464     void RecordResizeAllocation(
6465         uint32_t frameIndex,
6466         VmaAllocation allocation,
6467         VkDeviceSize newSize);
6468     void RecordSetAllocationUserData(uint32_t frameIndex,
6469         VmaAllocation allocation,
6470         const void* pUserData);
6471     void RecordCreateLostAllocation(uint32_t frameIndex,
6472         VmaAllocation allocation);
6473     void RecordMapMemory(uint32_t frameIndex,
6474         VmaAllocation allocation);
6475     void RecordUnmapMemory(uint32_t frameIndex,
6476         VmaAllocation allocation);
6477     void RecordFlushAllocation(uint32_t frameIndex,
6478         VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
6479     void RecordInvalidateAllocation(uint32_t frameIndex,
6480         VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
6481     void RecordCreateBuffer(uint32_t frameIndex,
6482         const VkBufferCreateInfo& bufCreateInfo,
6483         const VmaAllocationCreateInfo& allocCreateInfo,
6484         VmaAllocation allocation);
6485     void RecordCreateImage(uint32_t frameIndex,
6486         const VkImageCreateInfo& imageCreateInfo,
6487         const VmaAllocationCreateInfo& allocCreateInfo,
6488         VmaAllocation allocation);
6489     void RecordDestroyBuffer(uint32_t frameIndex,
6490         VmaAllocation allocation);
6491     void RecordDestroyImage(uint32_t frameIndex,
6492         VmaAllocation allocation);
6493     void RecordTouchAllocation(uint32_t frameIndex,
6494         VmaAllocation allocation);
6495     void RecordGetAllocationInfo(uint32_t frameIndex,
6496         VmaAllocation allocation);
6497     void RecordMakePoolAllocationsLost(uint32_t frameIndex,
6498         VmaPool pool);
6499     void RecordDefragmentationBegin(uint32_t frameIndex,
6500         const VmaDefragmentationInfo2& info,
6501         VmaDefragmentationContext ctx);
6502     void RecordDefragmentationEnd(uint32_t frameIndex,
6503         VmaDefragmentationContext ctx);
6504 
6505 private:
6506     struct CallParams
6507     {
6508         uint32_t threadId;
6509         double time;
6510     };
6511 
6512     class UserDataString
6513     {
6514     public:
6515         UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
GetString()6516         const char* GetString() const { return m_Str; }
6517 
6518     private:
6519         char m_PtrStr[17];
6520         const char* m_Str;
6521     };
6522 
6523     bool m_UseMutex;
6524     VmaRecordFlags m_Flags;
6525     FILE* m_File;
6526     VMA_MUTEX m_FileMutex;
6527     int64_t m_Freq;
6528     int64_t m_StartCounter;
6529 
6530     void GetBasicParams(CallParams& outParams);
6531 
6532     // T must be a pointer type, e.g. VmaAllocation, VmaPool.
6533     template<typename T>
PrintPointerList(uint64_t count,const T * pItems)6534     void PrintPointerList(uint64_t count, const T* pItems)
6535     {
6536         if(count)
6537         {
6538             fprintf(m_File, "%p", pItems[0]);
6539             for(uint64_t i = 1; i < count; ++i)
6540             {
6541                 fprintf(m_File, " %p", pItems[i]);
6542             }
6543         }
6544     }
6545 
6546     void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
6547     void Flush();
6548 };
6549 
6550 #endif // #if VMA_RECORDING_ENABLED
6551 
6552 // Main allocator object.
6553 struct VmaAllocator_T
6554 {
6555     VMA_CLASS_NO_COPY(VmaAllocator_T)
6556 public:
6557     bool m_UseMutex;
6558     bool m_UseKhrDedicatedAllocation;
6559     VkDevice m_hDevice;
6560     bool m_AllocationCallbacksSpecified;
6561     VkAllocationCallbacks m_AllocationCallbacks;
6562     VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
6563 
6564     // Number of bytes free out of limit, or VK_WHOLE_SIZE if no limit for that heap.
6565     VkDeviceSize m_HeapSizeLimit[VK_MAX_MEMORY_HEAPS];
6566     VMA_MUTEX m_HeapSizeLimitMutex;
6567 
6568     VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
6569     VkPhysicalDeviceMemoryProperties m_MemProps;
6570 
6571     // Default pools.
6572     VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
6573 
6574     // Each vector is sorted by memory (handle value).
6575     typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
6576     AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES];
6577     VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
6578 
6579     VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
6580     VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
6581     ~VmaAllocator_T();
6582 
GetAllocationCallbacksVmaAllocator_T6583     const VkAllocationCallbacks* GetAllocationCallbacks() const
6584     {
6585         return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
6586     }
GetVulkanFunctionsVmaAllocator_T6587     const VmaVulkanFunctions& GetVulkanFunctions() const
6588     {
6589         return m_VulkanFunctions;
6590     }
6591 
GetBufferImageGranularityVmaAllocator_T6592     VkDeviceSize GetBufferImageGranularity() const
6593     {
6594         return VMA_MAX(
6595             static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
6596             m_PhysicalDeviceProperties.limits.bufferImageGranularity);
6597     }
6598 
GetMemoryHeapCountVmaAllocator_T6599     uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
GetMemoryTypeCountVmaAllocator_T6600     uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
6601 
MemoryTypeIndexToHeapIndexVmaAllocator_T6602     uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
6603     {
6604         VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
6605         return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
6606     }
6607     // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
IsMemoryTypeNonCoherentVmaAllocator_T6608     bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
6609     {
6610         return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
6611             VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
6612     }
6613     // Minimum alignment for all allocations in specific memory type.
GetMemoryTypeMinAlignmentVmaAllocator_T6614     VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
6615     {
6616         return IsMemoryTypeNonCoherent(memTypeIndex) ?
6617             VMA_MAX((VkDeviceSize)VMA_DEBUG_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
6618             (VkDeviceSize)VMA_DEBUG_ALIGNMENT;
6619     }
6620 
IsIntegratedGpuVmaAllocator_T6621     bool IsIntegratedGpu() const
6622     {
6623         return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
6624     }
6625 
6626 #if VMA_RECORDING_ENABLED
GetRecorderVmaAllocator_T6627     VmaRecorder* GetRecorder() const { return m_pRecorder; }
6628 #endif
6629 
6630     void GetBufferMemoryRequirements(
6631         VkBuffer hBuffer,
6632         VkMemoryRequirements& memReq,
6633         bool& requiresDedicatedAllocation,
6634         bool& prefersDedicatedAllocation) const;
6635     void GetImageMemoryRequirements(
6636         VkImage hImage,
6637         VkMemoryRequirements& memReq,
6638         bool& requiresDedicatedAllocation,
6639         bool& prefersDedicatedAllocation) const;
6640 
6641     // Main allocation function.
6642     VkResult AllocateMemory(
6643         const VkMemoryRequirements& vkMemReq,
6644         bool requiresDedicatedAllocation,
6645         bool prefersDedicatedAllocation,
6646         VkBuffer dedicatedBuffer,
6647         VkImage dedicatedImage,
6648         const VmaAllocationCreateInfo& createInfo,
6649         VmaSuballocationType suballocType,
6650         size_t allocationCount,
6651         VmaAllocation* pAllocations);
6652 
6653     // Main deallocation function.
6654     void FreeMemory(
6655         size_t allocationCount,
6656         const VmaAllocation* pAllocations);
6657 
6658     VkResult ResizeAllocation(
6659         const VmaAllocation alloc,
6660         VkDeviceSize newSize);
6661 
6662     void CalculateStats(VmaStats* pStats);
6663 
6664 #if VMA_STATS_STRING_ENABLED
6665     void PrintDetailedMap(class VmaJsonWriter& json);
6666 #endif
6667 
6668     VkResult DefragmentationBegin(
6669         const VmaDefragmentationInfo2& info,
6670         VmaDefragmentationStats* pStats,
6671         VmaDefragmentationContext* pContext);
6672     VkResult DefragmentationEnd(
6673         VmaDefragmentationContext context);
6674 
6675     void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
6676     bool TouchAllocation(VmaAllocation hAllocation);
6677 
6678     VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
6679     void DestroyPool(VmaPool pool);
6680     void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
6681 
6682     void SetCurrentFrameIndex(uint32_t frameIndex);
GetCurrentFrameIndexVmaAllocator_T6683     uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
6684 
6685     void MakePoolAllocationsLost(
6686         VmaPool hPool,
6687         size_t* pLostAllocationCount);
6688     VkResult CheckPoolCorruption(VmaPool hPool);
6689     VkResult CheckCorruption(uint32_t memoryTypeBits);
6690 
6691     void CreateLostAllocation(VmaAllocation* pAllocation);
6692 
6693     VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
6694     void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
6695 
6696     VkResult Map(VmaAllocation hAllocation, void** ppData);
6697     void Unmap(VmaAllocation hAllocation);
6698 
6699     VkResult BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer);
6700     VkResult BindImageMemory(VmaAllocation hAllocation, VkImage hImage);
6701 
6702     void FlushOrInvalidateAllocation(
6703         VmaAllocation hAllocation,
6704         VkDeviceSize offset, VkDeviceSize size,
6705         VMA_CACHE_OPERATION op);
6706 
6707     void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
6708 
6709 private:
6710     VkDeviceSize m_PreferredLargeHeapBlockSize;
6711 
6712     VkPhysicalDevice m_PhysicalDevice;
6713     VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
6714 
6715     VMA_RW_MUTEX m_PoolsMutex;
6716     // Protected by m_PoolsMutex. Sorted by pointer value.
6717     VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools;
6718     uint32_t m_NextPoolId;
6719 
6720     VmaVulkanFunctions m_VulkanFunctions;
6721 
6722 #if VMA_RECORDING_ENABLED
6723     VmaRecorder* m_pRecorder;
6724 #endif
6725 
6726     void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
6727 
6728     VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
6729 
6730     VkResult AllocateMemoryOfType(
6731         VkDeviceSize size,
6732         VkDeviceSize alignment,
6733         bool dedicatedAllocation,
6734         VkBuffer dedicatedBuffer,
6735         VkImage dedicatedImage,
6736         const VmaAllocationCreateInfo& createInfo,
6737         uint32_t memTypeIndex,
6738         VmaSuballocationType suballocType,
6739         size_t allocationCount,
6740         VmaAllocation* pAllocations);
6741 
6742     // Helper function only to be used inside AllocateDedicatedMemory.
6743     VkResult AllocateDedicatedMemoryPage(
6744         VkDeviceSize size,
6745         VmaSuballocationType suballocType,
6746         uint32_t memTypeIndex,
6747         const VkMemoryAllocateInfo& allocInfo,
6748         bool map,
6749         bool isUserDataString,
6750         void* pUserData,
6751         VmaAllocation* pAllocation);
6752 
6753     // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
6754     VkResult AllocateDedicatedMemory(
6755         VkDeviceSize size,
6756         VmaSuballocationType suballocType,
6757         uint32_t memTypeIndex,
6758         bool map,
6759         bool isUserDataString,
6760         void* pUserData,
6761         VkBuffer dedicatedBuffer,
6762         VkImage dedicatedImage,
6763         size_t allocationCount,
6764         VmaAllocation* pAllocations);
6765 
6766     // Tries to free pMemory as Dedicated Memory. Returns true if found and freed.
6767     void FreeDedicatedMemory(VmaAllocation allocation);
6768 };
6769 
6770 ////////////////////////////////////////////////////////////////////////////////
6771 // Memory allocation #2 after VmaAllocator_T definition
6772 
VmaMalloc(VmaAllocator hAllocator,size_t size,size_t alignment)6773 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
6774 {
6775     return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
6776 }
6777 
VmaFree(VmaAllocator hAllocator,void * ptr)6778 static void VmaFree(VmaAllocator hAllocator, void* ptr)
6779 {
6780     VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
6781 }
6782 
6783 template<typename T>
VmaAllocate(VmaAllocator hAllocator)6784 static T* VmaAllocate(VmaAllocator hAllocator)
6785 {
6786     return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
6787 }
6788 
6789 template<typename T>
VmaAllocateArray(VmaAllocator hAllocator,size_t count)6790 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
6791 {
6792     return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
6793 }
6794 
6795 template<typename T>
vma_delete(VmaAllocator hAllocator,T * ptr)6796 static void vma_delete(VmaAllocator hAllocator, T* ptr)
6797 {
6798     if(ptr != VMA_NULL)
6799     {
6800         ptr->~T();
6801         VmaFree(hAllocator, ptr);
6802     }
6803 }
6804 
6805 template<typename T>
vma_delete_array(VmaAllocator hAllocator,T * ptr,size_t count)6806 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
6807 {
6808     if(ptr != VMA_NULL)
6809     {
6810         for(size_t i = count; i--; )
6811             ptr[i].~T();
6812         VmaFree(hAllocator, ptr);
6813     }
6814 }
6815 
6816 ////////////////////////////////////////////////////////////////////////////////
6817 // VmaStringBuilder
6818 
6819 #if VMA_STATS_STRING_ENABLED
6820 
6821 class VmaStringBuilder
6822 {
6823 public:
VmaStringBuilder(VmaAllocator alloc)6824     VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
GetLength()6825     size_t GetLength() const { return m_Data.size(); }
GetData()6826     const char* GetData() const { return m_Data.data(); }
6827 
Add(char ch)6828     void Add(char ch) { m_Data.push_back(ch); }
6829     void Add(const char* pStr);
AddNewLine()6830     void AddNewLine() { Add('\n'); }
6831     void AddNumber(uint32_t num);
6832     void AddNumber(uint64_t num);
6833     void AddPointer(const void* ptr);
6834 
6835 private:
6836     VmaVector< char, VmaStlAllocator<char> > m_Data;
6837 };
6838 
Add(const char * pStr)6839 void VmaStringBuilder::Add(const char* pStr)
6840 {
6841     const size_t strLen = strlen(pStr);
6842     if(strLen > 0)
6843     {
6844         const size_t oldCount = m_Data.size();
6845         m_Data.resize(oldCount + strLen);
6846         memcpy(m_Data.data() + oldCount, pStr, strLen);
6847     }
6848 }
6849 
AddNumber(uint32_t num)6850 void VmaStringBuilder::AddNumber(uint32_t num)
6851 {
6852     char buf[11];
6853     VmaUint32ToStr(buf, sizeof(buf), num);
6854     Add(buf);
6855 }
6856 
AddNumber(uint64_t num)6857 void VmaStringBuilder::AddNumber(uint64_t num)
6858 {
6859     char buf[21];
6860     VmaUint64ToStr(buf, sizeof(buf), num);
6861     Add(buf);
6862 }
6863 
AddPointer(const void * ptr)6864 void VmaStringBuilder::AddPointer(const void* ptr)
6865 {
6866     char buf[21];
6867     VmaPtrToStr(buf, sizeof(buf), ptr);
6868     Add(buf);
6869 }
6870 
6871 #endif // #if VMA_STATS_STRING_ENABLED
6872 
6873 ////////////////////////////////////////////////////////////////////////////////
6874 // VmaJsonWriter
6875 
6876 #if VMA_STATS_STRING_ENABLED
6877 
6878 class VmaJsonWriter
6879 {
6880     VMA_CLASS_NO_COPY(VmaJsonWriter)
6881 public:
6882     VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
6883     ~VmaJsonWriter();
6884 
6885     void BeginObject(bool singleLine = false);
6886     void EndObject();
6887 
6888     void BeginArray(bool singleLine = false);
6889     void EndArray();
6890 
6891     void WriteString(const char* pStr);
6892     void BeginString(const char* pStr = VMA_NULL);
6893     void ContinueString(const char* pStr);
6894     void ContinueString(uint32_t n);
6895     void ContinueString(uint64_t n);
6896     void ContinueString_Pointer(const void* ptr);
6897     void EndString(const char* pStr = VMA_NULL);
6898 
6899     void WriteNumber(uint32_t n);
6900     void WriteNumber(uint64_t n);
6901     void WriteBool(bool b);
6902     void WriteNull();
6903 
6904 private:
6905     static const char* const INDENT;
6906 
6907     enum COLLECTION_TYPE
6908     {
6909         COLLECTION_TYPE_OBJECT,
6910         COLLECTION_TYPE_ARRAY,
6911     };
6912     struct StackItem
6913     {
6914         COLLECTION_TYPE type;
6915         uint32_t valueCount;
6916         bool singleLineMode;
6917     };
6918 
6919     VmaStringBuilder& m_SB;
6920     VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
6921     bool m_InsideString;
6922 
6923     void BeginValue(bool isString);
6924     void WriteIndent(bool oneLess = false);
6925 };
6926 
6927 const char* const VmaJsonWriter::INDENT = "  ";
6928 
VmaJsonWriter(const VkAllocationCallbacks * pAllocationCallbacks,VmaStringBuilder & sb)6929 VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
6930     m_SB(sb),
6931     m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
6932     m_InsideString(false)
6933 {
6934 }
6935 
~VmaJsonWriter()6936 VmaJsonWriter::~VmaJsonWriter()
6937 {
6938     VMA_ASSERT(!m_InsideString);
6939     VMA_ASSERT(m_Stack.empty());
6940 }
6941 
BeginObject(bool singleLine)6942 void VmaJsonWriter::BeginObject(bool singleLine)
6943 {
6944     VMA_ASSERT(!m_InsideString);
6945 
6946     BeginValue(false);
6947     m_SB.Add('{');
6948 
6949     StackItem item;
6950     item.type = COLLECTION_TYPE_OBJECT;
6951     item.valueCount = 0;
6952     item.singleLineMode = singleLine;
6953     m_Stack.push_back(item);
6954 }
6955 
EndObject()6956 void VmaJsonWriter::EndObject()
6957 {
6958     VMA_ASSERT(!m_InsideString);
6959 
6960     WriteIndent(true);
6961     m_SB.Add('}');
6962 
6963     VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
6964     m_Stack.pop_back();
6965 }
6966 
BeginArray(bool singleLine)6967 void VmaJsonWriter::BeginArray(bool singleLine)
6968 {
6969     VMA_ASSERT(!m_InsideString);
6970 
6971     BeginValue(false);
6972     m_SB.Add('[');
6973 
6974     StackItem item;
6975     item.type = COLLECTION_TYPE_ARRAY;
6976     item.valueCount = 0;
6977     item.singleLineMode = singleLine;
6978     m_Stack.push_back(item);
6979 }
6980 
EndArray()6981 void VmaJsonWriter::EndArray()
6982 {
6983     VMA_ASSERT(!m_InsideString);
6984 
6985     WriteIndent(true);
6986     m_SB.Add(']');
6987 
6988     VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
6989     m_Stack.pop_back();
6990 }
6991 
WriteString(const char * pStr)6992 void VmaJsonWriter::WriteString(const char* pStr)
6993 {
6994     BeginString(pStr);
6995     EndString();
6996 }
6997 
BeginString(const char * pStr)6998 void VmaJsonWriter::BeginString(const char* pStr)
6999 {
7000     VMA_ASSERT(!m_InsideString);
7001 
7002     BeginValue(true);
7003     m_SB.Add('"');
7004     m_InsideString = true;
7005     if(pStr != VMA_NULL && pStr[0] != '\0')
7006     {
7007         ContinueString(pStr);
7008     }
7009 }
7010 
ContinueString(const char * pStr)7011 void VmaJsonWriter::ContinueString(const char* pStr)
7012 {
7013     VMA_ASSERT(m_InsideString);
7014 
7015     const size_t strLen = strlen(pStr);
7016     for(size_t i = 0; i < strLen; ++i)
7017     {
7018         char ch = pStr[i];
7019         if(ch == '\\')
7020         {
7021             m_SB.Add("\\\\");
7022         }
7023         else if(ch == '"')
7024         {
7025             m_SB.Add("\\\"");
7026         }
7027         else if(ch >= 32)
7028         {
7029             m_SB.Add(ch);
7030         }
7031         else switch(ch)
7032         {
7033         case '\b':
7034             m_SB.Add("\\b");
7035             break;
7036         case '\f':
7037             m_SB.Add("\\f");
7038             break;
7039         case '\n':
7040             m_SB.Add("\\n");
7041             break;
7042         case '\r':
7043             m_SB.Add("\\r");
7044             break;
7045         case '\t':
7046             m_SB.Add("\\t");
7047             break;
7048         default:
7049             VMA_ASSERT(0 && "Character not currently supported.");
7050             break;
7051         }
7052     }
7053 }
7054 
ContinueString(uint32_t n)7055 void VmaJsonWriter::ContinueString(uint32_t n)
7056 {
7057     VMA_ASSERT(m_InsideString);
7058     m_SB.AddNumber(n);
7059 }
7060 
ContinueString(uint64_t n)7061 void VmaJsonWriter::ContinueString(uint64_t n)
7062 {
7063     VMA_ASSERT(m_InsideString);
7064     m_SB.AddNumber(n);
7065 }
7066 
ContinueString_Pointer(const void * ptr)7067 void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
7068 {
7069     VMA_ASSERT(m_InsideString);
7070     m_SB.AddPointer(ptr);
7071 }
7072 
EndString(const char * pStr)7073 void VmaJsonWriter::EndString(const char* pStr)
7074 {
7075     VMA_ASSERT(m_InsideString);
7076     if(pStr != VMA_NULL && pStr[0] != '\0')
7077     {
7078         ContinueString(pStr);
7079     }
7080     m_SB.Add('"');
7081     m_InsideString = false;
7082 }
7083 
WriteNumber(uint32_t n)7084 void VmaJsonWriter::WriteNumber(uint32_t n)
7085 {
7086     VMA_ASSERT(!m_InsideString);
7087     BeginValue(false);
7088     m_SB.AddNumber(n);
7089 }
7090 
WriteNumber(uint64_t n)7091 void VmaJsonWriter::WriteNumber(uint64_t n)
7092 {
7093     VMA_ASSERT(!m_InsideString);
7094     BeginValue(false);
7095     m_SB.AddNumber(n);
7096 }
7097 
WriteBool(bool b)7098 void VmaJsonWriter::WriteBool(bool b)
7099 {
7100     VMA_ASSERT(!m_InsideString);
7101     BeginValue(false);
7102     m_SB.Add(b ? "true" : "false");
7103 }
7104 
WriteNull()7105 void VmaJsonWriter::WriteNull()
7106 {
7107     VMA_ASSERT(!m_InsideString);
7108     BeginValue(false);
7109     m_SB.Add("null");
7110 }
7111 
BeginValue(bool isString)7112 void VmaJsonWriter::BeginValue(bool isString)
7113 {
7114     if(!m_Stack.empty())
7115     {
7116         StackItem& currItem = m_Stack.back();
7117         if(currItem.type == COLLECTION_TYPE_OBJECT &&
7118             currItem.valueCount % 2 == 0)
7119         {
7120             VMA_ASSERT(isString);
7121         }
7122 
7123         if(currItem.type == COLLECTION_TYPE_OBJECT &&
7124             currItem.valueCount % 2 != 0)
7125         {
7126             m_SB.Add(": ");
7127         }
7128         else if(currItem.valueCount > 0)
7129         {
7130             m_SB.Add(", ");
7131             WriteIndent();
7132         }
7133         else
7134         {
7135             WriteIndent();
7136         }
7137         ++currItem.valueCount;
7138     }
7139 }
7140 
WriteIndent(bool oneLess)7141 void VmaJsonWriter::WriteIndent(bool oneLess)
7142 {
7143     if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
7144     {
7145         m_SB.AddNewLine();
7146 
7147         size_t count = m_Stack.size();
7148         if(count > 0 && oneLess)
7149         {
7150             --count;
7151         }
7152         for(size_t i = 0; i < count; ++i)
7153         {
7154             m_SB.Add(INDENT);
7155         }
7156     }
7157 }
7158 
7159 #endif // #if VMA_STATS_STRING_ENABLED
7160 
7161 ////////////////////////////////////////////////////////////////////////////////
7162 
SetUserData(VmaAllocator hAllocator,void * pUserData)7163 void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
7164 {
7165     if(IsUserDataString())
7166     {
7167         VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
7168 
7169         FreeUserDataString(hAllocator);
7170 
7171         if(pUserData != VMA_NULL)
7172         {
7173             const char* const newStrSrc = (char*)pUserData;
7174             const size_t newStrLen = strlen(newStrSrc);
7175             char* const newStrDst = vma_new_array(hAllocator, char, newStrLen + 1);
7176             memcpy(newStrDst, newStrSrc, newStrLen + 1);
7177             m_pUserData = newStrDst;
7178         }
7179     }
7180     else
7181     {
7182         m_pUserData = pUserData;
7183     }
7184 }
7185 
ChangeBlockAllocation(VmaAllocator hAllocator,VmaDeviceMemoryBlock * block,VkDeviceSize offset)7186 void VmaAllocation_T::ChangeBlockAllocation(
7187     VmaAllocator hAllocator,
7188     VmaDeviceMemoryBlock* block,
7189     VkDeviceSize offset)
7190 {
7191     VMA_ASSERT(block != VMA_NULL);
7192     VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7193 
7194     // Move mapping reference counter from old block to new block.
7195     if(block != m_BlockAllocation.m_Block)
7196     {
7197         uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
7198         if(IsPersistentMap())
7199             ++mapRefCount;
7200         m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
7201         block->Map(hAllocator, mapRefCount, VMA_NULL);
7202     }
7203 
7204     m_BlockAllocation.m_Block = block;
7205     m_BlockAllocation.m_Offset = offset;
7206 }
7207 
ChangeSize(VkDeviceSize newSize)7208 void VmaAllocation_T::ChangeSize(VkDeviceSize newSize)
7209 {
7210     VMA_ASSERT(newSize > 0);
7211     m_Size = newSize;
7212 }
7213 
ChangeOffset(VkDeviceSize newOffset)7214 void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
7215 {
7216     VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7217     m_BlockAllocation.m_Offset = newOffset;
7218 }
7219 
GetOffset()7220 VkDeviceSize VmaAllocation_T::GetOffset() const
7221 {
7222     switch(m_Type)
7223     {
7224     case ALLOCATION_TYPE_BLOCK:
7225         return m_BlockAllocation.m_Offset;
7226     case ALLOCATION_TYPE_DEDICATED:
7227         return 0;
7228     default:
7229         VMA_ASSERT(0);
7230         return 0;
7231     }
7232 }
7233 
GetMemory()7234 VkDeviceMemory VmaAllocation_T::GetMemory() const
7235 {
7236     switch(m_Type)
7237     {
7238     case ALLOCATION_TYPE_BLOCK:
7239         return m_BlockAllocation.m_Block->GetDeviceMemory();
7240     case ALLOCATION_TYPE_DEDICATED:
7241         return m_DedicatedAllocation.m_hMemory;
7242     default:
7243         VMA_ASSERT(0);
7244         return VK_NULL_HANDLE;
7245     }
7246 }
7247 
GetMemoryTypeIndex()7248 uint32_t VmaAllocation_T::GetMemoryTypeIndex() const
7249 {
7250     switch(m_Type)
7251     {
7252     case ALLOCATION_TYPE_BLOCK:
7253         return m_BlockAllocation.m_Block->GetMemoryTypeIndex();
7254     case ALLOCATION_TYPE_DEDICATED:
7255         return m_DedicatedAllocation.m_MemoryTypeIndex;
7256     default:
7257         VMA_ASSERT(0);
7258         return UINT32_MAX;
7259     }
7260 }
7261 
GetMappedData()7262 void* VmaAllocation_T::GetMappedData() const
7263 {
7264     switch(m_Type)
7265     {
7266     case ALLOCATION_TYPE_BLOCK:
7267         if(m_MapCount != 0)
7268         {
7269             void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
7270             VMA_ASSERT(pBlockData != VMA_NULL);
7271             return (char*)pBlockData + m_BlockAllocation.m_Offset;
7272         }
7273         else
7274         {
7275             return VMA_NULL;
7276         }
7277         break;
7278     case ALLOCATION_TYPE_DEDICATED:
7279         VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
7280         return m_DedicatedAllocation.m_pMappedData;
7281     default:
7282         VMA_ASSERT(0);
7283         return VMA_NULL;
7284     }
7285 }
7286 
CanBecomeLost()7287 bool VmaAllocation_T::CanBecomeLost() const
7288 {
7289     switch(m_Type)
7290     {
7291     case ALLOCATION_TYPE_BLOCK:
7292         return m_BlockAllocation.m_CanBecomeLost;
7293     case ALLOCATION_TYPE_DEDICATED:
7294         return false;
7295     default:
7296         VMA_ASSERT(0);
7297         return false;
7298     }
7299 }
7300 
GetPool()7301 VmaPool VmaAllocation_T::GetPool() const
7302 {
7303     VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7304     return m_BlockAllocation.m_hPool;
7305 }
7306 
MakeLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)7307 bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
7308 {
7309     VMA_ASSERT(CanBecomeLost());
7310 
7311     /*
7312     Warning: This is a carefully designed algorithm.
7313     Do not modify unless you really know what you're doing :)
7314     */
7315     uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
7316     for(;;)
7317     {
7318         if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
7319         {
7320             VMA_ASSERT(0);
7321             return false;
7322         }
7323         else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
7324         {
7325             return false;
7326         }
7327         else // Last use time earlier than current time.
7328         {
7329             if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
7330             {
7331                 // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
7332                 // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
7333                 return true;
7334             }
7335         }
7336     }
7337 }
7338 
7339 #if VMA_STATS_STRING_ENABLED
7340 
7341 // Correspond to values of enum VmaSuballocationType.
7342 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
7343     "FREE",
7344     "UNKNOWN",
7345     "BUFFER",
7346     "IMAGE_UNKNOWN",
7347     "IMAGE_LINEAR",
7348     "IMAGE_OPTIMAL",
7349 };
7350 
PrintParameters(class VmaJsonWriter & json)7351 void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
7352 {
7353     json.WriteString("Type");
7354     json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
7355 
7356     json.WriteString("Size");
7357     json.WriteNumber(m_Size);
7358 
7359     if(m_pUserData != VMA_NULL)
7360     {
7361         json.WriteString("UserData");
7362         if(IsUserDataString())
7363         {
7364             json.WriteString((const char*)m_pUserData);
7365         }
7366         else
7367         {
7368             json.BeginString();
7369             json.ContinueString_Pointer(m_pUserData);
7370             json.EndString();
7371         }
7372     }
7373 
7374     json.WriteString("CreationFrameIndex");
7375     json.WriteNumber(m_CreationFrameIndex);
7376 
7377     json.WriteString("LastUseFrameIndex");
7378     json.WriteNumber(GetLastUseFrameIndex());
7379 
7380     if(m_BufferImageUsage != 0)
7381     {
7382         json.WriteString("Usage");
7383         json.WriteNumber(m_BufferImageUsage);
7384     }
7385 }
7386 
7387 #endif
7388 
FreeUserDataString(VmaAllocator hAllocator)7389 void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
7390 {
7391     VMA_ASSERT(IsUserDataString());
7392     if(m_pUserData != VMA_NULL)
7393     {
7394         char* const oldStr = (char*)m_pUserData;
7395         const size_t oldStrLen = strlen(oldStr);
7396         vma_delete_array(hAllocator, oldStr, oldStrLen + 1);
7397         m_pUserData = VMA_NULL;
7398     }
7399 }
7400 
BlockAllocMap()7401 void VmaAllocation_T::BlockAllocMap()
7402 {
7403     VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
7404 
7405     if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
7406     {
7407         ++m_MapCount;
7408     }
7409     else
7410     {
7411         VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
7412     }
7413 }
7414 
BlockAllocUnmap()7415 void VmaAllocation_T::BlockAllocUnmap()
7416 {
7417     VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
7418 
7419     if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
7420     {
7421         --m_MapCount;
7422     }
7423     else
7424     {
7425         VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
7426     }
7427 }
7428 
DedicatedAllocMap(VmaAllocator hAllocator,void ** ppData)7429 VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
7430 {
7431     VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
7432 
7433     if(m_MapCount != 0)
7434     {
7435         if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
7436         {
7437             VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
7438             *ppData = m_DedicatedAllocation.m_pMappedData;
7439             ++m_MapCount;
7440             return VK_SUCCESS;
7441         }
7442         else
7443         {
7444             VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
7445             return VK_ERROR_MEMORY_MAP_FAILED;
7446         }
7447     }
7448     else
7449     {
7450         VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
7451             hAllocator->m_hDevice,
7452             m_DedicatedAllocation.m_hMemory,
7453             0, // offset
7454             VK_WHOLE_SIZE,
7455             0, // flags
7456             ppData);
7457         if(result == VK_SUCCESS)
7458         {
7459             m_DedicatedAllocation.m_pMappedData = *ppData;
7460             m_MapCount = 1;
7461         }
7462         return result;
7463     }
7464 }
7465 
DedicatedAllocUnmap(VmaAllocator hAllocator)7466 void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
7467 {
7468     VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
7469 
7470     if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
7471     {
7472         --m_MapCount;
7473         if(m_MapCount == 0)
7474         {
7475             m_DedicatedAllocation.m_pMappedData = VMA_NULL;
7476             (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
7477                 hAllocator->m_hDevice,
7478                 m_DedicatedAllocation.m_hMemory);
7479         }
7480     }
7481     else
7482     {
7483         VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
7484     }
7485 }
7486 
7487 #if VMA_STATS_STRING_ENABLED
7488 
VmaPrintStatInfo(VmaJsonWriter & json,const VmaStatInfo & stat)7489 static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
7490 {
7491     json.BeginObject();
7492 
7493     json.WriteString("Blocks");
7494     json.WriteNumber(stat.blockCount);
7495 
7496     json.WriteString("Allocations");
7497     json.WriteNumber(stat.allocationCount);
7498 
7499     json.WriteString("UnusedRanges");
7500     json.WriteNumber(stat.unusedRangeCount);
7501 
7502     json.WriteString("UsedBytes");
7503     json.WriteNumber(stat.usedBytes);
7504 
7505     json.WriteString("UnusedBytes");
7506     json.WriteNumber(stat.unusedBytes);
7507 
7508     if(stat.allocationCount > 1)
7509     {
7510         json.WriteString("AllocationSize");
7511         json.BeginObject(true);
7512         json.WriteString("Min");
7513         json.WriteNumber(stat.allocationSizeMin);
7514         json.WriteString("Avg");
7515         json.WriteNumber(stat.allocationSizeAvg);
7516         json.WriteString("Max");
7517         json.WriteNumber(stat.allocationSizeMax);
7518         json.EndObject();
7519     }
7520 
7521     if(stat.unusedRangeCount > 1)
7522     {
7523         json.WriteString("UnusedRangeSize");
7524         json.BeginObject(true);
7525         json.WriteString("Min");
7526         json.WriteNumber(stat.unusedRangeSizeMin);
7527         json.WriteString("Avg");
7528         json.WriteNumber(stat.unusedRangeSizeAvg);
7529         json.WriteString("Max");
7530         json.WriteNumber(stat.unusedRangeSizeMax);
7531         json.EndObject();
7532     }
7533 
7534     json.EndObject();
7535 }
7536 
7537 #endif // #if VMA_STATS_STRING_ENABLED
7538 
7539 struct VmaSuballocationItemSizeLess
7540 {
operatorVmaSuballocationItemSizeLess7541     bool operator()(
7542         const VmaSuballocationList::iterator lhs,
7543         const VmaSuballocationList::iterator rhs) const
7544     {
7545         return lhs->size < rhs->size;
7546     }
operatorVmaSuballocationItemSizeLess7547     bool operator()(
7548         const VmaSuballocationList::iterator lhs,
7549         VkDeviceSize rhsSize) const
7550     {
7551         return lhs->size < rhsSize;
7552     }
7553 };
7554 
7555 
7556 ////////////////////////////////////////////////////////////////////////////////
7557 // class VmaBlockMetadata
7558 
VmaBlockMetadata(VmaAllocator hAllocator)7559 VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
7560     m_Size(0),
7561     m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks())
7562 {
7563 }
7564 
7565 #if VMA_STATS_STRING_ENABLED
7566 
PrintDetailedMap_Begin(class VmaJsonWriter & json,VkDeviceSize unusedBytes,size_t allocationCount,size_t unusedRangeCount)7567 void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
7568     VkDeviceSize unusedBytes,
7569     size_t allocationCount,
7570     size_t unusedRangeCount) const
7571 {
7572     json.BeginObject();
7573 
7574     json.WriteString("TotalBytes");
7575     json.WriteNumber(GetSize());
7576 
7577     json.WriteString("UnusedBytes");
7578     json.WriteNumber(unusedBytes);
7579 
7580     json.WriteString("Allocations");
7581     json.WriteNumber((uint64_t)allocationCount);
7582 
7583     json.WriteString("UnusedRanges");
7584     json.WriteNumber((uint64_t)unusedRangeCount);
7585 
7586     json.WriteString("Suballocations");
7587     json.BeginArray();
7588 }
7589 
PrintDetailedMap_Allocation(class VmaJsonWriter & json,VkDeviceSize offset,VmaAllocation hAllocation)7590 void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
7591     VkDeviceSize offset,
7592     VmaAllocation hAllocation) const
7593 {
7594     json.BeginObject(true);
7595 
7596     json.WriteString("Offset");
7597     json.WriteNumber(offset);
7598 
7599     hAllocation->PrintParameters(json);
7600 
7601     json.EndObject();
7602 }
7603 
PrintDetailedMap_UnusedRange(class VmaJsonWriter & json,VkDeviceSize offset,VkDeviceSize size)7604 void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
7605     VkDeviceSize offset,
7606     VkDeviceSize size) const
7607 {
7608     json.BeginObject(true);
7609 
7610     json.WriteString("Offset");
7611     json.WriteNumber(offset);
7612 
7613     json.WriteString("Type");
7614     json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
7615 
7616     json.WriteString("Size");
7617     json.WriteNumber(size);
7618 
7619     json.EndObject();
7620 }
7621 
PrintDetailedMap_End(class VmaJsonWriter & json)7622 void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
7623 {
7624     json.EndArray();
7625     json.EndObject();
7626 }
7627 
7628 #endif // #if VMA_STATS_STRING_ENABLED
7629 
7630 ////////////////////////////////////////////////////////////////////////////////
7631 // class VmaBlockMetadata_Generic
7632 
VmaBlockMetadata_Generic(VmaAllocator hAllocator)7633 VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) :
7634     VmaBlockMetadata(hAllocator),
7635     m_FreeCount(0),
7636     m_SumFreeSize(0),
7637     m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
7638     m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
7639 {
7640 }
7641 
~VmaBlockMetadata_Generic()7642 VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
7643 {
7644 }
7645 
Init(VkDeviceSize size)7646 void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
7647 {
7648     VmaBlockMetadata::Init(size);
7649 
7650     m_FreeCount = 1;
7651     m_SumFreeSize = size;
7652 
7653     VmaSuballocation suballoc = {};
7654     suballoc.offset = 0;
7655     suballoc.size = size;
7656     suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
7657     suballoc.hAllocation = VK_NULL_HANDLE;
7658 
7659     VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
7660     m_Suballocations.push_back(suballoc);
7661     VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
7662     --suballocItem;
7663     m_FreeSuballocationsBySize.push_back(suballocItem);
7664 }
7665 
Validate()7666 bool VmaBlockMetadata_Generic::Validate() const
7667 {
7668     VMA_VALIDATE(!m_Suballocations.empty());
7669 
7670     // Expected offset of new suballocation as calculated from previous ones.
7671     VkDeviceSize calculatedOffset = 0;
7672     // Expected number of free suballocations as calculated from traversing their list.
7673     uint32_t calculatedFreeCount = 0;
7674     // Expected sum size of free suballocations as calculated from traversing their list.
7675     VkDeviceSize calculatedSumFreeSize = 0;
7676     // Expected number of free suballocations that should be registered in
7677     // m_FreeSuballocationsBySize calculated from traversing their list.
7678     size_t freeSuballocationsToRegister = 0;
7679     // True if previous visited suballocation was free.
7680     bool prevFree = false;
7681 
7682     for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7683         suballocItem != m_Suballocations.cend();
7684         ++suballocItem)
7685     {
7686         const VmaSuballocation& subAlloc = *suballocItem;
7687 
7688         // Actual offset of this suballocation doesn't match expected one.
7689         VMA_VALIDATE(subAlloc.offset == calculatedOffset);
7690 
7691         const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
7692         // Two adjacent free suballocations are invalid. They should be merged.
7693         VMA_VALIDATE(!prevFree || !currFree);
7694 
7695         VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE));
7696 
7697         if(currFree)
7698         {
7699             calculatedSumFreeSize += subAlloc.size;
7700             ++calculatedFreeCount;
7701             if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7702             {
7703                 ++freeSuballocationsToRegister;
7704             }
7705 
7706             // Margin required between allocations - every free space must be at least that large.
7707             VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);
7708         }
7709         else
7710         {
7711             VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset);
7712             VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size);
7713 
7714             // Margin required between allocations - previous allocation must be free.
7715             VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);
7716         }
7717 
7718         calculatedOffset += subAlloc.size;
7719         prevFree = currFree;
7720     }
7721 
7722     // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
7723     // match expected one.
7724     VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
7725 
7726     VkDeviceSize lastSize = 0;
7727     for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
7728     {
7729         VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
7730 
7731         // Only free suballocations can be registered in m_FreeSuballocationsBySize.
7732         VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
7733         // They must be sorted by size ascending.
7734         VMA_VALIDATE(suballocItem->size >= lastSize);
7735 
7736         lastSize = suballocItem->size;
7737     }
7738 
7739     // Check if totals match calculacted values.
7740     VMA_VALIDATE(ValidateFreeSuballocationList());
7741     VMA_VALIDATE(calculatedOffset == GetSize());
7742     VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
7743     VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
7744 
7745     return true;
7746 }
7747 
GetUnusedRangeSizeMax()7748 VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
7749 {
7750     if(!m_FreeSuballocationsBySize.empty())
7751     {
7752         return m_FreeSuballocationsBySize.back()->size;
7753     }
7754     else
7755     {
7756         return 0;
7757     }
7758 }
7759 
IsEmpty()7760 bool VmaBlockMetadata_Generic::IsEmpty() const
7761 {
7762     return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
7763 }
7764 
CalcAllocationStatInfo(VmaStatInfo & outInfo)7765 void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
7766 {
7767     outInfo.blockCount = 1;
7768 
7769     const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
7770     outInfo.allocationCount = rangeCount - m_FreeCount;
7771     outInfo.unusedRangeCount = m_FreeCount;
7772 
7773     outInfo.unusedBytes = m_SumFreeSize;
7774     outInfo.usedBytes = GetSize() - outInfo.unusedBytes;
7775 
7776     outInfo.allocationSizeMin = UINT64_MAX;
7777     outInfo.allocationSizeMax = 0;
7778     outInfo.unusedRangeSizeMin = UINT64_MAX;
7779     outInfo.unusedRangeSizeMax = 0;
7780 
7781     for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7782         suballocItem != m_Suballocations.cend();
7783         ++suballocItem)
7784     {
7785         const VmaSuballocation& suballoc = *suballocItem;
7786         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
7787         {
7788             outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
7789             outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
7790         }
7791         else
7792         {
7793             outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
7794             outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
7795         }
7796     }
7797 }
7798 
AddPoolStats(VmaPoolStats & inoutStats)7799 void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
7800 {
7801     const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
7802 
7803     inoutStats.size += GetSize();
7804     inoutStats.unusedSize += m_SumFreeSize;
7805     inoutStats.allocationCount += rangeCount - m_FreeCount;
7806     inoutStats.unusedRangeCount += m_FreeCount;
7807     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
7808 }
7809 
7810 #if VMA_STATS_STRING_ENABLED
7811 
PrintDetailedMap(class VmaJsonWriter & json)7812 void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
7813 {
7814     PrintDetailedMap_Begin(json,
7815         m_SumFreeSize, // unusedBytes
7816         m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
7817         m_FreeCount); // unusedRangeCount
7818 
7819     size_t i = 0;
7820     for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7821         suballocItem != m_Suballocations.cend();
7822         ++suballocItem, ++i)
7823     {
7824         if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
7825         {
7826             PrintDetailedMap_UnusedRange(json, suballocItem->offset, suballocItem->size);
7827         }
7828         else
7829         {
7830             PrintDetailedMap_Allocation(json, suballocItem->offset, suballocItem->hAllocation);
7831         }
7832     }
7833 
7834     PrintDetailedMap_End(json);
7835 }
7836 
7837 #endif // #if VMA_STATS_STRING_ENABLED
7838 
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)7839 bool VmaBlockMetadata_Generic::CreateAllocationRequest(
7840     uint32_t currentFrameIndex,
7841     uint32_t frameInUseCount,
7842     VkDeviceSize bufferImageGranularity,
7843     VkDeviceSize allocSize,
7844     VkDeviceSize allocAlignment,
7845     bool upperAddress,
7846     VmaSuballocationType allocType,
7847     bool canMakeOtherLost,
7848     uint32_t strategy,
7849     VmaAllocationRequest* pAllocationRequest)
7850 {
7851     VMA_ASSERT(allocSize > 0);
7852     VMA_ASSERT(!upperAddress);
7853     VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
7854     VMA_ASSERT(pAllocationRequest != VMA_NULL);
7855     VMA_HEAVY_ASSERT(Validate());
7856 
7857     // There is not enough total free space in this block to fullfill the request: Early return.
7858     if(canMakeOtherLost == false &&
7859         m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN)
7860     {
7861         return false;
7862     }
7863 
7864     // New algorithm, efficiently searching freeSuballocationsBySize.
7865     const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
7866     if(freeSuballocCount > 0)
7867     {
7868         if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
7869         {
7870             // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN.
7871             VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
7872                 m_FreeSuballocationsBySize.data(),
7873                 m_FreeSuballocationsBySize.data() + freeSuballocCount,
7874                 allocSize + 2 * VMA_DEBUG_MARGIN,
7875                 VmaSuballocationItemSizeLess());
7876             size_t index = it - m_FreeSuballocationsBySize.data();
7877             for(; index < freeSuballocCount; ++index)
7878             {
7879                 if(CheckAllocation(
7880                     currentFrameIndex,
7881                     frameInUseCount,
7882                     bufferImageGranularity,
7883                     allocSize,
7884                     allocAlignment,
7885                     allocType,
7886                     m_FreeSuballocationsBySize[index],
7887                     false, // canMakeOtherLost
7888                     &pAllocationRequest->offset,
7889                     &pAllocationRequest->itemsToMakeLostCount,
7890                     &pAllocationRequest->sumFreeSize,
7891                     &pAllocationRequest->sumItemSize))
7892                 {
7893                     pAllocationRequest->item = m_FreeSuballocationsBySize[index];
7894                     return true;
7895                 }
7896             }
7897         }
7898         else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
7899         {
7900             for(VmaSuballocationList::iterator it = m_Suballocations.begin();
7901                 it != m_Suballocations.end();
7902                 ++it)
7903             {
7904                 if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
7905                     currentFrameIndex,
7906                     frameInUseCount,
7907                     bufferImageGranularity,
7908                     allocSize,
7909                     allocAlignment,
7910                     allocType,
7911                     it,
7912                     false, // canMakeOtherLost
7913                     &pAllocationRequest->offset,
7914                     &pAllocationRequest->itemsToMakeLostCount,
7915                     &pAllocationRequest->sumFreeSize,
7916                     &pAllocationRequest->sumItemSize))
7917                 {
7918                     pAllocationRequest->item = it;
7919                     return true;
7920                 }
7921             }
7922         }
7923         else // WORST_FIT, FIRST_FIT
7924         {
7925             // Search staring from biggest suballocations.
7926             for(size_t index = freeSuballocCount; index--; )
7927             {
7928                 if(CheckAllocation(
7929                     currentFrameIndex,
7930                     frameInUseCount,
7931                     bufferImageGranularity,
7932                     allocSize,
7933                     allocAlignment,
7934                     allocType,
7935                     m_FreeSuballocationsBySize[index],
7936                     false, // canMakeOtherLost
7937                     &pAllocationRequest->offset,
7938                     &pAllocationRequest->itemsToMakeLostCount,
7939                     &pAllocationRequest->sumFreeSize,
7940                     &pAllocationRequest->sumItemSize))
7941                 {
7942                     pAllocationRequest->item = m_FreeSuballocationsBySize[index];
7943                     return true;
7944                 }
7945             }
7946         }
7947     }
7948 
7949     if(canMakeOtherLost)
7950     {
7951         // Brute-force algorithm. TODO: Come up with something better.
7952 
7953         pAllocationRequest->sumFreeSize = VK_WHOLE_SIZE;
7954         pAllocationRequest->sumItemSize = VK_WHOLE_SIZE;
7955 
7956         VmaAllocationRequest tmpAllocRequest = {};
7957         for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
7958             suballocIt != m_Suballocations.end();
7959             ++suballocIt)
7960         {
7961             if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
7962                 suballocIt->hAllocation->CanBecomeLost())
7963             {
7964                 if(CheckAllocation(
7965                     currentFrameIndex,
7966                     frameInUseCount,
7967                     bufferImageGranularity,
7968                     allocSize,
7969                     allocAlignment,
7970                     allocType,
7971                     suballocIt,
7972                     canMakeOtherLost,
7973                     &tmpAllocRequest.offset,
7974                     &tmpAllocRequest.itemsToMakeLostCount,
7975                     &tmpAllocRequest.sumFreeSize,
7976                     &tmpAllocRequest.sumItemSize))
7977                 {
7978                     tmpAllocRequest.item = suballocIt;
7979 
7980                     if(tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost() ||
7981                         strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
7982                     {
7983                         *pAllocationRequest = tmpAllocRequest;
7984                     }
7985                 }
7986             }
7987         }
7988 
7989         if(pAllocationRequest->sumItemSize != VK_WHOLE_SIZE)
7990         {
7991             return true;
7992         }
7993     }
7994 
7995     return false;
7996 }
7997 
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)7998 bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
7999     uint32_t currentFrameIndex,
8000     uint32_t frameInUseCount,
8001     VmaAllocationRequest* pAllocationRequest)
8002 {
8003     while(pAllocationRequest->itemsToMakeLostCount > 0)
8004     {
8005         if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
8006         {
8007             ++pAllocationRequest->item;
8008         }
8009         VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
8010         VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
8011         VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
8012         if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
8013         {
8014             pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
8015             --pAllocationRequest->itemsToMakeLostCount;
8016         }
8017         else
8018         {
8019             return false;
8020         }
8021     }
8022 
8023     VMA_HEAVY_ASSERT(Validate());
8024     VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
8025     VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
8026 
8027     return true;
8028 }
8029 
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)8030 uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
8031 {
8032     uint32_t lostAllocationCount = 0;
8033     for(VmaSuballocationList::iterator it = m_Suballocations.begin();
8034         it != m_Suballocations.end();
8035         ++it)
8036     {
8037         if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
8038             it->hAllocation->CanBecomeLost() &&
8039             it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
8040         {
8041             it = FreeSuballocation(it);
8042             ++lostAllocationCount;
8043         }
8044     }
8045     return lostAllocationCount;
8046 }
8047 
CheckCorruption(const void * pBlockData)8048 VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
8049 {
8050     for(VmaSuballocationList::iterator it = m_Suballocations.begin();
8051         it != m_Suballocations.end();
8052         ++it)
8053     {
8054         if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
8055         {
8056             if(!VmaValidateMagicValue(pBlockData, it->offset - VMA_DEBUG_MARGIN))
8057             {
8058                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
8059                 return VK_ERROR_VALIDATION_FAILED_EXT;
8060             }
8061             if(!VmaValidateMagicValue(pBlockData, it->offset + it->size))
8062             {
8063                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
8064                 return VK_ERROR_VALIDATION_FAILED_EXT;
8065             }
8066         }
8067     }
8068 
8069     return VK_SUCCESS;
8070 }
8071 
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,bool upperAddress,VmaAllocation hAllocation)8072 void VmaBlockMetadata_Generic::Alloc(
8073     const VmaAllocationRequest& request,
8074     VmaSuballocationType type,
8075     VkDeviceSize allocSize,
8076     bool upperAddress,
8077     VmaAllocation hAllocation)
8078 {
8079     VMA_ASSERT(!upperAddress);
8080     VMA_ASSERT(request.item != m_Suballocations.end());
8081     VmaSuballocation& suballoc = *request.item;
8082     // Given suballocation is a free block.
8083     VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8084     // Given offset is inside this suballocation.
8085     VMA_ASSERT(request.offset >= suballoc.offset);
8086     const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
8087     VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
8088     const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
8089 
8090     // Unregister this free suballocation from m_FreeSuballocationsBySize and update
8091     // it to become used.
8092     UnregisterFreeSuballocation(request.item);
8093 
8094     suballoc.offset = request.offset;
8095     suballoc.size = allocSize;
8096     suballoc.type = type;
8097     suballoc.hAllocation = hAllocation;
8098 
8099     // If there are any free bytes remaining at the end, insert new free suballocation after current one.
8100     if(paddingEnd)
8101     {
8102         VmaSuballocation paddingSuballoc = {};
8103         paddingSuballoc.offset = request.offset + allocSize;
8104         paddingSuballoc.size = paddingEnd;
8105         paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8106         VmaSuballocationList::iterator next = request.item;
8107         ++next;
8108         const VmaSuballocationList::iterator paddingEndItem =
8109             m_Suballocations.insert(next, paddingSuballoc);
8110         RegisterFreeSuballocation(paddingEndItem);
8111     }
8112 
8113     // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
8114     if(paddingBegin)
8115     {
8116         VmaSuballocation paddingSuballoc = {};
8117         paddingSuballoc.offset = request.offset - paddingBegin;
8118         paddingSuballoc.size = paddingBegin;
8119         paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8120         const VmaSuballocationList::iterator paddingBeginItem =
8121             m_Suballocations.insert(request.item, paddingSuballoc);
8122         RegisterFreeSuballocation(paddingBeginItem);
8123     }
8124 
8125     // Update totals.
8126     m_FreeCount = m_FreeCount - 1;
8127     if(paddingBegin > 0)
8128     {
8129         ++m_FreeCount;
8130     }
8131     if(paddingEnd > 0)
8132     {
8133         ++m_FreeCount;
8134     }
8135     m_SumFreeSize -= allocSize;
8136 }
8137 
Free(const VmaAllocation allocation)8138 void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation)
8139 {
8140     for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
8141         suballocItem != m_Suballocations.end();
8142         ++suballocItem)
8143     {
8144         VmaSuballocation& suballoc = *suballocItem;
8145         if(suballoc.hAllocation == allocation)
8146         {
8147             FreeSuballocation(suballocItem);
8148             VMA_HEAVY_ASSERT(Validate());
8149             return;
8150         }
8151     }
8152     VMA_ASSERT(0 && "Not found!");
8153 }
8154 
FreeAtOffset(VkDeviceSize offset)8155 void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
8156 {
8157     for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
8158         suballocItem != m_Suballocations.end();
8159         ++suballocItem)
8160     {
8161         VmaSuballocation& suballoc = *suballocItem;
8162         if(suballoc.offset == offset)
8163         {
8164             FreeSuballocation(suballocItem);
8165             return;
8166         }
8167     }
8168     VMA_ASSERT(0 && "Not found!");
8169 }
8170 
ResizeAllocation(const VmaAllocation alloc,VkDeviceSize newSize)8171 bool VmaBlockMetadata_Generic::ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize)
8172 {
8173     typedef VmaSuballocationList::iterator iter_type;
8174     for(iter_type suballocItem = m_Suballocations.begin();
8175         suballocItem != m_Suballocations.end();
8176         ++suballocItem)
8177     {
8178         VmaSuballocation& suballoc = *suballocItem;
8179         if(suballoc.hAllocation == alloc)
8180         {
8181             iter_type nextItem = suballocItem;
8182             ++nextItem;
8183 
8184             // Should have been ensured on higher level.
8185             VMA_ASSERT(newSize != alloc->GetSize() && newSize > 0);
8186 
8187             // Shrinking.
8188             if(newSize < alloc->GetSize())
8189             {
8190                 const VkDeviceSize sizeDiff = suballoc.size - newSize;
8191 
8192                 // There is next item.
8193                 if(nextItem != m_Suballocations.end())
8194                 {
8195                     // Next item is free.
8196                     if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8197                     {
8198                         // Grow this next item backward.
8199                         UnregisterFreeSuballocation(nextItem);
8200                         nextItem->offset -= sizeDiff;
8201                         nextItem->size += sizeDiff;
8202                         RegisterFreeSuballocation(nextItem);
8203                     }
8204                     // Next item is not free.
8205                     else
8206                     {
8207                         // Create free item after current one.
8208                         VmaSuballocation newFreeSuballoc;
8209                         newFreeSuballoc.hAllocation = VK_NULL_HANDLE;
8210                         newFreeSuballoc.offset = suballoc.offset + newSize;
8211                         newFreeSuballoc.size = sizeDiff;
8212                         newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8213                         iter_type newFreeSuballocIt = m_Suballocations.insert(nextItem, newFreeSuballoc);
8214                         RegisterFreeSuballocation(newFreeSuballocIt);
8215 
8216                         ++m_FreeCount;
8217                     }
8218                 }
8219                 // This is the last item.
8220                 else
8221                 {
8222                     // Create free item at the end.
8223                     VmaSuballocation newFreeSuballoc;
8224                     newFreeSuballoc.hAllocation = VK_NULL_HANDLE;
8225                     newFreeSuballoc.offset = suballoc.offset + newSize;
8226                     newFreeSuballoc.size = sizeDiff;
8227                     newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8228                     m_Suballocations.push_back(newFreeSuballoc);
8229 
8230                     iter_type newFreeSuballocIt = m_Suballocations.end();
8231                     RegisterFreeSuballocation(--newFreeSuballocIt);
8232 
8233                     ++m_FreeCount;
8234                 }
8235 
8236                 suballoc.size = newSize;
8237                 m_SumFreeSize += sizeDiff;
8238             }
8239             // Growing.
8240             else
8241             {
8242                 const VkDeviceSize sizeDiff = newSize - suballoc.size;
8243 
8244                 // There is next item.
8245                 if(nextItem != m_Suballocations.end())
8246                 {
8247                     // Next item is free.
8248                     if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8249                     {
8250                         // There is not enough free space, including margin.
8251                         if(nextItem->size < sizeDiff + VMA_DEBUG_MARGIN)
8252                         {
8253                             return false;
8254                         }
8255 
8256                         // There is more free space than required.
8257                         if(nextItem->size > sizeDiff)
8258                         {
8259                             // Move and shrink this next item.
8260                             UnregisterFreeSuballocation(nextItem);
8261                             nextItem->offset += sizeDiff;
8262                             nextItem->size -= sizeDiff;
8263                             RegisterFreeSuballocation(nextItem);
8264                         }
8265                         // There is exactly the amount of free space required.
8266                         else
8267                         {
8268                             // Remove this next free item.
8269                             UnregisterFreeSuballocation(nextItem);
8270                             m_Suballocations.erase(nextItem);
8271                             --m_FreeCount;
8272                         }
8273                     }
8274                     // Next item is not free - there is no space to grow.
8275                     else
8276                     {
8277                         return false;
8278                     }
8279                 }
8280                 // This is the last item - there is no space to grow.
8281                 else
8282                 {
8283                     return false;
8284                 }
8285 
8286                 suballoc.size = newSize;
8287                 m_SumFreeSize -= sizeDiff;
8288             }
8289 
8290             // We cannot call Validate() here because alloc object is updated to new size outside of this call.
8291             return true;
8292         }
8293     }
8294     VMA_ASSERT(0 && "Not found!");
8295     return false;
8296 }
8297 
ValidateFreeSuballocationList()8298 bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
8299 {
8300     VkDeviceSize lastSize = 0;
8301     for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
8302     {
8303         const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
8304 
8305         VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
8306         VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
8307         VMA_VALIDATE(it->size >= lastSize);
8308         lastSize = it->size;
8309     }
8310     return true;
8311 }
8312 
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)8313 bool VmaBlockMetadata_Generic::CheckAllocation(
8314     uint32_t currentFrameIndex,
8315     uint32_t frameInUseCount,
8316     VkDeviceSize bufferImageGranularity,
8317     VkDeviceSize allocSize,
8318     VkDeviceSize allocAlignment,
8319     VmaSuballocationType allocType,
8320     VmaSuballocationList::const_iterator suballocItem,
8321     bool canMakeOtherLost,
8322     VkDeviceSize* pOffset,
8323     size_t* itemsToMakeLostCount,
8324     VkDeviceSize* pSumFreeSize,
8325     VkDeviceSize* pSumItemSize) const
8326 {
8327     VMA_ASSERT(allocSize > 0);
8328     VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
8329     VMA_ASSERT(suballocItem != m_Suballocations.cend());
8330     VMA_ASSERT(pOffset != VMA_NULL);
8331 
8332     *itemsToMakeLostCount = 0;
8333     *pSumFreeSize = 0;
8334     *pSumItemSize = 0;
8335 
8336     if(canMakeOtherLost)
8337     {
8338         if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8339         {
8340             *pSumFreeSize = suballocItem->size;
8341         }
8342         else
8343         {
8344             if(suballocItem->hAllocation->CanBecomeLost() &&
8345                 suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8346             {
8347                 ++*itemsToMakeLostCount;
8348                 *pSumItemSize = suballocItem->size;
8349             }
8350             else
8351             {
8352                 return false;
8353             }
8354         }
8355 
8356         // Remaining size is too small for this request: Early return.
8357         if(GetSize() - suballocItem->offset < allocSize)
8358         {
8359             return false;
8360         }
8361 
8362         // Start from offset equal to beginning of this suballocation.
8363         *pOffset = suballocItem->offset;
8364 
8365         // Apply VMA_DEBUG_MARGIN at the beginning.
8366         if(VMA_DEBUG_MARGIN > 0)
8367         {
8368             *pOffset += VMA_DEBUG_MARGIN;
8369         }
8370 
8371         // Apply alignment.
8372         *pOffset = VmaAlignUp(*pOffset, allocAlignment);
8373 
8374         // Check previous suballocations for BufferImageGranularity conflicts.
8375         // Make bigger alignment if necessary.
8376         if(bufferImageGranularity > 1)
8377         {
8378             bool bufferImageGranularityConflict = false;
8379             VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
8380             while(prevSuballocItem != m_Suballocations.cbegin())
8381             {
8382                 --prevSuballocItem;
8383                 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
8384                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
8385                 {
8386                     if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
8387                     {
8388                         bufferImageGranularityConflict = true;
8389                         break;
8390                     }
8391                 }
8392                 else
8393                     // Already on previous page.
8394                     break;
8395             }
8396             if(bufferImageGranularityConflict)
8397             {
8398                 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
8399             }
8400         }
8401 
8402         // Now that we have final *pOffset, check if we are past suballocItem.
8403         // If yes, return false - this function should be called for another suballocItem as starting point.
8404         if(*pOffset >= suballocItem->offset + suballocItem->size)
8405         {
8406             return false;
8407         }
8408 
8409         // Calculate padding at the beginning based on current offset.
8410         const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
8411 
8412         // Calculate required margin at the end.
8413         const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
8414 
8415         const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
8416         // Another early return check.
8417         if(suballocItem->offset + totalSize > GetSize())
8418         {
8419             return false;
8420         }
8421 
8422         // Advance lastSuballocItem until desired size is reached.
8423         // Update itemsToMakeLostCount.
8424         VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
8425         if(totalSize > suballocItem->size)
8426         {
8427             VkDeviceSize remainingSize = totalSize - suballocItem->size;
8428             while(remainingSize > 0)
8429             {
8430                 ++lastSuballocItem;
8431                 if(lastSuballocItem == m_Suballocations.cend())
8432                 {
8433                     return false;
8434                 }
8435                 if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8436                 {
8437                     *pSumFreeSize += lastSuballocItem->size;
8438                 }
8439                 else
8440                 {
8441                     VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
8442                     if(lastSuballocItem->hAllocation->CanBecomeLost() &&
8443                         lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8444                     {
8445                         ++*itemsToMakeLostCount;
8446                         *pSumItemSize += lastSuballocItem->size;
8447                     }
8448                     else
8449                     {
8450                         return false;
8451                     }
8452                 }
8453                 remainingSize = (lastSuballocItem->size < remainingSize) ?
8454                     remainingSize - lastSuballocItem->size : 0;
8455             }
8456         }
8457 
8458         // Check next suballocations for BufferImageGranularity conflicts.
8459         // If conflict exists, we must mark more allocations lost or fail.
8460         if(bufferImageGranularity > 1)
8461         {
8462             VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
8463             ++nextSuballocItem;
8464             while(nextSuballocItem != m_Suballocations.cend())
8465             {
8466                 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
8467                 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8468                 {
8469                     if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
8470                     {
8471                         VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
8472                         if(nextSuballoc.hAllocation->CanBecomeLost() &&
8473                             nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8474                         {
8475                             ++*itemsToMakeLostCount;
8476                         }
8477                         else
8478                         {
8479                             return false;
8480                         }
8481                     }
8482                 }
8483                 else
8484                 {
8485                     // Already on next page.
8486                     break;
8487                 }
8488                 ++nextSuballocItem;
8489             }
8490         }
8491     }
8492     else
8493     {
8494         const VmaSuballocation& suballoc = *suballocItem;
8495         VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8496 
8497         *pSumFreeSize = suballoc.size;
8498 
8499         // Size of this suballocation is too small for this request: Early return.
8500         if(suballoc.size < allocSize)
8501         {
8502             return false;
8503         }
8504 
8505         // Start from offset equal to beginning of this suballocation.
8506         *pOffset = suballoc.offset;
8507 
8508         // Apply VMA_DEBUG_MARGIN at the beginning.
8509         if(VMA_DEBUG_MARGIN > 0)
8510         {
8511             *pOffset += VMA_DEBUG_MARGIN;
8512         }
8513 
8514         // Apply alignment.
8515         *pOffset = VmaAlignUp(*pOffset, allocAlignment);
8516 
8517         // Check previous suballocations for BufferImageGranularity conflicts.
8518         // Make bigger alignment if necessary.
8519         if(bufferImageGranularity > 1)
8520         {
8521             bool bufferImageGranularityConflict = false;
8522             VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
8523             while(prevSuballocItem != m_Suballocations.cbegin())
8524             {
8525                 --prevSuballocItem;
8526                 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
8527                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
8528                 {
8529                     if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
8530                     {
8531                         bufferImageGranularityConflict = true;
8532                         break;
8533                     }
8534                 }
8535                 else
8536                     // Already on previous page.
8537                     break;
8538             }
8539             if(bufferImageGranularityConflict)
8540             {
8541                 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
8542             }
8543         }
8544 
8545         // Calculate padding at the beginning based on current offset.
8546         const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
8547 
8548         // Calculate required margin at the end.
8549         const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
8550 
8551         // Fail if requested size plus margin before and after is bigger than size of this suballocation.
8552         if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
8553         {
8554             return false;
8555         }
8556 
8557         // Check next suballocations for BufferImageGranularity conflicts.
8558         // If conflict exists, allocation cannot be made here.
8559         if(bufferImageGranularity > 1)
8560         {
8561             VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
8562             ++nextSuballocItem;
8563             while(nextSuballocItem != m_Suballocations.cend())
8564             {
8565                 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
8566                 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8567                 {
8568                     if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
8569                     {
8570                         return false;
8571                     }
8572                 }
8573                 else
8574                 {
8575                     // Already on next page.
8576                     break;
8577                 }
8578                 ++nextSuballocItem;
8579             }
8580         }
8581     }
8582 
8583     // All tests passed: Success. pOffset is already filled.
8584     return true;
8585 }
8586 
MergeFreeWithNext(VmaSuballocationList::iterator item)8587 void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
8588 {
8589     VMA_ASSERT(item != m_Suballocations.end());
8590     VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8591 
8592     VmaSuballocationList::iterator nextItem = item;
8593     ++nextItem;
8594     VMA_ASSERT(nextItem != m_Suballocations.end());
8595     VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
8596 
8597     item->size += nextItem->size;
8598     --m_FreeCount;
8599     m_Suballocations.erase(nextItem);
8600 }
8601 
FreeSuballocation(VmaSuballocationList::iterator suballocItem)8602 VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
8603 {
8604     // Change this suballocation to be marked as free.
8605     VmaSuballocation& suballoc = *suballocItem;
8606     suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8607     suballoc.hAllocation = VK_NULL_HANDLE;
8608 
8609     // Update totals.
8610     ++m_FreeCount;
8611     m_SumFreeSize += suballoc.size;
8612 
8613     // Merge with previous and/or next suballocation if it's also free.
8614     bool mergeWithNext = false;
8615     bool mergeWithPrev = false;
8616 
8617     VmaSuballocationList::iterator nextItem = suballocItem;
8618     ++nextItem;
8619     if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
8620     {
8621         mergeWithNext = true;
8622     }
8623 
8624     VmaSuballocationList::iterator prevItem = suballocItem;
8625     if(suballocItem != m_Suballocations.begin())
8626     {
8627         --prevItem;
8628         if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8629         {
8630             mergeWithPrev = true;
8631         }
8632     }
8633 
8634     if(mergeWithNext)
8635     {
8636         UnregisterFreeSuballocation(nextItem);
8637         MergeFreeWithNext(suballocItem);
8638     }
8639 
8640     if(mergeWithPrev)
8641     {
8642         UnregisterFreeSuballocation(prevItem);
8643         MergeFreeWithNext(prevItem);
8644         RegisterFreeSuballocation(prevItem);
8645         return prevItem;
8646     }
8647     else
8648     {
8649         RegisterFreeSuballocation(suballocItem);
8650         return suballocItem;
8651     }
8652 }
8653 
RegisterFreeSuballocation(VmaSuballocationList::iterator item)8654 void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
8655 {
8656     VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8657     VMA_ASSERT(item->size > 0);
8658 
8659     // You may want to enable this validation at the beginning or at the end of
8660     // this function, depending on what do you want to check.
8661     VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8662 
8663     if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8664     {
8665         if(m_FreeSuballocationsBySize.empty())
8666         {
8667             m_FreeSuballocationsBySize.push_back(item);
8668         }
8669         else
8670         {
8671             VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
8672         }
8673     }
8674 
8675     //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8676 }
8677 
8678 
UnregisterFreeSuballocation(VmaSuballocationList::iterator item)8679 void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
8680 {
8681     VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8682     VMA_ASSERT(item->size > 0);
8683 
8684     // You may want to enable this validation at the beginning or at the end of
8685     // this function, depending on what do you want to check.
8686     VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8687 
8688     if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8689     {
8690         VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
8691             m_FreeSuballocationsBySize.data(),
8692             m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
8693             item,
8694             VmaSuballocationItemSizeLess());
8695         for(size_t index = it - m_FreeSuballocationsBySize.data();
8696             index < m_FreeSuballocationsBySize.size();
8697             ++index)
8698         {
8699             if(m_FreeSuballocationsBySize[index] == item)
8700             {
8701                 VmaVectorRemove(m_FreeSuballocationsBySize, index);
8702                 return;
8703             }
8704             VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
8705         }
8706         VMA_ASSERT(0 && "Not found.");
8707     }
8708 
8709     //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8710 }
8711 
IsBufferImageGranularityConflictPossible(VkDeviceSize bufferImageGranularity,VmaSuballocationType & inOutPrevSuballocType)8712 bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
8713     VkDeviceSize bufferImageGranularity,
8714     VmaSuballocationType& inOutPrevSuballocType) const
8715 {
8716     if(bufferImageGranularity == 1 || IsEmpty())
8717     {
8718         return false;
8719     }
8720 
8721     VkDeviceSize minAlignment = VK_WHOLE_SIZE;
8722     bool typeConflictFound = false;
8723     for(VmaSuballocationList::const_iterator it = m_Suballocations.cbegin();
8724         it != m_Suballocations.cend();
8725         ++it)
8726     {
8727         const VmaSuballocationType suballocType = it->type;
8728         if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)
8729         {
8730             minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment());
8731             if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))
8732             {
8733                 typeConflictFound = true;
8734             }
8735             inOutPrevSuballocType = suballocType;
8736         }
8737     }
8738 
8739     return typeConflictFound || minAlignment >= bufferImageGranularity;
8740 }
8741 
8742 ////////////////////////////////////////////////////////////////////////////////
8743 // class VmaBlockMetadata_Linear
8744 
VmaBlockMetadata_Linear(VmaAllocator hAllocator)8745 VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) :
8746     VmaBlockMetadata(hAllocator),
8747     m_SumFreeSize(0),
8748     m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8749     m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8750     m_1stVectorIndex(0),
8751     m_2ndVectorMode(SECOND_VECTOR_EMPTY),
8752     m_1stNullItemsBeginCount(0),
8753     m_1stNullItemsMiddleCount(0),
8754     m_2ndNullItemsCount(0)
8755 {
8756 }
8757 
~VmaBlockMetadata_Linear()8758 VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
8759 {
8760 }
8761 
Init(VkDeviceSize size)8762 void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
8763 {
8764     VmaBlockMetadata::Init(size);
8765     m_SumFreeSize = size;
8766 }
8767 
Validate()8768 bool VmaBlockMetadata_Linear::Validate() const
8769 {
8770     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8771     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8772 
8773     VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
8774     VMA_VALIDATE(!suballocations1st.empty() ||
8775         suballocations2nd.empty() ||
8776         m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
8777 
8778     if(!suballocations1st.empty())
8779     {
8780         // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
8781         VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE);
8782         // Null item at the end should be just pop_back().
8783         VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE);
8784     }
8785     if(!suballocations2nd.empty())
8786     {
8787         // Null item at the end should be just pop_back().
8788         VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE);
8789     }
8790 
8791     VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
8792     VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
8793 
8794     VkDeviceSize sumUsedSize = 0;
8795     const size_t suballoc1stCount = suballocations1st.size();
8796     VkDeviceSize offset = VMA_DEBUG_MARGIN;
8797 
8798     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8799     {
8800         const size_t suballoc2ndCount = suballocations2nd.size();
8801         size_t nullItem2ndCount = 0;
8802         for(size_t i = 0; i < suballoc2ndCount; ++i)
8803         {
8804             const VmaSuballocation& suballoc = suballocations2nd[i];
8805             const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8806 
8807             VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8808             VMA_VALIDATE(suballoc.offset >= offset);
8809 
8810             if(!currFree)
8811             {
8812                 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8813                 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8814                 sumUsedSize += suballoc.size;
8815             }
8816             else
8817             {
8818                 ++nullItem2ndCount;
8819             }
8820 
8821             offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8822         }
8823 
8824         VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
8825     }
8826 
8827     for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
8828     {
8829         const VmaSuballocation& suballoc = suballocations1st[i];
8830         VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
8831             suballoc.hAllocation == VK_NULL_HANDLE);
8832     }
8833 
8834     size_t nullItem1stCount = m_1stNullItemsBeginCount;
8835 
8836     for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
8837     {
8838         const VmaSuballocation& suballoc = suballocations1st[i];
8839         const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8840 
8841         VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8842         VMA_VALIDATE(suballoc.offset >= offset);
8843         VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
8844 
8845         if(!currFree)
8846         {
8847             VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8848             VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8849             sumUsedSize += suballoc.size;
8850         }
8851         else
8852         {
8853             ++nullItem1stCount;
8854         }
8855 
8856         offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8857     }
8858     VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
8859 
8860     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
8861     {
8862         const size_t suballoc2ndCount = suballocations2nd.size();
8863         size_t nullItem2ndCount = 0;
8864         for(size_t i = suballoc2ndCount; i--; )
8865         {
8866             const VmaSuballocation& suballoc = suballocations2nd[i];
8867             const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8868 
8869             VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8870             VMA_VALIDATE(suballoc.offset >= offset);
8871 
8872             if(!currFree)
8873             {
8874                 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8875                 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8876                 sumUsedSize += suballoc.size;
8877             }
8878             else
8879             {
8880                 ++nullItem2ndCount;
8881             }
8882 
8883             offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8884         }
8885 
8886         VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
8887     }
8888 
8889     VMA_VALIDATE(offset <= GetSize());
8890     VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
8891 
8892     return true;
8893 }
8894 
GetAllocationCount()8895 size_t VmaBlockMetadata_Linear::GetAllocationCount() const
8896 {
8897     return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) +
8898         AccessSuballocations2nd().size() - m_2ndNullItemsCount;
8899 }
8900 
GetUnusedRangeSizeMax()8901 VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
8902 {
8903     const VkDeviceSize size = GetSize();
8904 
8905     /*
8906     We don't consider gaps inside allocation vectors with freed allocations because
8907     they are not suitable for reuse in linear allocator. We consider only space that
8908     is available for new allocations.
8909     */
8910     if(IsEmpty())
8911     {
8912         return size;
8913     }
8914 
8915     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8916 
8917     switch(m_2ndVectorMode)
8918     {
8919     case SECOND_VECTOR_EMPTY:
8920         /*
8921         Available space is after end of 1st, as well as before beginning of 1st (which
8922         whould make it a ring buffer).
8923         */
8924         {
8925             const size_t suballocations1stCount = suballocations1st.size();
8926             VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
8927             const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
8928             const VmaSuballocation& lastSuballoc  = suballocations1st[suballocations1stCount - 1];
8929             return VMA_MAX(
8930                 firstSuballoc.offset,
8931                 size - (lastSuballoc.offset + lastSuballoc.size));
8932         }
8933         break;
8934 
8935     case SECOND_VECTOR_RING_BUFFER:
8936         /*
8937         Available space is only between end of 2nd and beginning of 1st.
8938         */
8939         {
8940             const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8941             const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
8942             const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
8943             return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
8944         }
8945         break;
8946 
8947     case SECOND_VECTOR_DOUBLE_STACK:
8948         /*
8949         Available space is only between end of 1st and top of 2nd.
8950         */
8951         {
8952             const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8953             const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
8954             const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
8955             return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
8956         }
8957         break;
8958 
8959     default:
8960         VMA_ASSERT(0);
8961         return 0;
8962     }
8963 }
8964 
CalcAllocationStatInfo(VmaStatInfo & outInfo)8965 void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
8966 {
8967     const VkDeviceSize size = GetSize();
8968     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8969     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8970     const size_t suballoc1stCount = suballocations1st.size();
8971     const size_t suballoc2ndCount = suballocations2nd.size();
8972 
8973     outInfo.blockCount = 1;
8974     outInfo.allocationCount = (uint32_t)GetAllocationCount();
8975     outInfo.unusedRangeCount = 0;
8976     outInfo.usedBytes = 0;
8977     outInfo.allocationSizeMin = UINT64_MAX;
8978     outInfo.allocationSizeMax = 0;
8979     outInfo.unusedRangeSizeMin = UINT64_MAX;
8980     outInfo.unusedRangeSizeMax = 0;
8981 
8982     VkDeviceSize lastOffset = 0;
8983 
8984     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8985     {
8986         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
8987         size_t nextAlloc2ndIndex = 0;
8988         while(lastOffset < freeSpace2ndTo1stEnd)
8989         {
8990             // Find next non-null allocation or move nextAllocIndex to the end.
8991             while(nextAlloc2ndIndex < suballoc2ndCount &&
8992                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
8993             {
8994                 ++nextAlloc2ndIndex;
8995             }
8996 
8997             // Found non-null allocation.
8998             if(nextAlloc2ndIndex < suballoc2ndCount)
8999             {
9000                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9001 
9002                 // 1. Process free space before this allocation.
9003                 if(lastOffset < suballoc.offset)
9004                 {
9005                     // There is free space from lastOffset to suballoc.offset.
9006                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9007                     ++outInfo.unusedRangeCount;
9008                     outInfo.unusedBytes += unusedRangeSize;
9009                     outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9010                     outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9011                 }
9012 
9013                 // 2. Process this allocation.
9014                 // There is allocation with suballoc.offset, suballoc.size.
9015                 outInfo.usedBytes += suballoc.size;
9016                 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9017                 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
9018 
9019                 // 3. Prepare for next iteration.
9020                 lastOffset = suballoc.offset + suballoc.size;
9021                 ++nextAlloc2ndIndex;
9022             }
9023             // We are at the end.
9024             else
9025             {
9026                 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9027                 if(lastOffset < freeSpace2ndTo1stEnd)
9028                 {
9029                     const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9030                     ++outInfo.unusedRangeCount;
9031                     outInfo.unusedBytes += unusedRangeSize;
9032                     outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9033                     outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9034                }
9035 
9036                 // End of loop.
9037                 lastOffset = freeSpace2ndTo1stEnd;
9038             }
9039         }
9040     }
9041 
9042     size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9043     const VkDeviceSize freeSpace1stTo2ndEnd =
9044         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9045     while(lastOffset < freeSpace1stTo2ndEnd)
9046     {
9047         // Find next non-null allocation or move nextAllocIndex to the end.
9048         while(nextAlloc1stIndex < suballoc1stCount &&
9049             suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9050         {
9051             ++nextAlloc1stIndex;
9052         }
9053 
9054         // Found non-null allocation.
9055         if(nextAlloc1stIndex < suballoc1stCount)
9056         {
9057             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9058 
9059             // 1. Process free space before this allocation.
9060             if(lastOffset < suballoc.offset)
9061             {
9062                 // There is free space from lastOffset to suballoc.offset.
9063                 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9064                 ++outInfo.unusedRangeCount;
9065                 outInfo.unusedBytes += unusedRangeSize;
9066                 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9067                 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9068             }
9069 
9070             // 2. Process this allocation.
9071             // There is allocation with suballoc.offset, suballoc.size.
9072             outInfo.usedBytes += suballoc.size;
9073             outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9074             outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
9075 
9076             // 3. Prepare for next iteration.
9077             lastOffset = suballoc.offset + suballoc.size;
9078             ++nextAlloc1stIndex;
9079         }
9080         // We are at the end.
9081         else
9082         {
9083             // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9084             if(lastOffset < freeSpace1stTo2ndEnd)
9085             {
9086                 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9087                 ++outInfo.unusedRangeCount;
9088                 outInfo.unusedBytes += unusedRangeSize;
9089                 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9090                 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9091            }
9092 
9093             // End of loop.
9094             lastOffset = freeSpace1stTo2ndEnd;
9095         }
9096     }
9097 
9098     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9099     {
9100         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9101         while(lastOffset < size)
9102         {
9103             // Find next non-null allocation or move nextAllocIndex to the end.
9104             while(nextAlloc2ndIndex != SIZE_MAX &&
9105                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9106             {
9107                 --nextAlloc2ndIndex;
9108             }
9109 
9110             // Found non-null allocation.
9111             if(nextAlloc2ndIndex != SIZE_MAX)
9112             {
9113                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9114 
9115                 // 1. Process free space before this allocation.
9116                 if(lastOffset < suballoc.offset)
9117                 {
9118                     // There is free space from lastOffset to suballoc.offset.
9119                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9120                     ++outInfo.unusedRangeCount;
9121                     outInfo.unusedBytes += unusedRangeSize;
9122                     outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9123                     outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9124                 }
9125 
9126                 // 2. Process this allocation.
9127                 // There is allocation with suballoc.offset, suballoc.size.
9128                 outInfo.usedBytes += suballoc.size;
9129                 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9130                 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
9131 
9132                 // 3. Prepare for next iteration.
9133                 lastOffset = suballoc.offset + suballoc.size;
9134                 --nextAlloc2ndIndex;
9135             }
9136             // We are at the end.
9137             else
9138             {
9139                 // There is free space from lastOffset to size.
9140                 if(lastOffset < size)
9141                 {
9142                     const VkDeviceSize unusedRangeSize = size - lastOffset;
9143                     ++outInfo.unusedRangeCount;
9144                     outInfo.unusedBytes += unusedRangeSize;
9145                     outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9146                     outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9147                }
9148 
9149                 // End of loop.
9150                 lastOffset = size;
9151             }
9152         }
9153     }
9154 
9155     outInfo.unusedBytes = size - outInfo.usedBytes;
9156 }
9157 
AddPoolStats(VmaPoolStats & inoutStats)9158 void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
9159 {
9160     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9161     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9162     const VkDeviceSize size = GetSize();
9163     const size_t suballoc1stCount = suballocations1st.size();
9164     const size_t suballoc2ndCount = suballocations2nd.size();
9165 
9166     inoutStats.size += size;
9167 
9168     VkDeviceSize lastOffset = 0;
9169 
9170     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9171     {
9172         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9173         size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
9174         while(lastOffset < freeSpace2ndTo1stEnd)
9175         {
9176             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9177             while(nextAlloc2ndIndex < suballoc2ndCount &&
9178                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9179             {
9180                 ++nextAlloc2ndIndex;
9181             }
9182 
9183             // Found non-null allocation.
9184             if(nextAlloc2ndIndex < suballoc2ndCount)
9185             {
9186                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9187 
9188                 // 1. Process free space before this allocation.
9189                 if(lastOffset < suballoc.offset)
9190                 {
9191                     // There is free space from lastOffset to suballoc.offset.
9192                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9193                     inoutStats.unusedSize += unusedRangeSize;
9194                     ++inoutStats.unusedRangeCount;
9195                     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9196                 }
9197 
9198                 // 2. Process this allocation.
9199                 // There is allocation with suballoc.offset, suballoc.size.
9200                 ++inoutStats.allocationCount;
9201 
9202                 // 3. Prepare for next iteration.
9203                 lastOffset = suballoc.offset + suballoc.size;
9204                 ++nextAlloc2ndIndex;
9205             }
9206             // We are at the end.
9207             else
9208             {
9209                 if(lastOffset < freeSpace2ndTo1stEnd)
9210                 {
9211                     // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9212                     const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9213                     inoutStats.unusedSize += unusedRangeSize;
9214                     ++inoutStats.unusedRangeCount;
9215                     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9216                 }
9217 
9218                 // End of loop.
9219                 lastOffset = freeSpace2ndTo1stEnd;
9220             }
9221         }
9222     }
9223 
9224     size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9225     const VkDeviceSize freeSpace1stTo2ndEnd =
9226         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9227     while(lastOffset < freeSpace1stTo2ndEnd)
9228     {
9229         // Find next non-null allocation or move nextAllocIndex to the end.
9230         while(nextAlloc1stIndex < suballoc1stCount &&
9231             suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9232         {
9233             ++nextAlloc1stIndex;
9234         }
9235 
9236         // Found non-null allocation.
9237         if(nextAlloc1stIndex < suballoc1stCount)
9238         {
9239             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9240 
9241             // 1. Process free space before this allocation.
9242             if(lastOffset < suballoc.offset)
9243             {
9244                 // There is free space from lastOffset to suballoc.offset.
9245                 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9246                 inoutStats.unusedSize += unusedRangeSize;
9247                 ++inoutStats.unusedRangeCount;
9248                 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9249             }
9250 
9251             // 2. Process this allocation.
9252             // There is allocation with suballoc.offset, suballoc.size.
9253             ++inoutStats.allocationCount;
9254 
9255             // 3. Prepare for next iteration.
9256             lastOffset = suballoc.offset + suballoc.size;
9257             ++nextAlloc1stIndex;
9258         }
9259         // We are at the end.
9260         else
9261         {
9262             if(lastOffset < freeSpace1stTo2ndEnd)
9263             {
9264                 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9265                 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9266                 inoutStats.unusedSize += unusedRangeSize;
9267                 ++inoutStats.unusedRangeCount;
9268                 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9269             }
9270 
9271             // End of loop.
9272             lastOffset = freeSpace1stTo2ndEnd;
9273         }
9274     }
9275 
9276     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9277     {
9278         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9279         while(lastOffset < size)
9280         {
9281             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9282             while(nextAlloc2ndIndex != SIZE_MAX &&
9283                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9284             {
9285                 --nextAlloc2ndIndex;
9286             }
9287 
9288             // Found non-null allocation.
9289             if(nextAlloc2ndIndex != SIZE_MAX)
9290             {
9291                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9292 
9293                 // 1. Process free space before this allocation.
9294                 if(lastOffset < suballoc.offset)
9295                 {
9296                     // There is free space from lastOffset to suballoc.offset.
9297                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9298                     inoutStats.unusedSize += unusedRangeSize;
9299                     ++inoutStats.unusedRangeCount;
9300                     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9301                 }
9302 
9303                 // 2. Process this allocation.
9304                 // There is allocation with suballoc.offset, suballoc.size.
9305                 ++inoutStats.allocationCount;
9306 
9307                 // 3. Prepare for next iteration.
9308                 lastOffset = suballoc.offset + suballoc.size;
9309                 --nextAlloc2ndIndex;
9310             }
9311             // We are at the end.
9312             else
9313             {
9314                 if(lastOffset < size)
9315                 {
9316                     // There is free space from lastOffset to size.
9317                     const VkDeviceSize unusedRangeSize = size - lastOffset;
9318                     inoutStats.unusedSize += unusedRangeSize;
9319                     ++inoutStats.unusedRangeCount;
9320                     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9321                 }
9322 
9323                 // End of loop.
9324                 lastOffset = size;
9325             }
9326         }
9327     }
9328 }
9329 
9330 #if VMA_STATS_STRING_ENABLED
PrintDetailedMap(class VmaJsonWriter & json)9331 void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
9332 {
9333     const VkDeviceSize size = GetSize();
9334     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9335     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9336     const size_t suballoc1stCount = suballocations1st.size();
9337     const size_t suballoc2ndCount = suballocations2nd.size();
9338 
9339     // FIRST PASS
9340 
9341     size_t unusedRangeCount = 0;
9342     VkDeviceSize usedBytes = 0;
9343 
9344     VkDeviceSize lastOffset = 0;
9345 
9346     size_t alloc2ndCount = 0;
9347     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9348     {
9349         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9350         size_t nextAlloc2ndIndex = 0;
9351         while(lastOffset < freeSpace2ndTo1stEnd)
9352         {
9353             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9354             while(nextAlloc2ndIndex < suballoc2ndCount &&
9355                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9356             {
9357                 ++nextAlloc2ndIndex;
9358             }
9359 
9360             // Found non-null allocation.
9361             if(nextAlloc2ndIndex < suballoc2ndCount)
9362             {
9363                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9364 
9365                 // 1. Process free space before this allocation.
9366                 if(lastOffset < suballoc.offset)
9367                 {
9368                     // There is free space from lastOffset to suballoc.offset.
9369                     ++unusedRangeCount;
9370                 }
9371 
9372                 // 2. Process this allocation.
9373                 // There is allocation with suballoc.offset, suballoc.size.
9374                 ++alloc2ndCount;
9375                 usedBytes += suballoc.size;
9376 
9377                 // 3. Prepare for next iteration.
9378                 lastOffset = suballoc.offset + suballoc.size;
9379                 ++nextAlloc2ndIndex;
9380             }
9381             // We are at the end.
9382             else
9383             {
9384                 if(lastOffset < freeSpace2ndTo1stEnd)
9385                 {
9386                     // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9387                     ++unusedRangeCount;
9388                 }
9389 
9390                 // End of loop.
9391                 lastOffset = freeSpace2ndTo1stEnd;
9392             }
9393         }
9394     }
9395 
9396     size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9397     size_t alloc1stCount = 0;
9398     const VkDeviceSize freeSpace1stTo2ndEnd =
9399         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9400     while(lastOffset < freeSpace1stTo2ndEnd)
9401     {
9402         // Find next non-null allocation or move nextAllocIndex to the end.
9403         while(nextAlloc1stIndex < suballoc1stCount &&
9404             suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9405         {
9406             ++nextAlloc1stIndex;
9407         }
9408 
9409         // Found non-null allocation.
9410         if(nextAlloc1stIndex < suballoc1stCount)
9411         {
9412             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9413 
9414             // 1. Process free space before this allocation.
9415             if(lastOffset < suballoc.offset)
9416             {
9417                 // There is free space from lastOffset to suballoc.offset.
9418                 ++unusedRangeCount;
9419             }
9420 
9421             // 2. Process this allocation.
9422             // There is allocation with suballoc.offset, suballoc.size.
9423             ++alloc1stCount;
9424             usedBytes += suballoc.size;
9425 
9426             // 3. Prepare for next iteration.
9427             lastOffset = suballoc.offset + suballoc.size;
9428             ++nextAlloc1stIndex;
9429         }
9430         // We are at the end.
9431         else
9432         {
9433             if(lastOffset < size)
9434             {
9435                 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9436                 ++unusedRangeCount;
9437             }
9438 
9439             // End of loop.
9440             lastOffset = freeSpace1stTo2ndEnd;
9441         }
9442     }
9443 
9444     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9445     {
9446         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9447         while(lastOffset < size)
9448         {
9449             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9450             while(nextAlloc2ndIndex != SIZE_MAX &&
9451                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9452             {
9453                 --nextAlloc2ndIndex;
9454             }
9455 
9456             // Found non-null allocation.
9457             if(nextAlloc2ndIndex != SIZE_MAX)
9458             {
9459                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9460 
9461                 // 1. Process free space before this allocation.
9462                 if(lastOffset < suballoc.offset)
9463                 {
9464                     // There is free space from lastOffset to suballoc.offset.
9465                     ++unusedRangeCount;
9466                 }
9467 
9468                 // 2. Process this allocation.
9469                 // There is allocation with suballoc.offset, suballoc.size.
9470                 ++alloc2ndCount;
9471                 usedBytes += suballoc.size;
9472 
9473                 // 3. Prepare for next iteration.
9474                 lastOffset = suballoc.offset + suballoc.size;
9475                 --nextAlloc2ndIndex;
9476             }
9477             // We are at the end.
9478             else
9479             {
9480                 if(lastOffset < size)
9481                 {
9482                     // There is free space from lastOffset to size.
9483                     ++unusedRangeCount;
9484                 }
9485 
9486                 // End of loop.
9487                 lastOffset = size;
9488             }
9489         }
9490     }
9491 
9492     const VkDeviceSize unusedBytes = size - usedBytes;
9493     PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
9494 
9495     // SECOND PASS
9496     lastOffset = 0;
9497 
9498     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9499     {
9500         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9501         size_t nextAlloc2ndIndex = 0;
9502         while(lastOffset < freeSpace2ndTo1stEnd)
9503         {
9504             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9505             while(nextAlloc2ndIndex < suballoc2ndCount &&
9506                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9507             {
9508                 ++nextAlloc2ndIndex;
9509             }
9510 
9511             // Found non-null allocation.
9512             if(nextAlloc2ndIndex < suballoc2ndCount)
9513             {
9514                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9515 
9516                 // 1. Process free space before this allocation.
9517                 if(lastOffset < suballoc.offset)
9518                 {
9519                     // There is free space from lastOffset to suballoc.offset.
9520                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9521                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9522                 }
9523 
9524                 // 2. Process this allocation.
9525                 // There is allocation with suballoc.offset, suballoc.size.
9526                 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
9527 
9528                 // 3. Prepare for next iteration.
9529                 lastOffset = suballoc.offset + suballoc.size;
9530                 ++nextAlloc2ndIndex;
9531             }
9532             // We are at the end.
9533             else
9534             {
9535                 if(lastOffset < freeSpace2ndTo1stEnd)
9536                 {
9537                     // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9538                     const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9539                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9540                 }
9541 
9542                 // End of loop.
9543                 lastOffset = freeSpace2ndTo1stEnd;
9544             }
9545         }
9546     }
9547 
9548     nextAlloc1stIndex = m_1stNullItemsBeginCount;
9549     while(lastOffset < freeSpace1stTo2ndEnd)
9550     {
9551         // Find next non-null allocation or move nextAllocIndex to the end.
9552         while(nextAlloc1stIndex < suballoc1stCount &&
9553             suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9554         {
9555             ++nextAlloc1stIndex;
9556         }
9557 
9558         // Found non-null allocation.
9559         if(nextAlloc1stIndex < suballoc1stCount)
9560         {
9561             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9562 
9563             // 1. Process free space before this allocation.
9564             if(lastOffset < suballoc.offset)
9565             {
9566                 // There is free space from lastOffset to suballoc.offset.
9567                 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9568                 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9569             }
9570 
9571             // 2. Process this allocation.
9572             // There is allocation with suballoc.offset, suballoc.size.
9573             PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
9574 
9575             // 3. Prepare for next iteration.
9576             lastOffset = suballoc.offset + suballoc.size;
9577             ++nextAlloc1stIndex;
9578         }
9579         // We are at the end.
9580         else
9581         {
9582             if(lastOffset < freeSpace1stTo2ndEnd)
9583             {
9584                 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9585                 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9586                 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9587             }
9588 
9589             // End of loop.
9590             lastOffset = freeSpace1stTo2ndEnd;
9591         }
9592     }
9593 
9594     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9595     {
9596         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9597         while(lastOffset < size)
9598         {
9599             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9600             while(nextAlloc2ndIndex != SIZE_MAX &&
9601                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9602             {
9603                 --nextAlloc2ndIndex;
9604             }
9605 
9606             // Found non-null allocation.
9607             if(nextAlloc2ndIndex != SIZE_MAX)
9608             {
9609                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9610 
9611                 // 1. Process free space before this allocation.
9612                 if(lastOffset < suballoc.offset)
9613                 {
9614                     // There is free space from lastOffset to suballoc.offset.
9615                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9616                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9617                 }
9618 
9619                 // 2. Process this allocation.
9620                 // There is allocation with suballoc.offset, suballoc.size.
9621                 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
9622 
9623                 // 3. Prepare for next iteration.
9624                 lastOffset = suballoc.offset + suballoc.size;
9625                 --nextAlloc2ndIndex;
9626             }
9627             // We are at the end.
9628             else
9629             {
9630                 if(lastOffset < size)
9631                 {
9632                     // There is free space from lastOffset to size.
9633                     const VkDeviceSize unusedRangeSize = size - lastOffset;
9634                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9635                 }
9636 
9637                 // End of loop.
9638                 lastOffset = size;
9639             }
9640         }
9641     }
9642 
9643     PrintDetailedMap_End(json);
9644 }
9645 #endif // #if VMA_STATS_STRING_ENABLED
9646 
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)9647 bool VmaBlockMetadata_Linear::CreateAllocationRequest(
9648     uint32_t currentFrameIndex,
9649     uint32_t frameInUseCount,
9650     VkDeviceSize bufferImageGranularity,
9651     VkDeviceSize allocSize,
9652     VkDeviceSize allocAlignment,
9653     bool upperAddress,
9654     VmaSuballocationType allocType,
9655     bool canMakeOtherLost,
9656     uint32_t strategy,
9657     VmaAllocationRequest* pAllocationRequest)
9658 {
9659     VMA_ASSERT(allocSize > 0);
9660     VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9661     VMA_ASSERT(pAllocationRequest != VMA_NULL);
9662     VMA_HEAVY_ASSERT(Validate());
9663 
9664     const VkDeviceSize size = GetSize();
9665     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9666     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9667 
9668     if(upperAddress)
9669     {
9670         if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9671         {
9672             VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
9673             return false;
9674         }
9675 
9676         // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
9677         if(allocSize > size)
9678         {
9679             return false;
9680         }
9681         VkDeviceSize resultBaseOffset = size - allocSize;
9682         if(!suballocations2nd.empty())
9683         {
9684             const VmaSuballocation& lastSuballoc = suballocations2nd.back();
9685             resultBaseOffset = lastSuballoc.offset - allocSize;
9686             if(allocSize > lastSuballoc.offset)
9687             {
9688                 return false;
9689             }
9690         }
9691 
9692         // Start from offset equal to end of free space.
9693         VkDeviceSize resultOffset = resultBaseOffset;
9694 
9695         // Apply VMA_DEBUG_MARGIN at the end.
9696         if(VMA_DEBUG_MARGIN > 0)
9697         {
9698             if(resultOffset < VMA_DEBUG_MARGIN)
9699             {
9700                 return false;
9701             }
9702             resultOffset -= VMA_DEBUG_MARGIN;
9703         }
9704 
9705         // Apply alignment.
9706         resultOffset = VmaAlignDown(resultOffset, allocAlignment);
9707 
9708         // Check next suballocations from 2nd for BufferImageGranularity conflicts.
9709         // Make bigger alignment if necessary.
9710         if(bufferImageGranularity > 1 && !suballocations2nd.empty())
9711         {
9712             bool bufferImageGranularityConflict = false;
9713             for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
9714             {
9715                 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
9716                 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9717                 {
9718                     if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
9719                     {
9720                         bufferImageGranularityConflict = true;
9721                         break;
9722                     }
9723                 }
9724                 else
9725                     // Already on previous page.
9726                     break;
9727             }
9728             if(bufferImageGranularityConflict)
9729             {
9730                 resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
9731             }
9732         }
9733 
9734         // There is enough free space.
9735         const VkDeviceSize endOf1st = !suballocations1st.empty() ?
9736             suballocations1st.back().offset + suballocations1st.back().size :
9737             0;
9738         if(endOf1st + VMA_DEBUG_MARGIN <= resultOffset)
9739         {
9740             // Check previous suballocations for BufferImageGranularity conflicts.
9741             // If conflict exists, allocation cannot be made here.
9742             if(bufferImageGranularity > 1)
9743             {
9744                 for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
9745                 {
9746                     const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
9747                     if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9748                     {
9749                         if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
9750                         {
9751                             return false;
9752                         }
9753                     }
9754                     else
9755                     {
9756                         // Already on next page.
9757                         break;
9758                     }
9759                 }
9760             }
9761 
9762             // All tests passed: Success.
9763             pAllocationRequest->offset = resultOffset;
9764             pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
9765             pAllocationRequest->sumItemSize = 0;
9766             // pAllocationRequest->item unused.
9767             pAllocationRequest->itemsToMakeLostCount = 0;
9768             return true;
9769         }
9770     }
9771     else // !upperAddress
9772     {
9773         if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9774         {
9775             // Try to allocate at the end of 1st vector.
9776 
9777             VkDeviceSize resultBaseOffset = 0;
9778             if(!suballocations1st.empty())
9779             {
9780                 const VmaSuballocation& lastSuballoc = suballocations1st.back();
9781                 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
9782             }
9783 
9784             // Start from offset equal to beginning of free space.
9785             VkDeviceSize resultOffset = resultBaseOffset;
9786 
9787             // Apply VMA_DEBUG_MARGIN at the beginning.
9788             if(VMA_DEBUG_MARGIN > 0)
9789             {
9790                 resultOffset += VMA_DEBUG_MARGIN;
9791             }
9792 
9793             // Apply alignment.
9794             resultOffset = VmaAlignUp(resultOffset, allocAlignment);
9795 
9796             // Check previous suballocations for BufferImageGranularity conflicts.
9797             // Make bigger alignment if necessary.
9798             if(bufferImageGranularity > 1 && !suballocations1st.empty())
9799             {
9800                 bool bufferImageGranularityConflict = false;
9801                 for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
9802                 {
9803                     const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
9804                     if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9805                     {
9806                         if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9807                         {
9808                             bufferImageGranularityConflict = true;
9809                             break;
9810                         }
9811                     }
9812                     else
9813                         // Already on previous page.
9814                         break;
9815                 }
9816                 if(bufferImageGranularityConflict)
9817                 {
9818                     resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
9819                 }
9820             }
9821 
9822             const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
9823                 suballocations2nd.back().offset : size;
9824 
9825             // There is enough free space at the end after alignment.
9826             if(resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd)
9827             {
9828                 // Check next suballocations for BufferImageGranularity conflicts.
9829                 // If conflict exists, allocation cannot be made here.
9830                 if(bufferImageGranularity > 1 && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9831                 {
9832                     for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
9833                     {
9834                         const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
9835                         if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9836                         {
9837                             if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9838                             {
9839                                 return false;
9840                             }
9841                         }
9842                         else
9843                         {
9844                             // Already on previous page.
9845                             break;
9846                         }
9847                     }
9848                 }
9849 
9850                 // All tests passed: Success.
9851                 pAllocationRequest->offset = resultOffset;
9852                 pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
9853                 pAllocationRequest->sumItemSize = 0;
9854                 // pAllocationRequest->item unused.
9855                 pAllocationRequest->itemsToMakeLostCount = 0;
9856                 return true;
9857             }
9858         }
9859 
9860         // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
9861         // beginning of 1st vector as the end of free space.
9862         if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9863         {
9864             VMA_ASSERT(!suballocations1st.empty());
9865 
9866             VkDeviceSize resultBaseOffset = 0;
9867             if(!suballocations2nd.empty())
9868             {
9869                 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
9870                 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
9871             }
9872 
9873             // Start from offset equal to beginning of free space.
9874             VkDeviceSize resultOffset = resultBaseOffset;
9875 
9876             // Apply VMA_DEBUG_MARGIN at the beginning.
9877             if(VMA_DEBUG_MARGIN > 0)
9878             {
9879                 resultOffset += VMA_DEBUG_MARGIN;
9880             }
9881 
9882             // Apply alignment.
9883             resultOffset = VmaAlignUp(resultOffset, allocAlignment);
9884 
9885             // Check previous suballocations for BufferImageGranularity conflicts.
9886             // Make bigger alignment if necessary.
9887             if(bufferImageGranularity > 1 && !suballocations2nd.empty())
9888             {
9889                 bool bufferImageGranularityConflict = false;
9890                 for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
9891                 {
9892                     const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
9893                     if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9894                     {
9895                         if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9896                         {
9897                             bufferImageGranularityConflict = true;
9898                             break;
9899                         }
9900                     }
9901                     else
9902                         // Already on previous page.
9903                         break;
9904                 }
9905                 if(bufferImageGranularityConflict)
9906                 {
9907                     resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
9908                 }
9909             }
9910 
9911             pAllocationRequest->itemsToMakeLostCount = 0;
9912             pAllocationRequest->sumItemSize = 0;
9913             size_t index1st = m_1stNullItemsBeginCount;
9914 
9915             if(canMakeOtherLost)
9916             {
9917                 while(index1st < suballocations1st.size() &&
9918                     resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset)
9919                 {
9920                     // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
9921                     const VmaSuballocation& suballoc = suballocations1st[index1st];
9922                     if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
9923                     {
9924                         // No problem.
9925                     }
9926                     else
9927                     {
9928                         VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
9929                         if(suballoc.hAllocation->CanBecomeLost() &&
9930                             suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9931                         {
9932                             ++pAllocationRequest->itemsToMakeLostCount;
9933                             pAllocationRequest->sumItemSize += suballoc.size;
9934                         }
9935                         else
9936                         {
9937                             return false;
9938                         }
9939                     }
9940                     ++index1st;
9941                 }
9942 
9943                 // Check next suballocations for BufferImageGranularity conflicts.
9944                 // If conflict exists, we must mark more allocations lost or fail.
9945                 if(bufferImageGranularity > 1)
9946                 {
9947                     while(index1st < suballocations1st.size())
9948                     {
9949                         const VmaSuballocation& suballoc = suballocations1st[index1st];
9950                         if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))
9951                         {
9952                             if(suballoc.hAllocation != VK_NULL_HANDLE)
9953                             {
9954                                 // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
9955                                 if(suballoc.hAllocation->CanBecomeLost() &&
9956                                     suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9957                                 {
9958                                     ++pAllocationRequest->itemsToMakeLostCount;
9959                                     pAllocationRequest->sumItemSize += suballoc.size;
9960                                 }
9961                                 else
9962                                 {
9963                                     return false;
9964                                 }
9965                             }
9966                         }
9967                         else
9968                         {
9969                             // Already on next page.
9970                             break;
9971                         }
9972                         ++index1st;
9973                     }
9974                 }
9975             }
9976 
9977             // There is enough free space at the end after alignment.
9978             if((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN < size) ||
9979                 (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset))
9980             {
9981                 // Check next suballocations for BufferImageGranularity conflicts.
9982                 // If conflict exists, allocation cannot be made here.
9983                 if(bufferImageGranularity > 1)
9984                 {
9985                     for(size_t nextSuballocIndex = index1st;
9986                         nextSuballocIndex < suballocations1st.size();
9987                         nextSuballocIndex++)
9988                     {
9989                         const VmaSuballocation& nextSuballoc = suballocations1st[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 next page.
10000                             break;
10001                         }
10002                     }
10003                 }
10004 
10005                 // All tests passed: Success.
10006                 pAllocationRequest->offset = resultOffset;
10007                 pAllocationRequest->sumFreeSize =
10008                     (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size)
10009                     - resultBaseOffset
10010                     - pAllocationRequest->sumItemSize;
10011                 // pAllocationRequest->item unused.
10012                 return true;
10013             }
10014         }
10015     }
10016 
10017     return false;
10018 }
10019 
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)10020 bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
10021     uint32_t currentFrameIndex,
10022     uint32_t frameInUseCount,
10023     VmaAllocationRequest* pAllocationRequest)
10024 {
10025     if(pAllocationRequest->itemsToMakeLostCount == 0)
10026     {
10027         return true;
10028     }
10029 
10030     VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
10031 
10032     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10033     size_t index1st = m_1stNullItemsBeginCount;
10034     size_t madeLostCount = 0;
10035     while(madeLostCount < pAllocationRequest->itemsToMakeLostCount)
10036     {
10037         VMA_ASSERT(index1st < suballocations1st.size());
10038         VmaSuballocation& suballoc = suballocations1st[index1st];
10039         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10040         {
10041             VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
10042             VMA_ASSERT(suballoc.hAllocation->CanBecomeLost());
10043             if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10044             {
10045                 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10046                 suballoc.hAllocation = VK_NULL_HANDLE;
10047                 m_SumFreeSize += suballoc.size;
10048                 ++m_1stNullItemsMiddleCount;
10049                 ++madeLostCount;
10050             }
10051             else
10052             {
10053                 return false;
10054             }
10055         }
10056         ++index1st;
10057     }
10058 
10059     CleanupAfterFree();
10060     //VMA_HEAVY_ASSERT(Validate()); // Already called by ClanupAfterFree().
10061 
10062     return true;
10063 }
10064 
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)10065 uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
10066 {
10067     uint32_t lostAllocationCount = 0;
10068 
10069     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10070     for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
10071     {
10072         VmaSuballocation& suballoc = suballocations1st[i];
10073         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
10074             suballoc.hAllocation->CanBecomeLost() &&
10075             suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10076         {
10077             suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10078             suballoc.hAllocation = VK_NULL_HANDLE;
10079             ++m_1stNullItemsMiddleCount;
10080             m_SumFreeSize += suballoc.size;
10081             ++lostAllocationCount;
10082         }
10083     }
10084 
10085     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10086     for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
10087     {
10088         VmaSuballocation& suballoc = suballocations2nd[i];
10089         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
10090             suballoc.hAllocation->CanBecomeLost() &&
10091             suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10092         {
10093             suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10094             suballoc.hAllocation = VK_NULL_HANDLE;
10095             ++m_2ndNullItemsCount;
10096             ++lostAllocationCount;
10097         }
10098     }
10099 
10100     if(lostAllocationCount)
10101     {
10102         CleanupAfterFree();
10103     }
10104 
10105     return lostAllocationCount;
10106 }
10107 
CheckCorruption(const void * pBlockData)10108 VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
10109 {
10110     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10111     for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
10112     {
10113         const VmaSuballocation& suballoc = suballocations1st[i];
10114         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10115         {
10116             if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
10117             {
10118                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
10119                 return VK_ERROR_VALIDATION_FAILED_EXT;
10120             }
10121             if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
10122             {
10123                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
10124                 return VK_ERROR_VALIDATION_FAILED_EXT;
10125             }
10126         }
10127     }
10128 
10129     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10130     for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
10131     {
10132         const VmaSuballocation& suballoc = suballocations2nd[i];
10133         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10134         {
10135             if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
10136             {
10137                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
10138                 return VK_ERROR_VALIDATION_FAILED_EXT;
10139             }
10140             if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
10141             {
10142                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
10143                 return VK_ERROR_VALIDATION_FAILED_EXT;
10144             }
10145         }
10146     }
10147 
10148     return VK_SUCCESS;
10149 }
10150 
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,bool upperAddress,VmaAllocation hAllocation)10151 void VmaBlockMetadata_Linear::Alloc(
10152     const VmaAllocationRequest& request,
10153     VmaSuballocationType type,
10154     VkDeviceSize allocSize,
10155     bool upperAddress,
10156     VmaAllocation hAllocation)
10157 {
10158     const VmaSuballocation newSuballoc = { request.offset, allocSize, hAllocation, type };
10159 
10160     if(upperAddress)
10161     {
10162         VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
10163             "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
10164         SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10165         suballocations2nd.push_back(newSuballoc);
10166         m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
10167     }
10168     else
10169     {
10170         SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10171 
10172         // First allocation.
10173         if(suballocations1st.empty())
10174         {
10175             suballocations1st.push_back(newSuballoc);
10176         }
10177         else
10178         {
10179             // New allocation at the end of 1st vector.
10180             if(request.offset >= suballocations1st.back().offset + suballocations1st.back().size)
10181             {
10182                 // Check if it fits before the end of the block.
10183                 VMA_ASSERT(request.offset + allocSize <= GetSize());
10184                 suballocations1st.push_back(newSuballoc);
10185             }
10186             // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
10187             else if(request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset)
10188             {
10189                 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10190 
10191                 switch(m_2ndVectorMode)
10192                 {
10193                 case SECOND_VECTOR_EMPTY:
10194                     // First allocation from second part ring buffer.
10195                     VMA_ASSERT(suballocations2nd.empty());
10196                     m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
10197                     break;
10198                 case SECOND_VECTOR_RING_BUFFER:
10199                     // 2-part ring buffer is already started.
10200                     VMA_ASSERT(!suballocations2nd.empty());
10201                     break;
10202                 case SECOND_VECTOR_DOUBLE_STACK:
10203                     VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
10204                     break;
10205                 default:
10206                     VMA_ASSERT(0);
10207                 }
10208 
10209                 suballocations2nd.push_back(newSuballoc);
10210             }
10211             else
10212             {
10213                 VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
10214             }
10215         }
10216     }
10217 
10218     m_SumFreeSize -= newSuballoc.size;
10219 }
10220 
Free(const VmaAllocation allocation)10221 void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation)
10222 {
10223     FreeAtOffset(allocation->GetOffset());
10224 }
10225 
FreeAtOffset(VkDeviceSize offset)10226 void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
10227 {
10228     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10229     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10230 
10231     if(!suballocations1st.empty())
10232     {
10233         // First allocation: Mark it as next empty at the beginning.
10234         VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
10235         if(firstSuballoc.offset == offset)
10236         {
10237             firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10238             firstSuballoc.hAllocation = VK_NULL_HANDLE;
10239             m_SumFreeSize += firstSuballoc.size;
10240             ++m_1stNullItemsBeginCount;
10241             CleanupAfterFree();
10242             return;
10243         }
10244     }
10245 
10246     // Last allocation in 2-part ring buffer or top of upper stack (same logic).
10247     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
10248         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10249     {
10250         VmaSuballocation& lastSuballoc = suballocations2nd.back();
10251         if(lastSuballoc.offset == offset)
10252         {
10253             m_SumFreeSize += lastSuballoc.size;
10254             suballocations2nd.pop_back();
10255             CleanupAfterFree();
10256             return;
10257         }
10258     }
10259     // Last allocation in 1st vector.
10260     else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY)
10261     {
10262         VmaSuballocation& lastSuballoc = suballocations1st.back();
10263         if(lastSuballoc.offset == offset)
10264         {
10265             m_SumFreeSize += lastSuballoc.size;
10266             suballocations1st.pop_back();
10267             CleanupAfterFree();
10268             return;
10269         }
10270     }
10271 
10272     // Item from the middle of 1st vector.
10273     {
10274         VmaSuballocation refSuballoc;
10275         refSuballoc.offset = offset;
10276         // Rest of members stays uninitialized intentionally for better performance.
10277         SuballocationVectorType::iterator it = VmaVectorFindSorted<VmaSuballocationOffsetLess>(
10278             suballocations1st.begin() + m_1stNullItemsBeginCount,
10279             suballocations1st.end(),
10280             refSuballoc);
10281         if(it != suballocations1st.end())
10282         {
10283             it->type = VMA_SUBALLOCATION_TYPE_FREE;
10284             it->hAllocation = VK_NULL_HANDLE;
10285             ++m_1stNullItemsMiddleCount;
10286             m_SumFreeSize += it->size;
10287             CleanupAfterFree();
10288             return;
10289         }
10290     }
10291 
10292     if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
10293     {
10294         // Item from the middle of 2nd vector.
10295         VmaSuballocation refSuballoc;
10296         refSuballoc.offset = offset;
10297         // Rest of members stays uninitialized intentionally for better performance.
10298         SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
10299             VmaVectorFindSorted<VmaSuballocationOffsetLess>(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc) :
10300             VmaVectorFindSorted<VmaSuballocationOffsetGreater>(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc);
10301         if(it != suballocations2nd.end())
10302         {
10303             it->type = VMA_SUBALLOCATION_TYPE_FREE;
10304             it->hAllocation = VK_NULL_HANDLE;
10305             ++m_2ndNullItemsCount;
10306             m_SumFreeSize += it->size;
10307             CleanupAfterFree();
10308             return;
10309         }
10310     }
10311 
10312     VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
10313 }
10314 
ShouldCompact1st()10315 bool VmaBlockMetadata_Linear::ShouldCompact1st() const
10316 {
10317     const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
10318     const size_t suballocCount = AccessSuballocations1st().size();
10319     return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
10320 }
10321 
CleanupAfterFree()10322 void VmaBlockMetadata_Linear::CleanupAfterFree()
10323 {
10324     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10325     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10326 
10327     if(IsEmpty())
10328     {
10329         suballocations1st.clear();
10330         suballocations2nd.clear();
10331         m_1stNullItemsBeginCount = 0;
10332         m_1stNullItemsMiddleCount = 0;
10333         m_2ndNullItemsCount = 0;
10334         m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10335     }
10336     else
10337     {
10338         const size_t suballoc1stCount = suballocations1st.size();
10339         const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
10340         VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
10341 
10342         // Find more null items at the beginning of 1st vector.
10343         while(m_1stNullItemsBeginCount < suballoc1stCount &&
10344             suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
10345         {
10346             ++m_1stNullItemsBeginCount;
10347             --m_1stNullItemsMiddleCount;
10348         }
10349 
10350         // Find more null items at the end of 1st vector.
10351         while(m_1stNullItemsMiddleCount > 0 &&
10352             suballocations1st.back().hAllocation == VK_NULL_HANDLE)
10353         {
10354             --m_1stNullItemsMiddleCount;
10355             suballocations1st.pop_back();
10356         }
10357 
10358         // Find more null items at the end of 2nd vector.
10359         while(m_2ndNullItemsCount > 0 &&
10360             suballocations2nd.back().hAllocation == VK_NULL_HANDLE)
10361         {
10362             --m_2ndNullItemsCount;
10363             suballocations2nd.pop_back();
10364         }
10365 
10366         if(ShouldCompact1st())
10367         {
10368             const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
10369             size_t srcIndex = m_1stNullItemsBeginCount;
10370             for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
10371             {
10372                 while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE)
10373                 {
10374                     ++srcIndex;
10375                 }
10376                 if(dstIndex != srcIndex)
10377                 {
10378                     suballocations1st[dstIndex] = suballocations1st[srcIndex];
10379                 }
10380                 ++srcIndex;
10381             }
10382             suballocations1st.resize(nonNullItemCount);
10383             m_1stNullItemsBeginCount = 0;
10384             m_1stNullItemsMiddleCount = 0;
10385         }
10386 
10387         // 2nd vector became empty.
10388         if(suballocations2nd.empty())
10389         {
10390             m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10391         }
10392 
10393         // 1st vector became empty.
10394         if(suballocations1st.size() - m_1stNullItemsBeginCount == 0)
10395         {
10396             suballocations1st.clear();
10397             m_1stNullItemsBeginCount = 0;
10398 
10399             if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10400             {
10401                 // Swap 1st with 2nd. Now 2nd is empty.
10402                 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10403                 m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
10404                 while(m_1stNullItemsBeginCount < suballocations2nd.size() &&
10405                     suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
10406                 {
10407                     ++m_1stNullItemsBeginCount;
10408                     --m_1stNullItemsMiddleCount;
10409                 }
10410                 m_2ndNullItemsCount = 0;
10411                 m_1stVectorIndex ^= 1;
10412             }
10413         }
10414     }
10415 
10416     VMA_HEAVY_ASSERT(Validate());
10417 }
10418 
10419 
10420 ////////////////////////////////////////////////////////////////////////////////
10421 // class VmaBlockMetadata_Buddy
10422 
VmaBlockMetadata_Buddy(VmaAllocator hAllocator)10423 VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :
10424     VmaBlockMetadata(hAllocator),
10425     m_Root(VMA_NULL),
10426     m_AllocationCount(0),
10427     m_FreeCount(1),
10428     m_SumFreeSize(0)
10429 {
10430     memset(m_FreeList, 0, sizeof(m_FreeList));
10431 }
10432 
~VmaBlockMetadata_Buddy()10433 VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
10434 {
10435     DeleteNode(m_Root);
10436 }
10437 
Init(VkDeviceSize size)10438 void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
10439 {
10440     VmaBlockMetadata::Init(size);
10441 
10442     m_UsableSize = VmaPrevPow2(size);
10443     m_SumFreeSize = m_UsableSize;
10444 
10445     // Calculate m_LevelCount.
10446     m_LevelCount = 1;
10447     while(m_LevelCount < MAX_LEVELS &&
10448         LevelToNodeSize(m_LevelCount) >= MIN_NODE_SIZE)
10449     {
10450         ++m_LevelCount;
10451     }
10452 
10453     Node* rootNode = vma_new(GetAllocationCallbacks(), Node)();
10454     rootNode->offset = 0;
10455     rootNode->type = Node::TYPE_FREE;
10456     rootNode->parent = VMA_NULL;
10457     rootNode->buddy = VMA_NULL;
10458 
10459     m_Root = rootNode;
10460     AddToFreeListFront(0, rootNode);
10461 }
10462 
Validate()10463 bool VmaBlockMetadata_Buddy::Validate() const
10464 {
10465     // Validate tree.
10466     ValidationContext ctx;
10467     if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
10468     {
10469         VMA_VALIDATE(false && "ValidateNode failed.");
10470     }
10471     VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
10472     VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
10473 
10474     // Validate free node lists.
10475     for(uint32_t level = 0; level < m_LevelCount; ++level)
10476     {
10477         VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
10478             m_FreeList[level].front->free.prev == VMA_NULL);
10479 
10480         for(Node* node = m_FreeList[level].front;
10481             node != VMA_NULL;
10482             node = node->free.next)
10483         {
10484             VMA_VALIDATE(node->type == Node::TYPE_FREE);
10485 
10486             if(node->free.next == VMA_NULL)
10487             {
10488                 VMA_VALIDATE(m_FreeList[level].back == node);
10489             }
10490             else
10491             {
10492                 VMA_VALIDATE(node->free.next->free.prev == node);
10493             }
10494         }
10495     }
10496 
10497     // Validate that free lists ar higher levels are empty.
10498     for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
10499     {
10500         VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
10501     }
10502 
10503     return true;
10504 }
10505 
GetUnusedRangeSizeMax()10506 VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
10507 {
10508     for(uint32_t level = 0; level < m_LevelCount; ++level)
10509     {
10510         if(m_FreeList[level].front != VMA_NULL)
10511         {
10512             return LevelToNodeSize(level);
10513         }
10514     }
10515     return 0;
10516 }
10517 
CalcAllocationStatInfo(VmaStatInfo & outInfo)10518 void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
10519 {
10520     const VkDeviceSize unusableSize = GetUnusableSize();
10521 
10522     outInfo.blockCount = 1;
10523 
10524     outInfo.allocationCount = outInfo.unusedRangeCount = 0;
10525     outInfo.usedBytes = outInfo.unusedBytes = 0;
10526 
10527     outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
10528     outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
10529     outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused.
10530 
10531     CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0));
10532 
10533     if(unusableSize > 0)
10534     {
10535         ++outInfo.unusedRangeCount;
10536         outInfo.unusedBytes += unusableSize;
10537         outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize);
10538         outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize);
10539     }
10540 }
10541 
AddPoolStats(VmaPoolStats & inoutStats)10542 void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
10543 {
10544     const VkDeviceSize unusableSize = GetUnusableSize();
10545 
10546     inoutStats.size += GetSize();
10547     inoutStats.unusedSize += m_SumFreeSize + unusableSize;
10548     inoutStats.allocationCount += m_AllocationCount;
10549     inoutStats.unusedRangeCount += m_FreeCount;
10550     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
10551 
10552     if(unusableSize > 0)
10553     {
10554         ++inoutStats.unusedRangeCount;
10555         // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
10556     }
10557 }
10558 
10559 #if VMA_STATS_STRING_ENABLED
10560 
PrintDetailedMap(class VmaJsonWriter & json)10561 void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
10562 {
10563     // TODO optimize
10564     VmaStatInfo stat;
10565     CalcAllocationStatInfo(stat);
10566 
10567     PrintDetailedMap_Begin(
10568         json,
10569         stat.unusedBytes,
10570         stat.allocationCount,
10571         stat.unusedRangeCount);
10572 
10573     PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
10574 
10575     const VkDeviceSize unusableSize = GetUnusableSize();
10576     if(unusableSize > 0)
10577     {
10578         PrintDetailedMap_UnusedRange(json,
10579             m_UsableSize, // offset
10580             unusableSize); // size
10581     }
10582 
10583     PrintDetailedMap_End(json);
10584 }
10585 
10586 #endif // #if VMA_STATS_STRING_ENABLED
10587 
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)10588 bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
10589     uint32_t currentFrameIndex,
10590     uint32_t frameInUseCount,
10591     VkDeviceSize bufferImageGranularity,
10592     VkDeviceSize allocSize,
10593     VkDeviceSize allocAlignment,
10594     bool upperAddress,
10595     VmaSuballocationType allocType,
10596     bool canMakeOtherLost,
10597     uint32_t strategy,
10598     VmaAllocationRequest* pAllocationRequest)
10599 {
10600     VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
10601 
10602     // Simple way to respect bufferImageGranularity. May be optimized some day.
10603     // Whenever it might be an OPTIMAL image...
10604     if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
10605         allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
10606         allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
10607     {
10608         allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity);
10609         allocSize = VMA_MAX(allocSize, bufferImageGranularity);
10610     }
10611 
10612     if(allocSize > m_UsableSize)
10613     {
10614         return false;
10615     }
10616 
10617     const uint32_t targetLevel = AllocSizeToLevel(allocSize);
10618     for(uint32_t level = targetLevel + 1; level--; )
10619     {
10620         for(Node* freeNode = m_FreeList[level].front;
10621             freeNode != VMA_NULL;
10622             freeNode = freeNode->free.next)
10623         {
10624             if(freeNode->offset % allocAlignment == 0)
10625             {
10626                 pAllocationRequest->offset = freeNode->offset;
10627                 pAllocationRequest->sumFreeSize = LevelToNodeSize(level);
10628                 pAllocationRequest->sumItemSize = 0;
10629                 pAllocationRequest->itemsToMakeLostCount = 0;
10630                 pAllocationRequest->customData = (void*)(uintptr_t)level;
10631                 return true;
10632             }
10633         }
10634     }
10635 
10636     return false;
10637 }
10638 
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)10639 bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
10640     uint32_t currentFrameIndex,
10641     uint32_t frameInUseCount,
10642     VmaAllocationRequest* pAllocationRequest)
10643 {
10644     /*
10645     Lost allocations are not supported in buddy allocator at the moment.
10646     Support might be added in the future.
10647     */
10648     return pAllocationRequest->itemsToMakeLostCount == 0;
10649 }
10650 
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)10651 uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
10652 {
10653     /*
10654     Lost allocations are not supported in buddy allocator at the moment.
10655     Support might be added in the future.
10656     */
10657     return 0;
10658 }
10659 
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,bool upperAddress,VmaAllocation hAllocation)10660 void VmaBlockMetadata_Buddy::Alloc(
10661     const VmaAllocationRequest& request,
10662     VmaSuballocationType type,
10663     VkDeviceSize allocSize,
10664     bool upperAddress,
10665     VmaAllocation hAllocation)
10666 {
10667     const uint32_t targetLevel = AllocSizeToLevel(allocSize);
10668     uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
10669 
10670     Node* currNode = m_FreeList[currLevel].front;
10671     VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
10672     while(currNode->offset != request.offset)
10673     {
10674         currNode = currNode->free.next;
10675         VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
10676     }
10677 
10678     // Go down, splitting free nodes.
10679     while(currLevel < targetLevel)
10680     {
10681         // currNode is already first free node at currLevel.
10682         // Remove it from list of free nodes at this currLevel.
10683         RemoveFromFreeList(currLevel, currNode);
10684 
10685         const uint32_t childrenLevel = currLevel + 1;
10686 
10687         // Create two free sub-nodes.
10688         Node* leftChild = vma_new(GetAllocationCallbacks(), Node)();
10689         Node* rightChild = vma_new(GetAllocationCallbacks(), Node)();
10690 
10691         leftChild->offset = currNode->offset;
10692         leftChild->type = Node::TYPE_FREE;
10693         leftChild->parent = currNode;
10694         leftChild->buddy = rightChild;
10695 
10696         rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
10697         rightChild->type = Node::TYPE_FREE;
10698         rightChild->parent = currNode;
10699         rightChild->buddy = leftChild;
10700 
10701         // Convert current currNode to split type.
10702         currNode->type = Node::TYPE_SPLIT;
10703         currNode->split.leftChild = leftChild;
10704 
10705         // Add child nodes to free list. Order is important!
10706         AddToFreeListFront(childrenLevel, rightChild);
10707         AddToFreeListFront(childrenLevel, leftChild);
10708 
10709         ++m_FreeCount;
10710         //m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2.
10711         ++currLevel;
10712         currNode = m_FreeList[currLevel].front;
10713 
10714         /*
10715         We can be sure that currNode, as left child of node previously split,
10716         also fullfills the alignment requirement.
10717         */
10718     }
10719 
10720     // Remove from free list.
10721     VMA_ASSERT(currLevel == targetLevel &&
10722         currNode != VMA_NULL &&
10723         currNode->type == Node::TYPE_FREE);
10724     RemoveFromFreeList(currLevel, currNode);
10725 
10726     // Convert to allocation node.
10727     currNode->type = Node::TYPE_ALLOCATION;
10728     currNode->allocation.alloc = hAllocation;
10729 
10730     ++m_AllocationCount;
10731     --m_FreeCount;
10732     m_SumFreeSize -= allocSize;
10733 }
10734 
DeleteNode(Node * node)10735 void VmaBlockMetadata_Buddy::DeleteNode(Node* node)
10736 {
10737     if(node->type == Node::TYPE_SPLIT)
10738     {
10739         DeleteNode(node->split.leftChild->buddy);
10740         DeleteNode(node->split.leftChild);
10741     }
10742 
10743     vma_delete(GetAllocationCallbacks(), node);
10744 }
10745 
ValidateNode(ValidationContext & ctx,const Node * parent,const Node * curr,uint32_t level,VkDeviceSize levelNodeSize)10746 bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
10747 {
10748     VMA_VALIDATE(level < m_LevelCount);
10749     VMA_VALIDATE(curr->parent == parent);
10750     VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
10751     VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
10752     switch(curr->type)
10753     {
10754     case Node::TYPE_FREE:
10755         // curr->free.prev, next are validated separately.
10756         ctx.calculatedSumFreeSize += levelNodeSize;
10757         ++ctx.calculatedFreeCount;
10758         break;
10759     case Node::TYPE_ALLOCATION:
10760         ++ctx.calculatedAllocationCount;
10761         ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize();
10762         VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);
10763         break;
10764     case Node::TYPE_SPLIT:
10765         {
10766             const uint32_t childrenLevel = level + 1;
10767             const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2;
10768             const Node* const leftChild = curr->split.leftChild;
10769             VMA_VALIDATE(leftChild != VMA_NULL);
10770             VMA_VALIDATE(leftChild->offset == curr->offset);
10771             if(!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
10772             {
10773                 VMA_VALIDATE(false && "ValidateNode for left child failed.");
10774             }
10775             const Node* const rightChild = leftChild->buddy;
10776             VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
10777             if(!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
10778             {
10779                 VMA_VALIDATE(false && "ValidateNode for right child failed.");
10780             }
10781         }
10782         break;
10783     default:
10784         return false;
10785     }
10786 
10787     return true;
10788 }
10789 
AllocSizeToLevel(VkDeviceSize allocSize)10790 uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
10791 {
10792     // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
10793     uint32_t level = 0;
10794     VkDeviceSize currLevelNodeSize = m_UsableSize;
10795     VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
10796     while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
10797     {
10798         ++level;
10799         currLevelNodeSize = nextLevelNodeSize;
10800         nextLevelNodeSize = currLevelNodeSize >> 1;
10801     }
10802     return level;
10803 }
10804 
FreeAtOffset(VmaAllocation alloc,VkDeviceSize offset)10805 void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset)
10806 {
10807     // Find node and level.
10808     Node* node = m_Root;
10809     VkDeviceSize nodeOffset = 0;
10810     uint32_t level = 0;
10811     VkDeviceSize levelNodeSize = LevelToNodeSize(0);
10812     while(node->type == Node::TYPE_SPLIT)
10813     {
10814         const VkDeviceSize nextLevelSize = levelNodeSize >> 1;
10815         if(offset < nodeOffset + nextLevelSize)
10816         {
10817             node = node->split.leftChild;
10818         }
10819         else
10820         {
10821             node = node->split.leftChild->buddy;
10822             nodeOffset += nextLevelSize;
10823         }
10824         ++level;
10825         levelNodeSize = nextLevelSize;
10826     }
10827 
10828     VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
10829     VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc);
10830 
10831     ++m_FreeCount;
10832     --m_AllocationCount;
10833     m_SumFreeSize += alloc->GetSize();
10834 
10835     node->type = Node::TYPE_FREE;
10836 
10837     // Join free nodes if possible.
10838     while(level > 0 && node->buddy->type == Node::TYPE_FREE)
10839     {
10840         RemoveFromFreeList(level, node->buddy);
10841         Node* const parent = node->parent;
10842 
10843         vma_delete(GetAllocationCallbacks(), node->buddy);
10844         vma_delete(GetAllocationCallbacks(), node);
10845         parent->type = Node::TYPE_FREE;
10846 
10847         node = parent;
10848         --level;
10849         //m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2.
10850         --m_FreeCount;
10851     }
10852 
10853     AddToFreeListFront(level, node);
10854 }
10855 
CalcAllocationStatInfoNode(VmaStatInfo & outInfo,const Node * node,VkDeviceSize levelNodeSize)10856 void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const
10857 {
10858     switch(node->type)
10859     {
10860     case Node::TYPE_FREE:
10861         ++outInfo.unusedRangeCount;
10862         outInfo.unusedBytes += levelNodeSize;
10863         outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, levelNodeSize);
10864         outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize);
10865         break;
10866     case Node::TYPE_ALLOCATION:
10867         {
10868             const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
10869             ++outInfo.allocationCount;
10870             outInfo.usedBytes += allocSize;
10871             outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize);
10872             outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize);
10873 
10874             const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize;
10875             if(unusedRangeSize > 0)
10876             {
10877                 ++outInfo.unusedRangeCount;
10878                 outInfo.unusedBytes += unusedRangeSize;
10879                 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize);
10880                 outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize);
10881             }
10882         }
10883         break;
10884     case Node::TYPE_SPLIT:
10885         {
10886             const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
10887             const Node* const leftChild = node->split.leftChild;
10888             CalcAllocationStatInfoNode(outInfo, leftChild, childrenNodeSize);
10889             const Node* const rightChild = leftChild->buddy;
10890             CalcAllocationStatInfoNode(outInfo, rightChild, childrenNodeSize);
10891         }
10892         break;
10893     default:
10894         VMA_ASSERT(0);
10895     }
10896 }
10897 
AddToFreeListFront(uint32_t level,Node * node)10898 void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
10899 {
10900     VMA_ASSERT(node->type == Node::TYPE_FREE);
10901 
10902     // List is empty.
10903     Node* const frontNode = m_FreeList[level].front;
10904     if(frontNode == VMA_NULL)
10905     {
10906         VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
10907         node->free.prev = node->free.next = VMA_NULL;
10908         m_FreeList[level].front = m_FreeList[level].back = node;
10909     }
10910     else
10911     {
10912         VMA_ASSERT(frontNode->free.prev == VMA_NULL);
10913         node->free.prev = VMA_NULL;
10914         node->free.next = frontNode;
10915         frontNode->free.prev = node;
10916         m_FreeList[level].front = node;
10917     }
10918 }
10919 
RemoveFromFreeList(uint32_t level,Node * node)10920 void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
10921 {
10922     VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
10923 
10924     // It is at the front.
10925     if(node->free.prev == VMA_NULL)
10926     {
10927         VMA_ASSERT(m_FreeList[level].front == node);
10928         m_FreeList[level].front = node->free.next;
10929     }
10930     else
10931     {
10932         Node* const prevFreeNode = node->free.prev;
10933         VMA_ASSERT(prevFreeNode->free.next == node);
10934         prevFreeNode->free.next = node->free.next;
10935     }
10936 
10937     // It is at the back.
10938     if(node->free.next == VMA_NULL)
10939     {
10940         VMA_ASSERT(m_FreeList[level].back == node);
10941         m_FreeList[level].back = node->free.prev;
10942     }
10943     else
10944     {
10945         Node* const nextFreeNode = node->free.next;
10946         VMA_ASSERT(nextFreeNode->free.prev == node);
10947         nextFreeNode->free.prev = node->free.prev;
10948     }
10949 }
10950 
10951 #if VMA_STATS_STRING_ENABLED
PrintDetailedMapNode(class VmaJsonWriter & json,const Node * node,VkDeviceSize levelNodeSize)10952 void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
10953 {
10954     switch(node->type)
10955     {
10956     case Node::TYPE_FREE:
10957         PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
10958         break;
10959     case Node::TYPE_ALLOCATION:
10960         {
10961             PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);
10962             const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
10963             if(allocSize < levelNodeSize)
10964             {
10965                 PrintDetailedMap_UnusedRange(json, node->offset + allocSize, levelNodeSize - allocSize);
10966             }
10967         }
10968         break;
10969     case Node::TYPE_SPLIT:
10970         {
10971             const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
10972             const Node* const leftChild = node->split.leftChild;
10973             PrintDetailedMapNode(json, leftChild, childrenNodeSize);
10974             const Node* const rightChild = leftChild->buddy;
10975             PrintDetailedMapNode(json, rightChild, childrenNodeSize);
10976         }
10977         break;
10978     default:
10979         VMA_ASSERT(0);
10980     }
10981 }
10982 #endif // #if VMA_STATS_STRING_ENABLED
10983 
10984 
10985 ////////////////////////////////////////////////////////////////////////////////
10986 // class VmaDeviceMemoryBlock
10987 
VmaDeviceMemoryBlock(VmaAllocator hAllocator)10988 VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) :
10989     m_pMetadata(VMA_NULL),
10990     m_MemoryTypeIndex(UINT32_MAX),
10991     m_Id(0),
10992     m_hMemory(VK_NULL_HANDLE),
10993     m_MapCount(0),
10994     m_pMappedData(VMA_NULL)
10995 {
10996 }
10997 
Init(VmaAllocator hAllocator,uint32_t newMemoryTypeIndex,VkDeviceMemory newMemory,VkDeviceSize newSize,uint32_t id,uint32_t algorithm)10998 void VmaDeviceMemoryBlock::Init(
10999     VmaAllocator hAllocator,
11000     uint32_t newMemoryTypeIndex,
11001     VkDeviceMemory newMemory,
11002     VkDeviceSize newSize,
11003     uint32_t id,
11004     uint32_t algorithm)
11005 {
11006     VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
11007 
11008     m_MemoryTypeIndex = newMemoryTypeIndex;
11009     m_Id = id;
11010     m_hMemory = newMemory;
11011 
11012     switch(algorithm)
11013     {
11014     case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
11015         m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator);
11016         break;
11017     case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
11018         m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator);
11019         break;
11020     default:
11021         VMA_ASSERT(0);
11022         // Fall-through.
11023     case 0:
11024         m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator);
11025     }
11026     m_pMetadata->Init(newSize);
11027 }
11028 
Destroy(VmaAllocator allocator)11029 void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
11030 {
11031     // This is the most important assert in the entire library.
11032     // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
11033     VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
11034 
11035     VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
11036     allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
11037     m_hMemory = VK_NULL_HANDLE;
11038 
11039     vma_delete(allocator, m_pMetadata);
11040     m_pMetadata = VMA_NULL;
11041 }
11042 
Validate()11043 bool VmaDeviceMemoryBlock::Validate() const
11044 {
11045     VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
11046         (m_pMetadata->GetSize() != 0));
11047 
11048     return m_pMetadata->Validate();
11049 }
11050 
CheckCorruption(VmaAllocator hAllocator)11051 VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
11052 {
11053     void* pData = nullptr;
11054     VkResult res = Map(hAllocator, 1, &pData);
11055     if(res != VK_SUCCESS)
11056     {
11057         return res;
11058     }
11059 
11060     res = m_pMetadata->CheckCorruption(pData);
11061 
11062     Unmap(hAllocator, 1);
11063 
11064     return res;
11065 }
11066 
Map(VmaAllocator hAllocator,uint32_t count,void ** ppData)11067 VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
11068 {
11069     if(count == 0)
11070     {
11071         return VK_SUCCESS;
11072     }
11073 
11074     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11075     if(m_MapCount != 0)
11076     {
11077         m_MapCount += count;
11078         VMA_ASSERT(m_pMappedData != VMA_NULL);
11079         if(ppData != VMA_NULL)
11080         {
11081             *ppData = m_pMappedData;
11082         }
11083         return VK_SUCCESS;
11084     }
11085     else
11086     {
11087         VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
11088             hAllocator->m_hDevice,
11089             m_hMemory,
11090             0, // offset
11091             VK_WHOLE_SIZE,
11092             0, // flags
11093             &m_pMappedData);
11094         if(result == VK_SUCCESS)
11095         {
11096             if(ppData != VMA_NULL)
11097             {
11098                 *ppData = m_pMappedData;
11099             }
11100             m_MapCount = count;
11101         }
11102         return result;
11103     }
11104 }
11105 
Unmap(VmaAllocator hAllocator,uint32_t count)11106 void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
11107 {
11108     if(count == 0)
11109     {
11110         return;
11111     }
11112 
11113     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11114     if(m_MapCount >= count)
11115     {
11116         m_MapCount -= count;
11117         if(m_MapCount == 0)
11118         {
11119             m_pMappedData = VMA_NULL;
11120             (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
11121         }
11122     }
11123     else
11124     {
11125         VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
11126     }
11127 }
11128 
WriteMagicValueAroundAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)11129 VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
11130 {
11131     VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
11132     VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
11133 
11134     void* pData;
11135     VkResult res = Map(hAllocator, 1, &pData);
11136     if(res != VK_SUCCESS)
11137     {
11138         return res;
11139     }
11140 
11141     VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN);
11142     VmaWriteMagicValue(pData, allocOffset + allocSize);
11143 
11144     Unmap(hAllocator, 1);
11145 
11146     return VK_SUCCESS;
11147 }
11148 
ValidateMagicValueAroundAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)11149 VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
11150 {
11151     VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
11152     VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
11153 
11154     void* pData;
11155     VkResult res = Map(hAllocator, 1, &pData);
11156     if(res != VK_SUCCESS)
11157     {
11158         return res;
11159     }
11160 
11161     if(!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN))
11162     {
11163         VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
11164     }
11165     else if(!VmaValidateMagicValue(pData, allocOffset + allocSize))
11166     {
11167         VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
11168     }
11169 
11170     Unmap(hAllocator, 1);
11171 
11172     return VK_SUCCESS;
11173 }
11174 
BindBufferMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkBuffer hBuffer)11175 VkResult VmaDeviceMemoryBlock::BindBufferMemory(
11176     const VmaAllocator hAllocator,
11177     const VmaAllocation hAllocation,
11178     VkBuffer hBuffer)
11179 {
11180     VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
11181         hAllocation->GetBlock() == this);
11182     // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
11183     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11184     return hAllocator->GetVulkanFunctions().vkBindBufferMemory(
11185         hAllocator->m_hDevice,
11186         hBuffer,
11187         m_hMemory,
11188         hAllocation->GetOffset());
11189 }
11190 
BindImageMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkImage hImage)11191 VkResult VmaDeviceMemoryBlock::BindImageMemory(
11192     const VmaAllocator hAllocator,
11193     const VmaAllocation hAllocation,
11194     VkImage hImage)
11195 {
11196     VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
11197         hAllocation->GetBlock() == this);
11198     // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
11199     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11200     return hAllocator->GetVulkanFunctions().vkBindImageMemory(
11201         hAllocator->m_hDevice,
11202         hImage,
11203         m_hMemory,
11204         hAllocation->GetOffset());
11205 }
11206 
InitStatInfo(VmaStatInfo & outInfo)11207 static void InitStatInfo(VmaStatInfo& outInfo)
11208 {
11209     memset(&outInfo, 0, sizeof(outInfo));
11210     outInfo.allocationSizeMin = UINT64_MAX;
11211     outInfo.unusedRangeSizeMin = UINT64_MAX;
11212 }
11213 
11214 // Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
VmaAddStatInfo(VmaStatInfo & inoutInfo,const VmaStatInfo & srcInfo)11215 static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
11216 {
11217     inoutInfo.blockCount += srcInfo.blockCount;
11218     inoutInfo.allocationCount += srcInfo.allocationCount;
11219     inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
11220     inoutInfo.usedBytes += srcInfo.usedBytes;
11221     inoutInfo.unusedBytes += srcInfo.unusedBytes;
11222     inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
11223     inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
11224     inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
11225     inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
11226 }
11227 
VmaPostprocessCalcStatInfo(VmaStatInfo & inoutInfo)11228 static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
11229 {
11230     inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
11231         VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
11232     inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
11233         VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
11234 }
11235 
VmaPool_T(VmaAllocator hAllocator,const VmaPoolCreateInfo & createInfo,VkDeviceSize preferredBlockSize)11236 VmaPool_T::VmaPool_T(
11237     VmaAllocator hAllocator,
11238     const VmaPoolCreateInfo& createInfo,
11239     VkDeviceSize preferredBlockSize) :
11240     m_BlockVector(
11241         hAllocator,
11242         createInfo.memoryTypeIndex,
11243         createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
11244         createInfo.minBlockCount,
11245         createInfo.maxBlockCount,
11246         (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
11247         createInfo.frameInUseCount,
11248         true, // isCustomPool
11249         createInfo.blockSize != 0, // explicitBlockSize
11250         createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK), // algorithm
11251     m_Id(0)
11252 {
11253 }
11254 
~VmaPool_T()11255 VmaPool_T::~VmaPool_T()
11256 {
11257 }
11258 
11259 #if VMA_STATS_STRING_ENABLED
11260 
11261 #endif // #if VMA_STATS_STRING_ENABLED
11262 
VmaBlockVector(VmaAllocator hAllocator,uint32_t memoryTypeIndex,VkDeviceSize preferredBlockSize,size_t minBlockCount,size_t maxBlockCount,VkDeviceSize bufferImageGranularity,uint32_t frameInUseCount,bool isCustomPool,bool explicitBlockSize,uint32_t algorithm)11263 VmaBlockVector::VmaBlockVector(
11264     VmaAllocator hAllocator,
11265     uint32_t memoryTypeIndex,
11266     VkDeviceSize preferredBlockSize,
11267     size_t minBlockCount,
11268     size_t maxBlockCount,
11269     VkDeviceSize bufferImageGranularity,
11270     uint32_t frameInUseCount,
11271     bool isCustomPool,
11272     bool explicitBlockSize,
11273     uint32_t algorithm) :
11274     m_hAllocator(hAllocator),
11275     m_MemoryTypeIndex(memoryTypeIndex),
11276     m_PreferredBlockSize(preferredBlockSize),
11277     m_MinBlockCount(minBlockCount),
11278     m_MaxBlockCount(maxBlockCount),
11279     m_BufferImageGranularity(bufferImageGranularity),
11280     m_FrameInUseCount(frameInUseCount),
11281     m_IsCustomPool(isCustomPool),
11282     m_ExplicitBlockSize(explicitBlockSize),
11283     m_Algorithm(algorithm),
11284     m_HasEmptyBlock(false),
11285     m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
11286     m_NextBlockId(0)
11287 {
11288 }
11289 
~VmaBlockVector()11290 VmaBlockVector::~VmaBlockVector()
11291 {
11292     for(size_t i = m_Blocks.size(); i--; )
11293     {
11294         m_Blocks[i]->Destroy(m_hAllocator);
11295         vma_delete(m_hAllocator, m_Blocks[i]);
11296     }
11297 }
11298 
CreateMinBlocks()11299 VkResult VmaBlockVector::CreateMinBlocks()
11300 {
11301     for(size_t i = 0; i < m_MinBlockCount; ++i)
11302     {
11303         VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
11304         if(res != VK_SUCCESS)
11305         {
11306             return res;
11307         }
11308     }
11309     return VK_SUCCESS;
11310 }
11311 
GetPoolStats(VmaPoolStats * pStats)11312 void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
11313 {
11314     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
11315 
11316     const size_t blockCount = m_Blocks.size();
11317 
11318     pStats->size = 0;
11319     pStats->unusedSize = 0;
11320     pStats->allocationCount = 0;
11321     pStats->unusedRangeCount = 0;
11322     pStats->unusedRangeSizeMax = 0;
11323     pStats->blockCount = blockCount;
11324 
11325     for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
11326     {
11327         const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
11328         VMA_ASSERT(pBlock);
11329         VMA_HEAVY_ASSERT(pBlock->Validate());
11330         pBlock->m_pMetadata->AddPoolStats(*pStats);
11331     }
11332 }
11333 
IsCorruptionDetectionEnabled()11334 bool VmaBlockVector::IsCorruptionDetectionEnabled() const
11335 {
11336     const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
11337     return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
11338         (VMA_DEBUG_MARGIN > 0) &&
11339         (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
11340 }
11341 
11342 static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
11343 
Allocate(VmaPool hCurrentPool,uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)11344 VkResult VmaBlockVector::Allocate(
11345     VmaPool hCurrentPool,
11346     uint32_t currentFrameIndex,
11347     VkDeviceSize size,
11348     VkDeviceSize alignment,
11349     const VmaAllocationCreateInfo& createInfo,
11350     VmaSuballocationType suballocType,
11351     size_t allocationCount,
11352     VmaAllocation* pAllocations)
11353 {
11354     size_t allocIndex;
11355     VkResult res = VK_SUCCESS;
11356 
11357     {
11358         VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
11359         for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
11360         {
11361             res = AllocatePage(
11362                 hCurrentPool,
11363                 currentFrameIndex,
11364                 size,
11365                 alignment,
11366                 createInfo,
11367                 suballocType,
11368                 pAllocations + allocIndex);
11369             if(res != VK_SUCCESS)
11370             {
11371                 break;
11372             }
11373         }
11374     }
11375 
11376     if(res != VK_SUCCESS)
11377     {
11378         // Free all already created allocations.
11379         while(allocIndex--)
11380         {
11381             Free(pAllocations[allocIndex]);
11382         }
11383         memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
11384     }
11385 
11386     return res;
11387 }
11388 
AllocatePage(VmaPool hCurrentPool,uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)11389 VkResult VmaBlockVector::AllocatePage(
11390     VmaPool hCurrentPool,
11391     uint32_t currentFrameIndex,
11392     VkDeviceSize size,
11393     VkDeviceSize alignment,
11394     const VmaAllocationCreateInfo& createInfo,
11395     VmaSuballocationType suballocType,
11396     VmaAllocation* pAllocation)
11397 {
11398     const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
11399     bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
11400     const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
11401     const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
11402     const bool canCreateNewBlock =
11403         ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
11404         (m_Blocks.size() < m_MaxBlockCount);
11405     uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
11406 
11407     // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
11408     // Which in turn is available only when maxBlockCount = 1.
11409     if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
11410     {
11411         canMakeOtherLost = false;
11412     }
11413 
11414     // Upper address can only be used with linear allocator and within single memory block.
11415     if(isUpperAddress &&
11416         (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
11417     {
11418         return VK_ERROR_FEATURE_NOT_PRESENT;
11419     }
11420 
11421     // Validate strategy.
11422     switch(strategy)
11423     {
11424     case 0:
11425         strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
11426         break;
11427     case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
11428     case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
11429     case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
11430         break;
11431     default:
11432         return VK_ERROR_FEATURE_NOT_PRESENT;
11433     }
11434 
11435     // Early reject: requested allocation size is larger that maximum block size for this block vector.
11436     if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
11437     {
11438         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11439     }
11440 
11441     /*
11442     Under certain condition, this whole section can be skipped for optimization, so
11443     we move on directly to trying to allocate with canMakeOtherLost. That's the case
11444     e.g. for custom pools with linear algorithm.
11445     */
11446     if(!canMakeOtherLost || canCreateNewBlock)
11447     {
11448         // 1. Search existing allocations. Try to allocate without making other allocations lost.
11449         VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
11450         allocFlagsCopy &= ~VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
11451 
11452         if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
11453         {
11454             // Use only last block.
11455             if(!m_Blocks.empty())
11456             {
11457                 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
11458                 VMA_ASSERT(pCurrBlock);
11459                 VkResult res = AllocateFromBlock(
11460                     pCurrBlock,
11461                     hCurrentPool,
11462                     currentFrameIndex,
11463                     size,
11464                     alignment,
11465                     allocFlagsCopy,
11466                     createInfo.pUserData,
11467                     suballocType,
11468                     strategy,
11469                     pAllocation);
11470                 if(res == VK_SUCCESS)
11471                 {
11472                     VMA_DEBUG_LOG("    Returned from last block #%u", (uint32_t)(m_Blocks.size() - 1));
11473                     return VK_SUCCESS;
11474                 }
11475             }
11476         }
11477         else
11478         {
11479             if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
11480             {
11481                 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
11482                 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
11483                 {
11484                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11485                     VMA_ASSERT(pCurrBlock);
11486                     VkResult res = AllocateFromBlock(
11487                         pCurrBlock,
11488                         hCurrentPool,
11489                         currentFrameIndex,
11490                         size,
11491                         alignment,
11492                         allocFlagsCopy,
11493                         createInfo.pUserData,
11494                         suballocType,
11495                         strategy,
11496                         pAllocation);
11497                     if(res == VK_SUCCESS)
11498                     {
11499                         VMA_DEBUG_LOG("    Returned from existing block #%u", (uint32_t)blockIndex);
11500                         return VK_SUCCESS;
11501                     }
11502                 }
11503             }
11504             else // WORST_FIT, FIRST_FIT
11505             {
11506                 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
11507                 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
11508                 {
11509                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11510                     VMA_ASSERT(pCurrBlock);
11511                     VkResult res = AllocateFromBlock(
11512                         pCurrBlock,
11513                         hCurrentPool,
11514                         currentFrameIndex,
11515                         size,
11516                         alignment,
11517                         allocFlagsCopy,
11518                         createInfo.pUserData,
11519                         suballocType,
11520                         strategy,
11521                         pAllocation);
11522                     if(res == VK_SUCCESS)
11523                     {
11524                         VMA_DEBUG_LOG("    Returned from existing block #%u", (uint32_t)blockIndex);
11525                         return VK_SUCCESS;
11526                     }
11527                 }
11528             }
11529         }
11530 
11531         // 2. Try to create new block.
11532         if(canCreateNewBlock)
11533         {
11534             // Calculate optimal size for new block.
11535             VkDeviceSize newBlockSize = m_PreferredBlockSize;
11536             uint32_t newBlockSizeShift = 0;
11537             const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
11538 
11539             if(!m_ExplicitBlockSize)
11540             {
11541                 // Allocate 1/8, 1/4, 1/2 as first blocks.
11542                 const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
11543                 for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
11544                 {
11545                     const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
11546                     if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
11547                     {
11548                         newBlockSize = smallerNewBlockSize;
11549                         ++newBlockSizeShift;
11550                     }
11551                     else
11552                     {
11553                         break;
11554                     }
11555                 }
11556             }
11557 
11558             size_t newBlockIndex = 0;
11559             VkResult res = CreateBlock(newBlockSize, &newBlockIndex);
11560             // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
11561             if(!m_ExplicitBlockSize)
11562             {
11563                 while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
11564                 {
11565                     const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
11566                     if(smallerNewBlockSize >= size)
11567                     {
11568                         newBlockSize = smallerNewBlockSize;
11569                         ++newBlockSizeShift;
11570                         res = CreateBlock(newBlockSize, &newBlockIndex);
11571                     }
11572                     else
11573                     {
11574                         break;
11575                     }
11576                 }
11577             }
11578 
11579             if(res == VK_SUCCESS)
11580             {
11581                 VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
11582                 VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
11583 
11584                 res = AllocateFromBlock(
11585                     pBlock,
11586                     hCurrentPool,
11587                     currentFrameIndex,
11588                     size,
11589                     alignment,
11590                     allocFlagsCopy,
11591                     createInfo.pUserData,
11592                     suballocType,
11593                     strategy,
11594                     pAllocation);
11595                 if(res == VK_SUCCESS)
11596                 {
11597                     VMA_DEBUG_LOG("    Created new block Size=%llu", newBlockSize);
11598                     return VK_SUCCESS;
11599                 }
11600                 else
11601                 {
11602                     // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
11603                     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11604                 }
11605             }
11606         }
11607     }
11608 
11609     // 3. Try to allocate from existing blocks with making other allocations lost.
11610     if(canMakeOtherLost)
11611     {
11612         uint32_t tryIndex = 0;
11613         for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
11614         {
11615             VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
11616             VmaAllocationRequest bestRequest = {};
11617             VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
11618 
11619             // 1. Search existing allocations.
11620             if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
11621             {
11622                 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
11623                 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
11624                 {
11625                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11626                     VMA_ASSERT(pCurrBlock);
11627                     VmaAllocationRequest currRequest = {};
11628                     if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
11629                         currentFrameIndex,
11630                         m_FrameInUseCount,
11631                         m_BufferImageGranularity,
11632                         size,
11633                         alignment,
11634                         (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
11635                         suballocType,
11636                         canMakeOtherLost,
11637                         strategy,
11638                         &currRequest))
11639                     {
11640                         const VkDeviceSize currRequestCost = currRequest.CalcCost();
11641                         if(pBestRequestBlock == VMA_NULL ||
11642                             currRequestCost < bestRequestCost)
11643                         {
11644                             pBestRequestBlock = pCurrBlock;
11645                             bestRequest = currRequest;
11646                             bestRequestCost = currRequestCost;
11647 
11648                             if(bestRequestCost == 0)
11649                             {
11650                                 break;
11651                             }
11652                         }
11653                     }
11654                 }
11655             }
11656             else // WORST_FIT, FIRST_FIT
11657             {
11658                 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
11659                 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
11660                 {
11661                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11662                     VMA_ASSERT(pCurrBlock);
11663                     VmaAllocationRequest currRequest = {};
11664                     if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
11665                         currentFrameIndex,
11666                         m_FrameInUseCount,
11667                         m_BufferImageGranularity,
11668                         size,
11669                         alignment,
11670                         (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
11671                         suballocType,
11672                         canMakeOtherLost,
11673                         strategy,
11674                         &currRequest))
11675                     {
11676                         const VkDeviceSize currRequestCost = currRequest.CalcCost();
11677                         if(pBestRequestBlock == VMA_NULL ||
11678                             currRequestCost < bestRequestCost ||
11679                             strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
11680                         {
11681                             pBestRequestBlock = pCurrBlock;
11682                             bestRequest = currRequest;
11683                             bestRequestCost = currRequestCost;
11684 
11685                             if(bestRequestCost == 0 ||
11686                                 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
11687                             {
11688                                 break;
11689                             }
11690                         }
11691                     }
11692                 }
11693             }
11694 
11695             if(pBestRequestBlock != VMA_NULL)
11696             {
11697                 if(mapped)
11698                 {
11699                     VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
11700                     if(res != VK_SUCCESS)
11701                     {
11702                         return res;
11703                     }
11704                 }
11705 
11706                 if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
11707                     currentFrameIndex,
11708                     m_FrameInUseCount,
11709                     &bestRequest))
11710                 {
11711                     // We no longer have an empty Allocation.
11712                     if(pBestRequestBlock->m_pMetadata->IsEmpty())
11713                     {
11714                         m_HasEmptyBlock = false;
11715                     }
11716                     // Allocate from this pBlock.
11717                     *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
11718                     pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, isUpperAddress, *pAllocation);
11719                     (*pAllocation)->InitBlockAllocation(
11720                         hCurrentPool,
11721                         pBestRequestBlock,
11722                         bestRequest.offset,
11723                         alignment,
11724                         size,
11725                         suballocType,
11726                         mapped,
11727                         (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
11728                     VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
11729                     VMA_DEBUG_LOG("    Returned from existing allocation #%u", (uint32_t)blockIndex);
11730                     (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
11731                     if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
11732                     {
11733                         m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
11734                     }
11735                     if(IsCorruptionDetectionEnabled())
11736                     {
11737                         VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
11738                         VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
11739                     }
11740                     return VK_SUCCESS;
11741                 }
11742                 // else: Some allocations must have been touched while we are here. Next try.
11743             }
11744             else
11745             {
11746                 // Could not find place in any of the blocks - break outer loop.
11747                 break;
11748             }
11749         }
11750         /* Maximum number of tries exceeded - a very unlike event when many other
11751         threads are simultaneously touching allocations making it impossible to make
11752         lost at the same time as we try to allocate. */
11753         if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
11754         {
11755             return VK_ERROR_TOO_MANY_OBJECTS;
11756         }
11757     }
11758 
11759     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11760 }
11761 
Free(VmaAllocation hAllocation)11762 void VmaBlockVector::Free(
11763     VmaAllocation hAllocation)
11764 {
11765     VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
11766 
11767     // Scope for lock.
11768     {
11769         VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
11770 
11771         VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
11772 
11773         if(IsCorruptionDetectionEnabled())
11774         {
11775             VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
11776             VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
11777         }
11778 
11779         if(hAllocation->IsPersistentMap())
11780         {
11781             pBlock->Unmap(m_hAllocator, 1);
11782         }
11783 
11784         pBlock->m_pMetadata->Free(hAllocation);
11785         VMA_HEAVY_ASSERT(pBlock->Validate());
11786 
11787         VMA_DEBUG_LOG("  Freed from MemoryTypeIndex=%u", memTypeIndex);
11788 
11789         // pBlock became empty after this deallocation.
11790         if(pBlock->m_pMetadata->IsEmpty())
11791         {
11792             // Already has empty Allocation. We don't want to have two, so delete this one.
11793             if(m_HasEmptyBlock && m_Blocks.size() > m_MinBlockCount)
11794             {
11795                 pBlockToDelete = pBlock;
11796                 Remove(pBlock);
11797             }
11798             // We now have first empty block.
11799             else
11800             {
11801                 m_HasEmptyBlock = true;
11802             }
11803         }
11804         // pBlock didn't become empty, but we have another empty block - find and free that one.
11805         // (This is optional, heuristics.)
11806         else if(m_HasEmptyBlock)
11807         {
11808             VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
11809             if(pLastBlock->m_pMetadata->IsEmpty() && m_Blocks.size() > m_MinBlockCount)
11810             {
11811                 pBlockToDelete = pLastBlock;
11812                 m_Blocks.pop_back();
11813                 m_HasEmptyBlock = false;
11814             }
11815         }
11816 
11817         IncrementallySortBlocks();
11818     }
11819 
11820     // Destruction of a free Allocation. Deferred until this point, outside of mutex
11821     // lock, for performance reason.
11822     if(pBlockToDelete != VMA_NULL)
11823     {
11824         VMA_DEBUG_LOG("    Deleted empty allocation");
11825         pBlockToDelete->Destroy(m_hAllocator);
11826         vma_delete(m_hAllocator, pBlockToDelete);
11827     }
11828 }
11829 
CalcMaxBlockSize()11830 VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
11831 {
11832     VkDeviceSize result = 0;
11833     for(size_t i = m_Blocks.size(); i--; )
11834     {
11835         result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
11836         if(result >= m_PreferredBlockSize)
11837         {
11838             break;
11839         }
11840     }
11841     return result;
11842 }
11843 
Remove(VmaDeviceMemoryBlock * pBlock)11844 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
11845 {
11846     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
11847     {
11848         if(m_Blocks[blockIndex] == pBlock)
11849         {
11850             VmaVectorRemove(m_Blocks, blockIndex);
11851             return;
11852         }
11853     }
11854     VMA_ASSERT(0);
11855 }
11856 
IncrementallySortBlocks()11857 void VmaBlockVector::IncrementallySortBlocks()
11858 {
11859     if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
11860     {
11861         // Bubble sort only until first swap.
11862         for(size_t i = 1; i < m_Blocks.size(); ++i)
11863         {
11864             if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
11865             {
11866                 VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
11867                 return;
11868             }
11869         }
11870     }
11871 }
11872 
AllocateFromBlock(VmaDeviceMemoryBlock * pBlock,VmaPool hCurrentPool,uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,VmaAllocationCreateFlags allocFlags,void * pUserData,VmaSuballocationType suballocType,uint32_t strategy,VmaAllocation * pAllocation)11873 VkResult VmaBlockVector::AllocateFromBlock(
11874     VmaDeviceMemoryBlock* pBlock,
11875     VmaPool hCurrentPool,
11876     uint32_t currentFrameIndex,
11877     VkDeviceSize size,
11878     VkDeviceSize alignment,
11879     VmaAllocationCreateFlags allocFlags,
11880     void* pUserData,
11881     VmaSuballocationType suballocType,
11882     uint32_t strategy,
11883     VmaAllocation* pAllocation)
11884 {
11885     VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
11886     const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
11887     const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
11888     const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
11889 
11890     VmaAllocationRequest currRequest = {};
11891     if(pBlock->m_pMetadata->CreateAllocationRequest(
11892         currentFrameIndex,
11893         m_FrameInUseCount,
11894         m_BufferImageGranularity,
11895         size,
11896         alignment,
11897         isUpperAddress,
11898         suballocType,
11899         false, // canMakeOtherLost
11900         strategy,
11901         &currRequest))
11902     {
11903         // Allocate from pCurrBlock.
11904         VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
11905 
11906         if(mapped)
11907         {
11908             VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
11909             if(res != VK_SUCCESS)
11910             {
11911                 return res;
11912             }
11913         }
11914 
11915         // We no longer have an empty Allocation.
11916         if(pBlock->m_pMetadata->IsEmpty())
11917         {
11918             m_HasEmptyBlock = false;
11919         }
11920 
11921         *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
11922         pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, isUpperAddress, *pAllocation);
11923         (*pAllocation)->InitBlockAllocation(
11924             hCurrentPool,
11925             pBlock,
11926             currRequest.offset,
11927             alignment,
11928             size,
11929             suballocType,
11930             mapped,
11931             (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
11932         VMA_HEAVY_ASSERT(pBlock->Validate());
11933         (*pAllocation)->SetUserData(m_hAllocator, pUserData);
11934         if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
11935         {
11936             m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
11937         }
11938         if(IsCorruptionDetectionEnabled())
11939         {
11940             VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
11941             VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
11942         }
11943         return VK_SUCCESS;
11944     }
11945     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11946 }
11947 
CreateBlock(VkDeviceSize blockSize,size_t * pNewBlockIndex)11948 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
11949 {
11950     VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
11951     allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
11952     allocInfo.allocationSize = blockSize;
11953     VkDeviceMemory mem = VK_NULL_HANDLE;
11954     VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
11955     if(res < 0)
11956     {
11957         return res;
11958     }
11959 
11960     // New VkDeviceMemory successfully created.
11961 
11962     // Create new Allocation for it.
11963     VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
11964     pBlock->Init(
11965         m_hAllocator,
11966         m_MemoryTypeIndex,
11967         mem,
11968         allocInfo.allocationSize,
11969         m_NextBlockId++,
11970         m_Algorithm);
11971 
11972     m_Blocks.push_back(pBlock);
11973     if(pNewBlockIndex != VMA_NULL)
11974     {
11975         *pNewBlockIndex = m_Blocks.size() - 1;
11976     }
11977 
11978     return VK_SUCCESS;
11979 }
11980 
ApplyDefragmentationMovesCpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,const VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves)11981 void VmaBlockVector::ApplyDefragmentationMovesCpu(
11982     class VmaBlockVectorDefragmentationContext* pDefragCtx,
11983     const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
11984 {
11985     const size_t blockCount = m_Blocks.size();
11986     const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
11987 
11988     enum BLOCK_FLAG
11989     {
11990         BLOCK_FLAG_USED = 0x00000001,
11991         BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
11992     };
11993 
11994     struct BlockInfo
11995     {
11996         uint32_t flags;
11997         void* pMappedData;
11998     };
11999     VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
12000         blockInfo(blockCount, VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
12001     memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
12002 
12003     // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
12004     const size_t moveCount = moves.size();
12005     for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12006     {
12007         const VmaDefragmentationMove& move = moves[moveIndex];
12008         blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
12009         blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
12010     }
12011 
12012     VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
12013 
12014     // Go over all blocks. Get mapped pointer or map if necessary.
12015     for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
12016     {
12017         BlockInfo& currBlockInfo = blockInfo[blockIndex];
12018         VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12019         if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
12020         {
12021             currBlockInfo.pMappedData = pBlock->GetMappedData();
12022             // It is not originally mapped - map it.
12023             if(currBlockInfo.pMappedData == VMA_NULL)
12024             {
12025                 pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
12026                 if(pDefragCtx->res == VK_SUCCESS)
12027                 {
12028                     currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
12029                 }
12030             }
12031         }
12032     }
12033 
12034     // Go over all moves. Do actual data transfer.
12035     if(pDefragCtx->res == VK_SUCCESS)
12036     {
12037         const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
12038         VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
12039 
12040         for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12041         {
12042             const VmaDefragmentationMove& move = moves[moveIndex];
12043 
12044             const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
12045             const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
12046 
12047             VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
12048 
12049             // Invalidate source.
12050             if(isNonCoherent)
12051             {
12052                 VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
12053                 memRange.memory = pSrcBlock->GetDeviceMemory();
12054                 memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
12055                 memRange.size = VMA_MIN(
12056                     VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
12057                     pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
12058                 (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
12059             }
12060 
12061             // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
12062             memmove(
12063                 reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
12064                 reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
12065                 static_cast<size_t>(move.size));
12066 
12067             if(IsCorruptionDetectionEnabled())
12068             {
12069                 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
12070                 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
12071             }
12072 
12073             // Flush destination.
12074             if(isNonCoherent)
12075             {
12076                 VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
12077                 memRange.memory = pDstBlock->GetDeviceMemory();
12078                 memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
12079                 memRange.size = VMA_MIN(
12080                     VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
12081                     pDstBlock->m_pMetadata->GetSize() - memRange.offset);
12082                 (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
12083             }
12084         }
12085     }
12086 
12087     // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
12088     // Regardless of pCtx->res == VK_SUCCESS.
12089     for(size_t blockIndex = blockCount; blockIndex--; )
12090     {
12091         const BlockInfo& currBlockInfo = blockInfo[blockIndex];
12092         if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
12093         {
12094             VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12095             pBlock->Unmap(m_hAllocator, 1);
12096         }
12097     }
12098 }
12099 
ApplyDefragmentationMovesGpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,const VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkCommandBuffer commandBuffer)12100 void VmaBlockVector::ApplyDefragmentationMovesGpu(
12101     class VmaBlockVectorDefragmentationContext* pDefragCtx,
12102     const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12103     VkCommandBuffer commandBuffer)
12104 {
12105     const size_t blockCount = m_Blocks.size();
12106 
12107     pDefragCtx->blockContexts.resize(blockCount);
12108     memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext));
12109 
12110     // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
12111     const size_t moveCount = moves.size();
12112     for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12113     {
12114         const VmaDefragmentationMove& move = moves[moveIndex];
12115         pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
12116         pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
12117     }
12118 
12119     VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
12120 
12121     // Go over all blocks. Create and bind buffer for whole block if necessary.
12122     {
12123         VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
12124         bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
12125             VK_BUFFER_USAGE_TRANSFER_DST_BIT;
12126 
12127         for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
12128         {
12129             VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
12130             VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12131             if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
12132             {
12133                 bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
12134                 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
12135                     m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
12136                 if(pDefragCtx->res == VK_SUCCESS)
12137                 {
12138                     pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
12139                         m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
12140                 }
12141             }
12142         }
12143     }
12144 
12145     // Go over all moves. Post data transfer commands to command buffer.
12146     if(pDefragCtx->res == VK_SUCCESS)
12147     {
12148         const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
12149         VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
12150 
12151         for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12152         {
12153             const VmaDefragmentationMove& move = moves[moveIndex];
12154 
12155             const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
12156             const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
12157 
12158             VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
12159 
12160             VkBufferCopy region = {
12161                 move.srcOffset,
12162                 move.dstOffset,
12163                 move.size };
12164             (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
12165                 commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, &region);
12166         }
12167     }
12168 
12169     // Save buffers to defrag context for later destruction.
12170     if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
12171     {
12172         pDefragCtx->res = VK_NOT_READY;
12173     }
12174 }
12175 
FreeEmptyBlocks(VmaDefragmentationStats * pDefragmentationStats)12176 void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
12177 {
12178     m_HasEmptyBlock = false;
12179     for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12180     {
12181         VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12182         if(pBlock->m_pMetadata->IsEmpty())
12183         {
12184             if(m_Blocks.size() > m_MinBlockCount)
12185             {
12186                 if(pDefragmentationStats != VMA_NULL)
12187                 {
12188                     ++pDefragmentationStats->deviceMemoryBlocksFreed;
12189                     pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
12190                 }
12191 
12192                 VmaVectorRemove(m_Blocks, blockIndex);
12193                 pBlock->Destroy(m_hAllocator);
12194                 vma_delete(m_hAllocator, pBlock);
12195             }
12196             else
12197             {
12198                 m_HasEmptyBlock = true;
12199             }
12200         }
12201     }
12202 }
12203 
12204 #if VMA_STATS_STRING_ENABLED
12205 
PrintDetailedMap(class VmaJsonWriter & json)12206 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
12207 {
12208     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12209 
12210     json.BeginObject();
12211 
12212     if(m_IsCustomPool)
12213     {
12214         json.WriteString("MemoryTypeIndex");
12215         json.WriteNumber(m_MemoryTypeIndex);
12216 
12217         json.WriteString("BlockSize");
12218         json.WriteNumber(m_PreferredBlockSize);
12219 
12220         json.WriteString("BlockCount");
12221         json.BeginObject(true);
12222         if(m_MinBlockCount > 0)
12223         {
12224             json.WriteString("Min");
12225             json.WriteNumber((uint64_t)m_MinBlockCount);
12226         }
12227         if(m_MaxBlockCount < SIZE_MAX)
12228         {
12229             json.WriteString("Max");
12230             json.WriteNumber((uint64_t)m_MaxBlockCount);
12231         }
12232         json.WriteString("Cur");
12233         json.WriteNumber((uint64_t)m_Blocks.size());
12234         json.EndObject();
12235 
12236         if(m_FrameInUseCount > 0)
12237         {
12238             json.WriteString("FrameInUseCount");
12239             json.WriteNumber(m_FrameInUseCount);
12240         }
12241 
12242         if(m_Algorithm != 0)
12243         {
12244             json.WriteString("Algorithm");
12245             json.WriteString(VmaAlgorithmToStr(m_Algorithm));
12246         }
12247     }
12248     else
12249     {
12250         json.WriteString("PreferredBlockSize");
12251         json.WriteNumber(m_PreferredBlockSize);
12252     }
12253 
12254     json.WriteString("Blocks");
12255     json.BeginObject();
12256     for(size_t i = 0; i < m_Blocks.size(); ++i)
12257     {
12258         json.BeginString();
12259         json.ContinueString(m_Blocks[i]->GetId());
12260         json.EndString();
12261 
12262         m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
12263     }
12264     json.EndObject();
12265 
12266     json.EndObject();
12267 }
12268 
12269 #endif // #if VMA_STATS_STRING_ENABLED
12270 
Defragment(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats,VkDeviceSize & maxCpuBytesToMove,uint32_t & maxCpuAllocationsToMove,VkDeviceSize & maxGpuBytesToMove,uint32_t & maxGpuAllocationsToMove,VkCommandBuffer commandBuffer)12271 void VmaBlockVector::Defragment(
12272     class VmaBlockVectorDefragmentationContext* pCtx,
12273     VmaDefragmentationStats* pStats,
12274     VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
12275     VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
12276     VkCommandBuffer commandBuffer)
12277 {
12278     pCtx->res = VK_SUCCESS;
12279 
12280     const VkMemoryPropertyFlags memPropFlags =
12281         m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
12282     const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
12283     const bool isHostCoherent = (memPropFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
12284 
12285     const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
12286         isHostVisible;
12287     const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
12288         (VMA_DEBUG_DETECT_CORRUPTION == 0 || !(isHostVisible && isHostCoherent));
12289 
12290     // There are options to defragment this memory type.
12291     if(canDefragmentOnCpu || canDefragmentOnGpu)
12292     {
12293         bool defragmentOnGpu;
12294         // There is only one option to defragment this memory type.
12295         if(canDefragmentOnGpu != canDefragmentOnCpu)
12296         {
12297             defragmentOnGpu = canDefragmentOnGpu;
12298         }
12299         // Both options are available: Heuristics to choose the best one.
12300         else
12301         {
12302             defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
12303                 m_hAllocator->IsIntegratedGpu();
12304         }
12305 
12306         bool overlappingMoveSupported = !defragmentOnGpu;
12307 
12308         if(m_hAllocator->m_UseMutex)
12309         {
12310             m_Mutex.LockWrite();
12311             pCtx->mutexLocked = true;
12312         }
12313 
12314         pCtx->Begin(overlappingMoveSupported);
12315 
12316         // Defragment.
12317 
12318         const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
12319         const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
12320         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > moves =
12321             VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >(VmaStlAllocator<VmaDefragmentationMove>(m_hAllocator->GetAllocationCallbacks()));
12322         pCtx->res = pCtx->GetAlgorithm()->Defragment(moves, maxBytesToMove, maxAllocationsToMove);
12323 
12324         // Accumulate statistics.
12325         if(pStats != VMA_NULL)
12326         {
12327             const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
12328             const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
12329             pStats->bytesMoved += bytesMoved;
12330             pStats->allocationsMoved += allocationsMoved;
12331             VMA_ASSERT(bytesMoved <= maxBytesToMove);
12332             VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
12333             if(defragmentOnGpu)
12334             {
12335                 maxGpuBytesToMove -= bytesMoved;
12336                 maxGpuAllocationsToMove -= allocationsMoved;
12337             }
12338             else
12339             {
12340                 maxCpuBytesToMove -= bytesMoved;
12341                 maxCpuAllocationsToMove -= allocationsMoved;
12342             }
12343         }
12344 
12345         if(pCtx->res >= VK_SUCCESS)
12346         {
12347             if(defragmentOnGpu)
12348             {
12349                 ApplyDefragmentationMovesGpu(pCtx, moves, commandBuffer);
12350             }
12351             else
12352             {
12353                 ApplyDefragmentationMovesCpu(pCtx, moves);
12354             }
12355         }
12356     }
12357 }
12358 
DefragmentationEnd(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats)12359 void VmaBlockVector::DefragmentationEnd(
12360     class VmaBlockVectorDefragmentationContext* pCtx,
12361     VmaDefragmentationStats* pStats)
12362 {
12363     // Destroy buffers.
12364     for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--; )
12365     {
12366         VmaBlockDefragmentationContext& blockCtx = pCtx->blockContexts[blockIndex];
12367         if(blockCtx.hBuffer)
12368         {
12369             (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(
12370                 m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
12371         }
12372     }
12373 
12374     if(pCtx->res >= VK_SUCCESS)
12375     {
12376         FreeEmptyBlocks(pStats);
12377     }
12378 
12379     if(pCtx->mutexLocked)
12380     {
12381         VMA_ASSERT(m_hAllocator->m_UseMutex);
12382         m_Mutex.UnlockWrite();
12383     }
12384 }
12385 
CalcAllocationCount()12386 size_t VmaBlockVector::CalcAllocationCount() const
12387 {
12388     size_t result = 0;
12389     for(size_t i = 0; i < m_Blocks.size(); ++i)
12390     {
12391         result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
12392     }
12393     return result;
12394 }
12395 
IsBufferImageGranularityConflictPossible()12396 bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
12397 {
12398     if(m_BufferImageGranularity == 1)
12399     {
12400         return false;
12401     }
12402     VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
12403     for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
12404     {
12405         VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
12406         VMA_ASSERT(m_Algorithm == 0);
12407         VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
12408         if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
12409         {
12410             return true;
12411         }
12412     }
12413     return false;
12414 }
12415 
MakePoolAllocationsLost(uint32_t currentFrameIndex,size_t * pLostAllocationCount)12416 void VmaBlockVector::MakePoolAllocationsLost(
12417     uint32_t currentFrameIndex,
12418     size_t* pLostAllocationCount)
12419 {
12420     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12421     size_t lostAllocationCount = 0;
12422     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12423     {
12424         VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12425         VMA_ASSERT(pBlock);
12426         lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
12427     }
12428     if(pLostAllocationCount != VMA_NULL)
12429     {
12430         *pLostAllocationCount = lostAllocationCount;
12431     }
12432 }
12433 
CheckCorruption()12434 VkResult VmaBlockVector::CheckCorruption()
12435 {
12436     if(!IsCorruptionDetectionEnabled())
12437     {
12438         return VK_ERROR_FEATURE_NOT_PRESENT;
12439     }
12440 
12441     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12442     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12443     {
12444         VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12445         VMA_ASSERT(pBlock);
12446         VkResult res = pBlock->CheckCorruption(m_hAllocator);
12447         if(res != VK_SUCCESS)
12448         {
12449             return res;
12450         }
12451     }
12452     return VK_SUCCESS;
12453 }
12454 
AddStats(VmaStats * pStats)12455 void VmaBlockVector::AddStats(VmaStats* pStats)
12456 {
12457     const uint32_t memTypeIndex = m_MemoryTypeIndex;
12458     const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
12459 
12460     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12461 
12462     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12463     {
12464         const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12465         VMA_ASSERT(pBlock);
12466         VMA_HEAVY_ASSERT(pBlock->Validate());
12467         VmaStatInfo allocationStatInfo;
12468         pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
12469         VmaAddStatInfo(pStats->total, allocationStatInfo);
12470         VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
12471         VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
12472     }
12473 }
12474 
12475 ////////////////////////////////////////////////////////////////////////////////
12476 // VmaDefragmentationAlgorithm_Generic members definition
12477 
VmaDefragmentationAlgorithm_Generic(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)12478 VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
12479     VmaAllocator hAllocator,
12480     VmaBlockVector* pBlockVector,
12481     uint32_t currentFrameIndex,
12482     bool overlappingMoveSupported) :
12483     VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
12484     m_AllocationCount(0),
12485     m_AllAllocations(false),
12486     m_BytesMoved(0),
12487     m_AllocationsMoved(0),
12488     m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
12489 {
12490     // Create block info for each block.
12491     const size_t blockCount = m_pBlockVector->m_Blocks.size();
12492     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12493     {
12494         BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
12495         pBlockInfo->m_OriginalBlockIndex = blockIndex;
12496         pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
12497         m_Blocks.push_back(pBlockInfo);
12498     }
12499 
12500     // Sort them by m_pBlock pointer value.
12501     VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
12502 }
12503 
~VmaDefragmentationAlgorithm_Generic()12504 VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
12505 {
12506     for(size_t i = m_Blocks.size(); i--; )
12507     {
12508         vma_delete(m_hAllocator, m_Blocks[i]);
12509     }
12510 }
12511 
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)12512 void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
12513 {
12514     // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
12515     if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
12516     {
12517         VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
12518         BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
12519         if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
12520         {
12521             AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
12522             (*it)->m_Allocations.push_back(allocInfo);
12523         }
12524         else
12525         {
12526             VMA_ASSERT(0);
12527         }
12528 
12529         ++m_AllocationCount;
12530     }
12531 }
12532 
DefragmentRound(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove)12533 VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
12534     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12535     VkDeviceSize maxBytesToMove,
12536     uint32_t maxAllocationsToMove)
12537 {
12538     if(m_Blocks.empty())
12539     {
12540         return VK_SUCCESS;
12541     }
12542 
12543     // This is a choice based on research.
12544     // Option 1:
12545     uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
12546     // Option 2:
12547     //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
12548     // Option 3:
12549     //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
12550 
12551     size_t srcBlockMinIndex = 0;
12552     // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
12553     /*
12554     if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
12555     {
12556         const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
12557         if(blocksWithNonMovableCount > 0)
12558         {
12559             srcBlockMinIndex = blocksWithNonMovableCount - 1;
12560         }
12561     }
12562     */
12563 
12564     size_t srcBlockIndex = m_Blocks.size() - 1;
12565     size_t srcAllocIndex = SIZE_MAX;
12566     for(;;)
12567     {
12568         // 1. Find next allocation to move.
12569         // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
12570         // 1.2. Then start from last to first m_Allocations.
12571         while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
12572         {
12573             if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
12574             {
12575                 // Finished: no more allocations to process.
12576                 if(srcBlockIndex == srcBlockMinIndex)
12577                 {
12578                     return VK_SUCCESS;
12579                 }
12580                 else
12581                 {
12582                     --srcBlockIndex;
12583                     srcAllocIndex = SIZE_MAX;
12584                 }
12585             }
12586             else
12587             {
12588                 srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
12589             }
12590         }
12591 
12592         BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
12593         AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
12594 
12595         const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
12596         const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
12597         const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
12598         const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
12599 
12600         // 2. Try to find new place for this allocation in preceding or current block.
12601         for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
12602         {
12603             BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
12604             VmaAllocationRequest dstAllocRequest;
12605             if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
12606                 m_CurrentFrameIndex,
12607                 m_pBlockVector->GetFrameInUseCount(),
12608                 m_pBlockVector->GetBufferImageGranularity(),
12609                 size,
12610                 alignment,
12611                 false, // upperAddress
12612                 suballocType,
12613                 false, // canMakeOtherLost
12614                 strategy,
12615                 &dstAllocRequest) &&
12616             MoveMakesSense(
12617                 dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
12618             {
12619                 VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
12620 
12621                 // Reached limit on number of allocations or bytes to move.
12622                 if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
12623                     (m_BytesMoved + size > maxBytesToMove))
12624                 {
12625                     return VK_SUCCESS;
12626                 }
12627 
12628                 VmaDefragmentationMove move;
12629                 move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
12630                 move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
12631                 move.srcOffset = srcOffset;
12632                 move.dstOffset = dstAllocRequest.offset;
12633                 move.size = size;
12634                 moves.push_back(move);
12635 
12636                 pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
12637                     dstAllocRequest,
12638                     suballocType,
12639                     size,
12640                     false, // upperAddress
12641                     allocInfo.m_hAllocation);
12642                 pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
12643 
12644                 allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
12645 
12646                 if(allocInfo.m_pChanged != VMA_NULL)
12647                 {
12648                     *allocInfo.m_pChanged = VK_TRUE;
12649                 }
12650 
12651                 ++m_AllocationsMoved;
12652                 m_BytesMoved += size;
12653 
12654                 VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
12655 
12656                 break;
12657             }
12658         }
12659 
12660         // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
12661 
12662         if(srcAllocIndex > 0)
12663         {
12664             --srcAllocIndex;
12665         }
12666         else
12667         {
12668             if(srcBlockIndex > 0)
12669             {
12670                 --srcBlockIndex;
12671                 srcAllocIndex = SIZE_MAX;
12672             }
12673             else
12674             {
12675                 return VK_SUCCESS;
12676             }
12677         }
12678     }
12679 }
12680 
CalcBlocksWithNonMovableCount()12681 size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
12682 {
12683     size_t result = 0;
12684     for(size_t i = 0; i < m_Blocks.size(); ++i)
12685     {
12686         if(m_Blocks[i]->m_HasNonMovableAllocations)
12687         {
12688             ++result;
12689         }
12690     }
12691     return result;
12692 }
12693 
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove)12694 VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
12695     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12696     VkDeviceSize maxBytesToMove,
12697     uint32_t maxAllocationsToMove)
12698 {
12699     if(!m_AllAllocations && m_AllocationCount == 0)
12700     {
12701         return VK_SUCCESS;
12702     }
12703 
12704     const size_t blockCount = m_Blocks.size();
12705     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12706     {
12707         BlockInfo* pBlockInfo = m_Blocks[blockIndex];
12708 
12709         if(m_AllAllocations)
12710         {
12711             VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
12712             for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
12713                 it != pMetadata->m_Suballocations.end();
12714                 ++it)
12715             {
12716                 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
12717                 {
12718                     AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
12719                     pBlockInfo->m_Allocations.push_back(allocInfo);
12720                 }
12721             }
12722         }
12723 
12724         pBlockInfo->CalcHasNonMovableAllocations();
12725 
12726         // This is a choice based on research.
12727         // Option 1:
12728         pBlockInfo->SortAllocationsByOffsetDescending();
12729         // Option 2:
12730         //pBlockInfo->SortAllocationsBySizeDescending();
12731     }
12732 
12733     // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
12734     VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
12735 
12736     // This is a choice based on research.
12737     const uint32_t roundCount = 2;
12738 
12739     // Execute defragmentation rounds (the main part).
12740     VkResult result = VK_SUCCESS;
12741     for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
12742     {
12743         result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove);
12744     }
12745 
12746     return result;
12747 }
12748 
MoveMakesSense(size_t dstBlockIndex,VkDeviceSize dstOffset,size_t srcBlockIndex,VkDeviceSize srcOffset)12749 bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
12750         size_t dstBlockIndex, VkDeviceSize dstOffset,
12751         size_t srcBlockIndex, VkDeviceSize srcOffset)
12752 {
12753     if(dstBlockIndex < srcBlockIndex)
12754     {
12755         return true;
12756     }
12757     if(dstBlockIndex > srcBlockIndex)
12758     {
12759         return false;
12760     }
12761     if(dstOffset < srcOffset)
12762     {
12763         return true;
12764     }
12765     return false;
12766 }
12767 
12768 ////////////////////////////////////////////////////////////////////////////////
12769 // VmaDefragmentationAlgorithm_Fast
12770 
VmaDefragmentationAlgorithm_Fast(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)12771 VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
12772     VmaAllocator hAllocator,
12773     VmaBlockVector* pBlockVector,
12774     uint32_t currentFrameIndex,
12775     bool overlappingMoveSupported) :
12776     VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
12777     m_OverlappingMoveSupported(overlappingMoveSupported),
12778     m_AllocationCount(0),
12779     m_AllAllocations(false),
12780     m_BytesMoved(0),
12781     m_AllocationsMoved(0),
12782     m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
12783 {
12784     VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
12785 
12786 }
12787 
~VmaDefragmentationAlgorithm_Fast()12788 VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
12789 {
12790 }
12791 
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove)12792 VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
12793     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12794     VkDeviceSize maxBytesToMove,
12795     uint32_t maxAllocationsToMove)
12796 {
12797     VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
12798 
12799     const size_t blockCount = m_pBlockVector->GetBlockCount();
12800     if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
12801     {
12802         return VK_SUCCESS;
12803     }
12804 
12805     PreprocessMetadata();
12806 
12807     // Sort blocks in order from most destination.
12808 
12809     m_BlockInfos.resize(blockCount);
12810     for(size_t i = 0; i < blockCount; ++i)
12811     {
12812         m_BlockInfos[i].origBlockIndex = i;
12813     }
12814 
12815     VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
12816         return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
12817             m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
12818     });
12819 
12820     // THE MAIN ALGORITHM
12821 
12822     FreeSpaceDatabase freeSpaceDb;
12823 
12824     size_t dstBlockInfoIndex = 0;
12825     size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
12826     VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
12827     VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
12828     VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
12829     VkDeviceSize dstOffset = 0;
12830 
12831     bool end = false;
12832     for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
12833     {
12834         const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
12835         VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
12836         VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
12837         for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
12838             !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
12839         {
12840             VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
12841             const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
12842             const VkDeviceSize srcAllocSize = srcSuballocIt->size;
12843             if(m_AllocationsMoved == maxAllocationsToMove ||
12844                 m_BytesMoved + srcAllocSize > maxBytesToMove)
12845             {
12846                 end = true;
12847                 break;
12848             }
12849             const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
12850 
12851             // Try to place it in one of free spaces from the database.
12852             size_t freeSpaceInfoIndex;
12853             VkDeviceSize dstAllocOffset;
12854             if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
12855                 freeSpaceInfoIndex, dstAllocOffset))
12856             {
12857                 size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
12858                 VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
12859                 VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
12860                 VkDeviceSize freeSpaceBlockSize = pFreeSpaceMetadata->GetSize();
12861 
12862                 // Same block
12863                 if(freeSpaceInfoIndex == srcBlockInfoIndex)
12864                 {
12865                     VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
12866 
12867                     // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
12868 
12869                     VmaSuballocation suballoc = *srcSuballocIt;
12870                     suballoc.offset = dstAllocOffset;
12871                     suballoc.hAllocation->ChangeOffset(dstAllocOffset);
12872                     m_BytesMoved += srcAllocSize;
12873                     ++m_AllocationsMoved;
12874 
12875                     VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12876                     ++nextSuballocIt;
12877                     pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
12878                     srcSuballocIt = nextSuballocIt;
12879 
12880                     InsertSuballoc(pFreeSpaceMetadata, suballoc);
12881 
12882                     VmaDefragmentationMove move = {
12883                         srcOrigBlockIndex, freeSpaceOrigBlockIndex,
12884                         srcAllocOffset, dstAllocOffset,
12885                         srcAllocSize };
12886                     moves.push_back(move);
12887                 }
12888                 // Different block
12889                 else
12890                 {
12891                     // MOVE OPTION 2: Move the allocation to a different block.
12892 
12893                     VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
12894 
12895                     VmaSuballocation suballoc = *srcSuballocIt;
12896                     suballoc.offset = dstAllocOffset;
12897                     suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
12898                     m_BytesMoved += srcAllocSize;
12899                     ++m_AllocationsMoved;
12900 
12901                     VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12902                     ++nextSuballocIt;
12903                     pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
12904                     srcSuballocIt = nextSuballocIt;
12905 
12906                     InsertSuballoc(pFreeSpaceMetadata, suballoc);
12907 
12908                     VmaDefragmentationMove move = {
12909                         srcOrigBlockIndex, freeSpaceOrigBlockIndex,
12910                         srcAllocOffset, dstAllocOffset,
12911                         srcAllocSize };
12912                     moves.push_back(move);
12913                 }
12914             }
12915             else
12916             {
12917                 dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
12918 
12919                 // If the allocation doesn't fit before the end of dstBlock, forward to next block.
12920                 while(dstBlockInfoIndex < srcBlockInfoIndex &&
12921                     dstAllocOffset + srcAllocSize > dstBlockSize)
12922                 {
12923                     // But before that, register remaining free space at the end of dst block.
12924                     freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
12925 
12926                     ++dstBlockInfoIndex;
12927                     dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
12928                     pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
12929                     pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
12930                     dstBlockSize = pDstMetadata->GetSize();
12931                     dstOffset = 0;
12932                     dstAllocOffset = 0;
12933                 }
12934 
12935                 // Same block
12936                 if(dstBlockInfoIndex == srcBlockInfoIndex)
12937                 {
12938                     VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
12939 
12940                     const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
12941 
12942                     bool skipOver = overlap;
12943                     if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
12944                     {
12945                         // If destination and source place overlap, skip if it would move it
12946                         // by only < 1/64 of its size.
12947                         skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
12948                     }
12949 
12950                     if(skipOver)
12951                     {
12952                         freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
12953 
12954                         dstOffset = srcAllocOffset + srcAllocSize;
12955                         ++srcSuballocIt;
12956                     }
12957                     // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
12958                     else
12959                     {
12960                         srcSuballocIt->offset = dstAllocOffset;
12961                         srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);
12962                         dstOffset = dstAllocOffset + srcAllocSize;
12963                         m_BytesMoved += srcAllocSize;
12964                         ++m_AllocationsMoved;
12965                         ++srcSuballocIt;
12966                         VmaDefragmentationMove move = {
12967                             srcOrigBlockIndex, dstOrigBlockIndex,
12968                             srcAllocOffset, dstAllocOffset,
12969                             srcAllocSize };
12970                         moves.push_back(move);
12971                     }
12972                 }
12973                 // Different block
12974                 else
12975                 {
12976                     // MOVE OPTION 2: Move the allocation to a different block.
12977 
12978                     VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
12979                     VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
12980 
12981                     VmaSuballocation suballoc = *srcSuballocIt;
12982                     suballoc.offset = dstAllocOffset;
12983                     suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
12984                     dstOffset = dstAllocOffset + srcAllocSize;
12985                     m_BytesMoved += srcAllocSize;
12986                     ++m_AllocationsMoved;
12987 
12988                     VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12989                     ++nextSuballocIt;
12990                     pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
12991                     srcSuballocIt = nextSuballocIt;
12992 
12993                     pDstMetadata->m_Suballocations.push_back(suballoc);
12994 
12995                     VmaDefragmentationMove move = {
12996                         srcOrigBlockIndex, dstOrigBlockIndex,
12997                         srcAllocOffset, dstAllocOffset,
12998                         srcAllocSize };
12999                     moves.push_back(move);
13000                 }
13001             }
13002         }
13003     }
13004 
13005     m_BlockInfos.clear();
13006 
13007     PostprocessMetadata();
13008 
13009     return VK_SUCCESS;
13010 }
13011 
PreprocessMetadata()13012 void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
13013 {
13014     const size_t blockCount = m_pBlockVector->GetBlockCount();
13015     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13016     {
13017         VmaBlockMetadata_Generic* const pMetadata =
13018             (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
13019         pMetadata->m_FreeCount = 0;
13020         pMetadata->m_SumFreeSize = pMetadata->GetSize();
13021         pMetadata->m_FreeSuballocationsBySize.clear();
13022         for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
13023             it != pMetadata->m_Suballocations.end(); )
13024         {
13025             if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
13026             {
13027                 VmaSuballocationList::iterator nextIt = it;
13028                 ++nextIt;
13029                 pMetadata->m_Suballocations.erase(it);
13030                 it = nextIt;
13031             }
13032             else
13033             {
13034                 ++it;
13035             }
13036         }
13037     }
13038 }
13039 
PostprocessMetadata()13040 void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
13041 {
13042     const size_t blockCount = m_pBlockVector->GetBlockCount();
13043     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13044     {
13045         VmaBlockMetadata_Generic* const pMetadata =
13046             (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
13047         const VkDeviceSize blockSize = pMetadata->GetSize();
13048 
13049         // No allocations in this block - entire area is free.
13050         if(pMetadata->m_Suballocations.empty())
13051         {
13052             pMetadata->m_FreeCount = 1;
13053             //pMetadata->m_SumFreeSize is already set to blockSize.
13054             VmaSuballocation suballoc = {
13055                 0, // offset
13056                 blockSize, // size
13057                 VMA_NULL, // hAllocation
13058                 VMA_SUBALLOCATION_TYPE_FREE };
13059             pMetadata->m_Suballocations.push_back(suballoc);
13060             pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
13061         }
13062         // There are some allocations in this block.
13063         else
13064         {
13065             VkDeviceSize offset = 0;
13066             VmaSuballocationList::iterator it;
13067             for(it = pMetadata->m_Suballocations.begin();
13068                 it != pMetadata->m_Suballocations.end();
13069                 ++it)
13070             {
13071                 VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
13072                 VMA_ASSERT(it->offset >= offset);
13073 
13074                 // Need to insert preceding free space.
13075                 if(it->offset > offset)
13076                 {
13077                     ++pMetadata->m_FreeCount;
13078                     const VkDeviceSize freeSize = it->offset - offset;
13079                     VmaSuballocation suballoc = {
13080                         offset, // offset
13081                         freeSize, // size
13082                         VMA_NULL, // hAllocation
13083                         VMA_SUBALLOCATION_TYPE_FREE };
13084                     VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
13085                     if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
13086                     {
13087                         pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
13088                     }
13089                 }
13090 
13091                 pMetadata->m_SumFreeSize -= it->size;
13092                 offset = it->offset + it->size;
13093             }
13094 
13095             // Need to insert trailing free space.
13096             if(offset < blockSize)
13097             {
13098                 ++pMetadata->m_FreeCount;
13099                 const VkDeviceSize freeSize = blockSize - offset;
13100                 VmaSuballocation suballoc = {
13101                     offset, // offset
13102                     freeSize, // size
13103                     VMA_NULL, // hAllocation
13104                     VMA_SUBALLOCATION_TYPE_FREE };
13105                 VMA_ASSERT(it == pMetadata->m_Suballocations.end());
13106                 VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
13107                 if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
13108                 {
13109                     pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
13110                 }
13111             }
13112 
13113             VMA_SORT(
13114                 pMetadata->m_FreeSuballocationsBySize.begin(),
13115                 pMetadata->m_FreeSuballocationsBySize.end(),
13116                 VmaSuballocationItemSizeLess());
13117         }
13118 
13119         VMA_HEAVY_ASSERT(pMetadata->Validate());
13120     }
13121 }
13122 
InsertSuballoc(VmaBlockMetadata_Generic * pMetadata,const VmaSuballocation & suballoc)13123 void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
13124 {
13125     // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
13126     VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
13127     while(it != pMetadata->m_Suballocations.end())
13128     {
13129         if(it->offset < suballoc.offset)
13130         {
13131             ++it;
13132         }
13133     }
13134     pMetadata->m_Suballocations.insert(it, suballoc);
13135 }
13136 
13137 ////////////////////////////////////////////////////////////////////////////////
13138 // VmaBlockVectorDefragmentationContext
13139 
VmaBlockVectorDefragmentationContext(VmaAllocator hAllocator,VmaPool hCustomPool,VmaBlockVector * pBlockVector,uint32_t currFrameIndex,uint32_t algorithmFlags)13140 VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
13141     VmaAllocator hAllocator,
13142     VmaPool hCustomPool,
13143     VmaBlockVector* pBlockVector,
13144     uint32_t currFrameIndex,
13145     uint32_t algorithmFlags) :
13146     res(VK_SUCCESS),
13147     mutexLocked(false),
13148     blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
13149     m_hAllocator(hAllocator),
13150     m_hCustomPool(hCustomPool),
13151     m_pBlockVector(pBlockVector),
13152     m_CurrFrameIndex(currFrameIndex),
13153     //m_AlgorithmFlags(algorithmFlags),
13154     m_pAlgorithm(VMA_NULL),
13155     m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
13156     m_AllAllocations(false)
13157 {
13158 }
13159 
~VmaBlockVectorDefragmentationContext()13160 VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
13161 {
13162     vma_delete(m_hAllocator, m_pAlgorithm);
13163 }
13164 
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)13165 void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
13166 {
13167     AllocInfo info = { hAlloc, pChanged };
13168     m_Allocations.push_back(info);
13169 }
13170 
Begin(bool overlappingMoveSupported)13171 void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported)
13172 {
13173     const bool allAllocations = m_AllAllocations ||
13174         m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
13175 
13176     /********************************
13177     HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
13178     ********************************/
13179 
13180     /*
13181     Fast algorithm is supported only when certain criteria are met:
13182     - VMA_DEBUG_MARGIN is 0.
13183     - All allocations in this block vector are moveable.
13184     - There is no possibility of image/buffer granularity conflict.
13185     */
13186     if(VMA_DEBUG_MARGIN == 0 &&
13187         allAllocations &&
13188         !m_pBlockVector->IsBufferImageGranularityConflictPossible())
13189     {
13190         m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
13191             m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
13192     }
13193     else
13194     {
13195         m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
13196             m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
13197     }
13198 
13199     if(allAllocations)
13200     {
13201         m_pAlgorithm->AddAll();
13202     }
13203     else
13204     {
13205         for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
13206         {
13207             m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
13208         }
13209     }
13210 }
13211 
13212 ////////////////////////////////////////////////////////////////////////////////
13213 // VmaDefragmentationContext
13214 
VmaDefragmentationContext_T(VmaAllocator hAllocator,uint32_t currFrameIndex,uint32_t flags,VmaDefragmentationStats * pStats)13215 VmaDefragmentationContext_T::VmaDefragmentationContext_T(
13216     VmaAllocator hAllocator,
13217     uint32_t currFrameIndex,
13218     uint32_t flags,
13219     VmaDefragmentationStats* pStats) :
13220     m_hAllocator(hAllocator),
13221     m_CurrFrameIndex(currFrameIndex),
13222     m_Flags(flags),
13223     m_pStats(pStats),
13224     m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
13225 {
13226     memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
13227 }
13228 
~VmaDefragmentationContext_T()13229 VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
13230 {
13231     for(size_t i = m_CustomPoolContexts.size(); i--; )
13232     {
13233         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
13234         pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_pStats);
13235         vma_delete(m_hAllocator, pBlockVectorCtx);
13236     }
13237     for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
13238     {
13239         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
13240         if(pBlockVectorCtx)
13241         {
13242             pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_pStats);
13243             vma_delete(m_hAllocator, pBlockVectorCtx);
13244         }
13245     }
13246 }
13247 
AddPools(uint32_t poolCount,VmaPool * pPools)13248 void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, VmaPool* pPools)
13249 {
13250     for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
13251     {
13252         VmaPool pool = pPools[poolIndex];
13253         VMA_ASSERT(pool);
13254         // Pools with algorithm other than default are not defragmented.
13255         if(pool->m_BlockVector.GetAlgorithm() == 0)
13256         {
13257             VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
13258 
13259             for(size_t i = m_CustomPoolContexts.size(); i--; )
13260             {
13261                 if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
13262                 {
13263                     pBlockVectorDefragCtx = m_CustomPoolContexts[i];
13264                     break;
13265                 }
13266             }
13267 
13268             if(!pBlockVectorDefragCtx)
13269             {
13270                 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13271                     m_hAllocator,
13272                     pool,
13273                     &pool->m_BlockVector,
13274                     m_CurrFrameIndex,
13275                     m_Flags);
13276                 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
13277             }
13278 
13279             pBlockVectorDefragCtx->AddAll();
13280         }
13281     }
13282 }
13283 
AddAllocations(uint32_t allocationCount,VmaAllocation * pAllocations,VkBool32 * pAllocationsChanged)13284 void VmaDefragmentationContext_T::AddAllocations(
13285     uint32_t allocationCount,
13286     VmaAllocation* pAllocations,
13287     VkBool32* pAllocationsChanged)
13288 {
13289     // Dispatch pAllocations among defragmentators. Create them when necessary.
13290     for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
13291     {
13292         const VmaAllocation hAlloc = pAllocations[allocIndex];
13293         VMA_ASSERT(hAlloc);
13294         // DedicatedAlloc cannot be defragmented.
13295         if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
13296             // Lost allocation cannot be defragmented.
13297             (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
13298         {
13299             VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
13300 
13301             const VmaPool hAllocPool = hAlloc->GetPool();
13302             // This allocation belongs to custom pool.
13303             if(hAllocPool != VK_NULL_HANDLE)
13304             {
13305                 // Pools with algorithm other than default are not defragmented.
13306                 if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
13307                 {
13308                     for(size_t i = m_CustomPoolContexts.size(); i--; )
13309                     {
13310                         if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
13311                         {
13312                             pBlockVectorDefragCtx = m_CustomPoolContexts[i];
13313                             break;
13314                         }
13315                     }
13316                     if(!pBlockVectorDefragCtx)
13317                     {
13318                         pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13319                             m_hAllocator,
13320                             hAllocPool,
13321                             &hAllocPool->m_BlockVector,
13322                             m_CurrFrameIndex,
13323                             m_Flags);
13324                         m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
13325                     }
13326                 }
13327             }
13328             // This allocation belongs to default pool.
13329             else
13330             {
13331                 const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
13332                 pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
13333                 if(!pBlockVectorDefragCtx)
13334                 {
13335                     pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13336                         m_hAllocator,
13337                         VMA_NULL, // hCustomPool
13338                         m_hAllocator->m_pBlockVectors[memTypeIndex],
13339                         m_CurrFrameIndex,
13340                         m_Flags);
13341                     m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
13342                 }
13343             }
13344 
13345             if(pBlockVectorDefragCtx)
13346             {
13347                 VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
13348                     &pAllocationsChanged[allocIndex] : VMA_NULL;
13349                 pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
13350             }
13351         }
13352     }
13353 }
13354 
Defragment(VkDeviceSize maxCpuBytesToMove,uint32_t maxCpuAllocationsToMove,VkDeviceSize maxGpuBytesToMove,uint32_t maxGpuAllocationsToMove,VkCommandBuffer commandBuffer,VmaDefragmentationStats * pStats)13355 VkResult VmaDefragmentationContext_T::Defragment(
13356     VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
13357     VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
13358     VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats)
13359 {
13360     if(pStats)
13361     {
13362         memset(pStats, 0, sizeof(VmaDefragmentationStats));
13363     }
13364 
13365     if(commandBuffer == VK_NULL_HANDLE)
13366     {
13367         maxGpuBytesToMove = 0;
13368         maxGpuAllocationsToMove = 0;
13369     }
13370 
13371     VkResult res = VK_SUCCESS;
13372 
13373     // Process default pools.
13374     for(uint32_t memTypeIndex = 0;
13375         memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
13376         ++memTypeIndex)
13377     {
13378         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
13379         if(pBlockVectorCtx)
13380         {
13381             VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
13382             pBlockVectorCtx->GetBlockVector()->Defragment(
13383                 pBlockVectorCtx,
13384                 pStats,
13385                 maxCpuBytesToMove, maxCpuAllocationsToMove,
13386                 maxGpuBytesToMove, maxGpuAllocationsToMove,
13387                 commandBuffer);
13388             if(pBlockVectorCtx->res != VK_SUCCESS)
13389             {
13390                 res = pBlockVectorCtx->res;
13391             }
13392         }
13393     }
13394 
13395     // Process custom pools.
13396     for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
13397         customCtxIndex < customCtxCount && res >= VK_SUCCESS;
13398         ++customCtxIndex)
13399     {
13400         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
13401         VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
13402         pBlockVectorCtx->GetBlockVector()->Defragment(
13403             pBlockVectorCtx,
13404             pStats,
13405             maxCpuBytesToMove, maxCpuAllocationsToMove,
13406             maxGpuBytesToMove, maxGpuAllocationsToMove,
13407             commandBuffer);
13408         if(pBlockVectorCtx->res != VK_SUCCESS)
13409         {
13410             res = pBlockVectorCtx->res;
13411         }
13412     }
13413 
13414     return res;
13415 }
13416 
13417 ////////////////////////////////////////////////////////////////////////////////
13418 // VmaRecorder
13419 
13420 #if VMA_RECORDING_ENABLED
13421 
VmaRecorder()13422 VmaRecorder::VmaRecorder() :
13423     m_UseMutex(true),
13424     m_Flags(0),
13425     m_File(VMA_NULL),
13426     m_Freq(INT64_MAX),
13427     m_StartCounter(INT64_MAX)
13428 {
13429 }
13430 
Init(const VmaRecordSettings & settings,bool useMutex)13431 VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
13432 {
13433     m_UseMutex = useMutex;
13434     m_Flags = settings.flags;
13435 
13436     QueryPerformanceFrequency((LARGE_INTEGER*)&m_Freq);
13437     QueryPerformanceCounter((LARGE_INTEGER*)&m_StartCounter);
13438 
13439     // Open file for writing.
13440     errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
13441     if(err != 0)
13442     {
13443         return VK_ERROR_INITIALIZATION_FAILED;
13444     }
13445 
13446     // Write header.
13447     fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
13448     fprintf(m_File, "%s\n", "1,5");
13449 
13450     return VK_SUCCESS;
13451 }
13452 
~VmaRecorder()13453 VmaRecorder::~VmaRecorder()
13454 {
13455     if(m_File != VMA_NULL)
13456     {
13457         fclose(m_File);
13458     }
13459 }
13460 
RecordCreateAllocator(uint32_t frameIndex)13461 void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
13462 {
13463     CallParams callParams;
13464     GetBasicParams(callParams);
13465 
13466     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13467     fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
13468     Flush();
13469 }
13470 
RecordDestroyAllocator(uint32_t frameIndex)13471 void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
13472 {
13473     CallParams callParams;
13474     GetBasicParams(callParams);
13475 
13476     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13477     fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
13478     Flush();
13479 }
13480 
RecordCreatePool(uint32_t frameIndex,const VmaPoolCreateInfo & createInfo,VmaPool pool)13481 void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
13482 {
13483     CallParams callParams;
13484     GetBasicParams(callParams);
13485 
13486     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13487     fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
13488         createInfo.memoryTypeIndex,
13489         createInfo.flags,
13490         createInfo.blockSize,
13491         (uint64_t)createInfo.minBlockCount,
13492         (uint64_t)createInfo.maxBlockCount,
13493         createInfo.frameInUseCount,
13494         pool);
13495     Flush();
13496 }
13497 
RecordDestroyPool(uint32_t frameIndex,VmaPool pool)13498 void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
13499 {
13500     CallParams callParams;
13501     GetBasicParams(callParams);
13502 
13503     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13504     fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
13505         pool);
13506     Flush();
13507 }
13508 
RecordAllocateMemory(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)13509 void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
13510         const VkMemoryRequirements& vkMemReq,
13511         const VmaAllocationCreateInfo& createInfo,
13512         VmaAllocation allocation)
13513 {
13514     CallParams callParams;
13515     GetBasicParams(callParams);
13516 
13517     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13518     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13519     fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
13520         vkMemReq.size,
13521         vkMemReq.alignment,
13522         vkMemReq.memoryTypeBits,
13523         createInfo.flags,
13524         createInfo.usage,
13525         createInfo.requiredFlags,
13526         createInfo.preferredFlags,
13527         createInfo.memoryTypeBits,
13528         createInfo.pool,
13529         allocation,
13530         userDataStr.GetString());
13531     Flush();
13532 }
13533 
RecordAllocateMemoryPages(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,uint64_t allocationCount,const VmaAllocation * pAllocations)13534 void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
13535     const VkMemoryRequirements& vkMemReq,
13536     const VmaAllocationCreateInfo& createInfo,
13537     uint64_t allocationCount,
13538     const VmaAllocation* pAllocations)
13539 {
13540     CallParams callParams;
13541     GetBasicParams(callParams);
13542 
13543     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13544     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13545     fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
13546         vkMemReq.size,
13547         vkMemReq.alignment,
13548         vkMemReq.memoryTypeBits,
13549         createInfo.flags,
13550         createInfo.usage,
13551         createInfo.requiredFlags,
13552         createInfo.preferredFlags,
13553         createInfo.memoryTypeBits,
13554         createInfo.pool);
13555     PrintPointerList(allocationCount, pAllocations);
13556     fprintf(m_File, ",%s\n", userDataStr.GetString());
13557     Flush();
13558 }
13559 
RecordAllocateMemoryForBuffer(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)13560 void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
13561     const VkMemoryRequirements& vkMemReq,
13562     bool requiresDedicatedAllocation,
13563     bool prefersDedicatedAllocation,
13564     const VmaAllocationCreateInfo& createInfo,
13565     VmaAllocation allocation)
13566 {
13567     CallParams callParams;
13568     GetBasicParams(callParams);
13569 
13570     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13571     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13572     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,
13573         vkMemReq.size,
13574         vkMemReq.alignment,
13575         vkMemReq.memoryTypeBits,
13576         requiresDedicatedAllocation ? 1 : 0,
13577         prefersDedicatedAllocation ? 1 : 0,
13578         createInfo.flags,
13579         createInfo.usage,
13580         createInfo.requiredFlags,
13581         createInfo.preferredFlags,
13582         createInfo.memoryTypeBits,
13583         createInfo.pool,
13584         allocation,
13585         userDataStr.GetString());
13586     Flush();
13587 }
13588 
RecordAllocateMemoryForImage(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)13589 void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
13590     const VkMemoryRequirements& vkMemReq,
13591     bool requiresDedicatedAllocation,
13592     bool prefersDedicatedAllocation,
13593     const VmaAllocationCreateInfo& createInfo,
13594     VmaAllocation allocation)
13595 {
13596     CallParams callParams;
13597     GetBasicParams(callParams);
13598 
13599     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13600     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13601     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,
13602         vkMemReq.size,
13603         vkMemReq.alignment,
13604         vkMemReq.memoryTypeBits,
13605         requiresDedicatedAllocation ? 1 : 0,
13606         prefersDedicatedAllocation ? 1 : 0,
13607         createInfo.flags,
13608         createInfo.usage,
13609         createInfo.requiredFlags,
13610         createInfo.preferredFlags,
13611         createInfo.memoryTypeBits,
13612         createInfo.pool,
13613         allocation,
13614         userDataStr.GetString());
13615     Flush();
13616 }
13617 
RecordFreeMemory(uint32_t frameIndex,VmaAllocation allocation)13618 void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
13619     VmaAllocation allocation)
13620 {
13621     CallParams callParams;
13622     GetBasicParams(callParams);
13623 
13624     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13625     fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13626         allocation);
13627     Flush();
13628 }
13629 
RecordFreeMemoryPages(uint32_t frameIndex,uint64_t allocationCount,const VmaAllocation * pAllocations)13630 void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
13631     uint64_t allocationCount,
13632     const VmaAllocation* pAllocations)
13633 {
13634     CallParams callParams;
13635     GetBasicParams(callParams);
13636 
13637     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13638     fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
13639     PrintPointerList(allocationCount, pAllocations);
13640     fprintf(m_File, "\n");
13641     Flush();
13642 }
13643 
RecordResizeAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize newSize)13644 void VmaRecorder::RecordResizeAllocation(
13645     uint32_t frameIndex,
13646     VmaAllocation allocation,
13647     VkDeviceSize newSize)
13648 {
13649     CallParams callParams;
13650     GetBasicParams(callParams);
13651 
13652     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13653     fprintf(m_File, "%u,%.3f,%u,vmaResizeAllocation,%p,%llu\n", callParams.threadId, callParams.time, frameIndex,
13654         allocation, newSize);
13655     Flush();
13656 }
13657 
RecordSetAllocationUserData(uint32_t frameIndex,VmaAllocation allocation,const void * pUserData)13658 void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
13659     VmaAllocation allocation,
13660     const void* pUserData)
13661 {
13662     CallParams callParams;
13663     GetBasicParams(callParams);
13664 
13665     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13666     UserDataString userDataStr(
13667         allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
13668         pUserData);
13669     fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
13670         allocation,
13671         userDataStr.GetString());
13672     Flush();
13673 }
13674 
RecordCreateLostAllocation(uint32_t frameIndex,VmaAllocation allocation)13675 void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
13676     VmaAllocation allocation)
13677 {
13678     CallParams callParams;
13679     GetBasicParams(callParams);
13680 
13681     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13682     fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
13683         allocation);
13684     Flush();
13685 }
13686 
RecordMapMemory(uint32_t frameIndex,VmaAllocation allocation)13687 void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
13688     VmaAllocation allocation)
13689 {
13690     CallParams callParams;
13691     GetBasicParams(callParams);
13692 
13693     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13694     fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13695         allocation);
13696     Flush();
13697 }
13698 
RecordUnmapMemory(uint32_t frameIndex,VmaAllocation allocation)13699 void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
13700     VmaAllocation allocation)
13701 {
13702     CallParams callParams;
13703     GetBasicParams(callParams);
13704 
13705     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13706     fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13707         allocation);
13708     Flush();
13709 }
13710 
RecordFlushAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)13711 void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
13712     VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
13713 {
13714     CallParams callParams;
13715     GetBasicParams(callParams);
13716 
13717     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13718     fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
13719         allocation,
13720         offset,
13721         size);
13722     Flush();
13723 }
13724 
RecordInvalidateAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)13725 void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
13726     VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
13727 {
13728     CallParams callParams;
13729     GetBasicParams(callParams);
13730 
13731     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13732     fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
13733         allocation,
13734         offset,
13735         size);
13736     Flush();
13737 }
13738 
RecordCreateBuffer(uint32_t frameIndex,const VkBufferCreateInfo & bufCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)13739 void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
13740     const VkBufferCreateInfo& bufCreateInfo,
13741     const VmaAllocationCreateInfo& allocCreateInfo,
13742     VmaAllocation allocation)
13743 {
13744     CallParams callParams;
13745     GetBasicParams(callParams);
13746 
13747     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13748     UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
13749     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,
13750         bufCreateInfo.flags,
13751         bufCreateInfo.size,
13752         bufCreateInfo.usage,
13753         bufCreateInfo.sharingMode,
13754         allocCreateInfo.flags,
13755         allocCreateInfo.usage,
13756         allocCreateInfo.requiredFlags,
13757         allocCreateInfo.preferredFlags,
13758         allocCreateInfo.memoryTypeBits,
13759         allocCreateInfo.pool,
13760         allocation,
13761         userDataStr.GetString());
13762     Flush();
13763 }
13764 
RecordCreateImage(uint32_t frameIndex,const VkImageCreateInfo & imageCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)13765 void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
13766     const VkImageCreateInfo& imageCreateInfo,
13767     const VmaAllocationCreateInfo& allocCreateInfo,
13768     VmaAllocation allocation)
13769 {
13770     CallParams callParams;
13771     GetBasicParams(callParams);
13772 
13773     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13774     UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
13775     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,
13776         imageCreateInfo.flags,
13777         imageCreateInfo.imageType,
13778         imageCreateInfo.format,
13779         imageCreateInfo.extent.width,
13780         imageCreateInfo.extent.height,
13781         imageCreateInfo.extent.depth,
13782         imageCreateInfo.mipLevels,
13783         imageCreateInfo.arrayLayers,
13784         imageCreateInfo.samples,
13785         imageCreateInfo.tiling,
13786         imageCreateInfo.usage,
13787         imageCreateInfo.sharingMode,
13788         imageCreateInfo.initialLayout,
13789         allocCreateInfo.flags,
13790         allocCreateInfo.usage,
13791         allocCreateInfo.requiredFlags,
13792         allocCreateInfo.preferredFlags,
13793         allocCreateInfo.memoryTypeBits,
13794         allocCreateInfo.pool,
13795         allocation,
13796         userDataStr.GetString());
13797     Flush();
13798 }
13799 
RecordDestroyBuffer(uint32_t frameIndex,VmaAllocation allocation)13800 void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
13801     VmaAllocation allocation)
13802 {
13803     CallParams callParams;
13804     GetBasicParams(callParams);
13805 
13806     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13807     fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
13808         allocation);
13809     Flush();
13810 }
13811 
RecordDestroyImage(uint32_t frameIndex,VmaAllocation allocation)13812 void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
13813     VmaAllocation allocation)
13814 {
13815     CallParams callParams;
13816     GetBasicParams(callParams);
13817 
13818     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13819     fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
13820         allocation);
13821     Flush();
13822 }
13823 
RecordTouchAllocation(uint32_t frameIndex,VmaAllocation allocation)13824 void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
13825     VmaAllocation allocation)
13826 {
13827     CallParams callParams;
13828     GetBasicParams(callParams);
13829 
13830     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13831     fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
13832         allocation);
13833     Flush();
13834 }
13835 
RecordGetAllocationInfo(uint32_t frameIndex,VmaAllocation allocation)13836 void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
13837     VmaAllocation allocation)
13838 {
13839     CallParams callParams;
13840     GetBasicParams(callParams);
13841 
13842     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13843     fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
13844         allocation);
13845     Flush();
13846 }
13847 
RecordMakePoolAllocationsLost(uint32_t frameIndex,VmaPool pool)13848 void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
13849     VmaPool pool)
13850 {
13851     CallParams callParams;
13852     GetBasicParams(callParams);
13853 
13854     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13855     fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
13856         pool);
13857     Flush();
13858 }
13859 
RecordDefragmentationBegin(uint32_t frameIndex,const VmaDefragmentationInfo2 & info,VmaDefragmentationContext ctx)13860 void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
13861     const VmaDefragmentationInfo2& info,
13862     VmaDefragmentationContext ctx)
13863 {
13864     CallParams callParams;
13865     GetBasicParams(callParams);
13866 
13867     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13868     fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
13869         info.flags);
13870     PrintPointerList(info.allocationCount, info.pAllocations);
13871     fprintf(m_File, ",");
13872     PrintPointerList(info.poolCount, info.pPools);
13873     fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
13874         info.maxCpuBytesToMove,
13875         info.maxCpuAllocationsToMove,
13876         info.maxGpuBytesToMove,
13877         info.maxGpuAllocationsToMove,
13878         info.commandBuffer,
13879         ctx);
13880     Flush();
13881 }
13882 
RecordDefragmentationEnd(uint32_t frameIndex,VmaDefragmentationContext ctx)13883 void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
13884     VmaDefragmentationContext ctx)
13885 {
13886     CallParams callParams;
13887     GetBasicParams(callParams);
13888 
13889     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13890     fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
13891         ctx);
13892     Flush();
13893 }
13894 
UserDataString(VmaAllocationCreateFlags allocFlags,const void * pUserData)13895 VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
13896 {
13897     if(pUserData != VMA_NULL)
13898     {
13899         if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
13900         {
13901             m_Str = (const char*)pUserData;
13902         }
13903         else
13904         {
13905             sprintf_s(m_PtrStr, "%p", pUserData);
13906             m_Str = m_PtrStr;
13907         }
13908     }
13909     else
13910     {
13911         m_Str = "";
13912     }
13913 }
13914 
WriteConfiguration(const VkPhysicalDeviceProperties & devProps,const VkPhysicalDeviceMemoryProperties & memProps,bool dedicatedAllocationExtensionEnabled)13915 void VmaRecorder::WriteConfiguration(
13916     const VkPhysicalDeviceProperties& devProps,
13917     const VkPhysicalDeviceMemoryProperties& memProps,
13918     bool dedicatedAllocationExtensionEnabled)
13919 {
13920     fprintf(m_File, "Config,Begin\n");
13921 
13922     fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
13923     fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
13924     fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
13925     fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
13926     fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
13927     fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
13928 
13929     fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
13930     fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
13931     fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
13932 
13933     fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
13934     for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
13935     {
13936         fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
13937         fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
13938     }
13939     fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
13940     for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
13941     {
13942         fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
13943         fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
13944     }
13945 
13946     fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
13947 
13948     fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
13949     fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);
13950     fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
13951     fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
13952     fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
13953     fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
13954     fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
13955     fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
13956     fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
13957 
13958     fprintf(m_File, "Config,End\n");
13959 }
13960 
GetBasicParams(CallParams & outParams)13961 void VmaRecorder::GetBasicParams(CallParams& outParams)
13962 {
13963     outParams.threadId = GetCurrentThreadId();
13964 
13965     LARGE_INTEGER counter;
13966     QueryPerformanceCounter(&counter);
13967     outParams.time = (double)(counter.QuadPart - m_StartCounter) / (double)m_Freq;
13968 }
13969 
PrintPointerList(uint64_t count,const VmaAllocation * pItems)13970 void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
13971 {
13972     if(count)
13973     {
13974         fprintf(m_File, "%p", pItems[0]);
13975         for(uint64_t i = 1; i < count; ++i)
13976         {
13977             fprintf(m_File, " %p", pItems[i]);
13978         }
13979     }
13980 }
13981 
Flush()13982 void VmaRecorder::Flush()
13983 {
13984     if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
13985     {
13986         fflush(m_File);
13987     }
13988 }
13989 
13990 #endif // #if VMA_RECORDING_ENABLED
13991 
13992 ////////////////////////////////////////////////////////////////////////////////
13993 // VmaAllocator_T
13994 
VmaAllocator_T(const VmaAllocatorCreateInfo * pCreateInfo)13995 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
13996     m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
13997     m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
13998     m_hDevice(pCreateInfo->device),
13999     m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
14000     m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
14001         *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
14002     m_PreferredLargeHeapBlockSize(0),
14003     m_PhysicalDevice(pCreateInfo->physicalDevice),
14004     m_CurrentFrameIndex(0),
14005     m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks())),
14006     m_NextPoolId(0)
14007 #if VMA_RECORDING_ENABLED
14008     ,m_pRecorder(VMA_NULL)
14009 #endif
14010 {
14011     if(VMA_DEBUG_DETECT_CORRUPTION)
14012     {
14013         // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
14014         VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
14015     }
14016 
14017     VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device);
14018 
14019 #if !(VMA_DEDICATED_ALLOCATION)
14020     if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
14021     {
14022         VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
14023     }
14024 #endif
14025 
14026     memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
14027     memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
14028     memset(&m_MemProps, 0, sizeof(m_MemProps));
14029 
14030     memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
14031     memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations));
14032 
14033     for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
14034     {
14035         m_HeapSizeLimit[i] = VK_WHOLE_SIZE;
14036     }
14037 
14038     if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
14039     {
14040         m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
14041         m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
14042     }
14043 
14044     ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
14045 
14046     (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
14047     (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
14048 
14049     VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT));
14050     VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
14051     VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
14052     VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
14053 
14054     m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
14055         pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
14056 
14057     if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
14058     {
14059         for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
14060         {
14061             const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
14062             if(limit != VK_WHOLE_SIZE)
14063             {
14064                 m_HeapSizeLimit[heapIndex] = limit;
14065                 if(limit < m_MemProps.memoryHeaps[heapIndex].size)
14066                 {
14067                     m_MemProps.memoryHeaps[heapIndex].size = limit;
14068                 }
14069             }
14070         }
14071     }
14072 
14073     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14074     {
14075         const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
14076 
14077         m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
14078             this,
14079             memTypeIndex,
14080             preferredBlockSize,
14081             0,
14082             SIZE_MAX,
14083             GetBufferImageGranularity(),
14084             pCreateInfo->frameInUseCount,
14085             false, // isCustomPool
14086             false, // explicitBlockSize
14087             false); // linearAlgorithm
14088         // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
14089         // becase minBlockCount is 0.
14090         m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
14091 
14092     }
14093 }
14094 
Init(const VmaAllocatorCreateInfo * pCreateInfo)14095 VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
14096 {
14097     VkResult res = VK_SUCCESS;
14098 
14099     if(pCreateInfo->pRecordSettings != VMA_NULL &&
14100         !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
14101     {
14102 #if VMA_RECORDING_ENABLED
14103         m_pRecorder = vma_new(this, VmaRecorder)();
14104         res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
14105         if(res != VK_SUCCESS)
14106         {
14107             return res;
14108         }
14109         m_pRecorder->WriteConfiguration(
14110             m_PhysicalDeviceProperties,
14111             m_MemProps,
14112             m_UseKhrDedicatedAllocation);
14113         m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
14114 #else
14115         VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
14116         return VK_ERROR_FEATURE_NOT_PRESENT;
14117 #endif
14118     }
14119 
14120     return res;
14121 }
14122 
~VmaAllocator_T()14123 VmaAllocator_T::~VmaAllocator_T()
14124 {
14125 #if VMA_RECORDING_ENABLED
14126     if(m_pRecorder != VMA_NULL)
14127     {
14128         m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
14129         vma_delete(this, m_pRecorder);
14130     }
14131 #endif
14132 
14133     VMA_ASSERT(m_Pools.empty());
14134 
14135     for(size_t i = GetMemoryTypeCount(); i--; )
14136     {
14137         vma_delete(this, m_pDedicatedAllocations[i]);
14138         vma_delete(this, m_pBlockVectors[i]);
14139     }
14140 }
14141 
ImportVulkanFunctions(const VmaVulkanFunctions * pVulkanFunctions)14142 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
14143 {
14144 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
14145     m_VulkanFunctions.vkGetPhysicalDeviceProperties = &vkGetPhysicalDeviceProperties;
14146     m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = &vkGetPhysicalDeviceMemoryProperties;
14147     m_VulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
14148     m_VulkanFunctions.vkFreeMemory = &vkFreeMemory;
14149     m_VulkanFunctions.vkMapMemory = &vkMapMemory;
14150     m_VulkanFunctions.vkUnmapMemory = &vkUnmapMemory;
14151     m_VulkanFunctions.vkFlushMappedMemoryRanges = &vkFlushMappedMemoryRanges;
14152     m_VulkanFunctions.vkInvalidateMappedMemoryRanges = &vkInvalidateMappedMemoryRanges;
14153     m_VulkanFunctions.vkBindBufferMemory = &vkBindBufferMemory;
14154     m_VulkanFunctions.vkBindImageMemory = &vkBindImageMemory;
14155     m_VulkanFunctions.vkGetBufferMemoryRequirements = &vkGetBufferMemoryRequirements;
14156     m_VulkanFunctions.vkGetImageMemoryRequirements = &vkGetImageMemoryRequirements;
14157     m_VulkanFunctions.vkCreateBuffer = &vkCreateBuffer;
14158     m_VulkanFunctions.vkDestroyBuffer = &vkDestroyBuffer;
14159     m_VulkanFunctions.vkCreateImage = &vkCreateImage;
14160     m_VulkanFunctions.vkDestroyImage = &vkDestroyImage;
14161     m_VulkanFunctions.vkCmdCopyBuffer = &vkCmdCopyBuffer;
14162 #if VMA_DEDICATED_ALLOCATION
14163     if(m_UseKhrDedicatedAllocation)
14164     {
14165         m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR =
14166             (PFN_vkGetBufferMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetBufferMemoryRequirements2KHR");
14167         m_VulkanFunctions.vkGetImageMemoryRequirements2KHR =
14168             (PFN_vkGetImageMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetImageMemoryRequirements2KHR");
14169     }
14170 #endif // #if VMA_DEDICATED_ALLOCATION
14171 #endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
14172 
14173 #define VMA_COPY_IF_NOT_NULL(funcName) \
14174     if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
14175 
14176     if(pVulkanFunctions != VMA_NULL)
14177     {
14178         VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
14179         VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
14180         VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
14181         VMA_COPY_IF_NOT_NULL(vkFreeMemory);
14182         VMA_COPY_IF_NOT_NULL(vkMapMemory);
14183         VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
14184         VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
14185         VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
14186         VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
14187         VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
14188         VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
14189         VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
14190         VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
14191         VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
14192         VMA_COPY_IF_NOT_NULL(vkCreateImage);
14193         VMA_COPY_IF_NOT_NULL(vkDestroyImage);
14194         VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
14195 #if VMA_DEDICATED_ALLOCATION
14196         VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
14197         VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
14198 #endif
14199     }
14200 
14201 #undef VMA_COPY_IF_NOT_NULL
14202 
14203     // If these asserts are hit, you must either #define VMA_STATIC_VULKAN_FUNCTIONS 1
14204     // or pass valid pointers as VmaAllocatorCreateInfo::pVulkanFunctions.
14205     VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
14206     VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
14207     VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
14208     VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
14209     VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
14210     VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
14211     VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
14212     VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
14213     VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
14214     VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
14215     VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
14216     VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
14217     VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
14218     VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
14219     VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
14220     VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
14221     VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
14222 #if VMA_DEDICATED_ALLOCATION
14223     if(m_UseKhrDedicatedAllocation)
14224     {
14225         VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
14226         VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
14227     }
14228 #endif
14229 }
14230 
CalcPreferredBlockSize(uint32_t memTypeIndex)14231 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
14232 {
14233     const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
14234     const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
14235     const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
14236     return isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize;
14237 }
14238 
AllocateMemoryOfType(VkDeviceSize size,VkDeviceSize alignment,bool dedicatedAllocation,VkBuffer dedicatedBuffer,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,uint32_t memTypeIndex,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)14239 VkResult VmaAllocator_T::AllocateMemoryOfType(
14240     VkDeviceSize size,
14241     VkDeviceSize alignment,
14242     bool dedicatedAllocation,
14243     VkBuffer dedicatedBuffer,
14244     VkImage dedicatedImage,
14245     const VmaAllocationCreateInfo& createInfo,
14246     uint32_t memTypeIndex,
14247     VmaSuballocationType suballocType,
14248     size_t allocationCount,
14249     VmaAllocation* pAllocations)
14250 {
14251     VMA_ASSERT(pAllocations != VMA_NULL);
14252     VMA_DEBUG_LOG("  AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, vkMemReq.size);
14253 
14254     VmaAllocationCreateInfo finalCreateInfo = createInfo;
14255 
14256     // If memory type is not HOST_VISIBLE, disable MAPPED.
14257     if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
14258         (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
14259     {
14260         finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
14261     }
14262 
14263     VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
14264     VMA_ASSERT(blockVector);
14265 
14266     const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
14267     bool preferDedicatedMemory =
14268         VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
14269         dedicatedAllocation ||
14270         // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
14271         size > preferredBlockSize / 2;
14272 
14273     if(preferDedicatedMemory &&
14274         (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
14275         finalCreateInfo.pool == VK_NULL_HANDLE)
14276     {
14277         finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
14278     }
14279 
14280     if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
14281     {
14282         if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14283         {
14284             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14285         }
14286         else
14287         {
14288             return AllocateDedicatedMemory(
14289                 size,
14290                 suballocType,
14291                 memTypeIndex,
14292                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
14293                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
14294                 finalCreateInfo.pUserData,
14295                 dedicatedBuffer,
14296                 dedicatedImage,
14297                 allocationCount,
14298                 pAllocations);
14299         }
14300     }
14301     else
14302     {
14303         VkResult res = blockVector->Allocate(
14304             VK_NULL_HANDLE, // hCurrentPool
14305             m_CurrentFrameIndex.load(),
14306             size,
14307             alignment,
14308             finalCreateInfo,
14309             suballocType,
14310             allocationCount,
14311             pAllocations);
14312         if(res == VK_SUCCESS)
14313         {
14314             return res;
14315         }
14316 
14317         // 5. Try dedicated memory.
14318         if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14319         {
14320             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14321         }
14322         else
14323         {
14324             res = AllocateDedicatedMemory(
14325                 size,
14326                 suballocType,
14327                 memTypeIndex,
14328                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
14329                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
14330                 finalCreateInfo.pUserData,
14331                 dedicatedBuffer,
14332                 dedicatedImage,
14333                 allocationCount,
14334                 pAllocations);
14335             if(res == VK_SUCCESS)
14336             {
14337                 // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
14338                 VMA_DEBUG_LOG("    Allocated as DedicatedMemory");
14339                 return VK_SUCCESS;
14340             }
14341             else
14342             {
14343                 // Everything failed: Return error code.
14344                 VMA_DEBUG_LOG("    vkAllocateMemory FAILED");
14345                 return res;
14346             }
14347         }
14348     }
14349 }
14350 
AllocateDedicatedMemory(VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,bool map,bool isUserDataString,void * pUserData,VkBuffer dedicatedBuffer,VkImage dedicatedImage,size_t allocationCount,VmaAllocation * pAllocations)14351 VkResult VmaAllocator_T::AllocateDedicatedMemory(
14352     VkDeviceSize size,
14353     VmaSuballocationType suballocType,
14354     uint32_t memTypeIndex,
14355     bool map,
14356     bool isUserDataString,
14357     void* pUserData,
14358     VkBuffer dedicatedBuffer,
14359     VkImage dedicatedImage,
14360     size_t allocationCount,
14361     VmaAllocation* pAllocations)
14362 {
14363     VMA_ASSERT(allocationCount > 0 && pAllocations);
14364 
14365     VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
14366     allocInfo.memoryTypeIndex = memTypeIndex;
14367     allocInfo.allocationSize = size;
14368 
14369 #if VMA_DEDICATED_ALLOCATION
14370     VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
14371     if(m_UseKhrDedicatedAllocation)
14372     {
14373         if(dedicatedBuffer != VK_NULL_HANDLE)
14374         {
14375             VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
14376             dedicatedAllocInfo.buffer = dedicatedBuffer;
14377             allocInfo.pNext = &dedicatedAllocInfo;
14378         }
14379         else if(dedicatedImage != VK_NULL_HANDLE)
14380         {
14381             dedicatedAllocInfo.image = dedicatedImage;
14382             allocInfo.pNext = &dedicatedAllocInfo;
14383         }
14384     }
14385 #endif // #if VMA_DEDICATED_ALLOCATION
14386 
14387     size_t allocIndex;
14388     VkResult res = VK_SUCCESS;
14389     for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14390     {
14391         res = AllocateDedicatedMemoryPage(
14392             size,
14393             suballocType,
14394             memTypeIndex,
14395             allocInfo,
14396             map,
14397             isUserDataString,
14398             pUserData,
14399             pAllocations + allocIndex);
14400         if(res != VK_SUCCESS)
14401         {
14402             break;
14403         }
14404     }
14405 
14406     if(res == VK_SUCCESS)
14407     {
14408         // Register them in m_pDedicatedAllocations.
14409         {
14410             VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
14411             AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
14412             VMA_ASSERT(pDedicatedAllocations);
14413             for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14414             {
14415                 VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, pAllocations[allocIndex]);
14416             }
14417         }
14418 
14419         VMA_DEBUG_LOG("    Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
14420     }
14421     else
14422     {
14423         // Free all already created allocations.
14424         while(allocIndex--)
14425         {
14426             VmaAllocation currAlloc = pAllocations[allocIndex];
14427             VkDeviceMemory hMemory = currAlloc->GetMemory();
14428 
14429             /*
14430             There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
14431             before vkFreeMemory.
14432 
14433             if(currAlloc->GetMappedData() != VMA_NULL)
14434             {
14435                 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
14436             }
14437             */
14438 
14439             FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
14440 
14441             currAlloc->SetUserData(this, VMA_NULL);
14442             vma_delete(this, currAlloc);
14443         }
14444 
14445         memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
14446     }
14447 
14448     return res;
14449 }
14450 
AllocateDedicatedMemoryPage(VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,const VkMemoryAllocateInfo & allocInfo,bool map,bool isUserDataString,void * pUserData,VmaAllocation * pAllocation)14451 VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
14452     VkDeviceSize size,
14453     VmaSuballocationType suballocType,
14454     uint32_t memTypeIndex,
14455     const VkMemoryAllocateInfo& allocInfo,
14456     bool map,
14457     bool isUserDataString,
14458     void* pUserData,
14459     VmaAllocation* pAllocation)
14460 {
14461     VkDeviceMemory hMemory = VK_NULL_HANDLE;
14462     VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
14463     if(res < 0)
14464     {
14465         VMA_DEBUG_LOG("    vkAllocateMemory FAILED");
14466         return res;
14467     }
14468 
14469     void* pMappedData = VMA_NULL;
14470     if(map)
14471     {
14472         res = (*m_VulkanFunctions.vkMapMemory)(
14473             m_hDevice,
14474             hMemory,
14475             0,
14476             VK_WHOLE_SIZE,
14477             0,
14478             &pMappedData);
14479         if(res < 0)
14480         {
14481             VMA_DEBUG_LOG("    vkMapMemory FAILED");
14482             FreeVulkanMemory(memTypeIndex, size, hMemory);
14483             return res;
14484         }
14485     }
14486 
14487     *pAllocation = vma_new(this, VmaAllocation_T)(m_CurrentFrameIndex.load(), isUserDataString);
14488     (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
14489     (*pAllocation)->SetUserData(this, pUserData);
14490     if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
14491     {
14492         FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
14493     }
14494 
14495     return VK_SUCCESS;
14496 }
14497 
GetBufferMemoryRequirements(VkBuffer hBuffer,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)14498 void VmaAllocator_T::GetBufferMemoryRequirements(
14499     VkBuffer hBuffer,
14500     VkMemoryRequirements& memReq,
14501     bool& requiresDedicatedAllocation,
14502     bool& prefersDedicatedAllocation) const
14503 {
14504 #if VMA_DEDICATED_ALLOCATION
14505     if(m_UseKhrDedicatedAllocation)
14506     {
14507         VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
14508         memReqInfo.buffer = hBuffer;
14509 
14510         VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
14511 
14512         VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
14513         memReq2.pNext = &memDedicatedReq;
14514 
14515         (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
14516 
14517         memReq = memReq2.memoryRequirements;
14518         requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
14519         prefersDedicatedAllocation  = (memDedicatedReq.prefersDedicatedAllocation  != VK_FALSE);
14520     }
14521     else
14522 #endif // #if VMA_DEDICATED_ALLOCATION
14523     {
14524         (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
14525         requiresDedicatedAllocation = false;
14526         prefersDedicatedAllocation  = false;
14527     }
14528 }
14529 
GetImageMemoryRequirements(VkImage hImage,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)14530 void VmaAllocator_T::GetImageMemoryRequirements(
14531     VkImage hImage,
14532     VkMemoryRequirements& memReq,
14533     bool& requiresDedicatedAllocation,
14534     bool& prefersDedicatedAllocation) const
14535 {
14536 #if VMA_DEDICATED_ALLOCATION
14537     if(m_UseKhrDedicatedAllocation)
14538     {
14539         VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
14540         memReqInfo.image = hImage;
14541 
14542         VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
14543 
14544         VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
14545         memReq2.pNext = &memDedicatedReq;
14546 
14547         (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
14548 
14549         memReq = memReq2.memoryRequirements;
14550         requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
14551         prefersDedicatedAllocation  = (memDedicatedReq.prefersDedicatedAllocation  != VK_FALSE);
14552     }
14553     else
14554 #endif // #if VMA_DEDICATED_ALLOCATION
14555     {
14556         (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
14557         requiresDedicatedAllocation = false;
14558         prefersDedicatedAllocation  = false;
14559     }
14560 }
14561 
AllocateMemory(const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,VkBuffer dedicatedBuffer,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)14562 VkResult VmaAllocator_T::AllocateMemory(
14563     const VkMemoryRequirements& vkMemReq,
14564     bool requiresDedicatedAllocation,
14565     bool prefersDedicatedAllocation,
14566     VkBuffer dedicatedBuffer,
14567     VkImage dedicatedImage,
14568     const VmaAllocationCreateInfo& createInfo,
14569     VmaSuballocationType suballocType,
14570     size_t allocationCount,
14571     VmaAllocation* pAllocations)
14572 {
14573     memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
14574 
14575     VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
14576 
14577     if(vkMemReq.size == 0)
14578     {
14579         return VK_ERROR_VALIDATION_FAILED_EXT;
14580     }
14581     if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
14582         (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14583     {
14584         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
14585         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14586     }
14587     if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
14588         (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
14589     {
14590         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
14591         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14592     }
14593     if(requiresDedicatedAllocation)
14594     {
14595         if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14596         {
14597             VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
14598             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14599         }
14600         if(createInfo.pool != VK_NULL_HANDLE)
14601         {
14602             VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
14603             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14604         }
14605     }
14606     if((createInfo.pool != VK_NULL_HANDLE) &&
14607         ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
14608     {
14609         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
14610         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14611     }
14612 
14613     if(createInfo.pool != VK_NULL_HANDLE)
14614     {
14615         const VkDeviceSize alignmentForPool = VMA_MAX(
14616             vkMemReq.alignment,
14617             GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex()));
14618         return createInfo.pool->m_BlockVector.Allocate(
14619             createInfo.pool,
14620             m_CurrentFrameIndex.load(),
14621             vkMemReq.size,
14622             alignmentForPool,
14623             createInfo,
14624             suballocType,
14625             allocationCount,
14626             pAllocations);
14627     }
14628     else
14629     {
14630         // Bit mask of memory Vulkan types acceptable for this allocation.
14631         uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
14632         uint32_t memTypeIndex = UINT32_MAX;
14633         VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
14634         if(res == VK_SUCCESS)
14635         {
14636             VkDeviceSize alignmentForMemType = VMA_MAX(
14637                 vkMemReq.alignment,
14638                 GetMemoryTypeMinAlignment(memTypeIndex));
14639 
14640             res = AllocateMemoryOfType(
14641                 vkMemReq.size,
14642                 alignmentForMemType,
14643                 requiresDedicatedAllocation || prefersDedicatedAllocation,
14644                 dedicatedBuffer,
14645                 dedicatedImage,
14646                 createInfo,
14647                 memTypeIndex,
14648                 suballocType,
14649                 allocationCount,
14650                 pAllocations);
14651             // Succeeded on first try.
14652             if(res == VK_SUCCESS)
14653             {
14654                 return res;
14655             }
14656             // Allocation from this memory type failed. Try other compatible memory types.
14657             else
14658             {
14659                 for(;;)
14660                 {
14661                     // Remove old memTypeIndex from list of possibilities.
14662                     memoryTypeBits &= ~(1u << memTypeIndex);
14663                     // Find alternative memTypeIndex.
14664                     res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
14665                     if(res == VK_SUCCESS)
14666                     {
14667                         alignmentForMemType = VMA_MAX(
14668                             vkMemReq.alignment,
14669                             GetMemoryTypeMinAlignment(memTypeIndex));
14670 
14671                         res = AllocateMemoryOfType(
14672                             vkMemReq.size,
14673                             alignmentForMemType,
14674                             requiresDedicatedAllocation || prefersDedicatedAllocation,
14675                             dedicatedBuffer,
14676                             dedicatedImage,
14677                             createInfo,
14678                             memTypeIndex,
14679                             suballocType,
14680                             allocationCount,
14681                             pAllocations);
14682                         // Allocation from this alternative memory type succeeded.
14683                         if(res == VK_SUCCESS)
14684                         {
14685                             return res;
14686                         }
14687                         // else: Allocation from this memory type failed. Try next one - next loop iteration.
14688                     }
14689                     // No other matching memory type index could be found.
14690                     else
14691                     {
14692                         // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
14693                         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14694                     }
14695                 }
14696             }
14697         }
14698         // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
14699         else
14700             return res;
14701     }
14702 }
14703 
FreeMemory(size_t allocationCount,const VmaAllocation * pAllocations)14704 void VmaAllocator_T::FreeMemory(
14705     size_t allocationCount,
14706     const VmaAllocation* pAllocations)
14707 {
14708     VMA_ASSERT(pAllocations);
14709 
14710     for(size_t allocIndex = allocationCount; allocIndex--; )
14711     {
14712         VmaAllocation allocation = pAllocations[allocIndex];
14713 
14714         if(allocation != VK_NULL_HANDLE)
14715         {
14716             if(TouchAllocation(allocation))
14717             {
14718                 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
14719                 {
14720                     FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
14721                 }
14722 
14723                 switch(allocation->GetType())
14724                 {
14725                 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
14726                     {
14727                         VmaBlockVector* pBlockVector = VMA_NULL;
14728                         VmaPool hPool = allocation->GetPool();
14729                         if(hPool != VK_NULL_HANDLE)
14730                         {
14731                             pBlockVector = &hPool->m_BlockVector;
14732                         }
14733                         else
14734                         {
14735                             const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
14736                             pBlockVector = m_pBlockVectors[memTypeIndex];
14737                         }
14738                         pBlockVector->Free(allocation);
14739                     }
14740                     break;
14741                 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
14742                     FreeDedicatedMemory(allocation);
14743                     break;
14744                 default:
14745                     VMA_ASSERT(0);
14746                 }
14747             }
14748 
14749             allocation->SetUserData(this, VMA_NULL);
14750             vma_delete(this, allocation);
14751         }
14752     }
14753 }
14754 
ResizeAllocation(const VmaAllocation alloc,VkDeviceSize newSize)14755 VkResult VmaAllocator_T::ResizeAllocation(
14756     const VmaAllocation alloc,
14757     VkDeviceSize newSize)
14758 {
14759     if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST)
14760     {
14761         return VK_ERROR_VALIDATION_FAILED_EXT;
14762     }
14763     if(newSize == alloc->GetSize())
14764     {
14765         return VK_SUCCESS;
14766     }
14767 
14768     switch(alloc->GetType())
14769     {
14770     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
14771         return VK_ERROR_FEATURE_NOT_PRESENT;
14772     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
14773         if(alloc->GetBlock()->m_pMetadata->ResizeAllocation(alloc, newSize))
14774         {
14775             alloc->ChangeSize(newSize);
14776             VMA_HEAVY_ASSERT(alloc->GetBlock()->m_pMetadata->Validate());
14777             return VK_SUCCESS;
14778         }
14779         else
14780         {
14781             return VK_ERROR_OUT_OF_POOL_MEMORY;
14782         }
14783     default:
14784         VMA_ASSERT(0);
14785         return VK_ERROR_VALIDATION_FAILED_EXT;
14786     }
14787 }
14788 
CalculateStats(VmaStats * pStats)14789 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
14790 {
14791     // Initialize.
14792     InitStatInfo(pStats->total);
14793     for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
14794         InitStatInfo(pStats->memoryType[i]);
14795     for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
14796         InitStatInfo(pStats->memoryHeap[i]);
14797 
14798     // Process default pools.
14799     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14800     {
14801         VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
14802         VMA_ASSERT(pBlockVector);
14803         pBlockVector->AddStats(pStats);
14804     }
14805 
14806     // Process custom pools.
14807     {
14808         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
14809         for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
14810         {
14811             m_Pools[poolIndex]->m_BlockVector.AddStats(pStats);
14812         }
14813     }
14814 
14815     // Process dedicated allocations.
14816     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14817     {
14818         const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
14819         VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
14820         AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
14821         VMA_ASSERT(pDedicatedAllocVector);
14822         for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
14823         {
14824             VmaStatInfo allocationStatInfo;
14825             (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo);
14826             VmaAddStatInfo(pStats->total, allocationStatInfo);
14827             VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
14828             VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
14829         }
14830     }
14831 
14832     // Postprocess.
14833     VmaPostprocessCalcStatInfo(pStats->total);
14834     for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
14835         VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
14836     for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
14837         VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
14838 }
14839 
14840 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
14841 
DefragmentationBegin(const VmaDefragmentationInfo2 & info,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)14842 VkResult VmaAllocator_T::DefragmentationBegin(
14843     const VmaDefragmentationInfo2& info,
14844     VmaDefragmentationStats* pStats,
14845     VmaDefragmentationContext* pContext)
14846 {
14847     if(info.pAllocationsChanged != VMA_NULL)
14848     {
14849         memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
14850     }
14851 
14852     *pContext = vma_new(this, VmaDefragmentationContext_T)(
14853         this, m_CurrentFrameIndex.load(), info.flags, pStats);
14854 
14855     (*pContext)->AddPools(info.poolCount, info.pPools);
14856     (*pContext)->AddAllocations(
14857         info.allocationCount, info.pAllocations, info.pAllocationsChanged);
14858 
14859     VkResult res = (*pContext)->Defragment(
14860         info.maxCpuBytesToMove, info.maxCpuAllocationsToMove,
14861         info.maxGpuBytesToMove, info.maxGpuAllocationsToMove,
14862         info.commandBuffer, pStats);
14863 
14864     if(res != VK_NOT_READY)
14865     {
14866         vma_delete(this, *pContext);
14867         *pContext = VMA_NULL;
14868     }
14869 
14870     return res;
14871 }
14872 
DefragmentationEnd(VmaDefragmentationContext context)14873 VkResult VmaAllocator_T::DefragmentationEnd(
14874     VmaDefragmentationContext context)
14875 {
14876     vma_delete(this, context);
14877     return VK_SUCCESS;
14878 }
14879 
GetAllocationInfo(VmaAllocation hAllocation,VmaAllocationInfo * pAllocationInfo)14880 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
14881 {
14882     if(hAllocation->CanBecomeLost())
14883     {
14884         /*
14885         Warning: This is a carefully designed algorithm.
14886         Do not modify unless you really know what you're doing :)
14887         */
14888         const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14889         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14890         for(;;)
14891         {
14892             if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
14893             {
14894                 pAllocationInfo->memoryType = UINT32_MAX;
14895                 pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
14896                 pAllocationInfo->offset = 0;
14897                 pAllocationInfo->size = hAllocation->GetSize();
14898                 pAllocationInfo->pMappedData = VMA_NULL;
14899                 pAllocationInfo->pUserData = hAllocation->GetUserData();
14900                 return;
14901             }
14902             else if(localLastUseFrameIndex == localCurrFrameIndex)
14903             {
14904                 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
14905                 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
14906                 pAllocationInfo->offset = hAllocation->GetOffset();
14907                 pAllocationInfo->size = hAllocation->GetSize();
14908                 pAllocationInfo->pMappedData = VMA_NULL;
14909                 pAllocationInfo->pUserData = hAllocation->GetUserData();
14910                 return;
14911             }
14912             else // Last use time earlier than current time.
14913             {
14914                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14915                 {
14916                     localLastUseFrameIndex = localCurrFrameIndex;
14917                 }
14918             }
14919         }
14920     }
14921     else
14922     {
14923 #if VMA_STATS_STRING_ENABLED
14924         uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14925         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14926         for(;;)
14927         {
14928             VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
14929             if(localLastUseFrameIndex == localCurrFrameIndex)
14930             {
14931                 break;
14932             }
14933             else // Last use time earlier than current time.
14934             {
14935                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14936                 {
14937                     localLastUseFrameIndex = localCurrFrameIndex;
14938                 }
14939             }
14940         }
14941 #endif
14942 
14943         pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
14944         pAllocationInfo->deviceMemory = hAllocation->GetMemory();
14945         pAllocationInfo->offset = hAllocation->GetOffset();
14946         pAllocationInfo->size = hAllocation->GetSize();
14947         pAllocationInfo->pMappedData = hAllocation->GetMappedData();
14948         pAllocationInfo->pUserData = hAllocation->GetUserData();
14949     }
14950 }
14951 
TouchAllocation(VmaAllocation hAllocation)14952 bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
14953 {
14954     // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
14955     if(hAllocation->CanBecomeLost())
14956     {
14957         uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14958         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14959         for(;;)
14960         {
14961             if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
14962             {
14963                 return false;
14964             }
14965             else if(localLastUseFrameIndex == localCurrFrameIndex)
14966             {
14967                 return true;
14968             }
14969             else // Last use time earlier than current time.
14970             {
14971                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14972                 {
14973                     localLastUseFrameIndex = localCurrFrameIndex;
14974                 }
14975             }
14976         }
14977     }
14978     else
14979     {
14980 #if VMA_STATS_STRING_ENABLED
14981         uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14982         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14983         for(;;)
14984         {
14985             VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
14986             if(localLastUseFrameIndex == localCurrFrameIndex)
14987             {
14988                 break;
14989             }
14990             else // Last use time earlier than current time.
14991             {
14992                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14993                 {
14994                     localLastUseFrameIndex = localCurrFrameIndex;
14995                 }
14996             }
14997         }
14998 #endif
14999 
15000         return true;
15001     }
15002 }
15003 
CreatePool(const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)15004 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
15005 {
15006     VMA_DEBUG_LOG("  CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
15007 
15008     VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
15009 
15010     if(newCreateInfo.maxBlockCount == 0)
15011     {
15012         newCreateInfo.maxBlockCount = SIZE_MAX;
15013     }
15014     if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
15015     {
15016         return VK_ERROR_INITIALIZATION_FAILED;
15017     }
15018 
15019     const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
15020 
15021     *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
15022 
15023     VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
15024     if(res != VK_SUCCESS)
15025     {
15026         vma_delete(this, *pPool);
15027         *pPool = VMA_NULL;
15028         return res;
15029     }
15030 
15031     // Add to m_Pools.
15032     {
15033         VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15034         (*pPool)->SetId(m_NextPoolId++);
15035         VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool);
15036     }
15037 
15038     return VK_SUCCESS;
15039 }
15040 
DestroyPool(VmaPool pool)15041 void VmaAllocator_T::DestroyPool(VmaPool pool)
15042 {
15043     // Remove from m_Pools.
15044     {
15045         VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15046         bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
15047         VMA_ASSERT(success && "Pool not found in Allocator.");
15048     }
15049 
15050     vma_delete(this, pool);
15051 }
15052 
GetPoolStats(VmaPool pool,VmaPoolStats * pPoolStats)15053 void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
15054 {
15055     pool->m_BlockVector.GetPoolStats(pPoolStats);
15056 }
15057 
SetCurrentFrameIndex(uint32_t frameIndex)15058 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
15059 {
15060     m_CurrentFrameIndex.store(frameIndex);
15061 }
15062 
MakePoolAllocationsLost(VmaPool hPool,size_t * pLostAllocationCount)15063 void VmaAllocator_T::MakePoolAllocationsLost(
15064     VmaPool hPool,
15065     size_t* pLostAllocationCount)
15066 {
15067     hPool->m_BlockVector.MakePoolAllocationsLost(
15068         m_CurrentFrameIndex.load(),
15069         pLostAllocationCount);
15070 }
15071 
CheckPoolCorruption(VmaPool hPool)15072 VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
15073 {
15074     return hPool->m_BlockVector.CheckCorruption();
15075 }
15076 
CheckCorruption(uint32_t memoryTypeBits)15077 VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
15078 {
15079     VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
15080 
15081     // Process default pools.
15082     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15083     {
15084         if(((1u << memTypeIndex) & memoryTypeBits) != 0)
15085         {
15086             VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
15087             VMA_ASSERT(pBlockVector);
15088             VkResult localRes = pBlockVector->CheckCorruption();
15089             switch(localRes)
15090             {
15091             case VK_ERROR_FEATURE_NOT_PRESENT:
15092                 break;
15093             case VK_SUCCESS:
15094                 finalRes = VK_SUCCESS;
15095                 break;
15096             default:
15097                 return localRes;
15098             }
15099         }
15100     }
15101 
15102     // Process custom pools.
15103     {
15104         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15105         for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
15106         {
15107             if(((1u << m_Pools[poolIndex]->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
15108             {
15109                 VkResult localRes = m_Pools[poolIndex]->m_BlockVector.CheckCorruption();
15110                 switch(localRes)
15111                 {
15112                 case VK_ERROR_FEATURE_NOT_PRESENT:
15113                     break;
15114                 case VK_SUCCESS:
15115                     finalRes = VK_SUCCESS;
15116                     break;
15117                 default:
15118                     return localRes;
15119                 }
15120             }
15121         }
15122     }
15123 
15124     return finalRes;
15125 }
15126 
CreateLostAllocation(VmaAllocation * pAllocation)15127 void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
15128 {
15129     *pAllocation = vma_new(this, VmaAllocation_T)(VMA_FRAME_INDEX_LOST, false);
15130     (*pAllocation)->InitLost();
15131 }
15132 
AllocateVulkanMemory(const VkMemoryAllocateInfo * pAllocateInfo,VkDeviceMemory * pMemory)15133 VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
15134 {
15135     const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
15136 
15137     VkResult res;
15138     if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
15139     {
15140         VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
15141         if(m_HeapSizeLimit[heapIndex] >= pAllocateInfo->allocationSize)
15142         {
15143             res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
15144             if(res == VK_SUCCESS)
15145             {
15146                 m_HeapSizeLimit[heapIndex] -= pAllocateInfo->allocationSize;
15147             }
15148         }
15149         else
15150         {
15151             res = VK_ERROR_OUT_OF_DEVICE_MEMORY;
15152         }
15153     }
15154     else
15155     {
15156         res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
15157     }
15158 
15159     if(res == VK_SUCCESS && m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
15160     {
15161         (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize);
15162     }
15163 
15164     return res;
15165 }
15166 
FreeVulkanMemory(uint32_t memoryType,VkDeviceSize size,VkDeviceMemory hMemory)15167 void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
15168 {
15169     if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
15170     {
15171         (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size);
15172     }
15173 
15174     (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
15175 
15176     const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memoryType);
15177     if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
15178     {
15179         VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
15180         m_HeapSizeLimit[heapIndex] += size;
15181     }
15182 }
15183 
Map(VmaAllocation hAllocation,void ** ppData)15184 VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
15185 {
15186     if(hAllocation->CanBecomeLost())
15187     {
15188         return VK_ERROR_MEMORY_MAP_FAILED;
15189     }
15190 
15191     switch(hAllocation->GetType())
15192     {
15193     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15194         {
15195             VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
15196             char *pBytes = VMA_NULL;
15197             VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
15198             if(res == VK_SUCCESS)
15199             {
15200                 *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
15201                 hAllocation->BlockAllocMap();
15202             }
15203             return res;
15204         }
15205     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15206         return hAllocation->DedicatedAllocMap(this, ppData);
15207     default:
15208         VMA_ASSERT(0);
15209         return VK_ERROR_MEMORY_MAP_FAILED;
15210     }
15211 }
15212 
Unmap(VmaAllocation hAllocation)15213 void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
15214 {
15215     switch(hAllocation->GetType())
15216     {
15217     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15218         {
15219             VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
15220             hAllocation->BlockAllocUnmap();
15221             pBlock->Unmap(this, 1);
15222         }
15223         break;
15224     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15225         hAllocation->DedicatedAllocUnmap(this);
15226         break;
15227     default:
15228         VMA_ASSERT(0);
15229     }
15230 }
15231 
BindBufferMemory(VmaAllocation hAllocation,VkBuffer hBuffer)15232 VkResult VmaAllocator_T::BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer)
15233 {
15234     VkResult res = VK_SUCCESS;
15235     switch(hAllocation->GetType())
15236     {
15237     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15238         res = GetVulkanFunctions().vkBindBufferMemory(
15239             m_hDevice,
15240             hBuffer,
15241             hAllocation->GetMemory(),
15242             0); //memoryOffset
15243         break;
15244     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15245     {
15246         VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
15247         VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
15248         res = pBlock->BindBufferMemory(this, hAllocation, hBuffer);
15249         break;
15250     }
15251     default:
15252         VMA_ASSERT(0);
15253     }
15254     return res;
15255 }
15256 
BindImageMemory(VmaAllocation hAllocation,VkImage hImage)15257 VkResult VmaAllocator_T::BindImageMemory(VmaAllocation hAllocation, VkImage hImage)
15258 {
15259     VkResult res = VK_SUCCESS;
15260     switch(hAllocation->GetType())
15261     {
15262     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15263         res = GetVulkanFunctions().vkBindImageMemory(
15264             m_hDevice,
15265             hImage,
15266             hAllocation->GetMemory(),
15267             0); //memoryOffset
15268         break;
15269     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15270     {
15271         VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
15272         VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
15273         res = pBlock->BindImageMemory(this, hAllocation, hImage);
15274         break;
15275     }
15276     default:
15277         VMA_ASSERT(0);
15278     }
15279     return res;
15280 }
15281 
FlushOrInvalidateAllocation(VmaAllocation hAllocation,VkDeviceSize offset,VkDeviceSize size,VMA_CACHE_OPERATION op)15282 void VmaAllocator_T::FlushOrInvalidateAllocation(
15283     VmaAllocation hAllocation,
15284     VkDeviceSize offset, VkDeviceSize size,
15285     VMA_CACHE_OPERATION op)
15286 {
15287     const uint32_t memTypeIndex = hAllocation->GetMemoryTypeIndex();
15288     if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
15289     {
15290         const VkDeviceSize allocationSize = hAllocation->GetSize();
15291         VMA_ASSERT(offset <= allocationSize);
15292 
15293         const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
15294 
15295         VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
15296         memRange.memory = hAllocation->GetMemory();
15297 
15298         switch(hAllocation->GetType())
15299         {
15300         case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15301             memRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
15302             if(size == VK_WHOLE_SIZE)
15303             {
15304                 memRange.size = allocationSize - memRange.offset;
15305             }
15306             else
15307             {
15308                 VMA_ASSERT(offset + size <= allocationSize);
15309                 memRange.size = VMA_MIN(
15310                     VmaAlignUp(size + (offset - memRange.offset), nonCoherentAtomSize),
15311                     allocationSize - memRange.offset);
15312             }
15313             break;
15314 
15315         case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15316         {
15317             // 1. Still within this allocation.
15318             memRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
15319             if(size == VK_WHOLE_SIZE)
15320             {
15321                 size = allocationSize - offset;
15322             }
15323             else
15324             {
15325                 VMA_ASSERT(offset + size <= allocationSize);
15326             }
15327             memRange.size = VmaAlignUp(size + (offset - memRange.offset), nonCoherentAtomSize);
15328 
15329             // 2. Adjust to whole block.
15330             const VkDeviceSize allocationOffset = hAllocation->GetOffset();
15331             VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
15332             const VkDeviceSize blockSize = hAllocation->GetBlock()->m_pMetadata->GetSize();
15333             memRange.offset += allocationOffset;
15334             memRange.size = VMA_MIN(memRange.size, blockSize - memRange.offset);
15335 
15336             break;
15337         }
15338 
15339         default:
15340             VMA_ASSERT(0);
15341         }
15342 
15343         switch(op)
15344         {
15345         case VMA_CACHE_FLUSH:
15346             (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
15347             break;
15348         case VMA_CACHE_INVALIDATE:
15349             (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
15350             break;
15351         default:
15352             VMA_ASSERT(0);
15353         }
15354     }
15355     // else: Just ignore this call.
15356 }
15357 
FreeDedicatedMemory(VmaAllocation allocation)15358 void VmaAllocator_T::FreeDedicatedMemory(VmaAllocation allocation)
15359 {
15360     VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
15361 
15362     const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
15363     {
15364         VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
15365         AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
15366         VMA_ASSERT(pDedicatedAllocations);
15367         bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
15368         VMA_ASSERT(success);
15369     }
15370 
15371     VkDeviceMemory hMemory = allocation->GetMemory();
15372 
15373     /*
15374     There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
15375     before vkFreeMemory.
15376 
15377     if(allocation->GetMappedData() != VMA_NULL)
15378     {
15379         (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
15380     }
15381     */
15382 
15383     FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
15384 
15385     VMA_DEBUG_LOG("    Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
15386 }
15387 
FillAllocation(const VmaAllocation hAllocation,uint8_t pattern)15388 void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
15389 {
15390     if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
15391         !hAllocation->CanBecomeLost() &&
15392         (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
15393     {
15394         void* pData = VMA_NULL;
15395         VkResult res = Map(hAllocation, &pData);
15396         if(res == VK_SUCCESS)
15397         {
15398             memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
15399             FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
15400             Unmap(hAllocation);
15401         }
15402         else
15403         {
15404             VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
15405         }
15406     }
15407 }
15408 
15409 #if VMA_STATS_STRING_ENABLED
15410 
PrintDetailedMap(VmaJsonWriter & json)15411 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
15412 {
15413     bool dedicatedAllocationsStarted = false;
15414     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15415     {
15416         VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
15417         AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
15418         VMA_ASSERT(pDedicatedAllocVector);
15419         if(pDedicatedAllocVector->empty() == false)
15420         {
15421             if(dedicatedAllocationsStarted == false)
15422             {
15423                 dedicatedAllocationsStarted = true;
15424                 json.WriteString("DedicatedAllocations");
15425                 json.BeginObject();
15426             }
15427 
15428             json.BeginString("Type ");
15429             json.ContinueString(memTypeIndex);
15430             json.EndString();
15431 
15432             json.BeginArray();
15433 
15434             for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
15435             {
15436                 json.BeginObject(true);
15437                 const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
15438                 hAlloc->PrintParameters(json);
15439                 json.EndObject();
15440             }
15441 
15442             json.EndArray();
15443         }
15444     }
15445     if(dedicatedAllocationsStarted)
15446     {
15447         json.EndObject();
15448     }
15449 
15450     {
15451         bool allocationsStarted = false;
15452         for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15453         {
15454             if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
15455             {
15456                 if(allocationsStarted == false)
15457                 {
15458                     allocationsStarted = true;
15459                     json.WriteString("DefaultPools");
15460                     json.BeginObject();
15461                 }
15462 
15463                 json.BeginString("Type ");
15464                 json.ContinueString(memTypeIndex);
15465                 json.EndString();
15466 
15467                 m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
15468             }
15469         }
15470         if(allocationsStarted)
15471         {
15472             json.EndObject();
15473         }
15474     }
15475 
15476     // Custom pools
15477     {
15478         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15479         const size_t poolCount = m_Pools.size();
15480         if(poolCount > 0)
15481         {
15482             json.WriteString("Pools");
15483             json.BeginObject();
15484             for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
15485             {
15486                 json.BeginString();
15487                 json.ContinueString(m_Pools[poolIndex]->GetId());
15488                 json.EndString();
15489 
15490                 m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
15491             }
15492             json.EndObject();
15493         }
15494     }
15495 }
15496 
15497 #endif // #if VMA_STATS_STRING_ENABLED
15498 
15499 ////////////////////////////////////////////////////////////////////////////////
15500 // Public interface
15501 
vmaCreateAllocator(const VmaAllocatorCreateInfo * pCreateInfo,VmaAllocator * pAllocator)15502 VkResult vmaCreateAllocator(
15503     const VmaAllocatorCreateInfo* pCreateInfo,
15504     VmaAllocator* pAllocator)
15505 {
15506     VMA_ASSERT(pCreateInfo && pAllocator);
15507     VMA_DEBUG_LOG("vmaCreateAllocator");
15508     *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
15509     return (*pAllocator)->Init(pCreateInfo);
15510 }
15511 
vmaDestroyAllocator(VmaAllocator allocator)15512 void vmaDestroyAllocator(
15513     VmaAllocator allocator)
15514 {
15515     if(allocator != VK_NULL_HANDLE)
15516     {
15517         VMA_DEBUG_LOG("vmaDestroyAllocator");
15518         VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
15519         vma_delete(&allocationCallbacks, allocator);
15520     }
15521 }
15522 
vmaGetPhysicalDeviceProperties(VmaAllocator allocator,const VkPhysicalDeviceProperties ** ppPhysicalDeviceProperties)15523 void vmaGetPhysicalDeviceProperties(
15524     VmaAllocator allocator,
15525     const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
15526 {
15527     VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
15528     *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
15529 }
15530 
vmaGetMemoryProperties(VmaAllocator allocator,const VkPhysicalDeviceMemoryProperties ** ppPhysicalDeviceMemoryProperties)15531 void vmaGetMemoryProperties(
15532     VmaAllocator allocator,
15533     const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
15534 {
15535     VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
15536     *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
15537 }
15538 
vmaGetMemoryTypeProperties(VmaAllocator allocator,uint32_t memoryTypeIndex,VkMemoryPropertyFlags * pFlags)15539 void vmaGetMemoryTypeProperties(
15540     VmaAllocator allocator,
15541     uint32_t memoryTypeIndex,
15542     VkMemoryPropertyFlags* pFlags)
15543 {
15544     VMA_ASSERT(allocator && pFlags);
15545     VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
15546     *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
15547 }
15548 
vmaSetCurrentFrameIndex(VmaAllocator allocator,uint32_t frameIndex)15549 void vmaSetCurrentFrameIndex(
15550     VmaAllocator allocator,
15551     uint32_t frameIndex)
15552 {
15553     VMA_ASSERT(allocator);
15554     VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
15555 
15556     VMA_DEBUG_GLOBAL_MUTEX_LOCK
15557 
15558     allocator->SetCurrentFrameIndex(frameIndex);
15559 }
15560 
vmaCalculateStats(VmaAllocator allocator,VmaStats * pStats)15561 void vmaCalculateStats(
15562     VmaAllocator allocator,
15563     VmaStats* pStats)
15564 {
15565     VMA_ASSERT(allocator && pStats);
15566     VMA_DEBUG_GLOBAL_MUTEX_LOCK
15567     allocator->CalculateStats(pStats);
15568 }
15569 
15570 #if VMA_STATS_STRING_ENABLED
15571 
vmaBuildStatsString(VmaAllocator allocator,char ** ppStatsString,VkBool32 detailedMap)15572 void vmaBuildStatsString(
15573     VmaAllocator allocator,
15574     char** ppStatsString,
15575     VkBool32 detailedMap)
15576 {
15577     VMA_ASSERT(allocator && ppStatsString);
15578     VMA_DEBUG_GLOBAL_MUTEX_LOCK
15579 
15580     VmaStringBuilder sb(allocator);
15581     {
15582         VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
15583         json.BeginObject();
15584 
15585         VmaStats stats;
15586         allocator->CalculateStats(&stats);
15587 
15588         json.WriteString("Total");
15589         VmaPrintStatInfo(json, stats.total);
15590 
15591         for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
15592         {
15593             json.BeginString("Heap ");
15594             json.ContinueString(heapIndex);
15595             json.EndString();
15596             json.BeginObject();
15597 
15598             json.WriteString("Size");
15599             json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
15600 
15601             json.WriteString("Flags");
15602             json.BeginArray(true);
15603             if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
15604             {
15605                 json.WriteString("DEVICE_LOCAL");
15606             }
15607             json.EndArray();
15608 
15609             if(stats.memoryHeap[heapIndex].blockCount > 0)
15610             {
15611                 json.WriteString("Stats");
15612                 VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
15613             }
15614 
15615             for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
15616             {
15617                 if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
15618                 {
15619                     json.BeginString("Type ");
15620                     json.ContinueString(typeIndex);
15621                     json.EndString();
15622 
15623                     json.BeginObject();
15624 
15625                     json.WriteString("Flags");
15626                     json.BeginArray(true);
15627                     VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
15628                     if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
15629                     {
15630                         json.WriteString("DEVICE_LOCAL");
15631                     }
15632                     if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
15633                     {
15634                         json.WriteString("HOST_VISIBLE");
15635                     }
15636                     if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
15637                     {
15638                         json.WriteString("HOST_COHERENT");
15639                     }
15640                     if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
15641                     {
15642                         json.WriteString("HOST_CACHED");
15643                     }
15644                     if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
15645                     {
15646                         json.WriteString("LAZILY_ALLOCATED");
15647                     }
15648                     json.EndArray();
15649 
15650                     if(stats.memoryType[typeIndex].blockCount > 0)
15651                     {
15652                         json.WriteString("Stats");
15653                         VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
15654                     }
15655 
15656                     json.EndObject();
15657                 }
15658             }
15659 
15660             json.EndObject();
15661         }
15662         if(detailedMap == VK_TRUE)
15663         {
15664             allocator->PrintDetailedMap(json);
15665         }
15666 
15667         json.EndObject();
15668     }
15669 
15670     const size_t len = sb.GetLength();
15671     char* const pChars = vma_new_array(allocator, char, len + 1);
15672     if(len > 0)
15673     {
15674         memcpy(pChars, sb.GetData(), len);
15675     }
15676     pChars[len] = '\0';
15677     *ppStatsString = pChars;
15678 }
15679 
vmaFreeStatsString(VmaAllocator allocator,char * pStatsString)15680 void vmaFreeStatsString(
15681     VmaAllocator allocator,
15682     char* pStatsString)
15683 {
15684     if(pStatsString != VMA_NULL)
15685     {
15686         VMA_ASSERT(allocator);
15687         size_t len = strlen(pStatsString);
15688         vma_delete_array(allocator, pStatsString, len + 1);
15689     }
15690 }
15691 
15692 #endif // #if VMA_STATS_STRING_ENABLED
15693 
15694 /*
15695 This function is not protected by any mutex because it just reads immutable data.
15696 */
vmaFindMemoryTypeIndex(VmaAllocator allocator,uint32_t memoryTypeBits,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)15697 VkResult vmaFindMemoryTypeIndex(
15698     VmaAllocator allocator,
15699     uint32_t memoryTypeBits,
15700     const VmaAllocationCreateInfo* pAllocationCreateInfo,
15701     uint32_t* pMemoryTypeIndex)
15702 {
15703     VMA_ASSERT(allocator != VK_NULL_HANDLE);
15704     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15705     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15706 
15707     if(pAllocationCreateInfo->memoryTypeBits != 0)
15708     {
15709         memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
15710     }
15711 
15712     uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
15713     uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
15714 
15715     const bool mapped = (pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
15716     if(mapped)
15717     {
15718         preferredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15719     }
15720 
15721     // Convert usage to requiredFlags and preferredFlags.
15722     switch(pAllocationCreateInfo->usage)
15723     {
15724     case VMA_MEMORY_USAGE_UNKNOWN:
15725         break;
15726     case VMA_MEMORY_USAGE_GPU_ONLY:
15727         if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15728         {
15729             preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
15730         }
15731         break;
15732     case VMA_MEMORY_USAGE_CPU_ONLY:
15733         requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
15734         break;
15735     case VMA_MEMORY_USAGE_CPU_TO_GPU:
15736         requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15737         if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15738         {
15739             preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
15740         }
15741         break;
15742     case VMA_MEMORY_USAGE_GPU_TO_CPU:
15743         requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15744         preferredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
15745         break;
15746     default:
15747         break;
15748     }
15749 
15750     *pMemoryTypeIndex = UINT32_MAX;
15751     uint32_t minCost = UINT32_MAX;
15752     for(uint32_t memTypeIndex = 0, memTypeBit = 1;
15753         memTypeIndex < allocator->GetMemoryTypeCount();
15754         ++memTypeIndex, memTypeBit <<= 1)
15755     {
15756         // This memory type is acceptable according to memoryTypeBits bitmask.
15757         if((memTypeBit & memoryTypeBits) != 0)
15758         {
15759             const VkMemoryPropertyFlags currFlags =
15760                 allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
15761             // This memory type contains requiredFlags.
15762             if((requiredFlags & ~currFlags) == 0)
15763             {
15764                 // Calculate cost as number of bits from preferredFlags not present in this memory type.
15765                 uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags);
15766                 // Remember memory type with lowest cost.
15767                 if(currCost < minCost)
15768                 {
15769                     *pMemoryTypeIndex = memTypeIndex;
15770                     if(currCost == 0)
15771                     {
15772                         return VK_SUCCESS;
15773                     }
15774                     minCost = currCost;
15775                 }
15776             }
15777         }
15778     }
15779     return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
15780 }
15781 
vmaFindMemoryTypeIndexForBufferInfo(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)15782 VkResult vmaFindMemoryTypeIndexForBufferInfo(
15783     VmaAllocator allocator,
15784     const VkBufferCreateInfo* pBufferCreateInfo,
15785     const VmaAllocationCreateInfo* pAllocationCreateInfo,
15786     uint32_t* pMemoryTypeIndex)
15787 {
15788     VMA_ASSERT(allocator != VK_NULL_HANDLE);
15789     VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
15790     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15791     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15792 
15793     const VkDevice hDev = allocator->m_hDevice;
15794     VkBuffer hBuffer = VK_NULL_HANDLE;
15795     VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
15796         hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
15797     if(res == VK_SUCCESS)
15798     {
15799         VkMemoryRequirements memReq = {};
15800         allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
15801             hDev, hBuffer, &memReq);
15802 
15803         res = vmaFindMemoryTypeIndex(
15804             allocator,
15805             memReq.memoryTypeBits,
15806             pAllocationCreateInfo,
15807             pMemoryTypeIndex);
15808 
15809         allocator->GetVulkanFunctions().vkDestroyBuffer(
15810             hDev, hBuffer, allocator->GetAllocationCallbacks());
15811     }
15812     return res;
15813 }
15814 
vmaFindMemoryTypeIndexForImageInfo(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)15815 VkResult vmaFindMemoryTypeIndexForImageInfo(
15816     VmaAllocator allocator,
15817     const VkImageCreateInfo* pImageCreateInfo,
15818     const VmaAllocationCreateInfo* pAllocationCreateInfo,
15819     uint32_t* pMemoryTypeIndex)
15820 {
15821     VMA_ASSERT(allocator != VK_NULL_HANDLE);
15822     VMA_ASSERT(pImageCreateInfo != VMA_NULL);
15823     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15824     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15825 
15826     const VkDevice hDev = allocator->m_hDevice;
15827     VkImage hImage = VK_NULL_HANDLE;
15828     VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
15829         hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
15830     if(res == VK_SUCCESS)
15831     {
15832         VkMemoryRequirements memReq = {};
15833         allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
15834             hDev, hImage, &memReq);
15835 
15836         res = vmaFindMemoryTypeIndex(
15837             allocator,
15838             memReq.memoryTypeBits,
15839             pAllocationCreateInfo,
15840             pMemoryTypeIndex);
15841 
15842         allocator->GetVulkanFunctions().vkDestroyImage(
15843             hDev, hImage, allocator->GetAllocationCallbacks());
15844     }
15845     return res;
15846 }
15847 
vmaCreatePool(VmaAllocator allocator,const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)15848 VkResult vmaCreatePool(
15849 	VmaAllocator allocator,
15850 	const VmaPoolCreateInfo* pCreateInfo,
15851 	VmaPool* pPool)
15852 {
15853     VMA_ASSERT(allocator && pCreateInfo && pPool);
15854 
15855     VMA_DEBUG_LOG("vmaCreatePool");
15856 
15857     VMA_DEBUG_GLOBAL_MUTEX_LOCK
15858 
15859     VkResult res = allocator->CreatePool(pCreateInfo, pPool);
15860 
15861 #if VMA_RECORDING_ENABLED
15862     if(allocator->GetRecorder() != VMA_NULL)
15863     {
15864         allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
15865     }
15866 #endif
15867 
15868     return res;
15869 }
15870 
vmaDestroyPool(VmaAllocator allocator,VmaPool pool)15871 void vmaDestroyPool(
15872     VmaAllocator allocator,
15873     VmaPool pool)
15874 {
15875     VMA_ASSERT(allocator);
15876 
15877     if(pool == VK_NULL_HANDLE)
15878     {
15879         return;
15880     }
15881 
15882     VMA_DEBUG_LOG("vmaDestroyPool");
15883 
15884     VMA_DEBUG_GLOBAL_MUTEX_LOCK
15885 
15886 #if VMA_RECORDING_ENABLED
15887     if(allocator->GetRecorder() != VMA_NULL)
15888     {
15889         allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
15890     }
15891 #endif
15892 
15893     allocator->DestroyPool(pool);
15894 }
15895 
vmaGetPoolStats(VmaAllocator allocator,VmaPool pool,VmaPoolStats * pPoolStats)15896 void vmaGetPoolStats(
15897     VmaAllocator allocator,
15898     VmaPool pool,
15899     VmaPoolStats* pPoolStats)
15900 {
15901     VMA_ASSERT(allocator && pool && pPoolStats);
15902 
15903     VMA_DEBUG_GLOBAL_MUTEX_LOCK
15904 
15905     allocator->GetPoolStats(pool, pPoolStats);
15906 }
15907 
vmaMakePoolAllocationsLost(VmaAllocator allocator,VmaPool pool,size_t * pLostAllocationCount)15908 void vmaMakePoolAllocationsLost(
15909     VmaAllocator allocator,
15910     VmaPool pool,
15911     size_t* pLostAllocationCount)
15912 {
15913     VMA_ASSERT(allocator && pool);
15914 
15915     VMA_DEBUG_GLOBAL_MUTEX_LOCK
15916 
15917 #if VMA_RECORDING_ENABLED
15918     if(allocator->GetRecorder() != VMA_NULL)
15919     {
15920         allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
15921     }
15922 #endif
15923 
15924     allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
15925 }
15926 
vmaCheckPoolCorruption(VmaAllocator allocator,VmaPool pool)15927 VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
15928 {
15929     VMA_ASSERT(allocator && pool);
15930 
15931     VMA_DEBUG_GLOBAL_MUTEX_LOCK
15932 
15933     VMA_DEBUG_LOG("vmaCheckPoolCorruption");
15934 
15935     return allocator->CheckPoolCorruption(pool);
15936 }
15937 
vmaAllocateMemory(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)15938 VkResult vmaAllocateMemory(
15939     VmaAllocator allocator,
15940     const VkMemoryRequirements* pVkMemoryRequirements,
15941     const VmaAllocationCreateInfo* pCreateInfo,
15942     VmaAllocation* pAllocation,
15943     VmaAllocationInfo* pAllocationInfo)
15944 {
15945     VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
15946 
15947     VMA_DEBUG_LOG("vmaAllocateMemory");
15948 
15949     VMA_DEBUG_GLOBAL_MUTEX_LOCK
15950 
15951 	VkResult result = allocator->AllocateMemory(
15952         *pVkMemoryRequirements,
15953         false, // requiresDedicatedAllocation
15954         false, // prefersDedicatedAllocation
15955         VK_NULL_HANDLE, // dedicatedBuffer
15956         VK_NULL_HANDLE, // dedicatedImage
15957         *pCreateInfo,
15958         VMA_SUBALLOCATION_TYPE_UNKNOWN,
15959         1, // allocationCount
15960         pAllocation);
15961 
15962 #if VMA_RECORDING_ENABLED
15963     if(allocator->GetRecorder() != VMA_NULL)
15964     {
15965         allocator->GetRecorder()->RecordAllocateMemory(
15966             allocator->GetCurrentFrameIndex(),
15967             *pVkMemoryRequirements,
15968             *pCreateInfo,
15969             *pAllocation);
15970     }
15971 #endif
15972 
15973     if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
15974     {
15975         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
15976     }
15977 
15978 	return result;
15979 }
15980 
vmaAllocateMemoryPages(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,size_t allocationCount,VmaAllocation * pAllocations,VmaAllocationInfo * pAllocationInfo)15981 VkResult vmaAllocateMemoryPages(
15982     VmaAllocator allocator,
15983     const VkMemoryRequirements* pVkMemoryRequirements,
15984     const VmaAllocationCreateInfo* pCreateInfo,
15985     size_t allocationCount,
15986     VmaAllocation* pAllocations,
15987     VmaAllocationInfo* pAllocationInfo)
15988 {
15989     if(allocationCount == 0)
15990     {
15991         return VK_SUCCESS;
15992     }
15993 
15994     VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
15995 
15996     VMA_DEBUG_LOG("vmaAllocateMemoryPages");
15997 
15998     VMA_DEBUG_GLOBAL_MUTEX_LOCK
15999 
16000 	VkResult result = allocator->AllocateMemory(
16001         *pVkMemoryRequirements,
16002         false, // requiresDedicatedAllocation
16003         false, // prefersDedicatedAllocation
16004         VK_NULL_HANDLE, // dedicatedBuffer
16005         VK_NULL_HANDLE, // dedicatedImage
16006         *pCreateInfo,
16007         VMA_SUBALLOCATION_TYPE_UNKNOWN,
16008         allocationCount,
16009         pAllocations);
16010 
16011 #if VMA_RECORDING_ENABLED
16012     if(allocator->GetRecorder() != VMA_NULL)
16013     {
16014         allocator->GetRecorder()->RecordAllocateMemoryPages(
16015             allocator->GetCurrentFrameIndex(),
16016             *pVkMemoryRequirements,
16017             *pCreateInfo,
16018             (uint64_t)allocationCount,
16019             pAllocations);
16020     }
16021 #endif
16022 
16023     if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
16024     {
16025         for(size_t i = 0; i < allocationCount; ++i)
16026         {
16027             allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
16028         }
16029     }
16030 
16031 	return result;
16032 }
16033 
vmaAllocateMemoryForBuffer(VmaAllocator allocator,VkBuffer buffer,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)16034 VkResult vmaAllocateMemoryForBuffer(
16035     VmaAllocator allocator,
16036     VkBuffer buffer,
16037     const VmaAllocationCreateInfo* pCreateInfo,
16038     VmaAllocation* pAllocation,
16039     VmaAllocationInfo* pAllocationInfo)
16040 {
16041     VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
16042 
16043     VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
16044 
16045     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16046 
16047     VkMemoryRequirements vkMemReq = {};
16048     bool requiresDedicatedAllocation = false;
16049     bool prefersDedicatedAllocation = false;
16050     allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
16051         requiresDedicatedAllocation,
16052         prefersDedicatedAllocation);
16053 
16054     VkResult result = allocator->AllocateMemory(
16055         vkMemReq,
16056         requiresDedicatedAllocation,
16057         prefersDedicatedAllocation,
16058         buffer, // dedicatedBuffer
16059         VK_NULL_HANDLE, // dedicatedImage
16060         *pCreateInfo,
16061         VMA_SUBALLOCATION_TYPE_BUFFER,
16062         1, // allocationCount
16063         pAllocation);
16064 
16065 #if VMA_RECORDING_ENABLED
16066     if(allocator->GetRecorder() != VMA_NULL)
16067     {
16068         allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
16069             allocator->GetCurrentFrameIndex(),
16070             vkMemReq,
16071             requiresDedicatedAllocation,
16072             prefersDedicatedAllocation,
16073             *pCreateInfo,
16074             *pAllocation);
16075     }
16076 #endif
16077 
16078     if(pAllocationInfo && result == VK_SUCCESS)
16079     {
16080         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16081     }
16082 
16083 	return result;
16084 }
16085 
vmaAllocateMemoryForImage(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)16086 VkResult vmaAllocateMemoryForImage(
16087     VmaAllocator allocator,
16088     VkImage image,
16089     const VmaAllocationCreateInfo* pCreateInfo,
16090     VmaAllocation* pAllocation,
16091     VmaAllocationInfo* pAllocationInfo)
16092 {
16093     VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
16094 
16095     VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
16096 
16097     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16098 
16099     VkMemoryRequirements vkMemReq = {};
16100     bool requiresDedicatedAllocation = false;
16101     bool prefersDedicatedAllocation  = false;
16102     allocator->GetImageMemoryRequirements(image, vkMemReq,
16103         requiresDedicatedAllocation, prefersDedicatedAllocation);
16104 
16105     VkResult result = allocator->AllocateMemory(
16106         vkMemReq,
16107         requiresDedicatedAllocation,
16108         prefersDedicatedAllocation,
16109         VK_NULL_HANDLE, // dedicatedBuffer
16110         image, // dedicatedImage
16111         *pCreateInfo,
16112         VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
16113         1, // allocationCount
16114         pAllocation);
16115 
16116 #if VMA_RECORDING_ENABLED
16117     if(allocator->GetRecorder() != VMA_NULL)
16118     {
16119         allocator->GetRecorder()->RecordAllocateMemoryForImage(
16120             allocator->GetCurrentFrameIndex(),
16121             vkMemReq,
16122             requiresDedicatedAllocation,
16123             prefersDedicatedAllocation,
16124             *pCreateInfo,
16125             *pAllocation);
16126     }
16127 #endif
16128 
16129     if(pAllocationInfo && result == VK_SUCCESS)
16130     {
16131         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16132     }
16133 
16134 	return result;
16135 }
16136 
vmaFreeMemory(VmaAllocator allocator,VmaAllocation allocation)16137 void vmaFreeMemory(
16138     VmaAllocator allocator,
16139     VmaAllocation allocation)
16140 {
16141     VMA_ASSERT(allocator);
16142 
16143     if(allocation == VK_NULL_HANDLE)
16144     {
16145         return;
16146     }
16147 
16148     VMA_DEBUG_LOG("vmaFreeMemory");
16149 
16150     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16151 
16152 #if VMA_RECORDING_ENABLED
16153     if(allocator->GetRecorder() != VMA_NULL)
16154     {
16155         allocator->GetRecorder()->RecordFreeMemory(
16156             allocator->GetCurrentFrameIndex(),
16157             allocation);
16158     }
16159 #endif
16160 
16161     allocator->FreeMemory(
16162         1, // allocationCount
16163         &allocation);
16164 }
16165 
vmaFreeMemoryPages(VmaAllocator allocator,size_t allocationCount,VmaAllocation * pAllocations)16166 void vmaFreeMemoryPages(
16167     VmaAllocator allocator,
16168     size_t allocationCount,
16169     VmaAllocation* pAllocations)
16170 {
16171     if(allocationCount == 0)
16172     {
16173         return;
16174     }
16175 
16176     VMA_ASSERT(allocator);
16177 
16178     VMA_DEBUG_LOG("vmaFreeMemoryPages");
16179 
16180     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16181 
16182 #if VMA_RECORDING_ENABLED
16183     if(allocator->GetRecorder() != VMA_NULL)
16184     {
16185         allocator->GetRecorder()->RecordFreeMemoryPages(
16186             allocator->GetCurrentFrameIndex(),
16187             (uint64_t)allocationCount,
16188             pAllocations);
16189     }
16190 #endif
16191 
16192     allocator->FreeMemory(allocationCount, pAllocations);
16193 }
16194 
vmaResizeAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize newSize)16195 VkResult vmaResizeAllocation(
16196     VmaAllocator allocator,
16197     VmaAllocation allocation,
16198     VkDeviceSize newSize)
16199 {
16200     VMA_ASSERT(allocator && allocation);
16201 
16202     VMA_DEBUG_LOG("vmaResizeAllocation");
16203 
16204     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16205 
16206 #if VMA_RECORDING_ENABLED
16207     if(allocator->GetRecorder() != VMA_NULL)
16208     {
16209         allocator->GetRecorder()->RecordResizeAllocation(
16210             allocator->GetCurrentFrameIndex(),
16211             allocation,
16212             newSize);
16213     }
16214 #endif
16215 
16216     return allocator->ResizeAllocation(allocation, newSize);
16217 }
16218 
vmaGetAllocationInfo(VmaAllocator allocator,VmaAllocation allocation,VmaAllocationInfo * pAllocationInfo)16219 void vmaGetAllocationInfo(
16220     VmaAllocator allocator,
16221     VmaAllocation allocation,
16222     VmaAllocationInfo* pAllocationInfo)
16223 {
16224     VMA_ASSERT(allocator && allocation && pAllocationInfo);
16225 
16226     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16227 
16228 #if VMA_RECORDING_ENABLED
16229     if(allocator->GetRecorder() != VMA_NULL)
16230     {
16231         allocator->GetRecorder()->RecordGetAllocationInfo(
16232             allocator->GetCurrentFrameIndex(),
16233             allocation);
16234     }
16235 #endif
16236 
16237     allocator->GetAllocationInfo(allocation, pAllocationInfo);
16238 }
16239 
vmaTouchAllocation(VmaAllocator allocator,VmaAllocation allocation)16240 VkBool32 vmaTouchAllocation(
16241     VmaAllocator allocator,
16242     VmaAllocation allocation)
16243 {
16244     VMA_ASSERT(allocator && allocation);
16245 
16246     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16247 
16248 #if VMA_RECORDING_ENABLED
16249     if(allocator->GetRecorder() != VMA_NULL)
16250     {
16251         allocator->GetRecorder()->RecordTouchAllocation(
16252             allocator->GetCurrentFrameIndex(),
16253             allocation);
16254     }
16255 #endif
16256 
16257     return allocator->TouchAllocation(allocation);
16258 }
16259 
vmaSetAllocationUserData(VmaAllocator allocator,VmaAllocation allocation,void * pUserData)16260 void vmaSetAllocationUserData(
16261     VmaAllocator allocator,
16262     VmaAllocation allocation,
16263     void* pUserData)
16264 {
16265     VMA_ASSERT(allocator && allocation);
16266 
16267     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16268 
16269     allocation->SetUserData(allocator, pUserData);
16270 
16271 #if VMA_RECORDING_ENABLED
16272     if(allocator->GetRecorder() != VMA_NULL)
16273     {
16274         allocator->GetRecorder()->RecordSetAllocationUserData(
16275             allocator->GetCurrentFrameIndex(),
16276             allocation,
16277             pUserData);
16278     }
16279 #endif
16280 }
16281 
vmaCreateLostAllocation(VmaAllocator allocator,VmaAllocation * pAllocation)16282 void vmaCreateLostAllocation(
16283     VmaAllocator allocator,
16284     VmaAllocation* pAllocation)
16285 {
16286     VMA_ASSERT(allocator && pAllocation);
16287 
16288     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
16289 
16290     allocator->CreateLostAllocation(pAllocation);
16291 
16292 #if VMA_RECORDING_ENABLED
16293     if(allocator->GetRecorder() != VMA_NULL)
16294     {
16295         allocator->GetRecorder()->RecordCreateLostAllocation(
16296             allocator->GetCurrentFrameIndex(),
16297             *pAllocation);
16298     }
16299 #endif
16300 }
16301 
vmaMapMemory(VmaAllocator allocator,VmaAllocation allocation,void ** ppData)16302 VkResult vmaMapMemory(
16303     VmaAllocator allocator,
16304     VmaAllocation allocation,
16305     void** ppData)
16306 {
16307     VMA_ASSERT(allocator && allocation && ppData);
16308 
16309     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16310 
16311     VkResult res = allocator->Map(allocation, ppData);
16312 
16313 #if VMA_RECORDING_ENABLED
16314     if(allocator->GetRecorder() != VMA_NULL)
16315     {
16316         allocator->GetRecorder()->RecordMapMemory(
16317             allocator->GetCurrentFrameIndex(),
16318             allocation);
16319     }
16320 #endif
16321 
16322     return res;
16323 }
16324 
vmaUnmapMemory(VmaAllocator allocator,VmaAllocation allocation)16325 void vmaUnmapMemory(
16326     VmaAllocator allocator,
16327     VmaAllocation allocation)
16328 {
16329     VMA_ASSERT(allocator && allocation);
16330 
16331     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16332 
16333 #if VMA_RECORDING_ENABLED
16334     if(allocator->GetRecorder() != VMA_NULL)
16335     {
16336         allocator->GetRecorder()->RecordUnmapMemory(
16337             allocator->GetCurrentFrameIndex(),
16338             allocation);
16339     }
16340 #endif
16341 
16342     allocator->Unmap(allocation);
16343 }
16344 
vmaFlushAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)16345 void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
16346 {
16347     VMA_ASSERT(allocator && allocation);
16348 
16349     VMA_DEBUG_LOG("vmaFlushAllocation");
16350 
16351     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16352 
16353     allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
16354 
16355 #if VMA_RECORDING_ENABLED
16356     if(allocator->GetRecorder() != VMA_NULL)
16357     {
16358         allocator->GetRecorder()->RecordFlushAllocation(
16359             allocator->GetCurrentFrameIndex(),
16360             allocation, offset, size);
16361     }
16362 #endif
16363 }
16364 
vmaInvalidateAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)16365 void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
16366 {
16367     VMA_ASSERT(allocator && allocation);
16368 
16369     VMA_DEBUG_LOG("vmaInvalidateAllocation");
16370 
16371     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16372 
16373     allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
16374 
16375 #if VMA_RECORDING_ENABLED
16376     if(allocator->GetRecorder() != VMA_NULL)
16377     {
16378         allocator->GetRecorder()->RecordInvalidateAllocation(
16379             allocator->GetCurrentFrameIndex(),
16380             allocation, offset, size);
16381     }
16382 #endif
16383 }
16384 
vmaCheckCorruption(VmaAllocator allocator,uint32_t memoryTypeBits)16385 VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
16386 {
16387     VMA_ASSERT(allocator);
16388 
16389     VMA_DEBUG_LOG("vmaCheckCorruption");
16390 
16391     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16392 
16393     return allocator->CheckCorruption(memoryTypeBits);
16394 }
16395 
vmaDefragment(VmaAllocator allocator,VmaAllocation * pAllocations,size_t allocationCount,VkBool32 * pAllocationsChanged,const VmaDefragmentationInfo * pDefragmentationInfo,VmaDefragmentationStats * pDefragmentationStats)16396 VkResult vmaDefragment(
16397     VmaAllocator allocator,
16398     VmaAllocation* pAllocations,
16399     size_t allocationCount,
16400     VkBool32* pAllocationsChanged,
16401     const VmaDefragmentationInfo *pDefragmentationInfo,
16402     VmaDefragmentationStats* pDefragmentationStats)
16403 {
16404     // Deprecated interface, reimplemented using new one.
16405 
16406     VmaDefragmentationInfo2 info2 = {};
16407     info2.allocationCount = (uint32_t)allocationCount;
16408     info2.pAllocations = pAllocations;
16409     info2.pAllocationsChanged = pAllocationsChanged;
16410     if(pDefragmentationInfo != VMA_NULL)
16411     {
16412         info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
16413         info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
16414     }
16415     else
16416     {
16417         info2.maxCpuAllocationsToMove = UINT32_MAX;
16418         info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
16419     }
16420     // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
16421 
16422     VmaDefragmentationContext ctx;
16423     VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
16424     if(res == VK_NOT_READY)
16425     {
16426         res = vmaDefragmentationEnd( allocator, ctx);
16427     }
16428     return res;
16429 }
16430 
vmaDefragmentationBegin(VmaAllocator allocator,const VmaDefragmentationInfo2 * pInfo,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)16431 VkResult vmaDefragmentationBegin(
16432     VmaAllocator allocator,
16433     const VmaDefragmentationInfo2* pInfo,
16434     VmaDefragmentationStats* pStats,
16435     VmaDefragmentationContext *pContext)
16436 {
16437     VMA_ASSERT(allocator && pInfo && pContext);
16438 
16439     // Degenerate case: Nothing to defragment.
16440     if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
16441     {
16442         return VK_SUCCESS;
16443     }
16444 
16445     VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
16446     VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
16447     VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
16448     VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
16449 
16450     VMA_DEBUG_LOG("vmaDefragmentationBegin");
16451 
16452     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16453 
16454     VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
16455 
16456 #if VMA_RECORDING_ENABLED
16457     if(allocator->GetRecorder() != VMA_NULL)
16458     {
16459         allocator->GetRecorder()->RecordDefragmentationBegin(
16460             allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
16461     }
16462 #endif
16463 
16464     return res;
16465 }
16466 
vmaDefragmentationEnd(VmaAllocator allocator,VmaDefragmentationContext context)16467 VkResult vmaDefragmentationEnd(
16468     VmaAllocator allocator,
16469     VmaDefragmentationContext context)
16470 {
16471     VMA_ASSERT(allocator);
16472 
16473     VMA_DEBUG_LOG("vmaDefragmentationEnd");
16474 
16475     if(context != VK_NULL_HANDLE)
16476     {
16477         VMA_DEBUG_GLOBAL_MUTEX_LOCK
16478 
16479 #if VMA_RECORDING_ENABLED
16480         if(allocator->GetRecorder() != VMA_NULL)
16481         {
16482             allocator->GetRecorder()->RecordDefragmentationEnd(
16483                 allocator->GetCurrentFrameIndex(), context);
16484         }
16485 #endif
16486 
16487         return allocator->DefragmentationEnd(context);
16488     }
16489     else
16490     {
16491         return VK_SUCCESS;
16492     }
16493 }
16494 
vmaBindBufferMemory(VmaAllocator allocator,VmaAllocation allocation,VkBuffer buffer)16495 VkResult vmaBindBufferMemory(
16496     VmaAllocator allocator,
16497     VmaAllocation allocation,
16498     VkBuffer buffer)
16499 {
16500     VMA_ASSERT(allocator && allocation && buffer);
16501 
16502     VMA_DEBUG_LOG("vmaBindBufferMemory");
16503 
16504     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16505 
16506     return allocator->BindBufferMemory(allocation, buffer);
16507 }
16508 
vmaBindImageMemory(VmaAllocator allocator,VmaAllocation allocation,VkImage image)16509 VkResult vmaBindImageMemory(
16510     VmaAllocator allocator,
16511     VmaAllocation allocation,
16512     VkImage image)
16513 {
16514     VMA_ASSERT(allocator && allocation && image);
16515 
16516     VMA_DEBUG_LOG("vmaBindImageMemory");
16517 
16518     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16519 
16520     return allocator->BindImageMemory(allocation, image);
16521 }
16522 
vmaCreateBuffer(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkBuffer * pBuffer,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)16523 VkResult vmaCreateBuffer(
16524     VmaAllocator allocator,
16525     const VkBufferCreateInfo* pBufferCreateInfo,
16526     const VmaAllocationCreateInfo* pAllocationCreateInfo,
16527     VkBuffer* pBuffer,
16528     VmaAllocation* pAllocation,
16529     VmaAllocationInfo* pAllocationInfo)
16530 {
16531     VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
16532 
16533     if(pBufferCreateInfo->size == 0)
16534     {
16535         return VK_ERROR_VALIDATION_FAILED_EXT;
16536     }
16537 
16538     VMA_DEBUG_LOG("vmaCreateBuffer");
16539 
16540     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16541 
16542     *pBuffer = VK_NULL_HANDLE;
16543     *pAllocation = VK_NULL_HANDLE;
16544 
16545     // 1. Create VkBuffer.
16546     VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
16547         allocator->m_hDevice,
16548         pBufferCreateInfo,
16549         allocator->GetAllocationCallbacks(),
16550         pBuffer);
16551     if(res >= 0)
16552     {
16553         // 2. vkGetBufferMemoryRequirements.
16554         VkMemoryRequirements vkMemReq = {};
16555         bool requiresDedicatedAllocation = false;
16556         bool prefersDedicatedAllocation  = false;
16557         allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
16558             requiresDedicatedAllocation, prefersDedicatedAllocation);
16559 
16560         // Make sure alignment requirements for specific buffer usages reported
16561         // in Physical Device Properties are included in alignment reported by memory requirements.
16562         if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT) != 0)
16563         {
16564            VMA_ASSERT(vkMemReq.alignment %
16565               allocator->m_PhysicalDeviceProperties.limits.minTexelBufferOffsetAlignment == 0);
16566         }
16567         if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) != 0)
16568         {
16569            VMA_ASSERT(vkMemReq.alignment %
16570               allocator->m_PhysicalDeviceProperties.limits.minUniformBufferOffsetAlignment == 0);
16571         }
16572         if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) != 0)
16573         {
16574            VMA_ASSERT(vkMemReq.alignment %
16575               allocator->m_PhysicalDeviceProperties.limits.minStorageBufferOffsetAlignment == 0);
16576         }
16577 
16578         // 3. Allocate memory using allocator.
16579         res = allocator->AllocateMemory(
16580             vkMemReq,
16581             requiresDedicatedAllocation,
16582             prefersDedicatedAllocation,
16583             *pBuffer, // dedicatedBuffer
16584             VK_NULL_HANDLE, // dedicatedImage
16585             *pAllocationCreateInfo,
16586             VMA_SUBALLOCATION_TYPE_BUFFER,
16587             1, // allocationCount
16588             pAllocation);
16589 
16590 #if VMA_RECORDING_ENABLED
16591         if(allocator->GetRecorder() != VMA_NULL)
16592         {
16593             allocator->GetRecorder()->RecordCreateBuffer(
16594                 allocator->GetCurrentFrameIndex(),
16595                 *pBufferCreateInfo,
16596                 *pAllocationCreateInfo,
16597                 *pAllocation);
16598         }
16599 #endif
16600 
16601         if(res >= 0)
16602         {
16603             // 3. Bind buffer with memory.
16604             res = allocator->BindBufferMemory(*pAllocation, *pBuffer);
16605             if(res >= 0)
16606             {
16607                 // All steps succeeded.
16608                 #if VMA_STATS_STRING_ENABLED
16609                     (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
16610                 #endif
16611                 if(pAllocationInfo != VMA_NULL)
16612                 {
16613                     allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16614                 }
16615 
16616                 return VK_SUCCESS;
16617             }
16618             allocator->FreeMemory(
16619                 1, // allocationCount
16620                 pAllocation);
16621             *pAllocation = VK_NULL_HANDLE;
16622             (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
16623             *pBuffer = VK_NULL_HANDLE;
16624             return res;
16625         }
16626         (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
16627         *pBuffer = VK_NULL_HANDLE;
16628         return res;
16629     }
16630     return res;
16631 }
16632 
vmaDestroyBuffer(VmaAllocator allocator,VkBuffer buffer,VmaAllocation allocation)16633 void vmaDestroyBuffer(
16634     VmaAllocator allocator,
16635     VkBuffer buffer,
16636     VmaAllocation allocation)
16637 {
16638     VMA_ASSERT(allocator);
16639 
16640     if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
16641     {
16642         return;
16643     }
16644 
16645     VMA_DEBUG_LOG("vmaDestroyBuffer");
16646 
16647     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16648 
16649 #if VMA_RECORDING_ENABLED
16650     if(allocator->GetRecorder() != VMA_NULL)
16651     {
16652         allocator->GetRecorder()->RecordDestroyBuffer(
16653             allocator->GetCurrentFrameIndex(),
16654             allocation);
16655     }
16656 #endif
16657 
16658     if(buffer != VK_NULL_HANDLE)
16659     {
16660         (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
16661     }
16662 
16663     if(allocation != VK_NULL_HANDLE)
16664     {
16665         allocator->FreeMemory(
16666             1, // allocationCount
16667             &allocation);
16668     }
16669 }
16670 
vmaCreateImage(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkImage * pImage,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)16671 VkResult vmaCreateImage(
16672     VmaAllocator allocator,
16673     const VkImageCreateInfo* pImageCreateInfo,
16674     const VmaAllocationCreateInfo* pAllocationCreateInfo,
16675     VkImage* pImage,
16676     VmaAllocation* pAllocation,
16677     VmaAllocationInfo* pAllocationInfo)
16678 {
16679     VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
16680 
16681     if(pImageCreateInfo->extent.width == 0 ||
16682         pImageCreateInfo->extent.height == 0 ||
16683         pImageCreateInfo->extent.depth == 0 ||
16684         pImageCreateInfo->mipLevels == 0 ||
16685         pImageCreateInfo->arrayLayers == 0)
16686     {
16687         return VK_ERROR_VALIDATION_FAILED_EXT;
16688     }
16689 
16690     VMA_DEBUG_LOG("vmaCreateImage");
16691 
16692     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16693 
16694     *pImage = VK_NULL_HANDLE;
16695     *pAllocation = VK_NULL_HANDLE;
16696 
16697     // 1. Create VkImage.
16698     VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
16699         allocator->m_hDevice,
16700         pImageCreateInfo,
16701         allocator->GetAllocationCallbacks(),
16702         pImage);
16703     if(res >= 0)
16704     {
16705         VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
16706             VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
16707             VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
16708 
16709         // 2. Allocate memory using allocator.
16710         VkMemoryRequirements vkMemReq = {};
16711         bool requiresDedicatedAllocation = false;
16712         bool prefersDedicatedAllocation  = false;
16713         allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
16714             requiresDedicatedAllocation, prefersDedicatedAllocation);
16715 
16716         res = allocator->AllocateMemory(
16717             vkMemReq,
16718             requiresDedicatedAllocation,
16719             prefersDedicatedAllocation,
16720             VK_NULL_HANDLE, // dedicatedBuffer
16721             *pImage, // dedicatedImage
16722             *pAllocationCreateInfo,
16723             suballocType,
16724             1, // allocationCount
16725             pAllocation);
16726 
16727 #if VMA_RECORDING_ENABLED
16728         if(allocator->GetRecorder() != VMA_NULL)
16729         {
16730             allocator->GetRecorder()->RecordCreateImage(
16731                 allocator->GetCurrentFrameIndex(),
16732                 *pImageCreateInfo,
16733                 *pAllocationCreateInfo,
16734                 *pAllocation);
16735         }
16736 #endif
16737 
16738         if(res >= 0)
16739         {
16740             // 3. Bind image with memory.
16741             res = allocator->BindImageMemory(*pAllocation, *pImage);
16742             if(res >= 0)
16743             {
16744                 // All steps succeeded.
16745                 #if VMA_STATS_STRING_ENABLED
16746                     (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
16747                 #endif
16748                 if(pAllocationInfo != VMA_NULL)
16749                 {
16750                     allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16751                 }
16752 
16753                 return VK_SUCCESS;
16754             }
16755             allocator->FreeMemory(
16756                 1, // allocationCount
16757                 pAllocation);
16758             *pAllocation = VK_NULL_HANDLE;
16759             (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
16760             *pImage = VK_NULL_HANDLE;
16761             return res;
16762         }
16763         (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
16764         *pImage = VK_NULL_HANDLE;
16765         return res;
16766     }
16767     return res;
16768 }
16769 
vmaDestroyImage(VmaAllocator allocator,VkImage image,VmaAllocation allocation)16770 void vmaDestroyImage(
16771     VmaAllocator allocator,
16772     VkImage image,
16773     VmaAllocation allocation)
16774 {
16775     VMA_ASSERT(allocator);
16776 
16777     if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
16778     {
16779         return;
16780     }
16781 
16782     VMA_DEBUG_LOG("vmaDestroyImage");
16783 
16784     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16785 
16786 #if VMA_RECORDING_ENABLED
16787     if(allocator->GetRecorder() != VMA_NULL)
16788     {
16789         allocator->GetRecorder()->RecordDestroyImage(
16790             allocator->GetCurrentFrameIndex(),
16791             allocation);
16792     }
16793 #endif
16794 
16795     if(image != VK_NULL_HANDLE)
16796     {
16797         (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
16798     }
16799     if(allocation != VK_NULL_HANDLE)
16800     {
16801         allocator->FreeMemory(
16802             1, // allocationCount
16803             &allocation);
16804     }
16805 }
16806 #if defined(__GNUC__)
16807 #pragma GCC diagnostic pop
16808 #if defined(__clang__)
16809 #pragma clang diagnostic pop
16810 #endif
16811 #endif
16812 #endif // #ifdef VMA_IMPLEMENTATION
16813 // clang-format on
16814