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 #ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H
24 #define AMD_VULKAN_MEMORY_ALLOCATOR_H
25 
26 #ifdef __cplusplus
27 extern "C" {
28 #endif
29 
30 /** \mainpage Vulkan Memory Allocator
31 
32 <b>Version 2.2.0</b> (2018-12-13)
33 
34 Copyright (c) 2017-2018 Advanced Micro Devices, Inc. All rights reserved. \n
35 License: MIT
36 
37 Documentation of all members: vk_mem_alloc.h
38 
39 \section main_table_of_contents Table of contents
40 
41 - <b>User guide</b>
42   - \subpage quick_start
43     - [Project setup](@ref quick_start_project_setup)
44     - [Initialization](@ref quick_start_initialization)
45     - [Resource allocation](@ref quick_start_resource_allocation)
46   - \subpage choosing_memory_type
47     - [Usage](@ref choosing_memory_type_usage)
48     - [Required and preferred flags](@ref choosing_memory_type_required_preferred_flags)
49     - [Explicit memory types](@ref choosing_memory_type_explicit_memory_types)
50     - [Custom memory pools](@ref choosing_memory_type_custom_memory_pools)
51   - \subpage memory_mapping
52     - [Mapping functions](@ref memory_mapping_mapping_functions)
53     - [Persistently mapped memory](@ref memory_mapping_persistently_mapped_memory)
54     - [Cache control](@ref memory_mapping_cache_control)
55     - [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable)
56   - \subpage custom_memory_pools
57     - [Choosing memory type index](@ref custom_memory_pools_MemTypeIndex)
58     - [Linear allocation algorithm](@ref linear_algorithm)
59       - [Free-at-once](@ref linear_algorithm_free_at_once)
60       - [Stack](@ref linear_algorithm_stack)
61       - [Double stack](@ref linear_algorithm_double_stack)
62       - [Ring buffer](@ref linear_algorithm_ring_buffer)
63     - [Buddy allocation algorithm](@ref buddy_algorithm)
64   - \subpage defragmentation
65   	- [Defragmenting CPU memory](@ref defragmentation_cpu)
66   	- [Defragmenting GPU memory](@ref defragmentation_gpu)
67   	- [Additional notes](@ref defragmentation_additional_notes)
68   	- [Writing custom allocation algorithm](@ref defragmentation_custom_algorithm)
69   - \subpage lost_allocations
70   - \subpage statistics
71     - [Numeric statistics](@ref statistics_numeric_statistics)
72     - [JSON dump](@ref statistics_json_dump)
73   - \subpage allocation_annotation
74     - [Allocation user data](@ref allocation_user_data)
75     - [Allocation names](@ref allocation_names)
76   - \subpage debugging_memory_usage
77     - [Memory initialization](@ref debugging_memory_usage_initialization)
78     - [Margins](@ref debugging_memory_usage_margins)
79     - [Corruption detection](@ref debugging_memory_usage_corruption_detection)
80   - \subpage record_and_replay
81 - \subpage usage_patterns
82   - [Simple patterns](@ref usage_patterns_simple)
83   - [Advanced patterns](@ref usage_patterns_advanced)
84 - \subpage configuration
85   - [Pointers to Vulkan functions](@ref config_Vulkan_functions)
86   - [Custom host memory allocator](@ref custom_memory_allocator)
87   - [Device memory allocation callbacks](@ref allocation_callbacks)
88   - [Device heap memory limit](@ref heap_memory_limit)
89   - \subpage vk_khr_dedicated_allocation
90 - \subpage general_considerations
91   - [Thread safety](@ref general_considerations_thread_safety)
92   - [Validation layer warnings](@ref general_considerations_validation_layer_warnings)
93   - [Allocation algorithm](@ref general_considerations_allocation_algorithm)
94   - [Features not supported](@ref general_considerations_features_not_supported)
95 
96 \section main_see_also See also
97 
98 - [Product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-memory-allocator/)
99 - [Source repository on GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)
100 
101 
102 
103 
104 \page quick_start Quick start
105 
106 \section quick_start_project_setup Project setup
107 
108 Vulkan Memory Allocator comes in form of a single header file.
109 You don't need to build it as a separate library project.
110 You can add this file directly to your project and submit it to code repository next to your other source files.
111 
112 "Single header" doesn't mean that everything is contained in C/C++ declarations,
113 like it tends to be in case of inline functions or C++ templates.
114 It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro.
115 If you don't do it properly, you will get linker errors.
116 
117 To do it properly:
118 
119 -# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library.
120    This includes declarations of all members of the library.
121 -# In exacly one CPP file define following macro before this include.
122    It enables also internal definitions.
123 
124 \code
125 #define VMA_IMPLEMENTATION
126 #include "vk_mem_alloc.h"
127 \endcode
128 
129 It may be a good idea to create dedicated CPP file just for this purpose.
130 
131 Note on language: This library is written in C++, but has C-compatible interface.
132 Thus you can include and use vk_mem_alloc.h in C or C++ code, but full
133 implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C.
134 
135 Please note that this library includes header `<vulkan/vulkan.h>`, which in turn
136 includes `<windows.h>` on Windows. If you need some specific macros defined
137 before including these headers (like `WIN32_LEAN_AND_MEAN` or
138 `WINVER` for Windows, `VK_USE_PLATFORM_WIN32_KHR` for Vulkan), you must define
139 them before every `#include` of this library.
140 
141 
142 \section quick_start_initialization Initialization
143 
144 At program startup:
145 
146 -# Initialize Vulkan to have `VkPhysicalDevice` and `VkDevice` object.
147 -# Fill VmaAllocatorCreateInfo structure and create #VmaAllocator object by
148    calling vmaCreateAllocator().
149 
150 \code
151 VmaAllocatorCreateInfo allocatorInfo = {};
152 allocatorInfo.physicalDevice = physicalDevice;
153 allocatorInfo.device = device;
154 
155 VmaAllocator allocator;
156 vmaCreateAllocator(&allocatorInfo, &allocator);
157 \endcode
158 
159 \section quick_start_resource_allocation Resource allocation
160 
161 When you want to create a buffer or image:
162 
163 -# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure.
164 -# Fill VmaAllocationCreateInfo structure.
165 -# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory
166    already allocated and bound to it.
167 
168 \code
169 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
170 bufferInfo.size = 65536;
171 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
172 
173 VmaAllocationCreateInfo allocInfo = {};
174 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
175 
176 VkBuffer buffer;
177 VmaAllocation allocation;
178 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
179 \endcode
180 
181 Don't forget to destroy your objects when no longer needed:
182 
183 \code
184 vmaDestroyBuffer(allocator, buffer, allocation);
185 vmaDestroyAllocator(allocator);
186 \endcode
187 
188 
189 \page choosing_memory_type Choosing memory type
190 
191 Physical devices in Vulkan support various combinations of memory heaps and
192 types. Help with choosing correct and optimal memory type for your specific
193 resource is one of the key features of this library. You can use it by filling
194 appropriate members of VmaAllocationCreateInfo structure, as described below.
195 You can also combine multiple methods.
196 
197 -# If you just want to find memory type index that meets your requirements, you
198    can use function vmaFindMemoryTypeIndex().
199 -# If you want to allocate a region of device memory without association with any
200    specific image or buffer, you can use function vmaAllocateMemory(). Usage of
201    this function is not recommended and usually not needed.
202 -# If you already have a buffer or an image created, you want to allocate memory
203    for it and then you will bind it yourself, you can use function
204    vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage().
205    For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory().
206 -# If you want to create a buffer or an image, allocate memory for it and bind
207    them together, all in one call, you can use function vmaCreateBuffer(),
208    vmaCreateImage(). This is the recommended way to use this library.
209 
210 When using 3. or 4., the library internally queries Vulkan for memory types
211 supported for that buffer or image (function `vkGetBufferMemoryRequirements()`)
212 and uses only one of these types.
213 
214 If no memory type can be found that meets all the requirements, these functions
215 return `VK_ERROR_FEATURE_NOT_PRESENT`.
216 
217 You can leave VmaAllocationCreateInfo structure completely filled with zeros.
218 It means no requirements are specified for memory type.
219 It is valid, although not very useful.
220 
221 \section choosing_memory_type_usage Usage
222 
223 The easiest way to specify memory requirements is to fill member
224 VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage.
225 It defines high level, common usage types.
226 For more details, see description of this enum.
227 
228 For example, if you want to create a uniform buffer that will be filled using
229 transfer only once or infrequently and used for rendering every frame, you can
230 do it using following code:
231 
232 \code
233 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
234 bufferInfo.size = 65536;
235 bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
236 
237 VmaAllocationCreateInfo allocInfo = {};
238 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
239 
240 VkBuffer buffer;
241 VmaAllocation allocation;
242 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
243 \endcode
244 
245 \section choosing_memory_type_required_preferred_flags Required and preferred flags
246 
247 You can specify more detailed requirements by filling members
248 VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags
249 with a combination of bits from enum `VkMemoryPropertyFlags`. For example,
250 if you want to create a buffer that will be persistently mapped on host (so it
251 must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`,
252 use following code:
253 
254 \code
255 VmaAllocationCreateInfo allocInfo = {};
256 allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
257 allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
258 allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
259 
260 VkBuffer buffer;
261 VmaAllocation allocation;
262 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
263 \endcode
264 
265 A memory type is chosen that has all the required flags and as many preferred
266 flags set as possible.
267 
268 If you use VmaAllocationCreateInfo::usage, it is just internally converted to
269 a set of required and preferred flags.
270 
271 \section choosing_memory_type_explicit_memory_types Explicit memory types
272 
273 If you inspected memory types available on the physical device and you have
274 a preference for memory types that you want to use, you can fill member
275 VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set
276 means that a memory type with that index is allowed to be used for the
277 allocation. Special value 0, just like `UINT32_MAX`, means there are no
278 restrictions to memory type index.
279 
280 Please note that this member is NOT just a memory type index.
281 Still you can use it to choose just one, specific memory type.
282 For example, if you already determined that your buffer should be created in
283 memory type 2, use following code:
284 
285 \code
286 uint32_t memoryTypeIndex = 2;
287 
288 VmaAllocationCreateInfo allocInfo = {};
289 allocInfo.memoryTypeBits = 1u << memoryTypeIndex;
290 
291 VkBuffer buffer;
292 VmaAllocation allocation;
293 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
294 \endcode
295 
296 \section choosing_memory_type_custom_memory_pools Custom memory pools
297 
298 If you allocate from custom memory pool, all the ways of specifying memory
299 requirements described above are not applicable and the aforementioned members
300 of VmaAllocationCreateInfo structure are ignored. Memory type is selected
301 explicitly when creating the pool and then used to make all the allocations from
302 that pool. For further details, see \ref custom_memory_pools.
303 
304 
305 \page memory_mapping Memory mapping
306 
307 To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`,
308 to be able to read from it or write to it in CPU code.
309 Mapping is possible only of memory allocated from a memory type that has
310 `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag.
311 Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose.
312 You can use them directly with memory allocated by this library,
313 but it is not recommended because of following issue:
314 Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed.
315 This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan.
316 Because of this, Vulkan Memory Allocator provides following facilities:
317 
318 \section memory_mapping_mapping_functions Mapping functions
319 
320 The library provides following functions for mapping of a specific #VmaAllocation: vmaMapMemory(), vmaUnmapMemory().
321 They are safer and more convenient to use than standard Vulkan functions.
322 You can map an allocation multiple times simultaneously - mapping is reference-counted internally.
323 You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block.
324 The way it's implemented is that the library always maps entire memory block, not just region of the allocation.
325 For further details, see description of vmaMapMemory() function.
326 Example:
327 
328 \code
329 // Having these objects initialized:
330 
331 struct ConstantBuffer
332 {
333     ...
334 };
335 ConstantBuffer constantBufferData;
336 
337 VmaAllocator allocator;
338 VkBuffer constantBuffer;
339 VmaAllocation constantBufferAllocation;
340 
341 // You can map and fill your buffer using following code:
342 
343 void* mappedData;
344 vmaMapMemory(allocator, constantBufferAllocation, &mappedData);
345 memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
346 vmaUnmapMemory(allocator, constantBufferAllocation);
347 \endcode
348 
349 When mapping, you may see a warning from Vulkan validation layer similar to this one:
350 
351 <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>
352 
353 It happens because the library maps entire `VkDeviceMemory` block, where different
354 types of images and buffers may end up together, especially on GPUs with unified memory like Intel.
355 You can safely ignore it if you are sure you access only memory of the intended
356 object that you wanted to map.
357 
358 
359 \section memory_mapping_persistently_mapped_memory Persistently mapped memory
360 
361 Kepping your memory persistently mapped is generally OK in Vulkan.
362 You don't need to unmap it before using its data on the GPU.
363 The library provides a special feature designed for that:
364 Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in
365 VmaAllocationCreateInfo::flags stay mapped all the time,
366 so you can just access CPU pointer to it any time
367 without a need to call any "map" or "unmap" function.
368 Example:
369 
370 \code
371 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
372 bufCreateInfo.size = sizeof(ConstantBuffer);
373 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
374 
375 VmaAllocationCreateInfo allocCreateInfo = {};
376 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
377 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
378 
379 VkBuffer buf;
380 VmaAllocation alloc;
381 VmaAllocationInfo allocInfo;
382 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
383 
384 // Buffer is already mapped. You can access its memory.
385 memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
386 \endcode
387 
388 There are some exceptions though, when you should consider mapping memory only for a short period of time:
389 
390 - When operating system is Windows 7 or 8.x (Windows 10 is not affected because it uses WDDM2),
391   device is discrete AMD GPU,
392   and memory type is the special 256 MiB pool of `DEVICE_LOCAL + HOST_VISIBLE` memory
393   (selected when you use #VMA_MEMORY_USAGE_CPU_TO_GPU),
394   then whenever a memory block allocated from this memory type stays mapped
395   for the time of any call to `vkQueueSubmit()` or `vkQueuePresentKHR()`, this
396   block is migrated by WDDM to system RAM, which degrades performance. It doesn't
397   matter if that particular memory block is actually used by the command buffer
398   being submitted.
399 - On Mac/MoltenVK there is a known bug - [Issue #175](https://github.com/KhronosGroup/MoltenVK/issues/175)
400   which requires unmapping before GPU can see updated texture.
401 - Keeping many large memory blocks mapped may impact performance or stability of some debugging tools.
402 
403 \section memory_mapping_cache_control Cache control
404 
405 Memory in Vulkan doesn't need to be unmapped before using it on GPU,
406 but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set,
407 you need to manually invalidate cache before reading of mapped pointer
408 and flush cache after writing to mapped pointer.
409 Vulkan provides following functions for this purpose `vkFlushMappedMemoryRanges()`,
410 `vkInvalidateMappedMemoryRanges()`, but this library provides more convenient
411 functions that refer to given allocation object: vmaFlushAllocation(),
412 vmaInvalidateAllocation().
413 
414 Regions of memory specified for flush/invalidate must be aligned to
415 `VkPhysicalDeviceLimits::nonCoherentAtomSize`. This is automatically ensured by the library.
416 In any memory type that is `HOST_VISIBLE` but not `HOST_COHERENT`, all allocations
417 within blocks are aligned to this value, so their offsets are always multiply of
418 `nonCoherentAtomSize` and two different allocations never share same "line" of this size.
419 
420 Please note that memory allocated with #VMA_MEMORY_USAGE_CPU_ONLY is guaranteed to be `HOST_COHERENT`.
421 
422 Also, Windows drivers from all 3 PC GPU vendors (AMD, Intel, NVIDIA)
423 currently provide `HOST_COHERENT` flag on all memory types that are
424 `HOST_VISIBLE`, so on this platform you may not need to bother.
425 
426 \section memory_mapping_finding_if_memory_mappable Finding out if memory is mappable
427 
428 It may happen that your allocation ends up in memory that is `HOST_VISIBLE` (available for mapping)
429 despite it wasn't explicitly requested.
430 For example, application may work on integrated graphics with unified memory (like Intel) or
431 allocation from video memory might have failed, so the library chose system memory as fallback.
432 
433 You can detect this case and map such allocation to access its memory on CPU directly,
434 instead of launching a transfer operation.
435 In order to do that: inspect `allocInfo.memoryType`, call vmaGetMemoryTypeProperties(),
436 and look for `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag in properties of that memory type.
437 
438 \code
439 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
440 bufCreateInfo.size = sizeof(ConstantBuffer);
441 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
442 
443 VmaAllocationCreateInfo allocCreateInfo = {};
444 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
445 allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
446 
447 VkBuffer buf;
448 VmaAllocation alloc;
449 VmaAllocationInfo allocInfo;
450 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
451 
452 VkMemoryPropertyFlags memFlags;
453 vmaGetMemoryTypeProperties(allocator, allocInfo.memoryType, &memFlags);
454 if((memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
455 {
456     // Allocation ended up in mappable memory. You can map it and access it directly.
457     void* mappedData;
458     vmaMapMemory(allocator, alloc, &mappedData);
459     memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
460     vmaUnmapMemory(allocator, alloc);
461 }
462 else
463 {
464     // Allocation ended up in non-mappable memory.
465     // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
466 }
467 \endcode
468 
469 You can even use #VMA_ALLOCATION_CREATE_MAPPED_BIT flag while creating allocations
470 that are not necessarily `HOST_VISIBLE` (e.g. using #VMA_MEMORY_USAGE_GPU_ONLY).
471 If the allocation ends up in memory type that is `HOST_VISIBLE`, it will be persistently mapped and you can use it directly.
472 If not, the flag is just ignored.
473 Example:
474 
475 \code
476 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
477 bufCreateInfo.size = sizeof(ConstantBuffer);
478 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
479 
480 VmaAllocationCreateInfo allocCreateInfo = {};
481 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
482 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
483 
484 VkBuffer buf;
485 VmaAllocation alloc;
486 VmaAllocationInfo allocInfo;
487 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
488 
489 if(allocInfo.pUserData != nullptr)
490 {
491     // Allocation ended up in mappable memory.
492     // It's persistently mapped. You can access it directly.
493     memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
494 }
495 else
496 {
497     // Allocation ended up in non-mappable memory.
498     // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
499 }
500 \endcode
501 
502 
503 \page custom_memory_pools Custom memory pools
504 
505 A memory pool contains a number of `VkDeviceMemory` blocks.
506 The library automatically creates and manages default pool for each memory type available on the device.
507 Default memory pool automatically grows in size.
508 Size of allocated blocks is also variable and managed automatically.
509 
510 You can create custom pool and allocate memory out of it.
511 It can be useful if you want to:
512 
513 - Keep certain kind of allocations separate from others.
514 - Enforce particular, fixed size of Vulkan memory blocks.
515 - Limit maximum amount of Vulkan memory allocated for that pool.
516 - Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool.
517 
518 To use custom memory pools:
519 
520 -# Fill VmaPoolCreateInfo structure.
521 -# Call vmaCreatePool() to obtain #VmaPool handle.
522 -# When making an allocation, set VmaAllocationCreateInfo::pool to this handle.
523    You don't need to specify any other parameters of this structure, like `usage`.
524 
525 Example:
526 
527 \code
528 // Create a pool that can have at most 2 blocks, 128 MiB each.
529 VmaPoolCreateInfo poolCreateInfo = {};
530 poolCreateInfo.memoryTypeIndex = ...
531 poolCreateInfo.blockSize = 128ull * 1024 * 1024;
532 poolCreateInfo.maxBlockCount = 2;
533 
534 VmaPool pool;
535 vmaCreatePool(allocator, &poolCreateInfo, &pool);
536 
537 // Allocate a buffer out of it.
538 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
539 bufCreateInfo.size = 1024;
540 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
541 
542 VmaAllocationCreateInfo allocCreateInfo = {};
543 allocCreateInfo.pool = pool;
544 
545 VkBuffer buf;
546 VmaAllocation alloc;
547 VmaAllocationInfo allocInfo;
548 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
549 \endcode
550 
551 You have to free all allocations made from this pool before destroying it.
552 
553 \code
554 vmaDestroyBuffer(allocator, buf, alloc);
555 vmaDestroyPool(allocator, pool);
556 \endcode
557 
558 \section custom_memory_pools_MemTypeIndex Choosing memory type index
559 
560 When creating a pool, you must explicitly specify memory type index.
561 To find the one suitable for your buffers or images, you can use helper functions
562 vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo().
563 You need to provide structures with example parameters of buffers or images
564 that you are going to create in that pool.
565 
566 \code
567 VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
568 exampleBufCreateInfo.size = 1024; // Whatever.
569 exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; // Change if needed.
570 
571 VmaAllocationCreateInfo allocCreateInfo = {};
572 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; // Change if needed.
573 
574 uint32_t memTypeIndex;
575 vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex);
576 
577 VmaPoolCreateInfo poolCreateInfo = {};
578 poolCreateInfo.memoryTypeIndex = memTypeIndex;
579 // ...
580 \endcode
581 
582 When creating buffers/images allocated in that pool, provide following parameters:
583 
584 - `VkBufferCreateInfo`: Prefer to pass same parameters as above.
585   Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior.
586   Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers
587   or the other way around.
588 - VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member.
589   Other members are ignored anyway.
590 
591 \section linear_algorithm Linear allocation algorithm
592 
593 Each Vulkan memory block managed by this library has accompanying metadata that
594 keeps track of used and unused regions. By default, the metadata structure and
595 algorithm tries to find best place for new allocations among free regions to
596 optimize memory usage. This way you can allocate and free objects in any order.
597 
598 ![Default allocation algorithm](../gfx/Linear_allocator_1_algo_default.png)
599 
600 Sometimes there is a need to use simpler, linear allocation algorithm. You can
601 create custom pool that uses such algorithm by adding flag
602 #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
603 #VmaPool object. Then an alternative metadata management is used. It always
604 creates new allocations after last one and doesn't reuse free regions after
605 allocations freed in the middle. It results in better allocation performance and
606 less memory consumed by metadata.
607 
608 ![Linear allocation algorithm](../gfx/Linear_allocator_2_algo_linear.png)
609 
610 With this one flag, you can create a custom pool that can be used in many ways:
611 free-at-once, stack, double stack, and ring buffer. See below for details.
612 
613 \subsection linear_algorithm_free_at_once Free-at-once
614 
615 In a pool that uses linear algorithm, you still need to free all the allocations
616 individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free
617 them in any order. New allocations are always made after last one - free space
618 in the middle is not reused. However, when you release all the allocation and
619 the pool becomes empty, allocation starts from the beginning again. This way you
620 can use linear algorithm to speed up creation of allocations that you are going
621 to release all at once.
622 
623 ![Free-at-once](../gfx/Linear_allocator_3_free_at_once.png)
624 
625 This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
626 value that allows multiple memory blocks.
627 
628 \subsection linear_algorithm_stack Stack
629 
630 When you free an allocation that was created last, its space can be reused.
631 Thanks to this, if you always release allocations in the order opposite to their
632 creation (LIFO - Last In First Out), you can achieve behavior of a stack.
633 
634 ![Stack](../gfx/Linear_allocator_4_stack.png)
635 
636 This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
637 value that allows multiple memory blocks.
638 
639 \subsection linear_algorithm_double_stack Double stack
640 
641 The space reserved by a custom pool with linear algorithm may be used by two
642 stacks:
643 
644 - First, default one, growing up from offset 0.
645 - Second, "upper" one, growing down from the end towards lower offsets.
646 
647 To make allocation from upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT
648 to VmaAllocationCreateInfo::flags.
649 
650 ![Double stack](../gfx/Linear_allocator_7_double_stack.png)
651 
652 Double stack is available only in pools with one memory block -
653 VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
654 
655 When the two stacks' ends meet so there is not enough space between them for a
656 new allocation, such allocation fails with usual
657 `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
658 
659 \subsection linear_algorithm_ring_buffer Ring buffer
660 
661 When you free some allocations from the beginning and there is not enough free space
662 for a new one at the end of a pool, allocator's "cursor" wraps around to the
663 beginning and starts allocation there. Thanks to this, if you always release
664 allocations in the same order as you created them (FIFO - First In First Out),
665 you can achieve behavior of a ring buffer / queue.
666 
667 ![Ring buffer](../gfx/Linear_allocator_5_ring_buffer.png)
668 
669 Pools with linear algorithm support [lost allocations](@ref lost_allocations) when used as ring buffer.
670 If there is not enough free space for a new allocation, but existing allocations
671 from the front of the queue can become lost, they become lost and the allocation
672 succeeds.
673 
674 ![Ring buffer with lost allocations](../gfx/Linear_allocator_6_ring_buffer_lost.png)
675 
676 Ring buffer is available only in pools with one memory block -
677 VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
678 
679 \section buddy_algorithm Buddy allocation algorithm
680 
681 There is another allocation algorithm that can be used with custom pools, called
682 "buddy". Its internal data structure is based on a tree of blocks, each having
683 size that is a power of two and a half of its parent's size. When you want to
684 allocate memory of certain size, a free node in the tree is located. If it's too
685 large, it is recursively split into two halves (called "buddies"). However, if
686 requested allocation size is not a power of two, the size of a tree node is
687 aligned up to the nearest power of two and the remaining space is wasted. When
688 two buddy nodes become free, they are merged back into one larger node.
689 
690 ![Buddy allocator](../gfx/Buddy_allocator.png)
691 
692 The advantage of buddy allocation algorithm over default algorithm is faster
693 allocation and deallocation, as well as smaller external fragmentation. The
694 disadvantage is more wasted space (internal fragmentation).
695 
696 For more information, please read ["Buddy memory allocation" on Wikipedia](https://en.wikipedia.org/wiki/Buddy_memory_allocation)
697 or other sources that describe this concept in general.
698 
699 To use buddy allocation algorithm with a custom pool, add flag
700 #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
701 #VmaPool object.
702 
703 Several limitations apply to pools that use buddy algorithm:
704 
705 - It is recommended to use VmaPoolCreateInfo::blockSize that is a power of two.
706   Otherwise, only largest power of two smaller than the size is used for
707   allocations. The remaining space always stays unused.
708 - [Margins](@ref debugging_memory_usage_margins) and
709   [corruption detection](@ref debugging_memory_usage_corruption_detection)
710   don't work in such pools.
711 - [Lost allocations](@ref lost_allocations) don't work in such pools. You can
712   use them, but they never become lost. Support may be added in the future.
713 - [Defragmentation](@ref defragmentation) doesn't work with allocations made from
714   such pool.
715 
716 \page defragmentation Defragmentation
717 
718 Interleaved allocations and deallocations of many objects of varying size can
719 cause fragmentation over time, which can lead to a situation where the library is unable
720 to find a continuous range of free memory for a new allocation despite there is
721 enough free space, just scattered across many small free ranges between existing
722 allocations.
723 
724 To mitigate this problem, you can use defragmentation feature:
725 structure #VmaDefragmentationInfo2, function vmaDefragmentationBegin(), vmaDefragmentationEnd().
726 Given set of allocations,
727 this function can move them to compact used memory, ensure more continuous free
728 space and possibly also free some `VkDeviceMemory` blocks.
729 
730 What the defragmentation does is:
731 
732 - Updates #VmaAllocation objects to point to new `VkDeviceMemory` and offset.
733   After allocation has been moved, its VmaAllocationInfo::deviceMemory and/or
734   VmaAllocationInfo::offset changes. You must query them again using
735   vmaGetAllocationInfo() if you need them.
736 - Moves actual data in memory.
737 
738 What it doesn't do, so you need to do it yourself:
739 
740 - Recreate buffers and images that were bound to allocations that were defragmented and
741   bind them with their new places in memory.
742   You must use `vkDestroyBuffer()`, `vkDestroyImage()`,
743   `vkCreateBuffer()`, `vkCreateImage()` for that purpose and NOT vmaDestroyBuffer(),
744   vmaDestroyImage(), vmaCreateBuffer(), vmaCreateImage(), because you don't need to
745   destroy or create allocation objects!
746 - Recreate views and update descriptors that point to these buffers and images.
747 
748 \section defragmentation_cpu Defragmenting CPU memory
749 
750 Following example demonstrates how you can run defragmentation on CPU.
751 Only allocations created in memory types that are `HOST_VISIBLE` can be defragmented.
752 Others are ignored.
753 
754 The way it works is:
755 
756 - It temporarily maps entire memory blocks when necessary.
757 - It moves data using `memmove()` function.
758 
759 \code
760 // Given following variables already initialized:
761 VkDevice device;
762 VmaAllocator allocator;
763 std::vector<VkBuffer> buffers;
764 std::vector<VmaAllocation> allocations;
765 
766 
767 const uint32_t allocCount = (uint32_t)allocations.size();
768 std::vector<VkBool32> allocationsChanged(allocCount);
769 
770 VmaDefragmentationInfo2 defragInfo = {};
771 defragInfo.allocationCount = allocCount;
772 defragInfo.pAllocations = allocations.data();
773 defragInfo.pAllocationsChanged = allocationsChanged.data();
774 defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE; // No limit.
775 defragInfo.maxCpuAllocationsToMove = UINT32_MAX; // No limit.
776 
777 VmaDefragmentationContext defragCtx;
778 vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx);
779 vmaDefragmentationEnd(allocator, defragCtx);
780 
781 for(uint32_t i = 0; i < allocCount; ++i)
782 {
783     if(allocationsChanged[i])
784     {
785         // Destroy buffer that is immutably bound to memory region which is no longer valid.
786         vkDestroyBuffer(device, buffers[i], nullptr);
787 
788         // Create new buffer with same parameters.
789         VkBufferCreateInfo bufferInfo = ...;
790         vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
791 
792         // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
793 
794         // Bind new buffer to new memory region. Data contained in it is already moved.
795         VmaAllocationInfo allocInfo;
796         vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
797         vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset);
798     }
799 }
800 \endcode
801 
802 Setting VmaDefragmentationInfo2::pAllocationsChanged is optional.
803 This output array tells whether particular allocation in VmaDefragmentationInfo2::pAllocations at the same index
804 has been modified during defragmentation.
805 You can pass null, but you then need to query every allocation passed to defragmentation
806 for new parameters using vmaGetAllocationInfo() if you might need to recreate and rebind a buffer or image associated with it.
807 
808 If you use [Custom memory pools](@ref choosing_memory_type_custom_memory_pools),
809 you can fill VmaDefragmentationInfo2::poolCount and VmaDefragmentationInfo2::pPools
810 instead of VmaDefragmentationInfo2::allocationCount and VmaDefragmentationInfo2::pAllocations
811 to defragment all allocations in given pools.
812 You cannot use VmaDefragmentationInfo2::pAllocationsChanged in that case.
813 You can also combine both methods.
814 
815 \section defragmentation_gpu Defragmenting GPU memory
816 
817 It is also possible to defragment allocations created in memory types that are not `HOST_VISIBLE`.
818 To do that, you need to pass a command buffer that meets requirements as described in
819 VmaDefragmentationInfo2::commandBuffer. The way it works is:
820 
821 - It creates temporary buffers and binds them to entire memory blocks when necessary.
822 - It issues `vkCmdCopyBuffer()` to passed command buffer.
823 
824 Example:
825 
826 \code
827 // Given following variables already initialized:
828 VkDevice device;
829 VmaAllocator allocator;
830 VkCommandBuffer commandBuffer;
831 std::vector<VkBuffer> buffers;
832 std::vector<VmaAllocation> allocations;
833 
834 
835 const uint32_t allocCount = (uint32_t)allocations.size();
836 std::vector<VkBool32> allocationsChanged(allocCount);
837 
838 VkCommandBufferBeginInfo cmdBufBeginInfo = ...;
839 vkBeginCommandBuffer(commandBuffer, &cmdBufBeginInfo);
840 
841 VmaDefragmentationInfo2 defragInfo = {};
842 defragInfo.allocationCount = allocCount;
843 defragInfo.pAllocations = allocations.data();
844 defragInfo.pAllocationsChanged = allocationsChanged.data();
845 defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE; // Notice it's "GPU" this time.
846 defragInfo.maxGpuAllocationsToMove = UINT32_MAX; // Notice it's "GPU" this time.
847 defragInfo.commandBuffer = commandBuffer;
848 
849 VmaDefragmentationContext defragCtx;
850 vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx);
851 
852 vkEndCommandBuffer(commandBuffer);
853 
854 // Submit commandBuffer.
855 // Wait for a fence that ensures commandBuffer execution finished.
856 
857 vmaDefragmentationEnd(allocator, defragCtx);
858 
859 for(uint32_t i = 0; i < allocCount; ++i)
860 {
861     if(allocationsChanged[i])
862     {
863         // Destroy buffer that is immutably bound to memory region which is no longer valid.
864         vkDestroyBuffer(device, buffers[i], nullptr);
865 
866         // Create new buffer with same parameters.
867         VkBufferCreateInfo bufferInfo = ...;
868         vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
869 
870         // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
871 
872         // Bind new buffer to new memory region. Data contained in it is already moved.
873         VmaAllocationInfo allocInfo;
874         vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
875         vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset);
876     }
877 }
878 \endcode
879 
880 You can combine these two methods by specifying non-zero `maxGpu*` as well as `maxCpu*` parameters.
881 The library automatically chooses best method to defragment each memory pool.
882 
883 You may try not to block your entire program to wait until defragmentation finishes,
884 but do it in the background, as long as you carefully fullfill requirements described
885 in function vmaDefragmentationBegin().
886 
887 \section defragmentation_additional_notes Additional notes
888 
889 While using defragmentation, you may experience validation layer warnings, which you just need to ignore.
890 See [Validation layer warnings](@ref general_considerations_validation_layer_warnings).
891 
892 If you defragment allocations bound to images, these images should be created with
893 `VK_IMAGE_CREATE_ALIAS_BIT` flag, to make sure that new image created with same
894 parameters and pointing to data copied to another memory region will interpret
895 its contents consistently. Otherwise you may experience corrupted data on some
896 implementations, e.g. due to different pixel swizzling used internally by the graphics driver.
897 
898 If you defragment allocations bound to images, new images to be bound to new
899 memory region after defragmentation should be created with `VK_IMAGE_LAYOUT_PREINITIALIZED`
900 and then transitioned to their original layout from before defragmentation using
901 an image memory barrier.
902 
903 Please don't expect memory to be fully compacted after defragmentation.
904 Algorithms inside are based on some heuristics that try to maximize number of Vulkan
905 memory blocks to make totally empty to release them, as well as to maximimze continuous
906 empty space inside remaining blocks, while minimizing the number and size of allocations that
907 need to be moved. Some fragmentation may still remain - this is normal.
908 
909 \section defragmentation_custom_algorithm Writing custom defragmentation algorithm
910 
911 If you want to implement your own, custom defragmentation algorithm,
912 there is infrastructure prepared for that,
913 but it is not exposed through the library API - you need to hack its source code.
914 Here are steps needed to do this:
915 
916 -# Main thing you need to do is to define your own class derived from base abstract
917    class `VmaDefragmentationAlgorithm` and implement your version of its pure virtual methods.
918    See definition and comments of this class for details.
919 -# Your code needs to interact with device memory block metadata.
920    If you need more access to its data than it's provided by its public interface,
921    declare your new class as a friend class e.g. in class `VmaBlockMetadata_Generic`.
922 -# If you want to create a flag that would enable your algorithm or pass some additional
923    flags to configure it, add them to `VmaDefragmentationFlagBits` and use them in
924    VmaDefragmentationInfo2::flags.
925 -# Modify function `VmaBlockVectorDefragmentationContext::Begin` to create object
926    of your new class whenever needed.
927 
928 
929 \page lost_allocations Lost allocations
930 
931 If your game oversubscribes video memory, if may work OK in previous-generation
932 graphics APIs (DirectX 9, 10, 11, OpenGL) because resources are automatically
933 paged to system RAM. In Vulkan you can't do it because when you run out of
934 memory, an allocation just fails. If you have more data (e.g. textures) that can
935 fit into VRAM and you don't need it all at once, you may want to upload them to
936 GPU on demand and "push out" ones that are not used for a long time to make room
937 for the new ones, effectively using VRAM (or a cartain memory pool) as a form of
938 cache. Vulkan Memory Allocator can help you with that by supporting a concept of
939 "lost allocations".
940 
941 To create an allocation that can become lost, include #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT
942 flag in VmaAllocationCreateInfo::flags. Before using a buffer or image bound to
943 such allocation in every new frame, you need to query it if it's not lost.
944 To check it, call vmaTouchAllocation().
945 If the allocation is lost, you should not use it or buffer/image bound to it.
946 You mustn't forget to destroy this allocation and this buffer/image.
947 vmaGetAllocationInfo() can also be used for checking status of the allocation.
948 Allocation is lost when returned VmaAllocationInfo::deviceMemory == `VK_NULL_HANDLE`.
949 
950 To create an allocation that can make some other allocations lost to make room
951 for it, use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag. You will
952 usually use both flags #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT and
953 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT at the same time.
954 
955 Warning! Current implementation uses quite naive, brute force algorithm,
956 which can make allocation calls that use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT
957 flag quite slow. A new, more optimal algorithm and data structure to speed this
958 up is planned for the future.
959 
960 <b>Q: When interleaving creation of new allocations with usage of existing ones,
961 how do you make sure that an allocation won't become lost while it's used in the
962 current frame?</b>
963 
964 It is ensured because vmaTouchAllocation() / vmaGetAllocationInfo() not only returns allocation
965 status/parameters and checks whether it's not lost, but when it's not, it also
966 atomically marks it as used in the current frame, which makes it impossible to
967 become lost in that frame. It uses lockless algorithm, so it works fast and
968 doesn't involve locking any internal mutex.
969 
970 <b>Q: What if my allocation may still be in use by the GPU when it's rendering a
971 previous frame while I already submit new frame on the CPU?</b>
972 
973 You can make sure that allocations "touched" by vmaTouchAllocation() / vmaGetAllocationInfo() will not
974 become lost for a number of additional frames back from the current one by
975 specifying this number as VmaAllocatorCreateInfo::frameInUseCount (for default
976 memory pool) and VmaPoolCreateInfo::frameInUseCount (for custom pool).
977 
978 <b>Q: How do you inform the library when new frame starts?</b>
979 
980 You need to call function vmaSetCurrentFrameIndex().
981 
982 Example code:
983 
984 \code
985 struct MyBuffer
986 {
987     VkBuffer m_Buf = nullptr;
988     VmaAllocation m_Alloc = nullptr;
989 
990     // Called when the buffer is really needed in the current frame.
991     void EnsureBuffer();
992 };
993 
994 void MyBuffer::EnsureBuffer()
995 {
996     // Buffer has been created.
997     if(m_Buf != VK_NULL_HANDLE)
998     {
999         // Check if its allocation is not lost + mark it as used in current frame.
1000         if(vmaTouchAllocation(allocator, m_Alloc))
1001         {
1002             // It's all OK - safe to use m_Buf.
1003             return;
1004         }
1005     }
1006 
1007     // Buffer not yet exists or lost - destroy and recreate it.
1008 
1009     vmaDestroyBuffer(allocator, m_Buf, m_Alloc);
1010 
1011     VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1012     bufCreateInfo.size = 1024;
1013     bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
1014 
1015     VmaAllocationCreateInfo allocCreateInfo = {};
1016     allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1017     allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
1018         VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
1019 
1020     vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &m_Buf, &m_Alloc, nullptr);
1021 }
1022 \endcode
1023 
1024 When using lost allocations, you may see some Vulkan validation layer warnings
1025 about overlapping regions of memory bound to different kinds of buffers and
1026 images. This is still valid as long as you implement proper handling of lost
1027 allocations (like in the example above) and don't use them.
1028 
1029 You can create an allocation that is already in lost state from the beginning using function
1030 vmaCreateLostAllocation(). It may be useful if you need a "dummy" allocation that is not null.
1031 
1032 You can call function vmaMakePoolAllocationsLost() to set all eligible allocations
1033 in a specified custom pool to lost state.
1034 Allocations that have been "touched" in current frame or VmaPoolCreateInfo::frameInUseCount frames back
1035 cannot become lost.
1036 
1037 <b>Q: Can I touch allocation that cannot become lost?</b>
1038 
1039 Yes, although it has no visible effect.
1040 Calls to vmaGetAllocationInfo() and vmaTouchAllocation() update last use frame index
1041 also for allocations that cannot become lost, but the only way to observe it is to dump
1042 internal allocator state using vmaBuildStatsString().
1043 You can use this feature for debugging purposes to explicitly mark allocations that you use
1044 in current frame and then analyze JSON dump to see for how long each allocation stays unused.
1045 
1046 
1047 \page statistics Statistics
1048 
1049 This library contains functions that return information about its internal state,
1050 especially the amount of memory allocated from Vulkan.
1051 Please keep in mind that these functions need to traverse all internal data structures
1052 to gather these information, so they may be quite time-consuming.
1053 Don't call them too often.
1054 
1055 \section statistics_numeric_statistics Numeric statistics
1056 
1057 You can query for overall statistics of the allocator using function vmaCalculateStats().
1058 Information are returned using structure #VmaStats.
1059 It contains #VmaStatInfo - number of allocated blocks, number of allocations
1060 (occupied ranges in these blocks), number of unused (free) ranges in these blocks,
1061 number of bytes used and unused (but still allocated from Vulkan) and other information.
1062 They are summed across memory heaps, memory types and total for whole allocator.
1063 
1064 You can query for statistics of a custom pool using function vmaGetPoolStats().
1065 Information are returned using structure #VmaPoolStats.
1066 
1067 You can query for information about specific allocation using function vmaGetAllocationInfo().
1068 It fill structure #VmaAllocationInfo.
1069 
1070 \section statistics_json_dump JSON dump
1071 
1072 You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString().
1073 The result is guaranteed to be correct JSON.
1074 It uses ANSI encoding.
1075 Any strings provided by user (see [Allocation names](@ref allocation_names))
1076 are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding,
1077 this JSON string can be treated as using this encoding.
1078 It must be freed using function vmaFreeStatsString().
1079 
1080 The format of this JSON string is not part of official documentation of the library,
1081 but it will not change in backward-incompatible way without increasing library major version number
1082 and appropriate mention in changelog.
1083 
1084 The JSON string contains all the data that can be obtained using vmaCalculateStats().
1085 It can also contain detailed map of allocated memory blocks and their regions -
1086 free and occupied by allocations.
1087 This allows e.g. to visualize the memory or assess fragmentation.
1088 
1089 
1090 \page allocation_annotation Allocation names and user data
1091 
1092 \section allocation_user_data Allocation user data
1093 
1094 You can annotate allocations with your own information, e.g. for debugging purposes.
1095 To do that, fill VmaAllocationCreateInfo::pUserData field when creating
1096 an allocation. It's an opaque `void*` pointer. You can use it e.g. as a pointer,
1097 some handle, index, key, ordinal number or any other value that would associate
1098 the allocation with your custom metadata.
1099 
1100 \code
1101 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1102 // Fill bufferInfo...
1103 
1104 MyBufferMetadata* pMetadata = CreateBufferMetadata();
1105 
1106 VmaAllocationCreateInfo allocCreateInfo = {};
1107 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1108 allocCreateInfo.pUserData = pMetadata;
1109 
1110 VkBuffer buffer;
1111 VmaAllocation allocation;
1112 vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, nullptr);
1113 \endcode
1114 
1115 The pointer may be later retrieved as VmaAllocationInfo::pUserData:
1116 
1117 \code
1118 VmaAllocationInfo allocInfo;
1119 vmaGetAllocationInfo(allocator, allocation, &allocInfo);
1120 MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData;
1121 \endcode
1122 
1123 It can also be changed using function vmaSetAllocationUserData().
1124 
1125 Values of (non-zero) allocations' `pUserData` are printed in JSON report created by
1126 vmaBuildStatsString(), in hexadecimal form.
1127 
1128 \section allocation_names Allocation names
1129 
1130 There is alternative mode available where `pUserData` pointer is used to point to
1131 a null-terminated string, giving a name to the allocation. To use this mode,
1132 set #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT flag in VmaAllocationCreateInfo::flags.
1133 Then `pUserData` passed as VmaAllocationCreateInfo::pUserData or argument to
1134 vmaSetAllocationUserData() must be either null or pointer to a null-terminated string.
1135 The library creates internal copy of the string, so the pointer you pass doesn't need
1136 to be valid for whole lifetime of the allocation. You can free it after the call.
1137 
1138 \code
1139 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
1140 // Fill imageInfo...
1141 
1142 std::string imageName = "Texture: ";
1143 imageName += fileName;
1144 
1145 VmaAllocationCreateInfo allocCreateInfo = {};
1146 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1147 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
1148 allocCreateInfo.pUserData = imageName.c_str();
1149 
1150 VkImage image;
1151 VmaAllocation allocation;
1152 vmaCreateImage(allocator, &imageInfo, &allocCreateInfo, &image, &allocation, nullptr);
1153 \endcode
1154 
1155 The value of `pUserData` pointer of the allocation will be different than the one
1156 you passed when setting allocation's name - pointing to a buffer managed
1157 internally that holds copy of the string.
1158 
1159 \code
1160 VmaAllocationInfo allocInfo;
1161 vmaGetAllocationInfo(allocator, allocation, &allocInfo);
1162 const char* imageName = (const char*)allocInfo.pUserData;
1163 printf("Image name: %s\n", imageName);
1164 \endcode
1165 
1166 That string is also printed in JSON report created by vmaBuildStatsString().
1167 
1168 
1169 \page debugging_memory_usage Debugging incorrect memory usage
1170 
1171 If you suspect a bug with memory usage, like usage of uninitialized memory or
1172 memory being overwritten out of bounds of an allocation,
1173 you can use debug features of this library to verify this.
1174 
1175 \section debugging_memory_usage_initialization Memory initialization
1176 
1177 If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used,
1178 you can enable automatic memory initialization to verify this.
1179 To do it, define macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to 1.
1180 
1181 \code
1182 #define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1
1183 #include "vk_mem_alloc.h"
1184 \endcode
1185 
1186 It makes memory of all new allocations initialized to bit pattern `0xDCDCDCDC`.
1187 Before an allocation is destroyed, its memory is filled with bit pattern `0xEFEFEFEF`.
1188 Memory is automatically mapped and unmapped if necessary.
1189 
1190 If you find these values while debugging your program, good chances are that you incorrectly
1191 read Vulkan memory that is allocated but not initialized, or already freed, respectively.
1192 
1193 Memory initialization works only with memory types that are `HOST_VISIBLE`.
1194 It works also with dedicated allocations.
1195 It doesn't work with allocations created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
1196 as they cannot be mapped.
1197 
1198 \section debugging_memory_usage_margins Margins
1199 
1200 By default, allocations are laid out in memory blocks next to each other if possible
1201 (considering required alignment, `bufferImageGranularity`, and `nonCoherentAtomSize`).
1202 
1203 ![Allocations without margin](../gfx/Margins_1.png)
1204 
1205 Define macro `VMA_DEBUG_MARGIN` to some non-zero value (e.g. 16) to enforce specified
1206 number of bytes as a margin before and after every allocation.
1207 
1208 \code
1209 #define VMA_DEBUG_MARGIN 16
1210 #include "vk_mem_alloc.h"
1211 \endcode
1212 
1213 ![Allocations with margin](../gfx/Margins_2.png)
1214 
1215 If your bug goes away after enabling margins, it means it may be caused by memory
1216 being overwritten outside of allocation boundaries. It is not 100% certain though.
1217 Change in application behavior may also be caused by different order and distribution
1218 of allocations across memory blocks after margins are applied.
1219 
1220 The margin is applied also before first and after last allocation in a block.
1221 It may occur only once between two adjacent allocations.
1222 
1223 Margins work with all types of memory.
1224 
1225 Margin is applied only to allocations made out of memory blocks and not to dedicated
1226 allocations, which have their own memory block of specific size.
1227 It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag
1228 or those automatically decided to put into dedicated allocations, e.g. due to its
1229 large size or recommended by VK_KHR_dedicated_allocation extension.
1230 Margins are also not active in custom pools created with #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag.
1231 
1232 Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space.
1233 
1234 Note that enabling margins increases memory usage and fragmentation.
1235 
1236 \section debugging_memory_usage_corruption_detection Corruption detection
1237 
1238 You can additionally define macro `VMA_DEBUG_DETECT_CORRUPTION` to 1 to enable validation
1239 of contents of the margins.
1240 
1241 \code
1242 #define VMA_DEBUG_MARGIN 16
1243 #define VMA_DEBUG_DETECT_CORRUPTION 1
1244 #include "vk_mem_alloc.h"
1245 \endcode
1246 
1247 When this feature is enabled, number of bytes specified as `VMA_DEBUG_MARGIN`
1248 (it must be multiply of 4) before and after every allocation is filled with a magic number.
1249 This idea is also know as "canary".
1250 Memory is automatically mapped and unmapped if necessary.
1251 
1252 This number is validated automatically when the allocation is destroyed.
1253 If it's not equal to the expected value, `VMA_ASSERT()` is executed.
1254 It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation,
1255 which indicates a serious bug.
1256 
1257 You can also explicitly request checking margins of all allocations in all memory blocks
1258 that belong to specified memory types by using function vmaCheckCorruption(),
1259 or in memory blocks that belong to specified custom pool, by using function
1260 vmaCheckPoolCorruption().
1261 
1262 Margin validation (corruption detection) works only for memory types that are
1263 `HOST_VISIBLE` and `HOST_COHERENT`.
1264 
1265 
1266 \page record_and_replay Record and replay
1267 
1268 \section record_and_replay_introduction Introduction
1269 
1270 While using the library, sequence of calls to its functions together with their
1271 parameters can be recorded to a file and later replayed using standalone player
1272 application. It can be useful to:
1273 
1274 - Test correctness - check if same sequence of calls will not cause crash or
1275   failures on a target platform.
1276 - Gather statistics - see number of allocations, peak memory usage, number of
1277   calls etc.
1278 - Benchmark performance - see how much time it takes to replay the whole
1279   sequence.
1280 
1281 \section record_and_replay_usage Usage
1282 
1283 <b>To record sequence of calls to a file:</b> Fill in
1284 VmaAllocatorCreateInfo::pRecordSettings member while creating #VmaAllocator
1285 object. File is opened and written during whole lifetime of the allocator.
1286 
1287 <b>To replay file:</b> Use VmaReplay - standalone command-line program.
1288 Precompiled binary can be found in "bin" directory.
1289 Its source can be found in "src/VmaReplay" directory.
1290 Its project is generated by Premake.
1291 Command line syntax is printed when the program is launched without parameters.
1292 Basic usage:
1293 
1294     VmaReplay.exe MyRecording.csv
1295 
1296 <b>Documentation of file format</b> can be found in file: "docs/Recording file format.md".
1297 It's a human-readable, text file in CSV format (Comma Separated Values).
1298 
1299 \section record_and_replay_additional_considerations Additional considerations
1300 
1301 - Replaying file that was recorded on a different GPU (with different parameters
1302   like `bufferImageGranularity`, `nonCoherentAtomSize`, and especially different
1303   set of memory heaps and types) may give different performance and memory usage
1304   results, as well as issue some warnings and errors.
1305 - Current implementation of recording in VMA, as well as VmaReplay application, is
1306   coded and tested only on Windows. Inclusion of recording code is driven by
1307   `VMA_RECORDING_ENABLED` macro. Support for other platforms should be easy to
1308   add. Contributions are welcomed.
1309 - Currently calls to vmaDefragment() function are not recorded.
1310 
1311 
1312 \page usage_patterns Recommended usage patterns
1313 
1314 See also slides from talk:
1315 [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)
1316 
1317 
1318 \section usage_patterns_simple Simple patterns
1319 
1320 \subsection usage_patterns_simple_render_targets Render targets
1321 
1322 <b>When:</b>
1323 Any resources that you frequently write and read on GPU,
1324 e.g. images used as color attachments (aka "render targets"), depth-stencil attachments,
1325 images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)").
1326 
1327 <b>What to do:</b>
1328 Create them in video memory that is fastest to access from GPU using
1329 #VMA_MEMORY_USAGE_GPU_ONLY.
1330 
1331 Consider using [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension
1332 and/or manually creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
1333 especially if they are large or if you plan to destroy and recreate them e.g. when
1334 display resolution changes.
1335 Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later.
1336 
1337 \subsection usage_patterns_simple_immutable_resources Immutable resources
1338 
1339 <b>When:</b>
1340 Any resources that you fill on CPU only once (aka "immutable") or infrequently
1341 and then read frequently on GPU,
1342 e.g. textures, vertex and index buffers, constant buffers that don't change often.
1343 
1344 <b>What to do:</b>
1345 Create them in video memory that is fastest to access from GPU using
1346 #VMA_MEMORY_USAGE_GPU_ONLY.
1347 
1348 To initialize content of such resource, create a CPU-side (aka "staging") copy of it
1349 in system memory - #VMA_MEMORY_USAGE_CPU_ONLY, map it, fill it,
1350 and submit a transfer from it to the GPU resource.
1351 You can keep the staging copy if you need it for another upload transfer in the future.
1352 If you don't, you can destroy it or reuse this buffer for uploading different resource
1353 after the transfer finishes.
1354 
1355 Prefer to create just buffers in system memory rather than images, even for uploading textures.
1356 Use `vkCmdCopyBufferToImage()`.
1357 Dont use images with `VK_IMAGE_TILING_LINEAR`.
1358 
1359 \subsection usage_patterns_dynamic_resources Dynamic resources
1360 
1361 <b>When:</b>
1362 Any resources that change frequently (aka "dynamic"), e.g. every frame or every draw call,
1363 written on CPU, read on GPU.
1364 
1365 <b>What to do:</b>
1366 Create them using #VMA_MEMORY_USAGE_CPU_TO_GPU.
1367 You can map it and write to it directly on CPU, as well as read from it on GPU.
1368 
1369 This is a more complex situation. Different solutions are possible,
1370 and the best one depends on specific GPU type, but you can use this simple approach for the start.
1371 Prefer to write to such resource sequentially (e.g. using `memcpy`).
1372 Don't perform random access or any reads from it on CPU, as it may be very slow.
1373 
1374 \subsection usage_patterns_readback Readback
1375 
1376 <b>When:</b>
1377 Resources that contain data written by GPU that you want to read back on CPU,
1378 e.g. results of some computations.
1379 
1380 <b>What to do:</b>
1381 Create them using #VMA_MEMORY_USAGE_GPU_TO_CPU.
1382 You can write to them directly on GPU, as well as map and read them on CPU.
1383 
1384 \section usage_patterns_advanced Advanced patterns
1385 
1386 \subsection usage_patterns_integrated_graphics Detecting integrated graphics
1387 
1388 You can support integrated graphics (like Intel HD Graphics, AMD APU) better
1389 by detecting it in Vulkan.
1390 To do it, call `vkGetPhysicalDeviceProperties()`, inspect
1391 `VkPhysicalDeviceProperties::deviceType` and look for `VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU`.
1392 When you find it, you can assume that memory is unified and all memory types are comparably fast
1393 to access from GPU, regardless of `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
1394 
1395 You can then sum up sizes of all available memory heaps and treat them as useful for
1396 your GPU resources, instead of only `DEVICE_LOCAL` ones.
1397 You can also prefer to create your resources in memory types that are `HOST_VISIBLE` to map them
1398 directly instead of submitting explicit transfer (see below).
1399 
1400 \subsection usage_patterns_direct_vs_transfer Direct access versus transfer
1401 
1402 For resources that you frequently write on CPU and read on GPU, many solutions are possible:
1403 
1404 -# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
1405    second copy in system memory using #VMA_MEMORY_USAGE_CPU_ONLY and submit explicit tranfer each time.
1406 -# Create just single copy using #VMA_MEMORY_USAGE_CPU_TO_GPU, map it and fill it on CPU,
1407    read it directly on GPU.
1408 -# Create just single copy using #VMA_MEMORY_USAGE_CPU_ONLY, map it and fill it on CPU,
1409    read it directly on GPU.
1410 
1411 Which solution is the most efficient depends on your resource and especially on the GPU.
1412 It is best to measure it and then make the decision.
1413 Some general recommendations:
1414 
1415 - On integrated graphics use (2) or (3) to avoid unnecesary time and memory overhead
1416   related to using a second copy and making transfer.
1417 - For small resources (e.g. constant buffers) use (2).
1418   Discrete AMD cards have special 256 MiB pool of video memory that is directly mappable.
1419   Even if the resource ends up in system memory, its data may be cached on GPU after first
1420   fetch over PCIe bus.
1421 - For larger resources (e.g. textures), decide between (1) and (2).
1422   You may want to differentiate NVIDIA and AMD, e.g. by looking for memory type that is
1423   both `DEVICE_LOCAL` and `HOST_VISIBLE`. When you find it, use (2), otherwise use (1).
1424 
1425 Similarly, for resources that you frequently write on GPU and read on CPU, multiple
1426 solutions are possible:
1427 
1428 -# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
1429    second copy in system memory using #VMA_MEMORY_USAGE_GPU_TO_CPU and submit explicit tranfer each time.
1430 -# Create just single copy using #VMA_MEMORY_USAGE_GPU_TO_CPU, write to it directly on GPU,
1431    map it and read it on CPU.
1432 
1433 You should take some measurements to decide which option is faster in case of your specific
1434 resource.
1435 
1436 If you don't want to specialize your code for specific types of GPUs, you can still make
1437 an simple optimization for cases when your resource ends up in mappable memory to use it
1438 directly in this case instead of creating CPU-side staging copy.
1439 For details see [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable).
1440 
1441 
1442 \page configuration Configuration
1443 
1444 Please check "CONFIGURATION SECTION" in the code to find macros that you can define
1445 before each include of this file or change directly in this file to provide
1446 your own implementation of basic facilities like assert, `min()` and `max()` functions,
1447 mutex, atomic etc.
1448 The library uses its own implementation of containers by default, but you can switch to using
1449 STL containers instead.
1450 
1451 \section config_Vulkan_functions Pointers to Vulkan functions
1452 
1453 The library uses Vulkan functions straight from the `vulkan.h` header by default.
1454 If you want to provide your own pointers to these functions, e.g. fetched using
1455 `vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`:
1456 
1457 -# Define `VMA_STATIC_VULKAN_FUNCTIONS 0`.
1458 -# Provide valid pointers through VmaAllocatorCreateInfo::pVulkanFunctions.
1459 
1460 \section custom_memory_allocator Custom host memory allocator
1461 
1462 If you use custom allocator for CPU memory rather than default operator `new`
1463 and `delete` from C++, you can make this library using your allocator as well
1464 by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These
1465 functions will be passed to Vulkan, as well as used by the library itself to
1466 make any CPU-side allocations.
1467 
1468 \section allocation_callbacks Device memory allocation callbacks
1469 
1470 The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally.
1471 You can setup callbacks to be informed about these calls, e.g. for the purpose
1472 of gathering some statistics. To do it, fill optional member
1473 VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
1474 
1475 \section heap_memory_limit Device heap memory limit
1476 
1477 If you want to test how your program behaves with limited amount of Vulkan device
1478 memory available without switching your graphics card to one that really has
1479 smaller VRAM, you can use a feature of this library intended for this purpose.
1480 To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit.
1481 
1482 
1483 
1484 \page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation
1485 
1486 VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve
1487 performance on some GPUs. It augments Vulkan API with possibility to query
1488 driver whether it prefers particular buffer or image to have its own, dedicated
1489 allocation (separate `VkDeviceMemory` block) for better efficiency - to be able
1490 to do some internal optimizations.
1491 
1492 The extension is supported by this library. It will be used automatically when
1493 enabled. To enable it:
1494 
1495 1 . When creating Vulkan device, check if following 2 device extensions are
1496 supported (call `vkEnumerateDeviceExtensionProperties()`).
1497 If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`).
1498 
1499 - VK_KHR_get_memory_requirements2
1500 - VK_KHR_dedicated_allocation
1501 
1502 If you enabled these extensions:
1503 
1504 2 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating
1505 your #VmaAllocator`to inform the library that you enabled required extensions
1506 and you want the library to use them.
1507 
1508 \code
1509 allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
1510 
1511 vmaCreateAllocator(&allocatorInfo, &allocator);
1512 \endcode
1513 
1514 That's all. The extension will be automatically used whenever you create a
1515 buffer using vmaCreateBuffer() or image using vmaCreateImage().
1516 
1517 When using the extension together with Vulkan Validation Layer, you will receive
1518 warnings like this:
1519 
1520     vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer.
1521 
1522 It is OK, you should just ignore it. It happens because you use function
1523 `vkGetBufferMemoryRequirements2KHR()` instead of standard
1524 `vkGetBufferMemoryRequirements()`, while the validation layer seems to be
1525 unaware of it.
1526 
1527 To learn more about this extension, see:
1528 
1529 - [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VK_KHR_dedicated_allocation)
1530 - [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5)
1531 
1532 
1533 
1534 \page general_considerations General considerations
1535 
1536 \section general_considerations_thread_safety Thread safety
1537 
1538 - The library has no global state, so separate #VmaAllocator objects can be used
1539   independently.
1540   There should be no need to create multiple such objects though - one per `VkDevice` is enough.
1541 - By default, all calls to functions that take #VmaAllocator as first parameter
1542   are safe to call from multiple threads simultaneously because they are
1543   synchronized internally when needed.
1544 - When the allocator is created with #VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT
1545   flag, calls to functions that take such #VmaAllocator object must be
1546   synchronized externally.
1547 - Access to a #VmaAllocation object must be externally synchronized. For example,
1548   you must not call vmaGetAllocationInfo() and vmaMapMemory() from different
1549   threads at the same time if you pass the same #VmaAllocation object to these
1550   functions.
1551 
1552 \section general_considerations_validation_layer_warnings Validation layer warnings
1553 
1554 When using this library, you can meet following types of warnings issued by
1555 Vulkan validation layer. They don't necessarily indicate a bug, so you may need
1556 to just ignore them.
1557 
1558 - *vkBindBufferMemory(): Binding memory to buffer 0xeb8e4 but vkGetBufferMemoryRequirements() has not been called on that buffer.*
1559   - It happens when VK_KHR_dedicated_allocation extension is enabled.
1560     `vkGetBufferMemoryRequirements2KHR` function is used instead, while validation layer seems to be unaware of it.
1561 - *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.*
1562   - It happens when you map a buffer or image, because the library maps entire
1563     `VkDeviceMemory` block, where different types of images and buffers may end
1564     up together, especially on GPUs with unified memory like Intel.
1565 - *Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug.*
1566   - It happens when you use lost allocations, and a new image or buffer is
1567     created in place of an existing object that bacame lost.
1568   - It may happen also when you use [defragmentation](@ref defragmentation).
1569 
1570 \section general_considerations_allocation_algorithm Allocation algorithm
1571 
1572 The library uses following algorithm for allocation, in order:
1573 
1574 -# Try to find free range of memory in existing blocks.
1575 -# If failed, try to create a new block of `VkDeviceMemory`, with preferred block size.
1576 -# If failed, try to create such block with size/2, size/4, size/8.
1577 -# If failed and #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag was
1578    specified, try to find space in existing blocks, possilby making some other
1579    allocations lost.
1580 -# If failed, try to allocate separate `VkDeviceMemory` for this allocation,
1581    just like when you use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
1582 -# If failed, choose other memory type that meets the requirements specified in
1583    VmaAllocationCreateInfo and go to point 1.
1584 -# If failed, return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
1585 
1586 \section general_considerations_features_not_supported Features not supported
1587 
1588 Features deliberately excluded from the scope of this library:
1589 
1590 - Data transfer. Uploading (straming) and downloading data of buffers and images
1591   between CPU and GPU memory and related synchronization is responsibility of the user.
1592 - Allocations for imported/exported external memory. They tend to require
1593   explicit memory type index and dedicated allocation anyway, so they don't
1594   interact with main features of this library. Such special purpose allocations
1595   should be made manually, using `vkCreateBuffer()` and `vkAllocateMemory()`.
1596 - Recreation of buffers and images. Although the library has functions for
1597   buffer and image creation (vmaCreateBuffer(), vmaCreateImage()), you need to
1598   recreate these objects yourself after defragmentation. That's because the big
1599   structures `VkBufferCreateInfo`, `VkImageCreateInfo` are not stored in
1600   #VmaAllocation object.
1601 - Handling CPU memory allocation failures. When dynamically creating small C++
1602   objects in CPU memory (not Vulkan memory), allocation failures are not checked
1603   and handled gracefully, because that would complicate code significantly and
1604   is usually not needed in desktop PC applications anyway.
1605 - Code free of any compiler warnings. Maintaining the library to compile and
1606   work correctly on so many different platforms is hard enough. Being free of
1607   any warnings, on any version of any compiler, is simply not feasible.
1608 - This is a C++ library with C interface.
1609   Bindings or ports to any other programming languages are welcomed as external projects and
1610   are not going to be included into this repository.
1611 
1612 */
1613 
1614 /*
1615 Define this macro to 0/1 to disable/enable support for recording functionality,
1616 available through VmaAllocatorCreateInfo::pRecordSettings.
1617 */
1618 #ifndef VMA_RECORDING_ENABLED
1619     #ifdef _WIN32
1620         #define VMA_RECORDING_ENABLED 1
1621     #else
1622         #define VMA_RECORDING_ENABLED 0
1623     #endif
1624 #endif
1625 
1626 #ifndef NOMINMAX
1627     #define NOMINMAX // For windows.h
1628 #endif
1629 
1630 #ifndef VULKAN_H_
1631     #include <vulkan/vulkan.h>
1632 #endif
1633 
1634 #if VMA_RECORDING_ENABLED
1635     #include <windows.h>
1636 #endif
1637 
1638 #if !defined(VMA_DEDICATED_ALLOCATION)
1639     #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation
1640         #define VMA_DEDICATED_ALLOCATION 1
1641     #else
1642         #define VMA_DEDICATED_ALLOCATION 0
1643     #endif
1644 #endif
1645 
1646 /** \struct VmaAllocator
1647 \brief Represents main object of this library initialized.
1648 
1649 Fill structure #VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it.
1650 Call function vmaDestroyAllocator() to destroy it.
1651 
1652 It is recommended to create just one object of this type per `VkDevice` object,
1653 right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed.
1654 */
1655 VK_DEFINE_HANDLE(VmaAllocator)
1656 
1657 /// Callback function called after successful vkAllocateMemory.
1658 typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)(
1659     VmaAllocator      allocator,
1660     uint32_t          memoryType,
1661     VkDeviceMemory    memory,
1662     VkDeviceSize      size);
1663 /// Callback function called before vkFreeMemory.
1664 typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)(
1665     VmaAllocator      allocator,
1666     uint32_t          memoryType,
1667     VkDeviceMemory    memory,
1668     VkDeviceSize      size);
1669 
1670 /** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`.
1671 
1672 Provided for informative purpose, e.g. to gather statistics about number of
1673 allocations or total amount of memory allocated in Vulkan.
1674 
1675 Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
1676 */
1677 typedef struct VmaDeviceMemoryCallbacks {
1678     /// Optional, can be null.
1679     PFN_vmaAllocateDeviceMemoryFunction pfnAllocate;
1680     /// Optional, can be null.
1681     PFN_vmaFreeDeviceMemoryFunction pfnFree;
1682 } VmaDeviceMemoryCallbacks;
1683 
1684 /// Flags for created #VmaAllocator.
1685 typedef enum VmaAllocatorCreateFlagBits {
1686     /** \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.
1687 
1688     Using this flag may increase performance because internal mutexes are not used.
1689     */
1690     VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001,
1691     /** \brief Enables usage of VK_KHR_dedicated_allocation extension.
1692 
1693     Using this extenion will automatically allocate dedicated blocks of memory for
1694     some buffers and images instead of suballocating place for them out of bigger
1695     memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
1696     flag) when it is recommended by the driver. It may improve performance on some
1697     GPUs.
1698 
1699     You may set this flag only if you found out that following device extensions are
1700     supported, you enabled them while creating Vulkan device passed as
1701     VmaAllocatorCreateInfo::device, and you want them to be used internally by this
1702     library:
1703 
1704     - VK_KHR_get_memory_requirements2
1705     - VK_KHR_dedicated_allocation
1706 
1707 When this flag is set, you can experience following warnings reported by Vulkan
1708 validation layer. You can ignore them.
1709 
1710 > vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer.
1711     */
1712     VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002,
1713 
1714     VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1715 } VmaAllocatorCreateFlagBits;
1716 typedef VkFlags VmaAllocatorCreateFlags;
1717 
1718 /** \brief Pointers to some Vulkan functions - a subset used by the library.
1719 
1720 Used in VmaAllocatorCreateInfo::pVulkanFunctions.
1721 */
1722 typedef struct VmaVulkanFunctions {
1723     PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
1724     PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
1725     PFN_vkAllocateMemory vkAllocateMemory;
1726     PFN_vkFreeMemory vkFreeMemory;
1727     PFN_vkMapMemory vkMapMemory;
1728     PFN_vkUnmapMemory vkUnmapMemory;
1729     PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
1730     PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
1731     PFN_vkBindBufferMemory vkBindBufferMemory;
1732     PFN_vkBindImageMemory vkBindImageMemory;
1733     PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
1734     PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
1735     PFN_vkCreateBuffer vkCreateBuffer;
1736     PFN_vkDestroyBuffer vkDestroyBuffer;
1737     PFN_vkCreateImage vkCreateImage;
1738     PFN_vkDestroyImage vkDestroyImage;
1739     PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
1740 #if VMA_DEDICATED_ALLOCATION
1741     PFN_vkGetBufferMemoryRequirements2KHR vkGetBufferMemoryRequirements2KHR;
1742     PFN_vkGetImageMemoryRequirements2KHR vkGetImageMemoryRequirements2KHR;
1743 #endif
1744 } VmaVulkanFunctions;
1745 
1746 /// Flags to be used in VmaRecordSettings::flags.
1747 typedef enum VmaRecordFlagBits {
1748     /** \brief Enables flush after recording every function call.
1749 
1750     Enable it if you expect your application to crash, which may leave recording file truncated.
1751     It may degrade performance though.
1752     */
1753     VMA_RECORD_FLUSH_AFTER_CALL_BIT = 0x00000001,
1754 
1755     VMA_RECORD_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1756 } VmaRecordFlagBits;
1757 typedef VkFlags VmaRecordFlags;
1758 
1759 /// Parameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSettings.
1760 typedef struct VmaRecordSettings
1761 {
1762     /// Flags for recording. Use #VmaRecordFlagBits enum.
1763     VmaRecordFlags flags;
1764     /** \brief Path to the file that should be written by the recording.
1765 
1766     Suggested extension: "csv".
1767     If the file already exists, it will be overwritten.
1768     It will be opened for the whole time #VmaAllocator object is alive.
1769     If opening this file fails, creation of the whole allocator object fails.
1770     */
1771     const char* pFilePath;
1772 } VmaRecordSettings;
1773 
1774 /// Description of a Allocator to be created.
1775 typedef struct VmaAllocatorCreateInfo
1776 {
1777     /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum.
1778     VmaAllocatorCreateFlags flags;
1779     /// Vulkan physical device.
1780     /** It must be valid throughout whole lifetime of created allocator. */
1781     VkPhysicalDevice physicalDevice;
1782     /// Vulkan device.
1783     /** It must be valid throughout whole lifetime of created allocator. */
1784     VkDevice device;
1785     /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional.
1786     /** Set to 0 to use default, which is currently 256 MiB. */
1787     VkDeviceSize preferredLargeHeapBlockSize;
1788     /// Custom CPU memory allocation callbacks. Optional.
1789     /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */
1790     const VkAllocationCallbacks* pAllocationCallbacks;
1791     /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional.
1792     /** Optional, can be null. */
1793     const VmaDeviceMemoryCallbacks* pDeviceMemoryCallbacks;
1794     /** \brief Maximum number of additional frames that are in use at the same time as current frame.
1795 
1796     This value is used only when you make allocations with
1797     VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
1798     lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
1799 
1800     For example, if you double-buffer your command buffers, so resources used for
1801     rendering in previous frame may still be in use by the GPU at the moment you
1802     allocate resources needed for the current frame, set this value to 1.
1803 
1804     If you want to allow any allocations other than used in the current frame to
1805     become lost, set this value to 0.
1806     */
1807     uint32_t frameInUseCount;
1808     /** \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.
1809 
1810     If not NULL, it must be a pointer to an array of
1811     `VkPhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on
1812     maximum number of bytes that can be allocated out of particular Vulkan memory
1813     heap.
1814 
1815     Any of the elements may be equal to `VK_WHOLE_SIZE`, which means no limit on that
1816     heap. This is also the default in case of `pHeapSizeLimit` = NULL.
1817 
1818     If there is a limit defined for a heap:
1819 
1820     - If user tries to allocate more memory from that heap using this allocator,
1821       the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
1822     - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the
1823       value of this limit will be reported instead when using vmaGetMemoryProperties().
1824 
1825     Warning! Using this feature may not be equivalent to installing a GPU with
1826     smaller amount of memory, because graphics driver doesn't necessary fail new
1827     allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is
1828     exceeded. It may return success and just silently migrate some device memory
1829     blocks to system RAM. This driver behavior can also be controlled using
1830     VK_AMD_memory_overallocation_behavior extension.
1831     */
1832     const VkDeviceSize* pHeapSizeLimit;
1833     /** \brief Pointers to Vulkan functions. Can be null if you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1`.
1834 
1835     If you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1` in configuration section,
1836     you can pass null as this member, because the library will fetch pointers to
1837     Vulkan functions internally in a static way, like:
1838 
1839         vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
1840 
1841     Fill this member if you want to provide your own pointers to Vulkan functions,
1842     e.g. fetched using `vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`.
1843     */
1844     const VmaVulkanFunctions* pVulkanFunctions;
1845     /** \brief Parameters for recording of VMA calls. Can be null.
1846 
1847     If not null, it enables recording of calls to VMA functions to a file.
1848     If support for recording is not enabled using `VMA_RECORDING_ENABLED` macro,
1849     creation of the allocator object fails with `VK_ERROR_FEATURE_NOT_PRESENT`.
1850     */
1851     const VmaRecordSettings* pRecordSettings;
1852 } VmaAllocatorCreateInfo;
1853 
1854 /// Creates Allocator object.
1855 VkResult vmaCreateAllocator(
1856     const VmaAllocatorCreateInfo* pCreateInfo,
1857     VmaAllocator* pAllocator);
1858 
1859 /// Destroys allocator object.
1860 void vmaDestroyAllocator(
1861     VmaAllocator allocator);
1862 
1863 /**
1864 PhysicalDeviceProperties are fetched from physicalDevice by the allocator.
1865 You can access it here, without fetching it again on your own.
1866 */
1867 void vmaGetPhysicalDeviceProperties(
1868     VmaAllocator allocator,
1869     const VkPhysicalDeviceProperties** ppPhysicalDeviceProperties);
1870 
1871 /**
1872 PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator.
1873 You can access it here, without fetching it again on your own.
1874 */
1875 void vmaGetMemoryProperties(
1876     VmaAllocator allocator,
1877     const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties);
1878 
1879 /**
1880 \brief Given Memory Type Index, returns Property Flags of this memory type.
1881 
1882 This is just a convenience function. Same information can be obtained using
1883 vmaGetMemoryProperties().
1884 */
1885 void vmaGetMemoryTypeProperties(
1886     VmaAllocator allocator,
1887     uint32_t memoryTypeIndex,
1888     VkMemoryPropertyFlags* pFlags);
1889 
1890 /** \brief Sets index of the current frame.
1891 
1892 This function must be used if you make allocations with
1893 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT and
1894 #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flags to inform the allocator
1895 when a new frame begins. Allocations queried using vmaGetAllocationInfo() cannot
1896 become lost in the current frame.
1897 */
1898 void vmaSetCurrentFrameIndex(
1899     VmaAllocator allocator,
1900     uint32_t frameIndex);
1901 
1902 /** \brief Calculated statistics of memory usage in entire allocator.
1903 */
1904 typedef struct VmaStatInfo
1905 {
1906     /// Number of `VkDeviceMemory` Vulkan memory blocks allocated.
1907     uint32_t blockCount;
1908     /// Number of #VmaAllocation allocation objects allocated.
1909     uint32_t allocationCount;
1910     /// Number of free ranges of memory between allocations.
1911     uint32_t unusedRangeCount;
1912     /// Total number of bytes occupied by all allocations.
1913     VkDeviceSize usedBytes;
1914     /// Total number of bytes occupied by unused ranges.
1915     VkDeviceSize unusedBytes;
1916     VkDeviceSize allocationSizeMin, allocationSizeAvg, allocationSizeMax;
1917     VkDeviceSize unusedRangeSizeMin, unusedRangeSizeAvg, unusedRangeSizeMax;
1918 } VmaStatInfo;
1919 
1920 /// General statistics from current state of Allocator.
1921 typedef struct VmaStats
1922 {
1923     VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
1924     VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
1925     VmaStatInfo total;
1926 } VmaStats;
1927 
1928 /// Retrieves statistics from current state of the Allocator.
1929 void vmaCalculateStats(
1930     VmaAllocator allocator,
1931     VmaStats* pStats);
1932 
1933 #define VMA_STATS_STRING_ENABLED 1
1934 
1935 #if VMA_STATS_STRING_ENABLED
1936 
1937 /// Builds and returns statistics as string in JSON format.
1938 /** @param[out] ppStatsString Must be freed using vmaFreeStatsString() function.
1939 */
1940 void vmaBuildStatsString(
1941     VmaAllocator allocator,
1942     char** ppStatsString,
1943     VkBool32 detailedMap);
1944 
1945 void vmaFreeStatsString(
1946     VmaAllocator allocator,
1947     char* pStatsString);
1948 
1949 #endif // #if VMA_STATS_STRING_ENABLED
1950 
1951 /** \struct VmaPool
1952 \brief Represents custom memory pool
1953 
1954 Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it.
1955 Call function vmaDestroyPool() to destroy it.
1956 
1957 For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools).
1958 */
1959 VK_DEFINE_HANDLE(VmaPool)
1960 
1961 typedef enum VmaMemoryUsage
1962 {
1963     /** No intended memory usage specified.
1964     Use other members of VmaAllocationCreateInfo to specify your requirements.
1965     */
1966     VMA_MEMORY_USAGE_UNKNOWN = 0,
1967     /** Memory will be used on device only, so fast access from the device is preferred.
1968     It usually means device-local GPU (video) memory.
1969     No need to be mappable on host.
1970     It is roughly equivalent of `D3D12_HEAP_TYPE_DEFAULT`.
1971 
1972     Usage:
1973 
1974     - Resources written and read by device, e.g. images used as attachments.
1975     - Resources transferred from host once (immutable) or infrequently and read by
1976       device multiple times, e.g. textures to be sampled, vertex buffers, uniform
1977       (constant) buffers, and majority of other types of resources used on GPU.
1978 
1979     Allocation may still end up in `HOST_VISIBLE` memory on some implementations.
1980     In such case, you are free to map it.
1981     You can use #VMA_ALLOCATION_CREATE_MAPPED_BIT with this usage type.
1982     */
1983     VMA_MEMORY_USAGE_GPU_ONLY = 1,
1984     /** Memory will be mappable on host.
1985     It usually means CPU (system) memory.
1986     Guarantees to be `HOST_VISIBLE` and `HOST_COHERENT`.
1987     CPU access is typically uncached. Writes may be write-combined.
1988     Resources created in this pool may still be accessible to the device, but access to them can be slow.
1989     It is roughly equivalent of `D3D12_HEAP_TYPE_UPLOAD`.
1990 
1991     Usage: Staging copy of resources used as transfer source.
1992     */
1993     VMA_MEMORY_USAGE_CPU_ONLY = 2,
1994     /**
1995     Memory that is both mappable on host (guarantees to be `HOST_VISIBLE`) and preferably fast to access by GPU.
1996     CPU access is typically uncached. Writes may be write-combined.
1997 
1998     Usage: Resources written frequently by host (dynamic), read by device. E.g. textures, vertex buffers, uniform buffers updated every frame or every draw call.
1999     */
2000     VMA_MEMORY_USAGE_CPU_TO_GPU = 3,
2001     /** Memory mappable on host (guarantees to be `HOST_VISIBLE`) and cached.
2002     It is roughly equivalent of `D3D12_HEAP_TYPE_READBACK`.
2003 
2004     Usage:
2005 
2006     - Resources written by device, read by host - results of some computations, e.g. screen capture, average scene luminance for HDR tone mapping.
2007     - 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.
2008     */
2009     VMA_MEMORY_USAGE_GPU_TO_CPU = 4,
2010     VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
2011 } VmaMemoryUsage;
2012 
2013 /// Flags to be passed as VmaAllocationCreateInfo::flags.
2014 typedef enum VmaAllocationCreateFlagBits {
2015     /** \brief Set this flag if the allocation should have its own memory block.
2016 
2017     Use it for special, big resources, like fullscreen images used as attachments.
2018 
2019     This flag must also be used for host visible resources that you want to map
2020     simultaneously because otherwise they might end up as regions of the same
2021     `VkDeviceMemory`, while mapping same `VkDeviceMemory` multiple times
2022     simultaneously is illegal.
2023 
2024     You should not use this flag if VmaAllocationCreateInfo::pool is not null.
2025     */
2026     VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001,
2027 
2028     /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block.
2029 
2030     If new allocation cannot be placed in any of the existing blocks, allocation
2031     fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
2032 
2033     You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and
2034     #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense.
2035 
2036     If VmaAllocationCreateInfo::pool is not null, this flag is implied and ignored. */
2037     VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002,
2038     /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it.
2039 
2040     Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData.
2041 
2042     Is it valid to use this flag for allocation made from memory type that is not
2043     `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is
2044     useful if you need an allocation that is efficient to use on GPU
2045     (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that
2046     support it (e.g. Intel GPU).
2047 
2048     You should not use this flag together with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT.
2049     */
2050     VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004,
2051     /** Allocation created with this flag can become lost as a result of another
2052     allocation with #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag, so you
2053     must check it before use.
2054 
2055     To check if allocation is not lost, call vmaGetAllocationInfo() and check if
2056     VmaAllocationInfo::deviceMemory is not `VK_NULL_HANDLE`.
2057 
2058     For details about supporting lost allocations, see Lost Allocations
2059     chapter of User Guide on Main Page.
2060 
2061     You should not use this flag together with #VMA_ALLOCATION_CREATE_MAPPED_BIT.
2062     */
2063     VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT = 0x00000008,
2064     /** While creating allocation using this flag, other allocations that were
2065     created with flag #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT can become lost.
2066 
2067     For details about supporting lost allocations, see Lost Allocations
2068     chapter of User Guide on Main Page.
2069     */
2070     VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT = 0x00000010,
2071     /** Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a
2072     null-terminated string. Instead of copying pointer value, a local copy of the
2073     string is made and stored in allocation's `pUserData`. The string is automatically
2074     freed together with the allocation. It is also used in vmaBuildStatsString().
2075     */
2076     VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020,
2077     /** Allocation will be created from upper stack in a double stack pool.
2078 
2079     This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag.
2080     */
2081     VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040,
2082 
2083     /** Allocation strategy that chooses smallest possible free range for the
2084     allocation.
2085     */
2086     VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT  = 0x00010000,
2087     /** Allocation strategy that chooses biggest possible free range for the
2088     allocation.
2089     */
2090     VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT = 0x00020000,
2091     /** Allocation strategy that chooses first suitable free range for the
2092     allocation.
2093 
2094     "First" doesn't necessarily means the one with smallest offset in memory,
2095     but rather the one that is easiest and fastest to find.
2096     */
2097     VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = 0x00040000,
2098 
2099     /** Allocation strategy that tries to minimize memory usage.
2100     */
2101     VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT,
2102     /** Allocation strategy that tries to minimize allocation time.
2103     */
2104     VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
2105     /** Allocation strategy that tries to minimize memory fragmentation.
2106     */
2107     VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT,
2108 
2109     /** A bit mask to extract only `STRATEGY` bits from entire set of flags.
2110     */
2111     VMA_ALLOCATION_CREATE_STRATEGY_MASK =
2112         VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT |
2113         VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT |
2114         VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
2115 
2116     VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2117 } VmaAllocationCreateFlagBits;
2118 typedef VkFlags VmaAllocationCreateFlags;
2119 
2120 typedef struct VmaAllocationCreateInfo
2121 {
2122     /// Use #VmaAllocationCreateFlagBits enum.
2123     VmaAllocationCreateFlags flags;
2124     /** \brief Intended usage of memory.
2125 
2126     You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n
2127     If `pool` is not null, this member is ignored.
2128     */
2129     VmaMemoryUsage usage;
2130     /** \brief Flags that must be set in a Memory Type chosen for an allocation.
2131 
2132     Leave 0 if you specify memory requirements in other way. \n
2133     If `pool` is not null, this member is ignored.*/
2134     VkMemoryPropertyFlags requiredFlags;
2135     /** \brief Flags that preferably should be set in a memory type chosen for an allocation.
2136 
2137     Set to 0 if no additional flags are prefered. \n
2138     If `pool` is not null, this member is ignored. */
2139     VkMemoryPropertyFlags preferredFlags;
2140     /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation.
2141 
2142     Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if
2143     it meets other requirements specified by this structure, with no further
2144     restrictions on memory type index. \n
2145     If `pool` is not null, this member is ignored.
2146     */
2147     uint32_t memoryTypeBits;
2148     /** \brief Pool that this allocation should be created in.
2149 
2150     Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members:
2151     `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.
2152     */
2153     VmaPool pool;
2154     /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().
2155 
2156     If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either
2157     null or pointer to a null-terminated string. The string will be then copied to
2158     internal buffer, so it doesn't need to be valid after allocation call.
2159     */
2160     void* pUserData;
2161 } VmaAllocationCreateInfo;
2162 
2163 /**
2164 \brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.
2165 
2166 This algorithm tries to find a memory type that:
2167 
2168 - Is allowed by memoryTypeBits.
2169 - Contains all the flags from pAllocationCreateInfo->requiredFlags.
2170 - Matches intended usage.
2171 - Has as many flags from pAllocationCreateInfo->preferredFlags as possible.
2172 
2173 \return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result
2174 from this function or any other allocating function probably means that your
2175 device doesn't support any memory type with requested features for the specific
2176 type of resource you want to use it for. Please check parameters of your
2177 resource, like image layout (OPTIMAL versus LINEAR) or mip level count.
2178 */
2179 VkResult vmaFindMemoryTypeIndex(
2180     VmaAllocator allocator,
2181     uint32_t memoryTypeBits,
2182     const VmaAllocationCreateInfo* pAllocationCreateInfo,
2183     uint32_t* pMemoryTypeIndex);
2184 
2185 /**
2186 \brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.
2187 
2188 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
2189 It internally creates a temporary, dummy buffer that never has memory bound.
2190 It is just a convenience function, equivalent to calling:
2191 
2192 - `vkCreateBuffer`
2193 - `vkGetBufferMemoryRequirements`
2194 - `vmaFindMemoryTypeIndex`
2195 - `vkDestroyBuffer`
2196 */
2197 VkResult vmaFindMemoryTypeIndexForBufferInfo(
2198     VmaAllocator allocator,
2199     const VkBufferCreateInfo* pBufferCreateInfo,
2200     const VmaAllocationCreateInfo* pAllocationCreateInfo,
2201     uint32_t* pMemoryTypeIndex);
2202 
2203 /**
2204 \brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.
2205 
2206 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
2207 It internally creates a temporary, dummy image that never has memory bound.
2208 It is just a convenience function, equivalent to calling:
2209 
2210 - `vkCreateImage`
2211 - `vkGetImageMemoryRequirements`
2212 - `vmaFindMemoryTypeIndex`
2213 - `vkDestroyImage`
2214 */
2215 VkResult vmaFindMemoryTypeIndexForImageInfo(
2216     VmaAllocator allocator,
2217     const VkImageCreateInfo* pImageCreateInfo,
2218     const VmaAllocationCreateInfo* pAllocationCreateInfo,
2219     uint32_t* pMemoryTypeIndex);
2220 
2221 /// Flags to be passed as VmaPoolCreateInfo::flags.
2222 typedef enum VmaPoolCreateFlagBits {
2223     /** \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.
2224 
2225     This is an optional optimization flag.
2226 
2227     If you always allocate using vmaCreateBuffer(), vmaCreateImage(),
2228     vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator
2229     knows exact type of your allocations so it can handle Buffer-Image Granularity
2230     in the optimal way.
2231 
2232     If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(),
2233     exact type of such allocations is not known, so allocator must be conservative
2234     in handling Buffer-Image Granularity, which can lead to suboptimal allocation
2235     (wasted memory). In that case, if you can make sure you always allocate only
2236     buffers and linear images or only optimal images out of this pool, use this flag
2237     to make allocator disregard Buffer-Image Granularity and so make allocations
2238     faster and more optimal.
2239     */
2240     VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002,
2241 
2242     /** \brief Enables alternative, linear allocation algorithm in this pool.
2243 
2244     Specify this flag to enable linear allocation algorithm, which always creates
2245     new allocations after last one and doesn't reuse space from allocations freed in
2246     between. It trades memory consumption for simplified algorithm and data
2247     structure, which has better performance and uses less memory for metadata.
2248 
2249     By using this flag, you can achieve behavior of free-at-once, stack,
2250     ring buffer, and double stack. For details, see documentation chapter
2251     \ref linear_algorithm.
2252 
2253     When using this flag, you must specify VmaPoolCreateInfo::maxBlockCount == 1 (or 0 for default).
2254 
2255     For more details, see [Linear allocation algorithm](@ref linear_algorithm).
2256     */
2257     VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004,
2258 
2259     /** \brief Enables alternative, buddy allocation algorithm in this pool.
2260 
2261     It operates on a tree of blocks, each having size that is a power of two and
2262     a half of its parent's size. Comparing to default algorithm, this one provides
2263     faster allocation and deallocation and decreased external fragmentation,
2264     at the expense of more memory wasted (internal fragmentation).
2265 
2266     For more details, see [Buddy allocation algorithm](@ref buddy_algorithm).
2267     */
2268     VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT = 0x00000008,
2269 
2270     /** Bit mask to extract only `ALGORITHM` bits from entire set of flags.
2271     */
2272     VMA_POOL_CREATE_ALGORITHM_MASK =
2273         VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT |
2274         VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT,
2275 
2276     VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2277 } VmaPoolCreateFlagBits;
2278 typedef VkFlags VmaPoolCreateFlags;
2279 
2280 /** \brief Describes parameter of created #VmaPool.
2281 */
2282 typedef struct VmaPoolCreateInfo {
2283     /** \brief Vulkan memory type index to allocate this pool from.
2284     */
2285     uint32_t memoryTypeIndex;
2286     /** \brief Use combination of #VmaPoolCreateFlagBits.
2287     */
2288     VmaPoolCreateFlags flags;
2289     /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. Optional.
2290 
2291     Specify nonzero to set explicit, constant size of memory blocks used by this
2292     pool.
2293 
2294     Leave 0 to use default and let the library manage block sizes automatically.
2295     Sizes of particular blocks may vary.
2296     */
2297     VkDeviceSize blockSize;
2298     /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty.
2299 
2300     Set to 0 to have no preallocated blocks and allow the pool be completely empty.
2301     */
2302     size_t minBlockCount;
2303     /** \brief Maximum number of blocks that can be allocated in this pool. Optional.
2304 
2305     Set to 0 to use default, which is `SIZE_MAX`, which means no limit.
2306 
2307     Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated
2308     throughout whole lifetime of this pool.
2309     */
2310     size_t maxBlockCount;
2311     /** \brief Maximum number of additional frames that are in use at the same time as current frame.
2312 
2313     This value is used only when you make allocations with
2314     #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
2315     lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
2316 
2317     For example, if you double-buffer your command buffers, so resources used for
2318     rendering in previous frame may still be in use by the GPU at the moment you
2319     allocate resources needed for the current frame, set this value to 1.
2320 
2321     If you want to allow any allocations other than used in the current frame to
2322     become lost, set this value to 0.
2323     */
2324     uint32_t frameInUseCount;
2325 } VmaPoolCreateInfo;
2326 
2327 /** \brief Describes parameter of existing #VmaPool.
2328 */
2329 typedef struct VmaPoolStats {
2330     /** \brief Total amount of `VkDeviceMemory` allocated from Vulkan for this pool, in bytes.
2331     */
2332     VkDeviceSize size;
2333     /** \brief Total number of bytes in the pool not used by any #VmaAllocation.
2334     */
2335     VkDeviceSize unusedSize;
2336     /** \brief Number of #VmaAllocation objects created from this pool that were not destroyed or lost.
2337     */
2338     size_t allocationCount;
2339     /** \brief Number of continuous memory ranges in the pool not used by any #VmaAllocation.
2340     */
2341     size_t unusedRangeCount;
2342     /** \brief Size of the largest continuous free memory region available for new allocation.
2343 
2344     Making a new allocation of that size is not guaranteed to succeed because of
2345     possible additional margin required to respect alignment and buffer/image
2346     granularity.
2347     */
2348     VkDeviceSize unusedRangeSizeMax;
2349     /** \brief Number of `VkDeviceMemory` blocks allocated for this pool.
2350     */
2351     size_t blockCount;
2352 } VmaPoolStats;
2353 
2354 /** \brief Allocates Vulkan device memory and creates #VmaPool object.
2355 
2356 @param allocator Allocator object.
2357 @param pCreateInfo Parameters of pool to create.
2358 @param[out] pPool Handle to created pool.
2359 */
2360 VkResult vmaCreatePool(
2361 	VmaAllocator allocator,
2362 	const VmaPoolCreateInfo* pCreateInfo,
2363 	VmaPool* pPool);
2364 
2365 /** \brief Destroys #VmaPool object and frees Vulkan device memory.
2366 */
2367 void vmaDestroyPool(
2368     VmaAllocator allocator,
2369     VmaPool pool);
2370 
2371 /** \brief Retrieves statistics of existing #VmaPool object.
2372 
2373 @param allocator Allocator object.
2374 @param pool Pool object.
2375 @param[out] pPoolStats Statistics of specified pool.
2376 */
2377 void vmaGetPoolStats(
2378     VmaAllocator allocator,
2379     VmaPool pool,
2380     VmaPoolStats* pPoolStats);
2381 
2382 /** \brief Marks all allocations in given pool as lost if they are not used in current frame or VmaPoolCreateInfo::frameInUseCount back from now.
2383 
2384 @param allocator Allocator object.
2385 @param pool Pool.
2386 @param[out] pLostAllocationCount Number of allocations marked as lost. Optional - pass null if you don't need this information.
2387 */
2388 void vmaMakePoolAllocationsLost(
2389     VmaAllocator allocator,
2390     VmaPool pool,
2391     size_t* pLostAllocationCount);
2392 
2393 /** \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions.
2394 
2395 Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
2396 `VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is
2397 `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
2398 
2399 Possible return values:
2400 
2401 - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool.
2402 - `VK_SUCCESS` - corruption detection has been performed and succeeded.
2403 - `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
2404   `VMA_ASSERT` is also fired in that case.
2405 - Other value: Error returned by Vulkan, e.g. memory mapping failure.
2406 */
2407 VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool);
2408 
2409 /** \struct VmaAllocation
2410 \brief Represents single memory allocation.
2411 
2412 It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type
2413 plus unique offset.
2414 
2415 There are multiple ways to create such object.
2416 You need to fill structure VmaAllocationCreateInfo.
2417 For more information see [Choosing memory type](@ref choosing_memory_type).
2418 
2419 Although the library provides convenience functions that create Vulkan buffer or image,
2420 allocate memory for it and bind them together,
2421 binding of the allocation to a buffer or an image is out of scope of the allocation itself.
2422 Allocation object can exist without buffer/image bound,
2423 binding can be done manually by the user, and destruction of it can be done
2424 independently of destruction of the allocation.
2425 
2426 The object also remembers its size and some other information.
2427 To retrieve this information, use function vmaGetAllocationInfo() and inspect
2428 returned structure VmaAllocationInfo.
2429 
2430 Some kinds allocations can be in lost state.
2431 For more information, see [Lost allocations](@ref lost_allocations).
2432 */
2433 VK_DEFINE_HANDLE(VmaAllocation)
2434 
2435 /** \brief Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
2436 */
2437 typedef struct VmaAllocationInfo {
2438     /** \brief Memory type index that this allocation was allocated from.
2439 
2440     It never changes.
2441     */
2442     uint32_t memoryType;
2443     /** \brief Handle to Vulkan memory object.
2444 
2445     Same memory object can be shared by multiple allocations.
2446 
2447     It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
2448 
2449     If the allocation is lost, it is equal to `VK_NULL_HANDLE`.
2450     */
2451     VkDeviceMemory deviceMemory;
2452     /** \brief Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation.
2453 
2454     It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
2455     */
2456     VkDeviceSize offset;
2457     /** \brief Size of this allocation, in bytes.
2458 
2459     It never changes, unless allocation is lost.
2460     */
2461     VkDeviceSize size;
2462     /** \brief Pointer to the beginning of this allocation as mapped data.
2463 
2464     If the allocation hasn't been mapped using vmaMapMemory() and hasn't been
2465     created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value null.
2466 
2467     It can change after call to vmaMapMemory(), vmaUnmapMemory().
2468     It can also change after call to vmaDefragment() if this allocation is passed to the function.
2469     */
2470     void* pMappedData;
2471     /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData().
2472 
2473     It can change after call to vmaSetAllocationUserData() for this allocation.
2474     */
2475     void* pUserData;
2476 } VmaAllocationInfo;
2477 
2478 /** \brief General purpose memory allocation.
2479 
2480 @param[out] pAllocation Handle to allocated memory.
2481 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
2482 
2483 You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
2484 
2485 It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(),
2486 vmaCreateBuffer(), vmaCreateImage() instead whenever possible.
2487 */
2488 VkResult vmaAllocateMemory(
2489     VmaAllocator allocator,
2490     const VkMemoryRequirements* pVkMemoryRequirements,
2491     const VmaAllocationCreateInfo* pCreateInfo,
2492     VmaAllocation* pAllocation,
2493     VmaAllocationInfo* pAllocationInfo);
2494 
2495 /** \brief General purpose memory allocation for multiple allocation objects at once.
2496 
2497 @param allocator Allocator object.
2498 @param pVkMemoryRequirements Memory requirements for each allocation.
2499 @param pCreateInfo Creation parameters for each alloction.
2500 @param allocationCount Number of allocations to make.
2501 @param[out] pAllocations Pointer to array that will be filled with handles to created allocations.
2502 @param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations.
2503 
2504 You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
2505 
2506 Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding.
2507 It is just a general purpose allocation function able to make multiple allocations at once.
2508 It may be internally optimized to be more efficient than calling vmaAllocateMemory() `allocationCount` times.
2509 
2510 All allocations are made using same parameters. All of them are created out of the same memory pool and type.
2511 If any allocation fails, all allocations already made within this function call are also freed, so that when
2512 returned result is not `VK_SUCCESS`, `pAllocation` array is always entirely filled with `VK_NULL_HANDLE`.
2513 */
2514 VkResult vmaAllocateMemoryPages(
2515     VmaAllocator allocator,
2516     const VkMemoryRequirements* pVkMemoryRequirements,
2517     const VmaAllocationCreateInfo* pCreateInfo,
2518     size_t allocationCount,
2519     VmaAllocation* pAllocations,
2520     VmaAllocationInfo* pAllocationInfo);
2521 
2522 /**
2523 @param[out] pAllocation Handle to allocated memory.
2524 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
2525 
2526 You should free the memory using vmaFreeMemory().
2527 */
2528 VkResult vmaAllocateMemoryForBuffer(
2529     VmaAllocator allocator,
2530     VkBuffer buffer,
2531     const VmaAllocationCreateInfo* pCreateInfo,
2532     VmaAllocation* pAllocation,
2533     VmaAllocationInfo* pAllocationInfo);
2534 
2535 /// Function similar to vmaAllocateMemoryForBuffer().
2536 VkResult vmaAllocateMemoryForImage(
2537     VmaAllocator allocator,
2538     VkImage image,
2539     const VmaAllocationCreateInfo* pCreateInfo,
2540     VmaAllocation* pAllocation,
2541     VmaAllocationInfo* pAllocationInfo);
2542 
2543 /** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().
2544 
2545 Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped.
2546 */
2547 void vmaFreeMemory(
2548     VmaAllocator allocator,
2549     VmaAllocation allocation);
2550 
2551 /** \brief Frees memory and destroys multiple allocations.
2552 
2553 Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding.
2554 It is just a general purpose function to free memory and destroy allocations made using e.g. vmaAllocateMemory(),
2555 vmaAllocateMemoryPages() and other functions.
2556 It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times.
2557 
2558 Allocations in `pAllocations` array can come from any memory pools and types.
2559 Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped.
2560 */
2561 void vmaFreeMemoryPages(
2562     VmaAllocator allocator,
2563     size_t allocationCount,
2564     VmaAllocation* pAllocations);
2565 
2566 /** \brief Tries to resize an allocation in place, if there is enough free memory after it.
2567 
2568 Tries to change allocation's size without moving or reallocating it.
2569 You can both shrink and grow allocation size.
2570 When growing, it succeeds only when the allocation belongs to a memory block with enough
2571 free space after it.
2572 
2573 Returns `VK_SUCCESS` if allocation's size has been successfully changed.
2574 Returns `VK_ERROR_OUT_OF_POOL_MEMORY` if allocation's size could not be changed.
2575 
2576 After successful call to this function, VmaAllocationInfo::size of this allocation changes.
2577 All other parameters stay the same: memory pool and type, alignment, offset, mapped pointer.
2578 
2579 - Calling this function on allocation that is in lost state fails with result `VK_ERROR_VALIDATION_FAILED_EXT`.
2580 - Calling this function with `newSize` same as current allocation size does nothing and returns `VK_SUCCESS`.
2581 - Resizing dedicated allocations, as well as allocations created in pools that use linear
2582   or buddy algorithm, is not supported.
2583   The function returns `VK_ERROR_FEATURE_NOT_PRESENT` in such cases.
2584   Support may be added in the future.
2585 */
2586 VkResult vmaResizeAllocation(
2587     VmaAllocator allocator,
2588     VmaAllocation allocation,
2589     VkDeviceSize newSize);
2590 
2591 /** \brief Returns current information about specified allocation and atomically marks it as used in current frame.
2592 
2593 Current paramters of given allocation are returned in `pAllocationInfo`.
2594 
2595 This function also atomically "touches" allocation - marks it as used in current frame,
2596 just like vmaTouchAllocation().
2597 If the allocation is in lost state, `pAllocationInfo->deviceMemory == VK_NULL_HANDLE`.
2598 
2599 Although this function uses atomics and doesn't lock any mutex, so it should be quite efficient,
2600 you can avoid calling it too often.
2601 
2602 - You can retrieve same VmaAllocationInfo structure while creating your resource, from function
2603   vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change
2604   (e.g. due to defragmentation or allocation becoming lost).
2605 - If you just want to check if allocation is not lost, vmaTouchAllocation() will work faster.
2606 */
2607 void vmaGetAllocationInfo(
2608     VmaAllocator allocator,
2609     VmaAllocation allocation,
2610     VmaAllocationInfo* pAllocationInfo);
2611 
2612 /** \brief Returns `VK_TRUE` if allocation is not lost and atomically marks it as used in current frame.
2613 
2614 If the allocation has been created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
2615 this function returns `VK_TRUE` if it's not in lost state, so it can still be used.
2616 It then also atomically "touches" the allocation - marks it as used in current frame,
2617 so that you can be sure it won't become lost in current frame or next `frameInUseCount` frames.
2618 
2619 If the allocation is in lost state, the function returns `VK_FALSE`.
2620 Memory of such allocation, as well as buffer or image bound to it, should not be used.
2621 Lost allocation and the buffer/image still need to be destroyed.
2622 
2623 If the allocation has been created without #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
2624 this function always returns `VK_TRUE`.
2625 */
2626 VkBool32 vmaTouchAllocation(
2627     VmaAllocator allocator,
2628     VmaAllocation allocation);
2629 
2630 /** \brief Sets pUserData in given allocation to new value.
2631 
2632 If the allocation was created with VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT,
2633 pUserData must be either null, or pointer to a null-terminated string. The function
2634 makes local copy of the string and sets it as allocation's `pUserData`. String
2635 passed as pUserData doesn't need to be valid for whole lifetime of the allocation -
2636 you can free it after this call. String previously pointed by allocation's
2637 pUserData is freed from memory.
2638 
2639 If the flag was not used, the value of pointer `pUserData` is just copied to
2640 allocation's `pUserData`. It is opaque, so you can use it however you want - e.g.
2641 as a pointer, ordinal number or some handle to you own data.
2642 */
2643 void vmaSetAllocationUserData(
2644     VmaAllocator allocator,
2645     VmaAllocation allocation,
2646     void* pUserData);
2647 
2648 /** \brief Creates new allocation that is in lost state from the beginning.
2649 
2650 It can be useful if you need a dummy, non-null allocation.
2651 
2652 You still need to destroy created object using vmaFreeMemory().
2653 
2654 Returned allocation is not tied to any specific memory pool or memory type and
2655 not bound to any image or buffer. It has size = 0. It cannot be turned into
2656 a real, non-empty allocation.
2657 */
2658 void vmaCreateLostAllocation(
2659     VmaAllocator allocator,
2660     VmaAllocation* pAllocation);
2661 
2662 /** \brief Maps memory represented by given allocation and returns pointer to it.
2663 
2664 Maps memory represented by given allocation to make it accessible to CPU code.
2665 When succeeded, `*ppData` contains pointer to first byte of this memory.
2666 If the allocation is part of bigger `VkDeviceMemory` block, the pointer is
2667 correctly offseted to the beginning of region assigned to this particular
2668 allocation.
2669 
2670 Mapping is internally reference-counted and synchronized, so despite raw Vulkan
2671 function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory`
2672 multiple times simultaneously, it is safe to call this function on allocations
2673 assigned to the same memory block. Actual Vulkan memory will be mapped on first
2674 mapping and unmapped on last unmapping.
2675 
2676 If the function succeeded, you must call vmaUnmapMemory() to unmap the
2677 allocation when mapping is no longer needed or before freeing the allocation, at
2678 the latest.
2679 
2680 It also safe to call this function multiple times on the same allocation. You
2681 must call vmaUnmapMemory() same number of times as you called vmaMapMemory().
2682 
2683 It is also safe to call this function on allocation created with
2684 #VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time.
2685 You must still call vmaUnmapMemory() same number of times as you called
2686 vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the
2687 "0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag.
2688 
2689 This function fails when used on allocation made in memory type that is not
2690 `HOST_VISIBLE`.
2691 
2692 This function always fails when called for allocation that was created with
2693 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocations cannot be
2694 mapped.
2695 */
2696 VkResult vmaMapMemory(
2697     VmaAllocator allocator,
2698     VmaAllocation allocation,
2699     void** ppData);
2700 
2701 /** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
2702 
2703 For details, see description of vmaMapMemory().
2704 */
2705 void vmaUnmapMemory(
2706     VmaAllocator allocator,
2707     VmaAllocation allocation);
2708 
2709 /** \brief Flushes memory of given allocation.
2710 
2711 Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation.
2712 
2713 - `offset` must be relative to the beginning of allocation.
2714 - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
2715 - `offset` and `size` don't have to be aligned.
2716   They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
2717 - If `size` is 0, this call is ignored.
2718 - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
2719   this call is ignored.
2720 */
2721 void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
2722 
2723 /** \brief Invalidates memory of given allocation.
2724 
2725 Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation.
2726 
2727 - `offset` must be relative to the beginning of allocation.
2728 - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
2729 - `offset` and `size` don't have to be aligned.
2730   They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
2731 - If `size` is 0, this call is ignored.
2732 - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
2733   this call is ignored.
2734 */
2735 void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
2736 
2737 /** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions.
2738 
2739 @param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked.
2740 
2741 Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
2742 `VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are
2743 `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
2744 
2745 Possible return values:
2746 
2747 - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types.
2748 - `VK_SUCCESS` - corruption detection has been performed and succeeded.
2749 - `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
2750   `VMA_ASSERT` is also fired in that case.
2751 - Other value: Error returned by Vulkan, e.g. memory mapping failure.
2752 */
2753 VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits);
2754 
2755 /** \struct VmaDefragmentationContext
2756 \brief Represents Opaque object that represents started defragmentation process.
2757 
2758 Fill structure #VmaDefragmentationInfo2 and call function vmaDefragmentationBegin() to create it.
2759 Call function vmaDefragmentationEnd() to destroy it.
2760 */
2761 VK_DEFINE_HANDLE(VmaDefragmentationContext)
2762 
2763 /// Flags to be used in vmaDefragmentationBegin(). None at the moment. Reserved for future use.
2764 typedef enum VmaDefragmentationFlagBits {
2765     VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2766 } VmaDefragmentationFlagBits;
2767 typedef VkFlags VmaDefragmentationFlags;
2768 
2769 /** \brief Parameters for defragmentation.
2770 
2771 To be used with function vmaDefragmentationBegin().
2772 */
2773 typedef struct VmaDefragmentationInfo2 {
2774     /** \brief Reserved for future use. Should be 0.
2775     */
2776     VmaDefragmentationFlags flags;
2777     /** \brief Number of allocations in `pAllocations` array.
2778     */
2779     uint32_t allocationCount;
2780     /** \brief Pointer to array of allocations that can be defragmented.
2781 
2782     The array should have `allocationCount` elements.
2783     The array should not contain nulls.
2784     Elements in the array should be unique - same allocation cannot occur twice.
2785     It is safe to pass allocations that are in the lost state - they are ignored.
2786     All allocations not present in this array are considered non-moveable during this defragmentation.
2787     */
2788     VmaAllocation* pAllocations;
2789     /** \brief Optional, output. Pointer to array that will be filled with information whether the allocation at certain index has been changed during defragmentation.
2790 
2791     The array should have `allocationCount` elements.
2792     You can pass null if you are not interested in this information.
2793     */
2794     VkBool32* pAllocationsChanged;
2795     /** \brief Numer of pools in `pPools` array.
2796     */
2797     uint32_t poolCount;
2798     /** \brief Either null or pointer to array of pools to be defragmented.
2799 
2800     All the allocations in the specified pools can be moved during defragmentation
2801     and there is no way to check if they were really moved as in `pAllocationsChanged`,
2802     so you must query all the allocations in all these pools for new `VkDeviceMemory`
2803     and offset using vmaGetAllocationInfo() if you might need to recreate buffers
2804     and images bound to them.
2805 
2806     The array should have `poolCount` elements.
2807     The array should not contain nulls.
2808     Elements in the array should be unique - same pool cannot occur twice.
2809 
2810     Using this array is equivalent to specifying all allocations from the pools in `pAllocations`.
2811     It might be more efficient.
2812     */
2813     VmaPool* pPools;
2814     /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on CPU side, like `memcpy()`, `memmove()`.
2815 
2816     `VK_WHOLE_SIZE` means no limit.
2817     */
2818     VkDeviceSize maxCpuBytesToMove;
2819     /** \brief Maximum number of allocations that can be moved to a different place using transfers on CPU side, like `memcpy()`, `memmove()`.
2820 
2821     `UINT32_MAX` means no limit.
2822     */
2823     uint32_t maxCpuAllocationsToMove;
2824     /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on GPU side, posted to `commandBuffer`.
2825 
2826     `VK_WHOLE_SIZE` means no limit.
2827     */
2828     VkDeviceSize maxGpuBytesToMove;
2829     /** \brief Maximum number of allocations that can be moved to a different place using transfers on GPU side, posted to `commandBuffer`.
2830 
2831     `UINT32_MAX` means no limit.
2832     */
2833     uint32_t maxGpuAllocationsToMove;
2834     /** \brief Optional. Command buffer where GPU copy commands will be posted.
2835 
2836     If not null, it must be a valid command buffer handle that supports Transfer queue type.
2837     It must be in the recording state and outside of a render pass instance.
2838     You need to submit it and make sure it finished execution before calling vmaDefragmentationEnd().
2839 
2840     Passing null means that only CPU defragmentation will be performed.
2841     */
2842     VkCommandBuffer commandBuffer;
2843 } VmaDefragmentationInfo2;
2844 
2845 /** \brief Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
2846 
2847 \deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
2848 */
2849 typedef struct VmaDefragmentationInfo {
2850     /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places.
2851 
2852     Default is `VK_WHOLE_SIZE`, which means no limit.
2853     */
2854     VkDeviceSize maxBytesToMove;
2855     /** \brief Maximum number of allocations that can be moved to different place.
2856 
2857     Default is `UINT32_MAX`, which means no limit.
2858     */
2859     uint32_t maxAllocationsToMove;
2860 } VmaDefragmentationInfo;
2861 
2862 /** \brief Statistics returned by function vmaDefragment(). */
2863 typedef struct VmaDefragmentationStats {
2864     /// Total number of bytes that have been copied while moving allocations to different places.
2865     VkDeviceSize bytesMoved;
2866     /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects.
2867     VkDeviceSize bytesFreed;
2868     /// Number of allocations that have been moved to different places.
2869     uint32_t allocationsMoved;
2870     /// Number of empty `VkDeviceMemory` objects that have been released to the system.
2871     uint32_t deviceMemoryBlocksFreed;
2872 } VmaDefragmentationStats;
2873 
2874 /** \brief Begins defragmentation process.
2875 
2876 @param allocator Allocator object.
2877 @param pInfo Structure filled with parameters of defragmentation.
2878 @param[out] pStats Optional. Statistics of defragmentation. You can pass null if you are not interested in this information.
2879 @param[out] pContext Context object that must be passed to vmaDefragmentationEnd() to finish defragmentation.
2880 @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.
2881 
2882 Use this function instead of old, deprecated vmaDefragment().
2883 
2884 Warning! Between the call to vmaDefragmentationBegin() and vmaDefragmentationEnd():
2885 
2886 - You should not use any of allocations passed as `pInfo->pAllocations` or
2887   any allocations that belong to pools passed as `pInfo->pPools`,
2888   including calling vmaGetAllocationInfo(), vmaTouchAllocation(), or access
2889   their data.
2890 - Some mutexes protecting internal data structures may be locked, so trying to
2891   make or free any allocations, bind buffers or images, map memory, or launch
2892   another simultaneous defragmentation in between may cause stall (when done on
2893   another thread) or deadlock (when done on the same thread), unless you are
2894   100% sure that defragmented allocations are in different pools.
2895 - Information returned via `pStats` and `pInfo->pAllocationsChanged` are undefined.
2896   They become valid after call to vmaDefragmentationEnd().
2897 - If `pInfo->commandBuffer` is not null, you must submit that command buffer
2898   and make sure it finished execution before calling vmaDefragmentationEnd().
2899 */
2900 VkResult vmaDefragmentationBegin(
2901     VmaAllocator allocator,
2902     const VmaDefragmentationInfo2* pInfo,
2903     VmaDefragmentationStats* pStats,
2904     VmaDefragmentationContext *pContext);
2905 
2906 /** \brief Ends defragmentation process.
2907 
2908 Use this function to finish defragmentation started by vmaDefragmentationBegin().
2909 It is safe to pass `context == null`. The function then does nothing.
2910 */
2911 VkResult vmaDefragmentationEnd(
2912     VmaAllocator allocator,
2913     VmaDefragmentationContext context);
2914 
2915 /** \brief Deprecated. Compacts memory by moving allocations.
2916 
2917 @param pAllocations Array of allocations that can be moved during this compation.
2918 @param allocationCount Number of elements in pAllocations and pAllocationsChanged arrays.
2919 @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.
2920 @param pDefragmentationInfo Configuration parameters. Optional - pass null to use default values.
2921 @param[out] pDefragmentationStats Statistics returned by the function. Optional - pass null if you don't need this information.
2922 @return `VK_SUCCESS` if completed, negative error code in case of error.
2923 
2924 \deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
2925 
2926 This function works by moving allocations to different places (different
2927 `VkDeviceMemory` objects and/or different offsets) in order to optimize memory
2928 usage. Only allocations that are in `pAllocations` array can be moved. All other
2929 allocations are considered nonmovable in this call. Basic rules:
2930 
2931 - Only allocations made in memory types that have
2932   `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`
2933   flags can be compacted. You may pass other allocations but it makes no sense -
2934   these will never be moved.
2935 - Custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT or
2936   #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag are not defragmented. Allocations
2937   passed to this function that come from such pools are ignored.
2938 - Allocations created with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT or
2939   created as dedicated allocations for any other reason are also ignored.
2940 - Both allocations made with or without #VMA_ALLOCATION_CREATE_MAPPED_BIT
2941   flag can be compacted. If not persistently mapped, memory will be mapped
2942   temporarily inside this function if needed.
2943 - You must not pass same #VmaAllocation object multiple times in `pAllocations` array.
2944 
2945 The function also frees empty `VkDeviceMemory` blocks.
2946 
2947 Warning: This function may be time-consuming, so you shouldn't call it too often
2948 (like after every resource creation/destruction).
2949 You can call it on special occasions (like when reloading a game level or
2950 when you just destroyed a lot of objects). Calling it every frame may be OK, but
2951 you should measure that on your platform.
2952 
2953 For more information, see [Defragmentation](@ref defragmentation) chapter.
2954 */
2955 VkResult vmaDefragment(
2956     VmaAllocator allocator,
2957     VmaAllocation* pAllocations,
2958     size_t allocationCount,
2959     VkBool32* pAllocationsChanged,
2960     const VmaDefragmentationInfo *pDefragmentationInfo,
2961     VmaDefragmentationStats* pDefragmentationStats);
2962 
2963 /** \brief Binds buffer to allocation.
2964 
2965 Binds specified buffer to region of memory represented by specified allocation.
2966 Gets `VkDeviceMemory` handle and offset from the allocation.
2967 If you want to create a buffer, allocate memory for it and bind them together separately,
2968 you should use this function for binding instead of standard `vkBindBufferMemory()`,
2969 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
2970 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
2971 (which is illegal in Vulkan).
2972 
2973 It is recommended to use function vmaCreateBuffer() instead of this one.
2974 */
2975 VkResult vmaBindBufferMemory(
2976     VmaAllocator allocator,
2977     VmaAllocation allocation,
2978     VkBuffer buffer);
2979 
2980 /** \brief Binds image to allocation.
2981 
2982 Binds specified image to region of memory represented by specified allocation.
2983 Gets `VkDeviceMemory` handle and offset from the allocation.
2984 If you want to create an image, allocate memory for it and bind them together separately,
2985 you should use this function for binding instead of standard `vkBindImageMemory()`,
2986 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
2987 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
2988 (which is illegal in Vulkan).
2989 
2990 It is recommended to use function vmaCreateImage() instead of this one.
2991 */
2992 VkResult vmaBindImageMemory(
2993     VmaAllocator allocator,
2994     VmaAllocation allocation,
2995     VkImage image);
2996 
2997 /**
2998 @param[out] pBuffer Buffer that was created.
2999 @param[out] pAllocation Allocation that was created.
3000 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
3001 
3002 This function automatically:
3003 
3004 -# Creates buffer.
3005 -# Allocates appropriate memory for it.
3006 -# Binds the buffer with the memory.
3007 
3008 If any of these operations fail, buffer and allocation are not created,
3009 returned value is negative error code, *pBuffer and *pAllocation are null.
3010 
3011 If the function succeeded, you must destroy both buffer and allocation when you
3012 no longer need them using either convenience function vmaDestroyBuffer() or
3013 separately, using `vkDestroyBuffer()` and vmaFreeMemory().
3014 
3015 If VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used,
3016 VK_KHR_dedicated_allocation extension is used internally to query driver whether
3017 it requires or prefers the new buffer to have dedicated allocation. If yes,
3018 and if dedicated allocation is possible (VmaAllocationCreateInfo::pool is null
3019 and VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated
3020 allocation for this buffer, just like when using
3021 VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
3022 */
3023 VkResult vmaCreateBuffer(
3024     VmaAllocator allocator,
3025     const VkBufferCreateInfo* pBufferCreateInfo,
3026     const VmaAllocationCreateInfo* pAllocationCreateInfo,
3027     VkBuffer* pBuffer,
3028     VmaAllocation* pAllocation,
3029     VmaAllocationInfo* pAllocationInfo);
3030 
3031 /** \brief Destroys Vulkan buffer and frees allocated memory.
3032 
3033 This is just a convenience function equivalent to:
3034 
3035 \code
3036 vkDestroyBuffer(device, buffer, allocationCallbacks);
3037 vmaFreeMemory(allocator, allocation);
3038 \endcode
3039 
3040 It it safe to pass null as buffer and/or allocation.
3041 */
3042 void vmaDestroyBuffer(
3043     VmaAllocator allocator,
3044     VkBuffer buffer,
3045     VmaAllocation allocation);
3046 
3047 /// Function similar to vmaCreateBuffer().
3048 VkResult vmaCreateImage(
3049     VmaAllocator allocator,
3050     const VkImageCreateInfo* pImageCreateInfo,
3051     const VmaAllocationCreateInfo* pAllocationCreateInfo,
3052     VkImage* pImage,
3053     VmaAllocation* pAllocation,
3054     VmaAllocationInfo* pAllocationInfo);
3055 
3056 /** \brief Destroys Vulkan image and frees allocated memory.
3057 
3058 This is just a convenience function equivalent to:
3059 
3060 \code
3061 vkDestroyImage(device, image, allocationCallbacks);
3062 vmaFreeMemory(allocator, allocation);
3063 \endcode
3064 
3065 It it safe to pass null as image and/or allocation.
3066 */
3067 void vmaDestroyImage(
3068     VmaAllocator allocator,
3069     VkImage image,
3070     VmaAllocation allocation);
3071 
3072 #ifdef __cplusplus
3073 }
3074 #endif
3075 
3076 #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
3077 
3078 // For Visual Studio IntelliSense.
3079 #if defined(__cplusplus) && defined(__INTELLISENSE__)
3080 #define VMA_IMPLEMENTATION
3081 #endif
3082 
3083 #ifdef VMA_IMPLEMENTATION
3084 #undef VMA_IMPLEMENTATION
3085 
3086 #include <cstdint>
3087 #include <cstdlib>
3088 #include <cstring>
3089 
3090 /*******************************************************************************
3091 CONFIGURATION SECTION
3092 
3093 Define some of these macros before each #include of this header or change them
3094 here if you need other then default behavior depending on your environment.
3095 */
3096 
3097 /*
3098 Define this macro to 1 to make the library fetch pointers to Vulkan functions
3099 internally, like:
3100 
3101     vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
3102 
3103 Define to 0 if you are going to provide you own pointers to Vulkan functions via
3104 VmaAllocatorCreateInfo::pVulkanFunctions.
3105 */
3106 #if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
3107 #define VMA_STATIC_VULKAN_FUNCTIONS 1
3108 #endif
3109 
3110 // Define this macro to 1 to make the library use STL containers instead of its own implementation.
3111 //#define VMA_USE_STL_CONTAINERS 1
3112 
3113 /* Set this macro to 1 to make the library including and using STL containers:
3114 std::pair, std::vector, std::list, std::unordered_map.
3115 
3116 Set it to 0 or undefined to make the library using its own implementation of
3117 the containers.
3118 */
3119 #if VMA_USE_STL_CONTAINERS
3120    #define VMA_USE_STL_VECTOR 1
3121    #define VMA_USE_STL_UNORDERED_MAP 1
3122    #define VMA_USE_STL_LIST 1
3123 #endif
3124 
3125 #ifndef VMA_USE_STL_SHARED_MUTEX
3126     // Minimum Visual Studio 2015 Update 2
3127     #if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918
3128         #define VMA_USE_STL_SHARED_MUTEX 1
3129     #endif
3130 #endif
3131 
3132 #if VMA_USE_STL_VECTOR
3133    #include <vector>
3134 #endif
3135 
3136 #if VMA_USE_STL_UNORDERED_MAP
3137    #include <unordered_map>
3138 #endif
3139 
3140 #if VMA_USE_STL_LIST
3141    #include <list>
3142 #endif
3143 
3144 /*
3145 Following headers are used in this CONFIGURATION section only, so feel free to
3146 remove them if not needed.
3147 */
3148 #include <cassert> // for assert
3149 #include <algorithm> // for min, max
3150 #include <mutex>
3151 #include <atomic> // for std::atomic
3152 
3153 #ifndef VMA_NULL
3154    // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
3155    #define VMA_NULL   nullptr
3156 #endif
3157 
3158 #if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
3159 #include <cstdlib>
aligned_alloc(size_t alignment,size_t size)3160 void *aligned_alloc(size_t alignment, size_t size)
3161 {
3162     // alignment must be >= sizeof(void*)
3163     if(alignment < sizeof(void*))
3164     {
3165         alignment = sizeof(void*);
3166     }
3167 
3168     return memalign(alignment, size);
3169 }
3170 #elif defined(__ANDROID__)
3171 #include <cstdlib>
aligned_alloc(size_t alignment,size_t size)3172 void *aligned_alloc(size_t alignment, size_t size)
3173 {
3174     // alignment must be >= sizeof(void*)
3175     if(alignment < sizeof(void*))
3176     {
3177         alignment = sizeof(void*);
3178     }
3179 
3180     void *pointer;
3181     if(posix_memalign(&pointer, alignment, size) == 0)
3182         return pointer;
3183     return VMA_NULL;
3184 }
3185 #elif defined(__APPLE__)
3186 #include <cstdlib>
3187 // aligned_alloc() is marked as macOS 10.15 only in the 10.15 SDK,
3188 // avoid the mess by using a different name
vma_aligned_alloc(size_t alignment,size_t size)3189 void *vma_aligned_alloc(size_t alignment, size_t size)
3190 {
3191     // alignment must be >= sizeof(void*)
3192     if(alignment < sizeof(void*))
3193     {
3194         alignment = sizeof(void*);
3195     }
3196 
3197     void *pointer;
3198     if(posix_memalign(&pointer, alignment, size) == 0)
3199         return pointer;
3200     return VMA_NULL;
3201 }
3202 #endif
3203 
3204 // If your compiler is not compatible with C++11 and definition of
3205 // aligned_alloc() function is missing, uncommeting following line may help:
3206 
3207 //#include <malloc.h>
3208 
3209 // Normal assert to check for programmer's errors, especially in Debug configuration.
3210 #ifndef VMA_ASSERT
3211    #ifdef _DEBUG
3212        #define VMA_ASSERT(expr)         assert(expr)
3213    #else
3214        #define VMA_ASSERT(expr)
3215    #endif
3216 #endif
3217 
3218 // Assert that will be called very often, like inside data structures e.g. operator[].
3219 // Making it non-empty can make program slow.
3220 #ifndef VMA_HEAVY_ASSERT
3221    #ifdef _DEBUG
3222        #define VMA_HEAVY_ASSERT(expr)   //VMA_ASSERT(expr)
3223    #else
3224        #define VMA_HEAVY_ASSERT(expr)
3225    #endif
3226 #endif
3227 
3228 #ifndef VMA_ALIGN_OF
3229    #define VMA_ALIGN_OF(type)       (__alignof(type))
3230 #endif
3231 
3232 #ifndef VMA_SYSTEM_ALIGNED_MALLOC
3233    #if defined(_WIN32)
3234        #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment)   (_aligned_malloc((size), (alignment)))
3235    #elif defined(__APPLE__)
3236        #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment)   (vma_aligned_alloc((alignment), (size) ))
3237    #else
3238        #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment)   (aligned_alloc((alignment), (size) ))
3239    #endif
3240 #endif
3241 
3242 #ifndef VMA_SYSTEM_FREE
3243    #if defined(_WIN32)
3244        #define VMA_SYSTEM_FREE(ptr)   _aligned_free(ptr)
3245    #else
3246        #define VMA_SYSTEM_FREE(ptr)   free(ptr)
3247    #endif
3248 #endif
3249 
3250 #ifndef VMA_MIN
3251    #define VMA_MIN(v1, v2)    (std::min((v1), (v2)))
3252 #endif
3253 
3254 #ifndef VMA_MAX
3255    #define VMA_MAX(v1, v2)    (std::max((v1), (v2)))
3256 #endif
3257 
3258 #ifndef VMA_SWAP
3259    #define VMA_SWAP(v1, v2)   std::swap((v1), (v2))
3260 #endif
3261 
3262 #ifndef VMA_SORT
3263    #define VMA_SORT(beg, end, cmp)  std::sort(beg, end, cmp)
3264 #endif
3265 
3266 #ifndef VMA_DEBUG_LOG
3267    #define VMA_DEBUG_LOG(format, ...)
3268    /*
3269    #define VMA_DEBUG_LOG(format, ...) do { \
3270        printf(format, __VA_ARGS__); \
3271        printf("\n"); \
3272    } while(false)
3273    */
3274 #endif
3275 
3276 // Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
3277 #if VMA_STATS_STRING_ENABLED
VmaUint32ToStr(char * outStr,size_t strLen,uint32_t num)3278     static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num)
3279     {
3280         snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
3281     }
VmaUint64ToStr(char * outStr,size_t strLen,uint64_t num)3282     static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
3283     {
3284         snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
3285     }
VmaPtrToStr(char * outStr,size_t strLen,const void * ptr)3286     static inline void VmaPtrToStr(char* outStr, size_t strLen, const void* ptr)
3287     {
3288         snprintf(outStr, strLen, "%p", ptr);
3289     }
3290 #endif
3291 
3292 #ifndef VMA_MUTEX
3293     class VmaMutex
3294     {
3295     public:
Lock()3296         void Lock() { m_Mutex.lock(); }
Unlock()3297         void Unlock() { m_Mutex.unlock(); }
3298     private:
3299         std::mutex m_Mutex;
3300     };
3301     #define VMA_MUTEX VmaMutex
3302 #endif
3303 
3304 // Read-write mutex, where "read" is shared access, "write" is exclusive access.
3305 #ifndef VMA_RW_MUTEX
3306     #if VMA_USE_STL_SHARED_MUTEX
3307         // Use std::shared_mutex from C++17.
3308         #include <shared_mutex>
3309         class VmaRWMutex
3310         {
3311         public:
LockRead()3312             void LockRead() { m_Mutex.lock_shared(); }
UnlockRead()3313             void UnlockRead() { m_Mutex.unlock_shared(); }
LockWrite()3314             void LockWrite() { m_Mutex.lock(); }
UnlockWrite()3315             void UnlockWrite() { m_Mutex.unlock(); }
3316         private:
3317             std::shared_mutex m_Mutex;
3318         };
3319         #define VMA_RW_MUTEX VmaRWMutex
3320     #elif defined(_WIN32) && !defined(__MINGW32__)
3321         // Use SRWLOCK from WinAPI.
3322         class VmaRWMutex
3323         {
3324         public:
VmaRWMutex()3325             VmaRWMutex() { InitializeSRWLock(&m_Lock); }
LockRead()3326             void LockRead() { AcquireSRWLockShared(&m_Lock); }
UnlockRead()3327             void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
LockWrite()3328             void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
UnlockWrite()3329             void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
3330         private:
3331             SRWLOCK m_Lock;
3332         };
3333         #define VMA_RW_MUTEX VmaRWMutex
3334     #else
3335         // Less efficient fallback: Use normal mutex.
3336         class VmaRWMutex
3337         {
3338         public:
LockRead()3339             void LockRead() { m_Mutex.Lock(); }
UnlockRead()3340             void UnlockRead() { m_Mutex.Unlock(); }
LockWrite()3341             void LockWrite() { m_Mutex.Lock(); }
UnlockWrite()3342             void UnlockWrite() { m_Mutex.Unlock(); }
3343         private:
3344             VMA_MUTEX m_Mutex;
3345         };
3346         #define VMA_RW_MUTEX VmaRWMutex
3347     #endif // #if VMA_USE_STL_SHARED_MUTEX
3348 #endif // #ifndef VMA_RW_MUTEX
3349 
3350 /*
3351 If providing your own implementation, you need to implement a subset of std::atomic:
3352 
3353 - Constructor(uint32_t desired)
3354 - uint32_t load() const
3355 - void store(uint32_t desired)
3356 - bool compare_exchange_weak(uint32_t& expected, uint32_t desired)
3357 */
3358 #ifndef VMA_ATOMIC_UINT32
3359    #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
3360 #endif
3361 
3362 #ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
3363     /**
3364     Every allocation will have its own memory block.
3365     Define to 1 for debugging purposes only.
3366     */
3367     #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
3368 #endif
3369 
3370 #ifndef VMA_DEBUG_ALIGNMENT
3371     /**
3372     Minimum alignment of all allocations, in bytes.
3373     Set to more than 1 for debugging purposes only. Must be power of two.
3374     */
3375     #define VMA_DEBUG_ALIGNMENT (1)
3376 #endif
3377 
3378 #ifndef VMA_DEBUG_MARGIN
3379     /**
3380     Minimum margin before and after every allocation, in bytes.
3381     Set nonzero for debugging purposes only.
3382     */
3383     #define VMA_DEBUG_MARGIN (0)
3384 #endif
3385 
3386 #ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
3387     /**
3388     Define this macro to 1 to automatically fill new allocations and destroyed
3389     allocations with some bit pattern.
3390     */
3391     #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)
3392 #endif
3393 
3394 #ifndef VMA_DEBUG_DETECT_CORRUPTION
3395     /**
3396     Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to
3397     enable writing magic value to the margin before and after every allocation and
3398     validating it, so that memory corruptions (out-of-bounds writes) are detected.
3399     */
3400     #define VMA_DEBUG_DETECT_CORRUPTION (0)
3401 #endif
3402 
3403 #ifndef VMA_DEBUG_GLOBAL_MUTEX
3404     /**
3405     Set this to 1 for debugging purposes only, to enable single mutex protecting all
3406     entry calls to the library. Can be useful for debugging multithreading issues.
3407     */
3408     #define VMA_DEBUG_GLOBAL_MUTEX (0)
3409 #endif
3410 
3411 #ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
3412     /**
3413     Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.
3414     Set to more than 1 for debugging purposes only. Must be power of two.
3415     */
3416     #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
3417 #endif
3418 
3419 #ifndef VMA_SMALL_HEAP_MAX_SIZE
3420    /// Maximum size of a memory heap in Vulkan to consider it "small".
3421    #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
3422 #endif
3423 
3424 #ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
3425    /// Default size of a block allocated as single VkDeviceMemory from a "large" heap.
3426    #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
3427 #endif
3428 
3429 #ifndef VMA_CLASS_NO_COPY
3430     #define VMA_CLASS_NO_COPY(className) \
3431         private: \
3432             className(const className&) = delete; \
3433             className& operator=(const className&) = delete;
3434 #endif
3435 
3436 static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX;
3437 
3438 // Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.
3439 static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
3440 
3441 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED   = 0xDC;
3442 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
3443 
3444 /*******************************************************************************
3445 END OF CONFIGURATION
3446 */
3447 
3448 static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
3449 
3450 static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
3451     VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
3452 
3453 // Returns number of bits set to 1 in (v).
VmaCountBitsSet(uint32_t v)3454 static inline uint32_t VmaCountBitsSet(uint32_t v)
3455 {
3456 	uint32_t c = v - ((v >> 1) & 0x55555555);
3457 	c = ((c >>  2) & 0x33333333) + (c & 0x33333333);
3458 	c = ((c >>  4) + c) & 0x0F0F0F0F;
3459 	c = ((c >>  8) + c) & 0x00FF00FF;
3460 	c = ((c >> 16) + c) & 0x0000FFFF;
3461 	return c;
3462 }
3463 
3464 // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
3465 // Use types like uint32_t, uint64_t as T.
3466 template <typename T>
VmaAlignUp(T val,T align)3467 static inline T VmaAlignUp(T val, T align)
3468 {
3469 	return (val + align - 1) / align * align;
3470 }
3471 // Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8.
3472 // Use types like uint32_t, uint64_t as T.
3473 template <typename T>
VmaAlignDown(T val,T align)3474 static inline T VmaAlignDown(T val, T align)
3475 {
3476     return val / align * align;
3477 }
3478 
3479 // Division with mathematical rounding to nearest number.
3480 template <typename T>
VmaRoundDiv(T x,T y)3481 static inline T VmaRoundDiv(T x, T y)
3482 {
3483 	return (x + (y / (T)2)) / y;
3484 }
3485 
3486 /*
3487 Returns true if given number is a power of two.
3488 T must be unsigned integer number or signed integer but always nonnegative.
3489 For 0 returns true.
3490 */
3491 template <typename T>
VmaIsPow2(T x)3492 inline bool VmaIsPow2(T x)
3493 {
3494     return (x & (x-1)) == 0;
3495 }
3496 
3497 // Returns smallest power of 2 greater or equal to v.
VmaNextPow2(uint32_t v)3498 static inline uint32_t VmaNextPow2(uint32_t v)
3499 {
3500 	v--;
3501     v |= v >> 1;
3502     v |= v >> 2;
3503     v |= v >> 4;
3504     v |= v >> 8;
3505     v |= v >> 16;
3506     v++;
3507     return v;
3508 }
VmaNextPow2(uint64_t v)3509 static inline uint64_t VmaNextPow2(uint64_t v)
3510 {
3511 	v--;
3512     v |= v >> 1;
3513     v |= v >> 2;
3514     v |= v >> 4;
3515     v |= v >> 8;
3516     v |= v >> 16;
3517     v |= v >> 32;
3518     v++;
3519     return v;
3520 }
3521 
3522 // Returns largest power of 2 less or equal to v.
VmaPrevPow2(uint32_t v)3523 static inline uint32_t VmaPrevPow2(uint32_t v)
3524 {
3525     v |= v >> 1;
3526     v |= v >> 2;
3527     v |= v >> 4;
3528     v |= v >> 8;
3529     v |= v >> 16;
3530     v = v ^ (v >> 1);
3531     return v;
3532 }
VmaPrevPow2(uint64_t v)3533 static inline uint64_t VmaPrevPow2(uint64_t v)
3534 {
3535     v |= v >> 1;
3536     v |= v >> 2;
3537     v |= v >> 4;
3538     v |= v >> 8;
3539     v |= v >> 16;
3540     v |= v >> 32;
3541     v = v ^ (v >> 1);
3542     return v;
3543 }
3544 
VmaStrIsEmpty(const char * pStr)3545 static inline bool VmaStrIsEmpty(const char* pStr)
3546 {
3547     return pStr == VMA_NULL || *pStr == '\0';
3548 }
3549 
VmaAlgorithmToStr(uint32_t algorithm)3550 static const char* VmaAlgorithmToStr(uint32_t algorithm)
3551 {
3552     switch(algorithm)
3553     {
3554     case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
3555         return "Linear";
3556     case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
3557         return "Buddy";
3558     case 0:
3559         return "Default";
3560     default:
3561         VMA_ASSERT(0);
3562         return "";
3563     }
3564 }
3565 
3566 #ifndef VMA_SORT
3567 
3568 template<typename Iterator, typename Compare>
VmaQuickSortPartition(Iterator beg,Iterator end,Compare cmp)3569 Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
3570 {
3571     Iterator centerValue = end; --centerValue;
3572     Iterator insertIndex = beg;
3573     for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
3574     {
3575         if(cmp(*memTypeIndex, *centerValue))
3576         {
3577             if(insertIndex != memTypeIndex)
3578             {
3579                 VMA_SWAP(*memTypeIndex, *insertIndex);
3580             }
3581             ++insertIndex;
3582         }
3583     }
3584     if(insertIndex != centerValue)
3585     {
3586         VMA_SWAP(*insertIndex, *centerValue);
3587     }
3588     return insertIndex;
3589 }
3590 
3591 template<typename Iterator, typename Compare>
VmaQuickSort(Iterator beg,Iterator end,Compare cmp)3592 void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
3593 {
3594     if(beg < end)
3595     {
3596         Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
3597         VmaQuickSort<Iterator, Compare>(beg, it, cmp);
3598         VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
3599     }
3600 }
3601 
3602 #define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
3603 
3604 #endif // #ifndef VMA_SORT
3605 
3606 /*
3607 Returns true if two memory blocks occupy overlapping pages.
3608 ResourceA must be in less memory offset than ResourceB.
3609 
3610 Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
3611 chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
3612 */
VmaBlocksOnSamePage(VkDeviceSize resourceAOffset,VkDeviceSize resourceASize,VkDeviceSize resourceBOffset,VkDeviceSize pageSize)3613 static inline bool VmaBlocksOnSamePage(
3614     VkDeviceSize resourceAOffset,
3615     VkDeviceSize resourceASize,
3616     VkDeviceSize resourceBOffset,
3617     VkDeviceSize pageSize)
3618 {
3619     VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
3620     VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
3621     VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
3622     VkDeviceSize resourceBStart = resourceBOffset;
3623     VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
3624     return resourceAEndPage == resourceBStartPage;
3625 }
3626 
3627 enum VmaSuballocationType
3628 {
3629     VMA_SUBALLOCATION_TYPE_FREE = 0,
3630     VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
3631     VMA_SUBALLOCATION_TYPE_BUFFER = 2,
3632     VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
3633     VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
3634     VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
3635     VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
3636 };
3637 
3638 /*
3639 Returns true if given suballocation types could conflict and must respect
3640 VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
3641 or linear image and another one is optimal image. If type is unknown, behave
3642 conservatively.
3643 */
VmaIsBufferImageGranularityConflict(VmaSuballocationType suballocType1,VmaSuballocationType suballocType2)3644 static inline bool VmaIsBufferImageGranularityConflict(
3645     VmaSuballocationType suballocType1,
3646     VmaSuballocationType suballocType2)
3647 {
3648     if(suballocType1 > suballocType2)
3649     {
3650         VMA_SWAP(suballocType1, suballocType2);
3651     }
3652 
3653     switch(suballocType1)
3654     {
3655     case VMA_SUBALLOCATION_TYPE_FREE:
3656         return false;
3657     case VMA_SUBALLOCATION_TYPE_UNKNOWN:
3658         return true;
3659     case VMA_SUBALLOCATION_TYPE_BUFFER:
3660         return
3661             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3662             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3663     case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
3664         return
3665             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3666             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
3667             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3668     case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
3669         return
3670             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3671     case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
3672         return false;
3673     default:
3674         VMA_ASSERT(0);
3675         return true;
3676     }
3677 }
3678 
VmaWriteMagicValue(void * pData,VkDeviceSize offset)3679 static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
3680 {
3681     uint32_t* pDst = (uint32_t*)((char*)pData + offset);
3682     const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3683     for(size_t i = 0; i != numberCount; ++i, ++pDst)
3684     {
3685         *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
3686     }
3687 }
3688 
VmaValidateMagicValue(const void * pData,VkDeviceSize offset)3689 static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
3690 {
3691     const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
3692     const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3693     for(size_t i = 0; i != numberCount; ++i, ++pSrc)
3694     {
3695         if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
3696         {
3697             return false;
3698         }
3699     }
3700     return true;
3701 }
3702 
3703 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
3704 struct VmaMutexLock
3705 {
VMA_CLASS_NO_COPYVmaMutexLock3706     VMA_CLASS_NO_COPY(VmaMutexLock)
3707 public:
3708     VmaMutexLock(VMA_MUTEX& mutex, bool useMutex) :
3709         m_pMutex(useMutex ? &mutex : VMA_NULL)
3710     { if(m_pMutex) { m_pMutex->Lock(); } }
~VmaMutexLockVmaMutexLock3711     ~VmaMutexLock()
3712     { if(m_pMutex) { m_pMutex->Unlock(); } }
3713 private:
3714     VMA_MUTEX* m_pMutex;
3715 };
3716 
3717 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
3718 struct VmaMutexLockRead
3719 {
VMA_CLASS_NO_COPYVmaMutexLockRead3720     VMA_CLASS_NO_COPY(VmaMutexLockRead)
3721 public:
3722     VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
3723         m_pMutex(useMutex ? &mutex : VMA_NULL)
3724     { if(m_pMutex) { m_pMutex->LockRead(); } }
~VmaMutexLockReadVmaMutexLockRead3725     ~VmaMutexLockRead() { if(m_pMutex) { m_pMutex->UnlockRead(); } }
3726 private:
3727     VMA_RW_MUTEX* m_pMutex;
3728 };
3729 
3730 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
3731 struct VmaMutexLockWrite
3732 {
VMA_CLASS_NO_COPYVmaMutexLockWrite3733     VMA_CLASS_NO_COPY(VmaMutexLockWrite)
3734 public:
3735     VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) :
3736         m_pMutex(useMutex ? &mutex : VMA_NULL)
3737     { if(m_pMutex) { m_pMutex->LockWrite(); } }
~VmaMutexLockWriteVmaMutexLockWrite3738     ~VmaMutexLockWrite() { if(m_pMutex) { m_pMutex->UnlockWrite(); } }
3739 private:
3740     VMA_RW_MUTEX* m_pMutex;
3741 };
3742 
3743 #if VMA_DEBUG_GLOBAL_MUTEX
3744     static VMA_MUTEX gDebugGlobalMutex;
3745     #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
3746 #else
3747     #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
3748 #endif
3749 
3750 // Minimum size of a free suballocation to register it in the free suballocation collection.
3751 static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
3752 
3753 /*
3754 Performs binary search and returns iterator to first element that is greater or
3755 equal to (key), according to comparison (cmp).
3756 
3757 Cmp should return true if first argument is less than second argument.
3758 
3759 Returned value is the found element, if present in the collection or place where
3760 new element with value (key) should be inserted.
3761 */
3762 template <typename CmpLess, typename IterT, typename KeyT>
VmaBinaryFindFirstNotLess(IterT beg,IterT end,const KeyT & key,CmpLess cmp)3763 static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, CmpLess cmp)
3764 {
3765     size_t down = 0, up = (end - beg);
3766     while(down < up)
3767     {
3768         const size_t mid = (down + up) / 2;
3769         if(cmp(*(beg+mid), key))
3770         {
3771             down = mid + 1;
3772         }
3773         else
3774         {
3775             up = mid;
3776         }
3777     }
3778     return beg + down;
3779 }
3780 
3781 /*
3782 Returns true if all pointers in the array are not-null and unique.
3783 Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
3784 T must be pointer type, e.g. VmaAllocation, VmaPool.
3785 */
3786 template<typename T>
VmaValidatePointerArray(uint32_t count,const T * arr)3787 static bool VmaValidatePointerArray(uint32_t count, const T* arr)
3788 {
3789     for(uint32_t i = 0; i < count; ++i)
3790     {
3791         const T iPtr = arr[i];
3792         if(iPtr == VMA_NULL)
3793         {
3794             return false;
3795         }
3796         for(uint32_t j = i + 1; j < count; ++j)
3797         {
3798             if(iPtr == arr[j])
3799             {
3800                 return false;
3801             }
3802         }
3803     }
3804     return true;
3805 }
3806 
3807 ////////////////////////////////////////////////////////////////////////////////
3808 // Memory allocation
3809 
VmaMalloc(const VkAllocationCallbacks * pAllocationCallbacks,size_t size,size_t alignment)3810 static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
3811 {
3812     if((pAllocationCallbacks != VMA_NULL) &&
3813         (pAllocationCallbacks->pfnAllocation != VMA_NULL))
3814     {
3815         return (*pAllocationCallbacks->pfnAllocation)(
3816             pAllocationCallbacks->pUserData,
3817             size,
3818             alignment,
3819             VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
3820     }
3821     else
3822     {
3823         return VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
3824     }
3825 }
3826 
VmaFree(const VkAllocationCallbacks * pAllocationCallbacks,void * ptr)3827 static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
3828 {
3829     if((pAllocationCallbacks != VMA_NULL) &&
3830         (pAllocationCallbacks->pfnFree != VMA_NULL))
3831     {
3832         (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
3833     }
3834     else
3835     {
3836         VMA_SYSTEM_FREE(ptr);
3837     }
3838 }
3839 
3840 template<typename T>
VmaAllocate(const VkAllocationCallbacks * pAllocationCallbacks)3841 static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
3842 {
3843     return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
3844 }
3845 
3846 template<typename T>
VmaAllocateArray(const VkAllocationCallbacks * pAllocationCallbacks,size_t count)3847 static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
3848 {
3849     return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
3850 }
3851 
3852 #define vma_new(allocator, type)   new(VmaAllocate<type>(allocator))(type)
3853 
3854 #define vma_new_array(allocator, type, count)   new(VmaAllocateArray<type>((allocator), (count)))(type)
3855 
3856 template<typename T>
vma_delete(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr)3857 static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
3858 {
3859     ptr->~T();
3860     VmaFree(pAllocationCallbacks, ptr);
3861 }
3862 
3863 template<typename T>
vma_delete_array(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr,size_t count)3864 static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
3865 {
3866     if(ptr != VMA_NULL)
3867     {
3868         for(size_t i = count; i--; )
3869         {
3870             ptr[i].~T();
3871         }
3872         VmaFree(pAllocationCallbacks, ptr);
3873     }
3874 }
3875 
3876 // STL-compatible allocator.
3877 template<typename T>
3878 class VmaStlAllocator
3879 {
3880 public:
3881     const VkAllocationCallbacks* const m_pCallbacks;
3882     typedef T value_type;
3883 
VmaStlAllocator(const VkAllocationCallbacks * pCallbacks)3884     VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
VmaStlAllocator(const VmaStlAllocator<U> & src)3885     template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
3886 
allocate(size_t n)3887     T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
deallocate(T * p,size_t)3888     void deallocate(T* p, size_t /*n*/) { VmaFree(m_pCallbacks, p); }
3889 
3890     template<typename U>
3891     bool operator==(const VmaStlAllocator<U>& rhs) const
3892     {
3893         return m_pCallbacks == rhs.m_pCallbacks;
3894     }
3895     template<typename U>
3896     bool operator!=(const VmaStlAllocator<U>& rhs) const
3897     {
3898         return m_pCallbacks != rhs.m_pCallbacks;
3899     }
3900 
3901     VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
3902 };
3903 
3904 #if VMA_USE_STL_VECTOR
3905 
3906 #define VmaVector std::vector
3907 
3908 template<typename T, typename allocatorT>
VmaVectorInsert(std::vector<T,allocatorT> & vec,size_t index,const T & item)3909 static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
3910 {
3911     vec.insert(vec.begin() + index, item);
3912 }
3913 
3914 template<typename T, typename allocatorT>
VmaVectorRemove(std::vector<T,allocatorT> & vec,size_t index)3915 static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
3916 {
3917     vec.erase(vec.begin() + index);
3918 }
3919 
3920 #else // #if VMA_USE_STL_VECTOR
3921 
3922 /* Class with interface compatible with subset of std::vector.
3923 T must be POD because constructors and destructors are not called and memcpy is
3924 used for these objects. */
3925 template<typename T, typename AllocatorT>
3926 class VmaVector
3927 {
3928 public:
3929     typedef T value_type;
3930 
VmaVector(const AllocatorT & allocator)3931     VmaVector(const AllocatorT& allocator) :
3932         m_Allocator(allocator),
3933         m_pArray(VMA_NULL),
3934         m_Count(0),
3935         m_Capacity(0)
3936     {
3937     }
3938 
VmaVector(size_t count,const AllocatorT & allocator)3939     VmaVector(size_t count, const AllocatorT& allocator) :
3940         m_Allocator(allocator),
3941         m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
3942         m_Count(count),
3943         m_Capacity(count)
3944     {
3945     }
3946 
VmaVector(const VmaVector<T,AllocatorT> & src)3947     VmaVector(const VmaVector<T, AllocatorT>& src) :
3948         m_Allocator(src.m_Allocator),
3949         m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
3950         m_Count(src.m_Count),
3951         m_Capacity(src.m_Count)
3952     {
3953         if(m_Count != 0)
3954         {
3955             memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
3956         }
3957     }
3958 
~VmaVector()3959     ~VmaVector()
3960     {
3961         VmaFree(m_Allocator.m_pCallbacks, m_pArray);
3962     }
3963 
3964     VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
3965     {
3966         if(&rhs != this)
3967         {
3968             resize(rhs.m_Count);
3969             if(m_Count != 0)
3970             {
3971                 memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
3972             }
3973         }
3974         return *this;
3975     }
3976 
empty()3977     bool empty() const { return m_Count == 0; }
size()3978     size_t size() const { return m_Count; }
data()3979     T* data() { return m_pArray; }
data()3980     const T* data() const { return m_pArray; }
3981 
3982     T& operator[](size_t index)
3983     {
3984         VMA_HEAVY_ASSERT(index < m_Count);
3985         return m_pArray[index];
3986     }
3987     const T& operator[](size_t index) const
3988     {
3989         VMA_HEAVY_ASSERT(index < m_Count);
3990         return m_pArray[index];
3991     }
3992 
front()3993     T& front()
3994     {
3995         VMA_HEAVY_ASSERT(m_Count > 0);
3996         return m_pArray[0];
3997     }
front()3998     const T& front() const
3999     {
4000         VMA_HEAVY_ASSERT(m_Count > 0);
4001         return m_pArray[0];
4002     }
back()4003     T& back()
4004     {
4005         VMA_HEAVY_ASSERT(m_Count > 0);
4006         return m_pArray[m_Count - 1];
4007     }
back()4008     const T& back() const
4009     {
4010         VMA_HEAVY_ASSERT(m_Count > 0);
4011         return m_pArray[m_Count - 1];
4012     }
4013 
4014     void reserve(size_t newCapacity, bool freeMemory = false)
4015     {
4016         newCapacity = VMA_MAX(newCapacity, m_Count);
4017 
4018         if((newCapacity < m_Capacity) && !freeMemory)
4019         {
4020             newCapacity = m_Capacity;
4021         }
4022 
4023         if(newCapacity != m_Capacity)
4024         {
4025             T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
4026             if(m_Count != 0)
4027             {
4028                 memcpy(newArray, m_pArray, m_Count * sizeof(T));
4029             }
4030             VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4031             m_Capacity = newCapacity;
4032             m_pArray = newArray;
4033         }
4034     }
4035 
4036     void resize(size_t newCount, bool freeMemory = false)
4037     {
4038         size_t newCapacity = m_Capacity;
4039         if(newCount > m_Capacity)
4040         {
4041             newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
4042         }
4043         else if(freeMemory)
4044         {
4045             newCapacity = newCount;
4046         }
4047 
4048         if(newCapacity != m_Capacity)
4049         {
4050             T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
4051             const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
4052             if(elementsToCopy != 0)
4053             {
4054                 memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
4055             }
4056             VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4057             m_Capacity = newCapacity;
4058             m_pArray = newArray;
4059         }
4060 
4061         m_Count = newCount;
4062     }
4063 
4064     void clear(bool freeMemory = false)
4065     {
4066         resize(0, freeMemory);
4067     }
4068 
insert(size_t index,const T & src)4069     void insert(size_t index, const T& src)
4070     {
4071         VMA_HEAVY_ASSERT(index <= m_Count);
4072         const size_t oldCount = size();
4073         resize(oldCount + 1);
4074         if(index < oldCount)
4075         {
4076             memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
4077         }
4078         m_pArray[index] = src;
4079     }
4080 
remove(size_t index)4081     void remove(size_t index)
4082     {
4083         VMA_HEAVY_ASSERT(index < m_Count);
4084         const size_t oldCount = size();
4085         if(index < oldCount - 1)
4086         {
4087             memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
4088         }
4089         resize(oldCount - 1);
4090     }
4091 
push_back(const T & src)4092     void push_back(const T& src)
4093     {
4094         const size_t newIndex = size();
4095         resize(newIndex + 1);
4096         m_pArray[newIndex] = src;
4097     }
4098 
pop_back()4099     void pop_back()
4100     {
4101         VMA_HEAVY_ASSERT(m_Count > 0);
4102         resize(size() - 1);
4103     }
4104 
push_front(const T & src)4105     void push_front(const T& src)
4106     {
4107         insert(0, src);
4108     }
4109 
pop_front()4110     void pop_front()
4111     {
4112         VMA_HEAVY_ASSERT(m_Count > 0);
4113         remove(0);
4114     }
4115 
4116     typedef T* iterator;
4117 
begin()4118     iterator begin() { return m_pArray; }
end()4119     iterator end() { return m_pArray + m_Count; }
4120 
4121 private:
4122     AllocatorT m_Allocator;
4123     T* m_pArray;
4124     size_t m_Count;
4125     size_t m_Capacity;
4126 };
4127 
4128 template<typename T, typename allocatorT>
VmaVectorInsert(VmaVector<T,allocatorT> & vec,size_t index,const T & item)4129 static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
4130 {
4131     vec.insert(index, item);
4132 }
4133 
4134 template<typename T, typename allocatorT>
VmaVectorRemove(VmaVector<T,allocatorT> & vec,size_t index)4135 static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
4136 {
4137     vec.remove(index);
4138 }
4139 
4140 #endif // #if VMA_USE_STL_VECTOR
4141 
4142 template<typename CmpLess, typename VectorT>
VmaVectorInsertSorted(VectorT & vector,const typename VectorT::value_type & value)4143 size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
4144 {
4145     const size_t indexToInsert = VmaBinaryFindFirstNotLess(
4146         vector.data(),
4147         vector.data() + vector.size(),
4148         value,
4149         CmpLess()) - vector.data();
4150     VmaVectorInsert(vector, indexToInsert, value);
4151     return indexToInsert;
4152 }
4153 
4154 template<typename CmpLess, typename VectorT>
VmaVectorRemoveSorted(VectorT & vector,const typename VectorT::value_type & value)4155 bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
4156 {
4157     CmpLess comparator;
4158     typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
4159         vector.begin(),
4160         vector.end(),
4161         value,
4162         comparator);
4163     if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
4164     {
4165         size_t indexToRemove = it - vector.begin();
4166         VmaVectorRemove(vector, indexToRemove);
4167         return true;
4168     }
4169     return false;
4170 }
4171 
4172 template<typename CmpLess, typename IterT, typename KeyT>
VmaVectorFindSorted(const IterT & beg,const IterT & end,const KeyT & value)4173 IterT VmaVectorFindSorted(const IterT& beg, const IterT& end, const KeyT& value)
4174 {
4175     CmpLess comparator;
4176     IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
4177         beg, end, value, comparator);
4178     if(it == end ||
4179         (!comparator(*it, value) && !comparator(value, *it)))
4180     {
4181         return it;
4182     }
4183     return end;
4184 }
4185 
4186 ////////////////////////////////////////////////////////////////////////////////
4187 // class VmaPoolAllocator
4188 
4189 /*
4190 Allocator for objects of type T using a list of arrays (pools) to speed up
4191 allocation. Number of elements that can be allocated is not bounded because
4192 allocator can create multiple blocks.
4193 */
4194 template<typename T>
4195 class VmaPoolAllocator
4196 {
4197     VMA_CLASS_NO_COPY(VmaPoolAllocator)
4198 public:
4199     VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock);
4200     ~VmaPoolAllocator();
4201     void Clear();
4202     T* Alloc();
4203     void Free(T* ptr);
4204 
4205 private:
4206     union Item
4207     {
4208         uint32_t NextFreeIndex;
4209         T Value;
4210     };
4211 
4212     struct ItemBlock
4213     {
4214         Item* pItems;
4215         uint32_t FirstFreeIndex;
4216     };
4217 
4218     const VkAllocationCallbacks* m_pAllocationCallbacks;
4219     size_t m_ItemsPerBlock;
4220     VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
4221 
4222     ItemBlock& CreateNewBlock();
4223 };
4224 
4225 template<typename T>
VmaPoolAllocator(const VkAllocationCallbacks * pAllocationCallbacks,size_t itemsPerBlock)4226 VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock) :
4227     m_pAllocationCallbacks(pAllocationCallbacks),
4228     m_ItemsPerBlock(itemsPerBlock),
4229     m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
4230 {
4231     VMA_ASSERT(itemsPerBlock > 0);
4232 }
4233 
4234 template<typename T>
~VmaPoolAllocator()4235 VmaPoolAllocator<T>::~VmaPoolAllocator()
4236 {
4237     Clear();
4238 }
4239 
4240 template<typename T>
Clear()4241 void VmaPoolAllocator<T>::Clear()
4242 {
4243     for(size_t i = m_ItemBlocks.size(); i--; )
4244         vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemsPerBlock);
4245     m_ItemBlocks.clear();
4246 }
4247 
4248 template<typename T>
Alloc()4249 T* VmaPoolAllocator<T>::Alloc()
4250 {
4251     for(size_t i = m_ItemBlocks.size(); i--; )
4252     {
4253         ItemBlock& block = m_ItemBlocks[i];
4254         // This block has some free items: Use first one.
4255         if(block.FirstFreeIndex != UINT32_MAX)
4256         {
4257             Item* const pItem = &block.pItems[block.FirstFreeIndex];
4258             block.FirstFreeIndex = pItem->NextFreeIndex;
4259             return &pItem->Value;
4260         }
4261     }
4262 
4263     // No block has free item: Create new one and use it.
4264     ItemBlock& newBlock = CreateNewBlock();
4265     Item* const pItem = &newBlock.pItems[0];
4266     newBlock.FirstFreeIndex = pItem->NextFreeIndex;
4267     return &pItem->Value;
4268 }
4269 
4270 template<typename T>
Free(T * ptr)4271 void VmaPoolAllocator<T>::Free(T* ptr)
4272 {
4273     // Search all memory blocks to find ptr.
4274     for(size_t i = 0; i < m_ItemBlocks.size(); ++i)
4275     {
4276         ItemBlock& block = m_ItemBlocks[i];
4277 
4278         // Casting to union.
4279         Item* pItemPtr;
4280         memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
4281 
4282         // Check if pItemPtr is in address range of this block.
4283         if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + m_ItemsPerBlock))
4284         {
4285             const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
4286             pItemPtr->NextFreeIndex = block.FirstFreeIndex;
4287             block.FirstFreeIndex = index;
4288             return;
4289         }
4290     }
4291     VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
4292 }
4293 
4294 template<typename T>
CreateNewBlock()4295 typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
4296 {
4297     ItemBlock newBlock = {
4298         vma_new_array(m_pAllocationCallbacks, Item, m_ItemsPerBlock), 0 };
4299 
4300     m_ItemBlocks.push_back(newBlock);
4301 
4302     // Setup singly-linked list of all free items in this block.
4303     for(uint32_t i = 0; i < m_ItemsPerBlock - 1; ++i)
4304         newBlock.pItems[i].NextFreeIndex = i + 1;
4305     newBlock.pItems[m_ItemsPerBlock - 1].NextFreeIndex = UINT32_MAX;
4306     return m_ItemBlocks.back();
4307 }
4308 
4309 ////////////////////////////////////////////////////////////////////////////////
4310 // class VmaRawList, VmaList
4311 
4312 #if VMA_USE_STL_LIST
4313 
4314 #define VmaList std::list
4315 
4316 #else // #if VMA_USE_STL_LIST
4317 
4318 template<typename T>
4319 struct VmaListItem
4320 {
4321     VmaListItem* pPrev;
4322     VmaListItem* pNext;
4323     T Value;
4324 };
4325 
4326 // Doubly linked list.
4327 template<typename T>
4328 class VmaRawList
4329 {
4330     VMA_CLASS_NO_COPY(VmaRawList)
4331 public:
4332     typedef VmaListItem<T> ItemType;
4333 
4334     VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
4335     ~VmaRawList();
4336     void Clear();
4337 
GetCount()4338     size_t GetCount() const { return m_Count; }
IsEmpty()4339     bool IsEmpty() const { return m_Count == 0; }
4340 
Front()4341     ItemType* Front() { return m_pFront; }
Front()4342     const ItemType* Front() const { return m_pFront; }
Back()4343     ItemType* Back() { return m_pBack; }
Back()4344     const ItemType* Back() const { return m_pBack; }
4345 
4346     ItemType* PushBack();
4347     ItemType* PushFront();
4348     ItemType* PushBack(const T& value);
4349     ItemType* PushFront(const T& value);
4350     void PopBack();
4351     void PopFront();
4352 
4353     // Item can be null - it means PushBack.
4354     ItemType* InsertBefore(ItemType* pItem);
4355     // Item can be null - it means PushFront.
4356     ItemType* InsertAfter(ItemType* pItem);
4357 
4358     ItemType* InsertBefore(ItemType* pItem, const T& value);
4359     ItemType* InsertAfter(ItemType* pItem, const T& value);
4360 
4361     void Remove(ItemType* pItem);
4362 
4363 private:
4364     const VkAllocationCallbacks* const m_pAllocationCallbacks;
4365     VmaPoolAllocator<ItemType> m_ItemAllocator;
4366     ItemType* m_pFront;
4367     ItemType* m_pBack;
4368     size_t m_Count;
4369 };
4370 
4371 template<typename T>
VmaRawList(const VkAllocationCallbacks * pAllocationCallbacks)4372 VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
4373     m_pAllocationCallbacks(pAllocationCallbacks),
4374     m_ItemAllocator(pAllocationCallbacks, 128),
4375     m_pFront(VMA_NULL),
4376     m_pBack(VMA_NULL),
4377     m_Count(0)
4378 {
4379 }
4380 
4381 template<typename T>
~VmaRawList()4382 VmaRawList<T>::~VmaRawList()
4383 {
4384     // Intentionally not calling Clear, because that would be unnecessary
4385     // computations to return all items to m_ItemAllocator as free.
4386 }
4387 
4388 template<typename T>
Clear()4389 void VmaRawList<T>::Clear()
4390 {
4391     if(IsEmpty() == false)
4392     {
4393         ItemType* pItem = m_pBack;
4394         while(pItem != VMA_NULL)
4395         {
4396             ItemType* const pPrevItem = pItem->pPrev;
4397             m_ItemAllocator.Free(pItem);
4398             pItem = pPrevItem;
4399         }
4400         m_pFront = VMA_NULL;
4401         m_pBack = VMA_NULL;
4402         m_Count = 0;
4403     }
4404 }
4405 
4406 template<typename T>
PushBack()4407 VmaListItem<T>* VmaRawList<T>::PushBack()
4408 {
4409     ItemType* const pNewItem = m_ItemAllocator.Alloc();
4410     pNewItem->pNext = VMA_NULL;
4411     if(IsEmpty())
4412     {
4413         pNewItem->pPrev = VMA_NULL;
4414         m_pFront = pNewItem;
4415         m_pBack = pNewItem;
4416         m_Count = 1;
4417     }
4418     else
4419     {
4420         pNewItem->pPrev = m_pBack;
4421         m_pBack->pNext = pNewItem;
4422         m_pBack = pNewItem;
4423         ++m_Count;
4424     }
4425     return pNewItem;
4426 }
4427 
4428 template<typename T>
PushFront()4429 VmaListItem<T>* VmaRawList<T>::PushFront()
4430 {
4431     ItemType* const pNewItem = m_ItemAllocator.Alloc();
4432     pNewItem->pPrev = VMA_NULL;
4433     if(IsEmpty())
4434     {
4435         pNewItem->pNext = VMA_NULL;
4436         m_pFront = pNewItem;
4437         m_pBack = pNewItem;
4438         m_Count = 1;
4439     }
4440     else
4441     {
4442         pNewItem->pNext = m_pFront;
4443         m_pFront->pPrev = pNewItem;
4444         m_pFront = pNewItem;
4445         ++m_Count;
4446     }
4447     return pNewItem;
4448 }
4449 
4450 template<typename T>
PushBack(const T & value)4451 VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
4452 {
4453     ItemType* const pNewItem = PushBack();
4454     pNewItem->Value = value;
4455     return pNewItem;
4456 }
4457 
4458 template<typename T>
PushFront(const T & value)4459 VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
4460 {
4461     ItemType* const pNewItem = PushFront();
4462     pNewItem->Value = value;
4463     return pNewItem;
4464 }
4465 
4466 template<typename T>
PopBack()4467 void VmaRawList<T>::PopBack()
4468 {
4469     VMA_HEAVY_ASSERT(m_Count > 0);
4470     ItemType* const pBackItem = m_pBack;
4471     ItemType* const pPrevItem = pBackItem->pPrev;
4472     if(pPrevItem != VMA_NULL)
4473     {
4474         pPrevItem->pNext = VMA_NULL;
4475     }
4476     m_pBack = pPrevItem;
4477     m_ItemAllocator.Free(pBackItem);
4478     --m_Count;
4479 }
4480 
4481 template<typename T>
PopFront()4482 void VmaRawList<T>::PopFront()
4483 {
4484     VMA_HEAVY_ASSERT(m_Count > 0);
4485     ItemType* const pFrontItem = m_pFront;
4486     ItemType* const pNextItem = pFrontItem->pNext;
4487     if(pNextItem != VMA_NULL)
4488     {
4489         pNextItem->pPrev = VMA_NULL;
4490     }
4491     m_pFront = pNextItem;
4492     m_ItemAllocator.Free(pFrontItem);
4493     --m_Count;
4494 }
4495 
4496 template<typename T>
Remove(ItemType * pItem)4497 void VmaRawList<T>::Remove(ItemType* pItem)
4498 {
4499     VMA_HEAVY_ASSERT(pItem != VMA_NULL);
4500     VMA_HEAVY_ASSERT(m_Count > 0);
4501 
4502     if(pItem->pPrev != VMA_NULL)
4503     {
4504         pItem->pPrev->pNext = pItem->pNext;
4505     }
4506     else
4507     {
4508         VMA_HEAVY_ASSERT(m_pFront == pItem);
4509         m_pFront = pItem->pNext;
4510     }
4511 
4512     if(pItem->pNext != VMA_NULL)
4513     {
4514         pItem->pNext->pPrev = pItem->pPrev;
4515     }
4516     else
4517     {
4518         VMA_HEAVY_ASSERT(m_pBack == pItem);
4519         m_pBack = pItem->pPrev;
4520     }
4521 
4522     m_ItemAllocator.Free(pItem);
4523     --m_Count;
4524 }
4525 
4526 template<typename T>
InsertBefore(ItemType * pItem)4527 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
4528 {
4529     if(pItem != VMA_NULL)
4530     {
4531         ItemType* const prevItem = pItem->pPrev;
4532         ItemType* const newItem = m_ItemAllocator.Alloc();
4533         newItem->pPrev = prevItem;
4534         newItem->pNext = pItem;
4535         pItem->pPrev = newItem;
4536         if(prevItem != VMA_NULL)
4537         {
4538             prevItem->pNext = newItem;
4539         }
4540         else
4541         {
4542             VMA_HEAVY_ASSERT(m_pFront == pItem);
4543             m_pFront = newItem;
4544         }
4545         ++m_Count;
4546         return newItem;
4547     }
4548     else
4549         return PushBack();
4550 }
4551 
4552 template<typename T>
InsertAfter(ItemType * pItem)4553 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
4554 {
4555     if(pItem != VMA_NULL)
4556     {
4557         ItemType* const nextItem = pItem->pNext;
4558         ItemType* const newItem = m_ItemAllocator.Alloc();
4559         newItem->pNext = nextItem;
4560         newItem->pPrev = pItem;
4561         pItem->pNext = newItem;
4562         if(nextItem != VMA_NULL)
4563         {
4564             nextItem->pPrev = newItem;
4565         }
4566         else
4567         {
4568             VMA_HEAVY_ASSERT(m_pBack == pItem);
4569             m_pBack = newItem;
4570         }
4571         ++m_Count;
4572         return newItem;
4573     }
4574     else
4575         return PushFront();
4576 }
4577 
4578 template<typename T>
InsertBefore(ItemType * pItem,const T & value)4579 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
4580 {
4581     ItemType* const newItem = InsertBefore(pItem);
4582     newItem->Value = value;
4583     return newItem;
4584 }
4585 
4586 template<typename T>
InsertAfter(ItemType * pItem,const T & value)4587 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
4588 {
4589     ItemType* const newItem = InsertAfter(pItem);
4590     newItem->Value = value;
4591     return newItem;
4592 }
4593 
4594 template<typename T, typename AllocatorT>
4595 class VmaList
4596 {
VMA_CLASS_NO_COPY(VmaList)4597     VMA_CLASS_NO_COPY(VmaList)
4598 public:
4599     class iterator
4600     {
4601     public:
4602         iterator() :
4603             m_pList(VMA_NULL),
4604             m_pItem(VMA_NULL)
4605         {
4606         }
4607 
4608         T& operator*() const
4609         {
4610             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4611             return m_pItem->Value;
4612         }
4613         T* operator->() const
4614         {
4615             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4616             return &m_pItem->Value;
4617         }
4618 
4619         iterator& operator++()
4620         {
4621             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4622             m_pItem = m_pItem->pNext;
4623             return *this;
4624         }
4625         iterator& operator--()
4626         {
4627             if(m_pItem != VMA_NULL)
4628             {
4629                 m_pItem = m_pItem->pPrev;
4630             }
4631             else
4632             {
4633                 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4634                 m_pItem = m_pList->Back();
4635             }
4636             return *this;
4637         }
4638 
4639         iterator operator++(int)
4640         {
4641             iterator result = *this;
4642             ++*this;
4643             return result;
4644         }
4645         iterator operator--(int)
4646         {
4647             iterator result = *this;
4648             --*this;
4649             return result;
4650         }
4651 
4652         bool operator==(const iterator& rhs) const
4653         {
4654             VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4655             return m_pItem == rhs.m_pItem;
4656         }
4657         bool operator!=(const iterator& rhs) const
4658         {
4659             VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4660             return m_pItem != rhs.m_pItem;
4661         }
4662 
4663     private:
4664         VmaRawList<T>* m_pList;
4665         VmaListItem<T>* m_pItem;
4666 
4667         iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
4668             m_pList(pList),
4669             m_pItem(pItem)
4670         {
4671         }
4672 
4673         friend class VmaList<T, AllocatorT>;
4674     };
4675 
4676     class const_iterator
4677     {
4678     public:
const_iterator()4679         const_iterator() :
4680             m_pList(VMA_NULL),
4681             m_pItem(VMA_NULL)
4682         {
4683         }
4684 
const_iterator(const iterator & src)4685         const_iterator(const iterator& src) :
4686             m_pList(src.m_pList),
4687             m_pItem(src.m_pItem)
4688         {
4689         }
4690 
4691         const T& operator*() const
4692         {
4693             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4694             return m_pItem->Value;
4695         }
4696         const T* operator->() const
4697         {
4698             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4699             return &m_pItem->Value;
4700         }
4701 
4702         const_iterator& operator++()
4703         {
4704             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4705             m_pItem = m_pItem->pNext;
4706             return *this;
4707         }
4708         const_iterator& operator--()
4709         {
4710             if(m_pItem != VMA_NULL)
4711             {
4712                 m_pItem = m_pItem->pPrev;
4713             }
4714             else
4715             {
4716                 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4717                 m_pItem = m_pList->Back();
4718             }
4719             return *this;
4720         }
4721 
4722         const_iterator operator++(int)
4723         {
4724             const_iterator result = *this;
4725             ++*this;
4726             return result;
4727         }
4728         const_iterator operator--(int)
4729         {
4730             const_iterator result = *this;
4731             --*this;
4732             return result;
4733         }
4734 
4735         bool operator==(const const_iterator& rhs) const
4736         {
4737             VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4738             return m_pItem == rhs.m_pItem;
4739         }
4740         bool operator!=(const const_iterator& rhs) const
4741         {
4742             VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4743             return m_pItem != rhs.m_pItem;
4744         }
4745 
4746     private:
const_iterator(const VmaRawList<T> * pList,const VmaListItem<T> * pItem)4747         const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
4748             m_pList(pList),
4749             m_pItem(pItem)
4750         {
4751         }
4752 
4753         const VmaRawList<T>* m_pList;
4754         const VmaListItem<T>* m_pItem;
4755 
4756         friend class VmaList<T, AllocatorT>;
4757     };
4758 
VmaList(const AllocatorT & allocator)4759     VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
4760 
empty()4761     bool empty() const { return m_RawList.IsEmpty(); }
size()4762     size_t size() const { return m_RawList.GetCount(); }
4763 
begin()4764     iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
end()4765     iterator end() { return iterator(&m_RawList, VMA_NULL); }
4766 
cbegin()4767     const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
cend()4768     const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
4769 
clear()4770     void clear() { m_RawList.Clear(); }
push_back(const T & value)4771     void push_back(const T& value) { m_RawList.PushBack(value); }
erase(iterator it)4772     void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
insert(iterator it,const T & value)4773     iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
4774 
4775 private:
4776     VmaRawList<T> m_RawList;
4777 };
4778 
4779 #endif // #if VMA_USE_STL_LIST
4780 
4781 ////////////////////////////////////////////////////////////////////////////////
4782 // class VmaMap
4783 
4784 // Unused in this version.
4785 #if 0
4786 
4787 #if VMA_USE_STL_UNORDERED_MAP
4788 
4789 #define VmaPair std::pair
4790 
4791 #define VMA_MAP_TYPE(KeyT, ValueT) \
4792     std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
4793 
4794 #else // #if VMA_USE_STL_UNORDERED_MAP
4795 
4796 template<typename T1, typename T2>
4797 struct VmaPair
4798 {
4799     T1 first;
4800     T2 second;
4801 
4802     VmaPair() : first(), second() { }
4803     VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
4804 };
4805 
4806 /* Class compatible with subset of interface of std::unordered_map.
4807 KeyT, ValueT must be POD because they will be stored in VmaVector.
4808 */
4809 template<typename KeyT, typename ValueT>
4810 class VmaMap
4811 {
4812 public:
4813     typedef VmaPair<KeyT, ValueT> PairType;
4814     typedef PairType* iterator;
4815 
4816     VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
4817 
4818     iterator begin() { return m_Vector.begin(); }
4819     iterator end() { return m_Vector.end(); }
4820 
4821     void insert(const PairType& pair);
4822     iterator find(const KeyT& key);
4823     void erase(iterator it);
4824 
4825 private:
4826     VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
4827 };
4828 
4829 #define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
4830 
4831 template<typename FirstT, typename SecondT>
4832 struct VmaPairFirstLess
4833 {
4834     bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
4835     {
4836         return lhs.first < rhs.first;
4837     }
4838     bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
4839     {
4840         return lhs.first < rhsFirst;
4841     }
4842 };
4843 
4844 template<typename KeyT, typename ValueT>
4845 void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
4846 {
4847     const size_t indexToInsert = VmaBinaryFindFirstNotLess(
4848         m_Vector.data(),
4849         m_Vector.data() + m_Vector.size(),
4850         pair,
4851         VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
4852     VmaVectorInsert(m_Vector, indexToInsert, pair);
4853 }
4854 
4855 template<typename KeyT, typename ValueT>
4856 VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
4857 {
4858     PairType* it = VmaBinaryFindFirstNotLess(
4859         m_Vector.data(),
4860         m_Vector.data() + m_Vector.size(),
4861         key,
4862         VmaPairFirstLess<KeyT, ValueT>());
4863     if((it != m_Vector.end()) && (it->first == key))
4864     {
4865         return it;
4866     }
4867     else
4868     {
4869         return m_Vector.end();
4870     }
4871 }
4872 
4873 template<typename KeyT, typename ValueT>
4874 void VmaMap<KeyT, ValueT>::erase(iterator it)
4875 {
4876     VmaVectorRemove(m_Vector, it - m_Vector.begin());
4877 }
4878 
4879 #endif // #if VMA_USE_STL_UNORDERED_MAP
4880 
4881 #endif // #if 0
4882 
4883 ////////////////////////////////////////////////////////////////////////////////
4884 
4885 class VmaDeviceMemoryBlock;
4886 
4887 enum VMA_CACHE_OPERATION { VMA_CACHE_FLUSH, VMA_CACHE_INVALIDATE };
4888 
4889 struct VmaAllocation_T
4890 {
4891     VMA_CLASS_NO_COPY(VmaAllocation_T)
4892 private:
4893     static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
4894 
4895     enum FLAGS
4896     {
4897         FLAG_USER_DATA_STRING = 0x01,
4898     };
4899 
4900 public:
4901     enum ALLOCATION_TYPE
4902     {
4903         ALLOCATION_TYPE_NONE,
4904         ALLOCATION_TYPE_BLOCK,
4905         ALLOCATION_TYPE_DEDICATED,
4906     };
4907 
VmaAllocation_TVmaAllocation_T4908     VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
4909         m_Alignment(1),
4910         m_Size(0),
4911         m_pUserData(VMA_NULL),
4912         m_LastUseFrameIndex(currentFrameIndex),
4913         m_Type((uint8_t)ALLOCATION_TYPE_NONE),
4914         m_SuballocationType((uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN),
4915         m_MapCount(0),
4916         m_Flags(userDataString ? (uint8_t)FLAG_USER_DATA_STRING : 0)
4917     {
4918 #if VMA_STATS_STRING_ENABLED
4919         m_CreationFrameIndex = currentFrameIndex;
4920         m_BufferImageUsage = 0;
4921 #endif
4922     }
4923 
~VmaAllocation_TVmaAllocation_T4924     ~VmaAllocation_T()
4925     {
4926         VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
4927 
4928         // Check if owned string was freed.
4929         VMA_ASSERT(m_pUserData == VMA_NULL);
4930     }
4931 
InitBlockAllocationVmaAllocation_T4932     void InitBlockAllocation(
4933         VmaPool hPool,
4934         VmaDeviceMemoryBlock* block,
4935         VkDeviceSize offset,
4936         VkDeviceSize alignment,
4937         VkDeviceSize size,
4938         VmaSuballocationType suballocationType,
4939         bool mapped,
4940         bool canBecomeLost)
4941     {
4942         VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4943         VMA_ASSERT(block != VMA_NULL);
4944         m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
4945         m_Alignment = alignment;
4946         m_Size = size;
4947         m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
4948         m_SuballocationType = (uint8_t)suballocationType;
4949         m_BlockAllocation.m_hPool = hPool;
4950         m_BlockAllocation.m_Block = block;
4951         m_BlockAllocation.m_Offset = offset;
4952         m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
4953     }
4954 
InitLostVmaAllocation_T4955     void InitLost()
4956     {
4957         VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4958         VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
4959         m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
4960         m_BlockAllocation.m_hPool = VK_NULL_HANDLE;
4961         m_BlockAllocation.m_Block = VMA_NULL;
4962         m_BlockAllocation.m_Offset = 0;
4963         m_BlockAllocation.m_CanBecomeLost = true;
4964     }
4965 
4966     void ChangeBlockAllocation(
4967         VmaAllocator hAllocator,
4968         VmaDeviceMemoryBlock* block,
4969         VkDeviceSize offset);
4970 
4971     void ChangeSize(VkDeviceSize newSize);
4972     void ChangeOffset(VkDeviceSize newOffset);
4973 
4974     // pMappedData not null means allocation is created with MAPPED flag.
InitDedicatedAllocationVmaAllocation_T4975     void InitDedicatedAllocation(
4976         uint32_t memoryTypeIndex,
4977         VkDeviceMemory hMemory,
4978         VmaSuballocationType suballocationType,
4979         void* pMappedData,
4980         VkDeviceSize size)
4981     {
4982         VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4983         VMA_ASSERT(hMemory != VK_NULL_HANDLE);
4984         m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
4985         m_Alignment = 0;
4986         m_Size = size;
4987         m_SuballocationType = (uint8_t)suballocationType;
4988         m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
4989         m_DedicatedAllocation.m_MemoryTypeIndex = memoryTypeIndex;
4990         m_DedicatedAllocation.m_hMemory = hMemory;
4991         m_DedicatedAllocation.m_pMappedData = pMappedData;
4992     }
4993 
GetTypeVmaAllocation_T4994     ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
GetAlignmentVmaAllocation_T4995     VkDeviceSize GetAlignment() const { return m_Alignment; }
GetSizeVmaAllocation_T4996     VkDeviceSize GetSize() const { return m_Size; }
IsUserDataStringVmaAllocation_T4997     bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
GetUserDataVmaAllocation_T4998     void* GetUserData() const { return m_pUserData; }
4999     void SetUserData(VmaAllocator hAllocator, void* pUserData);
GetSuballocationTypeVmaAllocation_T5000     VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
5001 
GetBlockVmaAllocation_T5002     VmaDeviceMemoryBlock* GetBlock() const
5003     {
5004         VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
5005         return m_BlockAllocation.m_Block;
5006     }
5007     VkDeviceSize GetOffset() const;
5008     VkDeviceMemory GetMemory() const;
5009     uint32_t GetMemoryTypeIndex() const;
IsPersistentMapVmaAllocation_T5010     bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
5011     void* GetMappedData() const;
5012     bool CanBecomeLost() const;
5013     VmaPool GetPool() const;
5014 
GetLastUseFrameIndexVmaAllocation_T5015     uint32_t GetLastUseFrameIndex() const
5016     {
5017         return m_LastUseFrameIndex.load();
5018     }
CompareExchangeLastUseFrameIndexVmaAllocation_T5019     bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
5020     {
5021         return m_LastUseFrameIndex.compare_exchange_weak(expected, desired);
5022     }
5023     /*
5024     - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
5025       makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
5026     - Else, returns false.
5027 
5028     If hAllocation is already lost, assert - you should not call it then.
5029     If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
5030     */
5031     bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5032 
DedicatedAllocCalcStatsInfoVmaAllocation_T5033     void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
5034     {
5035         VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
5036         outInfo.blockCount = 1;
5037         outInfo.allocationCount = 1;
5038         outInfo.unusedRangeCount = 0;
5039         outInfo.usedBytes = m_Size;
5040         outInfo.unusedBytes = 0;
5041         outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
5042         outInfo.unusedRangeSizeMin = UINT64_MAX;
5043         outInfo.unusedRangeSizeMax = 0;
5044     }
5045 
5046     void BlockAllocMap();
5047     void BlockAllocUnmap();
5048     VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
5049     void DedicatedAllocUnmap(VmaAllocator hAllocator);
5050 
5051 #if VMA_STATS_STRING_ENABLED
GetCreationFrameIndexVmaAllocation_T5052     uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; }
GetBufferImageUsageVmaAllocation_T5053     uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
5054 
InitBufferImageUsageVmaAllocation_T5055     void InitBufferImageUsage(uint32_t bufferImageUsage)
5056     {
5057         VMA_ASSERT(m_BufferImageUsage == 0);
5058         m_BufferImageUsage = bufferImageUsage;
5059     }
5060 
5061     void PrintParameters(class VmaJsonWriter& json) const;
5062 #endif
5063 
5064 private:
5065     VkDeviceSize m_Alignment;
5066     VkDeviceSize m_Size;
5067     void* m_pUserData;
5068     VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
5069     uint8_t m_Type; // ALLOCATION_TYPE
5070     uint8_t m_SuballocationType; // VmaSuballocationType
5071     // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
5072     // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
5073     uint8_t m_MapCount;
5074     uint8_t m_Flags; // enum FLAGS
5075 
5076     // Allocation out of VmaDeviceMemoryBlock.
5077     struct BlockAllocation
5078     {
5079         VmaPool m_hPool; // Null if belongs to general memory.
5080         VmaDeviceMemoryBlock* m_Block;
5081         VkDeviceSize m_Offset;
5082         bool m_CanBecomeLost;
5083     };
5084 
5085     // Allocation for an object that has its own private VkDeviceMemory.
5086     struct DedicatedAllocation
5087     {
5088         uint32_t m_MemoryTypeIndex;
5089         VkDeviceMemory m_hMemory;
5090         void* m_pMappedData; // Not null means memory is mapped.
5091     };
5092 
5093     union
5094     {
5095         // Allocation out of VmaDeviceMemoryBlock.
5096         BlockAllocation m_BlockAllocation;
5097         // Allocation for an object that has its own private VkDeviceMemory.
5098         DedicatedAllocation m_DedicatedAllocation;
5099     };
5100 
5101 #if VMA_STATS_STRING_ENABLED
5102     uint32_t m_CreationFrameIndex;
5103     uint32_t m_BufferImageUsage; // 0 if unknown.
5104 #endif
5105 
5106     void FreeUserDataString(VmaAllocator hAllocator);
5107 };
5108 
5109 /*
5110 Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
5111 allocated memory block or free.
5112 */
5113 struct VmaSuballocation
5114 {
5115     VkDeviceSize offset;
5116     VkDeviceSize size;
5117     VmaAllocation hAllocation;
5118     VmaSuballocationType type;
5119 };
5120 
5121 // Comparator for offsets.
5122 struct VmaSuballocationOffsetLess
5123 {
operatorVmaSuballocationOffsetLess5124     bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
5125     {
5126         return lhs.offset < rhs.offset;
5127     }
5128 };
5129 struct VmaSuballocationOffsetGreater
5130 {
operatorVmaSuballocationOffsetGreater5131     bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
5132     {
5133         return lhs.offset > rhs.offset;
5134     }
5135 };
5136 
5137 typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
5138 
5139 // Cost of one additional allocation lost, as equivalent in bytes.
5140 static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
5141 
5142 /*
5143 Parameters of planned allocation inside a VmaDeviceMemoryBlock.
5144 
5145 If canMakeOtherLost was false:
5146 - item points to a FREE suballocation.
5147 - itemsToMakeLostCount is 0.
5148 
5149 If canMakeOtherLost was true:
5150 - item points to first of sequence of suballocations, which are either FREE,
5151   or point to VmaAllocations that can become lost.
5152 - itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
5153   the requested allocation to succeed.
5154 */
5155 struct VmaAllocationRequest
5156 {
5157     VkDeviceSize offset;
5158     VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
5159     VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
5160     VmaSuballocationList::iterator item;
5161     size_t itemsToMakeLostCount;
5162     void* customData;
5163 
CalcCostVmaAllocationRequest5164     VkDeviceSize CalcCost() const
5165     {
5166         return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
5167     }
5168 };
5169 
5170 /*
5171 Data structure used for bookkeeping of allocations and unused ranges of memory
5172 in a single VkDeviceMemory block.
5173 */
5174 class VmaBlockMetadata
5175 {
5176 public:
5177     VmaBlockMetadata(VmaAllocator hAllocator);
~VmaBlockMetadata()5178     virtual ~VmaBlockMetadata() { }
Init(VkDeviceSize size)5179     virtual void Init(VkDeviceSize size) { m_Size = size; }
5180 
5181     // Validates all data structures inside this object. If not valid, returns false.
5182     virtual bool Validate() const = 0;
GetSize()5183     VkDeviceSize GetSize() const { return m_Size; }
5184     virtual size_t GetAllocationCount() const = 0;
5185     virtual VkDeviceSize GetSumFreeSize() const = 0;
5186     virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0;
5187     // Returns true if this block is empty - contains only single free suballocation.
5188     virtual bool IsEmpty() const = 0;
5189 
5190     virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
5191     // Shouldn't modify blockCount.
5192     virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
5193 
5194 #if VMA_STATS_STRING_ENABLED
5195     virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
5196 #endif
5197 
5198     // Tries to find a place for suballocation with given parameters inside this block.
5199     // If succeeded, fills pAllocationRequest and returns true.
5200     // If failed, returns false.
5201     virtual bool CreateAllocationRequest(
5202         uint32_t currentFrameIndex,
5203         uint32_t frameInUseCount,
5204         VkDeviceSize bufferImageGranularity,
5205         VkDeviceSize allocSize,
5206         VkDeviceSize allocAlignment,
5207         bool upperAddress,
5208         VmaSuballocationType allocType,
5209         bool canMakeOtherLost,
5210         // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
5211         uint32_t strategy,
5212         VmaAllocationRequest* pAllocationRequest) = 0;
5213 
5214     virtual bool MakeRequestedAllocationsLost(
5215         uint32_t currentFrameIndex,
5216         uint32_t frameInUseCount,
5217         VmaAllocationRequest* pAllocationRequest) = 0;
5218 
5219     virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0;
5220 
5221     virtual VkResult CheckCorruption(const void* pBlockData) = 0;
5222 
5223     // Makes actual allocation based on request. Request must already be checked and valid.
5224     virtual void Alloc(
5225         const VmaAllocationRequest& request,
5226         VmaSuballocationType type,
5227         VkDeviceSize allocSize,
5228         bool upperAddress,
5229         VmaAllocation hAllocation) = 0;
5230 
5231     // Frees suballocation assigned to given memory region.
5232     virtual void Free(const VmaAllocation allocation) = 0;
5233     virtual void FreeAtOffset(VkDeviceSize offset) = 0;
5234 
5235     // Tries to resize (grow or shrink) space for given allocation, in place.
ResizeAllocation(const VmaAllocation,VkDeviceSize)5236     virtual bool ResizeAllocation(const VmaAllocation /*alloc*/, VkDeviceSize /*newSize*/) { return false; }
5237 
5238 protected:
GetAllocationCallbacks()5239     const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
5240 
5241 #if VMA_STATS_STRING_ENABLED
5242     void PrintDetailedMap_Begin(class VmaJsonWriter& json,
5243         VkDeviceSize unusedBytes,
5244         size_t allocationCount,
5245         size_t unusedRangeCount) const;
5246     void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
5247         VkDeviceSize offset,
5248         VmaAllocation hAllocation) const;
5249     void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
5250         VkDeviceSize offset,
5251         VkDeviceSize size) const;
5252     void PrintDetailedMap_End(class VmaJsonWriter& json) const;
5253 #endif
5254 
5255 private:
5256     VkDeviceSize m_Size;
5257     const VkAllocationCallbacks* m_pAllocationCallbacks;
5258 };
5259 
5260 #define VMA_VALIDATE(cond) do { if(!(cond)) { \
5261         VMA_ASSERT(0 && "Validation failed: " #cond); \
5262         return false; \
5263     } } while(false)
5264 
5265 class VmaBlockMetadata_Generic : public VmaBlockMetadata
5266 {
5267     VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
5268 public:
5269     VmaBlockMetadata_Generic(VmaAllocator hAllocator);
5270     virtual ~VmaBlockMetadata_Generic();
5271     virtual void Init(VkDeviceSize size);
5272 
5273     virtual bool Validate() const;
GetAllocationCount()5274     virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
GetSumFreeSize()5275     virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
5276     virtual VkDeviceSize GetUnusedRangeSizeMax() const;
5277     virtual bool IsEmpty() const;
5278 
5279     virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5280     virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5281 
5282 #if VMA_STATS_STRING_ENABLED
5283     virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5284 #endif
5285 
5286     virtual bool CreateAllocationRequest(
5287         uint32_t currentFrameIndex,
5288         uint32_t frameInUseCount,
5289         VkDeviceSize bufferImageGranularity,
5290         VkDeviceSize allocSize,
5291         VkDeviceSize allocAlignment,
5292         bool upperAddress,
5293         VmaSuballocationType allocType,
5294         bool canMakeOtherLost,
5295         uint32_t strategy,
5296         VmaAllocationRequest* pAllocationRequest);
5297 
5298     virtual bool MakeRequestedAllocationsLost(
5299         uint32_t currentFrameIndex,
5300         uint32_t frameInUseCount,
5301         VmaAllocationRequest* pAllocationRequest);
5302 
5303     virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5304 
5305     virtual VkResult CheckCorruption(const void* pBlockData);
5306 
5307     virtual void Alloc(
5308         const VmaAllocationRequest& request,
5309         VmaSuballocationType type,
5310         VkDeviceSize allocSize,
5311         bool upperAddress,
5312         VmaAllocation hAllocation);
5313 
5314     virtual void Free(const VmaAllocation allocation);
5315     virtual void FreeAtOffset(VkDeviceSize offset);
5316 
5317     virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize);
5318 
5319     ////////////////////////////////////////////////////////////////////////////////
5320     // For defragmentation
5321 
5322     bool IsBufferImageGranularityConflictPossible(
5323         VkDeviceSize bufferImageGranularity,
5324         VmaSuballocationType& inOutPrevSuballocType) const;
5325 
5326 private:
5327     friend class VmaDefragmentationAlgorithm_Generic;
5328     friend class VmaDefragmentationAlgorithm_Fast;
5329 
5330     uint32_t m_FreeCount;
5331     VkDeviceSize m_SumFreeSize;
5332     VmaSuballocationList m_Suballocations;
5333     // Suballocations that are free and have size greater than certain threshold.
5334     // Sorted by size, ascending.
5335     VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
5336 
5337     bool ValidateFreeSuballocationList() const;
5338 
5339     // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
5340     // If yes, fills pOffset and returns true. If no, returns false.
5341     bool CheckAllocation(
5342         uint32_t currentFrameIndex,
5343         uint32_t frameInUseCount,
5344         VkDeviceSize bufferImageGranularity,
5345         VkDeviceSize allocSize,
5346         VkDeviceSize allocAlignment,
5347         VmaSuballocationType allocType,
5348         VmaSuballocationList::const_iterator suballocItem,
5349         bool canMakeOtherLost,
5350         VkDeviceSize* pOffset,
5351         size_t* itemsToMakeLostCount,
5352         VkDeviceSize* pSumFreeSize,
5353         VkDeviceSize* pSumItemSize) const;
5354     // Given free suballocation, it merges it with following one, which must also be free.
5355     void MergeFreeWithNext(VmaSuballocationList::iterator item);
5356     // Releases given suballocation, making it free.
5357     // Merges it with adjacent free suballocations if applicable.
5358     // Returns iterator to new free suballocation at this place.
5359     VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
5360     // Given free suballocation, it inserts it into sorted list of
5361     // m_FreeSuballocationsBySize if it's suitable.
5362     void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
5363     // Given free suballocation, it removes it from sorted list of
5364     // m_FreeSuballocationsBySize if it's suitable.
5365     void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
5366 };
5367 
5368 /*
5369 Allocations and their references in internal data structure look like this:
5370 
5371 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
5372 
5373         0 +-------+
5374           |       |
5375           |       |
5376           |       |
5377           +-------+
5378           | Alloc |  1st[m_1stNullItemsBeginCount]
5379           +-------+
5380           | Alloc |  1st[m_1stNullItemsBeginCount + 1]
5381           +-------+
5382           |  ...  |
5383           +-------+
5384           | Alloc |  1st[1st.size() - 1]
5385           +-------+
5386           |       |
5387           |       |
5388           |       |
5389 GetSize() +-------+
5390 
5391 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
5392 
5393         0 +-------+
5394           | Alloc |  2nd[0]
5395           +-------+
5396           | Alloc |  2nd[1]
5397           +-------+
5398           |  ...  |
5399           +-------+
5400           | Alloc |  2nd[2nd.size() - 1]
5401           +-------+
5402           |       |
5403           |       |
5404           |       |
5405           +-------+
5406           | Alloc |  1st[m_1stNullItemsBeginCount]
5407           +-------+
5408           | Alloc |  1st[m_1stNullItemsBeginCount + 1]
5409           +-------+
5410           |  ...  |
5411           +-------+
5412           | Alloc |  1st[1st.size() - 1]
5413           +-------+
5414           |       |
5415 GetSize() +-------+
5416 
5417 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
5418 
5419         0 +-------+
5420           |       |
5421           |       |
5422           |       |
5423           +-------+
5424           | Alloc |  1st[m_1stNullItemsBeginCount]
5425           +-------+
5426           | Alloc |  1st[m_1stNullItemsBeginCount + 1]
5427           +-------+
5428           |  ...  |
5429           +-------+
5430           | Alloc |  1st[1st.size() - 1]
5431           +-------+
5432           |       |
5433           |       |
5434           |       |
5435           +-------+
5436           | Alloc |  2nd[2nd.size() - 1]
5437           +-------+
5438           |  ...  |
5439           +-------+
5440           | Alloc |  2nd[1]
5441           +-------+
5442           | Alloc |  2nd[0]
5443 GetSize() +-------+
5444 
5445 */
5446 class VmaBlockMetadata_Linear : public VmaBlockMetadata
5447 {
5448     VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
5449 public:
5450     VmaBlockMetadata_Linear(VmaAllocator hAllocator);
5451     virtual ~VmaBlockMetadata_Linear();
5452     virtual void Init(VkDeviceSize size);
5453 
5454     virtual bool Validate() const;
5455     virtual size_t GetAllocationCount() const;
GetSumFreeSize()5456     virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
5457     virtual VkDeviceSize GetUnusedRangeSizeMax() const;
IsEmpty()5458     virtual bool IsEmpty() const { return GetAllocationCount() == 0; }
5459 
5460     virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5461     virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5462 
5463 #if VMA_STATS_STRING_ENABLED
5464     virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5465 #endif
5466 
5467     virtual bool CreateAllocationRequest(
5468         uint32_t currentFrameIndex,
5469         uint32_t frameInUseCount,
5470         VkDeviceSize bufferImageGranularity,
5471         VkDeviceSize allocSize,
5472         VkDeviceSize allocAlignment,
5473         bool upperAddress,
5474         VmaSuballocationType allocType,
5475         bool canMakeOtherLost,
5476         uint32_t strategy,
5477         VmaAllocationRequest* pAllocationRequest);
5478 
5479     virtual bool MakeRequestedAllocationsLost(
5480         uint32_t currentFrameIndex,
5481         uint32_t frameInUseCount,
5482         VmaAllocationRequest* pAllocationRequest);
5483 
5484     virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5485 
5486     virtual VkResult CheckCorruption(const void* pBlockData);
5487 
5488     virtual void Alloc(
5489         const VmaAllocationRequest& request,
5490         VmaSuballocationType type,
5491         VkDeviceSize allocSize,
5492         bool upperAddress,
5493         VmaAllocation hAllocation);
5494 
5495     virtual void Free(const VmaAllocation allocation);
5496     virtual void FreeAtOffset(VkDeviceSize offset);
5497 
5498 private:
5499     /*
5500     There are two suballocation vectors, used in ping-pong way.
5501     The one with index m_1stVectorIndex is called 1st.
5502     The one with index (m_1stVectorIndex ^ 1) is called 2nd.
5503     2nd can be non-empty only when 1st is not empty.
5504     When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
5505     */
5506     typedef VmaVector< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > SuballocationVectorType;
5507 
5508     enum SECOND_VECTOR_MODE
5509     {
5510         SECOND_VECTOR_EMPTY,
5511         /*
5512         Suballocations in 2nd vector are created later than the ones in 1st, but they
5513         all have smaller offset.
5514         */
5515         SECOND_VECTOR_RING_BUFFER,
5516         /*
5517         Suballocations in 2nd vector are upper side of double stack.
5518         They all have offsets higher than those in 1st vector.
5519         Top of this stack means smaller offsets, but higher indices in this vector.
5520         */
5521         SECOND_VECTOR_DOUBLE_STACK,
5522     };
5523 
5524     VkDeviceSize m_SumFreeSize;
5525     SuballocationVectorType m_Suballocations0, m_Suballocations1;
5526     uint32_t m_1stVectorIndex;
5527     SECOND_VECTOR_MODE m_2ndVectorMode;
5528 
AccessSuballocations1st()5529     SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
AccessSuballocations2nd()5530     SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
AccessSuballocations1st()5531     const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
AccessSuballocations2nd()5532     const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
5533 
5534     // Number of items in 1st vector with hAllocation = null at the beginning.
5535     size_t m_1stNullItemsBeginCount;
5536     // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
5537     size_t m_1stNullItemsMiddleCount;
5538     // Number of items in 2nd vector with hAllocation = null.
5539     size_t m_2ndNullItemsCount;
5540 
5541     bool ShouldCompact1st() const;
5542     void CleanupAfterFree();
5543 };
5544 
5545 /*
5546 - GetSize() is the original size of allocated memory block.
5547 - m_UsableSize is this size aligned down to a power of two.
5548   All allocations and calculations happen relative to m_UsableSize.
5549 - GetUnusableSize() is the difference between them.
5550   It is repoted as separate, unused range, not available for allocations.
5551 
5552 Node at level 0 has size = m_UsableSize.
5553 Each next level contains nodes with size 2 times smaller than current level.
5554 m_LevelCount is the maximum number of levels to use in the current object.
5555 */
5556 class VmaBlockMetadata_Buddy : public VmaBlockMetadata
5557 {
5558     VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
5559 public:
5560     VmaBlockMetadata_Buddy(VmaAllocator hAllocator);
5561     virtual ~VmaBlockMetadata_Buddy();
5562     virtual void Init(VkDeviceSize size);
5563 
5564     virtual bool Validate() const;
GetAllocationCount()5565     virtual size_t GetAllocationCount() const { return m_AllocationCount; }
GetSumFreeSize()5566     virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); }
5567     virtual VkDeviceSize GetUnusedRangeSizeMax() const;
IsEmpty()5568     virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; }
5569 
5570     virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5571     virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5572 
5573 #if VMA_STATS_STRING_ENABLED
5574     virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5575 #endif
5576 
5577     virtual bool CreateAllocationRequest(
5578         uint32_t currentFrameIndex,
5579         uint32_t frameInUseCount,
5580         VkDeviceSize bufferImageGranularity,
5581         VkDeviceSize allocSize,
5582         VkDeviceSize allocAlignment,
5583         bool upperAddress,
5584         VmaSuballocationType allocType,
5585         bool canMakeOtherLost,
5586         uint32_t strategy,
5587         VmaAllocationRequest* pAllocationRequest);
5588 
5589     virtual bool MakeRequestedAllocationsLost(
5590         uint32_t currentFrameIndex,
5591         uint32_t frameInUseCount,
5592         VmaAllocationRequest* pAllocationRequest);
5593 
5594     virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5595 
CheckCorruption(const void *)5596     virtual VkResult CheckCorruption(const void* /*pBlockData*/) { return VK_ERROR_FEATURE_NOT_PRESENT; }
5597 
5598     virtual void Alloc(
5599         const VmaAllocationRequest& request,
5600         VmaSuballocationType type,
5601         VkDeviceSize allocSize,
5602         bool upperAddress,
5603         VmaAllocation hAllocation);
5604 
Free(const VmaAllocation allocation)5605     virtual void Free(const VmaAllocation allocation) { FreeAtOffset(allocation, allocation->GetOffset()); }
FreeAtOffset(VkDeviceSize offset)5606     virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VMA_NULL, offset); }
5607 
5608 private:
5609     static const VkDeviceSize MIN_NODE_SIZE = 32;
5610     static const size_t MAX_LEVELS = 30;
5611 
5612     struct ValidationContext
5613     {
5614         size_t calculatedAllocationCount;
5615         size_t calculatedFreeCount;
5616         VkDeviceSize calculatedSumFreeSize;
5617 
ValidationContextValidationContext5618         ValidationContext() :
5619             calculatedAllocationCount(0),
5620             calculatedFreeCount(0),
5621             calculatedSumFreeSize(0) { }
5622     };
5623 
5624     struct Node
5625     {
5626         VkDeviceSize offset;
5627         enum TYPE
5628         {
5629             TYPE_FREE,
5630             TYPE_ALLOCATION,
5631             TYPE_SPLIT,
5632             TYPE_COUNT
5633         } type;
5634         Node* parent;
5635         Node* buddy;
5636 
5637         union
5638         {
5639             struct
5640             {
5641                 Node* prev;
5642                 Node* next;
5643             } free;
5644             struct
5645             {
5646                 VmaAllocation alloc;
5647             } allocation;
5648             struct
5649             {
5650                 Node* leftChild;
5651             } split;
5652         };
5653     };
5654 
5655     // Size of the memory block aligned down to a power of two.
5656     VkDeviceSize m_UsableSize;
5657     uint32_t m_LevelCount;
5658 
5659     Node* m_Root;
5660     struct {
5661         Node* front;
5662         Node* back;
5663     } m_FreeList[MAX_LEVELS];
5664     // Number of nodes in the tree with type == TYPE_ALLOCATION.
5665     size_t m_AllocationCount;
5666     // Number of nodes in the tree with type == TYPE_FREE.
5667     size_t m_FreeCount;
5668     // This includes space wasted due to internal fragmentation. Doesn't include unusable size.
5669     VkDeviceSize m_SumFreeSize;
5670 
GetUnusableSize()5671     VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
5672     void DeleteNode(Node* node);
5673     bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
5674     uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
LevelToNodeSize(uint32_t level)5675     inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
5676     // Alloc passed just for validation. Can be null.
5677     void FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset);
5678     void CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const;
5679     // Adds node to the front of FreeList at given level.
5680     // node->type must be FREE.
5681     // node->free.prev, next can be undefined.
5682     void AddToFreeListFront(uint32_t level, Node* node);
5683     // Removes node from FreeList at given level.
5684     // node->type must be FREE.
5685     // node->free.prev, next stay untouched.
5686     void RemoveFromFreeList(uint32_t level, Node* node);
5687 
5688 #if VMA_STATS_STRING_ENABLED
5689     void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
5690 #endif
5691 };
5692 
5693 /*
5694 Represents a single block of device memory (`VkDeviceMemory`) with all the
5695 data about its regions (aka suballocations, #VmaAllocation), assigned and free.
5696 
5697 Thread-safety: This class must be externally synchronized.
5698 */
5699 class VmaDeviceMemoryBlock
5700 {
5701     VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
5702 public:
5703     VmaBlockMetadata* m_pMetadata;
5704 
5705     VmaDeviceMemoryBlock(VmaAllocator hAllocator);
5706 
~VmaDeviceMemoryBlock()5707     ~VmaDeviceMemoryBlock()
5708     {
5709         VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
5710         VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
5711     }
5712 
5713     // Always call after construction.
5714     void Init(
5715         VmaAllocator hAllocator,
5716         uint32_t newMemoryTypeIndex,
5717         VkDeviceMemory newMemory,
5718         VkDeviceSize newSize,
5719         uint32_t id,
5720         uint32_t algorithm);
5721     // Always call before destruction.
5722     void Destroy(VmaAllocator allocator);
5723 
GetDeviceMemory()5724     VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
GetMemoryTypeIndex()5725     uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetId()5726     uint32_t GetId() const { return m_Id; }
GetMappedData()5727     void* GetMappedData() const { return m_pMappedData; }
5728 
5729     // Validates all data structures inside this object. If not valid, returns false.
5730     bool Validate() const;
5731 
5732     VkResult CheckCorruption(VmaAllocator hAllocator);
5733 
5734     // ppData can be null.
5735     VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
5736     void Unmap(VmaAllocator hAllocator, uint32_t count);
5737 
5738     VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5739     VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5740 
5741     VkResult BindBufferMemory(
5742         const VmaAllocator hAllocator,
5743         const VmaAllocation hAllocation,
5744         VkBuffer hBuffer);
5745     VkResult BindImageMemory(
5746         const VmaAllocator hAllocator,
5747         const VmaAllocation hAllocation,
5748         VkImage hImage);
5749 
5750 private:
5751     uint32_t m_MemoryTypeIndex;
5752     uint32_t m_Id;
5753     VkDeviceMemory m_hMemory;
5754 
5755     /*
5756     Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
5757     Also protects m_MapCount, m_pMappedData.
5758     Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
5759     */
5760     VMA_MUTEX m_Mutex;
5761     uint32_t m_MapCount;
5762     void* m_pMappedData;
5763 };
5764 
5765 struct VmaPointerLess
5766 {
operatorVmaPointerLess5767     bool operator()(const void* lhs, const void* rhs) const
5768     {
5769         return lhs < rhs;
5770     }
5771 };
5772 
5773 struct VmaDefragmentationMove
5774 {
5775     size_t srcBlockIndex;
5776     size_t dstBlockIndex;
5777     VkDeviceSize srcOffset;
5778     VkDeviceSize dstOffset;
5779     VkDeviceSize size;
5780 };
5781 
5782 class VmaDefragmentationAlgorithm;
5783 
5784 /*
5785 Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
5786 Vulkan memory type.
5787 
5788 Synchronized internally with a mutex.
5789 */
5790 struct VmaBlockVector
5791 {
5792     VMA_CLASS_NO_COPY(VmaBlockVector)
5793 public:
5794     VmaBlockVector(
5795         VmaAllocator hAllocator,
5796         uint32_t memoryTypeIndex,
5797         VkDeviceSize preferredBlockSize,
5798         size_t minBlockCount,
5799         size_t maxBlockCount,
5800         VkDeviceSize bufferImageGranularity,
5801         uint32_t frameInUseCount,
5802         bool isCustomPool,
5803         bool explicitBlockSize,
5804         uint32_t algorithm);
5805     ~VmaBlockVector();
5806 
5807     VkResult CreateMinBlocks();
5808 
GetMemoryTypeIndexVmaBlockVector5809     uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetPreferredBlockSizeVmaBlockVector5810     VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
GetBufferImageGranularityVmaBlockVector5811     VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
GetFrameInUseCountVmaBlockVector5812     uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
GetAlgorithmVmaBlockVector5813     uint32_t GetAlgorithm() const { return m_Algorithm; }
5814 
5815     void GetPoolStats(VmaPoolStats* pStats);
5816 
IsEmptyVmaBlockVector5817     bool IsEmpty() const { return m_Blocks.empty(); }
5818     bool IsCorruptionDetectionEnabled() const;
5819 
5820     VkResult Allocate(
5821         VmaPool hCurrentPool,
5822         uint32_t currentFrameIndex,
5823         VkDeviceSize size,
5824         VkDeviceSize alignment,
5825         const VmaAllocationCreateInfo& createInfo,
5826         VmaSuballocationType suballocType,
5827         size_t allocationCount,
5828         VmaAllocation* pAllocations);
5829 
5830     void Free(
5831         VmaAllocation hAllocation);
5832 
5833     // Adds statistics of this BlockVector to pStats.
5834     void AddStats(VmaStats* pStats);
5835 
5836 #if VMA_STATS_STRING_ENABLED
5837     void PrintDetailedMap(class VmaJsonWriter& json);
5838 #endif
5839 
5840     void MakePoolAllocationsLost(
5841         uint32_t currentFrameIndex,
5842         size_t* pLostAllocationCount);
5843     VkResult CheckCorruption();
5844 
5845     // Saves results in pCtx->res.
5846     void Defragment(
5847         class VmaBlockVectorDefragmentationContext* pCtx,
5848         VmaDefragmentationStats* pStats,
5849         VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
5850         VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
5851         VkCommandBuffer commandBuffer);
5852     void DefragmentationEnd(
5853         class VmaBlockVectorDefragmentationContext* pCtx,
5854         VmaDefragmentationStats* pStats);
5855 
5856     ////////////////////////////////////////////////////////////////////////////////
5857     // To be used only while the m_Mutex is locked. Used during defragmentation.
5858 
GetBlockCountVmaBlockVector5859     size_t GetBlockCount() const { return m_Blocks.size(); }
GetBlockVmaBlockVector5860     VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
5861     size_t CalcAllocationCount() const;
5862     bool IsBufferImageGranularityConflictPossible() const;
5863 
5864 private:
5865     friend class VmaDefragmentationAlgorithm_Generic;
5866 
5867     const VmaAllocator m_hAllocator;
5868     const uint32_t m_MemoryTypeIndex;
5869     const VkDeviceSize m_PreferredBlockSize;
5870     const size_t m_MinBlockCount;
5871     const size_t m_MaxBlockCount;
5872     const VkDeviceSize m_BufferImageGranularity;
5873     const uint32_t m_FrameInUseCount;
5874     const bool m_IsCustomPool;
5875     const bool m_ExplicitBlockSize;
5876     const uint32_t m_Algorithm;
5877     /* There can be at most one allocation that is completely empty - a
5878     hysteresis to avoid pessimistic case of alternating creation and destruction
5879     of a VkDeviceMemory. */
5880     bool m_HasEmptyBlock;
5881     VMA_RW_MUTEX m_Mutex;
5882     // Incrementally sorted by sumFreeSize, ascending.
5883     VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
5884     uint32_t m_NextBlockId;
5885 
5886     VkDeviceSize CalcMaxBlockSize() const;
5887 
5888     // Finds and removes given block from vector.
5889     void Remove(VmaDeviceMemoryBlock* pBlock);
5890 
5891     // Performs single step in sorting m_Blocks. They may not be fully sorted
5892     // after this call.
5893     void IncrementallySortBlocks();
5894 
5895     VkResult AllocatePage(
5896         VmaPool hCurrentPool,
5897         uint32_t currentFrameIndex,
5898         VkDeviceSize size,
5899         VkDeviceSize alignment,
5900         const VmaAllocationCreateInfo& createInfo,
5901         VmaSuballocationType suballocType,
5902         VmaAllocation* pAllocation);
5903 
5904     // To be used only without CAN_MAKE_OTHER_LOST flag.
5905     VkResult AllocateFromBlock(
5906         VmaDeviceMemoryBlock* pBlock,
5907         VmaPool hCurrentPool,
5908         uint32_t currentFrameIndex,
5909         VkDeviceSize size,
5910         VkDeviceSize alignment,
5911         VmaAllocationCreateFlags allocFlags,
5912         void* pUserData,
5913         VmaSuballocationType suballocType,
5914         uint32_t strategy,
5915         VmaAllocation* pAllocation);
5916 
5917     VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
5918 
5919     // Saves result to pCtx->res.
5920     void ApplyDefragmentationMovesCpu(
5921         class VmaBlockVectorDefragmentationContext* pDefragCtx,
5922         const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
5923     // Saves result to pCtx->res.
5924     void ApplyDefragmentationMovesGpu(
5925         class VmaBlockVectorDefragmentationContext* pDefragCtx,
5926         const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
5927         VkCommandBuffer commandBuffer);
5928 
5929     /*
5930     Used during defragmentation. pDefragmentationStats is optional. It's in/out
5931     - updated with new data.
5932     */
5933     void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
5934 };
5935 
5936 struct VmaPool_T
5937 {
5938     VMA_CLASS_NO_COPY(VmaPool_T)
5939 public:
5940     VmaBlockVector m_BlockVector;
5941 
5942     VmaPool_T(
5943         VmaAllocator hAllocator,
5944         const VmaPoolCreateInfo& createInfo,
5945         VkDeviceSize preferredBlockSize);
5946     ~VmaPool_T();
5947 
GetIdVmaPool_T5948     uint32_t GetId() const { return m_Id; }
SetIdVmaPool_T5949     void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
5950 
5951 #if VMA_STATS_STRING_ENABLED
5952     //void PrintDetailedMap(class VmaStringBuilder& sb);
5953 #endif
5954 
5955 private:
5956     uint32_t m_Id;
5957 };
5958 
5959 /*
5960 Performs defragmentation:
5961 
5962 - Updates `pBlockVector->m_pMetadata`.
5963 - Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
5964 - Does not move actual data, only returns requested moves as `moves`.
5965 */
5966 class VmaDefragmentationAlgorithm
5967 {
VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)5968     VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
5969 public:
5970     VmaDefragmentationAlgorithm(
5971         VmaAllocator hAllocator,
5972         VmaBlockVector* pBlockVector,
5973         uint32_t currentFrameIndex) :
5974         m_hAllocator(hAllocator),
5975         m_pBlockVector(pBlockVector),
5976         m_CurrentFrameIndex(currentFrameIndex)
5977     {
5978     }
~VmaDefragmentationAlgorithm()5979     virtual ~VmaDefragmentationAlgorithm()
5980     {
5981     }
5982 
5983     virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
5984     virtual void AddAll() = 0;
5985 
5986     virtual VkResult Defragment(
5987         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
5988         VkDeviceSize maxBytesToMove,
5989         uint32_t maxAllocationsToMove) = 0;
5990 
5991     virtual VkDeviceSize GetBytesMoved() const = 0;
5992     virtual uint32_t GetAllocationsMoved() const = 0;
5993 
5994 protected:
5995     VmaAllocator const m_hAllocator;
5996     VmaBlockVector* const m_pBlockVector;
5997     const uint32_t m_CurrentFrameIndex;
5998 
5999     struct AllocationInfo
6000     {
6001         VmaAllocation m_hAllocation;
6002         VkBool32* m_pChanged;
6003 
AllocationInfoAllocationInfo6004         AllocationInfo() :
6005             m_hAllocation(VK_NULL_HANDLE),
6006             m_pChanged(VMA_NULL)
6007         {
6008         }
AllocationInfoAllocationInfo6009         AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
6010             m_hAllocation(hAlloc),
6011             m_pChanged(pChanged)
6012         {
6013         }
6014     };
6015 };
6016 
6017 class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
6018 {
6019     VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
6020 public:
6021     VmaDefragmentationAlgorithm_Generic(
6022         VmaAllocator hAllocator,
6023         VmaBlockVector* pBlockVector,
6024         uint32_t currentFrameIndex,
6025         bool overlappingMoveSupported);
6026     virtual ~VmaDefragmentationAlgorithm_Generic();
6027 
6028     virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
AddAll()6029     virtual void AddAll() { m_AllAllocations = true; }
6030 
6031     virtual VkResult Defragment(
6032         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6033         VkDeviceSize maxBytesToMove,
6034         uint32_t maxAllocationsToMove);
6035 
GetBytesMoved()6036     virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
GetAllocationsMoved()6037     virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
6038 
6039 private:
6040     uint32_t m_AllocationCount;
6041     bool m_AllAllocations;
6042 
6043     VkDeviceSize m_BytesMoved;
6044     uint32_t m_AllocationsMoved;
6045 
6046     struct AllocationInfoSizeGreater
6047     {
operatorAllocationInfoSizeGreater6048         bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
6049         {
6050             return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
6051         }
6052     };
6053 
6054     struct AllocationInfoOffsetGreater
6055     {
operatorAllocationInfoOffsetGreater6056         bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
6057         {
6058             return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
6059         }
6060     };
6061 
6062     struct BlockInfo
6063     {
6064         size_t m_OriginalBlockIndex;
6065         VmaDeviceMemoryBlock* m_pBlock;
6066         bool m_HasNonMovableAllocations;
6067         VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
6068 
BlockInfoBlockInfo6069         BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
6070             m_OriginalBlockIndex(SIZE_MAX),
6071             m_pBlock(VMA_NULL),
6072             m_HasNonMovableAllocations(true),
6073             m_Allocations(pAllocationCallbacks)
6074         {
6075         }
6076 
CalcHasNonMovableAllocationsBlockInfo6077         void CalcHasNonMovableAllocations()
6078         {
6079             const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
6080             const size_t defragmentAllocCount = m_Allocations.size();
6081             m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
6082         }
6083 
SortAllocationsBySizeDescendingBlockInfo6084         void SortAllocationsBySizeDescending()
6085         {
6086             VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
6087         }
6088 
SortAllocationsByOffsetDescendingBlockInfo6089         void SortAllocationsByOffsetDescending()
6090         {
6091             VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
6092         }
6093     };
6094 
6095     struct BlockPointerLess
6096     {
operatorBlockPointerLess6097         bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
6098         {
6099             return pLhsBlockInfo->m_pBlock < pRhsBlock;
6100         }
operatorBlockPointerLess6101         bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
6102         {
6103             return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
6104         }
6105     };
6106 
6107     // 1. Blocks with some non-movable allocations go first.
6108     // 2. Blocks with smaller sumFreeSize go first.
6109     struct BlockInfoCompareMoveDestination
6110     {
operatorBlockInfoCompareMoveDestination6111         bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
6112         {
6113             if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
6114             {
6115                 return true;
6116             }
6117             if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
6118             {
6119                 return false;
6120             }
6121             if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
6122             {
6123                 return true;
6124             }
6125             return false;
6126         }
6127     };
6128 
6129     typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
6130     BlockInfoVector m_Blocks;
6131 
6132     VkResult DefragmentRound(
6133         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6134         VkDeviceSize maxBytesToMove,
6135         uint32_t maxAllocationsToMove);
6136 
6137     size_t CalcBlocksWithNonMovableCount() const;
6138 
6139     static bool MoveMakesSense(
6140         size_t dstBlockIndex, VkDeviceSize dstOffset,
6141         size_t srcBlockIndex, VkDeviceSize srcOffset);
6142 };
6143 
6144 class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
6145 {
6146     VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
6147 public:
6148     VmaDefragmentationAlgorithm_Fast(
6149         VmaAllocator hAllocator,
6150         VmaBlockVector* pBlockVector,
6151         uint32_t currentFrameIndex,
6152         bool overlappingMoveSupported);
6153     virtual ~VmaDefragmentationAlgorithm_Fast();
6154 
AddAllocation(VmaAllocation,VkBool32 *)6155     virtual void AddAllocation(VmaAllocation /*hAlloc*/, VkBool32* /*pChanged*/) { ++m_AllocationCount; }
AddAll()6156     virtual void AddAll() { m_AllAllocations = true; }
6157 
6158     virtual VkResult Defragment(
6159         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6160         VkDeviceSize maxBytesToMove,
6161         uint32_t maxAllocationsToMove);
6162 
GetBytesMoved()6163     virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
GetAllocationsMoved()6164     virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
6165 
6166 private:
6167     struct BlockInfo
6168     {
6169         size_t origBlockIndex;
6170     };
6171 
6172     class FreeSpaceDatabase
6173     {
6174     public:
FreeSpaceDatabase()6175         FreeSpaceDatabase()
6176         {
6177             FreeSpace s = {};
6178             s.blockInfoIndex = SIZE_MAX;
6179             for(size_t i = 0; i < MAX_COUNT; ++i)
6180             {
6181                 m_FreeSpaces[i] = s;
6182             }
6183         }
6184 
Register(size_t blockInfoIndex,VkDeviceSize offset,VkDeviceSize size)6185         void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
6186         {
6187             if(size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
6188             {
6189                 return;
6190             }
6191 
6192             // Find first invalid or the smallest structure.
6193             size_t bestIndex = SIZE_MAX;
6194             for(size_t i = 0; i < MAX_COUNT; ++i)
6195             {
6196                 // Empty structure.
6197                 if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
6198                 {
6199                     bestIndex = i;
6200                     break;
6201                 }
6202                 if(m_FreeSpaces[i].size < size &&
6203                     (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
6204                 {
6205                     bestIndex = i;
6206                 }
6207             }
6208 
6209             if(bestIndex != SIZE_MAX)
6210             {
6211                 m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
6212                 m_FreeSpaces[bestIndex].offset = offset;
6213                 m_FreeSpaces[bestIndex].size = size;
6214             }
6215         }
6216 
Fetch(VkDeviceSize alignment,VkDeviceSize size,size_t & outBlockInfoIndex,VkDeviceSize & outDstOffset)6217         bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
6218             size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
6219         {
6220             size_t bestIndex = SIZE_MAX;
6221             VkDeviceSize bestFreeSpaceAfter = 0;
6222             for(size_t i = 0; i < MAX_COUNT; ++i)
6223             {
6224                 // Structure is valid.
6225                 if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
6226                 {
6227                     const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment);
6228                     // Allocation fits into this structure.
6229                     if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
6230                     {
6231                         const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
6232                             (dstOffset + size);
6233                         if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
6234                         {
6235                             bestIndex = i;
6236                             bestFreeSpaceAfter = freeSpaceAfter;
6237                         }
6238                     }
6239                 }
6240             }
6241 
6242             if(bestIndex != SIZE_MAX)
6243             {
6244                 outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
6245                 outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment);
6246 
6247                 if(bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
6248                 {
6249                     // Leave this structure for remaining empty space.
6250                     const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
6251                     m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
6252                     m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
6253                 }
6254                 else
6255                 {
6256                     // This structure becomes invalid.
6257                     m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX;
6258                 }
6259 
6260                 return true;
6261             }
6262 
6263             return false;
6264         }
6265 
6266     private:
6267         static const size_t MAX_COUNT = 4;
6268 
6269         struct FreeSpace
6270         {
6271             size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
6272             VkDeviceSize offset;
6273             VkDeviceSize size;
6274         } m_FreeSpaces[MAX_COUNT];
6275     };
6276 
6277     const bool m_OverlappingMoveSupported;
6278 
6279     uint32_t m_AllocationCount;
6280     bool m_AllAllocations;
6281 
6282     VkDeviceSize m_BytesMoved;
6283     uint32_t m_AllocationsMoved;
6284 
6285     VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
6286 
6287     void PreprocessMetadata();
6288     void PostprocessMetadata();
6289     void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
6290 };
6291 
6292 struct VmaBlockDefragmentationContext
6293 {
6294     enum BLOCK_FLAG
6295     {
6296         BLOCK_FLAG_USED = 0x00000001,
6297     };
6298     uint32_t flags;
6299     VkBuffer hBuffer;
6300 
VmaBlockDefragmentationContextVmaBlockDefragmentationContext6301     VmaBlockDefragmentationContext() :
6302         flags(0),
6303         hBuffer(VK_NULL_HANDLE)
6304     {
6305     }
6306 };
6307 
6308 class VmaBlockVectorDefragmentationContext
6309 {
6310     VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
6311 public:
6312     VkResult res;
6313     bool mutexLocked;
6314     VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
6315 
6316     VmaBlockVectorDefragmentationContext(
6317         VmaAllocator hAllocator,
6318         VmaPool hCustomPool, // Optional.
6319         VmaBlockVector* pBlockVector,
6320         uint32_t currFrameIndex,
6321         uint32_t flags);
6322     ~VmaBlockVectorDefragmentationContext();
6323 
GetCustomPool()6324     VmaPool GetCustomPool() const { return m_hCustomPool; }
GetBlockVector()6325     VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
GetAlgorithm()6326     VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
6327 
6328     void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
AddAll()6329     void AddAll() { m_AllAllocations = true; }
6330 
6331     void Begin(bool overlappingMoveSupported);
6332 
6333 private:
6334     const VmaAllocator m_hAllocator;
6335     // Null if not from custom pool.
6336     const VmaPool m_hCustomPool;
6337     // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
6338     VmaBlockVector* const m_pBlockVector;
6339     const uint32_t m_CurrFrameIndex;
6340     /*const uint32_t m_AlgorithmFlags;*/
6341     // Owner of this object.
6342     VmaDefragmentationAlgorithm* m_pAlgorithm;
6343 
6344     struct AllocInfo
6345     {
6346         VmaAllocation hAlloc;
6347         VkBool32* pChanged;
6348     };
6349     // Used between constructor and Begin.
6350     VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
6351     bool m_AllAllocations;
6352 };
6353 
6354 struct VmaDefragmentationContext_T
6355 {
6356 private:
6357     VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
6358 public:
6359     VmaDefragmentationContext_T(
6360         VmaAllocator hAllocator,
6361         uint32_t currFrameIndex,
6362         uint32_t flags,
6363         VmaDefragmentationStats* pStats);
6364     ~VmaDefragmentationContext_T();
6365 
6366     void AddPools(uint32_t poolCount, VmaPool* pPools);
6367     void AddAllocations(
6368         uint32_t allocationCount,
6369         VmaAllocation* pAllocations,
6370         VkBool32* pAllocationsChanged);
6371 
6372     /*
6373     Returns:
6374     - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
6375     - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
6376     - Negative value if error occured and object can be destroyed immediately.
6377     */
6378     VkResult Defragment(
6379         VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
6380         VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
6381         VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats);
6382 
6383 private:
6384     const VmaAllocator m_hAllocator;
6385     const uint32_t m_CurrFrameIndex;
6386     const uint32_t m_Flags;
6387     VmaDefragmentationStats* const m_pStats;
6388     // Owner of these objects.
6389     VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
6390     // Owner of these objects.
6391     VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
6392 };
6393 
6394 #if VMA_RECORDING_ENABLED
6395 
6396 class VmaRecorder
6397 {
6398 public:
6399     VmaRecorder();
6400     VkResult Init(const VmaRecordSettings& settings, bool useMutex);
6401     void WriteConfiguration(
6402         const VkPhysicalDeviceProperties& devProps,
6403         const VkPhysicalDeviceMemoryProperties& memProps,
6404         bool dedicatedAllocationExtensionEnabled);
6405     ~VmaRecorder();
6406 
6407     void RecordCreateAllocator(uint32_t frameIndex);
6408     void RecordDestroyAllocator(uint32_t frameIndex);
6409     void RecordCreatePool(uint32_t frameIndex,
6410         const VmaPoolCreateInfo& createInfo,
6411         VmaPool pool);
6412     void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
6413     void RecordAllocateMemory(uint32_t frameIndex,
6414         const VkMemoryRequirements& vkMemReq,
6415         const VmaAllocationCreateInfo& createInfo,
6416         VmaAllocation allocation);
6417     void RecordAllocateMemoryPages(uint32_t frameIndex,
6418         const VkMemoryRequirements& vkMemReq,
6419         const VmaAllocationCreateInfo& createInfo,
6420         uint64_t allocationCount,
6421         const VmaAllocation* pAllocations);
6422     void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
6423         const VkMemoryRequirements& vkMemReq,
6424         bool requiresDedicatedAllocation,
6425         bool prefersDedicatedAllocation,
6426         const VmaAllocationCreateInfo& createInfo,
6427         VmaAllocation allocation);
6428     void RecordAllocateMemoryForImage(uint32_t frameIndex,
6429         const VkMemoryRequirements& vkMemReq,
6430         bool requiresDedicatedAllocation,
6431         bool prefersDedicatedAllocation,
6432         const VmaAllocationCreateInfo& createInfo,
6433         VmaAllocation allocation);
6434     void RecordFreeMemory(uint32_t frameIndex,
6435         VmaAllocation allocation);
6436     void RecordFreeMemoryPages(uint32_t frameIndex,
6437         uint64_t allocationCount,
6438         const VmaAllocation* pAllocations);
6439     void RecordResizeAllocation(
6440         uint32_t frameIndex,
6441         VmaAllocation allocation,
6442         VkDeviceSize newSize);
6443     void RecordSetAllocationUserData(uint32_t frameIndex,
6444         VmaAllocation allocation,
6445         const void* pUserData);
6446     void RecordCreateLostAllocation(uint32_t frameIndex,
6447         VmaAllocation allocation);
6448     void RecordMapMemory(uint32_t frameIndex,
6449         VmaAllocation allocation);
6450     void RecordUnmapMemory(uint32_t frameIndex,
6451         VmaAllocation allocation);
6452     void RecordFlushAllocation(uint32_t frameIndex,
6453         VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
6454     void RecordInvalidateAllocation(uint32_t frameIndex,
6455         VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
6456     void RecordCreateBuffer(uint32_t frameIndex,
6457         const VkBufferCreateInfo& bufCreateInfo,
6458         const VmaAllocationCreateInfo& allocCreateInfo,
6459         VmaAllocation allocation);
6460     void RecordCreateImage(uint32_t frameIndex,
6461         const VkImageCreateInfo& imageCreateInfo,
6462         const VmaAllocationCreateInfo& allocCreateInfo,
6463         VmaAllocation allocation);
6464     void RecordDestroyBuffer(uint32_t frameIndex,
6465         VmaAllocation allocation);
6466     void RecordDestroyImage(uint32_t frameIndex,
6467         VmaAllocation allocation);
6468     void RecordTouchAllocation(uint32_t frameIndex,
6469         VmaAllocation allocation);
6470     void RecordGetAllocationInfo(uint32_t frameIndex,
6471         VmaAllocation allocation);
6472     void RecordMakePoolAllocationsLost(uint32_t frameIndex,
6473         VmaPool pool);
6474     void RecordDefragmentationBegin(uint32_t frameIndex,
6475         const VmaDefragmentationInfo2& info,
6476         VmaDefragmentationContext ctx);
6477     void RecordDefragmentationEnd(uint32_t frameIndex,
6478         VmaDefragmentationContext ctx);
6479 
6480 private:
6481     struct CallParams
6482     {
6483         uint32_t threadId;
6484         double time;
6485     };
6486 
6487     class UserDataString
6488     {
6489     public:
6490         UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
GetString()6491         const char* GetString() const { return m_Str; }
6492 
6493     private:
6494         char m_PtrStr[17];
6495         const char* m_Str;
6496     };
6497 
6498     bool m_UseMutex;
6499     VmaRecordFlags m_Flags;
6500     FILE* m_File;
6501     VMA_MUTEX m_FileMutex;
6502     int64_t m_Freq;
6503     int64_t m_StartCounter;
6504 
6505     void GetBasicParams(CallParams& outParams);
6506 
6507     // T must be a pointer type, e.g. VmaAllocation, VmaPool.
6508     template<typename T>
PrintPointerList(uint64_t count,const T * pItems)6509     void PrintPointerList(uint64_t count, const T* pItems)
6510     {
6511         if(count)
6512         {
6513             fprintf(m_File, "%p", pItems[0]);
6514             for(uint64_t i = 1; i < count; ++i)
6515             {
6516                 fprintf(m_File, " %p", pItems[i]);
6517             }
6518         }
6519     }
6520 
6521     void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
6522     void Flush();
6523 };
6524 
6525 #endif // #if VMA_RECORDING_ENABLED
6526 
6527 // Main allocator object.
6528 struct VmaAllocator_T
6529 {
6530     VMA_CLASS_NO_COPY(VmaAllocator_T)
6531 public:
6532     bool m_UseMutex;
6533     bool m_UseKhrDedicatedAllocation;
6534     VkDevice m_hDevice;
6535     bool m_AllocationCallbacksSpecified;
6536     VkAllocationCallbacks m_AllocationCallbacks;
6537     VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
6538 
6539     // Number of bytes free out of limit, or VK_WHOLE_SIZE if no limit for that heap.
6540     VkDeviceSize m_HeapSizeLimit[VK_MAX_MEMORY_HEAPS];
6541     VMA_MUTEX m_HeapSizeLimitMutex;
6542 
6543     VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
6544     VkPhysicalDeviceMemoryProperties m_MemProps;
6545 
6546     // Default pools.
6547     VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
6548 
6549     // Each vector is sorted by memory (handle value).
6550     typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
6551     AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES];
6552     VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
6553 
6554     VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
6555     VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
6556     ~VmaAllocator_T();
6557 
GetAllocationCallbacksVmaAllocator_T6558     const VkAllocationCallbacks* GetAllocationCallbacks() const
6559     {
6560         return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
6561     }
GetVulkanFunctionsVmaAllocator_T6562     const VmaVulkanFunctions& GetVulkanFunctions() const
6563     {
6564         return m_VulkanFunctions;
6565     }
6566 
GetBufferImageGranularityVmaAllocator_T6567     VkDeviceSize GetBufferImageGranularity() const
6568     {
6569         return VMA_MAX(
6570             static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
6571             m_PhysicalDeviceProperties.limits.bufferImageGranularity);
6572     }
6573 
GetMemoryHeapCountVmaAllocator_T6574     uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
GetMemoryTypeCountVmaAllocator_T6575     uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
6576 
MemoryTypeIndexToHeapIndexVmaAllocator_T6577     uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
6578     {
6579         VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
6580         return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
6581     }
6582     // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
IsMemoryTypeNonCoherentVmaAllocator_T6583     bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
6584     {
6585         return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
6586             VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
6587     }
6588     // Minimum alignment for all allocations in specific memory type.
GetMemoryTypeMinAlignmentVmaAllocator_T6589     VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
6590     {
6591         return IsMemoryTypeNonCoherent(memTypeIndex) ?
6592             VMA_MAX((VkDeviceSize)VMA_DEBUG_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
6593             (VkDeviceSize)VMA_DEBUG_ALIGNMENT;
6594     }
6595 
IsIntegratedGpuVmaAllocator_T6596     bool IsIntegratedGpu() const
6597     {
6598         return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
6599     }
6600 
6601 #if VMA_RECORDING_ENABLED
GetRecorderVmaAllocator_T6602     VmaRecorder* GetRecorder() const { return m_pRecorder; }
6603 #endif
6604 
6605     void GetBufferMemoryRequirements(
6606         VkBuffer hBuffer,
6607         VkMemoryRequirements& memReq,
6608         bool& requiresDedicatedAllocation,
6609         bool& prefersDedicatedAllocation) const;
6610     void GetImageMemoryRequirements(
6611         VkImage hImage,
6612         VkMemoryRequirements& memReq,
6613         bool& requiresDedicatedAllocation,
6614         bool& prefersDedicatedAllocation) const;
6615 
6616     // Main allocation function.
6617     VkResult AllocateMemory(
6618         const VkMemoryRequirements& vkMemReq,
6619         bool requiresDedicatedAllocation,
6620         bool prefersDedicatedAllocation,
6621         VkBuffer dedicatedBuffer,
6622         VkImage dedicatedImage,
6623         const VmaAllocationCreateInfo& createInfo,
6624         VmaSuballocationType suballocType,
6625         size_t allocationCount,
6626         VmaAllocation* pAllocations);
6627 
6628     // Main deallocation function.
6629     void FreeMemory(
6630         size_t allocationCount,
6631         const VmaAllocation* pAllocations);
6632 
6633     VkResult ResizeAllocation(
6634         const VmaAllocation alloc,
6635         VkDeviceSize newSize);
6636 
6637     void CalculateStats(VmaStats* pStats);
6638 
6639 #if VMA_STATS_STRING_ENABLED
6640     void PrintDetailedMap(class VmaJsonWriter& json);
6641 #endif
6642 
6643     VkResult DefragmentationBegin(
6644         const VmaDefragmentationInfo2& info,
6645         VmaDefragmentationStats* pStats,
6646         VmaDefragmentationContext* pContext);
6647     VkResult DefragmentationEnd(
6648         VmaDefragmentationContext context);
6649 
6650     void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
6651     bool TouchAllocation(VmaAllocation hAllocation);
6652 
6653     VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
6654     void DestroyPool(VmaPool pool);
6655     void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
6656 
6657     void SetCurrentFrameIndex(uint32_t frameIndex);
GetCurrentFrameIndexVmaAllocator_T6658     uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
6659 
6660     void MakePoolAllocationsLost(
6661         VmaPool hPool,
6662         size_t* pLostAllocationCount);
6663     VkResult CheckPoolCorruption(VmaPool hPool);
6664     VkResult CheckCorruption(uint32_t memoryTypeBits);
6665 
6666     void CreateLostAllocation(VmaAllocation* pAllocation);
6667 
6668     VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
6669     void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
6670 
6671     VkResult Map(VmaAllocation hAllocation, void** ppData);
6672     void Unmap(VmaAllocation hAllocation);
6673 
6674     VkResult BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer);
6675     VkResult BindImageMemory(VmaAllocation hAllocation, VkImage hImage);
6676 
6677     void FlushOrInvalidateAllocation(
6678         VmaAllocation hAllocation,
6679         VkDeviceSize offset, VkDeviceSize size,
6680         VMA_CACHE_OPERATION op);
6681 
6682     void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
6683 
6684 private:
6685     VkDeviceSize m_PreferredLargeHeapBlockSize;
6686 
6687     VkPhysicalDevice m_PhysicalDevice;
6688     VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
6689 
6690     VMA_RW_MUTEX m_PoolsMutex;
6691     // Protected by m_PoolsMutex. Sorted by pointer value.
6692     VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools;
6693     uint32_t m_NextPoolId;
6694 
6695     VmaVulkanFunctions m_VulkanFunctions;
6696 
6697 #if VMA_RECORDING_ENABLED
6698     VmaRecorder* m_pRecorder;
6699 #endif
6700 
6701     void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
6702 
6703     VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
6704 
6705     VkResult AllocateMemoryOfType(
6706         VkDeviceSize size,
6707         VkDeviceSize alignment,
6708         bool dedicatedAllocation,
6709         VkBuffer dedicatedBuffer,
6710         VkImage dedicatedImage,
6711         const VmaAllocationCreateInfo& createInfo,
6712         uint32_t memTypeIndex,
6713         VmaSuballocationType suballocType,
6714         size_t allocationCount,
6715         VmaAllocation* pAllocations);
6716 
6717     // Helper function only to be used inside AllocateDedicatedMemory.
6718     VkResult AllocateDedicatedMemoryPage(
6719         VkDeviceSize size,
6720         VmaSuballocationType suballocType,
6721         uint32_t memTypeIndex,
6722         const VkMemoryAllocateInfo& allocInfo,
6723         bool map,
6724         bool isUserDataString,
6725         void* pUserData,
6726         VmaAllocation* pAllocation);
6727 
6728     // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
6729     VkResult AllocateDedicatedMemory(
6730         VkDeviceSize size,
6731         VmaSuballocationType suballocType,
6732         uint32_t memTypeIndex,
6733         bool map,
6734         bool isUserDataString,
6735         void* pUserData,
6736         VkBuffer dedicatedBuffer,
6737         VkImage dedicatedImage,
6738         size_t allocationCount,
6739         VmaAllocation* pAllocations);
6740 
6741     // Tries to free pMemory as Dedicated Memory. Returns true if found and freed.
6742     void FreeDedicatedMemory(VmaAllocation allocation);
6743 };
6744 
6745 ////////////////////////////////////////////////////////////////////////////////
6746 // Memory allocation #2 after VmaAllocator_T definition
6747 
VmaMalloc(VmaAllocator hAllocator,size_t size,size_t alignment)6748 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
6749 {
6750     return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
6751 }
6752 
VmaFree(VmaAllocator hAllocator,void * ptr)6753 static void VmaFree(VmaAllocator hAllocator, void* ptr)
6754 {
6755     VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
6756 }
6757 
6758 template<typename T>
VmaAllocate(VmaAllocator hAllocator)6759 static T* VmaAllocate(VmaAllocator hAllocator)
6760 {
6761     return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
6762 }
6763 
6764 template<typename T>
VmaAllocateArray(VmaAllocator hAllocator,size_t count)6765 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
6766 {
6767     return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
6768 }
6769 
6770 template<typename T>
vma_delete(VmaAllocator hAllocator,T * ptr)6771 static void vma_delete(VmaAllocator hAllocator, T* ptr)
6772 {
6773     if(ptr != VMA_NULL)
6774     {
6775         ptr->~T();
6776         VmaFree(hAllocator, ptr);
6777     }
6778 }
6779 
6780 template<typename T>
vma_delete_array(VmaAllocator hAllocator,T * ptr,size_t count)6781 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
6782 {
6783     if(ptr != VMA_NULL)
6784     {
6785         for(size_t i = count; i--; )
6786             ptr[i].~T();
6787         VmaFree(hAllocator, ptr);
6788     }
6789 }
6790 
6791 ////////////////////////////////////////////////////////////////////////////////
6792 // VmaStringBuilder
6793 
6794 #if VMA_STATS_STRING_ENABLED
6795 
6796 class VmaStringBuilder
6797 {
6798 public:
VmaStringBuilder(VmaAllocator alloc)6799     VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
GetLength()6800     size_t GetLength() const { return m_Data.size(); }
GetData()6801     const char* GetData() const { return m_Data.data(); }
6802 
Add(char ch)6803     void Add(char ch) { m_Data.push_back(ch); }
6804     void Add(const char* pStr);
AddNewLine()6805     void AddNewLine() { Add('\n'); }
6806     void AddNumber(uint32_t num);
6807     void AddNumber(uint64_t num);
6808     void AddPointer(const void* ptr);
6809 
6810 private:
6811     VmaVector< char, VmaStlAllocator<char> > m_Data;
6812 };
6813 
Add(const char * pStr)6814 void VmaStringBuilder::Add(const char* pStr)
6815 {
6816     const size_t strLen = strlen(pStr);
6817     if(strLen > 0)
6818     {
6819         const size_t oldCount = m_Data.size();
6820         m_Data.resize(oldCount + strLen);
6821         memcpy(m_Data.data() + oldCount, pStr, strLen);
6822     }
6823 }
6824 
AddNumber(uint32_t num)6825 void VmaStringBuilder::AddNumber(uint32_t num)
6826 {
6827     char buf[11];
6828     VmaUint32ToStr(buf, sizeof(buf), num);
6829     Add(buf);
6830 }
6831 
AddNumber(uint64_t num)6832 void VmaStringBuilder::AddNumber(uint64_t num)
6833 {
6834     char buf[21];
6835     VmaUint64ToStr(buf, sizeof(buf), num);
6836     Add(buf);
6837 }
6838 
AddPointer(const void * ptr)6839 void VmaStringBuilder::AddPointer(const void* ptr)
6840 {
6841     char buf[21];
6842     VmaPtrToStr(buf, sizeof(buf), ptr);
6843     Add(buf);
6844 }
6845 
6846 #endif // #if VMA_STATS_STRING_ENABLED
6847 
6848 ////////////////////////////////////////////////////////////////////////////////
6849 // VmaJsonWriter
6850 
6851 #if VMA_STATS_STRING_ENABLED
6852 
6853 class VmaJsonWriter
6854 {
6855     VMA_CLASS_NO_COPY(VmaJsonWriter)
6856 public:
6857     VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
6858     ~VmaJsonWriter();
6859 
6860     void BeginObject(bool singleLine = false);
6861     void EndObject();
6862 
6863     void BeginArray(bool singleLine = false);
6864     void EndArray();
6865 
6866     void WriteString(const char* pStr);
6867     void BeginString(const char* pStr = VMA_NULL);
6868     void ContinueString(const char* pStr);
6869     void ContinueString(uint32_t n);
6870     void ContinueString(uint64_t n);
6871     void ContinueString_Pointer(const void* ptr);
6872     void EndString(const char* pStr = VMA_NULL);
6873 
6874     void WriteNumber(uint32_t n);
6875     void WriteNumber(uint64_t n);
6876     void WriteBool(bool b);
6877     void WriteNull();
6878 
6879 private:
6880     static const char* const INDENT;
6881 
6882     enum COLLECTION_TYPE
6883     {
6884         COLLECTION_TYPE_OBJECT,
6885         COLLECTION_TYPE_ARRAY,
6886     };
6887     struct StackItem
6888     {
6889         COLLECTION_TYPE type;
6890         uint32_t valueCount;
6891         bool singleLineMode;
6892     };
6893 
6894     VmaStringBuilder& m_SB;
6895     VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
6896     bool m_InsideString;
6897 
6898     void BeginValue(bool isString);
6899     void WriteIndent(bool oneLess = false);
6900 };
6901 
6902 const char* const VmaJsonWriter::INDENT = "  ";
6903 
VmaJsonWriter(const VkAllocationCallbacks * pAllocationCallbacks,VmaStringBuilder & sb)6904 VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
6905     m_SB(sb),
6906     m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
6907     m_InsideString(false)
6908 {
6909 }
6910 
~VmaJsonWriter()6911 VmaJsonWriter::~VmaJsonWriter()
6912 {
6913     VMA_ASSERT(!m_InsideString);
6914     VMA_ASSERT(m_Stack.empty());
6915 }
6916 
BeginObject(bool singleLine)6917 void VmaJsonWriter::BeginObject(bool singleLine)
6918 {
6919     VMA_ASSERT(!m_InsideString);
6920 
6921     BeginValue(false);
6922     m_SB.Add('{');
6923 
6924     StackItem item;
6925     item.type = COLLECTION_TYPE_OBJECT;
6926     item.valueCount = 0;
6927     item.singleLineMode = singleLine;
6928     m_Stack.push_back(item);
6929 }
6930 
EndObject()6931 void VmaJsonWriter::EndObject()
6932 {
6933     VMA_ASSERT(!m_InsideString);
6934 
6935     WriteIndent(true);
6936     m_SB.Add('}');
6937 
6938     VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
6939     m_Stack.pop_back();
6940 }
6941 
BeginArray(bool singleLine)6942 void VmaJsonWriter::BeginArray(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_ARRAY;
6951     item.valueCount = 0;
6952     item.singleLineMode = singleLine;
6953     m_Stack.push_back(item);
6954 }
6955 
EndArray()6956 void VmaJsonWriter::EndArray()
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_ARRAY);
6964     m_Stack.pop_back();
6965 }
6966 
WriteString(const char * pStr)6967 void VmaJsonWriter::WriteString(const char* pStr)
6968 {
6969     BeginString(pStr);
6970     EndString();
6971 }
6972 
BeginString(const char * pStr)6973 void VmaJsonWriter::BeginString(const char* pStr)
6974 {
6975     VMA_ASSERT(!m_InsideString);
6976 
6977     BeginValue(true);
6978     m_SB.Add('"');
6979     m_InsideString = true;
6980     if(pStr != VMA_NULL && pStr[0] != '\0')
6981     {
6982         ContinueString(pStr);
6983     }
6984 }
6985 
ContinueString(const char * pStr)6986 void VmaJsonWriter::ContinueString(const char* pStr)
6987 {
6988     VMA_ASSERT(m_InsideString);
6989 
6990     const size_t strLen = strlen(pStr);
6991     for(size_t i = 0; i < strLen; ++i)
6992     {
6993         char ch = pStr[i];
6994         if(ch == '\\')
6995         {
6996             m_SB.Add("\\\\");
6997         }
6998         else if(ch == '"')
6999         {
7000             m_SB.Add("\\\"");
7001         }
7002         else if(ch >= 32)
7003         {
7004             m_SB.Add(ch);
7005         }
7006         else switch(ch)
7007         {
7008         case '\b':
7009             m_SB.Add("\\b");
7010             break;
7011         case '\f':
7012             m_SB.Add("\\f");
7013             break;
7014         case '\n':
7015             m_SB.Add("\\n");
7016             break;
7017         case '\r':
7018             m_SB.Add("\\r");
7019             break;
7020         case '\t':
7021             m_SB.Add("\\t");
7022             break;
7023         default:
7024             VMA_ASSERT(0 && "Character not currently supported.");
7025             break;
7026         }
7027     }
7028 }
7029 
ContinueString(uint32_t n)7030 void VmaJsonWriter::ContinueString(uint32_t n)
7031 {
7032     VMA_ASSERT(m_InsideString);
7033     m_SB.AddNumber(n);
7034 }
7035 
ContinueString(uint64_t n)7036 void VmaJsonWriter::ContinueString(uint64_t n)
7037 {
7038     VMA_ASSERT(m_InsideString);
7039     m_SB.AddNumber(n);
7040 }
7041 
ContinueString_Pointer(const void * ptr)7042 void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
7043 {
7044     VMA_ASSERT(m_InsideString);
7045     m_SB.AddPointer(ptr);
7046 }
7047 
EndString(const char * pStr)7048 void VmaJsonWriter::EndString(const char* pStr)
7049 {
7050     VMA_ASSERT(m_InsideString);
7051     if(pStr != VMA_NULL && pStr[0] != '\0')
7052     {
7053         ContinueString(pStr);
7054     }
7055     m_SB.Add('"');
7056     m_InsideString = false;
7057 }
7058 
WriteNumber(uint32_t n)7059 void VmaJsonWriter::WriteNumber(uint32_t n)
7060 {
7061     VMA_ASSERT(!m_InsideString);
7062     BeginValue(false);
7063     m_SB.AddNumber(n);
7064 }
7065 
WriteNumber(uint64_t n)7066 void VmaJsonWriter::WriteNumber(uint64_t n)
7067 {
7068     VMA_ASSERT(!m_InsideString);
7069     BeginValue(false);
7070     m_SB.AddNumber(n);
7071 }
7072 
WriteBool(bool b)7073 void VmaJsonWriter::WriteBool(bool b)
7074 {
7075     VMA_ASSERT(!m_InsideString);
7076     BeginValue(false);
7077     m_SB.Add(b ? "true" : "false");
7078 }
7079 
WriteNull()7080 void VmaJsonWriter::WriteNull()
7081 {
7082     VMA_ASSERT(!m_InsideString);
7083     BeginValue(false);
7084     m_SB.Add("null");
7085 }
7086 
BeginValue(bool isString)7087 void VmaJsonWriter::BeginValue(bool isString)
7088 {
7089     if(!m_Stack.empty())
7090     {
7091         StackItem& currItem = m_Stack.back();
7092         if(currItem.type == COLLECTION_TYPE_OBJECT &&
7093             currItem.valueCount % 2 == 0)
7094         {
7095             (void) isString;
7096             VMA_ASSERT(isString);
7097         }
7098 
7099         if(currItem.type == COLLECTION_TYPE_OBJECT &&
7100             currItem.valueCount % 2 != 0)
7101         {
7102             m_SB.Add(": ");
7103         }
7104         else if(currItem.valueCount > 0)
7105         {
7106             m_SB.Add(", ");
7107             WriteIndent();
7108         }
7109         else
7110         {
7111             WriteIndent();
7112         }
7113         ++currItem.valueCount;
7114     }
7115 }
7116 
WriteIndent(bool oneLess)7117 void VmaJsonWriter::WriteIndent(bool oneLess)
7118 {
7119     if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
7120     {
7121         m_SB.AddNewLine();
7122 
7123         size_t count = m_Stack.size();
7124         if(count > 0 && oneLess)
7125         {
7126             --count;
7127         }
7128         for(size_t i = 0; i < count; ++i)
7129         {
7130             m_SB.Add(INDENT);
7131         }
7132     }
7133 }
7134 
7135 #endif // #if VMA_STATS_STRING_ENABLED
7136 
7137 ////////////////////////////////////////////////////////////////////////////////
7138 
SetUserData(VmaAllocator hAllocator,void * pUserData)7139 void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
7140 {
7141     if(IsUserDataString())
7142     {
7143         VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
7144 
7145         FreeUserDataString(hAllocator);
7146 
7147         if(pUserData != VMA_NULL)
7148         {
7149             const char* const newStrSrc = (char*)pUserData;
7150             const size_t newStrLen = strlen(newStrSrc);
7151             char* const newStrDst = vma_new_array(hAllocator, char, newStrLen + 1);
7152             memcpy(newStrDst, newStrSrc, newStrLen + 1);
7153             m_pUserData = newStrDst;
7154         }
7155     }
7156     else
7157     {
7158         m_pUserData = pUserData;
7159     }
7160 }
7161 
ChangeBlockAllocation(VmaAllocator hAllocator,VmaDeviceMemoryBlock * block,VkDeviceSize offset)7162 void VmaAllocation_T::ChangeBlockAllocation(
7163     VmaAllocator hAllocator,
7164     VmaDeviceMemoryBlock* block,
7165     VkDeviceSize offset)
7166 {
7167     VMA_ASSERT(block != VMA_NULL);
7168     VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7169 
7170     // Move mapping reference counter from old block to new block.
7171     if(block != m_BlockAllocation.m_Block)
7172     {
7173         uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
7174         if(IsPersistentMap())
7175             ++mapRefCount;
7176         m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
7177         block->Map(hAllocator, mapRefCount, VMA_NULL);
7178     }
7179 
7180     m_BlockAllocation.m_Block = block;
7181     m_BlockAllocation.m_Offset = offset;
7182 }
7183 
ChangeSize(VkDeviceSize newSize)7184 void VmaAllocation_T::ChangeSize(VkDeviceSize newSize)
7185 {
7186     VMA_ASSERT(newSize > 0);
7187     m_Size = newSize;
7188 }
7189 
ChangeOffset(VkDeviceSize newOffset)7190 void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
7191 {
7192     VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7193     m_BlockAllocation.m_Offset = newOffset;
7194 }
7195 
GetOffset()7196 VkDeviceSize VmaAllocation_T::GetOffset() const
7197 {
7198     switch(m_Type)
7199     {
7200     case ALLOCATION_TYPE_BLOCK:
7201         return m_BlockAllocation.m_Offset;
7202     case ALLOCATION_TYPE_DEDICATED:
7203         return 0;
7204     default:
7205         VMA_ASSERT(0);
7206         return 0;
7207     }
7208 }
7209 
GetMemory()7210 VkDeviceMemory VmaAllocation_T::GetMemory() const
7211 {
7212     switch(m_Type)
7213     {
7214     case ALLOCATION_TYPE_BLOCK:
7215         return m_BlockAllocation.m_Block->GetDeviceMemory();
7216     case ALLOCATION_TYPE_DEDICATED:
7217         return m_DedicatedAllocation.m_hMemory;
7218     default:
7219         VMA_ASSERT(0);
7220         return VK_NULL_HANDLE;
7221     }
7222 }
7223 
GetMemoryTypeIndex()7224 uint32_t VmaAllocation_T::GetMemoryTypeIndex() const
7225 {
7226     switch(m_Type)
7227     {
7228     case ALLOCATION_TYPE_BLOCK:
7229         return m_BlockAllocation.m_Block->GetMemoryTypeIndex();
7230     case ALLOCATION_TYPE_DEDICATED:
7231         return m_DedicatedAllocation.m_MemoryTypeIndex;
7232     default:
7233         VMA_ASSERT(0);
7234         return UINT32_MAX;
7235     }
7236 }
7237 
GetMappedData()7238 void* VmaAllocation_T::GetMappedData() const
7239 {
7240     switch(m_Type)
7241     {
7242     case ALLOCATION_TYPE_BLOCK:
7243         if(m_MapCount != 0)
7244         {
7245             void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
7246             VMA_ASSERT(pBlockData != VMA_NULL);
7247             return (char*)pBlockData + m_BlockAllocation.m_Offset;
7248         }
7249         else
7250         {
7251             return VMA_NULL;
7252         }
7253         break;
7254     case ALLOCATION_TYPE_DEDICATED:
7255         VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
7256         return m_DedicatedAllocation.m_pMappedData;
7257     default:
7258         VMA_ASSERT(0);
7259         return VMA_NULL;
7260     }
7261 }
7262 
CanBecomeLost()7263 bool VmaAllocation_T::CanBecomeLost() const
7264 {
7265     switch(m_Type)
7266     {
7267     case ALLOCATION_TYPE_BLOCK:
7268         return m_BlockAllocation.m_CanBecomeLost;
7269     case ALLOCATION_TYPE_DEDICATED:
7270         return false;
7271     default:
7272         VMA_ASSERT(0);
7273         return false;
7274     }
7275 }
7276 
GetPool()7277 VmaPool VmaAllocation_T::GetPool() const
7278 {
7279     VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7280     return m_BlockAllocation.m_hPool;
7281 }
7282 
MakeLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)7283 bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
7284 {
7285     VMA_ASSERT(CanBecomeLost());
7286 
7287     /*
7288     Warning: This is a carefully designed algorithm.
7289     Do not modify unless you really know what you're doing :)
7290     */
7291     uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
7292     for(;;)
7293     {
7294         if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
7295         {
7296             VMA_ASSERT(0);
7297             return false;
7298         }
7299         else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
7300         {
7301             return false;
7302         }
7303         else // Last use time earlier than current time.
7304         {
7305             if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
7306             {
7307                 // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
7308                 // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
7309                 return true;
7310             }
7311         }
7312     }
7313 }
7314 
7315 #if VMA_STATS_STRING_ENABLED
7316 
7317 // Correspond to values of enum VmaSuballocationType.
7318 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
7319     "FREE",
7320     "UNKNOWN",
7321     "BUFFER",
7322     "IMAGE_UNKNOWN",
7323     "IMAGE_LINEAR",
7324     "IMAGE_OPTIMAL",
7325 };
7326 
PrintParameters(class VmaJsonWriter & json)7327 void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
7328 {
7329     json.WriteString("Type");
7330     json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
7331 
7332     json.WriteString("Size");
7333     json.WriteNumber(m_Size);
7334 
7335     if(m_pUserData != VMA_NULL)
7336     {
7337         json.WriteString("UserData");
7338         if(IsUserDataString())
7339         {
7340             json.WriteString((const char*)m_pUserData);
7341         }
7342         else
7343         {
7344             json.BeginString();
7345             json.ContinueString_Pointer(m_pUserData);
7346             json.EndString();
7347         }
7348     }
7349 
7350     json.WriteString("CreationFrameIndex");
7351     json.WriteNumber(m_CreationFrameIndex);
7352 
7353     json.WriteString("LastUseFrameIndex");
7354     json.WriteNumber(GetLastUseFrameIndex());
7355 
7356     if(m_BufferImageUsage != 0)
7357     {
7358         json.WriteString("Usage");
7359         json.WriteNumber(m_BufferImageUsage);
7360     }
7361 }
7362 
7363 #endif
7364 
FreeUserDataString(VmaAllocator hAllocator)7365 void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
7366 {
7367     VMA_ASSERT(IsUserDataString());
7368     if(m_pUserData != VMA_NULL)
7369     {
7370         char* const oldStr = (char*)m_pUserData;
7371         const size_t oldStrLen = strlen(oldStr);
7372         vma_delete_array(hAllocator, oldStr, oldStrLen + 1);
7373         m_pUserData = VMA_NULL;
7374     }
7375 }
7376 
BlockAllocMap()7377 void VmaAllocation_T::BlockAllocMap()
7378 {
7379     VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
7380 
7381     if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
7382     {
7383         ++m_MapCount;
7384     }
7385     else
7386     {
7387         VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
7388     }
7389 }
7390 
BlockAllocUnmap()7391 void VmaAllocation_T::BlockAllocUnmap()
7392 {
7393     VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
7394 
7395     if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
7396     {
7397         --m_MapCount;
7398     }
7399     else
7400     {
7401         VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
7402     }
7403 }
7404 
DedicatedAllocMap(VmaAllocator hAllocator,void ** ppData)7405 VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
7406 {
7407     VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
7408 
7409     if(m_MapCount != 0)
7410     {
7411         if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
7412         {
7413             VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
7414             *ppData = m_DedicatedAllocation.m_pMappedData;
7415             ++m_MapCount;
7416             return VK_SUCCESS;
7417         }
7418         else
7419         {
7420             VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
7421             return VK_ERROR_MEMORY_MAP_FAILED;
7422         }
7423     }
7424     else
7425     {
7426         VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
7427             hAllocator->m_hDevice,
7428             m_DedicatedAllocation.m_hMemory,
7429             0, // offset
7430             VK_WHOLE_SIZE,
7431             0, // flags
7432             ppData);
7433         if(result == VK_SUCCESS)
7434         {
7435             m_DedicatedAllocation.m_pMappedData = *ppData;
7436             m_MapCount = 1;
7437         }
7438         return result;
7439     }
7440 }
7441 
DedicatedAllocUnmap(VmaAllocator hAllocator)7442 void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
7443 {
7444     VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
7445 
7446     if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
7447     {
7448         --m_MapCount;
7449         if(m_MapCount == 0)
7450         {
7451             m_DedicatedAllocation.m_pMappedData = VMA_NULL;
7452             (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
7453                 hAllocator->m_hDevice,
7454                 m_DedicatedAllocation.m_hMemory);
7455         }
7456     }
7457     else
7458     {
7459         VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
7460     }
7461 }
7462 
7463 #if VMA_STATS_STRING_ENABLED
7464 
VmaPrintStatInfo(VmaJsonWriter & json,const VmaStatInfo & stat)7465 static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
7466 {
7467     json.BeginObject();
7468 
7469     json.WriteString("Blocks");
7470     json.WriteNumber(stat.blockCount);
7471 
7472     json.WriteString("Allocations");
7473     json.WriteNumber(stat.allocationCount);
7474 
7475     json.WriteString("UnusedRanges");
7476     json.WriteNumber(stat.unusedRangeCount);
7477 
7478     json.WriteString("UsedBytes");
7479     json.WriteNumber(stat.usedBytes);
7480 
7481     json.WriteString("UnusedBytes");
7482     json.WriteNumber(stat.unusedBytes);
7483 
7484     if(stat.allocationCount > 1)
7485     {
7486         json.WriteString("AllocationSize");
7487         json.BeginObject(true);
7488         json.WriteString("Min");
7489         json.WriteNumber(stat.allocationSizeMin);
7490         json.WriteString("Avg");
7491         json.WriteNumber(stat.allocationSizeAvg);
7492         json.WriteString("Max");
7493         json.WriteNumber(stat.allocationSizeMax);
7494         json.EndObject();
7495     }
7496 
7497     if(stat.unusedRangeCount > 1)
7498     {
7499         json.WriteString("UnusedRangeSize");
7500         json.BeginObject(true);
7501         json.WriteString("Min");
7502         json.WriteNumber(stat.unusedRangeSizeMin);
7503         json.WriteString("Avg");
7504         json.WriteNumber(stat.unusedRangeSizeAvg);
7505         json.WriteString("Max");
7506         json.WriteNumber(stat.unusedRangeSizeMax);
7507         json.EndObject();
7508     }
7509 
7510     json.EndObject();
7511 }
7512 
7513 #endif // #if VMA_STATS_STRING_ENABLED
7514 
7515 struct VmaSuballocationItemSizeLess
7516 {
operatorVmaSuballocationItemSizeLess7517     bool operator()(
7518         const VmaSuballocationList::iterator lhs,
7519         const VmaSuballocationList::iterator rhs) const
7520     {
7521         return lhs->size < rhs->size;
7522     }
operatorVmaSuballocationItemSizeLess7523     bool operator()(
7524         const VmaSuballocationList::iterator lhs,
7525         VkDeviceSize rhsSize) const
7526     {
7527         return lhs->size < rhsSize;
7528     }
7529 };
7530 
7531 
7532 ////////////////////////////////////////////////////////////////////////////////
7533 // class VmaBlockMetadata
7534 
VmaBlockMetadata(VmaAllocator hAllocator)7535 VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
7536     m_Size(0),
7537     m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks())
7538 {
7539 }
7540 
7541 #if VMA_STATS_STRING_ENABLED
7542 
PrintDetailedMap_Begin(class VmaJsonWriter & json,VkDeviceSize unusedBytes,size_t allocationCount,size_t unusedRangeCount)7543 void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
7544     VkDeviceSize unusedBytes,
7545     size_t allocationCount,
7546     size_t unusedRangeCount) const
7547 {
7548     json.BeginObject();
7549 
7550     json.WriteString("TotalBytes");
7551     json.WriteNumber(GetSize());
7552 
7553     json.WriteString("UnusedBytes");
7554     json.WriteNumber(unusedBytes);
7555 
7556     json.WriteString("Allocations");
7557     json.WriteNumber((uint64_t)allocationCount);
7558 
7559     json.WriteString("UnusedRanges");
7560     json.WriteNumber((uint64_t)unusedRangeCount);
7561 
7562     json.WriteString("Suballocations");
7563     json.BeginArray();
7564 }
7565 
PrintDetailedMap_Allocation(class VmaJsonWriter & json,VkDeviceSize offset,VmaAllocation hAllocation)7566 void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
7567     VkDeviceSize offset,
7568     VmaAllocation hAllocation) const
7569 {
7570     json.BeginObject(true);
7571 
7572     json.WriteString("Offset");
7573     json.WriteNumber(offset);
7574 
7575     hAllocation->PrintParameters(json);
7576 
7577     json.EndObject();
7578 }
7579 
PrintDetailedMap_UnusedRange(class VmaJsonWriter & json,VkDeviceSize offset,VkDeviceSize size)7580 void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
7581     VkDeviceSize offset,
7582     VkDeviceSize size) const
7583 {
7584     json.BeginObject(true);
7585 
7586     json.WriteString("Offset");
7587     json.WriteNumber(offset);
7588 
7589     json.WriteString("Type");
7590     json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
7591 
7592     json.WriteString("Size");
7593     json.WriteNumber(size);
7594 
7595     json.EndObject();
7596 }
7597 
PrintDetailedMap_End(class VmaJsonWriter & json)7598 void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
7599 {
7600     json.EndArray();
7601     json.EndObject();
7602 }
7603 
7604 #endif // #if VMA_STATS_STRING_ENABLED
7605 
7606 ////////////////////////////////////////////////////////////////////////////////
7607 // class VmaBlockMetadata_Generic
7608 
VmaBlockMetadata_Generic(VmaAllocator hAllocator)7609 VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) :
7610     VmaBlockMetadata(hAllocator),
7611     m_FreeCount(0),
7612     m_SumFreeSize(0),
7613     m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
7614     m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
7615 {
7616 }
7617 
~VmaBlockMetadata_Generic()7618 VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
7619 {
7620 }
7621 
Init(VkDeviceSize size)7622 void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
7623 {
7624     VmaBlockMetadata::Init(size);
7625 
7626     m_FreeCount = 1;
7627     m_SumFreeSize = size;
7628 
7629     VmaSuballocation suballoc = {};
7630     suballoc.offset = 0;
7631     suballoc.size = size;
7632     suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
7633     suballoc.hAllocation = VK_NULL_HANDLE;
7634 
7635     VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
7636     m_Suballocations.push_back(suballoc);
7637     VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
7638     --suballocItem;
7639     m_FreeSuballocationsBySize.push_back(suballocItem);
7640 }
7641 
Validate()7642 bool VmaBlockMetadata_Generic::Validate() const
7643 {
7644     VMA_VALIDATE(!m_Suballocations.empty());
7645 
7646     // Expected offset of new suballocation as calculated from previous ones.
7647     VkDeviceSize calculatedOffset = 0;
7648     // Expected number of free suballocations as calculated from traversing their list.
7649     uint32_t calculatedFreeCount = 0;
7650     // Expected sum size of free suballocations as calculated from traversing their list.
7651     VkDeviceSize calculatedSumFreeSize = 0;
7652     // Expected number of free suballocations that should be registered in
7653     // m_FreeSuballocationsBySize calculated from traversing their list.
7654     size_t freeSuballocationsToRegister = 0;
7655     // True if previous visited suballocation was free.
7656     bool prevFree = false;
7657 
7658     for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7659         suballocItem != m_Suballocations.cend();
7660         ++suballocItem)
7661     {
7662         const VmaSuballocation& subAlloc = *suballocItem;
7663 
7664         // Actual offset of this suballocation doesn't match expected one.
7665         VMA_VALIDATE(subAlloc.offset == calculatedOffset);
7666 
7667         const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
7668         // Two adjacent free suballocations are invalid. They should be merged.
7669         VMA_VALIDATE(!prevFree || !currFree);
7670 
7671         VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE));
7672 
7673         if(currFree)
7674         {
7675             calculatedSumFreeSize += subAlloc.size;
7676             ++calculatedFreeCount;
7677             if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7678             {
7679                 ++freeSuballocationsToRegister;
7680             }
7681 
7682             // Margin required between allocations - every free space must be at least that large.
7683 #if VMA_DEBUG_MARGIN
7684             VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);
7685 #endif
7686         }
7687         else
7688         {
7689             VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset);
7690             VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size);
7691 
7692             // Margin required between allocations - previous allocation must be free.
7693             VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);
7694         }
7695 
7696         calculatedOffset += subAlloc.size;
7697         prevFree = currFree;
7698     }
7699 
7700     // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
7701     // match expected one.
7702     VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
7703 
7704     VkDeviceSize lastSize = 0;
7705     for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
7706     {
7707         VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
7708 
7709         // Only free suballocations can be registered in m_FreeSuballocationsBySize.
7710         VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
7711         // They must be sorted by size ascending.
7712         VMA_VALIDATE(suballocItem->size >= lastSize);
7713 
7714         lastSize = suballocItem->size;
7715     }
7716 
7717     // Check if totals match calculacted values.
7718     VMA_VALIDATE(ValidateFreeSuballocationList());
7719     VMA_VALIDATE(calculatedOffset == GetSize());
7720     VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
7721     VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
7722 
7723     return true;
7724 }
7725 
GetUnusedRangeSizeMax()7726 VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
7727 {
7728     if(!m_FreeSuballocationsBySize.empty())
7729     {
7730         return m_FreeSuballocationsBySize.back()->size;
7731     }
7732     else
7733     {
7734         return 0;
7735     }
7736 }
7737 
IsEmpty()7738 bool VmaBlockMetadata_Generic::IsEmpty() const
7739 {
7740     return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
7741 }
7742 
CalcAllocationStatInfo(VmaStatInfo & outInfo)7743 void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
7744 {
7745     outInfo.blockCount = 1;
7746 
7747     const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
7748     outInfo.allocationCount = rangeCount - m_FreeCount;
7749     outInfo.unusedRangeCount = m_FreeCount;
7750 
7751     outInfo.unusedBytes = m_SumFreeSize;
7752     outInfo.usedBytes = GetSize() - outInfo.unusedBytes;
7753 
7754     outInfo.allocationSizeMin = UINT64_MAX;
7755     outInfo.allocationSizeMax = 0;
7756     outInfo.unusedRangeSizeMin = UINT64_MAX;
7757     outInfo.unusedRangeSizeMax = 0;
7758 
7759     for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7760         suballocItem != m_Suballocations.cend();
7761         ++suballocItem)
7762     {
7763         const VmaSuballocation& suballoc = *suballocItem;
7764         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
7765         {
7766             outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
7767             outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
7768         }
7769         else
7770         {
7771             outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
7772             outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
7773         }
7774     }
7775 }
7776 
AddPoolStats(VmaPoolStats & inoutStats)7777 void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
7778 {
7779     const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
7780 
7781     inoutStats.size += GetSize();
7782     inoutStats.unusedSize += m_SumFreeSize;
7783     inoutStats.allocationCount += rangeCount - m_FreeCount;
7784     inoutStats.unusedRangeCount += m_FreeCount;
7785     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
7786 }
7787 
7788 #if VMA_STATS_STRING_ENABLED
7789 
PrintDetailedMap(class VmaJsonWriter & json)7790 void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
7791 {
7792     PrintDetailedMap_Begin(json,
7793         m_SumFreeSize, // unusedBytes
7794         m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
7795         m_FreeCount); // unusedRangeCount
7796 
7797     size_t i = 0;
7798     for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7799         suballocItem != m_Suballocations.cend();
7800         ++suballocItem, ++i)
7801     {
7802         if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
7803         {
7804             PrintDetailedMap_UnusedRange(json, suballocItem->offset, suballocItem->size);
7805         }
7806         else
7807         {
7808             PrintDetailedMap_Allocation(json, suballocItem->offset, suballocItem->hAllocation);
7809         }
7810     }
7811 
7812     PrintDetailedMap_End(json);
7813 }
7814 
7815 #endif // #if VMA_STATS_STRING_ENABLED
7816 
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)7817 bool VmaBlockMetadata_Generic::CreateAllocationRequest(
7818     uint32_t currentFrameIndex,
7819     uint32_t frameInUseCount,
7820     VkDeviceSize bufferImageGranularity,
7821     VkDeviceSize allocSize,
7822     VkDeviceSize allocAlignment,
7823     bool upperAddress,
7824     VmaSuballocationType allocType,
7825     bool canMakeOtherLost,
7826     uint32_t strategy,
7827     VmaAllocationRequest* pAllocationRequest)
7828 {
7829     VMA_ASSERT(allocSize > 0);
7830     VMA_ASSERT(!upperAddress);
7831     (void) upperAddress;
7832     VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
7833     VMA_ASSERT(pAllocationRequest != VMA_NULL);
7834     VMA_HEAVY_ASSERT(Validate());
7835 
7836     // There is not enough total free space in this block to fullfill the request: Early return.
7837     if(canMakeOtherLost == false &&
7838         m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN)
7839     {
7840         return false;
7841     }
7842 
7843     // New algorithm, efficiently searching freeSuballocationsBySize.
7844     const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
7845     if(freeSuballocCount > 0)
7846     {
7847         if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
7848         {
7849             // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN.
7850             VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
7851                 m_FreeSuballocationsBySize.data(),
7852                 m_FreeSuballocationsBySize.data() + freeSuballocCount,
7853                 allocSize + 2 * VMA_DEBUG_MARGIN,
7854                 VmaSuballocationItemSizeLess());
7855             size_t index = it - m_FreeSuballocationsBySize.data();
7856             for(; index < freeSuballocCount; ++index)
7857             {
7858                 if(CheckAllocation(
7859                     currentFrameIndex,
7860                     frameInUseCount,
7861                     bufferImageGranularity,
7862                     allocSize,
7863                     allocAlignment,
7864                     allocType,
7865                     m_FreeSuballocationsBySize[index],
7866                     false, // canMakeOtherLost
7867                     &pAllocationRequest->offset,
7868                     &pAllocationRequest->itemsToMakeLostCount,
7869                     &pAllocationRequest->sumFreeSize,
7870                     &pAllocationRequest->sumItemSize))
7871                 {
7872                     pAllocationRequest->item = m_FreeSuballocationsBySize[index];
7873                     return true;
7874                 }
7875             }
7876         }
7877         else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
7878         {
7879             for(VmaSuballocationList::iterator it = m_Suballocations.begin();
7880                 it != m_Suballocations.end();
7881                 ++it)
7882             {
7883                 if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
7884                     currentFrameIndex,
7885                     frameInUseCount,
7886                     bufferImageGranularity,
7887                     allocSize,
7888                     allocAlignment,
7889                     allocType,
7890                     it,
7891                     false, // canMakeOtherLost
7892                     &pAllocationRequest->offset,
7893                     &pAllocationRequest->itemsToMakeLostCount,
7894                     &pAllocationRequest->sumFreeSize,
7895                     &pAllocationRequest->sumItemSize))
7896                 {
7897                     pAllocationRequest->item = it;
7898                     return true;
7899                 }
7900             }
7901         }
7902         else // WORST_FIT, FIRST_FIT
7903         {
7904             // Search staring from biggest suballocations.
7905             for(size_t index = freeSuballocCount; index--; )
7906             {
7907                 if(CheckAllocation(
7908                     currentFrameIndex,
7909                     frameInUseCount,
7910                     bufferImageGranularity,
7911                     allocSize,
7912                     allocAlignment,
7913                     allocType,
7914                     m_FreeSuballocationsBySize[index],
7915                     false, // canMakeOtherLost
7916                     &pAllocationRequest->offset,
7917                     &pAllocationRequest->itemsToMakeLostCount,
7918                     &pAllocationRequest->sumFreeSize,
7919                     &pAllocationRequest->sumItemSize))
7920                 {
7921                     pAllocationRequest->item = m_FreeSuballocationsBySize[index];
7922                     return true;
7923                 }
7924             }
7925         }
7926     }
7927 
7928     if(canMakeOtherLost)
7929     {
7930         // Brute-force algorithm. TODO: Come up with something better.
7931 
7932         pAllocationRequest->sumFreeSize = VK_WHOLE_SIZE;
7933         pAllocationRequest->sumItemSize = VK_WHOLE_SIZE;
7934 
7935         VmaAllocationRequest tmpAllocRequest = {};
7936         for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
7937             suballocIt != m_Suballocations.end();
7938             ++suballocIt)
7939         {
7940             if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
7941                 suballocIt->hAllocation->CanBecomeLost())
7942             {
7943                 if(CheckAllocation(
7944                     currentFrameIndex,
7945                     frameInUseCount,
7946                     bufferImageGranularity,
7947                     allocSize,
7948                     allocAlignment,
7949                     allocType,
7950                     suballocIt,
7951                     canMakeOtherLost,
7952                     &tmpAllocRequest.offset,
7953                     &tmpAllocRequest.itemsToMakeLostCount,
7954                     &tmpAllocRequest.sumFreeSize,
7955                     &tmpAllocRequest.sumItemSize))
7956                 {
7957                     tmpAllocRequest.item = suballocIt;
7958 
7959                     if(tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost() ||
7960                         strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
7961                     {
7962                         *pAllocationRequest = tmpAllocRequest;
7963                     }
7964                 }
7965             }
7966         }
7967 
7968         if(pAllocationRequest->sumItemSize != VK_WHOLE_SIZE)
7969         {
7970             return true;
7971         }
7972     }
7973 
7974     return false;
7975 }
7976 
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)7977 bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
7978     uint32_t currentFrameIndex,
7979     uint32_t frameInUseCount,
7980     VmaAllocationRequest* pAllocationRequest)
7981 {
7982     while(pAllocationRequest->itemsToMakeLostCount > 0)
7983     {
7984         if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
7985         {
7986             ++pAllocationRequest->item;
7987         }
7988         VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
7989         VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
7990         VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
7991         if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
7992         {
7993             pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
7994             --pAllocationRequest->itemsToMakeLostCount;
7995         }
7996         else
7997         {
7998             return false;
7999         }
8000     }
8001 
8002     VMA_HEAVY_ASSERT(Validate());
8003     VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
8004     VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
8005 
8006     return true;
8007 }
8008 
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)8009 uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
8010 {
8011     uint32_t lostAllocationCount = 0;
8012     for(VmaSuballocationList::iterator it = m_Suballocations.begin();
8013         it != m_Suballocations.end();
8014         ++it)
8015     {
8016         if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
8017             it->hAllocation->CanBecomeLost() &&
8018             it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
8019         {
8020             it = FreeSuballocation(it);
8021             ++lostAllocationCount;
8022         }
8023     }
8024     return lostAllocationCount;
8025 }
8026 
CheckCorruption(const void * pBlockData)8027 VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
8028 {
8029     for(VmaSuballocationList::iterator it = m_Suballocations.begin();
8030         it != m_Suballocations.end();
8031         ++it)
8032     {
8033         if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
8034         {
8035             if(!VmaValidateMagicValue(pBlockData, it->offset - VMA_DEBUG_MARGIN))
8036             {
8037                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
8038                 return VK_ERROR_VALIDATION_FAILED_EXT;
8039             }
8040             if(!VmaValidateMagicValue(pBlockData, it->offset + it->size))
8041             {
8042                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
8043                 return VK_ERROR_VALIDATION_FAILED_EXT;
8044             }
8045         }
8046     }
8047 
8048     return VK_SUCCESS;
8049 }
8050 
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,bool upperAddress,VmaAllocation hAllocation)8051 void VmaBlockMetadata_Generic::Alloc(
8052     const VmaAllocationRequest& request,
8053     VmaSuballocationType type,
8054     VkDeviceSize allocSize,
8055     bool upperAddress,
8056     VmaAllocation hAllocation)
8057 {
8058     VMA_ASSERT(!upperAddress);
8059     (void) upperAddress;
8060     VMA_ASSERT(request.item != m_Suballocations.end());
8061     VmaSuballocation& suballoc = *request.item;
8062     // Given suballocation is a free block.
8063     VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8064     // Given offset is inside this suballocation.
8065     VMA_ASSERT(request.offset >= suballoc.offset);
8066     const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
8067     VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
8068     const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
8069 
8070     // Unregister this free suballocation from m_FreeSuballocationsBySize and update
8071     // it to become used.
8072     UnregisterFreeSuballocation(request.item);
8073 
8074     suballoc.offset = request.offset;
8075     suballoc.size = allocSize;
8076     suballoc.type = type;
8077     suballoc.hAllocation = hAllocation;
8078 
8079     // If there are any free bytes remaining at the end, insert new free suballocation after current one.
8080     if(paddingEnd)
8081     {
8082         VmaSuballocation paddingSuballoc = {};
8083         paddingSuballoc.offset = request.offset + allocSize;
8084         paddingSuballoc.size = paddingEnd;
8085         paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8086         VmaSuballocationList::iterator next = request.item;
8087         ++next;
8088         const VmaSuballocationList::iterator paddingEndItem =
8089             m_Suballocations.insert(next, paddingSuballoc);
8090         RegisterFreeSuballocation(paddingEndItem);
8091     }
8092 
8093     // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
8094     if(paddingBegin)
8095     {
8096         VmaSuballocation paddingSuballoc = {};
8097         paddingSuballoc.offset = request.offset - paddingBegin;
8098         paddingSuballoc.size = paddingBegin;
8099         paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8100         const VmaSuballocationList::iterator paddingBeginItem =
8101             m_Suballocations.insert(request.item, paddingSuballoc);
8102         RegisterFreeSuballocation(paddingBeginItem);
8103     }
8104 
8105     // Update totals.
8106     m_FreeCount = m_FreeCount - 1;
8107     if(paddingBegin > 0)
8108     {
8109         ++m_FreeCount;
8110     }
8111     if(paddingEnd > 0)
8112     {
8113         ++m_FreeCount;
8114     }
8115     m_SumFreeSize -= allocSize;
8116 }
8117 
Free(const VmaAllocation allocation)8118 void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation)
8119 {
8120     for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
8121         suballocItem != m_Suballocations.end();
8122         ++suballocItem)
8123     {
8124         VmaSuballocation& suballoc = *suballocItem;
8125         if(suballoc.hAllocation == allocation)
8126         {
8127             FreeSuballocation(suballocItem);
8128             VMA_HEAVY_ASSERT(Validate());
8129             return;
8130         }
8131     }
8132     VMA_ASSERT(0 && "Not found!");
8133 }
8134 
FreeAtOffset(VkDeviceSize offset)8135 void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
8136 {
8137     for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
8138         suballocItem != m_Suballocations.end();
8139         ++suballocItem)
8140     {
8141         VmaSuballocation& suballoc = *suballocItem;
8142         if(suballoc.offset == offset)
8143         {
8144             FreeSuballocation(suballocItem);
8145             return;
8146         }
8147     }
8148     VMA_ASSERT(0 && "Not found!");
8149 }
8150 
ResizeAllocation(const VmaAllocation alloc,VkDeviceSize newSize)8151 bool VmaBlockMetadata_Generic::ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize)
8152 {
8153     typedef VmaSuballocationList::iterator iter_type;
8154     for(iter_type suballocItem = m_Suballocations.begin();
8155         suballocItem != m_Suballocations.end();
8156         ++suballocItem)
8157     {
8158         VmaSuballocation& suballoc = *suballocItem;
8159         if(suballoc.hAllocation == alloc)
8160         {
8161             iter_type nextItem = suballocItem;
8162             ++nextItem;
8163 
8164             // Should have been ensured on higher level.
8165             VMA_ASSERT(newSize != alloc->GetSize() && newSize > 0);
8166 
8167             // Shrinking.
8168             if(newSize < alloc->GetSize())
8169             {
8170                 const VkDeviceSize sizeDiff = suballoc.size - newSize;
8171 
8172                 // There is next item.
8173                 if(nextItem != m_Suballocations.end())
8174                 {
8175                     // Next item is free.
8176                     if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8177                     {
8178                         // Grow this next item backward.
8179                         UnregisterFreeSuballocation(nextItem);
8180                         nextItem->offset -= sizeDiff;
8181                         nextItem->size += sizeDiff;
8182                         RegisterFreeSuballocation(nextItem);
8183                     }
8184                     // Next item is not free.
8185                     else
8186                     {
8187                         // Create free item after current one.
8188                         VmaSuballocation newFreeSuballoc;
8189                         newFreeSuballoc.hAllocation = VK_NULL_HANDLE;
8190                         newFreeSuballoc.offset = suballoc.offset + newSize;
8191                         newFreeSuballoc.size = sizeDiff;
8192                         newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8193                         iter_type newFreeSuballocIt = m_Suballocations.insert(nextItem, newFreeSuballoc);
8194                         RegisterFreeSuballocation(newFreeSuballocIt);
8195 
8196                         ++m_FreeCount;
8197                     }
8198                 }
8199                 // This is the last item.
8200                 else
8201                 {
8202                     // Create free item at the end.
8203                     VmaSuballocation newFreeSuballoc;
8204                     newFreeSuballoc.hAllocation = VK_NULL_HANDLE;
8205                     newFreeSuballoc.offset = suballoc.offset + newSize;
8206                     newFreeSuballoc.size = sizeDiff;
8207                     newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8208                     m_Suballocations.push_back(newFreeSuballoc);
8209 
8210                     iter_type newFreeSuballocIt = m_Suballocations.end();
8211                     RegisterFreeSuballocation(--newFreeSuballocIt);
8212 
8213                     ++m_FreeCount;
8214                 }
8215 
8216                 suballoc.size = newSize;
8217                 m_SumFreeSize += sizeDiff;
8218             }
8219             // Growing.
8220             else
8221             {
8222                 const VkDeviceSize sizeDiff = newSize - suballoc.size;
8223 
8224                 // There is next item.
8225                 if(nextItem != m_Suballocations.end())
8226                 {
8227                     // Next item is free.
8228                     if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8229                     {
8230                         // There is not enough free space, including margin.
8231                         if(nextItem->size < sizeDiff + VMA_DEBUG_MARGIN)
8232                         {
8233                             return false;
8234                         }
8235 
8236                         // There is more free space than required.
8237                         if(nextItem->size > sizeDiff)
8238                         {
8239                             // Move and shrink this next item.
8240                             UnregisterFreeSuballocation(nextItem);
8241                             nextItem->offset += sizeDiff;
8242                             nextItem->size -= sizeDiff;
8243                             RegisterFreeSuballocation(nextItem);
8244                         }
8245                         // There is exactly the amount of free space required.
8246                         else
8247                         {
8248                             // Remove this next free item.
8249                             UnregisterFreeSuballocation(nextItem);
8250                             m_Suballocations.erase(nextItem);
8251                             --m_FreeCount;
8252                         }
8253                     }
8254                     // Next item is not free - there is no space to grow.
8255                     else
8256                     {
8257                         return false;
8258                     }
8259                 }
8260                 // This is the last item - there is no space to grow.
8261                 else
8262                 {
8263                     return false;
8264                 }
8265 
8266                 suballoc.size = newSize;
8267                 m_SumFreeSize -= sizeDiff;
8268             }
8269 
8270             // We cannot call Validate() here because alloc object is updated to new size outside of this call.
8271             return true;
8272         }
8273     }
8274     VMA_ASSERT(0 && "Not found!");
8275     return false;
8276 }
8277 
ValidateFreeSuballocationList()8278 bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
8279 {
8280     VkDeviceSize lastSize = 0;
8281     for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
8282     {
8283         const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
8284 
8285         VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
8286         VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
8287         VMA_VALIDATE(it->size >= lastSize);
8288         lastSize = it->size;
8289     }
8290     return true;
8291 }
8292 
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)8293 bool VmaBlockMetadata_Generic::CheckAllocation(
8294     uint32_t currentFrameIndex,
8295     uint32_t frameInUseCount,
8296     VkDeviceSize bufferImageGranularity,
8297     VkDeviceSize allocSize,
8298     VkDeviceSize allocAlignment,
8299     VmaSuballocationType allocType,
8300     VmaSuballocationList::const_iterator suballocItem,
8301     bool canMakeOtherLost,
8302     VkDeviceSize* pOffset,
8303     size_t* itemsToMakeLostCount,
8304     VkDeviceSize* pSumFreeSize,
8305     VkDeviceSize* pSumItemSize) const
8306 {
8307     VMA_ASSERT(allocSize > 0);
8308     VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
8309     VMA_ASSERT(suballocItem != m_Suballocations.cend());
8310     VMA_ASSERT(pOffset != VMA_NULL);
8311 
8312     *itemsToMakeLostCount = 0;
8313     *pSumFreeSize = 0;
8314     *pSumItemSize = 0;
8315 
8316     if(canMakeOtherLost)
8317     {
8318         if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8319         {
8320             *pSumFreeSize = suballocItem->size;
8321         }
8322         else
8323         {
8324             if(suballocItem->hAllocation->CanBecomeLost() &&
8325                 suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8326             {
8327                 ++*itemsToMakeLostCount;
8328                 *pSumItemSize = suballocItem->size;
8329             }
8330             else
8331             {
8332                 return false;
8333             }
8334         }
8335 
8336         // Remaining size is too small for this request: Early return.
8337         if(GetSize() - suballocItem->offset < allocSize)
8338         {
8339             return false;
8340         }
8341 
8342         // Start from offset equal to beginning of this suballocation.
8343         *pOffset = suballocItem->offset;
8344 
8345         // Apply VMA_DEBUG_MARGIN at the beginning.
8346         if(VMA_DEBUG_MARGIN > 0)
8347         {
8348             *pOffset += VMA_DEBUG_MARGIN;
8349         }
8350 
8351         // Apply alignment.
8352         *pOffset = VmaAlignUp(*pOffset, allocAlignment);
8353 
8354         // Check previous suballocations for BufferImageGranularity conflicts.
8355         // Make bigger alignment if necessary.
8356         if(bufferImageGranularity > 1)
8357         {
8358             bool bufferImageGranularityConflict = false;
8359             VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
8360             while(prevSuballocItem != m_Suballocations.cbegin())
8361             {
8362                 --prevSuballocItem;
8363                 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
8364                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
8365                 {
8366                     if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
8367                     {
8368                         bufferImageGranularityConflict = true;
8369                         break;
8370                     }
8371                 }
8372                 else
8373                     // Already on previous page.
8374                     break;
8375             }
8376             if(bufferImageGranularityConflict)
8377             {
8378                 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
8379             }
8380         }
8381 
8382         // Now that we have final *pOffset, check if we are past suballocItem.
8383         // If yes, return false - this function should be called for another suballocItem as starting point.
8384         if(*pOffset >= suballocItem->offset + suballocItem->size)
8385         {
8386             return false;
8387         }
8388 
8389         // Calculate padding at the beginning based on current offset.
8390         const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
8391 
8392         // Calculate required margin at the end.
8393         const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
8394 
8395         const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
8396         // Another early return check.
8397         if(suballocItem->offset + totalSize > GetSize())
8398         {
8399             return false;
8400         }
8401 
8402         // Advance lastSuballocItem until desired size is reached.
8403         // Update itemsToMakeLostCount.
8404         VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
8405         if(totalSize > suballocItem->size)
8406         {
8407             VkDeviceSize remainingSize = totalSize - suballocItem->size;
8408             while(remainingSize > 0)
8409             {
8410                 ++lastSuballocItem;
8411                 if(lastSuballocItem == m_Suballocations.cend())
8412                 {
8413                     return false;
8414                 }
8415                 if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8416                 {
8417                     *pSumFreeSize += lastSuballocItem->size;
8418                 }
8419                 else
8420                 {
8421                     VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
8422                     if(lastSuballocItem->hAllocation->CanBecomeLost() &&
8423                         lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8424                     {
8425                         ++*itemsToMakeLostCount;
8426                         *pSumItemSize += lastSuballocItem->size;
8427                     }
8428                     else
8429                     {
8430                         return false;
8431                     }
8432                 }
8433                 remainingSize = (lastSuballocItem->size < remainingSize) ?
8434                     remainingSize - lastSuballocItem->size : 0;
8435             }
8436         }
8437 
8438         // Check next suballocations for BufferImageGranularity conflicts.
8439         // If conflict exists, we must mark more allocations lost or fail.
8440         if(bufferImageGranularity > 1)
8441         {
8442             VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
8443             ++nextSuballocItem;
8444             while(nextSuballocItem != m_Suballocations.cend())
8445             {
8446                 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
8447                 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8448                 {
8449                     if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
8450                     {
8451                         VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
8452                         if(nextSuballoc.hAllocation->CanBecomeLost() &&
8453                             nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8454                         {
8455                             ++*itemsToMakeLostCount;
8456                         }
8457                         else
8458                         {
8459                             return false;
8460                         }
8461                     }
8462                 }
8463                 else
8464                 {
8465                     // Already on next page.
8466                     break;
8467                 }
8468                 ++nextSuballocItem;
8469             }
8470         }
8471     }
8472     else
8473     {
8474         const VmaSuballocation& suballoc = *suballocItem;
8475         VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8476 
8477         *pSumFreeSize = suballoc.size;
8478 
8479         // Size of this suballocation is too small for this request: Early return.
8480         if(suballoc.size < allocSize)
8481         {
8482             return false;
8483         }
8484 
8485         // Start from offset equal to beginning of this suballocation.
8486         *pOffset = suballoc.offset;
8487 
8488         // Apply VMA_DEBUG_MARGIN at the beginning.
8489         if(VMA_DEBUG_MARGIN > 0)
8490         {
8491             *pOffset += VMA_DEBUG_MARGIN;
8492         }
8493 
8494         // Apply alignment.
8495         *pOffset = VmaAlignUp(*pOffset, allocAlignment);
8496 
8497         // Check previous suballocations for BufferImageGranularity conflicts.
8498         // Make bigger alignment if necessary.
8499         if(bufferImageGranularity > 1)
8500         {
8501             bool bufferImageGranularityConflict = false;
8502             VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
8503             while(prevSuballocItem != m_Suballocations.cbegin())
8504             {
8505                 --prevSuballocItem;
8506                 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
8507                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
8508                 {
8509                     if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
8510                     {
8511                         bufferImageGranularityConflict = true;
8512                         break;
8513                     }
8514                 }
8515                 else
8516                     // Already on previous page.
8517                     break;
8518             }
8519             if(bufferImageGranularityConflict)
8520             {
8521                 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
8522             }
8523         }
8524 
8525         // Calculate padding at the beginning based on current offset.
8526         const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
8527 
8528         // Calculate required margin at the end.
8529         const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
8530 
8531         // Fail if requested size plus margin before and after is bigger than size of this suballocation.
8532         if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
8533         {
8534             return false;
8535         }
8536 
8537         // Check next suballocations for BufferImageGranularity conflicts.
8538         // If conflict exists, allocation cannot be made here.
8539         if(bufferImageGranularity > 1)
8540         {
8541             VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
8542             ++nextSuballocItem;
8543             while(nextSuballocItem != m_Suballocations.cend())
8544             {
8545                 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
8546                 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8547                 {
8548                     if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
8549                     {
8550                         return false;
8551                     }
8552                 }
8553                 else
8554                 {
8555                     // Already on next page.
8556                     break;
8557                 }
8558                 ++nextSuballocItem;
8559             }
8560         }
8561     }
8562 
8563     // All tests passed: Success. pOffset is already filled.
8564     return true;
8565 }
8566 
MergeFreeWithNext(VmaSuballocationList::iterator item)8567 void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
8568 {
8569     VMA_ASSERT(item != m_Suballocations.end());
8570     VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8571 
8572     VmaSuballocationList::iterator nextItem = item;
8573     ++nextItem;
8574     VMA_ASSERT(nextItem != m_Suballocations.end());
8575     VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
8576 
8577     item->size += nextItem->size;
8578     --m_FreeCount;
8579     m_Suballocations.erase(nextItem);
8580 }
8581 
FreeSuballocation(VmaSuballocationList::iterator suballocItem)8582 VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
8583 {
8584     // Change this suballocation to be marked as free.
8585     VmaSuballocation& suballoc = *suballocItem;
8586     suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8587     suballoc.hAllocation = VK_NULL_HANDLE;
8588 
8589     // Update totals.
8590     ++m_FreeCount;
8591     m_SumFreeSize += suballoc.size;
8592 
8593     // Merge with previous and/or next suballocation if it's also free.
8594     bool mergeWithNext = false;
8595     bool mergeWithPrev = false;
8596 
8597     VmaSuballocationList::iterator nextItem = suballocItem;
8598     ++nextItem;
8599     if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
8600     {
8601         mergeWithNext = true;
8602     }
8603 
8604     VmaSuballocationList::iterator prevItem = suballocItem;
8605     if(suballocItem != m_Suballocations.begin())
8606     {
8607         --prevItem;
8608         if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8609         {
8610             mergeWithPrev = true;
8611         }
8612     }
8613 
8614     if(mergeWithNext)
8615     {
8616         UnregisterFreeSuballocation(nextItem);
8617         MergeFreeWithNext(suballocItem);
8618     }
8619 
8620     if(mergeWithPrev)
8621     {
8622         UnregisterFreeSuballocation(prevItem);
8623         MergeFreeWithNext(prevItem);
8624         RegisterFreeSuballocation(prevItem);
8625         return prevItem;
8626     }
8627     else
8628     {
8629         RegisterFreeSuballocation(suballocItem);
8630         return suballocItem;
8631     }
8632 }
8633 
RegisterFreeSuballocation(VmaSuballocationList::iterator item)8634 void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
8635 {
8636     VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8637     VMA_ASSERT(item->size > 0);
8638 
8639     // You may want to enable this validation at the beginning or at the end of
8640     // this function, depending on what do you want to check.
8641     VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8642 
8643     if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8644     {
8645         if(m_FreeSuballocationsBySize.empty())
8646         {
8647             m_FreeSuballocationsBySize.push_back(item);
8648         }
8649         else
8650         {
8651             VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
8652         }
8653     }
8654 
8655     //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8656 }
8657 
8658 
UnregisterFreeSuballocation(VmaSuballocationList::iterator item)8659 void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
8660 {
8661     VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8662     VMA_ASSERT(item->size > 0);
8663 
8664     // You may want to enable this validation at the beginning or at the end of
8665     // this function, depending on what do you want to check.
8666     VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8667 
8668     if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8669     {
8670         VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
8671             m_FreeSuballocationsBySize.data(),
8672             m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
8673             item,
8674             VmaSuballocationItemSizeLess());
8675         for(size_t index = it - m_FreeSuballocationsBySize.data();
8676             index < m_FreeSuballocationsBySize.size();
8677             ++index)
8678         {
8679             if(m_FreeSuballocationsBySize[index] == item)
8680             {
8681                 VmaVectorRemove(m_FreeSuballocationsBySize, index);
8682                 return;
8683             }
8684             VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
8685         }
8686         VMA_ASSERT(0 && "Not found.");
8687     }
8688 
8689     //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8690 }
8691 
IsBufferImageGranularityConflictPossible(VkDeviceSize bufferImageGranularity,VmaSuballocationType & inOutPrevSuballocType)8692 bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
8693     VkDeviceSize bufferImageGranularity,
8694     VmaSuballocationType& inOutPrevSuballocType) const
8695 {
8696     if(bufferImageGranularity == 1 || IsEmpty())
8697     {
8698         return false;
8699     }
8700 
8701     VkDeviceSize minAlignment = VK_WHOLE_SIZE;
8702     bool typeConflictFound = false;
8703     for(VmaSuballocationList::const_iterator it = m_Suballocations.cbegin();
8704         it != m_Suballocations.cend();
8705         ++it)
8706     {
8707         const VmaSuballocationType suballocType = it->type;
8708         if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)
8709         {
8710             minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment());
8711             if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))
8712             {
8713                 typeConflictFound = true;
8714             }
8715             inOutPrevSuballocType = suballocType;
8716         }
8717     }
8718 
8719     return typeConflictFound || minAlignment >= bufferImageGranularity;
8720 }
8721 
8722 ////////////////////////////////////////////////////////////////////////////////
8723 // class VmaBlockMetadata_Linear
8724 
VmaBlockMetadata_Linear(VmaAllocator hAllocator)8725 VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) :
8726     VmaBlockMetadata(hAllocator),
8727     m_SumFreeSize(0),
8728     m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8729     m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8730     m_1stVectorIndex(0),
8731     m_2ndVectorMode(SECOND_VECTOR_EMPTY),
8732     m_1stNullItemsBeginCount(0),
8733     m_1stNullItemsMiddleCount(0),
8734     m_2ndNullItemsCount(0)
8735 {
8736 }
8737 
~VmaBlockMetadata_Linear()8738 VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
8739 {
8740 }
8741 
Init(VkDeviceSize size)8742 void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
8743 {
8744     VmaBlockMetadata::Init(size);
8745     m_SumFreeSize = size;
8746 }
8747 
Validate()8748 bool VmaBlockMetadata_Linear::Validate() const
8749 {
8750     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8751     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8752 
8753     VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
8754     VMA_VALIDATE(!suballocations1st.empty() ||
8755         suballocations2nd.empty() ||
8756         m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
8757 
8758     if(!suballocations1st.empty())
8759     {
8760         // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
8761         VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE);
8762         // Null item at the end should be just pop_back().
8763         VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE);
8764     }
8765     if(!suballocations2nd.empty())
8766     {
8767         // Null item at the end should be just pop_back().
8768         VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE);
8769     }
8770 
8771     VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
8772     VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
8773 
8774     VkDeviceSize sumUsedSize = 0;
8775     const size_t suballoc1stCount = suballocations1st.size();
8776     VkDeviceSize offset = VMA_DEBUG_MARGIN;
8777 
8778     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8779     {
8780         const size_t suballoc2ndCount = suballocations2nd.size();
8781         size_t nullItem2ndCount = 0;
8782         for(size_t i = 0; i < suballoc2ndCount; ++i)
8783         {
8784             const VmaSuballocation& suballoc = suballocations2nd[i];
8785             const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8786 
8787             VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8788             VMA_VALIDATE(suballoc.offset >= offset);
8789 
8790             if(!currFree)
8791             {
8792                 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8793                 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8794                 sumUsedSize += suballoc.size;
8795             }
8796             else
8797             {
8798                 ++nullItem2ndCount;
8799             }
8800 
8801             offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8802         }
8803 
8804         VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
8805     }
8806 
8807     for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
8808     {
8809         const VmaSuballocation& suballoc = suballocations1st[i];
8810         VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
8811             suballoc.hAllocation == VK_NULL_HANDLE);
8812     }
8813 
8814     size_t nullItem1stCount = m_1stNullItemsBeginCount;
8815 
8816     for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
8817     {
8818         const VmaSuballocation& suballoc = suballocations1st[i];
8819         const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8820 
8821         VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8822         VMA_VALIDATE(suballoc.offset >= offset);
8823         VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
8824 
8825         if(!currFree)
8826         {
8827             VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8828             VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8829             sumUsedSize += suballoc.size;
8830         }
8831         else
8832         {
8833             ++nullItem1stCount;
8834         }
8835 
8836         offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8837     }
8838     VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
8839 
8840     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
8841     {
8842         const size_t suballoc2ndCount = suballocations2nd.size();
8843         size_t nullItem2ndCount = 0;
8844         for(size_t i = suballoc2ndCount; i--; )
8845         {
8846             const VmaSuballocation& suballoc = suballocations2nd[i];
8847             const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8848 
8849             VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8850             VMA_VALIDATE(suballoc.offset >= offset);
8851 
8852             if(!currFree)
8853             {
8854                 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8855                 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8856                 sumUsedSize += suballoc.size;
8857             }
8858             else
8859             {
8860                 ++nullItem2ndCount;
8861             }
8862 
8863             offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8864         }
8865 
8866         VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
8867     }
8868 
8869     VMA_VALIDATE(offset <= GetSize());
8870     VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
8871 
8872     return true;
8873 }
8874 
GetAllocationCount()8875 size_t VmaBlockMetadata_Linear::GetAllocationCount() const
8876 {
8877     return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) +
8878         AccessSuballocations2nd().size() - m_2ndNullItemsCount;
8879 }
8880 
GetUnusedRangeSizeMax()8881 VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
8882 {
8883     const VkDeviceSize size = GetSize();
8884 
8885     /*
8886     We don't consider gaps inside allocation vectors with freed allocations because
8887     they are not suitable for reuse in linear allocator. We consider only space that
8888     is available for new allocations.
8889     */
8890     if(IsEmpty())
8891     {
8892         return size;
8893     }
8894 
8895     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8896 
8897     switch(m_2ndVectorMode)
8898     {
8899     case SECOND_VECTOR_EMPTY:
8900         /*
8901         Available space is after end of 1st, as well as before beginning of 1st (which
8902         whould make it a ring buffer).
8903         */
8904         {
8905             const size_t suballocations1stCount = suballocations1st.size();
8906             VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
8907             const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
8908             const VmaSuballocation& lastSuballoc  = suballocations1st[suballocations1stCount - 1];
8909             return VMA_MAX(
8910                 firstSuballoc.offset,
8911                 size - (lastSuballoc.offset + lastSuballoc.size));
8912         }
8913         break;
8914 
8915     case SECOND_VECTOR_RING_BUFFER:
8916         /*
8917         Available space is only between end of 2nd and beginning of 1st.
8918         */
8919         {
8920             const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8921             const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
8922             const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
8923             return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
8924         }
8925         break;
8926 
8927     case SECOND_VECTOR_DOUBLE_STACK:
8928         /*
8929         Available space is only between end of 1st and top of 2nd.
8930         */
8931         {
8932             const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8933             const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
8934             const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
8935             return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
8936         }
8937         break;
8938 
8939     default:
8940         VMA_ASSERT(0);
8941         return 0;
8942     }
8943 }
8944 
CalcAllocationStatInfo(VmaStatInfo & outInfo)8945 void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
8946 {
8947     const VkDeviceSize size = GetSize();
8948     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8949     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8950     const size_t suballoc1stCount = suballocations1st.size();
8951     const size_t suballoc2ndCount = suballocations2nd.size();
8952 
8953     outInfo.blockCount = 1;
8954     outInfo.allocationCount = (uint32_t)GetAllocationCount();
8955     outInfo.unusedRangeCount = 0;
8956     outInfo.usedBytes = 0;
8957     outInfo.allocationSizeMin = UINT64_MAX;
8958     outInfo.allocationSizeMax = 0;
8959     outInfo.unusedRangeSizeMin = UINT64_MAX;
8960     outInfo.unusedRangeSizeMax = 0;
8961 
8962     VkDeviceSize lastOffset = 0;
8963 
8964     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8965     {
8966         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
8967         size_t nextAlloc2ndIndex = 0;
8968         while(lastOffset < freeSpace2ndTo1stEnd)
8969         {
8970             // Find next non-null allocation or move nextAllocIndex to the end.
8971             while(nextAlloc2ndIndex < suballoc2ndCount &&
8972                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
8973             {
8974                 ++nextAlloc2ndIndex;
8975             }
8976 
8977             // Found non-null allocation.
8978             if(nextAlloc2ndIndex < suballoc2ndCount)
8979             {
8980                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
8981 
8982                 // 1. Process free space before this allocation.
8983                 if(lastOffset < suballoc.offset)
8984                 {
8985                     // There is free space from lastOffset to suballoc.offset.
8986                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
8987                     ++outInfo.unusedRangeCount;
8988                     outInfo.unusedBytes += unusedRangeSize;
8989                     outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
8990                     outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
8991                 }
8992 
8993                 // 2. Process this allocation.
8994                 // There is allocation with suballoc.offset, suballoc.size.
8995                 outInfo.usedBytes += suballoc.size;
8996                 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
8997                 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
8998 
8999                 // 3. Prepare for next iteration.
9000                 lastOffset = suballoc.offset + suballoc.size;
9001                 ++nextAlloc2ndIndex;
9002             }
9003             // We are at the end.
9004             else
9005             {
9006                 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9007                 if(lastOffset < freeSpace2ndTo1stEnd)
9008                 {
9009                     const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9010                     ++outInfo.unusedRangeCount;
9011                     outInfo.unusedBytes += unusedRangeSize;
9012                     outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9013                     outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9014                }
9015 
9016                 // End of loop.
9017                 lastOffset = freeSpace2ndTo1stEnd;
9018             }
9019         }
9020     }
9021 
9022     size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9023     const VkDeviceSize freeSpace1stTo2ndEnd =
9024         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9025     while(lastOffset < freeSpace1stTo2ndEnd)
9026     {
9027         // Find next non-null allocation or move nextAllocIndex to the end.
9028         while(nextAlloc1stIndex < suballoc1stCount &&
9029             suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9030         {
9031             ++nextAlloc1stIndex;
9032         }
9033 
9034         // Found non-null allocation.
9035         if(nextAlloc1stIndex < suballoc1stCount)
9036         {
9037             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9038 
9039             // 1. Process free space before this allocation.
9040             if(lastOffset < suballoc.offset)
9041             {
9042                 // There is free space from lastOffset to suballoc.offset.
9043                 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9044                 ++outInfo.unusedRangeCount;
9045                 outInfo.unusedBytes += unusedRangeSize;
9046                 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9047                 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9048             }
9049 
9050             // 2. Process this allocation.
9051             // There is allocation with suballoc.offset, suballoc.size.
9052             outInfo.usedBytes += suballoc.size;
9053             outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9054             outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
9055 
9056             // 3. Prepare for next iteration.
9057             lastOffset = suballoc.offset + suballoc.size;
9058             ++nextAlloc1stIndex;
9059         }
9060         // We are at the end.
9061         else
9062         {
9063             // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9064             if(lastOffset < freeSpace1stTo2ndEnd)
9065             {
9066                 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9067                 ++outInfo.unusedRangeCount;
9068                 outInfo.unusedBytes += unusedRangeSize;
9069                 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9070                 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9071            }
9072 
9073             // End of loop.
9074             lastOffset = freeSpace1stTo2ndEnd;
9075         }
9076     }
9077 
9078     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9079     {
9080         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9081         while(lastOffset < size)
9082         {
9083             // Find next non-null allocation or move nextAllocIndex to the end.
9084             while(nextAlloc2ndIndex != SIZE_MAX &&
9085                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9086             {
9087                 --nextAlloc2ndIndex;
9088             }
9089 
9090             // Found non-null allocation.
9091             if(nextAlloc2ndIndex != SIZE_MAX)
9092             {
9093                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9094 
9095                 // 1. Process free space before this allocation.
9096                 if(lastOffset < suballoc.offset)
9097                 {
9098                     // There is free space from lastOffset to suballoc.offset.
9099                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9100                     ++outInfo.unusedRangeCount;
9101                     outInfo.unusedBytes += unusedRangeSize;
9102                     outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9103                     outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9104                 }
9105 
9106                 // 2. Process this allocation.
9107                 // There is allocation with suballoc.offset, suballoc.size.
9108                 outInfo.usedBytes += suballoc.size;
9109                 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9110                 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
9111 
9112                 // 3. Prepare for next iteration.
9113                 lastOffset = suballoc.offset + suballoc.size;
9114                 --nextAlloc2ndIndex;
9115             }
9116             // We are at the end.
9117             else
9118             {
9119                 // There is free space from lastOffset to size.
9120                 if(lastOffset < size)
9121                 {
9122                     const VkDeviceSize unusedRangeSize = size - lastOffset;
9123                     ++outInfo.unusedRangeCount;
9124                     outInfo.unusedBytes += unusedRangeSize;
9125                     outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9126                     outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9127                }
9128 
9129                 // End of loop.
9130                 lastOffset = size;
9131             }
9132         }
9133     }
9134 
9135     outInfo.unusedBytes = size - outInfo.usedBytes;
9136 }
9137 
AddPoolStats(VmaPoolStats & inoutStats)9138 void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
9139 {
9140     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9141     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9142     const VkDeviceSize size = GetSize();
9143     const size_t suballoc1stCount = suballocations1st.size();
9144     const size_t suballoc2ndCount = suballocations2nd.size();
9145 
9146     inoutStats.size += size;
9147 
9148     VkDeviceSize lastOffset = 0;
9149 
9150     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9151     {
9152         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9153         size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
9154         while(lastOffset < freeSpace2ndTo1stEnd)
9155         {
9156             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9157             while(nextAlloc2ndIndex < suballoc2ndCount &&
9158                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9159             {
9160                 ++nextAlloc2ndIndex;
9161             }
9162 
9163             // Found non-null allocation.
9164             if(nextAlloc2ndIndex < suballoc2ndCount)
9165             {
9166                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9167 
9168                 // 1. Process free space before this allocation.
9169                 if(lastOffset < suballoc.offset)
9170                 {
9171                     // There is free space from lastOffset to suballoc.offset.
9172                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9173                     inoutStats.unusedSize += unusedRangeSize;
9174                     ++inoutStats.unusedRangeCount;
9175                     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9176                 }
9177 
9178                 // 2. Process this allocation.
9179                 // There is allocation with suballoc.offset, suballoc.size.
9180                 ++inoutStats.allocationCount;
9181 
9182                 // 3. Prepare for next iteration.
9183                 lastOffset = suballoc.offset + suballoc.size;
9184                 ++nextAlloc2ndIndex;
9185             }
9186             // We are at the end.
9187             else
9188             {
9189                 if(lastOffset < freeSpace2ndTo1stEnd)
9190                 {
9191                     // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9192                     const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9193                     inoutStats.unusedSize += unusedRangeSize;
9194                     ++inoutStats.unusedRangeCount;
9195                     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9196                 }
9197 
9198                 // End of loop.
9199                 lastOffset = freeSpace2ndTo1stEnd;
9200             }
9201         }
9202     }
9203 
9204     size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9205     const VkDeviceSize freeSpace1stTo2ndEnd =
9206         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9207     while(lastOffset < freeSpace1stTo2ndEnd)
9208     {
9209         // Find next non-null allocation or move nextAllocIndex to the end.
9210         while(nextAlloc1stIndex < suballoc1stCount &&
9211             suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9212         {
9213             ++nextAlloc1stIndex;
9214         }
9215 
9216         // Found non-null allocation.
9217         if(nextAlloc1stIndex < suballoc1stCount)
9218         {
9219             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9220 
9221             // 1. Process free space before this allocation.
9222             if(lastOffset < suballoc.offset)
9223             {
9224                 // There is free space from lastOffset to suballoc.offset.
9225                 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9226                 inoutStats.unusedSize += unusedRangeSize;
9227                 ++inoutStats.unusedRangeCount;
9228                 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9229             }
9230 
9231             // 2. Process this allocation.
9232             // There is allocation with suballoc.offset, suballoc.size.
9233             ++inoutStats.allocationCount;
9234 
9235             // 3. Prepare for next iteration.
9236             lastOffset = suballoc.offset + suballoc.size;
9237             ++nextAlloc1stIndex;
9238         }
9239         // We are at the end.
9240         else
9241         {
9242             if(lastOffset < freeSpace1stTo2ndEnd)
9243             {
9244                 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9245                 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9246                 inoutStats.unusedSize += unusedRangeSize;
9247                 ++inoutStats.unusedRangeCount;
9248                 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9249             }
9250 
9251             // End of loop.
9252             lastOffset = freeSpace1stTo2ndEnd;
9253         }
9254     }
9255 
9256     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9257     {
9258         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9259         while(lastOffset < size)
9260         {
9261             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9262             while(nextAlloc2ndIndex != SIZE_MAX &&
9263                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9264             {
9265                 --nextAlloc2ndIndex;
9266             }
9267 
9268             // Found non-null allocation.
9269             if(nextAlloc2ndIndex != SIZE_MAX)
9270             {
9271                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9272 
9273                 // 1. Process free space before this allocation.
9274                 if(lastOffset < suballoc.offset)
9275                 {
9276                     // There is free space from lastOffset to suballoc.offset.
9277                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9278                     inoutStats.unusedSize += unusedRangeSize;
9279                     ++inoutStats.unusedRangeCount;
9280                     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9281                 }
9282 
9283                 // 2. Process this allocation.
9284                 // There is allocation with suballoc.offset, suballoc.size.
9285                 ++inoutStats.allocationCount;
9286 
9287                 // 3. Prepare for next iteration.
9288                 lastOffset = suballoc.offset + suballoc.size;
9289                 --nextAlloc2ndIndex;
9290             }
9291             // We are at the end.
9292             else
9293             {
9294                 if(lastOffset < size)
9295                 {
9296                     // There is free space from lastOffset to size.
9297                     const VkDeviceSize unusedRangeSize = size - lastOffset;
9298                     inoutStats.unusedSize += unusedRangeSize;
9299                     ++inoutStats.unusedRangeCount;
9300                     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9301                 }
9302 
9303                 // End of loop.
9304                 lastOffset = size;
9305             }
9306         }
9307     }
9308 }
9309 
9310 #if VMA_STATS_STRING_ENABLED
PrintDetailedMap(class VmaJsonWriter & json)9311 void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
9312 {
9313     const VkDeviceSize size = GetSize();
9314     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9315     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9316     const size_t suballoc1stCount = suballocations1st.size();
9317     const size_t suballoc2ndCount = suballocations2nd.size();
9318 
9319     // FIRST PASS
9320 
9321     size_t unusedRangeCount = 0;
9322     VkDeviceSize usedBytes = 0;
9323 
9324     VkDeviceSize lastOffset = 0;
9325 
9326     size_t alloc2ndCount = 0;
9327     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9328     {
9329         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9330         size_t nextAlloc2ndIndex = 0;
9331         while(lastOffset < freeSpace2ndTo1stEnd)
9332         {
9333             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9334             while(nextAlloc2ndIndex < suballoc2ndCount &&
9335                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9336             {
9337                 ++nextAlloc2ndIndex;
9338             }
9339 
9340             // Found non-null allocation.
9341             if(nextAlloc2ndIndex < suballoc2ndCount)
9342             {
9343                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9344 
9345                 // 1. Process free space before this allocation.
9346                 if(lastOffset < suballoc.offset)
9347                 {
9348                     // There is free space from lastOffset to suballoc.offset.
9349                     ++unusedRangeCount;
9350                 }
9351 
9352                 // 2. Process this allocation.
9353                 // There is allocation with suballoc.offset, suballoc.size.
9354                 ++alloc2ndCount;
9355                 usedBytes += suballoc.size;
9356 
9357                 // 3. Prepare for next iteration.
9358                 lastOffset = suballoc.offset + suballoc.size;
9359                 ++nextAlloc2ndIndex;
9360             }
9361             // We are at the end.
9362             else
9363             {
9364                 if(lastOffset < freeSpace2ndTo1stEnd)
9365                 {
9366                     // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9367                     ++unusedRangeCount;
9368                 }
9369 
9370                 // End of loop.
9371                 lastOffset = freeSpace2ndTo1stEnd;
9372             }
9373         }
9374     }
9375 
9376     size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9377     size_t alloc1stCount = 0;
9378     const VkDeviceSize freeSpace1stTo2ndEnd =
9379         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9380     while(lastOffset < freeSpace1stTo2ndEnd)
9381     {
9382         // Find next non-null allocation or move nextAllocIndex to the end.
9383         while(nextAlloc1stIndex < suballoc1stCount &&
9384             suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9385         {
9386             ++nextAlloc1stIndex;
9387         }
9388 
9389         // Found non-null allocation.
9390         if(nextAlloc1stIndex < suballoc1stCount)
9391         {
9392             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9393 
9394             // 1. Process free space before this allocation.
9395             if(lastOffset < suballoc.offset)
9396             {
9397                 // There is free space from lastOffset to suballoc.offset.
9398                 ++unusedRangeCount;
9399             }
9400 
9401             // 2. Process this allocation.
9402             // There is allocation with suballoc.offset, suballoc.size.
9403             ++alloc1stCount;
9404             usedBytes += suballoc.size;
9405 
9406             // 3. Prepare for next iteration.
9407             lastOffset = suballoc.offset + suballoc.size;
9408             ++nextAlloc1stIndex;
9409         }
9410         // We are at the end.
9411         else
9412         {
9413             if(lastOffset < size)
9414             {
9415                 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9416                 ++unusedRangeCount;
9417             }
9418 
9419             // End of loop.
9420             lastOffset = freeSpace1stTo2ndEnd;
9421         }
9422     }
9423 
9424     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9425     {
9426         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9427         while(lastOffset < size)
9428         {
9429             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9430             while(nextAlloc2ndIndex != SIZE_MAX &&
9431                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9432             {
9433                 --nextAlloc2ndIndex;
9434             }
9435 
9436             // Found non-null allocation.
9437             if(nextAlloc2ndIndex != SIZE_MAX)
9438             {
9439                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9440 
9441                 // 1. Process free space before this allocation.
9442                 if(lastOffset < suballoc.offset)
9443                 {
9444                     // There is free space from lastOffset to suballoc.offset.
9445                     ++unusedRangeCount;
9446                 }
9447 
9448                 // 2. Process this allocation.
9449                 // There is allocation with suballoc.offset, suballoc.size.
9450                 ++alloc2ndCount;
9451                 usedBytes += suballoc.size;
9452 
9453                 // 3. Prepare for next iteration.
9454                 lastOffset = suballoc.offset + suballoc.size;
9455                 --nextAlloc2ndIndex;
9456             }
9457             // We are at the end.
9458             else
9459             {
9460                 if(lastOffset < size)
9461                 {
9462                     // There is free space from lastOffset to size.
9463                     ++unusedRangeCount;
9464                 }
9465 
9466                 // End of loop.
9467                 lastOffset = size;
9468             }
9469         }
9470     }
9471 
9472     const VkDeviceSize unusedBytes = size - usedBytes;
9473     PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
9474 
9475     // SECOND PASS
9476     lastOffset = 0;
9477 
9478     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9479     {
9480         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9481         size_t nextAlloc2ndIndex = 0;
9482         while(lastOffset < freeSpace2ndTo1stEnd)
9483         {
9484             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9485             while(nextAlloc2ndIndex < suballoc2ndCount &&
9486                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9487             {
9488                 ++nextAlloc2ndIndex;
9489             }
9490 
9491             // Found non-null allocation.
9492             if(nextAlloc2ndIndex < suballoc2ndCount)
9493             {
9494                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9495 
9496                 // 1. Process free space before this allocation.
9497                 if(lastOffset < suballoc.offset)
9498                 {
9499                     // There is free space from lastOffset to suballoc.offset.
9500                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9501                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9502                 }
9503 
9504                 // 2. Process this allocation.
9505                 // There is allocation with suballoc.offset, suballoc.size.
9506                 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
9507 
9508                 // 3. Prepare for next iteration.
9509                 lastOffset = suballoc.offset + suballoc.size;
9510                 ++nextAlloc2ndIndex;
9511             }
9512             // We are at the end.
9513             else
9514             {
9515                 if(lastOffset < freeSpace2ndTo1stEnd)
9516                 {
9517                     // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9518                     const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9519                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9520                 }
9521 
9522                 // End of loop.
9523                 lastOffset = freeSpace2ndTo1stEnd;
9524             }
9525         }
9526     }
9527 
9528     nextAlloc1stIndex = m_1stNullItemsBeginCount;
9529     while(lastOffset < freeSpace1stTo2ndEnd)
9530     {
9531         // Find next non-null allocation or move nextAllocIndex to the end.
9532         while(nextAlloc1stIndex < suballoc1stCount &&
9533             suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9534         {
9535             ++nextAlloc1stIndex;
9536         }
9537 
9538         // Found non-null allocation.
9539         if(nextAlloc1stIndex < suballoc1stCount)
9540         {
9541             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9542 
9543             // 1. Process free space before this allocation.
9544             if(lastOffset < suballoc.offset)
9545             {
9546                 // There is free space from lastOffset to suballoc.offset.
9547                 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9548                 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9549             }
9550 
9551             // 2. Process this allocation.
9552             // There is allocation with suballoc.offset, suballoc.size.
9553             PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
9554 
9555             // 3. Prepare for next iteration.
9556             lastOffset = suballoc.offset + suballoc.size;
9557             ++nextAlloc1stIndex;
9558         }
9559         // We are at the end.
9560         else
9561         {
9562             if(lastOffset < freeSpace1stTo2ndEnd)
9563             {
9564                 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9565                 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9566                 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9567             }
9568 
9569             // End of loop.
9570             lastOffset = freeSpace1stTo2ndEnd;
9571         }
9572     }
9573 
9574     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9575     {
9576         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9577         while(lastOffset < size)
9578         {
9579             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9580             while(nextAlloc2ndIndex != SIZE_MAX &&
9581                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9582             {
9583                 --nextAlloc2ndIndex;
9584             }
9585 
9586             // Found non-null allocation.
9587             if(nextAlloc2ndIndex != SIZE_MAX)
9588             {
9589                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9590 
9591                 // 1. Process free space before this allocation.
9592                 if(lastOffset < suballoc.offset)
9593                 {
9594                     // There is free space from lastOffset to suballoc.offset.
9595                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9596                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9597                 }
9598 
9599                 // 2. Process this allocation.
9600                 // There is allocation with suballoc.offset, suballoc.size.
9601                 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
9602 
9603                 // 3. Prepare for next iteration.
9604                 lastOffset = suballoc.offset + suballoc.size;
9605                 --nextAlloc2ndIndex;
9606             }
9607             // We are at the end.
9608             else
9609             {
9610                 if(lastOffset < size)
9611                 {
9612                     // There is free space from lastOffset to size.
9613                     const VkDeviceSize unusedRangeSize = size - lastOffset;
9614                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9615                 }
9616 
9617                 // End of loop.
9618                 lastOffset = size;
9619             }
9620         }
9621     }
9622 
9623     PrintDetailedMap_End(json);
9624 }
9625 #endif // #if VMA_STATS_STRING_ENABLED
9626 
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t,VmaAllocationRequest * pAllocationRequest)9627 bool VmaBlockMetadata_Linear::CreateAllocationRequest(
9628     uint32_t currentFrameIndex,
9629     uint32_t frameInUseCount,
9630     VkDeviceSize bufferImageGranularity,
9631     VkDeviceSize allocSize,
9632     VkDeviceSize allocAlignment,
9633     bool upperAddress,
9634     VmaSuballocationType allocType,
9635     bool canMakeOtherLost,
9636     uint32_t /*strategy*/,
9637     VmaAllocationRequest* pAllocationRequest)
9638 {
9639     VMA_ASSERT(allocSize > 0);
9640     VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9641     VMA_ASSERT(pAllocationRequest != VMA_NULL);
9642     VMA_HEAVY_ASSERT(Validate());
9643 
9644     const VkDeviceSize size = GetSize();
9645     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9646     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9647 
9648     if(upperAddress)
9649     {
9650         if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9651         {
9652             VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
9653             return false;
9654         }
9655 
9656         // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
9657         if(allocSize > size)
9658         {
9659             return false;
9660         }
9661         VkDeviceSize resultBaseOffset = size - allocSize;
9662         if(!suballocations2nd.empty())
9663         {
9664             const VmaSuballocation& lastSuballoc = suballocations2nd.back();
9665             resultBaseOffset = lastSuballoc.offset - allocSize;
9666             if(allocSize > lastSuballoc.offset)
9667             {
9668                 return false;
9669             }
9670         }
9671 
9672         // Start from offset equal to end of free space.
9673         VkDeviceSize resultOffset = resultBaseOffset;
9674 
9675         // Apply VMA_DEBUG_MARGIN at the end.
9676         if(VMA_DEBUG_MARGIN > 0)
9677         {
9678 #if VMA_DEBUG_MARGIN
9679             if(resultOffset < VMA_DEBUG_MARGIN)
9680             {
9681                 return false;
9682             }
9683 #endif
9684             resultOffset -= VMA_DEBUG_MARGIN;
9685         }
9686 
9687         // Apply alignment.
9688         resultOffset = VmaAlignDown(resultOffset, allocAlignment);
9689 
9690         // Check next suballocations from 2nd for BufferImageGranularity conflicts.
9691         // Make bigger alignment if necessary.
9692         if(bufferImageGranularity > 1 && !suballocations2nd.empty())
9693         {
9694             bool bufferImageGranularityConflict = false;
9695             for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
9696             {
9697                 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
9698                 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9699                 {
9700                     if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
9701                     {
9702                         bufferImageGranularityConflict = true;
9703                         break;
9704                     }
9705                 }
9706                 else
9707                     // Already on previous page.
9708                     break;
9709             }
9710             if(bufferImageGranularityConflict)
9711             {
9712                 resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
9713             }
9714         }
9715 
9716         // There is enough free space.
9717         const VkDeviceSize endOf1st = !suballocations1st.empty() ?
9718             suballocations1st.back().offset + suballocations1st.back().size :
9719             0;
9720         if(endOf1st + VMA_DEBUG_MARGIN <= resultOffset)
9721         {
9722             // Check previous suballocations for BufferImageGranularity conflicts.
9723             // If conflict exists, allocation cannot be made here.
9724             if(bufferImageGranularity > 1)
9725             {
9726                 for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
9727                 {
9728                     const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
9729                     if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9730                     {
9731                         if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
9732                         {
9733                             return false;
9734                         }
9735                     }
9736                     else
9737                     {
9738                         // Already on next page.
9739                         break;
9740                     }
9741                 }
9742             }
9743 
9744             // All tests passed: Success.
9745             pAllocationRequest->offset = resultOffset;
9746             pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
9747             pAllocationRequest->sumItemSize = 0;
9748             // pAllocationRequest->item unused.
9749             pAllocationRequest->itemsToMakeLostCount = 0;
9750             return true;
9751         }
9752     }
9753     else // !upperAddress
9754     {
9755         if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9756         {
9757             // Try to allocate at the end of 1st vector.
9758 
9759             VkDeviceSize resultBaseOffset = 0;
9760             if(!suballocations1st.empty())
9761             {
9762                 const VmaSuballocation& lastSuballoc = suballocations1st.back();
9763                 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
9764             }
9765 
9766             // Start from offset equal to beginning of free space.
9767             VkDeviceSize resultOffset = resultBaseOffset;
9768 
9769             // Apply VMA_DEBUG_MARGIN at the beginning.
9770             if(VMA_DEBUG_MARGIN > 0)
9771             {
9772                 resultOffset += VMA_DEBUG_MARGIN;
9773             }
9774 
9775             // Apply alignment.
9776             resultOffset = VmaAlignUp(resultOffset, allocAlignment);
9777 
9778             // Check previous suballocations for BufferImageGranularity conflicts.
9779             // Make bigger alignment if necessary.
9780             if(bufferImageGranularity > 1 && !suballocations1st.empty())
9781             {
9782                 bool bufferImageGranularityConflict = false;
9783                 for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
9784                 {
9785                     const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
9786                     if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9787                     {
9788                         if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9789                         {
9790                             bufferImageGranularityConflict = true;
9791                             break;
9792                         }
9793                     }
9794                     else
9795                         // Already on previous page.
9796                         break;
9797                 }
9798                 if(bufferImageGranularityConflict)
9799                 {
9800                     resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
9801                 }
9802             }
9803 
9804             const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
9805                 suballocations2nd.back().offset : size;
9806 
9807             // There is enough free space at the end after alignment.
9808             if(resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd)
9809             {
9810                 // Check next suballocations for BufferImageGranularity conflicts.
9811                 // If conflict exists, allocation cannot be made here.
9812                 if(bufferImageGranularity > 1 && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9813                 {
9814                     for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
9815                     {
9816                         const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
9817                         if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9818                         {
9819                             if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9820                             {
9821                                 return false;
9822                             }
9823                         }
9824                         else
9825                         {
9826                             // Already on previous page.
9827                             break;
9828                         }
9829                     }
9830                 }
9831 
9832                 // All tests passed: Success.
9833                 pAllocationRequest->offset = resultOffset;
9834                 pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
9835                 pAllocationRequest->sumItemSize = 0;
9836                 // pAllocationRequest->item unused.
9837                 pAllocationRequest->itemsToMakeLostCount = 0;
9838                 return true;
9839             }
9840         }
9841 
9842         // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
9843         // beginning of 1st vector as the end of free space.
9844         if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9845         {
9846             VMA_ASSERT(!suballocations1st.empty());
9847 
9848             VkDeviceSize resultBaseOffset = 0;
9849             if(!suballocations2nd.empty())
9850             {
9851                 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
9852                 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
9853             }
9854 
9855             // Start from offset equal to beginning of free space.
9856             VkDeviceSize resultOffset = resultBaseOffset;
9857 
9858             // Apply VMA_DEBUG_MARGIN at the beginning.
9859             if(VMA_DEBUG_MARGIN > 0)
9860             {
9861                 resultOffset += VMA_DEBUG_MARGIN;
9862             }
9863 
9864             // Apply alignment.
9865             resultOffset = VmaAlignUp(resultOffset, allocAlignment);
9866 
9867             // Check previous suballocations for BufferImageGranularity conflicts.
9868             // Make bigger alignment if necessary.
9869             if(bufferImageGranularity > 1 && !suballocations2nd.empty())
9870             {
9871                 bool bufferImageGranularityConflict = false;
9872                 for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
9873                 {
9874                     const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
9875                     if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9876                     {
9877                         if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9878                         {
9879                             bufferImageGranularityConflict = true;
9880                             break;
9881                         }
9882                     }
9883                     else
9884                         // Already on previous page.
9885                         break;
9886                 }
9887                 if(bufferImageGranularityConflict)
9888                 {
9889                     resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
9890                 }
9891             }
9892 
9893             pAllocationRequest->itemsToMakeLostCount = 0;
9894             pAllocationRequest->sumItemSize = 0;
9895             size_t index1st = m_1stNullItemsBeginCount;
9896 
9897             if(canMakeOtherLost)
9898             {
9899                 while(index1st < suballocations1st.size() &&
9900                     resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset)
9901                 {
9902                     // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
9903                     const VmaSuballocation& suballoc = suballocations1st[index1st];
9904                     if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
9905                     {
9906                         // No problem.
9907                     }
9908                     else
9909                     {
9910                         VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
9911                         if(suballoc.hAllocation->CanBecomeLost() &&
9912                             suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9913                         {
9914                             ++pAllocationRequest->itemsToMakeLostCount;
9915                             pAllocationRequest->sumItemSize += suballoc.size;
9916                         }
9917                         else
9918                         {
9919                             return false;
9920                         }
9921                     }
9922                     ++index1st;
9923                 }
9924 
9925                 // Check next suballocations for BufferImageGranularity conflicts.
9926                 // If conflict exists, we must mark more allocations lost or fail.
9927                 if(bufferImageGranularity > 1)
9928                 {
9929                     while(index1st < suballocations1st.size())
9930                     {
9931                         const VmaSuballocation& suballoc = suballocations1st[index1st];
9932                         if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))
9933                         {
9934                             if(suballoc.hAllocation != VK_NULL_HANDLE)
9935                             {
9936                                 // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
9937                                 if(suballoc.hAllocation->CanBecomeLost() &&
9938                                     suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9939                                 {
9940                                     ++pAllocationRequest->itemsToMakeLostCount;
9941                                     pAllocationRequest->sumItemSize += suballoc.size;
9942                                 }
9943                                 else
9944                                 {
9945                                     return false;
9946                                 }
9947                             }
9948                         }
9949                         else
9950                         {
9951                             // Already on next page.
9952                             break;
9953                         }
9954                         ++index1st;
9955                     }
9956                 }
9957             }
9958 
9959             // There is enough free space at the end after alignment.
9960             if((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN < size) ||
9961                 (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset))
9962             {
9963                 // Check next suballocations for BufferImageGranularity conflicts.
9964                 // If conflict exists, allocation cannot be made here.
9965                 if(bufferImageGranularity > 1)
9966                 {
9967                     for(size_t nextSuballocIndex = index1st;
9968                         nextSuballocIndex < suballocations1st.size();
9969                         nextSuballocIndex++)
9970                     {
9971                         const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
9972                         if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9973                         {
9974                             if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9975                             {
9976                                 return false;
9977                             }
9978                         }
9979                         else
9980                         {
9981                             // Already on next page.
9982                             break;
9983                         }
9984                     }
9985                 }
9986 
9987                 // All tests passed: Success.
9988                 pAllocationRequest->offset = resultOffset;
9989                 pAllocationRequest->sumFreeSize =
9990                     (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size)
9991                     - resultBaseOffset
9992                     - pAllocationRequest->sumItemSize;
9993                 // pAllocationRequest->item unused.
9994                 return true;
9995             }
9996         }
9997     }
9998 
9999     return false;
10000 }
10001 
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)10002 bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
10003     uint32_t currentFrameIndex,
10004     uint32_t frameInUseCount,
10005     VmaAllocationRequest* pAllocationRequest)
10006 {
10007     if(pAllocationRequest->itemsToMakeLostCount == 0)
10008     {
10009         return true;
10010     }
10011 
10012     VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
10013 
10014     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10015     size_t index1st = m_1stNullItemsBeginCount;
10016     size_t madeLostCount = 0;
10017     while(madeLostCount < pAllocationRequest->itemsToMakeLostCount)
10018     {
10019         VMA_ASSERT(index1st < suballocations1st.size());
10020         VmaSuballocation& suballoc = suballocations1st[index1st];
10021         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10022         {
10023             VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
10024             VMA_ASSERT(suballoc.hAllocation->CanBecomeLost());
10025             if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10026             {
10027                 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10028                 suballoc.hAllocation = VK_NULL_HANDLE;
10029                 m_SumFreeSize += suballoc.size;
10030                 ++m_1stNullItemsMiddleCount;
10031                 ++madeLostCount;
10032             }
10033             else
10034             {
10035                 return false;
10036             }
10037         }
10038         ++index1st;
10039     }
10040 
10041     CleanupAfterFree();
10042     //VMA_HEAVY_ASSERT(Validate()); // Already called by ClanupAfterFree().
10043 
10044     return true;
10045 }
10046 
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)10047 uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
10048 {
10049     uint32_t lostAllocationCount = 0;
10050 
10051     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10052     for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
10053     {
10054         VmaSuballocation& suballoc = suballocations1st[i];
10055         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
10056             suballoc.hAllocation->CanBecomeLost() &&
10057             suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10058         {
10059             suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10060             suballoc.hAllocation = VK_NULL_HANDLE;
10061             ++m_1stNullItemsMiddleCount;
10062             m_SumFreeSize += suballoc.size;
10063             ++lostAllocationCount;
10064         }
10065     }
10066 
10067     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10068     for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
10069     {
10070         VmaSuballocation& suballoc = suballocations2nd[i];
10071         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
10072             suballoc.hAllocation->CanBecomeLost() &&
10073             suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10074         {
10075             suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10076             suballoc.hAllocation = VK_NULL_HANDLE;
10077             ++m_2ndNullItemsCount;
10078             ++lostAllocationCount;
10079         }
10080     }
10081 
10082     if(lostAllocationCount)
10083     {
10084         CleanupAfterFree();
10085     }
10086 
10087     return lostAllocationCount;
10088 }
10089 
CheckCorruption(const void * pBlockData)10090 VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
10091 {
10092     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10093     for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
10094     {
10095         const VmaSuballocation& suballoc = suballocations1st[i];
10096         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10097         {
10098             if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
10099             {
10100                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
10101                 return VK_ERROR_VALIDATION_FAILED_EXT;
10102             }
10103             if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
10104             {
10105                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
10106                 return VK_ERROR_VALIDATION_FAILED_EXT;
10107             }
10108         }
10109     }
10110 
10111     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10112     for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
10113     {
10114         const VmaSuballocation& suballoc = suballocations2nd[i];
10115         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10116         {
10117             if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
10118             {
10119                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
10120                 return VK_ERROR_VALIDATION_FAILED_EXT;
10121             }
10122             if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
10123             {
10124                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
10125                 return VK_ERROR_VALIDATION_FAILED_EXT;
10126             }
10127         }
10128     }
10129 
10130     return VK_SUCCESS;
10131 }
10132 
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,bool upperAddress,VmaAllocation hAllocation)10133 void VmaBlockMetadata_Linear::Alloc(
10134     const VmaAllocationRequest& request,
10135     VmaSuballocationType type,
10136     VkDeviceSize allocSize,
10137     bool upperAddress,
10138     VmaAllocation hAllocation)
10139 {
10140     const VmaSuballocation newSuballoc = { request.offset, allocSize, hAllocation, type };
10141 
10142     if(upperAddress)
10143     {
10144         VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
10145             "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
10146         SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10147         suballocations2nd.push_back(newSuballoc);
10148         m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
10149     }
10150     else
10151     {
10152         SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10153 
10154         // First allocation.
10155         if(suballocations1st.empty())
10156         {
10157             suballocations1st.push_back(newSuballoc);
10158         }
10159         else
10160         {
10161             // New allocation at the end of 1st vector.
10162             if(request.offset >= suballocations1st.back().offset + suballocations1st.back().size)
10163             {
10164                 // Check if it fits before the end of the block.
10165                 VMA_ASSERT(request.offset + allocSize <= GetSize());
10166                 suballocations1st.push_back(newSuballoc);
10167             }
10168             // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
10169             else if(request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset)
10170             {
10171                 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10172 
10173                 switch(m_2ndVectorMode)
10174                 {
10175                 case SECOND_VECTOR_EMPTY:
10176                     // First allocation from second part ring buffer.
10177                     VMA_ASSERT(suballocations2nd.empty());
10178                     m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
10179                     break;
10180                 case SECOND_VECTOR_RING_BUFFER:
10181                     // 2-part ring buffer is already started.
10182                     VMA_ASSERT(!suballocations2nd.empty());
10183                     break;
10184                 case SECOND_VECTOR_DOUBLE_STACK:
10185                     VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
10186                     break;
10187                 default:
10188                     VMA_ASSERT(0);
10189                 }
10190 
10191                 suballocations2nd.push_back(newSuballoc);
10192             }
10193             else
10194             {
10195                 VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
10196             }
10197         }
10198     }
10199 
10200     m_SumFreeSize -= newSuballoc.size;
10201 }
10202 
Free(const VmaAllocation allocation)10203 void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation)
10204 {
10205     FreeAtOffset(allocation->GetOffset());
10206 }
10207 
FreeAtOffset(VkDeviceSize offset)10208 void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
10209 {
10210     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10211     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10212 
10213     if(!suballocations1st.empty())
10214     {
10215         // First allocation: Mark it as next empty at the beginning.
10216         VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
10217         if(firstSuballoc.offset == offset)
10218         {
10219             firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10220             firstSuballoc.hAllocation = VK_NULL_HANDLE;
10221             m_SumFreeSize += firstSuballoc.size;
10222             ++m_1stNullItemsBeginCount;
10223             CleanupAfterFree();
10224             return;
10225         }
10226     }
10227 
10228     // Last allocation in 2-part ring buffer or top of upper stack (same logic).
10229     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
10230         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10231     {
10232         VmaSuballocation& lastSuballoc = suballocations2nd.back();
10233         if(lastSuballoc.offset == offset)
10234         {
10235             m_SumFreeSize += lastSuballoc.size;
10236             suballocations2nd.pop_back();
10237             CleanupAfterFree();
10238             return;
10239         }
10240     }
10241     // Last allocation in 1st vector.
10242     else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY)
10243     {
10244         VmaSuballocation& lastSuballoc = suballocations1st.back();
10245         if(lastSuballoc.offset == offset)
10246         {
10247             m_SumFreeSize += lastSuballoc.size;
10248             suballocations1st.pop_back();
10249             CleanupAfterFree();
10250             return;
10251         }
10252     }
10253 
10254     // Item from the middle of 1st vector.
10255     {
10256         VmaSuballocation refSuballoc;
10257         refSuballoc.offset = offset;
10258         // Rest of members stays uninitialized intentionally for better performance.
10259         SuballocationVectorType::iterator it = VmaVectorFindSorted<VmaSuballocationOffsetLess>(
10260             suballocations1st.begin() + m_1stNullItemsBeginCount,
10261             suballocations1st.end(),
10262             refSuballoc);
10263         if(it != suballocations1st.end())
10264         {
10265             it->type = VMA_SUBALLOCATION_TYPE_FREE;
10266             it->hAllocation = VK_NULL_HANDLE;
10267             ++m_1stNullItemsMiddleCount;
10268             m_SumFreeSize += it->size;
10269             CleanupAfterFree();
10270             return;
10271         }
10272     }
10273 
10274     if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
10275     {
10276         // Item from the middle of 2nd vector.
10277         VmaSuballocation refSuballoc;
10278         refSuballoc.offset = offset;
10279         // Rest of members stays uninitialized intentionally for better performance.
10280         SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
10281             VmaVectorFindSorted<VmaSuballocationOffsetLess>(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc) :
10282             VmaVectorFindSorted<VmaSuballocationOffsetGreater>(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc);
10283         if(it != suballocations2nd.end())
10284         {
10285             it->type = VMA_SUBALLOCATION_TYPE_FREE;
10286             it->hAllocation = VK_NULL_HANDLE;
10287             ++m_2ndNullItemsCount;
10288             m_SumFreeSize += it->size;
10289             CleanupAfterFree();
10290             return;
10291         }
10292     }
10293 
10294     VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
10295 }
10296 
ShouldCompact1st()10297 bool VmaBlockMetadata_Linear::ShouldCompact1st() const
10298 {
10299     const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
10300     const size_t suballocCount = AccessSuballocations1st().size();
10301     return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
10302 }
10303 
CleanupAfterFree()10304 void VmaBlockMetadata_Linear::CleanupAfterFree()
10305 {
10306     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10307     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10308 
10309     if(IsEmpty())
10310     {
10311         suballocations1st.clear();
10312         suballocations2nd.clear();
10313         m_1stNullItemsBeginCount = 0;
10314         m_1stNullItemsMiddleCount = 0;
10315         m_2ndNullItemsCount = 0;
10316         m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10317     }
10318     else
10319     {
10320         const size_t suballoc1stCount = suballocations1st.size();
10321         const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
10322         VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
10323 
10324         // Find more null items at the beginning of 1st vector.
10325         while(m_1stNullItemsBeginCount < suballoc1stCount &&
10326             suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
10327         {
10328             ++m_1stNullItemsBeginCount;
10329             --m_1stNullItemsMiddleCount;
10330         }
10331 
10332         // Find more null items at the end of 1st vector.
10333         while(m_1stNullItemsMiddleCount > 0 &&
10334             suballocations1st.back().hAllocation == VK_NULL_HANDLE)
10335         {
10336             --m_1stNullItemsMiddleCount;
10337             suballocations1st.pop_back();
10338         }
10339 
10340         // Find more null items at the end of 2nd vector.
10341         while(m_2ndNullItemsCount > 0 &&
10342             suballocations2nd.back().hAllocation == VK_NULL_HANDLE)
10343         {
10344             --m_2ndNullItemsCount;
10345             suballocations2nd.pop_back();
10346         }
10347 
10348         if(ShouldCompact1st())
10349         {
10350             const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
10351             size_t srcIndex = m_1stNullItemsBeginCount;
10352             for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
10353             {
10354                 while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE)
10355                 {
10356                     ++srcIndex;
10357                 }
10358                 if(dstIndex != srcIndex)
10359                 {
10360                     suballocations1st[dstIndex] = suballocations1st[srcIndex];
10361                 }
10362                 ++srcIndex;
10363             }
10364             suballocations1st.resize(nonNullItemCount);
10365             m_1stNullItemsBeginCount = 0;
10366             m_1stNullItemsMiddleCount = 0;
10367         }
10368 
10369         // 2nd vector became empty.
10370         if(suballocations2nd.empty())
10371         {
10372             m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10373         }
10374 
10375         // 1st vector became empty.
10376         if(suballocations1st.size() - m_1stNullItemsBeginCount == 0)
10377         {
10378             suballocations1st.clear();
10379             m_1stNullItemsBeginCount = 0;
10380 
10381             if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10382             {
10383                 // Swap 1st with 2nd. Now 2nd is empty.
10384                 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10385                 m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
10386                 while(m_1stNullItemsBeginCount < suballocations2nd.size() &&
10387                     suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
10388                 {
10389                     ++m_1stNullItemsBeginCount;
10390                     --m_1stNullItemsMiddleCount;
10391                 }
10392                 m_2ndNullItemsCount = 0;
10393                 m_1stVectorIndex ^= 1;
10394             }
10395         }
10396     }
10397 
10398     VMA_HEAVY_ASSERT(Validate());
10399 }
10400 
10401 
10402 ////////////////////////////////////////////////////////////////////////////////
10403 // class VmaBlockMetadata_Buddy
10404 
VmaBlockMetadata_Buddy(VmaAllocator hAllocator)10405 VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :
10406     VmaBlockMetadata(hAllocator),
10407     m_Root(VMA_NULL),
10408     m_AllocationCount(0),
10409     m_FreeCount(1),
10410     m_SumFreeSize(0)
10411 {
10412     memset(m_FreeList, 0, sizeof(m_FreeList));
10413 }
10414 
~VmaBlockMetadata_Buddy()10415 VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
10416 {
10417     DeleteNode(m_Root);
10418 }
10419 
Init(VkDeviceSize size)10420 void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
10421 {
10422     VmaBlockMetadata::Init(size);
10423 
10424     m_UsableSize = VmaPrevPow2(size);
10425     m_SumFreeSize = m_UsableSize;
10426 
10427     // Calculate m_LevelCount.
10428     m_LevelCount = 1;
10429     while(m_LevelCount < MAX_LEVELS &&
10430         LevelToNodeSize(m_LevelCount) >= MIN_NODE_SIZE)
10431     {
10432         ++m_LevelCount;
10433     }
10434 
10435     Node* rootNode = vma_new(GetAllocationCallbacks(), Node)();
10436     rootNode->offset = 0;
10437     rootNode->type = Node::TYPE_FREE;
10438     rootNode->parent = VMA_NULL;
10439     rootNode->buddy = VMA_NULL;
10440 
10441     m_Root = rootNode;
10442     AddToFreeListFront(0, rootNode);
10443 }
10444 
Validate()10445 bool VmaBlockMetadata_Buddy::Validate() const
10446 {
10447     // Validate tree.
10448     ValidationContext ctx;
10449     if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
10450     {
10451         VMA_VALIDATE(false && "ValidateNode failed.");
10452     }
10453     VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
10454     VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
10455 
10456     // Validate free node lists.
10457     for(uint32_t level = 0; level < m_LevelCount; ++level)
10458     {
10459         VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
10460             m_FreeList[level].front->free.prev == VMA_NULL);
10461 
10462         for(Node* node = m_FreeList[level].front;
10463             node != VMA_NULL;
10464             node = node->free.next)
10465         {
10466             VMA_VALIDATE(node->type == Node::TYPE_FREE);
10467 
10468             if(node->free.next == VMA_NULL)
10469             {
10470                 VMA_VALIDATE(m_FreeList[level].back == node);
10471             }
10472             else
10473             {
10474                 VMA_VALIDATE(node->free.next->free.prev == node);
10475             }
10476         }
10477     }
10478 
10479     // Validate that free lists ar higher levels are empty.
10480     for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
10481     {
10482         VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
10483     }
10484 
10485     return true;
10486 }
10487 
GetUnusedRangeSizeMax()10488 VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
10489 {
10490     for(uint32_t level = 0; level < m_LevelCount; ++level)
10491     {
10492         if(m_FreeList[level].front != VMA_NULL)
10493         {
10494             return LevelToNodeSize(level);
10495         }
10496     }
10497     return 0;
10498 }
10499 
CalcAllocationStatInfo(VmaStatInfo & outInfo)10500 void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
10501 {
10502     const VkDeviceSize unusableSize = GetUnusableSize();
10503 
10504     outInfo.blockCount = 1;
10505 
10506     outInfo.allocationCount = outInfo.unusedRangeCount = 0;
10507     outInfo.usedBytes = outInfo.unusedBytes = 0;
10508 
10509     outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
10510     outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
10511     outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused.
10512 
10513     CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0));
10514 
10515     if(unusableSize > 0)
10516     {
10517         ++outInfo.unusedRangeCount;
10518         outInfo.unusedBytes += unusableSize;
10519         outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize);
10520         outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize);
10521     }
10522 }
10523 
AddPoolStats(VmaPoolStats & inoutStats)10524 void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
10525 {
10526     const VkDeviceSize unusableSize = GetUnusableSize();
10527 
10528     inoutStats.size += GetSize();
10529     inoutStats.unusedSize += m_SumFreeSize + unusableSize;
10530     inoutStats.allocationCount += m_AllocationCount;
10531     inoutStats.unusedRangeCount += m_FreeCount;
10532     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
10533 
10534     if(unusableSize > 0)
10535     {
10536         ++inoutStats.unusedRangeCount;
10537         // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
10538     }
10539 }
10540 
10541 #if VMA_STATS_STRING_ENABLED
10542 
PrintDetailedMap(class VmaJsonWriter & json)10543 void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
10544 {
10545     // TODO optimize
10546     VmaStatInfo stat;
10547     CalcAllocationStatInfo(stat);
10548 
10549     PrintDetailedMap_Begin(
10550         json,
10551         stat.unusedBytes,
10552         stat.allocationCount,
10553         stat.unusedRangeCount);
10554 
10555     PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
10556 
10557     const VkDeviceSize unusableSize = GetUnusableSize();
10558     if(unusableSize > 0)
10559     {
10560         PrintDetailedMap_UnusedRange(json,
10561             m_UsableSize, // offset
10562             unusableSize); // size
10563     }
10564 
10565     PrintDetailedMap_End(json);
10566 }
10567 
10568 #endif // #if VMA_STATS_STRING_ENABLED
10569 
CreateAllocationRequest(uint32_t,uint32_t,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool,uint32_t,VmaAllocationRequest * pAllocationRequest)10570 bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
10571     uint32_t /*currentFrameIndex*/,
10572     uint32_t /*frameInUseCount*/,
10573     VkDeviceSize bufferImageGranularity,
10574     VkDeviceSize allocSize,
10575     VkDeviceSize allocAlignment,
10576     bool upperAddress,
10577     VmaSuballocationType allocType,
10578     bool /*canMakeOtherLost*/,
10579     uint32_t /*strategy*/,
10580     VmaAllocationRequest* pAllocationRequest)
10581 {
10582     VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
10583     (void) upperAddress;
10584 
10585     // Simple way to respect bufferImageGranularity. May be optimized some day.
10586     // Whenever it might be an OPTIMAL image...
10587     if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
10588         allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
10589         allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
10590     {
10591         allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity);
10592         allocSize = VMA_MAX(allocSize, bufferImageGranularity);
10593     }
10594 
10595     if(allocSize > m_UsableSize)
10596     {
10597         return false;
10598     }
10599 
10600     const uint32_t targetLevel = AllocSizeToLevel(allocSize);
10601     for(uint32_t level = targetLevel + 1; level--; )
10602     {
10603         for(Node* freeNode = m_FreeList[level].front;
10604             freeNode != VMA_NULL;
10605             freeNode = freeNode->free.next)
10606         {
10607             if(freeNode->offset % allocAlignment == 0)
10608             {
10609                 pAllocationRequest->offset = freeNode->offset;
10610                 pAllocationRequest->sumFreeSize = LevelToNodeSize(level);
10611                 pAllocationRequest->sumItemSize = 0;
10612                 pAllocationRequest->itemsToMakeLostCount = 0;
10613                 pAllocationRequest->customData = (void*)(uintptr_t)level;
10614                 return true;
10615             }
10616         }
10617     }
10618 
10619     return false;
10620 }
10621 
MakeRequestedAllocationsLost(uint32_t,uint32_t,VmaAllocationRequest * pAllocationRequest)10622 bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
10623     uint32_t /*currentFrameIndex*/,
10624     uint32_t /*frameInUseCount*/,
10625     VmaAllocationRequest* pAllocationRequest)
10626 {
10627     /*
10628     Lost allocations are not supported in buddy allocator at the moment.
10629     Support might be added in the future.
10630     */
10631     return pAllocationRequest->itemsToMakeLostCount == 0;
10632 }
10633 
MakeAllocationsLost(uint32_t,uint32_t)10634 uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t /*currentFrameIndex*/, uint32_t /*frameInUseCount*/)
10635 {
10636     /*
10637     Lost allocations are not supported in buddy allocator at the moment.
10638     Support might be added in the future.
10639     */
10640     return 0;
10641 }
10642 
Alloc(const VmaAllocationRequest & request,VmaSuballocationType,VkDeviceSize allocSize,bool,VmaAllocation hAllocation)10643 void VmaBlockMetadata_Buddy::Alloc(
10644     const VmaAllocationRequest& request,
10645     VmaSuballocationType /*type*/,
10646     VkDeviceSize allocSize,
10647     bool /*upperAddress*/,
10648     VmaAllocation hAllocation)
10649 {
10650     const uint32_t targetLevel = AllocSizeToLevel(allocSize);
10651     uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
10652 
10653     Node* currNode = m_FreeList[currLevel].front;
10654     VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
10655     while(currNode->offset != request.offset)
10656     {
10657         currNode = currNode->free.next;
10658         VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
10659     }
10660 
10661     // Go down, splitting free nodes.
10662     while(currLevel < targetLevel)
10663     {
10664         // currNode is already first free node at currLevel.
10665         // Remove it from list of free nodes at this currLevel.
10666         RemoveFromFreeList(currLevel, currNode);
10667 
10668         const uint32_t childrenLevel = currLevel + 1;
10669 
10670         // Create two free sub-nodes.
10671         Node* leftChild = vma_new(GetAllocationCallbacks(), Node)();
10672         Node* rightChild = vma_new(GetAllocationCallbacks(), Node)();
10673 
10674         leftChild->offset = currNode->offset;
10675         leftChild->type = Node::TYPE_FREE;
10676         leftChild->parent = currNode;
10677         leftChild->buddy = rightChild;
10678 
10679         rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
10680         rightChild->type = Node::TYPE_FREE;
10681         rightChild->parent = currNode;
10682         rightChild->buddy = leftChild;
10683 
10684         // Convert current currNode to split type.
10685         currNode->type = Node::TYPE_SPLIT;
10686         currNode->split.leftChild = leftChild;
10687 
10688         // Add child nodes to free list. Order is important!
10689         AddToFreeListFront(childrenLevel, rightChild);
10690         AddToFreeListFront(childrenLevel, leftChild);
10691 
10692         ++m_FreeCount;
10693         //m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2.
10694         ++currLevel;
10695         currNode = m_FreeList[currLevel].front;
10696 
10697         /*
10698         We can be sure that currNode, as left child of node previously split,
10699         also fullfills the alignment requirement.
10700         */
10701     }
10702 
10703     // Remove from free list.
10704     VMA_ASSERT(currLevel == targetLevel &&
10705         currNode != VMA_NULL &&
10706         currNode->type == Node::TYPE_FREE);
10707     RemoveFromFreeList(currLevel, currNode);
10708 
10709     // Convert to allocation node.
10710     currNode->type = Node::TYPE_ALLOCATION;
10711     currNode->allocation.alloc = hAllocation;
10712 
10713     ++m_AllocationCount;
10714     --m_FreeCount;
10715     m_SumFreeSize -= allocSize;
10716 }
10717 
DeleteNode(Node * node)10718 void VmaBlockMetadata_Buddy::DeleteNode(Node* node)
10719 {
10720     if(node->type == Node::TYPE_SPLIT)
10721     {
10722         DeleteNode(node->split.leftChild->buddy);
10723         DeleteNode(node->split.leftChild);
10724     }
10725 
10726     vma_delete(GetAllocationCallbacks(), node);
10727 }
10728 
ValidateNode(ValidationContext & ctx,const Node * parent,const Node * curr,uint32_t level,VkDeviceSize levelNodeSize)10729 bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
10730 {
10731     VMA_VALIDATE(level < m_LevelCount);
10732     VMA_VALIDATE(curr->parent == parent);
10733     VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
10734     VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
10735     switch(curr->type)
10736     {
10737     case Node::TYPE_FREE:
10738         // curr->free.prev, next are validated separately.
10739         ctx.calculatedSumFreeSize += levelNodeSize;
10740         ++ctx.calculatedFreeCount;
10741         break;
10742     case Node::TYPE_ALLOCATION:
10743         ++ctx.calculatedAllocationCount;
10744         ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize();
10745         VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);
10746         break;
10747     case Node::TYPE_SPLIT:
10748         {
10749             const uint32_t childrenLevel = level + 1;
10750             const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2;
10751             const Node* const leftChild = curr->split.leftChild;
10752             VMA_VALIDATE(leftChild != VMA_NULL);
10753             VMA_VALIDATE(leftChild->offset == curr->offset);
10754             if(!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
10755             {
10756                 VMA_VALIDATE(false && "ValidateNode for left child failed.");
10757             }
10758             const Node* const rightChild = leftChild->buddy;
10759             VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
10760             if(!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
10761             {
10762                 VMA_VALIDATE(false && "ValidateNode for right child failed.");
10763             }
10764         }
10765         break;
10766     default:
10767         return false;
10768     }
10769 
10770     return true;
10771 }
10772 
AllocSizeToLevel(VkDeviceSize allocSize)10773 uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
10774 {
10775     // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
10776     uint32_t level = 0;
10777     VkDeviceSize currLevelNodeSize = m_UsableSize;
10778     VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
10779     while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
10780     {
10781         ++level;
10782         currLevelNodeSize = nextLevelNodeSize;
10783         nextLevelNodeSize = currLevelNodeSize >> 1;
10784     }
10785     return level;
10786 }
10787 
FreeAtOffset(VmaAllocation alloc,VkDeviceSize offset)10788 void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset)
10789 {
10790     // Find node and level.
10791     Node* node = m_Root;
10792     VkDeviceSize nodeOffset = 0;
10793     uint32_t level = 0;
10794     VkDeviceSize levelNodeSize = LevelToNodeSize(0);
10795     while(node->type == Node::TYPE_SPLIT)
10796     {
10797         const VkDeviceSize nextLevelSize = levelNodeSize >> 1;
10798         if(offset < nodeOffset + nextLevelSize)
10799         {
10800             node = node->split.leftChild;
10801         }
10802         else
10803         {
10804             node = node->split.leftChild->buddy;
10805             nodeOffset += nextLevelSize;
10806         }
10807         ++level;
10808         levelNodeSize = nextLevelSize;
10809     }
10810 
10811     VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
10812     VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc);
10813 
10814     ++m_FreeCount;
10815     --m_AllocationCount;
10816     m_SumFreeSize += alloc->GetSize();
10817 
10818     node->type = Node::TYPE_FREE;
10819 
10820     // Join free nodes if possible.
10821     while(level > 0 && node->buddy->type == Node::TYPE_FREE)
10822     {
10823         RemoveFromFreeList(level, node->buddy);
10824         Node* const parent = node->parent;
10825 
10826         vma_delete(GetAllocationCallbacks(), node->buddy);
10827         vma_delete(GetAllocationCallbacks(), node);
10828         parent->type = Node::TYPE_FREE;
10829 
10830         node = parent;
10831         --level;
10832         //m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2.
10833         --m_FreeCount;
10834     }
10835 
10836     AddToFreeListFront(level, node);
10837 }
10838 
CalcAllocationStatInfoNode(VmaStatInfo & outInfo,const Node * node,VkDeviceSize levelNodeSize)10839 void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const
10840 {
10841     switch(node->type)
10842     {
10843     case Node::TYPE_FREE:
10844         ++outInfo.unusedRangeCount;
10845         outInfo.unusedBytes += levelNodeSize;
10846         outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, levelNodeSize);
10847         outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize);
10848         break;
10849     case Node::TYPE_ALLOCATION:
10850         {
10851             const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
10852             ++outInfo.allocationCount;
10853             outInfo.usedBytes += allocSize;
10854             outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize);
10855             outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize);
10856 
10857             const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize;
10858             if(unusedRangeSize > 0)
10859             {
10860                 ++outInfo.unusedRangeCount;
10861                 outInfo.unusedBytes += unusedRangeSize;
10862                 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize);
10863                 outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize);
10864             }
10865         }
10866         break;
10867     case Node::TYPE_SPLIT:
10868         {
10869             const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
10870             const Node* const leftChild = node->split.leftChild;
10871             CalcAllocationStatInfoNode(outInfo, leftChild, childrenNodeSize);
10872             const Node* const rightChild = leftChild->buddy;
10873             CalcAllocationStatInfoNode(outInfo, rightChild, childrenNodeSize);
10874         }
10875         break;
10876     default:
10877         VMA_ASSERT(0);
10878     }
10879 }
10880 
AddToFreeListFront(uint32_t level,Node * node)10881 void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
10882 {
10883     VMA_ASSERT(node->type == Node::TYPE_FREE);
10884 
10885     // List is empty.
10886     Node* const frontNode = m_FreeList[level].front;
10887     if(frontNode == VMA_NULL)
10888     {
10889         VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
10890         node->free.prev = node->free.next = VMA_NULL;
10891         m_FreeList[level].front = m_FreeList[level].back = node;
10892     }
10893     else
10894     {
10895         VMA_ASSERT(frontNode->free.prev == VMA_NULL);
10896         node->free.prev = VMA_NULL;
10897         node->free.next = frontNode;
10898         frontNode->free.prev = node;
10899         m_FreeList[level].front = node;
10900     }
10901 }
10902 
RemoveFromFreeList(uint32_t level,Node * node)10903 void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
10904 {
10905     VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
10906 
10907     // It is at the front.
10908     if(node->free.prev == VMA_NULL)
10909     {
10910         VMA_ASSERT(m_FreeList[level].front == node);
10911         m_FreeList[level].front = node->free.next;
10912     }
10913     else
10914     {
10915         Node* const prevFreeNode = node->free.prev;
10916         VMA_ASSERT(prevFreeNode->free.next == node);
10917         prevFreeNode->free.next = node->free.next;
10918     }
10919 
10920     // It is at the back.
10921     if(node->free.next == VMA_NULL)
10922     {
10923         VMA_ASSERT(m_FreeList[level].back == node);
10924         m_FreeList[level].back = node->free.prev;
10925     }
10926     else
10927     {
10928         Node* const nextFreeNode = node->free.next;
10929         VMA_ASSERT(nextFreeNode->free.prev == node);
10930         nextFreeNode->free.prev = node->free.prev;
10931     }
10932 }
10933 
10934 #if VMA_STATS_STRING_ENABLED
PrintDetailedMapNode(class VmaJsonWriter & json,const Node * node,VkDeviceSize levelNodeSize)10935 void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
10936 {
10937     switch(node->type)
10938     {
10939     case Node::TYPE_FREE:
10940         PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
10941         break;
10942     case Node::TYPE_ALLOCATION:
10943         {
10944             PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);
10945             const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
10946             if(allocSize < levelNodeSize)
10947             {
10948                 PrintDetailedMap_UnusedRange(json, node->offset + allocSize, levelNodeSize - allocSize);
10949             }
10950         }
10951         break;
10952     case Node::TYPE_SPLIT:
10953         {
10954             const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
10955             const Node* const leftChild = node->split.leftChild;
10956             PrintDetailedMapNode(json, leftChild, childrenNodeSize);
10957             const Node* const rightChild = leftChild->buddy;
10958             PrintDetailedMapNode(json, rightChild, childrenNodeSize);
10959         }
10960         break;
10961     default:
10962         VMA_ASSERT(0);
10963     }
10964 }
10965 #endif // #if VMA_STATS_STRING_ENABLED
10966 
10967 
10968 ////////////////////////////////////////////////////////////////////////////////
10969 // class VmaDeviceMemoryBlock
10970 
VmaDeviceMemoryBlock(VmaAllocator)10971 VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator /*hAllocator*/) :
10972     m_pMetadata(VMA_NULL),
10973     m_MemoryTypeIndex(UINT32_MAX),
10974     m_Id(0),
10975     m_hMemory(VK_NULL_HANDLE),
10976     m_MapCount(0),
10977     m_pMappedData(VMA_NULL)
10978 {
10979 }
10980 
Init(VmaAllocator hAllocator,uint32_t newMemoryTypeIndex,VkDeviceMemory newMemory,VkDeviceSize newSize,uint32_t id,uint32_t algorithm)10981 void VmaDeviceMemoryBlock::Init(
10982     VmaAllocator hAllocator,
10983     uint32_t newMemoryTypeIndex,
10984     VkDeviceMemory newMemory,
10985     VkDeviceSize newSize,
10986     uint32_t id,
10987     uint32_t algorithm)
10988 {
10989     VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
10990 
10991     m_MemoryTypeIndex = newMemoryTypeIndex;
10992     m_Id = id;
10993     m_hMemory = newMemory;
10994 
10995     switch(algorithm)
10996     {
10997     case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
10998         m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator);
10999         break;
11000     case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
11001         m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator);
11002         break;
11003     default:
11004         VMA_ASSERT(0);
11005         // Fall-through.
11006     case 0:
11007         m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator);
11008     }
11009     m_pMetadata->Init(newSize);
11010 }
11011 
Destroy(VmaAllocator allocator)11012 void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
11013 {
11014     // This is the most important assert in the entire library.
11015     // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
11016     VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
11017 
11018     VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
11019     allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
11020     m_hMemory = VK_NULL_HANDLE;
11021 
11022     vma_delete(allocator, m_pMetadata);
11023     m_pMetadata = VMA_NULL;
11024 }
11025 
Validate()11026 bool VmaDeviceMemoryBlock::Validate() const
11027 {
11028     VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
11029         (m_pMetadata->GetSize() != 0));
11030 
11031     return m_pMetadata->Validate();
11032 }
11033 
CheckCorruption(VmaAllocator hAllocator)11034 VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
11035 {
11036     void* pData = nullptr;
11037     VkResult res = Map(hAllocator, 1, &pData);
11038     if(res != VK_SUCCESS)
11039     {
11040         return res;
11041     }
11042 
11043     res = m_pMetadata->CheckCorruption(pData);
11044 
11045     Unmap(hAllocator, 1);
11046 
11047     return res;
11048 }
11049 
Map(VmaAllocator hAllocator,uint32_t count,void ** ppData)11050 VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
11051 {
11052     if(count == 0)
11053     {
11054         return VK_SUCCESS;
11055     }
11056 
11057     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11058     if(m_MapCount != 0)
11059     {
11060         m_MapCount += count;
11061         VMA_ASSERT(m_pMappedData != VMA_NULL);
11062         if(ppData != VMA_NULL)
11063         {
11064             *ppData = m_pMappedData;
11065         }
11066         return VK_SUCCESS;
11067     }
11068     else
11069     {
11070         VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
11071             hAllocator->m_hDevice,
11072             m_hMemory,
11073             0, // offset
11074             VK_WHOLE_SIZE,
11075             0, // flags
11076             &m_pMappedData);
11077         if(result == VK_SUCCESS)
11078         {
11079             if(ppData != VMA_NULL)
11080             {
11081                 *ppData = m_pMappedData;
11082             }
11083             m_MapCount = count;
11084         }
11085         return result;
11086     }
11087 }
11088 
Unmap(VmaAllocator hAllocator,uint32_t count)11089 void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
11090 {
11091     if(count == 0)
11092     {
11093         return;
11094     }
11095 
11096     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11097     if(m_MapCount >= count)
11098     {
11099         m_MapCount -= count;
11100         if(m_MapCount == 0)
11101         {
11102             m_pMappedData = VMA_NULL;
11103             (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
11104         }
11105     }
11106     else
11107     {
11108         VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
11109     }
11110 }
11111 
WriteMagicValueAroundAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)11112 VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
11113 {
11114     VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
11115     VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
11116 
11117     void* pData;
11118     VkResult res = Map(hAllocator, 1, &pData);
11119     if(res != VK_SUCCESS)
11120     {
11121         return res;
11122     }
11123 
11124     VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN);
11125     VmaWriteMagicValue(pData, allocOffset + allocSize);
11126 
11127     Unmap(hAllocator, 1);
11128 
11129     return VK_SUCCESS;
11130 }
11131 
ValidateMagicValueAroundAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)11132 VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
11133 {
11134     VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
11135     VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
11136 
11137     void* pData;
11138     VkResult res = Map(hAllocator, 1, &pData);
11139     if(res != VK_SUCCESS)
11140     {
11141         return res;
11142     }
11143 
11144     if(!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN))
11145     {
11146         VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
11147     }
11148     else if(!VmaValidateMagicValue(pData, allocOffset + allocSize))
11149     {
11150         VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
11151     }
11152 
11153     Unmap(hAllocator, 1);
11154 
11155     return VK_SUCCESS;
11156 }
11157 
BindBufferMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkBuffer hBuffer)11158 VkResult VmaDeviceMemoryBlock::BindBufferMemory(
11159     const VmaAllocator hAllocator,
11160     const VmaAllocation hAllocation,
11161     VkBuffer hBuffer)
11162 {
11163     VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
11164         hAllocation->GetBlock() == this);
11165     // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
11166     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11167     return hAllocator->GetVulkanFunctions().vkBindBufferMemory(
11168         hAllocator->m_hDevice,
11169         hBuffer,
11170         m_hMemory,
11171         hAllocation->GetOffset());
11172 }
11173 
BindImageMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkImage hImage)11174 VkResult VmaDeviceMemoryBlock::BindImageMemory(
11175     const VmaAllocator hAllocator,
11176     const VmaAllocation hAllocation,
11177     VkImage hImage)
11178 {
11179     VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
11180         hAllocation->GetBlock() == this);
11181     // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
11182     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11183     return hAllocator->GetVulkanFunctions().vkBindImageMemory(
11184         hAllocator->m_hDevice,
11185         hImage,
11186         m_hMemory,
11187         hAllocation->GetOffset());
11188 }
11189 
InitStatInfo(VmaStatInfo & outInfo)11190 static void InitStatInfo(VmaStatInfo& outInfo)
11191 {
11192     memset(&outInfo, 0, sizeof(outInfo));
11193     outInfo.allocationSizeMin = UINT64_MAX;
11194     outInfo.unusedRangeSizeMin = UINT64_MAX;
11195 }
11196 
11197 // Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
VmaAddStatInfo(VmaStatInfo & inoutInfo,const VmaStatInfo & srcInfo)11198 static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
11199 {
11200     inoutInfo.blockCount += srcInfo.blockCount;
11201     inoutInfo.allocationCount += srcInfo.allocationCount;
11202     inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
11203     inoutInfo.usedBytes += srcInfo.usedBytes;
11204     inoutInfo.unusedBytes += srcInfo.unusedBytes;
11205     inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
11206     inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
11207     inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
11208     inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
11209 }
11210 
VmaPostprocessCalcStatInfo(VmaStatInfo & inoutInfo)11211 static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
11212 {
11213     inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
11214         VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
11215     inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
11216         VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
11217 }
11218 
VmaPool_T(VmaAllocator hAllocator,const VmaPoolCreateInfo & createInfo,VkDeviceSize preferredBlockSize)11219 VmaPool_T::VmaPool_T(
11220     VmaAllocator hAllocator,
11221     const VmaPoolCreateInfo& createInfo,
11222     VkDeviceSize preferredBlockSize) :
11223     m_BlockVector(
11224         hAllocator,
11225         createInfo.memoryTypeIndex,
11226         createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
11227         createInfo.minBlockCount,
11228         createInfo.maxBlockCount,
11229         (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
11230         createInfo.frameInUseCount,
11231         true, // isCustomPool
11232         createInfo.blockSize != 0, // explicitBlockSize
11233         createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK), // algorithm
11234     m_Id(0)
11235 {
11236 }
11237 
~VmaPool_T()11238 VmaPool_T::~VmaPool_T()
11239 {
11240 }
11241 
11242 #if VMA_STATS_STRING_ENABLED
11243 
11244 #endif // #if VMA_STATS_STRING_ENABLED
11245 
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)11246 VmaBlockVector::VmaBlockVector(
11247     VmaAllocator hAllocator,
11248     uint32_t memoryTypeIndex,
11249     VkDeviceSize preferredBlockSize,
11250     size_t minBlockCount,
11251     size_t maxBlockCount,
11252     VkDeviceSize bufferImageGranularity,
11253     uint32_t frameInUseCount,
11254     bool isCustomPool,
11255     bool explicitBlockSize,
11256     uint32_t algorithm) :
11257     m_hAllocator(hAllocator),
11258     m_MemoryTypeIndex(memoryTypeIndex),
11259     m_PreferredBlockSize(preferredBlockSize),
11260     m_MinBlockCount(minBlockCount),
11261     m_MaxBlockCount(maxBlockCount),
11262     m_BufferImageGranularity(bufferImageGranularity),
11263     m_FrameInUseCount(frameInUseCount),
11264     m_IsCustomPool(isCustomPool),
11265     m_ExplicitBlockSize(explicitBlockSize),
11266     m_Algorithm(algorithm),
11267     m_HasEmptyBlock(false),
11268     m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
11269     m_NextBlockId(0)
11270 {
11271 }
11272 
~VmaBlockVector()11273 VmaBlockVector::~VmaBlockVector()
11274 {
11275     for(size_t i = m_Blocks.size(); i--; )
11276     {
11277         m_Blocks[i]->Destroy(m_hAllocator);
11278         vma_delete(m_hAllocator, m_Blocks[i]);
11279     }
11280 }
11281 
CreateMinBlocks()11282 VkResult VmaBlockVector::CreateMinBlocks()
11283 {
11284     for(size_t i = 0; i < m_MinBlockCount; ++i)
11285     {
11286         VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
11287         if(res != VK_SUCCESS)
11288         {
11289             return res;
11290         }
11291     }
11292     return VK_SUCCESS;
11293 }
11294 
GetPoolStats(VmaPoolStats * pStats)11295 void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
11296 {
11297     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
11298 
11299     const size_t blockCount = m_Blocks.size();
11300 
11301     pStats->size = 0;
11302     pStats->unusedSize = 0;
11303     pStats->allocationCount = 0;
11304     pStats->unusedRangeCount = 0;
11305     pStats->unusedRangeSizeMax = 0;
11306     pStats->blockCount = blockCount;
11307 
11308     for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
11309     {
11310         const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
11311         VMA_ASSERT(pBlock);
11312         VMA_HEAVY_ASSERT(pBlock->Validate());
11313         pBlock->m_pMetadata->AddPoolStats(*pStats);
11314     }
11315 }
11316 
IsCorruptionDetectionEnabled()11317 bool VmaBlockVector::IsCorruptionDetectionEnabled() const
11318 {
11319     const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
11320     return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
11321         (VMA_DEBUG_MARGIN > 0) &&
11322         (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
11323 }
11324 
11325 static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
11326 
Allocate(VmaPool hCurrentPool,uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)11327 VkResult VmaBlockVector::Allocate(
11328     VmaPool hCurrentPool,
11329     uint32_t currentFrameIndex,
11330     VkDeviceSize size,
11331     VkDeviceSize alignment,
11332     const VmaAllocationCreateInfo& createInfo,
11333     VmaSuballocationType suballocType,
11334     size_t allocationCount,
11335     VmaAllocation* pAllocations)
11336 {
11337     size_t allocIndex;
11338     VkResult res = VK_SUCCESS;
11339 
11340     {
11341         VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
11342         for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
11343         {
11344             res = AllocatePage(
11345                 hCurrentPool,
11346                 currentFrameIndex,
11347                 size,
11348                 alignment,
11349                 createInfo,
11350                 suballocType,
11351                 pAllocations + allocIndex);
11352             if(res != VK_SUCCESS)
11353             {
11354                 break;
11355             }
11356         }
11357     }
11358 
11359     if(res != VK_SUCCESS)
11360     {
11361         // Free all already created allocations.
11362         while(allocIndex--)
11363         {
11364             Free(pAllocations[allocIndex]);
11365         }
11366         memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
11367     }
11368 
11369     return res;
11370 }
11371 
AllocatePage(VmaPool hCurrentPool,uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)11372 VkResult VmaBlockVector::AllocatePage(
11373     VmaPool hCurrentPool,
11374     uint32_t currentFrameIndex,
11375     VkDeviceSize size,
11376     VkDeviceSize alignment,
11377     const VmaAllocationCreateInfo& createInfo,
11378     VmaSuballocationType suballocType,
11379     VmaAllocation* pAllocation)
11380 {
11381     const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
11382     bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
11383     const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
11384     const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
11385     const bool canCreateNewBlock =
11386         ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
11387         (m_Blocks.size() < m_MaxBlockCount);
11388     uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
11389 
11390     // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
11391     // Which in turn is available only when maxBlockCount = 1.
11392     if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
11393     {
11394         canMakeOtherLost = false;
11395     }
11396 
11397     // Upper address can only be used with linear allocator and within single memory block.
11398     if(isUpperAddress &&
11399         (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
11400     {
11401         return VK_ERROR_FEATURE_NOT_PRESENT;
11402     }
11403 
11404     // Validate strategy.
11405     switch(strategy)
11406     {
11407     case 0:
11408         strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
11409         break;
11410     case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
11411     case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
11412     case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
11413         break;
11414     default:
11415         return VK_ERROR_FEATURE_NOT_PRESENT;
11416     }
11417 
11418     // Early reject: requested allocation size is larger that maximum block size for this block vector.
11419     if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
11420     {
11421         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11422     }
11423 
11424     /*
11425     Under certain condition, this whole section can be skipped for optimization, so
11426     we move on directly to trying to allocate with canMakeOtherLost. That's the case
11427     e.g. for custom pools with linear algorithm.
11428     */
11429     if(!canMakeOtherLost || canCreateNewBlock)
11430     {
11431         // 1. Search existing allocations. Try to allocate without making other allocations lost.
11432         VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
11433         allocFlagsCopy &= ~VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
11434 
11435         if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
11436         {
11437             // Use only last block.
11438             if(!m_Blocks.empty())
11439             {
11440                 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
11441                 VMA_ASSERT(pCurrBlock);
11442                 VkResult res = AllocateFromBlock(
11443                     pCurrBlock,
11444                     hCurrentPool,
11445                     currentFrameIndex,
11446                     size,
11447                     alignment,
11448                     allocFlagsCopy,
11449                     createInfo.pUserData,
11450                     suballocType,
11451                     strategy,
11452                     pAllocation);
11453                 if(res == VK_SUCCESS)
11454                 {
11455                     VMA_DEBUG_LOG("    Returned from last block #%u", (uint32_t)(m_Blocks.size() - 1));
11456                     return VK_SUCCESS;
11457                 }
11458             }
11459         }
11460         else
11461         {
11462             if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
11463             {
11464                 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
11465                 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
11466                 {
11467                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11468                     VMA_ASSERT(pCurrBlock);
11469                     VkResult res = AllocateFromBlock(
11470                         pCurrBlock,
11471                         hCurrentPool,
11472                         currentFrameIndex,
11473                         size,
11474                         alignment,
11475                         allocFlagsCopy,
11476                         createInfo.pUserData,
11477                         suballocType,
11478                         strategy,
11479                         pAllocation);
11480                     if(res == VK_SUCCESS)
11481                     {
11482                         VMA_DEBUG_LOG("    Returned from existing block #%u", (uint32_t)blockIndex);
11483                         return VK_SUCCESS;
11484                     }
11485                 }
11486             }
11487             else // WORST_FIT, FIRST_FIT
11488             {
11489                 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
11490                 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
11491                 {
11492                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11493                     VMA_ASSERT(pCurrBlock);
11494                     VkResult res = AllocateFromBlock(
11495                         pCurrBlock,
11496                         hCurrentPool,
11497                         currentFrameIndex,
11498                         size,
11499                         alignment,
11500                         allocFlagsCopy,
11501                         createInfo.pUserData,
11502                         suballocType,
11503                         strategy,
11504                         pAllocation);
11505                     if(res == VK_SUCCESS)
11506                     {
11507                         VMA_DEBUG_LOG("    Returned from existing block #%u", (uint32_t)blockIndex);
11508                         return VK_SUCCESS;
11509                     }
11510                 }
11511             }
11512         }
11513 
11514         // 2. Try to create new block.
11515         if(canCreateNewBlock)
11516         {
11517             // Calculate optimal size for new block.
11518             VkDeviceSize newBlockSize = m_PreferredBlockSize;
11519             uint32_t newBlockSizeShift = 0;
11520             const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
11521 
11522             if(!m_ExplicitBlockSize)
11523             {
11524                 // Allocate 1/8, 1/4, 1/2 as first blocks.
11525                 const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
11526                 for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
11527                 {
11528                     const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
11529                     if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
11530                     {
11531                         newBlockSize = smallerNewBlockSize;
11532                         ++newBlockSizeShift;
11533                     }
11534                     else
11535                     {
11536                         break;
11537                     }
11538                 }
11539             }
11540 
11541             size_t newBlockIndex = 0;
11542             VkResult res = CreateBlock(newBlockSize, &newBlockIndex);
11543             // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
11544             if(!m_ExplicitBlockSize)
11545             {
11546                 while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
11547                 {
11548                     const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
11549                     if(smallerNewBlockSize >= size)
11550                     {
11551                         newBlockSize = smallerNewBlockSize;
11552                         ++newBlockSizeShift;
11553                         res = CreateBlock(newBlockSize, &newBlockIndex);
11554                     }
11555                     else
11556                     {
11557                         break;
11558                     }
11559                 }
11560             }
11561 
11562             if(res == VK_SUCCESS)
11563             {
11564                 VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
11565                 VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
11566 
11567                 res = AllocateFromBlock(
11568                     pBlock,
11569                     hCurrentPool,
11570                     currentFrameIndex,
11571                     size,
11572                     alignment,
11573                     allocFlagsCopy,
11574                     createInfo.pUserData,
11575                     suballocType,
11576                     strategy,
11577                     pAllocation);
11578                 if(res == VK_SUCCESS)
11579                 {
11580                     VMA_DEBUG_LOG("    Created new block Size=%llu", newBlockSize);
11581                     return VK_SUCCESS;
11582                 }
11583                 else
11584                 {
11585                     // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
11586                     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11587                 }
11588             }
11589         }
11590     }
11591 
11592     // 3. Try to allocate from existing blocks with making other allocations lost.
11593     if(canMakeOtherLost)
11594     {
11595         uint32_t tryIndex = 0;
11596         for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
11597         {
11598             VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
11599             VmaAllocationRequest bestRequest = {};
11600             VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
11601 
11602             // 1. Search existing allocations.
11603             if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
11604             {
11605                 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
11606                 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
11607                 {
11608                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11609                     VMA_ASSERT(pCurrBlock);
11610                     VmaAllocationRequest currRequest = {};
11611                     if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
11612                         currentFrameIndex,
11613                         m_FrameInUseCount,
11614                         m_BufferImageGranularity,
11615                         size,
11616                         alignment,
11617                         (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
11618                         suballocType,
11619                         canMakeOtherLost,
11620                         strategy,
11621                         &currRequest))
11622                     {
11623                         const VkDeviceSize currRequestCost = currRequest.CalcCost();
11624                         if(pBestRequestBlock == VMA_NULL ||
11625                             currRequestCost < bestRequestCost)
11626                         {
11627                             pBestRequestBlock = pCurrBlock;
11628                             bestRequest = currRequest;
11629                             bestRequestCost = currRequestCost;
11630 
11631                             if(bestRequestCost == 0)
11632                             {
11633                                 break;
11634                             }
11635                         }
11636                     }
11637                 }
11638             }
11639             else // WORST_FIT, FIRST_FIT
11640             {
11641                 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
11642                 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
11643                 {
11644                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11645                     VMA_ASSERT(pCurrBlock);
11646                     VmaAllocationRequest currRequest = {};
11647                     if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
11648                         currentFrameIndex,
11649                         m_FrameInUseCount,
11650                         m_BufferImageGranularity,
11651                         size,
11652                         alignment,
11653                         (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
11654                         suballocType,
11655                         canMakeOtherLost,
11656                         strategy,
11657                         &currRequest))
11658                     {
11659                         const VkDeviceSize currRequestCost = currRequest.CalcCost();
11660                         if(pBestRequestBlock == VMA_NULL ||
11661                             currRequestCost < bestRequestCost ||
11662                             strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
11663                         {
11664                             pBestRequestBlock = pCurrBlock;
11665                             bestRequest = currRequest;
11666                             bestRequestCost = currRequestCost;
11667 
11668                             if(bestRequestCost == 0 ||
11669                                 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
11670                             {
11671                                 break;
11672                             }
11673                         }
11674                     }
11675                 }
11676             }
11677 
11678             if(pBestRequestBlock != VMA_NULL)
11679             {
11680                 if(mapped)
11681                 {
11682                     VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
11683                     if(res != VK_SUCCESS)
11684                     {
11685                         return res;
11686                     }
11687                 }
11688 
11689                 if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
11690                     currentFrameIndex,
11691                     m_FrameInUseCount,
11692                     &bestRequest))
11693                 {
11694                     // We no longer have an empty Allocation.
11695                     if(pBestRequestBlock->m_pMetadata->IsEmpty())
11696                     {
11697                         m_HasEmptyBlock = false;
11698                     }
11699                     // Allocate from this pBlock.
11700                     *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
11701                     pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, isUpperAddress, *pAllocation);
11702                     (*pAllocation)->InitBlockAllocation(
11703                         hCurrentPool,
11704                         pBestRequestBlock,
11705                         bestRequest.offset,
11706                         alignment,
11707                         size,
11708                         suballocType,
11709                         mapped,
11710                         (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
11711                     VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
11712                     VMA_DEBUG_LOG("    Returned from existing allocation #%u", (uint32_t)blockIndex);
11713                     (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
11714                     if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
11715                     {
11716                         m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
11717                     }
11718                     if(IsCorruptionDetectionEnabled())
11719                     {
11720                         VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
11721                         (void) res;
11722                         VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
11723                     }
11724                     return VK_SUCCESS;
11725                 }
11726                 // else: Some allocations must have been touched while we are here. Next try.
11727             }
11728             else
11729             {
11730                 // Could not find place in any of the blocks - break outer loop.
11731                 break;
11732             }
11733         }
11734         /* Maximum number of tries exceeded - a very unlike event when many other
11735         threads are simultaneously touching allocations making it impossible to make
11736         lost at the same time as we try to allocate. */
11737         if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
11738         {
11739             return VK_ERROR_TOO_MANY_OBJECTS;
11740         }
11741     }
11742 
11743     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11744 }
11745 
Free(VmaAllocation hAllocation)11746 void VmaBlockVector::Free(
11747     VmaAllocation hAllocation)
11748 {
11749     VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
11750 
11751     // Scope for lock.
11752     {
11753         VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
11754 
11755         VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
11756 
11757         if(IsCorruptionDetectionEnabled())
11758         {
11759             VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
11760             (void) res;
11761             VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
11762         }
11763 
11764         if(hAllocation->IsPersistentMap())
11765         {
11766             pBlock->Unmap(m_hAllocator, 1);
11767         }
11768 
11769         pBlock->m_pMetadata->Free(hAllocation);
11770         VMA_HEAVY_ASSERT(pBlock->Validate());
11771 
11772         VMA_DEBUG_LOG("  Freed from MemoryTypeIndex=%u", memTypeIndex);
11773 
11774         // pBlock became empty after this deallocation.
11775         if(pBlock->m_pMetadata->IsEmpty())
11776         {
11777             // Already has empty Allocation. We don't want to have two, so delete this one.
11778             if(m_HasEmptyBlock && m_Blocks.size() > m_MinBlockCount)
11779             {
11780                 pBlockToDelete = pBlock;
11781                 Remove(pBlock);
11782             }
11783             // We now have first empty block.
11784             else
11785             {
11786                 m_HasEmptyBlock = true;
11787             }
11788         }
11789         // pBlock didn't become empty, but we have another empty block - find and free that one.
11790         // (This is optional, heuristics.)
11791         else if(m_HasEmptyBlock)
11792         {
11793             VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
11794             if(pLastBlock->m_pMetadata->IsEmpty() && m_Blocks.size() > m_MinBlockCount)
11795             {
11796                 pBlockToDelete = pLastBlock;
11797                 m_Blocks.pop_back();
11798                 m_HasEmptyBlock = false;
11799             }
11800         }
11801 
11802         IncrementallySortBlocks();
11803     }
11804 
11805     // Destruction of a free Allocation. Deferred until this point, outside of mutex
11806     // lock, for performance reason.
11807     if(pBlockToDelete != VMA_NULL)
11808     {
11809         VMA_DEBUG_LOG("    Deleted empty allocation");
11810         pBlockToDelete->Destroy(m_hAllocator);
11811         vma_delete(m_hAllocator, pBlockToDelete);
11812     }
11813 }
11814 
CalcMaxBlockSize()11815 VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
11816 {
11817     VkDeviceSize result = 0;
11818     for(size_t i = m_Blocks.size(); i--; )
11819     {
11820         result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
11821         if(result >= m_PreferredBlockSize)
11822         {
11823             break;
11824         }
11825     }
11826     return result;
11827 }
11828 
Remove(VmaDeviceMemoryBlock * pBlock)11829 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
11830 {
11831     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
11832     {
11833         if(m_Blocks[blockIndex] == pBlock)
11834         {
11835             VmaVectorRemove(m_Blocks, blockIndex);
11836             return;
11837         }
11838     }
11839     VMA_ASSERT(0);
11840 }
11841 
IncrementallySortBlocks()11842 void VmaBlockVector::IncrementallySortBlocks()
11843 {
11844     if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
11845     {
11846         // Bubble sort only until first swap.
11847         for(size_t i = 1; i < m_Blocks.size(); ++i)
11848         {
11849             if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
11850             {
11851                 VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
11852                 return;
11853             }
11854         }
11855     }
11856 }
11857 
AllocateFromBlock(VmaDeviceMemoryBlock * pBlock,VmaPool hCurrentPool,uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,VmaAllocationCreateFlags allocFlags,void * pUserData,VmaSuballocationType suballocType,uint32_t strategy,VmaAllocation * pAllocation)11858 VkResult VmaBlockVector::AllocateFromBlock(
11859     VmaDeviceMemoryBlock* pBlock,
11860     VmaPool hCurrentPool,
11861     uint32_t currentFrameIndex,
11862     VkDeviceSize size,
11863     VkDeviceSize alignment,
11864     VmaAllocationCreateFlags allocFlags,
11865     void* pUserData,
11866     VmaSuballocationType suballocType,
11867     uint32_t strategy,
11868     VmaAllocation* pAllocation)
11869 {
11870     VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
11871     const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
11872     const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
11873     const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
11874 
11875     VmaAllocationRequest currRequest = {};
11876     if(pBlock->m_pMetadata->CreateAllocationRequest(
11877         currentFrameIndex,
11878         m_FrameInUseCount,
11879         m_BufferImageGranularity,
11880         size,
11881         alignment,
11882         isUpperAddress,
11883         suballocType,
11884         false, // canMakeOtherLost
11885         strategy,
11886         &currRequest))
11887     {
11888         // Allocate from pCurrBlock.
11889         VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
11890 
11891         if(mapped)
11892         {
11893             VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
11894             if(res != VK_SUCCESS)
11895             {
11896                 return res;
11897             }
11898         }
11899 
11900         // We no longer have an empty Allocation.
11901         if(pBlock->m_pMetadata->IsEmpty())
11902         {
11903             m_HasEmptyBlock = false;
11904         }
11905 
11906         *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
11907         pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, isUpperAddress, *pAllocation);
11908         (*pAllocation)->InitBlockAllocation(
11909             hCurrentPool,
11910             pBlock,
11911             currRequest.offset,
11912             alignment,
11913             size,
11914             suballocType,
11915             mapped,
11916             (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
11917         VMA_HEAVY_ASSERT(pBlock->Validate());
11918         (*pAllocation)->SetUserData(m_hAllocator, pUserData);
11919         if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
11920         {
11921             m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
11922         }
11923         if(IsCorruptionDetectionEnabled())
11924         {
11925             VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
11926             (void) res;
11927             VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
11928         }
11929         return VK_SUCCESS;
11930     }
11931     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11932 }
11933 
CreateBlock(VkDeviceSize blockSize,size_t * pNewBlockIndex)11934 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
11935 {
11936     VkMemoryAllocateInfo allocInfo = {};
11937     allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
11938     allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
11939     allocInfo.allocationSize = blockSize;
11940     VkDeviceMemory mem = VK_NULL_HANDLE;
11941     VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
11942     if(res < 0)
11943     {
11944         return res;
11945     }
11946 
11947     // New VkDeviceMemory successfully created.
11948 
11949     // Create new Allocation for it.
11950     VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
11951     pBlock->Init(
11952         m_hAllocator,
11953         m_MemoryTypeIndex,
11954         mem,
11955         allocInfo.allocationSize,
11956         m_NextBlockId++,
11957         m_Algorithm);
11958 
11959     m_Blocks.push_back(pBlock);
11960     if(pNewBlockIndex != VMA_NULL)
11961     {
11962         *pNewBlockIndex = m_Blocks.size() - 1;
11963     }
11964 
11965     return VK_SUCCESS;
11966 }
11967 
ApplyDefragmentationMovesCpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,const VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves)11968 void VmaBlockVector::ApplyDefragmentationMovesCpu(
11969     class VmaBlockVectorDefragmentationContext* pDefragCtx,
11970     const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
11971 {
11972     const size_t blockCount = m_Blocks.size();
11973     const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
11974 
11975     enum BLOCK_FLAG
11976     {
11977         BLOCK_FLAG_USED = 0x00000001,
11978         BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
11979     };
11980 
11981     struct BlockInfo
11982     {
11983         uint32_t flags;
11984         void* pMappedData;
11985     };
11986     VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
11987         blockInfo(blockCount, VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
11988     memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
11989 
11990     // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
11991     const size_t moveCount = moves.size();
11992     for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
11993     {
11994         const VmaDefragmentationMove& move = moves[moveIndex];
11995         blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
11996         blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
11997     }
11998 
11999     VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
12000 
12001     // Go over all blocks. Get mapped pointer or map if necessary.
12002     for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
12003     {
12004         BlockInfo& currBlockInfo = blockInfo[blockIndex];
12005         VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12006         if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
12007         {
12008             currBlockInfo.pMappedData = pBlock->GetMappedData();
12009             // It is not originally mapped - map it.
12010             if(currBlockInfo.pMappedData == VMA_NULL)
12011             {
12012                 pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
12013                 if(pDefragCtx->res == VK_SUCCESS)
12014                 {
12015                     currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
12016                 }
12017             }
12018         }
12019     }
12020 
12021     // Go over all moves. Do actual data transfer.
12022     if(pDefragCtx->res == VK_SUCCESS)
12023     {
12024         const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
12025         VkMappedMemoryRange memRange = {};
12026         memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
12027 
12028         for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12029         {
12030             const VmaDefragmentationMove& move = moves[moveIndex];
12031 
12032             const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
12033             const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
12034 
12035             VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
12036 
12037             // Invalidate source.
12038             if(isNonCoherent)
12039             {
12040                 VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
12041                 memRange.memory = pSrcBlock->GetDeviceMemory();
12042                 memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
12043                 memRange.size = VMA_MIN(
12044                     VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
12045                     pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
12046                 (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
12047             }
12048 
12049             // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
12050             memmove(
12051                 reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
12052                 reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
12053                 static_cast<size_t>(move.size));
12054 
12055             if(IsCorruptionDetectionEnabled())
12056             {
12057                 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
12058                 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
12059             }
12060 
12061             // Flush destination.
12062             if(isNonCoherent)
12063             {
12064                 VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
12065                 memRange.memory = pDstBlock->GetDeviceMemory();
12066                 memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
12067                 memRange.size = VMA_MIN(
12068                     VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
12069                     pDstBlock->m_pMetadata->GetSize() - memRange.offset);
12070                 (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
12071             }
12072         }
12073     }
12074 
12075     // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
12076     // Regardless of pCtx->res == VK_SUCCESS.
12077     for(size_t blockIndex = blockCount; blockIndex--; )
12078     {
12079         const BlockInfo& currBlockInfo = blockInfo[blockIndex];
12080         if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
12081         {
12082             VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12083             pBlock->Unmap(m_hAllocator, 1);
12084         }
12085     }
12086 }
12087 
ApplyDefragmentationMovesGpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,const VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkCommandBuffer commandBuffer)12088 void VmaBlockVector::ApplyDefragmentationMovesGpu(
12089     class VmaBlockVectorDefragmentationContext* pDefragCtx,
12090     const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12091     VkCommandBuffer commandBuffer)
12092 {
12093     const size_t blockCount = m_Blocks.size();
12094 
12095     pDefragCtx->blockContexts.resize(blockCount);
12096     for (size_t i = 0; i < blockCount; ++i)
12097         pDefragCtx->blockContexts[i] = VmaBlockDefragmentationContext();
12098 
12099     // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
12100     const size_t moveCount = moves.size();
12101     for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12102     {
12103         const VmaDefragmentationMove& move = moves[moveIndex];
12104         pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
12105         pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
12106     }
12107 
12108     VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
12109 
12110     // Go over all blocks. Create and bind buffer for whole block if necessary.
12111     {
12112         VkBufferCreateInfo bufCreateInfo = {};
12113         bufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
12114         bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
12115             VK_BUFFER_USAGE_TRANSFER_DST_BIT;
12116 
12117         for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
12118         {
12119             VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
12120             VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12121             if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
12122             {
12123                 bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
12124                 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
12125                     m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
12126                 if(pDefragCtx->res == VK_SUCCESS)
12127                 {
12128                     pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
12129                         m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
12130                 }
12131             }
12132         }
12133     }
12134 
12135     // Go over all moves. Post data transfer commands to command buffer.
12136     if(pDefragCtx->res == VK_SUCCESS)
12137     {
12138         /*const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
12139         VkMappedMemoryRange memRange = {};
12140         memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;*/
12141 
12142         for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12143         {
12144             const VmaDefragmentationMove& move = moves[moveIndex];
12145 
12146             const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
12147             const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
12148 
12149             VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
12150 
12151             VkBufferCopy region = {
12152                 move.srcOffset,
12153                 move.dstOffset,
12154                 move.size };
12155             (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
12156                 commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, &region);
12157         }
12158     }
12159 
12160     // Save buffers to defrag context for later destruction.
12161     if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
12162     {
12163         pDefragCtx->res = VK_NOT_READY;
12164     }
12165 }
12166 
FreeEmptyBlocks(VmaDefragmentationStats * pDefragmentationStats)12167 void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
12168 {
12169     m_HasEmptyBlock = false;
12170     for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12171     {
12172         VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12173         if(pBlock->m_pMetadata->IsEmpty())
12174         {
12175             if(m_Blocks.size() > m_MinBlockCount)
12176             {
12177                 if(pDefragmentationStats != VMA_NULL)
12178                 {
12179                     ++pDefragmentationStats->deviceMemoryBlocksFreed;
12180                     pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
12181                 }
12182 
12183                 VmaVectorRemove(m_Blocks, blockIndex);
12184                 pBlock->Destroy(m_hAllocator);
12185                 vma_delete(m_hAllocator, pBlock);
12186             }
12187             else
12188             {
12189                 m_HasEmptyBlock = true;
12190             }
12191         }
12192     }
12193 }
12194 
12195 #if VMA_STATS_STRING_ENABLED
12196 
PrintDetailedMap(class VmaJsonWriter & json)12197 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
12198 {
12199     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12200 
12201     json.BeginObject();
12202 
12203     if(m_IsCustomPool)
12204     {
12205         json.WriteString("MemoryTypeIndex");
12206         json.WriteNumber(m_MemoryTypeIndex);
12207 
12208         json.WriteString("BlockSize");
12209         json.WriteNumber(m_PreferredBlockSize);
12210 
12211         json.WriteString("BlockCount");
12212         json.BeginObject(true);
12213         if(m_MinBlockCount > 0)
12214         {
12215             json.WriteString("Min");
12216             json.WriteNumber((uint64_t)m_MinBlockCount);
12217         }
12218         if(m_MaxBlockCount < SIZE_MAX)
12219         {
12220             json.WriteString("Max");
12221             json.WriteNumber((uint64_t)m_MaxBlockCount);
12222         }
12223         json.WriteString("Cur");
12224         json.WriteNumber((uint64_t)m_Blocks.size());
12225         json.EndObject();
12226 
12227         if(m_FrameInUseCount > 0)
12228         {
12229             json.WriteString("FrameInUseCount");
12230             json.WriteNumber(m_FrameInUseCount);
12231         }
12232 
12233         if(m_Algorithm != 0)
12234         {
12235             json.WriteString("Algorithm");
12236             json.WriteString(VmaAlgorithmToStr(m_Algorithm));
12237         }
12238     }
12239     else
12240     {
12241         json.WriteString("PreferredBlockSize");
12242         json.WriteNumber(m_PreferredBlockSize);
12243     }
12244 
12245     json.WriteString("Blocks");
12246     json.BeginObject();
12247     for(size_t i = 0; i < m_Blocks.size(); ++i)
12248     {
12249         json.BeginString();
12250         json.ContinueString(m_Blocks[i]->GetId());
12251         json.EndString();
12252 
12253         m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
12254     }
12255     json.EndObject();
12256 
12257     json.EndObject();
12258 }
12259 
12260 #endif // #if VMA_STATS_STRING_ENABLED
12261 
Defragment(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats,VkDeviceSize & maxCpuBytesToMove,uint32_t & maxCpuAllocationsToMove,VkDeviceSize & maxGpuBytesToMove,uint32_t & maxGpuAllocationsToMove,VkCommandBuffer commandBuffer)12262 void VmaBlockVector::Defragment(
12263     class VmaBlockVectorDefragmentationContext* pCtx,
12264     VmaDefragmentationStats* pStats,
12265     VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
12266     VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
12267     VkCommandBuffer commandBuffer)
12268 {
12269     pCtx->res = VK_SUCCESS;
12270 
12271     const VkMemoryPropertyFlags memPropFlags =
12272         m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
12273     const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
12274     const bool isHostCoherent = (memPropFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
12275 
12276     const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
12277         isHostVisible;
12278     const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
12279         (VMA_DEBUG_DETECT_CORRUPTION == 0 || !(isHostVisible && isHostCoherent));
12280 
12281     // There are options to defragment this memory type.
12282     if(canDefragmentOnCpu || canDefragmentOnGpu)
12283     {
12284         bool defragmentOnGpu;
12285         // There is only one option to defragment this memory type.
12286         if(canDefragmentOnGpu != canDefragmentOnCpu)
12287         {
12288             defragmentOnGpu = canDefragmentOnGpu;
12289         }
12290         // Both options are available: Heuristics to choose the best one.
12291         else
12292         {
12293             defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
12294                 m_hAllocator->IsIntegratedGpu();
12295         }
12296 
12297         bool overlappingMoveSupported = !defragmentOnGpu;
12298 
12299         if(m_hAllocator->m_UseMutex)
12300         {
12301             m_Mutex.LockWrite();
12302             pCtx->mutexLocked = true;
12303         }
12304 
12305         pCtx->Begin(overlappingMoveSupported);
12306 
12307         // Defragment.
12308 
12309         const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
12310         const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
12311         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > moves =
12312             VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >(VmaStlAllocator<VmaDefragmentationMove>(m_hAllocator->GetAllocationCallbacks()));
12313         pCtx->res = pCtx->GetAlgorithm()->Defragment(moves, maxBytesToMove, maxAllocationsToMove);
12314 
12315         // Accumulate statistics.
12316         if(pStats != VMA_NULL)
12317         {
12318             const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
12319             const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
12320             pStats->bytesMoved += bytesMoved;
12321             pStats->allocationsMoved += allocationsMoved;
12322             VMA_ASSERT(bytesMoved <= maxBytesToMove);
12323             VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
12324             if(defragmentOnGpu)
12325             {
12326                 maxGpuBytesToMove -= bytesMoved;
12327                 maxGpuAllocationsToMove -= allocationsMoved;
12328             }
12329             else
12330             {
12331                 maxCpuBytesToMove -= bytesMoved;
12332                 maxCpuAllocationsToMove -= allocationsMoved;
12333             }
12334         }
12335 
12336         if(pCtx->res >= VK_SUCCESS)
12337         {
12338             if(defragmentOnGpu)
12339             {
12340                 ApplyDefragmentationMovesGpu(pCtx, moves, commandBuffer);
12341             }
12342             else
12343             {
12344                 ApplyDefragmentationMovesCpu(pCtx, moves);
12345             }
12346         }
12347     }
12348 }
12349 
DefragmentationEnd(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats)12350 void VmaBlockVector::DefragmentationEnd(
12351     class VmaBlockVectorDefragmentationContext* pCtx,
12352     VmaDefragmentationStats* pStats)
12353 {
12354     // Destroy buffers.
12355     for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--; )
12356     {
12357         VmaBlockDefragmentationContext& blockCtx = pCtx->blockContexts[blockIndex];
12358         if(blockCtx.hBuffer)
12359         {
12360             (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(
12361                 m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
12362         }
12363     }
12364 
12365     if(pCtx->res >= VK_SUCCESS)
12366     {
12367         FreeEmptyBlocks(pStats);
12368     }
12369 
12370     if(pCtx->mutexLocked)
12371     {
12372         VMA_ASSERT(m_hAllocator->m_UseMutex);
12373         m_Mutex.UnlockWrite();
12374     }
12375 }
12376 
CalcAllocationCount()12377 size_t VmaBlockVector::CalcAllocationCount() const
12378 {
12379     size_t result = 0;
12380     for(size_t i = 0; i < m_Blocks.size(); ++i)
12381     {
12382         result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
12383     }
12384     return result;
12385 }
12386 
IsBufferImageGranularityConflictPossible()12387 bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
12388 {
12389     if(m_BufferImageGranularity == 1)
12390     {
12391         return false;
12392     }
12393     VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
12394     for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
12395     {
12396         VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
12397         VMA_ASSERT(m_Algorithm == 0);
12398         VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
12399         if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
12400         {
12401             return true;
12402         }
12403     }
12404     return false;
12405 }
12406 
MakePoolAllocationsLost(uint32_t currentFrameIndex,size_t * pLostAllocationCount)12407 void VmaBlockVector::MakePoolAllocationsLost(
12408     uint32_t currentFrameIndex,
12409     size_t* pLostAllocationCount)
12410 {
12411     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12412     size_t lostAllocationCount = 0;
12413     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12414     {
12415         VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12416         VMA_ASSERT(pBlock);
12417         lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
12418     }
12419     if(pLostAllocationCount != VMA_NULL)
12420     {
12421         *pLostAllocationCount = lostAllocationCount;
12422     }
12423 }
12424 
CheckCorruption()12425 VkResult VmaBlockVector::CheckCorruption()
12426 {
12427     if(!IsCorruptionDetectionEnabled())
12428     {
12429         return VK_ERROR_FEATURE_NOT_PRESENT;
12430     }
12431 
12432     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12433     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12434     {
12435         VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12436         VMA_ASSERT(pBlock);
12437         VkResult res = pBlock->CheckCorruption(m_hAllocator);
12438         if(res != VK_SUCCESS)
12439         {
12440             return res;
12441         }
12442     }
12443     return VK_SUCCESS;
12444 }
12445 
AddStats(VmaStats * pStats)12446 void VmaBlockVector::AddStats(VmaStats* pStats)
12447 {
12448     const uint32_t memTypeIndex = m_MemoryTypeIndex;
12449     const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
12450 
12451     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12452 
12453     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12454     {
12455         const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12456         VMA_ASSERT(pBlock);
12457         VMA_HEAVY_ASSERT(pBlock->Validate());
12458         VmaStatInfo allocationStatInfo;
12459         pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
12460         VmaAddStatInfo(pStats->total, allocationStatInfo);
12461         VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
12462         VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
12463     }
12464 }
12465 
12466 ////////////////////////////////////////////////////////////////////////////////
12467 // VmaDefragmentationAlgorithm_Generic members definition
12468 
VmaDefragmentationAlgorithm_Generic(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool)12469 VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
12470     VmaAllocator hAllocator,
12471     VmaBlockVector* pBlockVector,
12472     uint32_t currentFrameIndex,
12473     bool /*overlappingMoveSupported*/) :
12474     VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
12475     m_AllocationCount(0),
12476     m_AllAllocations(false),
12477     m_BytesMoved(0),
12478     m_AllocationsMoved(0),
12479     m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
12480 {
12481     // Create block info for each block.
12482     const size_t blockCount = m_pBlockVector->m_Blocks.size();
12483     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12484     {
12485         BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
12486         pBlockInfo->m_OriginalBlockIndex = blockIndex;
12487         pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
12488         m_Blocks.push_back(pBlockInfo);
12489     }
12490 
12491     // Sort them by m_pBlock pointer value.
12492     VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
12493 }
12494 
~VmaDefragmentationAlgorithm_Generic()12495 VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
12496 {
12497     for(size_t i = m_Blocks.size(); i--; )
12498     {
12499         vma_delete(m_hAllocator, m_Blocks[i]);
12500     }
12501 }
12502 
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)12503 void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
12504 {
12505     // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
12506     if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
12507     {
12508         VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
12509         BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
12510         if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
12511         {
12512             AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
12513             (*it)->m_Allocations.push_back(allocInfo);
12514         }
12515         else
12516         {
12517             VMA_ASSERT(0);
12518         }
12519 
12520         ++m_AllocationCount;
12521     }
12522 }
12523 
DefragmentRound(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove)12524 VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
12525     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12526     VkDeviceSize maxBytesToMove,
12527     uint32_t maxAllocationsToMove)
12528 {
12529     if(m_Blocks.empty())
12530     {
12531         return VK_SUCCESS;
12532     }
12533 
12534     // This is a choice based on research.
12535     // Option 1:
12536     uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
12537     // Option 2:
12538     //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
12539     // Option 3:
12540     //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
12541 
12542     size_t srcBlockMinIndex = 0;
12543     // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
12544     /*
12545     if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
12546     {
12547         const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
12548         if(blocksWithNonMovableCount > 0)
12549         {
12550             srcBlockMinIndex = blocksWithNonMovableCount - 1;
12551         }
12552     }
12553     */
12554 
12555     size_t srcBlockIndex = m_Blocks.size() - 1;
12556     size_t srcAllocIndex = SIZE_MAX;
12557     for(;;)
12558     {
12559         // 1. Find next allocation to move.
12560         // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
12561         // 1.2. Then start from last to first m_Allocations.
12562         while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
12563         {
12564             if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
12565             {
12566                 // Finished: no more allocations to process.
12567                 if(srcBlockIndex == srcBlockMinIndex)
12568                 {
12569                     return VK_SUCCESS;
12570                 }
12571                 else
12572                 {
12573                     --srcBlockIndex;
12574                     srcAllocIndex = SIZE_MAX;
12575                 }
12576             }
12577             else
12578             {
12579                 srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
12580             }
12581         }
12582 
12583         BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
12584         AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
12585 
12586         const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
12587         const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
12588         const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
12589         const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
12590 
12591         // 2. Try to find new place for this allocation in preceding or current block.
12592         for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
12593         {
12594             BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
12595             VmaAllocationRequest dstAllocRequest;
12596             if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
12597                 m_CurrentFrameIndex,
12598                 m_pBlockVector->GetFrameInUseCount(),
12599                 m_pBlockVector->GetBufferImageGranularity(),
12600                 size,
12601                 alignment,
12602                 false, // upperAddress
12603                 suballocType,
12604                 false, // canMakeOtherLost
12605                 strategy,
12606                 &dstAllocRequest) &&
12607             MoveMakesSense(
12608                 dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
12609             {
12610                 VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
12611 
12612                 // Reached limit on number of allocations or bytes to move.
12613                 if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
12614                     (m_BytesMoved + size > maxBytesToMove))
12615                 {
12616                     return VK_SUCCESS;
12617                 }
12618 
12619                 VmaDefragmentationMove move;
12620                 move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
12621                 move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
12622                 move.srcOffset = srcOffset;
12623                 move.dstOffset = dstAllocRequest.offset;
12624                 move.size = size;
12625                 moves.push_back(move);
12626 
12627                 pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
12628                     dstAllocRequest,
12629                     suballocType,
12630                     size,
12631                     false, // upperAddress
12632                     allocInfo.m_hAllocation);
12633                 pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
12634 
12635                 allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
12636 
12637                 if(allocInfo.m_pChanged != VMA_NULL)
12638                 {
12639                     *allocInfo.m_pChanged = VK_TRUE;
12640                 }
12641 
12642                 ++m_AllocationsMoved;
12643                 m_BytesMoved += size;
12644 
12645                 VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
12646 
12647                 break;
12648             }
12649         }
12650 
12651         // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
12652 
12653         if(srcAllocIndex > 0)
12654         {
12655             --srcAllocIndex;
12656         }
12657         else
12658         {
12659             if(srcBlockIndex > 0)
12660             {
12661                 --srcBlockIndex;
12662                 srcAllocIndex = SIZE_MAX;
12663             }
12664             else
12665             {
12666                 return VK_SUCCESS;
12667             }
12668         }
12669     }
12670 }
12671 
CalcBlocksWithNonMovableCount()12672 size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
12673 {
12674     size_t result = 0;
12675     for(size_t i = 0; i < m_Blocks.size(); ++i)
12676     {
12677         if(m_Blocks[i]->m_HasNonMovableAllocations)
12678         {
12679             ++result;
12680         }
12681     }
12682     return result;
12683 }
12684 
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove)12685 VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
12686     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12687     VkDeviceSize maxBytesToMove,
12688     uint32_t maxAllocationsToMove)
12689 {
12690     if(!m_AllAllocations && m_AllocationCount == 0)
12691     {
12692         return VK_SUCCESS;
12693     }
12694 
12695     const size_t blockCount = m_Blocks.size();
12696     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12697     {
12698         BlockInfo* pBlockInfo = m_Blocks[blockIndex];
12699 
12700         if(m_AllAllocations)
12701         {
12702             VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
12703             for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
12704                 it != pMetadata->m_Suballocations.end();
12705                 ++it)
12706             {
12707                 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
12708                 {
12709                     AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
12710                     pBlockInfo->m_Allocations.push_back(allocInfo);
12711                 }
12712             }
12713         }
12714 
12715         pBlockInfo->CalcHasNonMovableAllocations();
12716 
12717         // This is a choice based on research.
12718         // Option 1:
12719         pBlockInfo->SortAllocationsByOffsetDescending();
12720         // Option 2:
12721         //pBlockInfo->SortAllocationsBySizeDescending();
12722     }
12723 
12724     // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
12725     VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
12726 
12727     // This is a choice based on research.
12728     const uint32_t roundCount = 2;
12729 
12730     // Execute defragmentation rounds (the main part).
12731     VkResult result = VK_SUCCESS;
12732     for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
12733     {
12734         result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove);
12735     }
12736 
12737     return result;
12738 }
12739 
MoveMakesSense(size_t dstBlockIndex,VkDeviceSize dstOffset,size_t srcBlockIndex,VkDeviceSize srcOffset)12740 bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
12741         size_t dstBlockIndex, VkDeviceSize dstOffset,
12742         size_t srcBlockIndex, VkDeviceSize srcOffset)
12743 {
12744     if(dstBlockIndex < srcBlockIndex)
12745     {
12746         return true;
12747     }
12748     if(dstBlockIndex > srcBlockIndex)
12749     {
12750         return false;
12751     }
12752     if(dstOffset < srcOffset)
12753     {
12754         return true;
12755     }
12756     return false;
12757 }
12758 
12759 ////////////////////////////////////////////////////////////////////////////////
12760 // VmaDefragmentationAlgorithm_Fast
12761 
VmaDefragmentationAlgorithm_Fast(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)12762 VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
12763     VmaAllocator hAllocator,
12764     VmaBlockVector* pBlockVector,
12765     uint32_t currentFrameIndex,
12766     bool overlappingMoveSupported) :
12767     VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
12768     m_OverlappingMoveSupported(overlappingMoveSupported),
12769     m_AllocationCount(0),
12770     m_AllAllocations(false),
12771     m_BytesMoved(0),
12772     m_AllocationsMoved(0),
12773     m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
12774 {
12775     VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
12776 
12777 }
12778 
~VmaDefragmentationAlgorithm_Fast()12779 VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
12780 {
12781 }
12782 
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove)12783 VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
12784     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12785     VkDeviceSize maxBytesToMove,
12786     uint32_t maxAllocationsToMove)
12787 {
12788     VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
12789 
12790     const size_t blockCount = m_pBlockVector->GetBlockCount();
12791     if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
12792     {
12793         return VK_SUCCESS;
12794     }
12795 
12796     PreprocessMetadata();
12797 
12798     // Sort blocks in order from most destination.
12799 
12800     m_BlockInfos.resize(blockCount);
12801     for(size_t i = 0; i < blockCount; ++i)
12802     {
12803         m_BlockInfos[i].origBlockIndex = i;
12804     }
12805 
12806     VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
12807         return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
12808             m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
12809     });
12810 
12811     // THE MAIN ALGORITHM
12812 
12813     FreeSpaceDatabase freeSpaceDb;
12814 
12815     size_t dstBlockInfoIndex = 0;
12816     size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
12817     VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
12818     VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
12819     VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
12820     VkDeviceSize dstOffset = 0;
12821 
12822     bool end = false;
12823     for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
12824     {
12825         const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
12826         VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
12827         VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
12828         for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
12829             !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
12830         {
12831             VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
12832             const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
12833             const VkDeviceSize srcAllocSize = srcSuballocIt->size;
12834             if(m_AllocationsMoved == maxAllocationsToMove ||
12835                 m_BytesMoved + srcAllocSize > maxBytesToMove)
12836             {
12837                 end = true;
12838                 break;
12839             }
12840             const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
12841 
12842             // Try to place it in one of free spaces from the database.
12843             size_t freeSpaceInfoIndex;
12844             VkDeviceSize dstAllocOffset;
12845             if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
12846                 freeSpaceInfoIndex, dstAllocOffset))
12847             {
12848                 size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
12849                 VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
12850                 VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
12851                 /*VkDeviceSize freeSpaceBlockSize = pFreeSpaceMetadata->GetSize();*/
12852 
12853                 // Same block
12854                 if(freeSpaceInfoIndex == srcBlockInfoIndex)
12855                 {
12856                     VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
12857 
12858                     // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
12859 
12860                     VmaSuballocation suballoc = *srcSuballocIt;
12861                     suballoc.offset = dstAllocOffset;
12862                     suballoc.hAllocation->ChangeOffset(dstAllocOffset);
12863                     m_BytesMoved += srcAllocSize;
12864                     ++m_AllocationsMoved;
12865 
12866                     VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12867                     ++nextSuballocIt;
12868                     pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
12869                     srcSuballocIt = nextSuballocIt;
12870 
12871                     InsertSuballoc(pFreeSpaceMetadata, suballoc);
12872 
12873                     VmaDefragmentationMove move = {
12874                         srcOrigBlockIndex, freeSpaceOrigBlockIndex,
12875                         srcAllocOffset, dstAllocOffset,
12876                         srcAllocSize };
12877                     moves.push_back(move);
12878                 }
12879                 // Different block
12880                 else
12881                 {
12882                     // MOVE OPTION 2: Move the allocation to a different block.
12883 
12884                     VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
12885 
12886                     VmaSuballocation suballoc = *srcSuballocIt;
12887                     suballoc.offset = dstAllocOffset;
12888                     suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
12889                     m_BytesMoved += srcAllocSize;
12890                     ++m_AllocationsMoved;
12891 
12892                     VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12893                     ++nextSuballocIt;
12894                     pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
12895                     srcSuballocIt = nextSuballocIt;
12896 
12897                     InsertSuballoc(pFreeSpaceMetadata, suballoc);
12898 
12899                     VmaDefragmentationMove move = {
12900                         srcOrigBlockIndex, freeSpaceOrigBlockIndex,
12901                         srcAllocOffset, dstAllocOffset,
12902                         srcAllocSize };
12903                     moves.push_back(move);
12904                 }
12905             }
12906             else
12907             {
12908                 dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
12909 
12910                 // If the allocation doesn't fit before the end of dstBlock, forward to next block.
12911                 while(dstBlockInfoIndex < srcBlockInfoIndex &&
12912                     dstAllocOffset + srcAllocSize > dstBlockSize)
12913                 {
12914                     // But before that, register remaining free space at the end of dst block.
12915                     freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
12916 
12917                     ++dstBlockInfoIndex;
12918                     dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
12919                     pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
12920                     pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
12921                     dstBlockSize = pDstMetadata->GetSize();
12922                     dstOffset = 0;
12923                     dstAllocOffset = 0;
12924                 }
12925 
12926                 // Same block
12927                 if(dstBlockInfoIndex == srcBlockInfoIndex)
12928                 {
12929                     VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
12930 
12931                     const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
12932 
12933                     bool skipOver = overlap;
12934                     if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
12935                     {
12936                         // If destination and source place overlap, skip if it would move it
12937                         // by only < 1/64 of its size.
12938                         skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
12939                     }
12940 
12941                     if(skipOver)
12942                     {
12943                         freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
12944 
12945                         dstOffset = srcAllocOffset + srcAllocSize;
12946                         ++srcSuballocIt;
12947                     }
12948                     // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
12949                     else
12950                     {
12951                         srcSuballocIt->offset = dstAllocOffset;
12952                         srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);
12953                         dstOffset = dstAllocOffset + srcAllocSize;
12954                         m_BytesMoved += srcAllocSize;
12955                         ++m_AllocationsMoved;
12956                         ++srcSuballocIt;
12957                         VmaDefragmentationMove move = {
12958                             srcOrigBlockIndex, dstOrigBlockIndex,
12959                             srcAllocOffset, dstAllocOffset,
12960                             srcAllocSize };
12961                         moves.push_back(move);
12962                     }
12963                 }
12964                 // Different block
12965                 else
12966                 {
12967                     // MOVE OPTION 2: Move the allocation to a different block.
12968 
12969                     VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
12970                     VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
12971 
12972                     VmaSuballocation suballoc = *srcSuballocIt;
12973                     suballoc.offset = dstAllocOffset;
12974                     suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
12975                     dstOffset = dstAllocOffset + srcAllocSize;
12976                     m_BytesMoved += srcAllocSize;
12977                     ++m_AllocationsMoved;
12978 
12979                     VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12980                     ++nextSuballocIt;
12981                     pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
12982                     srcSuballocIt = nextSuballocIt;
12983 
12984                     pDstMetadata->m_Suballocations.push_back(suballoc);
12985 
12986                     VmaDefragmentationMove move = {
12987                         srcOrigBlockIndex, dstOrigBlockIndex,
12988                         srcAllocOffset, dstAllocOffset,
12989                         srcAllocSize };
12990                     moves.push_back(move);
12991                 }
12992             }
12993         }
12994     }
12995 
12996     m_BlockInfos.clear();
12997 
12998     PostprocessMetadata();
12999 
13000     return VK_SUCCESS;
13001 }
13002 
PreprocessMetadata()13003 void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
13004 {
13005     const size_t blockCount = m_pBlockVector->GetBlockCount();
13006     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13007     {
13008         VmaBlockMetadata_Generic* const pMetadata =
13009             (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
13010         pMetadata->m_FreeCount = 0;
13011         pMetadata->m_SumFreeSize = pMetadata->GetSize();
13012         pMetadata->m_FreeSuballocationsBySize.clear();
13013         for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
13014             it != pMetadata->m_Suballocations.end(); )
13015         {
13016             if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
13017             {
13018                 VmaSuballocationList::iterator nextIt = it;
13019                 ++nextIt;
13020                 pMetadata->m_Suballocations.erase(it);
13021                 it = nextIt;
13022             }
13023             else
13024             {
13025                 ++it;
13026             }
13027         }
13028     }
13029 }
13030 
PostprocessMetadata()13031 void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
13032 {
13033     const size_t blockCount = m_pBlockVector->GetBlockCount();
13034     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13035     {
13036         VmaBlockMetadata_Generic* const pMetadata =
13037             (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
13038         const VkDeviceSize blockSize = pMetadata->GetSize();
13039 
13040         // No allocations in this block - entire area is free.
13041         if(pMetadata->m_Suballocations.empty())
13042         {
13043             pMetadata->m_FreeCount = 1;
13044             //pMetadata->m_SumFreeSize is already set to blockSize.
13045             VmaSuballocation suballoc = {
13046                 0, // offset
13047                 blockSize, // size
13048                 VMA_NULL, // hAllocation
13049                 VMA_SUBALLOCATION_TYPE_FREE };
13050             pMetadata->m_Suballocations.push_back(suballoc);
13051             pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
13052         }
13053         // There are some allocations in this block.
13054         else
13055         {
13056             VkDeviceSize offset = 0;
13057             VmaSuballocationList::iterator it;
13058             for(it = pMetadata->m_Suballocations.begin();
13059                 it != pMetadata->m_Suballocations.end();
13060                 ++it)
13061             {
13062                 VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
13063                 VMA_ASSERT(it->offset >= offset);
13064 
13065                 // Need to insert preceding free space.
13066                 if(it->offset > offset)
13067                 {
13068                     ++pMetadata->m_FreeCount;
13069                     const VkDeviceSize freeSize = it->offset - offset;
13070                     VmaSuballocation suballoc = {
13071                         offset, // offset
13072                         freeSize, // size
13073                         VMA_NULL, // hAllocation
13074                         VMA_SUBALLOCATION_TYPE_FREE };
13075                     VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
13076                     if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
13077                     {
13078                         pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
13079                     }
13080                 }
13081 
13082                 pMetadata->m_SumFreeSize -= it->size;
13083                 offset = it->offset + it->size;
13084             }
13085 
13086             // Need to insert trailing free space.
13087             if(offset < blockSize)
13088             {
13089                 ++pMetadata->m_FreeCount;
13090                 const VkDeviceSize freeSize = blockSize - offset;
13091                 VmaSuballocation suballoc = {
13092                     offset, // offset
13093                     freeSize, // size
13094                     VMA_NULL, // hAllocation
13095                     VMA_SUBALLOCATION_TYPE_FREE };
13096                 VMA_ASSERT(it == pMetadata->m_Suballocations.end());
13097                 VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
13098                 if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
13099                 {
13100                     pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
13101                 }
13102             }
13103 
13104             VMA_SORT(
13105                 pMetadata->m_FreeSuballocationsBySize.begin(),
13106                 pMetadata->m_FreeSuballocationsBySize.end(),
13107                 VmaSuballocationItemSizeLess());
13108         }
13109 
13110         VMA_HEAVY_ASSERT(pMetadata->Validate());
13111     }
13112 }
13113 
InsertSuballoc(VmaBlockMetadata_Generic * pMetadata,const VmaSuballocation & suballoc)13114 void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
13115 {
13116     // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
13117     VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
13118     while(it != pMetadata->m_Suballocations.end())
13119     {
13120         if(it->offset < suballoc.offset)
13121         {
13122             ++it;
13123         }
13124     }
13125     pMetadata->m_Suballocations.insert(it, suballoc);
13126 }
13127 
13128 ////////////////////////////////////////////////////////////////////////////////
13129 // VmaBlockVectorDefragmentationContext
13130 
VmaBlockVectorDefragmentationContext(VmaAllocator hAllocator,VmaPool hCustomPool,VmaBlockVector * pBlockVector,uint32_t currFrameIndex,uint32_t)13131 VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
13132     VmaAllocator hAllocator,
13133     VmaPool hCustomPool,
13134     VmaBlockVector* pBlockVector,
13135     uint32_t currFrameIndex,
13136     uint32_t /*algorithmFlags*/) :
13137     res(VK_SUCCESS),
13138     mutexLocked(false),
13139     blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
13140     m_hAllocator(hAllocator),
13141     m_hCustomPool(hCustomPool),
13142     m_pBlockVector(pBlockVector),
13143     m_CurrFrameIndex(currFrameIndex),
13144     /*m_AlgorithmFlags(algorithmFlags),*/
13145     m_pAlgorithm(VMA_NULL),
13146     m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
13147     m_AllAllocations(false)
13148 {
13149 }
13150 
~VmaBlockVectorDefragmentationContext()13151 VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
13152 {
13153     vma_delete(m_hAllocator, m_pAlgorithm);
13154 }
13155 
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)13156 void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
13157 {
13158     AllocInfo info = { hAlloc, pChanged };
13159     m_Allocations.push_back(info);
13160 }
13161 
Begin(bool overlappingMoveSupported)13162 void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported)
13163 {
13164     const bool allAllocations = m_AllAllocations ||
13165         m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
13166 
13167     /********************************
13168     HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
13169     ********************************/
13170 
13171     /*
13172     Fast algorithm is supported only when certain criteria are met:
13173     - VMA_DEBUG_MARGIN is 0.
13174     - All allocations in this block vector are moveable.
13175     - There is no possibility of image/buffer granularity conflict.
13176     */
13177     if(VMA_DEBUG_MARGIN == 0 &&
13178         allAllocations &&
13179         !m_pBlockVector->IsBufferImageGranularityConflictPossible())
13180     {
13181         m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
13182             m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
13183     }
13184     else
13185     {
13186         m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
13187             m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
13188     }
13189 
13190     if(allAllocations)
13191     {
13192         m_pAlgorithm->AddAll();
13193     }
13194     else
13195     {
13196         for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
13197         {
13198             m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
13199         }
13200     }
13201 }
13202 
13203 ////////////////////////////////////////////////////////////////////////////////
13204 // VmaDefragmentationContext
13205 
VmaDefragmentationContext_T(VmaAllocator hAllocator,uint32_t currFrameIndex,uint32_t flags,VmaDefragmentationStats * pStats)13206 VmaDefragmentationContext_T::VmaDefragmentationContext_T(
13207     VmaAllocator hAllocator,
13208     uint32_t currFrameIndex,
13209     uint32_t flags,
13210     VmaDefragmentationStats* pStats) :
13211     m_hAllocator(hAllocator),
13212     m_CurrFrameIndex(currFrameIndex),
13213     m_Flags(flags),
13214     m_pStats(pStats),
13215     m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
13216 {
13217     memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
13218 }
13219 
~VmaDefragmentationContext_T()13220 VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
13221 {
13222     for(size_t i = m_CustomPoolContexts.size(); i--; )
13223     {
13224         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
13225         pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_pStats);
13226         vma_delete(m_hAllocator, pBlockVectorCtx);
13227     }
13228     for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
13229     {
13230         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
13231         if(pBlockVectorCtx)
13232         {
13233             pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_pStats);
13234             vma_delete(m_hAllocator, pBlockVectorCtx);
13235         }
13236     }
13237 }
13238 
AddPools(uint32_t poolCount,VmaPool * pPools)13239 void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, VmaPool* pPools)
13240 {
13241     for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
13242     {
13243         VmaPool pool = pPools[poolIndex];
13244         VMA_ASSERT(pool);
13245         // Pools with algorithm other than default are not defragmented.
13246         if(pool->m_BlockVector.GetAlgorithm() == 0)
13247         {
13248             VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
13249 
13250             for(size_t i = m_CustomPoolContexts.size(); i--; )
13251             {
13252                 if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
13253                 {
13254                     pBlockVectorDefragCtx = m_CustomPoolContexts[i];
13255                     break;
13256                 }
13257             }
13258 
13259             if(!pBlockVectorDefragCtx)
13260             {
13261                 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13262                     m_hAllocator,
13263                     pool,
13264                     &pool->m_BlockVector,
13265                     m_CurrFrameIndex,
13266                     m_Flags);
13267                 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
13268             }
13269 
13270             pBlockVectorDefragCtx->AddAll();
13271         }
13272     }
13273 }
13274 
AddAllocations(uint32_t allocationCount,VmaAllocation * pAllocations,VkBool32 * pAllocationsChanged)13275 void VmaDefragmentationContext_T::AddAllocations(
13276     uint32_t allocationCount,
13277     VmaAllocation* pAllocations,
13278     VkBool32* pAllocationsChanged)
13279 {
13280     // Dispatch pAllocations among defragmentators. Create them when necessary.
13281     for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
13282     {
13283         const VmaAllocation hAlloc = pAllocations[allocIndex];
13284         VMA_ASSERT(hAlloc);
13285         // DedicatedAlloc cannot be defragmented.
13286         if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
13287             // Lost allocation cannot be defragmented.
13288             (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
13289         {
13290             VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
13291 
13292             const VmaPool hAllocPool = hAlloc->GetPool();
13293             // This allocation belongs to custom pool.
13294             if(hAllocPool != VK_NULL_HANDLE)
13295             {
13296                 // Pools with algorithm other than default are not defragmented.
13297                 if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
13298                 {
13299                     for(size_t i = m_CustomPoolContexts.size(); i--; )
13300                     {
13301                         if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
13302                         {
13303                             pBlockVectorDefragCtx = m_CustomPoolContexts[i];
13304                             break;
13305                         }
13306                     }
13307                     if(!pBlockVectorDefragCtx)
13308                     {
13309                         pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13310                             m_hAllocator,
13311                             hAllocPool,
13312                             &hAllocPool->m_BlockVector,
13313                             m_CurrFrameIndex,
13314                             m_Flags);
13315                         m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
13316                     }
13317                 }
13318             }
13319             // This allocation belongs to default pool.
13320             else
13321             {
13322                 const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
13323                 pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
13324                 if(!pBlockVectorDefragCtx)
13325                 {
13326                     pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13327                         m_hAllocator,
13328                         VMA_NULL, // hCustomPool
13329                         m_hAllocator->m_pBlockVectors[memTypeIndex],
13330                         m_CurrFrameIndex,
13331                         m_Flags);
13332                     m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
13333                 }
13334             }
13335 
13336             if(pBlockVectorDefragCtx)
13337             {
13338                 VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
13339                     &pAllocationsChanged[allocIndex] : VMA_NULL;
13340                 pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
13341             }
13342         }
13343     }
13344 }
13345 
Defragment(VkDeviceSize maxCpuBytesToMove,uint32_t maxCpuAllocationsToMove,VkDeviceSize maxGpuBytesToMove,uint32_t maxGpuAllocationsToMove,VkCommandBuffer commandBuffer,VmaDefragmentationStats * pStats)13346 VkResult VmaDefragmentationContext_T::Defragment(
13347     VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
13348     VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
13349     VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats)
13350 {
13351     if(pStats)
13352     {
13353         memset(pStats, 0, sizeof(VmaDefragmentationStats));
13354     }
13355 
13356     if(commandBuffer == VK_NULL_HANDLE)
13357     {
13358         maxGpuBytesToMove = 0;
13359         maxGpuAllocationsToMove = 0;
13360     }
13361 
13362     VkResult res = VK_SUCCESS;
13363 
13364     // Process default pools.
13365     for(uint32_t memTypeIndex = 0;
13366         memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
13367         ++memTypeIndex)
13368     {
13369         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
13370         if(pBlockVectorCtx)
13371         {
13372             VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
13373             pBlockVectorCtx->GetBlockVector()->Defragment(
13374                 pBlockVectorCtx,
13375                 pStats,
13376                 maxCpuBytesToMove, maxCpuAllocationsToMove,
13377                 maxGpuBytesToMove, maxGpuAllocationsToMove,
13378                 commandBuffer);
13379             if(pBlockVectorCtx->res != VK_SUCCESS)
13380             {
13381                 res = pBlockVectorCtx->res;
13382             }
13383         }
13384     }
13385 
13386     // Process custom pools.
13387     for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
13388         customCtxIndex < customCtxCount && res >= VK_SUCCESS;
13389         ++customCtxIndex)
13390     {
13391         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
13392         VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
13393         pBlockVectorCtx->GetBlockVector()->Defragment(
13394             pBlockVectorCtx,
13395             pStats,
13396             maxCpuBytesToMove, maxCpuAllocationsToMove,
13397             maxGpuBytesToMove, maxGpuAllocationsToMove,
13398             commandBuffer);
13399         if(pBlockVectorCtx->res != VK_SUCCESS)
13400         {
13401             res = pBlockVectorCtx->res;
13402         }
13403     }
13404 
13405     return res;
13406 }
13407 
13408 ////////////////////////////////////////////////////////////////////////////////
13409 // VmaRecorder
13410 
13411 #if VMA_RECORDING_ENABLED
13412 
VmaRecorder()13413 VmaRecorder::VmaRecorder() :
13414     m_UseMutex(true),
13415     m_Flags(0),
13416     m_File(VMA_NULL),
13417     m_Freq(INT64_MAX),
13418     m_StartCounter(INT64_MAX)
13419 {
13420 }
13421 
Init(const VmaRecordSettings & settings,bool useMutex)13422 VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
13423 {
13424     m_UseMutex = useMutex;
13425     m_Flags = settings.flags;
13426 
13427     QueryPerformanceFrequency((LARGE_INTEGER*)&m_Freq);
13428     QueryPerformanceCounter((LARGE_INTEGER*)&m_StartCounter);
13429 
13430     // Open file for writing.
13431     errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
13432     if(err != 0)
13433     {
13434         return VK_ERROR_INITIALIZATION_FAILED;
13435     }
13436 
13437     // Write header.
13438     fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
13439     fprintf(m_File, "%s\n", "1,5");
13440 
13441     return VK_SUCCESS;
13442 }
13443 
~VmaRecorder()13444 VmaRecorder::~VmaRecorder()
13445 {
13446     if(m_File != VMA_NULL)
13447     {
13448         fclose(m_File);
13449     }
13450 }
13451 
RecordCreateAllocator(uint32_t frameIndex)13452 void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
13453 {
13454     CallParams callParams;
13455     GetBasicParams(callParams);
13456 
13457     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13458     fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
13459     Flush();
13460 }
13461 
RecordDestroyAllocator(uint32_t frameIndex)13462 void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
13463 {
13464     CallParams callParams;
13465     GetBasicParams(callParams);
13466 
13467     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13468     fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
13469     Flush();
13470 }
13471 
RecordCreatePool(uint32_t frameIndex,const VmaPoolCreateInfo & createInfo,VmaPool pool)13472 void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
13473 {
13474     CallParams callParams;
13475     GetBasicParams(callParams);
13476 
13477     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13478     fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
13479         createInfo.memoryTypeIndex,
13480         createInfo.flags,
13481         createInfo.blockSize,
13482         (uint64_t)createInfo.minBlockCount,
13483         (uint64_t)createInfo.maxBlockCount,
13484         createInfo.frameInUseCount,
13485         pool);
13486     Flush();
13487 }
13488 
RecordDestroyPool(uint32_t frameIndex,VmaPool pool)13489 void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
13490 {
13491     CallParams callParams;
13492     GetBasicParams(callParams);
13493 
13494     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13495     fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
13496         pool);
13497     Flush();
13498 }
13499 
RecordAllocateMemory(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)13500 void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
13501         const VkMemoryRequirements& vkMemReq,
13502         const VmaAllocationCreateInfo& createInfo,
13503         VmaAllocation allocation)
13504 {
13505     CallParams callParams;
13506     GetBasicParams(callParams);
13507 
13508     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13509     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13510     fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
13511         vkMemReq.size,
13512         vkMemReq.alignment,
13513         vkMemReq.memoryTypeBits,
13514         createInfo.flags,
13515         createInfo.usage,
13516         createInfo.requiredFlags,
13517         createInfo.preferredFlags,
13518         createInfo.memoryTypeBits,
13519         createInfo.pool,
13520         allocation,
13521         userDataStr.GetString());
13522     Flush();
13523 }
13524 
RecordAllocateMemoryPages(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,uint64_t allocationCount,const VmaAllocation * pAllocations)13525 void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
13526     const VkMemoryRequirements& vkMemReq,
13527     const VmaAllocationCreateInfo& createInfo,
13528     uint64_t allocationCount,
13529     const VmaAllocation* pAllocations)
13530 {
13531     CallParams callParams;
13532     GetBasicParams(callParams);
13533 
13534     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13535     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13536     fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
13537         vkMemReq.size,
13538         vkMemReq.alignment,
13539         vkMemReq.memoryTypeBits,
13540         createInfo.flags,
13541         createInfo.usage,
13542         createInfo.requiredFlags,
13543         createInfo.preferredFlags,
13544         createInfo.memoryTypeBits,
13545         createInfo.pool);
13546     PrintPointerList(allocationCount, pAllocations);
13547     fprintf(m_File, ",%s\n", userDataStr.GetString());
13548     Flush();
13549 }
13550 
RecordAllocateMemoryForBuffer(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)13551 void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
13552     const VkMemoryRequirements& vkMemReq,
13553     bool requiresDedicatedAllocation,
13554     bool prefersDedicatedAllocation,
13555     const VmaAllocationCreateInfo& createInfo,
13556     VmaAllocation allocation)
13557 {
13558     CallParams callParams;
13559     GetBasicParams(callParams);
13560 
13561     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13562     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13563     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,
13564         vkMemReq.size,
13565         vkMemReq.alignment,
13566         vkMemReq.memoryTypeBits,
13567         requiresDedicatedAllocation ? 1 : 0,
13568         prefersDedicatedAllocation ? 1 : 0,
13569         createInfo.flags,
13570         createInfo.usage,
13571         createInfo.requiredFlags,
13572         createInfo.preferredFlags,
13573         createInfo.memoryTypeBits,
13574         createInfo.pool,
13575         allocation,
13576         userDataStr.GetString());
13577     Flush();
13578 }
13579 
RecordAllocateMemoryForImage(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)13580 void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
13581     const VkMemoryRequirements& vkMemReq,
13582     bool requiresDedicatedAllocation,
13583     bool prefersDedicatedAllocation,
13584     const VmaAllocationCreateInfo& createInfo,
13585     VmaAllocation allocation)
13586 {
13587     CallParams callParams;
13588     GetBasicParams(callParams);
13589 
13590     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13591     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13592     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,
13593         vkMemReq.size,
13594         vkMemReq.alignment,
13595         vkMemReq.memoryTypeBits,
13596         requiresDedicatedAllocation ? 1 : 0,
13597         prefersDedicatedAllocation ? 1 : 0,
13598         createInfo.flags,
13599         createInfo.usage,
13600         createInfo.requiredFlags,
13601         createInfo.preferredFlags,
13602         createInfo.memoryTypeBits,
13603         createInfo.pool,
13604         allocation,
13605         userDataStr.GetString());
13606     Flush();
13607 }
13608 
RecordFreeMemory(uint32_t frameIndex,VmaAllocation allocation)13609 void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
13610     VmaAllocation allocation)
13611 {
13612     CallParams callParams;
13613     GetBasicParams(callParams);
13614 
13615     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13616     fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13617         allocation);
13618     Flush();
13619 }
13620 
RecordFreeMemoryPages(uint32_t frameIndex,uint64_t allocationCount,const VmaAllocation * pAllocations)13621 void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
13622     uint64_t allocationCount,
13623     const VmaAllocation* pAllocations)
13624 {
13625     CallParams callParams;
13626     GetBasicParams(callParams);
13627 
13628     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13629     fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
13630     PrintPointerList(allocationCount, pAllocations);
13631     fprintf(m_File, "\n");
13632     Flush();
13633 }
13634 
RecordResizeAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize newSize)13635 void VmaRecorder::RecordResizeAllocation(
13636     uint32_t frameIndex,
13637     VmaAllocation allocation,
13638     VkDeviceSize newSize)
13639 {
13640     CallParams callParams;
13641     GetBasicParams(callParams);
13642 
13643     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13644     fprintf(m_File, "%u,%.3f,%u,vmaResizeAllocation,%p,%llu\n", callParams.threadId, callParams.time, frameIndex,
13645         allocation, newSize);
13646     Flush();
13647 }
13648 
RecordSetAllocationUserData(uint32_t frameIndex,VmaAllocation allocation,const void * pUserData)13649 void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
13650     VmaAllocation allocation,
13651     const void* pUserData)
13652 {
13653     CallParams callParams;
13654     GetBasicParams(callParams);
13655 
13656     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13657     UserDataString userDataStr(
13658         allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
13659         pUserData);
13660     fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
13661         allocation,
13662         userDataStr.GetString());
13663     Flush();
13664 }
13665 
RecordCreateLostAllocation(uint32_t frameIndex,VmaAllocation allocation)13666 void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
13667     VmaAllocation allocation)
13668 {
13669     CallParams callParams;
13670     GetBasicParams(callParams);
13671 
13672     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13673     fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
13674         allocation);
13675     Flush();
13676 }
13677 
RecordMapMemory(uint32_t frameIndex,VmaAllocation allocation)13678 void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
13679     VmaAllocation allocation)
13680 {
13681     CallParams callParams;
13682     GetBasicParams(callParams);
13683 
13684     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13685     fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13686         allocation);
13687     Flush();
13688 }
13689 
RecordUnmapMemory(uint32_t frameIndex,VmaAllocation allocation)13690 void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
13691     VmaAllocation allocation)
13692 {
13693     CallParams callParams;
13694     GetBasicParams(callParams);
13695 
13696     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13697     fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13698         allocation);
13699     Flush();
13700 }
13701 
RecordFlushAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)13702 void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
13703     VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
13704 {
13705     CallParams callParams;
13706     GetBasicParams(callParams);
13707 
13708     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13709     fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
13710         allocation,
13711         offset,
13712         size);
13713     Flush();
13714 }
13715 
RecordInvalidateAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)13716 void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
13717     VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
13718 {
13719     CallParams callParams;
13720     GetBasicParams(callParams);
13721 
13722     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13723     fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
13724         allocation,
13725         offset,
13726         size);
13727     Flush();
13728 }
13729 
RecordCreateBuffer(uint32_t frameIndex,const VkBufferCreateInfo & bufCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)13730 void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
13731     const VkBufferCreateInfo& bufCreateInfo,
13732     const VmaAllocationCreateInfo& allocCreateInfo,
13733     VmaAllocation allocation)
13734 {
13735     CallParams callParams;
13736     GetBasicParams(callParams);
13737 
13738     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13739     UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
13740     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,
13741         bufCreateInfo.flags,
13742         bufCreateInfo.size,
13743         bufCreateInfo.usage,
13744         bufCreateInfo.sharingMode,
13745         allocCreateInfo.flags,
13746         allocCreateInfo.usage,
13747         allocCreateInfo.requiredFlags,
13748         allocCreateInfo.preferredFlags,
13749         allocCreateInfo.memoryTypeBits,
13750         allocCreateInfo.pool,
13751         allocation,
13752         userDataStr.GetString());
13753     Flush();
13754 }
13755 
RecordCreateImage(uint32_t frameIndex,const VkImageCreateInfo & imageCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)13756 void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
13757     const VkImageCreateInfo& imageCreateInfo,
13758     const VmaAllocationCreateInfo& allocCreateInfo,
13759     VmaAllocation allocation)
13760 {
13761     CallParams callParams;
13762     GetBasicParams(callParams);
13763 
13764     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13765     UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
13766     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,
13767         imageCreateInfo.flags,
13768         imageCreateInfo.imageType,
13769         imageCreateInfo.format,
13770         imageCreateInfo.extent.width,
13771         imageCreateInfo.extent.height,
13772         imageCreateInfo.extent.depth,
13773         imageCreateInfo.mipLevels,
13774         imageCreateInfo.arrayLayers,
13775         imageCreateInfo.samples,
13776         imageCreateInfo.tiling,
13777         imageCreateInfo.usage,
13778         imageCreateInfo.sharingMode,
13779         imageCreateInfo.initialLayout,
13780         allocCreateInfo.flags,
13781         allocCreateInfo.usage,
13782         allocCreateInfo.requiredFlags,
13783         allocCreateInfo.preferredFlags,
13784         allocCreateInfo.memoryTypeBits,
13785         allocCreateInfo.pool,
13786         allocation,
13787         userDataStr.GetString());
13788     Flush();
13789 }
13790 
RecordDestroyBuffer(uint32_t frameIndex,VmaAllocation allocation)13791 void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
13792     VmaAllocation allocation)
13793 {
13794     CallParams callParams;
13795     GetBasicParams(callParams);
13796 
13797     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13798     fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
13799         allocation);
13800     Flush();
13801 }
13802 
RecordDestroyImage(uint32_t frameIndex,VmaAllocation allocation)13803 void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
13804     VmaAllocation allocation)
13805 {
13806     CallParams callParams;
13807     GetBasicParams(callParams);
13808 
13809     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13810     fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
13811         allocation);
13812     Flush();
13813 }
13814 
RecordTouchAllocation(uint32_t frameIndex,VmaAllocation allocation)13815 void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
13816     VmaAllocation allocation)
13817 {
13818     CallParams callParams;
13819     GetBasicParams(callParams);
13820 
13821     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13822     fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
13823         allocation);
13824     Flush();
13825 }
13826 
RecordGetAllocationInfo(uint32_t frameIndex,VmaAllocation allocation)13827 void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
13828     VmaAllocation allocation)
13829 {
13830     CallParams callParams;
13831     GetBasicParams(callParams);
13832 
13833     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13834     fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
13835         allocation);
13836     Flush();
13837 }
13838 
RecordMakePoolAllocationsLost(uint32_t frameIndex,VmaPool pool)13839 void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
13840     VmaPool pool)
13841 {
13842     CallParams callParams;
13843     GetBasicParams(callParams);
13844 
13845     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13846     fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
13847         pool);
13848     Flush();
13849 }
13850 
RecordDefragmentationBegin(uint32_t frameIndex,const VmaDefragmentationInfo2 & info,VmaDefragmentationContext ctx)13851 void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
13852     const VmaDefragmentationInfo2& info,
13853     VmaDefragmentationContext ctx)
13854 {
13855     CallParams callParams;
13856     GetBasicParams(callParams);
13857 
13858     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13859     fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
13860         info.flags);
13861     PrintPointerList(info.allocationCount, info.pAllocations);
13862     fprintf(m_File, ",");
13863     PrintPointerList(info.poolCount, info.pPools);
13864     fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
13865         info.maxCpuBytesToMove,
13866         info.maxCpuAllocationsToMove,
13867         info.maxGpuBytesToMove,
13868         info.maxGpuAllocationsToMove,
13869         info.commandBuffer,
13870         ctx);
13871     Flush();
13872 }
13873 
RecordDefragmentationEnd(uint32_t frameIndex,VmaDefragmentationContext ctx)13874 void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
13875     VmaDefragmentationContext ctx)
13876 {
13877     CallParams callParams;
13878     GetBasicParams(callParams);
13879 
13880     VmaMutexLock lock(m_FileMutex, m_UseMutex);
13881     fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
13882         ctx);
13883     Flush();
13884 }
13885 
UserDataString(VmaAllocationCreateFlags allocFlags,const void * pUserData)13886 VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
13887 {
13888     if(pUserData != VMA_NULL)
13889     {
13890         if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
13891         {
13892             m_Str = (const char*)pUserData;
13893         }
13894         else
13895         {
13896             sprintf_s(m_PtrStr, "%p", pUserData);
13897             m_Str = m_PtrStr;
13898         }
13899     }
13900     else
13901     {
13902         m_Str = "";
13903     }
13904 }
13905 
WriteConfiguration(const VkPhysicalDeviceProperties & devProps,const VkPhysicalDeviceMemoryProperties & memProps,bool dedicatedAllocationExtensionEnabled)13906 void VmaRecorder::WriteConfiguration(
13907     const VkPhysicalDeviceProperties& devProps,
13908     const VkPhysicalDeviceMemoryProperties& memProps,
13909     bool dedicatedAllocationExtensionEnabled)
13910 {
13911     fprintf(m_File, "Config,Begin\n");
13912 
13913     fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
13914     fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
13915     fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
13916     fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
13917     fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
13918     fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
13919 
13920     fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
13921     fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
13922     fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
13923 
13924     fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
13925     for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
13926     {
13927         fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
13928         fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
13929     }
13930     fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
13931     for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
13932     {
13933         fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
13934         fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
13935     }
13936 
13937     fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
13938 
13939     fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
13940     fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);
13941     fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
13942     fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
13943     fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
13944     fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
13945     fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
13946     fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
13947     fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
13948 
13949     fprintf(m_File, "Config,End\n");
13950 }
13951 
GetBasicParams(CallParams & outParams)13952 void VmaRecorder::GetBasicParams(CallParams& outParams)
13953 {
13954     outParams.threadId = GetCurrentThreadId();
13955 
13956     LARGE_INTEGER counter;
13957     QueryPerformanceCounter(&counter);
13958     outParams.time = (double)(counter.QuadPart - m_StartCounter) / (double)m_Freq;
13959 }
13960 
PrintPointerList(uint64_t count,const VmaAllocation * pItems)13961 void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
13962 {
13963     if(count)
13964     {
13965         fprintf(m_File, "%p", pItems[0]);
13966         for(uint64_t i = 1; i < count; ++i)
13967         {
13968             fprintf(m_File, " %p", pItems[i]);
13969         }
13970     }
13971 }
13972 
Flush()13973 void VmaRecorder::Flush()
13974 {
13975     if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
13976     {
13977         fflush(m_File);
13978     }
13979 }
13980 
13981 #endif // #if VMA_RECORDING_ENABLED
13982 
13983 ////////////////////////////////////////////////////////////////////////////////
13984 // VmaAllocator_T
13985 
VmaAllocator_T(const VmaAllocatorCreateInfo * pCreateInfo)13986 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
13987     m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
13988     m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
13989     m_hDevice(pCreateInfo->device),
13990     m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
13991     m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
13992         *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
13993     m_PreferredLargeHeapBlockSize(0),
13994     m_PhysicalDevice(pCreateInfo->physicalDevice),
13995     m_CurrentFrameIndex(0),
13996     m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks())),
13997     m_NextPoolId(0)
13998 #if VMA_RECORDING_ENABLED
13999     ,m_pRecorder(VMA_NULL)
14000 #endif
14001 {
14002     if(VMA_DEBUG_DETECT_CORRUPTION)
14003     {
14004         // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
14005         VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
14006     }
14007 
14008     VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device);
14009 
14010 #if !(VMA_DEDICATED_ALLOCATION)
14011     if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
14012     {
14013         VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
14014     }
14015 #endif
14016 
14017     memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
14018     memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
14019     memset(&m_MemProps, 0, sizeof(m_MemProps));
14020 
14021     memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
14022     memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations));
14023 
14024     for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
14025     {
14026         m_HeapSizeLimit[i] = VK_WHOLE_SIZE;
14027     }
14028 
14029     if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
14030     {
14031         m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
14032         m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
14033     }
14034 
14035     ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
14036 
14037     (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
14038     (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
14039 
14040     VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT));
14041     VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
14042     VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
14043     VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
14044 
14045     m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
14046         pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
14047 
14048     if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
14049     {
14050         for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
14051         {
14052             const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
14053             if(limit != VK_WHOLE_SIZE)
14054             {
14055                 m_HeapSizeLimit[heapIndex] = limit;
14056                 if(limit < m_MemProps.memoryHeaps[heapIndex].size)
14057                 {
14058                     m_MemProps.memoryHeaps[heapIndex].size = limit;
14059                 }
14060             }
14061         }
14062     }
14063 
14064     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14065     {
14066         const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
14067 
14068         m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
14069             this,
14070             memTypeIndex,
14071             preferredBlockSize,
14072             0,
14073             SIZE_MAX,
14074             GetBufferImageGranularity(),
14075             pCreateInfo->frameInUseCount,
14076             false, // isCustomPool
14077             false, // explicitBlockSize
14078             false); // linearAlgorithm
14079         // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
14080         // becase minBlockCount is 0.
14081         m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
14082 
14083     }
14084 }
14085 
Init(const VmaAllocatorCreateInfo * pCreateInfo)14086 VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
14087 {
14088     VkResult res = VK_SUCCESS;
14089 
14090     if(pCreateInfo->pRecordSettings != VMA_NULL &&
14091         !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
14092     {
14093 #if VMA_RECORDING_ENABLED
14094         m_pRecorder = vma_new(this, VmaRecorder)();
14095         res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
14096         if(res != VK_SUCCESS)
14097         {
14098             return res;
14099         }
14100         m_pRecorder->WriteConfiguration(
14101             m_PhysicalDeviceProperties,
14102             m_MemProps,
14103             m_UseKhrDedicatedAllocation);
14104         m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
14105 #else
14106         VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
14107         return VK_ERROR_FEATURE_NOT_PRESENT;
14108 #endif
14109     }
14110 
14111     return res;
14112 }
14113 
~VmaAllocator_T()14114 VmaAllocator_T::~VmaAllocator_T()
14115 {
14116 #if VMA_RECORDING_ENABLED
14117     if(m_pRecorder != VMA_NULL)
14118     {
14119         m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
14120         vma_delete(this, m_pRecorder);
14121     }
14122 #endif
14123 
14124     VMA_ASSERT(m_Pools.empty());
14125 
14126     for(size_t i = GetMemoryTypeCount(); i--; )
14127     {
14128         vma_delete(this, m_pDedicatedAllocations[i]);
14129         vma_delete(this, m_pBlockVectors[i]);
14130     }
14131 }
14132 
ImportVulkanFunctions(const VmaVulkanFunctions * pVulkanFunctions)14133 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
14134 {
14135 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
14136     m_VulkanFunctions.vkGetPhysicalDeviceProperties = &vkGetPhysicalDeviceProperties;
14137     m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = &vkGetPhysicalDeviceMemoryProperties;
14138     m_VulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
14139     m_VulkanFunctions.vkFreeMemory = &vkFreeMemory;
14140     m_VulkanFunctions.vkMapMemory = &vkMapMemory;
14141     m_VulkanFunctions.vkUnmapMemory = &vkUnmapMemory;
14142     m_VulkanFunctions.vkFlushMappedMemoryRanges = &vkFlushMappedMemoryRanges;
14143     m_VulkanFunctions.vkInvalidateMappedMemoryRanges = &vkInvalidateMappedMemoryRanges;
14144     m_VulkanFunctions.vkBindBufferMemory = &vkBindBufferMemory;
14145     m_VulkanFunctions.vkBindImageMemory = &vkBindImageMemory;
14146     m_VulkanFunctions.vkGetBufferMemoryRequirements = &vkGetBufferMemoryRequirements;
14147     m_VulkanFunctions.vkGetImageMemoryRequirements = &vkGetImageMemoryRequirements;
14148     m_VulkanFunctions.vkCreateBuffer = &vkCreateBuffer;
14149     m_VulkanFunctions.vkDestroyBuffer = &vkDestroyBuffer;
14150     m_VulkanFunctions.vkCreateImage = &vkCreateImage;
14151     m_VulkanFunctions.vkDestroyImage = &vkDestroyImage;
14152     m_VulkanFunctions.vkCmdCopyBuffer = &vkCmdCopyBuffer;
14153 #if VMA_DEDICATED_ALLOCATION
14154     if(m_UseKhrDedicatedAllocation)
14155     {
14156         m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR =
14157             (PFN_vkGetBufferMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetBufferMemoryRequirements2KHR");
14158         m_VulkanFunctions.vkGetImageMemoryRequirements2KHR =
14159             (PFN_vkGetImageMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetImageMemoryRequirements2KHR");
14160     }
14161 #endif // #if VMA_DEDICATED_ALLOCATION
14162 #endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
14163 
14164 #define VMA_COPY_IF_NOT_NULL(funcName) \
14165     if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
14166 
14167     if(pVulkanFunctions != VMA_NULL)
14168     {
14169         VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
14170         VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
14171         VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
14172         VMA_COPY_IF_NOT_NULL(vkFreeMemory);
14173         VMA_COPY_IF_NOT_NULL(vkMapMemory);
14174         VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
14175         VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
14176         VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
14177         VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
14178         VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
14179         VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
14180         VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
14181         VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
14182         VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
14183         VMA_COPY_IF_NOT_NULL(vkCreateImage);
14184         VMA_COPY_IF_NOT_NULL(vkDestroyImage);
14185         VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
14186 #if VMA_DEDICATED_ALLOCATION
14187         VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
14188         VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
14189 #endif
14190     }
14191 
14192 #undef VMA_COPY_IF_NOT_NULL
14193 
14194     // If these asserts are hit, you must either #define VMA_STATIC_VULKAN_FUNCTIONS 1
14195     // or pass valid pointers as VmaAllocatorCreateInfo::pVulkanFunctions.
14196     VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
14197     VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
14198     VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
14199     VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
14200     VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
14201     VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
14202     VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
14203     VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
14204     VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
14205     VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
14206     VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
14207     VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
14208     VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
14209     VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
14210     VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
14211     VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
14212     VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
14213 #if VMA_DEDICATED_ALLOCATION
14214     if(m_UseKhrDedicatedAllocation)
14215     {
14216         VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
14217         VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
14218     }
14219 #endif
14220 }
14221 
CalcPreferredBlockSize(uint32_t memTypeIndex)14222 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
14223 {
14224     const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
14225     const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
14226     const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
14227     return isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize;
14228 }
14229 
AllocateMemoryOfType(VkDeviceSize size,VkDeviceSize alignment,bool dedicatedAllocation,VkBuffer dedicatedBuffer,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,uint32_t memTypeIndex,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)14230 VkResult VmaAllocator_T::AllocateMemoryOfType(
14231     VkDeviceSize size,
14232     VkDeviceSize alignment,
14233     bool dedicatedAllocation,
14234     VkBuffer dedicatedBuffer,
14235     VkImage dedicatedImage,
14236     const VmaAllocationCreateInfo& createInfo,
14237     uint32_t memTypeIndex,
14238     VmaSuballocationType suballocType,
14239     size_t allocationCount,
14240     VmaAllocation* pAllocations)
14241 {
14242     VMA_ASSERT(pAllocations != VMA_NULL);
14243     VMA_DEBUG_LOG("  AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, vkMemReq.size);
14244 
14245     VmaAllocationCreateInfo finalCreateInfo = createInfo;
14246 
14247     // If memory type is not HOST_VISIBLE, disable MAPPED.
14248     if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
14249         (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
14250     {
14251         finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
14252     }
14253 
14254     VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
14255     VMA_ASSERT(blockVector);
14256 
14257     const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
14258     bool preferDedicatedMemory =
14259         VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
14260         dedicatedAllocation ||
14261         // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
14262         size > preferredBlockSize / 2;
14263 
14264     if(preferDedicatedMemory &&
14265         (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
14266         finalCreateInfo.pool == VK_NULL_HANDLE)
14267     {
14268         finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
14269     }
14270 
14271     if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
14272     {
14273         if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14274         {
14275             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14276         }
14277         else
14278         {
14279             return AllocateDedicatedMemory(
14280                 size,
14281                 suballocType,
14282                 memTypeIndex,
14283                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
14284                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
14285                 finalCreateInfo.pUserData,
14286                 dedicatedBuffer,
14287                 dedicatedImage,
14288                 allocationCount,
14289                 pAllocations);
14290         }
14291     }
14292     else
14293     {
14294         VkResult res = blockVector->Allocate(
14295             VK_NULL_HANDLE, // hCurrentPool
14296             m_CurrentFrameIndex.load(),
14297             size,
14298             alignment,
14299             finalCreateInfo,
14300             suballocType,
14301             allocationCount,
14302             pAllocations);
14303         if(res == VK_SUCCESS)
14304         {
14305             return res;
14306         }
14307 
14308         // 5. Try dedicated memory.
14309         if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14310         {
14311             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14312         }
14313         else
14314         {
14315             res = AllocateDedicatedMemory(
14316                 size,
14317                 suballocType,
14318                 memTypeIndex,
14319                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
14320                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
14321                 finalCreateInfo.pUserData,
14322                 dedicatedBuffer,
14323                 dedicatedImage,
14324                 allocationCount,
14325                 pAllocations);
14326             if(res == VK_SUCCESS)
14327             {
14328                 // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
14329                 VMA_DEBUG_LOG("    Allocated as DedicatedMemory");
14330                 return VK_SUCCESS;
14331             }
14332             else
14333             {
14334                 // Everything failed: Return error code.
14335                 VMA_DEBUG_LOG("    vkAllocateMemory FAILED");
14336                 return res;
14337             }
14338         }
14339     }
14340 }
14341 
AllocateDedicatedMemory(VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,bool map,bool isUserDataString,void * pUserData,VkBuffer,VkImage,size_t allocationCount,VmaAllocation * pAllocations)14342 VkResult VmaAllocator_T::AllocateDedicatedMemory(
14343     VkDeviceSize size,
14344     VmaSuballocationType suballocType,
14345     uint32_t memTypeIndex,
14346     bool map,
14347     bool isUserDataString,
14348     void* pUserData,
14349     VkBuffer /*dedicatedBuffer*/,
14350     VkImage /*dedicatedImage*/,
14351     size_t allocationCount,
14352     VmaAllocation* pAllocations)
14353 {
14354     VMA_ASSERT(allocationCount > 0 && pAllocations);
14355 
14356     VkMemoryAllocateInfo allocInfo = {};
14357     allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
14358     allocInfo.memoryTypeIndex = memTypeIndex;
14359     allocInfo.allocationSize = size;
14360 
14361 #if VMA_DEDICATED_ALLOCATION
14362     VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = {};
14363     dedicatedAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR;
14364     if(m_UseKhrDedicatedAllocation)
14365     {
14366         if(dedicatedBuffer != VK_NULL_HANDLE)
14367         {
14368             VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
14369             dedicatedAllocInfo.buffer = dedicatedBuffer;
14370             allocInfo.pNext = &dedicatedAllocInfo;
14371         }
14372         else if(dedicatedImage != VK_NULL_HANDLE)
14373         {
14374             dedicatedAllocInfo.image = dedicatedImage;
14375             allocInfo.pNext = &dedicatedAllocInfo;
14376         }
14377     }
14378 #endif // #if VMA_DEDICATED_ALLOCATION
14379 
14380     size_t allocIndex;
14381     VkResult res = VK_SUCCESS;
14382     for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14383     {
14384         res = AllocateDedicatedMemoryPage(
14385             size,
14386             suballocType,
14387             memTypeIndex,
14388             allocInfo,
14389             map,
14390             isUserDataString,
14391             pUserData,
14392             pAllocations + allocIndex);
14393         if(res != VK_SUCCESS)
14394         {
14395             break;
14396         }
14397     }
14398 
14399     if(res == VK_SUCCESS)
14400     {
14401         // Register them in m_pDedicatedAllocations.
14402         {
14403             VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
14404             AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
14405             VMA_ASSERT(pDedicatedAllocations);
14406             for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14407             {
14408                 VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, pAllocations[allocIndex]);
14409             }
14410         }
14411 
14412         VMA_DEBUG_LOG("    Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
14413     }
14414     else
14415     {
14416         // Free all already created allocations.
14417         while(allocIndex--)
14418         {
14419             VmaAllocation currAlloc = pAllocations[allocIndex];
14420             VkDeviceMemory hMemory = currAlloc->GetMemory();
14421 
14422             /*
14423             There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
14424             before vkFreeMemory.
14425 
14426             if(currAlloc->GetMappedData() != VMA_NULL)
14427             {
14428                 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
14429             }
14430             */
14431 
14432             FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
14433 
14434             currAlloc->SetUserData(this, VMA_NULL);
14435             vma_delete(this, currAlloc);
14436         }
14437 
14438         memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
14439     }
14440 
14441     return res;
14442 }
14443 
AllocateDedicatedMemoryPage(VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,const VkMemoryAllocateInfo & allocInfo,bool map,bool isUserDataString,void * pUserData,VmaAllocation * pAllocation)14444 VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
14445     VkDeviceSize size,
14446     VmaSuballocationType suballocType,
14447     uint32_t memTypeIndex,
14448     const VkMemoryAllocateInfo& allocInfo,
14449     bool map,
14450     bool isUserDataString,
14451     void* pUserData,
14452     VmaAllocation* pAllocation)
14453 {
14454     VkDeviceMemory hMemory = VK_NULL_HANDLE;
14455     VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
14456     if(res < 0)
14457     {
14458         VMA_DEBUG_LOG("    vkAllocateMemory FAILED");
14459         return res;
14460     }
14461 
14462     void* pMappedData = VMA_NULL;
14463     if(map)
14464     {
14465         res = (*m_VulkanFunctions.vkMapMemory)(
14466             m_hDevice,
14467             hMemory,
14468             0,
14469             VK_WHOLE_SIZE,
14470             0,
14471             &pMappedData);
14472         if(res < 0)
14473         {
14474             VMA_DEBUG_LOG("    vkMapMemory FAILED");
14475             FreeVulkanMemory(memTypeIndex, size, hMemory);
14476             return res;
14477         }
14478     }
14479 
14480     *pAllocation = vma_new(this, VmaAllocation_T)(m_CurrentFrameIndex.load(), isUserDataString);
14481     (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
14482     (*pAllocation)->SetUserData(this, pUserData);
14483     if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
14484     {
14485         FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
14486     }
14487 
14488     return VK_SUCCESS;
14489 }
14490 
GetBufferMemoryRequirements(VkBuffer hBuffer,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)14491 void VmaAllocator_T::GetBufferMemoryRequirements(
14492     VkBuffer hBuffer,
14493     VkMemoryRequirements& memReq,
14494     bool& requiresDedicatedAllocation,
14495     bool& prefersDedicatedAllocation) const
14496 {
14497 #if VMA_DEDICATED_ALLOCATION
14498     if(m_UseKhrDedicatedAllocation)
14499     {
14500         VkBufferMemoryRequirementsInfo2KHR memReqInfo = {};
14501         memReqInfo.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR;
14502         memReqInfo.buffer = hBuffer;
14503 
14504         VkMemoryDedicatedRequirementsKHR memDedicatedReq = {};
14505         memDedicatedReq.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR;
14506 
14507         VkMemoryRequirements2KHR memReq2 = {};
14508         memReq2.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR;
14509         memReq2.pNext = &memDedicatedReq;
14510 
14511         (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
14512 
14513         memReq = memReq2.memoryRequirements;
14514         requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
14515         prefersDedicatedAllocation  = (memDedicatedReq.prefersDedicatedAllocation  != VK_FALSE);
14516     }
14517     else
14518 #endif // #if VMA_DEDICATED_ALLOCATION
14519     {
14520         (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
14521         requiresDedicatedAllocation = false;
14522         prefersDedicatedAllocation  = false;
14523     }
14524 }
14525 
GetImageMemoryRequirements(VkImage hImage,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)14526 void VmaAllocator_T::GetImageMemoryRequirements(
14527     VkImage hImage,
14528     VkMemoryRequirements& memReq,
14529     bool& requiresDedicatedAllocation,
14530     bool& prefersDedicatedAllocation) const
14531 {
14532 #if VMA_DEDICATED_ALLOCATION
14533     if(m_UseKhrDedicatedAllocation)
14534     {
14535         VkImageMemoryRequirementsInfo2KHR memReqInfo = {};
14536         memReqInfo.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR;
14537         memReqInfo.image = hImage;
14538 
14539         VkMemoryDedicatedRequirementsKHR memDedicatedReq = {};
14540         memDedicatedReq.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR;
14541 
14542         VkMemoryRequirements2KHR memReq2 = {};
14543         memReq2.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR;
14544         memReq2.pNext = &memDedicatedReq;
14545 
14546         (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
14547 
14548         memReq = memReq2.memoryRequirements;
14549         requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
14550         prefersDedicatedAllocation  = (memDedicatedReq.prefersDedicatedAllocation  != VK_FALSE);
14551     }
14552     else
14553 #endif // #if VMA_DEDICATED_ALLOCATION
14554     {
14555         (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
14556         requiresDedicatedAllocation = false;
14557         prefersDedicatedAllocation  = false;
14558     }
14559 }
14560 
AllocateMemory(const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,VkBuffer dedicatedBuffer,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)14561 VkResult VmaAllocator_T::AllocateMemory(
14562     const VkMemoryRequirements& vkMemReq,
14563     bool requiresDedicatedAllocation,
14564     bool prefersDedicatedAllocation,
14565     VkBuffer dedicatedBuffer,
14566     VkImage dedicatedImage,
14567     const VmaAllocationCreateInfo& createInfo,
14568     VmaSuballocationType suballocType,
14569     size_t allocationCount,
14570     VmaAllocation* pAllocations)
14571 {
14572     memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
14573 
14574     VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
14575 
14576     if(vkMemReq.size == 0)
14577     {
14578         return VK_ERROR_VALIDATION_FAILED_EXT;
14579     }
14580     if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
14581         (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14582     {
14583         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
14584         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14585     }
14586     if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
14587         (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
14588     {
14589         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
14590         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14591     }
14592     if(requiresDedicatedAllocation)
14593     {
14594         if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14595         {
14596             VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
14597             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14598         }
14599         if(createInfo.pool != VK_NULL_HANDLE)
14600         {
14601             VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
14602             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14603         }
14604     }
14605     if((createInfo.pool != VK_NULL_HANDLE) &&
14606         ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
14607     {
14608         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
14609         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14610     }
14611 
14612     if(createInfo.pool != VK_NULL_HANDLE)
14613     {
14614         const VkDeviceSize alignmentForPool = VMA_MAX(
14615             vkMemReq.alignment,
14616             GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex()));
14617         return createInfo.pool->m_BlockVector.Allocate(
14618             createInfo.pool,
14619             m_CurrentFrameIndex.load(),
14620             vkMemReq.size,
14621             alignmentForPool,
14622             createInfo,
14623             suballocType,
14624             allocationCount,
14625             pAllocations);
14626     }
14627     else
14628     {
14629         // Bit mask of memory Vulkan types acceptable for this allocation.
14630         uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
14631         uint32_t memTypeIndex = UINT32_MAX;
14632         VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
14633         if(res == VK_SUCCESS)
14634         {
14635             VkDeviceSize alignmentForMemType = VMA_MAX(
14636                 vkMemReq.alignment,
14637                 GetMemoryTypeMinAlignment(memTypeIndex));
14638 
14639             res = AllocateMemoryOfType(
14640                 vkMemReq.size,
14641                 alignmentForMemType,
14642                 requiresDedicatedAllocation || prefersDedicatedAllocation,
14643                 dedicatedBuffer,
14644                 dedicatedImage,
14645                 createInfo,
14646                 memTypeIndex,
14647                 suballocType,
14648                 allocationCount,
14649                 pAllocations);
14650             // Succeeded on first try.
14651             if(res == VK_SUCCESS)
14652             {
14653                 return res;
14654             }
14655             // Allocation from this memory type failed. Try other compatible memory types.
14656             else
14657             {
14658                 for(;;)
14659                 {
14660                     // Remove old memTypeIndex from list of possibilities.
14661                     memoryTypeBits &= ~(1u << memTypeIndex);
14662                     // Find alternative memTypeIndex.
14663                     res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
14664                     if(res == VK_SUCCESS)
14665                     {
14666                         alignmentForMemType = VMA_MAX(
14667                             vkMemReq.alignment,
14668                             GetMemoryTypeMinAlignment(memTypeIndex));
14669 
14670                         res = AllocateMemoryOfType(
14671                             vkMemReq.size,
14672                             alignmentForMemType,
14673                             requiresDedicatedAllocation || prefersDedicatedAllocation,
14674                             dedicatedBuffer,
14675                             dedicatedImage,
14676                             createInfo,
14677                             memTypeIndex,
14678                             suballocType,
14679                             allocationCount,
14680                             pAllocations);
14681                         // Allocation from this alternative memory type succeeded.
14682                         if(res == VK_SUCCESS)
14683                         {
14684                             return res;
14685                         }
14686                         // else: Allocation from this memory type failed. Try next one - next loop iteration.
14687                     }
14688                     // No other matching memory type index could be found.
14689                     else
14690                     {
14691                         // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
14692                         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14693                     }
14694                 }
14695             }
14696         }
14697         // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
14698         else
14699             return res;
14700     }
14701 }
14702 
FreeMemory(size_t allocationCount,const VmaAllocation * pAllocations)14703 void VmaAllocator_T::FreeMemory(
14704     size_t allocationCount,
14705     const VmaAllocation* pAllocations)
14706 {
14707     VMA_ASSERT(pAllocations);
14708 
14709     for(size_t allocIndex = allocationCount; allocIndex--; )
14710     {
14711         VmaAllocation allocation = pAllocations[allocIndex];
14712 
14713         if(allocation != VK_NULL_HANDLE)
14714         {
14715             if(TouchAllocation(allocation))
14716             {
14717                 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
14718                 {
14719                     FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
14720                 }
14721 
14722                 switch(allocation->GetType())
14723                 {
14724                 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
14725                     {
14726                         VmaBlockVector* pBlockVector = VMA_NULL;
14727                         VmaPool hPool = allocation->GetPool();
14728                         if(hPool != VK_NULL_HANDLE)
14729                         {
14730                             pBlockVector = &hPool->m_BlockVector;
14731                         }
14732                         else
14733                         {
14734                             const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
14735                             pBlockVector = m_pBlockVectors[memTypeIndex];
14736                         }
14737                         pBlockVector->Free(allocation);
14738                     }
14739                     break;
14740                 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
14741                     FreeDedicatedMemory(allocation);
14742                     break;
14743                 default:
14744                     VMA_ASSERT(0);
14745                 }
14746             }
14747 
14748             allocation->SetUserData(this, VMA_NULL);
14749             vma_delete(this, allocation);
14750         }
14751     }
14752 }
14753 
ResizeAllocation(const VmaAllocation alloc,VkDeviceSize newSize)14754 VkResult VmaAllocator_T::ResizeAllocation(
14755     const VmaAllocation alloc,
14756     VkDeviceSize newSize)
14757 {
14758     if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST)
14759     {
14760         return VK_ERROR_VALIDATION_FAILED_EXT;
14761     }
14762     if(newSize == alloc->GetSize())
14763     {
14764         return VK_SUCCESS;
14765     }
14766 
14767     switch(alloc->GetType())
14768     {
14769     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
14770         return VK_ERROR_FEATURE_NOT_PRESENT;
14771     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
14772         if(alloc->GetBlock()->m_pMetadata->ResizeAllocation(alloc, newSize))
14773         {
14774             alloc->ChangeSize(newSize);
14775             VMA_HEAVY_ASSERT(alloc->GetBlock()->m_pMetadata->Validate());
14776             return VK_SUCCESS;
14777         }
14778         else
14779         {
14780             return VkResult(-1000069000); // VK_ERROR_OUT_OF_POOL_MEMORY
14781         }
14782     default:
14783         VMA_ASSERT(0);
14784         return VK_ERROR_VALIDATION_FAILED_EXT;
14785     }
14786 }
14787 
CalculateStats(VmaStats * pStats)14788 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
14789 {
14790     // Initialize.
14791     InitStatInfo(pStats->total);
14792     for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
14793         InitStatInfo(pStats->memoryType[i]);
14794     for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
14795         InitStatInfo(pStats->memoryHeap[i]);
14796 
14797     // Process default pools.
14798     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14799     {
14800         VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
14801         VMA_ASSERT(pBlockVector);
14802         pBlockVector->AddStats(pStats);
14803     }
14804 
14805     // Process custom pools.
14806     {
14807         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
14808         for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
14809         {
14810             m_Pools[poolIndex]->m_BlockVector.AddStats(pStats);
14811         }
14812     }
14813 
14814     // Process dedicated allocations.
14815     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14816     {
14817         const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
14818         VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
14819         AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
14820         VMA_ASSERT(pDedicatedAllocVector);
14821         for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
14822         {
14823             VmaStatInfo allocationStatInfo;
14824             (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo);
14825             VmaAddStatInfo(pStats->total, allocationStatInfo);
14826             VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
14827             VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
14828         }
14829     }
14830 
14831     // Postprocess.
14832     VmaPostprocessCalcStatInfo(pStats->total);
14833     for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
14834         VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
14835     for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
14836         VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
14837 }
14838 
14839 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
14840 
DefragmentationBegin(const VmaDefragmentationInfo2 & info,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)14841 VkResult VmaAllocator_T::DefragmentationBegin(
14842     const VmaDefragmentationInfo2& info,
14843     VmaDefragmentationStats* pStats,
14844     VmaDefragmentationContext* pContext)
14845 {
14846     if(info.pAllocationsChanged != VMA_NULL)
14847     {
14848         memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
14849     }
14850 
14851     *pContext = vma_new(this, VmaDefragmentationContext_T)(
14852         this, m_CurrentFrameIndex.load(), info.flags, pStats);
14853 
14854     (*pContext)->AddPools(info.poolCount, info.pPools);
14855     (*pContext)->AddAllocations(
14856         info.allocationCount, info.pAllocations, info.pAllocationsChanged);
14857 
14858     VkResult res = (*pContext)->Defragment(
14859         info.maxCpuBytesToMove, info.maxCpuAllocationsToMove,
14860         info.maxGpuBytesToMove, info.maxGpuAllocationsToMove,
14861         info.commandBuffer, pStats);
14862 
14863     if(res != VK_NOT_READY)
14864     {
14865         vma_delete(this, *pContext);
14866         *pContext = VMA_NULL;
14867     }
14868 
14869     return res;
14870 }
14871 
DefragmentationEnd(VmaDefragmentationContext context)14872 VkResult VmaAllocator_T::DefragmentationEnd(
14873     VmaDefragmentationContext context)
14874 {
14875     vma_delete(this, context);
14876     return VK_SUCCESS;
14877 }
14878 
GetAllocationInfo(VmaAllocation hAllocation,VmaAllocationInfo * pAllocationInfo)14879 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
14880 {
14881     if(hAllocation->CanBecomeLost())
14882     {
14883         /*
14884         Warning: This is a carefully designed algorithm.
14885         Do not modify unless you really know what you're doing :)
14886         */
14887         const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14888         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14889         for(;;)
14890         {
14891             if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
14892             {
14893                 pAllocationInfo->memoryType = UINT32_MAX;
14894                 pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
14895                 pAllocationInfo->offset = 0;
14896                 pAllocationInfo->size = hAllocation->GetSize();
14897                 pAllocationInfo->pMappedData = VMA_NULL;
14898                 pAllocationInfo->pUserData = hAllocation->GetUserData();
14899                 return;
14900             }
14901             else if(localLastUseFrameIndex == localCurrFrameIndex)
14902             {
14903                 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
14904                 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
14905                 pAllocationInfo->offset = hAllocation->GetOffset();
14906                 pAllocationInfo->size = hAllocation->GetSize();
14907                 pAllocationInfo->pMappedData = VMA_NULL;
14908                 pAllocationInfo->pUserData = hAllocation->GetUserData();
14909                 return;
14910             }
14911             else // Last use time earlier than current time.
14912             {
14913                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14914                 {
14915                     localLastUseFrameIndex = localCurrFrameIndex;
14916                 }
14917             }
14918         }
14919     }
14920     else
14921     {
14922 #if VMA_STATS_STRING_ENABLED
14923         uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14924         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14925         for(;;)
14926         {
14927             VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
14928             if(localLastUseFrameIndex == localCurrFrameIndex)
14929             {
14930                 break;
14931             }
14932             else // Last use time earlier than current time.
14933             {
14934                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14935                 {
14936                     localLastUseFrameIndex = localCurrFrameIndex;
14937                 }
14938             }
14939         }
14940 #endif
14941 
14942         pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
14943         pAllocationInfo->deviceMemory = hAllocation->GetMemory();
14944         pAllocationInfo->offset = hAllocation->GetOffset();
14945         pAllocationInfo->size = hAllocation->GetSize();
14946         pAllocationInfo->pMappedData = hAllocation->GetMappedData();
14947         pAllocationInfo->pUserData = hAllocation->GetUserData();
14948     }
14949 }
14950 
TouchAllocation(VmaAllocation hAllocation)14951 bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
14952 {
14953     // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
14954     if(hAllocation->CanBecomeLost())
14955     {
14956         uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14957         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14958         for(;;)
14959         {
14960             if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
14961             {
14962                 return false;
14963             }
14964             else if(localLastUseFrameIndex == localCurrFrameIndex)
14965             {
14966                 return true;
14967             }
14968             else // Last use time earlier than current time.
14969             {
14970                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14971                 {
14972                     localLastUseFrameIndex = localCurrFrameIndex;
14973                 }
14974             }
14975         }
14976     }
14977     else
14978     {
14979 #if VMA_STATS_STRING_ENABLED
14980         uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14981         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14982         for(;;)
14983         {
14984             VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
14985             if(localLastUseFrameIndex == localCurrFrameIndex)
14986             {
14987                 break;
14988             }
14989             else // Last use time earlier than current time.
14990             {
14991                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14992                 {
14993                     localLastUseFrameIndex = localCurrFrameIndex;
14994                 }
14995             }
14996         }
14997 #endif
14998 
14999         return true;
15000     }
15001 }
15002 
CreatePool(const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)15003 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
15004 {
15005     VMA_DEBUG_LOG("  CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
15006 
15007     VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
15008 
15009     if(newCreateInfo.maxBlockCount == 0)
15010     {
15011         newCreateInfo.maxBlockCount = SIZE_MAX;
15012     }
15013     if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
15014     {
15015         return VK_ERROR_INITIALIZATION_FAILED;
15016     }
15017 
15018     const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
15019 
15020     *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
15021 
15022     VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
15023     if(res != VK_SUCCESS)
15024     {
15025         vma_delete(this, *pPool);
15026         *pPool = VMA_NULL;
15027         return res;
15028     }
15029 
15030     // Add to m_Pools.
15031     {
15032         VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15033         (*pPool)->SetId(m_NextPoolId++);
15034         VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool);
15035     }
15036 
15037     return VK_SUCCESS;
15038 }
15039 
DestroyPool(VmaPool pool)15040 void VmaAllocator_T::DestroyPool(VmaPool pool)
15041 {
15042     // Remove from m_Pools.
15043     {
15044         VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15045         bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
15046         (void) success;
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 = {};
15296         memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
15297         memRange.memory = hAllocation->GetMemory();
15298 
15299         switch(hAllocation->GetType())
15300         {
15301         case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15302             memRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
15303             if(size == VK_WHOLE_SIZE)
15304             {
15305                 memRange.size = allocationSize - memRange.offset;
15306             }
15307             else
15308             {
15309                 VMA_ASSERT(offset + size <= allocationSize);
15310                 memRange.size = VMA_MIN(
15311                     VmaAlignUp(size + (offset - memRange.offset), nonCoherentAtomSize),
15312                     allocationSize - memRange.offset);
15313             }
15314             break;
15315 
15316         case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15317         {
15318             // 1. Still within this allocation.
15319             memRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
15320             if(size == VK_WHOLE_SIZE)
15321             {
15322                 size = allocationSize - offset;
15323             }
15324             else
15325             {
15326                 VMA_ASSERT(offset + size <= allocationSize);
15327             }
15328             memRange.size = VmaAlignUp(size + (offset - memRange.offset), nonCoherentAtomSize);
15329 
15330             // 2. Adjust to whole block.
15331             const VkDeviceSize allocationOffset = hAllocation->GetOffset();
15332             VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
15333             const VkDeviceSize blockSize = hAllocation->GetBlock()->m_pMetadata->GetSize();
15334             memRange.offset += allocationOffset;
15335             memRange.size = VMA_MIN(memRange.size, blockSize - memRange.offset);
15336 
15337             break;
15338         }
15339 
15340         default:
15341             VMA_ASSERT(0);
15342         }
15343 
15344         switch(op)
15345         {
15346         case VMA_CACHE_FLUSH:
15347             (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
15348             break;
15349         case VMA_CACHE_INVALIDATE:
15350             (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
15351             break;
15352         default:
15353             VMA_ASSERT(0);
15354         }
15355     }
15356     // else: Just ignore this call.
15357 }
15358 
FreeDedicatedMemory(VmaAllocation allocation)15359 void VmaAllocator_T::FreeDedicatedMemory(VmaAllocation allocation)
15360 {
15361     VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
15362 
15363     const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
15364     {
15365         VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
15366         AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
15367         VMA_ASSERT(pDedicatedAllocations);
15368         bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
15369         (void) success;
15370         VMA_ASSERT(success);
15371     }
15372 
15373     VkDeviceMemory hMemory = allocation->GetMemory();
15374 
15375     /*
15376     There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
15377     before vkFreeMemory.
15378 
15379     if(allocation->GetMappedData() != VMA_NULL)
15380     {
15381         (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
15382     }
15383     */
15384 
15385     FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
15386 
15387     VMA_DEBUG_LOG("    Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
15388 }
15389 
FillAllocation(const VmaAllocation hAllocation,uint8_t pattern)15390 void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
15391 {
15392     if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
15393         !hAllocation->CanBecomeLost() &&
15394         (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
15395     {
15396         void* pData = VMA_NULL;
15397         VkResult res = Map(hAllocation, &pData);
15398         if(res == VK_SUCCESS)
15399         {
15400             memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
15401             FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
15402             Unmap(hAllocation);
15403         }
15404         else
15405         {
15406             VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
15407         }
15408     }
15409 }
15410 
15411 #if VMA_STATS_STRING_ENABLED
15412 
PrintDetailedMap(VmaJsonWriter & json)15413 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
15414 {
15415     bool dedicatedAllocationsStarted = false;
15416     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15417     {
15418         VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
15419         AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
15420         VMA_ASSERT(pDedicatedAllocVector);
15421         if(pDedicatedAllocVector->empty() == false)
15422         {
15423             if(dedicatedAllocationsStarted == false)
15424             {
15425                 dedicatedAllocationsStarted = true;
15426                 json.WriteString("DedicatedAllocations");
15427                 json.BeginObject();
15428             }
15429 
15430             json.BeginString("Type ");
15431             json.ContinueString(memTypeIndex);
15432             json.EndString();
15433 
15434             json.BeginArray();
15435 
15436             for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
15437             {
15438                 json.BeginObject(true);
15439                 const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
15440                 hAlloc->PrintParameters(json);
15441                 json.EndObject();
15442             }
15443 
15444             json.EndArray();
15445         }
15446     }
15447     if(dedicatedAllocationsStarted)
15448     {
15449         json.EndObject();
15450     }
15451 
15452     {
15453         bool allocationsStarted = false;
15454         for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15455         {
15456             if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
15457             {
15458                 if(allocationsStarted == false)
15459                 {
15460                     allocationsStarted = true;
15461                     json.WriteString("DefaultPools");
15462                     json.BeginObject();
15463                 }
15464 
15465                 json.BeginString("Type ");
15466                 json.ContinueString(memTypeIndex);
15467                 json.EndString();
15468 
15469                 m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
15470             }
15471         }
15472         if(allocationsStarted)
15473         {
15474             json.EndObject();
15475         }
15476     }
15477 
15478     // Custom pools
15479     {
15480         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15481         const size_t poolCount = m_Pools.size();
15482         if(poolCount > 0)
15483         {
15484             json.WriteString("Pools");
15485             json.BeginObject();
15486             for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
15487             {
15488                 json.BeginString();
15489                 json.ContinueString(m_Pools[poolIndex]->GetId());
15490                 json.EndString();
15491 
15492                 m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
15493             }
15494             json.EndObject();
15495         }
15496     }
15497 }
15498 
15499 #endif // #if VMA_STATS_STRING_ENABLED
15500 
15501 ////////////////////////////////////////////////////////////////////////////////
15502 // Public interface
15503 
vmaCreateAllocator(const VmaAllocatorCreateInfo * pCreateInfo,VmaAllocator * pAllocator)15504 VkResult vmaCreateAllocator(
15505     const VmaAllocatorCreateInfo* pCreateInfo,
15506     VmaAllocator* pAllocator)
15507 {
15508     VMA_ASSERT(pCreateInfo && pAllocator);
15509     VMA_DEBUG_LOG("vmaCreateAllocator");
15510     *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
15511     return (*pAllocator)->Init(pCreateInfo);
15512 }
15513 
vmaDestroyAllocator(VmaAllocator allocator)15514 void vmaDestroyAllocator(
15515     VmaAllocator allocator)
15516 {
15517     if(allocator != VK_NULL_HANDLE)
15518     {
15519         VMA_DEBUG_LOG("vmaDestroyAllocator");
15520         VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
15521         vma_delete(&allocationCallbacks, allocator);
15522     }
15523 }
15524 
vmaGetPhysicalDeviceProperties(VmaAllocator allocator,const VkPhysicalDeviceProperties ** ppPhysicalDeviceProperties)15525 void vmaGetPhysicalDeviceProperties(
15526     VmaAllocator allocator,
15527     const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
15528 {
15529     VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
15530     *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
15531 }
15532 
vmaGetMemoryProperties(VmaAllocator allocator,const VkPhysicalDeviceMemoryProperties ** ppPhysicalDeviceMemoryProperties)15533 void vmaGetMemoryProperties(
15534     VmaAllocator allocator,
15535     const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
15536 {
15537     VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
15538     *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
15539 }
15540 
vmaGetMemoryTypeProperties(VmaAllocator allocator,uint32_t memoryTypeIndex,VkMemoryPropertyFlags * pFlags)15541 void vmaGetMemoryTypeProperties(
15542     VmaAllocator allocator,
15543     uint32_t memoryTypeIndex,
15544     VkMemoryPropertyFlags* pFlags)
15545 {
15546     VMA_ASSERT(allocator && pFlags);
15547     VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
15548     *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
15549 }
15550 
vmaSetCurrentFrameIndex(VmaAllocator allocator,uint32_t frameIndex)15551 void vmaSetCurrentFrameIndex(
15552     VmaAllocator allocator,
15553     uint32_t frameIndex)
15554 {
15555     VMA_ASSERT(allocator);
15556     VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
15557 
15558     VMA_DEBUG_GLOBAL_MUTEX_LOCK
15559 
15560     allocator->SetCurrentFrameIndex(frameIndex);
15561 }
15562 
vmaCalculateStats(VmaAllocator allocator,VmaStats * pStats)15563 void vmaCalculateStats(
15564     VmaAllocator allocator,
15565     VmaStats* pStats)
15566 {
15567     VMA_ASSERT(allocator && pStats);
15568     VMA_DEBUG_GLOBAL_MUTEX_LOCK
15569     allocator->CalculateStats(pStats);
15570 }
15571 
15572 #if VMA_STATS_STRING_ENABLED
15573 
vmaBuildStatsString(VmaAllocator allocator,char ** ppStatsString,VkBool32 detailedMap)15574 void vmaBuildStatsString(
15575     VmaAllocator allocator,
15576     char** ppStatsString,
15577     VkBool32 detailedMap)
15578 {
15579     VMA_ASSERT(allocator && ppStatsString);
15580     VMA_DEBUG_GLOBAL_MUTEX_LOCK
15581 
15582     VmaStringBuilder sb(allocator);
15583     {
15584         VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
15585         json.BeginObject();
15586 
15587         VmaStats stats;
15588         allocator->CalculateStats(&stats);
15589 
15590         json.WriteString("Total");
15591         VmaPrintStatInfo(json, stats.total);
15592 
15593         for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
15594         {
15595             json.BeginString("Heap ");
15596             json.ContinueString(heapIndex);
15597             json.EndString();
15598             json.BeginObject();
15599 
15600             json.WriteString("Size");
15601             json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
15602 
15603             json.WriteString("Flags");
15604             json.BeginArray(true);
15605             if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
15606             {
15607                 json.WriteString("DEVICE_LOCAL");
15608             }
15609             json.EndArray();
15610 
15611             if(stats.memoryHeap[heapIndex].blockCount > 0)
15612             {
15613                 json.WriteString("Stats");
15614                 VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
15615             }
15616 
15617             for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
15618             {
15619                 if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
15620                 {
15621                     json.BeginString("Type ");
15622                     json.ContinueString(typeIndex);
15623                     json.EndString();
15624 
15625                     json.BeginObject();
15626 
15627                     json.WriteString("Flags");
15628                     json.BeginArray(true);
15629                     VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
15630                     if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
15631                     {
15632                         json.WriteString("DEVICE_LOCAL");
15633                     }
15634                     if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
15635                     {
15636                         json.WriteString("HOST_VISIBLE");
15637                     }
15638                     if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
15639                     {
15640                         json.WriteString("HOST_COHERENT");
15641                     }
15642                     if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
15643                     {
15644                         json.WriteString("HOST_CACHED");
15645                     }
15646                     if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
15647                     {
15648                         json.WriteString("LAZILY_ALLOCATED");
15649                     }
15650                     json.EndArray();
15651 
15652                     if(stats.memoryType[typeIndex].blockCount > 0)
15653                     {
15654                         json.WriteString("Stats");
15655                         VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
15656                     }
15657 
15658                     json.EndObject();
15659                 }
15660             }
15661 
15662             json.EndObject();
15663         }
15664         if(detailedMap == VK_TRUE)
15665         {
15666             allocator->PrintDetailedMap(json);
15667         }
15668 
15669         json.EndObject();
15670     }
15671 
15672     const size_t len = sb.GetLength();
15673     char* const pChars = vma_new_array(allocator, char, len + 1);
15674     if(len > 0)
15675     {
15676         memcpy(pChars, sb.GetData(), len);
15677     }
15678     pChars[len] = '\0';
15679     *ppStatsString = pChars;
15680 }
15681 
vmaFreeStatsString(VmaAllocator allocator,char * pStatsString)15682 void vmaFreeStatsString(
15683     VmaAllocator allocator,
15684     char* pStatsString)
15685 {
15686     if(pStatsString != VMA_NULL)
15687     {
15688         VMA_ASSERT(allocator);
15689         size_t len = strlen(pStatsString);
15690         vma_delete_array(allocator, pStatsString, len + 1);
15691     }
15692 }
15693 
15694 #endif // #if VMA_STATS_STRING_ENABLED
15695 
15696 /*
15697 This function is not protected by any mutex because it just reads immutable data.
15698 */
vmaFindMemoryTypeIndex(VmaAllocator allocator,uint32_t memoryTypeBits,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)15699 VkResult vmaFindMemoryTypeIndex(
15700     VmaAllocator allocator,
15701     uint32_t memoryTypeBits,
15702     const VmaAllocationCreateInfo* pAllocationCreateInfo,
15703     uint32_t* pMemoryTypeIndex)
15704 {
15705     VMA_ASSERT(allocator != VK_NULL_HANDLE);
15706     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15707     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15708 
15709     if(pAllocationCreateInfo->memoryTypeBits != 0)
15710     {
15711         memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
15712     }
15713 
15714     uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
15715     uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
15716 
15717     const bool mapped = (pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
15718     if(mapped)
15719     {
15720         preferredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15721     }
15722 
15723     // Convert usage to requiredFlags and preferredFlags.
15724     switch(pAllocationCreateInfo->usage)
15725     {
15726     case VMA_MEMORY_USAGE_UNKNOWN:
15727         break;
15728     case VMA_MEMORY_USAGE_GPU_ONLY:
15729         if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15730         {
15731             preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
15732         }
15733         break;
15734     case VMA_MEMORY_USAGE_CPU_ONLY:
15735         requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
15736         break;
15737     case VMA_MEMORY_USAGE_CPU_TO_GPU:
15738         requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15739         if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15740         {
15741             preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
15742         }
15743         break;
15744     case VMA_MEMORY_USAGE_GPU_TO_CPU:
15745         requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15746         preferredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
15747         break;
15748     default:
15749         break;
15750     }
15751 
15752     *pMemoryTypeIndex = UINT32_MAX;
15753     uint32_t minCost = UINT32_MAX;
15754     for(uint32_t memTypeIndex = 0, memTypeBit = 1;
15755         memTypeIndex < allocator->GetMemoryTypeCount();
15756         ++memTypeIndex, memTypeBit <<= 1)
15757     {
15758         // This memory type is acceptable according to memoryTypeBits bitmask.
15759         if((memTypeBit & memoryTypeBits) != 0)
15760         {
15761             const VkMemoryPropertyFlags currFlags =
15762                 allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
15763             // This memory type contains requiredFlags.
15764             if((requiredFlags & ~currFlags) == 0)
15765             {
15766                 // Calculate cost as number of bits from preferredFlags not present in this memory type.
15767                 uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags);
15768                 // Remember memory type with lowest cost.
15769                 if(currCost < minCost)
15770                 {
15771                     *pMemoryTypeIndex = memTypeIndex;
15772                     if(currCost == 0)
15773                     {
15774                         return VK_SUCCESS;
15775                     }
15776                     minCost = currCost;
15777                 }
15778             }
15779         }
15780     }
15781     return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
15782 }
15783 
vmaFindMemoryTypeIndexForBufferInfo(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)15784 VkResult vmaFindMemoryTypeIndexForBufferInfo(
15785     VmaAllocator allocator,
15786     const VkBufferCreateInfo* pBufferCreateInfo,
15787     const VmaAllocationCreateInfo* pAllocationCreateInfo,
15788     uint32_t* pMemoryTypeIndex)
15789 {
15790     VMA_ASSERT(allocator != VK_NULL_HANDLE);
15791     VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
15792     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15793     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15794 
15795     const VkDevice hDev = allocator->m_hDevice;
15796     VkBuffer hBuffer = VK_NULL_HANDLE;
15797     VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
15798         hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
15799     if(res == VK_SUCCESS)
15800     {
15801         VkMemoryRequirements memReq = {};
15802         allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
15803             hDev, hBuffer, &memReq);
15804 
15805         res = vmaFindMemoryTypeIndex(
15806             allocator,
15807             memReq.memoryTypeBits,
15808             pAllocationCreateInfo,
15809             pMemoryTypeIndex);
15810 
15811         allocator->GetVulkanFunctions().vkDestroyBuffer(
15812             hDev, hBuffer, allocator->GetAllocationCallbacks());
15813     }
15814     return res;
15815 }
15816 
vmaFindMemoryTypeIndexForImageInfo(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)15817 VkResult vmaFindMemoryTypeIndexForImageInfo(
15818     VmaAllocator allocator,
15819     const VkImageCreateInfo* pImageCreateInfo,
15820     const VmaAllocationCreateInfo* pAllocationCreateInfo,
15821     uint32_t* pMemoryTypeIndex)
15822 {
15823     VMA_ASSERT(allocator != VK_NULL_HANDLE);
15824     VMA_ASSERT(pImageCreateInfo != VMA_NULL);
15825     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15826     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15827 
15828     const VkDevice hDev = allocator->m_hDevice;
15829     VkImage hImage = VK_NULL_HANDLE;
15830     VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
15831         hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
15832     if(res == VK_SUCCESS)
15833     {
15834         VkMemoryRequirements memReq = {};
15835         allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
15836             hDev, hImage, &memReq);
15837 
15838         res = vmaFindMemoryTypeIndex(
15839             allocator,
15840             memReq.memoryTypeBits,
15841             pAllocationCreateInfo,
15842             pMemoryTypeIndex);
15843 
15844         allocator->GetVulkanFunctions().vkDestroyImage(
15845             hDev, hImage, allocator->GetAllocationCallbacks());
15846     }
15847     return res;
15848 }
15849 
vmaCreatePool(VmaAllocator allocator,const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)15850 VkResult vmaCreatePool(
15851 	VmaAllocator allocator,
15852 	const VmaPoolCreateInfo* pCreateInfo,
15853 	VmaPool* pPool)
15854 {
15855     VMA_ASSERT(allocator && pCreateInfo && pPool);
15856 
15857     VMA_DEBUG_LOG("vmaCreatePool");
15858 
15859     VMA_DEBUG_GLOBAL_MUTEX_LOCK
15860 
15861     VkResult res = allocator->CreatePool(pCreateInfo, pPool);
15862 
15863 #if VMA_RECORDING_ENABLED
15864     if(allocator->GetRecorder() != VMA_NULL)
15865     {
15866         allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
15867     }
15868 #endif
15869 
15870     return res;
15871 }
15872 
vmaDestroyPool(VmaAllocator allocator,VmaPool pool)15873 void vmaDestroyPool(
15874     VmaAllocator allocator,
15875     VmaPool pool)
15876 {
15877     VMA_ASSERT(allocator);
15878 
15879     if(pool == VK_NULL_HANDLE)
15880     {
15881         return;
15882     }
15883 
15884     VMA_DEBUG_LOG("vmaDestroyPool");
15885 
15886     VMA_DEBUG_GLOBAL_MUTEX_LOCK
15887 
15888 #if VMA_RECORDING_ENABLED
15889     if(allocator->GetRecorder() != VMA_NULL)
15890     {
15891         allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
15892     }
15893 #endif
15894 
15895     allocator->DestroyPool(pool);
15896 }
15897 
vmaGetPoolStats(VmaAllocator allocator,VmaPool pool,VmaPoolStats * pPoolStats)15898 void vmaGetPoolStats(
15899     VmaAllocator allocator,
15900     VmaPool pool,
15901     VmaPoolStats* pPoolStats)
15902 {
15903     VMA_ASSERT(allocator && pool && pPoolStats);
15904 
15905     VMA_DEBUG_GLOBAL_MUTEX_LOCK
15906 
15907     allocator->GetPoolStats(pool, pPoolStats);
15908 }
15909 
vmaMakePoolAllocationsLost(VmaAllocator allocator,VmaPool pool,size_t * pLostAllocationCount)15910 void vmaMakePoolAllocationsLost(
15911     VmaAllocator allocator,
15912     VmaPool pool,
15913     size_t* pLostAllocationCount)
15914 {
15915     VMA_ASSERT(allocator && pool);
15916 
15917     VMA_DEBUG_GLOBAL_MUTEX_LOCK
15918 
15919 #if VMA_RECORDING_ENABLED
15920     if(allocator->GetRecorder() != VMA_NULL)
15921     {
15922         allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
15923     }
15924 #endif
15925 
15926     allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
15927 }
15928 
vmaCheckPoolCorruption(VmaAllocator allocator,VmaPool pool)15929 VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
15930 {
15931     VMA_ASSERT(allocator && pool);
15932 
15933     VMA_DEBUG_GLOBAL_MUTEX_LOCK
15934 
15935     VMA_DEBUG_LOG("vmaCheckPoolCorruption");
15936 
15937     return allocator->CheckPoolCorruption(pool);
15938 }
15939 
vmaAllocateMemory(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)15940 VkResult vmaAllocateMemory(
15941     VmaAllocator allocator,
15942     const VkMemoryRequirements* pVkMemoryRequirements,
15943     const VmaAllocationCreateInfo* pCreateInfo,
15944     VmaAllocation* pAllocation,
15945     VmaAllocationInfo* pAllocationInfo)
15946 {
15947     VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
15948 
15949     VMA_DEBUG_LOG("vmaAllocateMemory");
15950 
15951     VMA_DEBUG_GLOBAL_MUTEX_LOCK
15952 
15953 	VkResult result = allocator->AllocateMemory(
15954         *pVkMemoryRequirements,
15955         false, // requiresDedicatedAllocation
15956         false, // prefersDedicatedAllocation
15957         VK_NULL_HANDLE, // dedicatedBuffer
15958         VK_NULL_HANDLE, // dedicatedImage
15959         *pCreateInfo,
15960         VMA_SUBALLOCATION_TYPE_UNKNOWN,
15961         1, // allocationCount
15962         pAllocation);
15963 
15964 #if VMA_RECORDING_ENABLED
15965     if(allocator->GetRecorder() != VMA_NULL)
15966     {
15967         allocator->GetRecorder()->RecordAllocateMemory(
15968             allocator->GetCurrentFrameIndex(),
15969             *pVkMemoryRequirements,
15970             *pCreateInfo,
15971             *pAllocation);
15972     }
15973 #endif
15974 
15975     if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
15976     {
15977         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
15978     }
15979 
15980 	return result;
15981 }
15982 
vmaAllocateMemoryPages(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,size_t allocationCount,VmaAllocation * pAllocations,VmaAllocationInfo * pAllocationInfo)15983 VkResult vmaAllocateMemoryPages(
15984     VmaAllocator allocator,
15985     const VkMemoryRequirements* pVkMemoryRequirements,
15986     const VmaAllocationCreateInfo* pCreateInfo,
15987     size_t allocationCount,
15988     VmaAllocation* pAllocations,
15989     VmaAllocationInfo* pAllocationInfo)
15990 {
15991     if(allocationCount == 0)
15992     {
15993         return VK_SUCCESS;
15994     }
15995 
15996     VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
15997 
15998     VMA_DEBUG_LOG("vmaAllocateMemoryPages");
15999 
16000     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16001 
16002 	VkResult result = allocator->AllocateMemory(
16003         *pVkMemoryRequirements,
16004         false, // requiresDedicatedAllocation
16005         false, // prefersDedicatedAllocation
16006         VK_NULL_HANDLE, // dedicatedBuffer
16007         VK_NULL_HANDLE, // dedicatedImage
16008         *pCreateInfo,
16009         VMA_SUBALLOCATION_TYPE_UNKNOWN,
16010         allocationCount,
16011         pAllocations);
16012 
16013 #if VMA_RECORDING_ENABLED
16014     if(allocator->GetRecorder() != VMA_NULL)
16015     {
16016         allocator->GetRecorder()->RecordAllocateMemoryPages(
16017             allocator->GetCurrentFrameIndex(),
16018             *pVkMemoryRequirements,
16019             *pCreateInfo,
16020             (uint64_t)allocationCount,
16021             pAllocations);
16022     }
16023 #endif
16024 
16025     if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
16026     {
16027         for(size_t i = 0; i < allocationCount; ++i)
16028         {
16029             allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
16030         }
16031     }
16032 
16033 	return result;
16034 }
16035 
vmaAllocateMemoryForBuffer(VmaAllocator allocator,VkBuffer buffer,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)16036 VkResult vmaAllocateMemoryForBuffer(
16037     VmaAllocator allocator,
16038     VkBuffer buffer,
16039     const VmaAllocationCreateInfo* pCreateInfo,
16040     VmaAllocation* pAllocation,
16041     VmaAllocationInfo* pAllocationInfo)
16042 {
16043     VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
16044 
16045     VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
16046 
16047     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16048 
16049     VkMemoryRequirements vkMemReq = {};
16050     bool requiresDedicatedAllocation = false;
16051     bool prefersDedicatedAllocation = false;
16052     allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
16053         requiresDedicatedAllocation,
16054         prefersDedicatedAllocation);
16055 
16056     VkResult result = allocator->AllocateMemory(
16057         vkMemReq,
16058         requiresDedicatedAllocation,
16059         prefersDedicatedAllocation,
16060         buffer, // dedicatedBuffer
16061         VK_NULL_HANDLE, // dedicatedImage
16062         *pCreateInfo,
16063         VMA_SUBALLOCATION_TYPE_BUFFER,
16064         1, // allocationCount
16065         pAllocation);
16066 
16067 #if VMA_RECORDING_ENABLED
16068     if(allocator->GetRecorder() != VMA_NULL)
16069     {
16070         allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
16071             allocator->GetCurrentFrameIndex(),
16072             vkMemReq,
16073             requiresDedicatedAllocation,
16074             prefersDedicatedAllocation,
16075             *pCreateInfo,
16076             *pAllocation);
16077     }
16078 #endif
16079 
16080     if(pAllocationInfo && result == VK_SUCCESS)
16081     {
16082         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16083     }
16084 
16085 	return result;
16086 }
16087 
vmaAllocateMemoryForImage(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)16088 VkResult vmaAllocateMemoryForImage(
16089     VmaAllocator allocator,
16090     VkImage image,
16091     const VmaAllocationCreateInfo* pCreateInfo,
16092     VmaAllocation* pAllocation,
16093     VmaAllocationInfo* pAllocationInfo)
16094 {
16095     VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
16096 
16097     VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
16098 
16099     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16100 
16101     VkMemoryRequirements vkMemReq = {};
16102     bool requiresDedicatedAllocation = false;
16103     bool prefersDedicatedAllocation  = false;
16104     allocator->GetImageMemoryRequirements(image, vkMemReq,
16105         requiresDedicatedAllocation, prefersDedicatedAllocation);
16106 
16107     VkResult result = allocator->AllocateMemory(
16108         vkMemReq,
16109         requiresDedicatedAllocation,
16110         prefersDedicatedAllocation,
16111         VK_NULL_HANDLE, // dedicatedBuffer
16112         image, // dedicatedImage
16113         *pCreateInfo,
16114         VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
16115         1, // allocationCount
16116         pAllocation);
16117 
16118 #if VMA_RECORDING_ENABLED
16119     if(allocator->GetRecorder() != VMA_NULL)
16120     {
16121         allocator->GetRecorder()->RecordAllocateMemoryForImage(
16122             allocator->GetCurrentFrameIndex(),
16123             vkMemReq,
16124             requiresDedicatedAllocation,
16125             prefersDedicatedAllocation,
16126             *pCreateInfo,
16127             *pAllocation);
16128     }
16129 #endif
16130 
16131     if(pAllocationInfo && result == VK_SUCCESS)
16132     {
16133         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16134     }
16135 
16136 	return result;
16137 }
16138 
vmaFreeMemory(VmaAllocator allocator,VmaAllocation allocation)16139 void vmaFreeMemory(
16140     VmaAllocator allocator,
16141     VmaAllocation allocation)
16142 {
16143     VMA_ASSERT(allocator);
16144 
16145     if(allocation == VK_NULL_HANDLE)
16146     {
16147         return;
16148     }
16149 
16150     VMA_DEBUG_LOG("vmaFreeMemory");
16151 
16152     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16153 
16154 #if VMA_RECORDING_ENABLED
16155     if(allocator->GetRecorder() != VMA_NULL)
16156     {
16157         allocator->GetRecorder()->RecordFreeMemory(
16158             allocator->GetCurrentFrameIndex(),
16159             allocation);
16160     }
16161 #endif
16162 
16163     allocator->FreeMemory(
16164         1, // allocationCount
16165         &allocation);
16166 }
16167 
vmaFreeMemoryPages(VmaAllocator allocator,size_t allocationCount,VmaAllocation * pAllocations)16168 void vmaFreeMemoryPages(
16169     VmaAllocator allocator,
16170     size_t allocationCount,
16171     VmaAllocation* pAllocations)
16172 {
16173     if(allocationCount == 0)
16174     {
16175         return;
16176     }
16177 
16178     VMA_ASSERT(allocator);
16179 
16180     VMA_DEBUG_LOG("vmaFreeMemoryPages");
16181 
16182     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16183 
16184 #if VMA_RECORDING_ENABLED
16185     if(allocator->GetRecorder() != VMA_NULL)
16186     {
16187         allocator->GetRecorder()->RecordFreeMemoryPages(
16188             allocator->GetCurrentFrameIndex(),
16189             (uint64_t)allocationCount,
16190             pAllocations);
16191     }
16192 #endif
16193 
16194     allocator->FreeMemory(allocationCount, pAllocations);
16195 }
16196 
vmaResizeAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize newSize)16197 VkResult vmaResizeAllocation(
16198     VmaAllocator allocator,
16199     VmaAllocation allocation,
16200     VkDeviceSize newSize)
16201 {
16202     VMA_ASSERT(allocator && allocation);
16203 
16204     VMA_DEBUG_LOG("vmaResizeAllocation");
16205 
16206     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16207 
16208 #if VMA_RECORDING_ENABLED
16209     if(allocator->GetRecorder() != VMA_NULL)
16210     {
16211         allocator->GetRecorder()->RecordResizeAllocation(
16212             allocator->GetCurrentFrameIndex(),
16213             allocation,
16214             newSize);
16215     }
16216 #endif
16217 
16218     return allocator->ResizeAllocation(allocation, newSize);
16219 }
16220 
vmaGetAllocationInfo(VmaAllocator allocator,VmaAllocation allocation,VmaAllocationInfo * pAllocationInfo)16221 void vmaGetAllocationInfo(
16222     VmaAllocator allocator,
16223     VmaAllocation allocation,
16224     VmaAllocationInfo* pAllocationInfo)
16225 {
16226     VMA_ASSERT(allocator && allocation && pAllocationInfo);
16227 
16228     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16229 
16230 #if VMA_RECORDING_ENABLED
16231     if(allocator->GetRecorder() != VMA_NULL)
16232     {
16233         allocator->GetRecorder()->RecordGetAllocationInfo(
16234             allocator->GetCurrentFrameIndex(),
16235             allocation);
16236     }
16237 #endif
16238 
16239     allocator->GetAllocationInfo(allocation, pAllocationInfo);
16240 }
16241 
vmaTouchAllocation(VmaAllocator allocator,VmaAllocation allocation)16242 VkBool32 vmaTouchAllocation(
16243     VmaAllocator allocator,
16244     VmaAllocation allocation)
16245 {
16246     VMA_ASSERT(allocator && allocation);
16247 
16248     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16249 
16250 #if VMA_RECORDING_ENABLED
16251     if(allocator->GetRecorder() != VMA_NULL)
16252     {
16253         allocator->GetRecorder()->RecordTouchAllocation(
16254             allocator->GetCurrentFrameIndex(),
16255             allocation);
16256     }
16257 #endif
16258 
16259     return allocator->TouchAllocation(allocation);
16260 }
16261 
vmaSetAllocationUserData(VmaAllocator allocator,VmaAllocation allocation,void * pUserData)16262 void vmaSetAllocationUserData(
16263     VmaAllocator allocator,
16264     VmaAllocation allocation,
16265     void* pUserData)
16266 {
16267     VMA_ASSERT(allocator && allocation);
16268 
16269     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16270 
16271     allocation->SetUserData(allocator, pUserData);
16272 
16273 #if VMA_RECORDING_ENABLED
16274     if(allocator->GetRecorder() != VMA_NULL)
16275     {
16276         allocator->GetRecorder()->RecordSetAllocationUserData(
16277             allocator->GetCurrentFrameIndex(),
16278             allocation,
16279             pUserData);
16280     }
16281 #endif
16282 }
16283 
vmaCreateLostAllocation(VmaAllocator allocator,VmaAllocation * pAllocation)16284 void vmaCreateLostAllocation(
16285     VmaAllocator allocator,
16286     VmaAllocation* pAllocation)
16287 {
16288     VMA_ASSERT(allocator && pAllocation);
16289 
16290     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
16291 
16292     allocator->CreateLostAllocation(pAllocation);
16293 
16294 #if VMA_RECORDING_ENABLED
16295     if(allocator->GetRecorder() != VMA_NULL)
16296     {
16297         allocator->GetRecorder()->RecordCreateLostAllocation(
16298             allocator->GetCurrentFrameIndex(),
16299             *pAllocation);
16300     }
16301 #endif
16302 }
16303 
vmaMapMemory(VmaAllocator allocator,VmaAllocation allocation,void ** ppData)16304 VkResult vmaMapMemory(
16305     VmaAllocator allocator,
16306     VmaAllocation allocation,
16307     void** ppData)
16308 {
16309     VMA_ASSERT(allocator && allocation && ppData);
16310 
16311     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16312 
16313     VkResult res = allocator->Map(allocation, ppData);
16314 
16315 #if VMA_RECORDING_ENABLED
16316     if(allocator->GetRecorder() != VMA_NULL)
16317     {
16318         allocator->GetRecorder()->RecordMapMemory(
16319             allocator->GetCurrentFrameIndex(),
16320             allocation);
16321     }
16322 #endif
16323 
16324     return res;
16325 }
16326 
vmaUnmapMemory(VmaAllocator allocator,VmaAllocation allocation)16327 void vmaUnmapMemory(
16328     VmaAllocator allocator,
16329     VmaAllocation allocation)
16330 {
16331     VMA_ASSERT(allocator && allocation);
16332 
16333     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16334 
16335 #if VMA_RECORDING_ENABLED
16336     if(allocator->GetRecorder() != VMA_NULL)
16337     {
16338         allocator->GetRecorder()->RecordUnmapMemory(
16339             allocator->GetCurrentFrameIndex(),
16340             allocation);
16341     }
16342 #endif
16343 
16344     allocator->Unmap(allocation);
16345 }
16346 
vmaFlushAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)16347 void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
16348 {
16349     VMA_ASSERT(allocator && allocation);
16350 
16351     VMA_DEBUG_LOG("vmaFlushAllocation");
16352 
16353     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16354 
16355     allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
16356 
16357 #if VMA_RECORDING_ENABLED
16358     if(allocator->GetRecorder() != VMA_NULL)
16359     {
16360         allocator->GetRecorder()->RecordFlushAllocation(
16361             allocator->GetCurrentFrameIndex(),
16362             allocation, offset, size);
16363     }
16364 #endif
16365 }
16366 
vmaInvalidateAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)16367 void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
16368 {
16369     VMA_ASSERT(allocator && allocation);
16370 
16371     VMA_DEBUG_LOG("vmaInvalidateAllocation");
16372 
16373     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16374 
16375     allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
16376 
16377 #if VMA_RECORDING_ENABLED
16378     if(allocator->GetRecorder() != VMA_NULL)
16379     {
16380         allocator->GetRecorder()->RecordInvalidateAllocation(
16381             allocator->GetCurrentFrameIndex(),
16382             allocation, offset, size);
16383     }
16384 #endif
16385 }
16386 
vmaCheckCorruption(VmaAllocator allocator,uint32_t memoryTypeBits)16387 VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
16388 {
16389     VMA_ASSERT(allocator);
16390 
16391     VMA_DEBUG_LOG("vmaCheckCorruption");
16392 
16393     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16394 
16395     return allocator->CheckCorruption(memoryTypeBits);
16396 }
16397 
vmaDefragment(VmaAllocator allocator,VmaAllocation * pAllocations,size_t allocationCount,VkBool32 * pAllocationsChanged,const VmaDefragmentationInfo * pDefragmentationInfo,VmaDefragmentationStats * pDefragmentationStats)16398 VkResult vmaDefragment(
16399     VmaAllocator allocator,
16400     VmaAllocation* pAllocations,
16401     size_t allocationCount,
16402     VkBool32* pAllocationsChanged,
16403     const VmaDefragmentationInfo *pDefragmentationInfo,
16404     VmaDefragmentationStats* pDefragmentationStats)
16405 {
16406     // Deprecated interface, reimplemented using new one.
16407 
16408     VmaDefragmentationInfo2 info2 = {};
16409     info2.allocationCount = (uint32_t)allocationCount;
16410     info2.pAllocations = pAllocations;
16411     info2.pAllocationsChanged = pAllocationsChanged;
16412     if(pDefragmentationInfo != VMA_NULL)
16413     {
16414         info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
16415         info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
16416     }
16417     else
16418     {
16419         info2.maxCpuAllocationsToMove = UINT32_MAX;
16420         info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
16421     }
16422     // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
16423 
16424     VmaDefragmentationContext ctx;
16425     VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
16426     if(res == VK_NOT_READY)
16427     {
16428         res = vmaDefragmentationEnd( allocator, ctx);
16429     }
16430     return res;
16431 }
16432 
vmaDefragmentationBegin(VmaAllocator allocator,const VmaDefragmentationInfo2 * pInfo,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)16433 VkResult vmaDefragmentationBegin(
16434     VmaAllocator allocator,
16435     const VmaDefragmentationInfo2* pInfo,
16436     VmaDefragmentationStats* pStats,
16437     VmaDefragmentationContext *pContext)
16438 {
16439     VMA_ASSERT(allocator && pInfo && pContext);
16440 
16441     // Degenerate case: Nothing to defragment.
16442     if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
16443     {
16444         return VK_SUCCESS;
16445     }
16446 
16447     VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
16448     VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
16449     VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
16450     VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
16451 
16452     VMA_DEBUG_LOG("vmaDefragmentationBegin");
16453 
16454     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16455 
16456     VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
16457 
16458 #if VMA_RECORDING_ENABLED
16459     if(allocator->GetRecorder() != VMA_NULL)
16460     {
16461         allocator->GetRecorder()->RecordDefragmentationBegin(
16462             allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
16463     }
16464 #endif
16465 
16466     return res;
16467 }
16468 
vmaDefragmentationEnd(VmaAllocator allocator,VmaDefragmentationContext context)16469 VkResult vmaDefragmentationEnd(
16470     VmaAllocator allocator,
16471     VmaDefragmentationContext context)
16472 {
16473     VMA_ASSERT(allocator);
16474 
16475     VMA_DEBUG_LOG("vmaDefragmentationEnd");
16476 
16477     if(context != VK_NULL_HANDLE)
16478     {
16479         VMA_DEBUG_GLOBAL_MUTEX_LOCK
16480 
16481 #if VMA_RECORDING_ENABLED
16482         if(allocator->GetRecorder() != VMA_NULL)
16483         {
16484             allocator->GetRecorder()->RecordDefragmentationEnd(
16485                 allocator->GetCurrentFrameIndex(), context);
16486         }
16487 #endif
16488 
16489         return allocator->DefragmentationEnd(context);
16490     }
16491     else
16492     {
16493         return VK_SUCCESS;
16494     }
16495 }
16496 
vmaBindBufferMemory(VmaAllocator allocator,VmaAllocation allocation,VkBuffer buffer)16497 VkResult vmaBindBufferMemory(
16498     VmaAllocator allocator,
16499     VmaAllocation allocation,
16500     VkBuffer buffer)
16501 {
16502     VMA_ASSERT(allocator && allocation && buffer);
16503 
16504     VMA_DEBUG_LOG("vmaBindBufferMemory");
16505 
16506     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16507 
16508     return allocator->BindBufferMemory(allocation, buffer);
16509 }
16510 
vmaBindImageMemory(VmaAllocator allocator,VmaAllocation allocation,VkImage image)16511 VkResult vmaBindImageMemory(
16512     VmaAllocator allocator,
16513     VmaAllocation allocation,
16514     VkImage image)
16515 {
16516     VMA_ASSERT(allocator && allocation && image);
16517 
16518     VMA_DEBUG_LOG("vmaBindImageMemory");
16519 
16520     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16521 
16522     return allocator->BindImageMemory(allocation, image);
16523 }
16524 
vmaCreateBuffer(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkBuffer * pBuffer,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)16525 VkResult vmaCreateBuffer(
16526     VmaAllocator allocator,
16527     const VkBufferCreateInfo* pBufferCreateInfo,
16528     const VmaAllocationCreateInfo* pAllocationCreateInfo,
16529     VkBuffer* pBuffer,
16530     VmaAllocation* pAllocation,
16531     VmaAllocationInfo* pAllocationInfo)
16532 {
16533     VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
16534 
16535     if(pBufferCreateInfo->size == 0)
16536     {
16537         return VK_ERROR_VALIDATION_FAILED_EXT;
16538     }
16539 
16540     VMA_DEBUG_LOG("vmaCreateBuffer");
16541 
16542     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16543 
16544     *pBuffer = VK_NULL_HANDLE;
16545     *pAllocation = VK_NULL_HANDLE;
16546 
16547     // 1. Create VkBuffer.
16548     VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
16549         allocator->m_hDevice,
16550         pBufferCreateInfo,
16551         allocator->GetAllocationCallbacks(),
16552         pBuffer);
16553     if(res >= 0)
16554     {
16555         // 2. vkGetBufferMemoryRequirements.
16556         VkMemoryRequirements vkMemReq = {};
16557         bool requiresDedicatedAllocation = false;
16558         bool prefersDedicatedAllocation  = false;
16559         allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
16560             requiresDedicatedAllocation, prefersDedicatedAllocation);
16561 
16562         // Make sure alignment requirements for specific buffer usages reported
16563         // in Physical Device Properties are included in alignment reported by memory requirements.
16564         if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT) != 0)
16565         {
16566            VMA_ASSERT(vkMemReq.alignment %
16567               allocator->m_PhysicalDeviceProperties.limits.minTexelBufferOffsetAlignment == 0);
16568         }
16569         if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) != 0)
16570         {
16571            VMA_ASSERT(vkMemReq.alignment %
16572               allocator->m_PhysicalDeviceProperties.limits.minUniformBufferOffsetAlignment == 0);
16573         }
16574         if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) != 0)
16575         {
16576            VMA_ASSERT(vkMemReq.alignment %
16577               allocator->m_PhysicalDeviceProperties.limits.minStorageBufferOffsetAlignment == 0);
16578         }
16579 
16580         // 3. Allocate memory using allocator.
16581         res = allocator->AllocateMemory(
16582             vkMemReq,
16583             requiresDedicatedAllocation,
16584             prefersDedicatedAllocation,
16585             *pBuffer, // dedicatedBuffer
16586             VK_NULL_HANDLE, // dedicatedImage
16587             *pAllocationCreateInfo,
16588             VMA_SUBALLOCATION_TYPE_BUFFER,
16589             1, // allocationCount
16590             pAllocation);
16591 
16592 #if VMA_RECORDING_ENABLED
16593         if(allocator->GetRecorder() != VMA_NULL)
16594         {
16595             allocator->GetRecorder()->RecordCreateBuffer(
16596                 allocator->GetCurrentFrameIndex(),
16597                 *pBufferCreateInfo,
16598                 *pAllocationCreateInfo,
16599                 *pAllocation);
16600         }
16601 #endif
16602 
16603         if(res >= 0)
16604         {
16605             // 3. Bind buffer with memory.
16606             res = allocator->BindBufferMemory(*pAllocation, *pBuffer);
16607             if(res >= 0)
16608             {
16609                 // All steps succeeded.
16610                 #if VMA_STATS_STRING_ENABLED
16611                     (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
16612                 #endif
16613                 if(pAllocationInfo != VMA_NULL)
16614                 {
16615                     allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16616                 }
16617 
16618                 return VK_SUCCESS;
16619             }
16620             allocator->FreeMemory(
16621                 1, // allocationCount
16622                 pAllocation);
16623             *pAllocation = VK_NULL_HANDLE;
16624             (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
16625             *pBuffer = VK_NULL_HANDLE;
16626             return res;
16627         }
16628         (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
16629         *pBuffer = VK_NULL_HANDLE;
16630         return res;
16631     }
16632     return res;
16633 }
16634 
vmaDestroyBuffer(VmaAllocator allocator,VkBuffer buffer,VmaAllocation allocation)16635 void vmaDestroyBuffer(
16636     VmaAllocator allocator,
16637     VkBuffer buffer,
16638     VmaAllocation allocation)
16639 {
16640     VMA_ASSERT(allocator);
16641 
16642     if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
16643     {
16644         return;
16645     }
16646 
16647     VMA_DEBUG_LOG("vmaDestroyBuffer");
16648 
16649     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16650 
16651 #if VMA_RECORDING_ENABLED
16652     if(allocator->GetRecorder() != VMA_NULL)
16653     {
16654         allocator->GetRecorder()->RecordDestroyBuffer(
16655             allocator->GetCurrentFrameIndex(),
16656             allocation);
16657     }
16658 #endif
16659 
16660     if(buffer != VK_NULL_HANDLE)
16661     {
16662         (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
16663     }
16664 
16665     if(allocation != VK_NULL_HANDLE)
16666     {
16667         allocator->FreeMemory(
16668             1, // allocationCount
16669             &allocation);
16670     }
16671 }
16672 
vmaCreateImage(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkImage * pImage,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)16673 VkResult vmaCreateImage(
16674     VmaAllocator allocator,
16675     const VkImageCreateInfo* pImageCreateInfo,
16676     const VmaAllocationCreateInfo* pAllocationCreateInfo,
16677     VkImage* pImage,
16678     VmaAllocation* pAllocation,
16679     VmaAllocationInfo* pAllocationInfo)
16680 {
16681     VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
16682 
16683     if(pImageCreateInfo->extent.width == 0 ||
16684         pImageCreateInfo->extent.height == 0 ||
16685         pImageCreateInfo->extent.depth == 0 ||
16686         pImageCreateInfo->mipLevels == 0 ||
16687         pImageCreateInfo->arrayLayers == 0)
16688     {
16689         return VK_ERROR_VALIDATION_FAILED_EXT;
16690     }
16691 
16692     VMA_DEBUG_LOG("vmaCreateImage");
16693 
16694     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16695 
16696     *pImage = VK_NULL_HANDLE;
16697     *pAllocation = VK_NULL_HANDLE;
16698 
16699     // 1. Create VkImage.
16700     VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
16701         allocator->m_hDevice,
16702         pImageCreateInfo,
16703         allocator->GetAllocationCallbacks(),
16704         pImage);
16705     if(res >= 0)
16706     {
16707         VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
16708             VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
16709             VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
16710 
16711         // 2. Allocate memory using allocator.
16712         VkMemoryRequirements vkMemReq = {};
16713         bool requiresDedicatedAllocation = false;
16714         bool prefersDedicatedAllocation  = false;
16715         allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
16716             requiresDedicatedAllocation, prefersDedicatedAllocation);
16717 
16718         res = allocator->AllocateMemory(
16719             vkMemReq,
16720             requiresDedicatedAllocation,
16721             prefersDedicatedAllocation,
16722             VK_NULL_HANDLE, // dedicatedBuffer
16723             *pImage, // dedicatedImage
16724             *pAllocationCreateInfo,
16725             suballocType,
16726             1, // allocationCount
16727             pAllocation);
16728 
16729 #if VMA_RECORDING_ENABLED
16730         if(allocator->GetRecorder() != VMA_NULL)
16731         {
16732             allocator->GetRecorder()->RecordCreateImage(
16733                 allocator->GetCurrentFrameIndex(),
16734                 *pImageCreateInfo,
16735                 *pAllocationCreateInfo,
16736                 *pAllocation);
16737         }
16738 #endif
16739 
16740         if(res >= 0)
16741         {
16742             // 3. Bind image with memory.
16743             res = allocator->BindImageMemory(*pAllocation, *pImage);
16744             if(res >= 0)
16745             {
16746                 // All steps succeeded.
16747                 #if VMA_STATS_STRING_ENABLED
16748                     (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
16749                 #endif
16750                 if(pAllocationInfo != VMA_NULL)
16751                 {
16752                     allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16753                 }
16754 
16755                 return VK_SUCCESS;
16756             }
16757             allocator->FreeMemory(
16758                 1, // allocationCount
16759                 pAllocation);
16760             *pAllocation = VK_NULL_HANDLE;
16761             (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
16762             *pImage = VK_NULL_HANDLE;
16763             return res;
16764         }
16765         (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
16766         *pImage = VK_NULL_HANDLE;
16767         return res;
16768     }
16769     return res;
16770 }
16771 
vmaDestroyImage(VmaAllocator allocator,VkImage image,VmaAllocation allocation)16772 void vmaDestroyImage(
16773     VmaAllocator allocator,
16774     VkImage image,
16775     VmaAllocation allocation)
16776 {
16777     VMA_ASSERT(allocator);
16778 
16779     if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
16780     {
16781         return;
16782     }
16783 
16784     VMA_DEBUG_LOG("vmaDestroyImage");
16785 
16786     VMA_DEBUG_GLOBAL_MUTEX_LOCK
16787 
16788 #if VMA_RECORDING_ENABLED
16789     if(allocator->GetRecorder() != VMA_NULL)
16790     {
16791         allocator->GetRecorder()->RecordDestroyImage(
16792             allocator->GetCurrentFrameIndex(),
16793             allocation);
16794     }
16795 #endif
16796 
16797     if(image != VK_NULL_HANDLE)
16798     {
16799         (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
16800     }
16801     if(allocation != VK_NULL_HANDLE)
16802     {
16803         allocator->FreeMemory(
16804             1, // allocationCount
16805             &allocation);
16806     }
16807 }
16808 
16809 #endif // #ifdef VMA_IMPLEMENTATION
16810