• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.0.0</b> (2018-03-19)
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   - \subpage defragmentation
59   - \subpage lost_allocations
60   - \subpage statistics
61     - [Numeric statistics](@ref statistics_numeric_statistics)
62     - [JSON dump](@ref statistics_json_dump)
63   - \subpage allocation_annotation
64     - [Allocation user data](@ref allocation_user_data)
65     - [Allocation names](@ref allocation_names)
66 - \subpage usage_patterns
67   - [Simple patterns](@ref usage_patterns_simple)
68   - [Advanced patterns](@ref usage_patterns_advanced)
69 - \subpage configuration
70   - [Pointers to Vulkan functions](@ref config_Vulkan_functions)
71   - [Custom host memory allocator](@ref custom_memory_allocator)
72   - [Device memory allocation callbacks](@ref allocation_callbacks)
73   - [Device heap memory limit](@ref heap_memory_limit)
74   - \subpage vk_khr_dedicated_allocation
75 - \subpage general_considerations
76   - [Thread safety](@ref general_considerations_thread_safety)
77   - [Allocation algorithm](@ref general_considerations_allocation_algorithm)
78   - [Features not supported](@ref general_considerations_features_not_supported)
79 
80 \section main_see_also See also
81 
82 - [Product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-memory-allocator/)
83 - [Source repository on GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)
84 
85 
86 
87 
88 \page quick_start Quick start
89 
90 \section quick_start_project_setup Project setup
91 
92 Vulkan Memory Allocator comes in form of a single header file.
93 You don't need to build it as a separate library project.
94 You can add this file directly to your project and submit it to code repository next to your other source files.
95 
96 "Single header" doesn't mean that everything is contained in C/C++ declarations,
97 like it tends to be in case of inline functions or C++ templates.
98 It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro.
99 If you don't do it properly, you will get linker errors.
100 
101 To do it properly:
102 
103 -# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library.
104    This includes declarations of all members of the library.
105 -# In exacly one CPP file define following macro before this include.
106    It enables also internal definitions.
107 
108 \code
109 #define VMA_IMPLEMENTATION
110 #include "vk_mem_alloc.h"
111 \endcode
112 
113 It may be a good idea to create dedicated CPP file just for this purpose.
114 
115 \section quick_start_initialization Initialization
116 
117 At program startup:
118 
119 -# Initialize Vulkan to have `VkPhysicalDevice` and `VkDevice` object.
120 -# Fill VmaAllocatorCreateInfo structure and create #VmaAllocator object by
121    calling vmaCreateAllocator().
122 
123 \code
124 VmaAllocatorCreateInfo allocatorInfo = {};
125 allocatorInfo.physicalDevice = physicalDevice;
126 allocatorInfo.device = device;
127 
128 VmaAllocator allocator;
129 vmaCreateAllocator(&allocatorInfo, &allocator);
130 \endcode
131 
132 \section quick_start_resource_allocation Resource allocation
133 
134 When you want to create a buffer or image:
135 
136 -# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure.
137 -# Fill VmaAllocationCreateInfo structure.
138 -# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory
139    already allocated and bound to it.
140 
141 \code
142 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
143 bufferInfo.size = 65536;
144 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
145 
146 VmaAllocationCreateInfo allocInfo = {};
147 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
148 
149 VkBuffer buffer;
150 VmaAllocation allocation;
151 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
152 \endcode
153 
154 Don't forget to destroy your objects when no longer needed:
155 
156 \code
157 vmaDestroyBuffer(allocator, buffer, allocation);
158 vmaDestroyAllocator(allocator);
159 \endcode
160 
161 
162 \page choosing_memory_type Choosing memory type
163 
164 Physical devices in Vulkan support various combinations of memory heaps and
165 types. Help with choosing correct and optimal memory type for your specific
166 resource is one of the key features of this library. You can use it by filling
167 appropriate members of VmaAllocationCreateInfo structure, as described below.
168 You can also combine multiple methods.
169 
170 -# If you just want to find memory type index that meets your requirements, you
171    can use function vmaFindMemoryTypeIndex().
172 -# If you want to allocate a region of device memory without association with any
173    specific image or buffer, you can use function vmaAllocateMemory(). Usage of
174    this function is not recommended and usually not needed.
175 -# If you already have a buffer or an image created, you want to allocate memory
176    for it and then you will bind it yourself, you can use function
177    vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage().
178    For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory().
179 -# If you want to create a buffer or an image, allocate memory for it and bind
180    them together, all in one call, you can use function vmaCreateBuffer(),
181    vmaCreateImage(). This is the recommended way to use this library.
182 
183 When using 3. or 4., the library internally queries Vulkan for memory types
184 supported for that buffer or image (function `vkGetBufferMemoryRequirements()`)
185 and uses only one of these types.
186 
187 If no memory type can be found that meets all the requirements, these functions
188 return `VK_ERROR_FEATURE_NOT_PRESENT`.
189 
190 You can leave VmaAllocationCreateInfo structure completely filled with zeros.
191 It means no requirements are specified for memory type.
192 It is valid, although not very useful.
193 
194 \section choosing_memory_type_usage Usage
195 
196 The easiest way to specify memory requirements is to fill member
197 VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage.
198 It defines high level, common usage types.
199 For more details, see description of this enum.
200 
201 For example, if you want to create a uniform buffer that will be filled using
202 transfer only once or infrequently and used for rendering every frame, you can
203 do it using following code:
204 
205 \code
206 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
207 bufferInfo.size = 65536;
208 bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
209 
210 VmaAllocationCreateInfo allocInfo = {};
211 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
212 
213 VkBuffer buffer;
214 VmaAllocation allocation;
215 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
216 \endcode
217 
218 \section choosing_memory_type_required_preferred_flags Required and preferred flags
219 
220 You can specify more detailed requirements by filling members
221 VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags
222 with a combination of bits from enum `VkMemoryPropertyFlags`. For example,
223 if you want to create a buffer that will be persistently mapped on host (so it
224 must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`,
225 use following code:
226 
227 \code
228 VmaAllocationCreateInfo allocInfo = {};
229 allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
230 allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
231 allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
232 
233 VkBuffer buffer;
234 VmaAllocation allocation;
235 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
236 \endcode
237 
238 A memory type is chosen that has all the required flags and as many preferred
239 flags set as possible.
240 
241 If you use VmaAllocationCreateInfo::usage, it is just internally converted to
242 a set of required and preferred flags.
243 
244 \section choosing_memory_type_explicit_memory_types Explicit memory types
245 
246 If you inspected memory types available on the physical device and you have
247 a preference for memory types that you want to use, you can fill member
248 VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set
249 means that a memory type with that index is allowed to be used for the
250 allocation. Special value 0, just like `UINT32_MAX`, means there are no
251 restrictions to memory type index.
252 
253 Please note that this member is NOT just a memory type index.
254 Still you can use it to choose just one, specific memory type.
255 For example, if you already determined that your buffer should be created in
256 memory type 2, use following code:
257 
258 \code
259 uint32_t memoryTypeIndex = 2;
260 
261 VmaAllocationCreateInfo allocInfo = {};
262 allocInfo.memoryTypeBits = 1u << memoryTypeIndex;
263 
264 VkBuffer buffer;
265 VmaAllocation allocation;
266 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
267 \endcode
268 
269 \section choosing_memory_type_custom_memory_pools Custom memory pools
270 
271 If you allocate from custom memory pool, all the ways of specifying memory
272 requirements described above are not applicable and the aforementioned members
273 of VmaAllocationCreateInfo structure are ignored. Memory type is selected
274 explicitly when creating the pool and then used to make all the allocations from
275 that pool. For further details, see \ref custom_memory_pools.
276 
277 
278 \page memory_mapping Memory mapping
279 
280 To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`,
281 to be able to read from it or write to it in CPU code.
282 Mapping is possible only of memory allocated from a memory type that has
283 `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag.
284 Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose.
285 You can use them directly with memory allocated by this library,
286 but it is not recommended because of following issue:
287 Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed.
288 This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan.
289 Because of this, Vulkan Memory Allocator provides following facilities:
290 
291 \section memory_mapping_mapping_functions Mapping functions
292 
293 The library provides following functions for mapping of a specific #VmaAllocation: vmaMapMemory(), vmaUnmapMemory().
294 They are safer and more convenient to use than standard Vulkan functions.
295 You can map an allocation multiple times simultaneously - mapping is reference-counted internally.
296 You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block.
297 They way it's implemented is that the library always maps entire memory block, not just region of the allocation.
298 For further details, see description of vmaMapMemory() function.
299 Example:
300 
301 \code
302 // Having these objects initialized:
303 
304 struct ConstantBuffer
305 {
306     ...
307 };
308 ConstantBuffer constantBufferData;
309 
310 VmaAllocator allocator;
311 VmaBuffer constantBuffer;
312 VmaAllocation constantBufferAllocation;
313 
314 // You can map and fill your buffer using following code:
315 
316 void* mappedData;
317 vmaMapMemory(allocator, constantBufferAllocation, &mappedData);
318 memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
319 vmaUnmapMemory(allocator, constantBufferAllocation);
320 \endcode
321 
322 \section memory_mapping_persistently_mapped_memory Persistently mapped memory
323 
324 Kepping your memory persistently mapped is generally OK in Vulkan.
325 You don't need to unmap it before using its data on the GPU.
326 The library provides a special feature designed for that:
327 Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in
328 VmaAllocationCreateInfo::flags stay mapped all the time,
329 so you can just access CPU pointer to it any time
330 without a need to call any "map" or "unmap" function.
331 Example:
332 
333 \code
334 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
335 bufCreateInfo.size = sizeof(ConstantBuffer);
336 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
337 
338 VmaAllocationCreateInfo allocCreateInfo = {};
339 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
340 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
341 
342 VkBuffer buf;
343 VmaAllocation alloc;
344 VmaAllocationInfo allocInfo;
345 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
346 
347 // Buffer is already mapped. You can access its memory.
348 memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
349 \endcode
350 
351 There are some exceptions though, when you should consider mapping memory only for a short period of time:
352 
353 - When operating system is Windows 7 or 8.x (Windows 10 is not affected because it uses WDDM2),
354   device is discrete AMD GPU,
355   and memory type is the special 256 MiB pool of `DEVICE_LOCAL + HOST_VISIBLE` memory
356   (selected when you use #VMA_MEMORY_USAGE_CPU_TO_GPU),
357   then whenever a memory block allocated from this memory type stays mapped
358   for the time of any call to `vkQueueSubmit()` or `vkQueuePresentKHR()`, this
359   block is migrated by WDDM to system RAM, which degrades performance. It doesn't
360   matter if that particular memory block is actually used by the command buffer
361   being submitted.
362 - Keeping many large memory blocks mapped may impact performance or stability of some debugging tools.
363 
364 \section memory_mapping_cache_control Cache control
365 
366 Memory in Vulkan doesn't need to be unmapped before using it on GPU,
367 but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set,
368 you need to manually invalidate cache before reading of mapped pointer
369 using function `vkvkInvalidateMappedMemoryRanges()`
370 and flush cache after writing to mapped pointer
371 using function `vkFlushMappedMemoryRanges()`.
372 Example:
373 
374 \code
375 memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
376 
377 VkMemoryPropertyFlags memFlags;
378 vmaGetMemoryTypeProperties(allocator, allocInfo.memoryType, &memFlags);
379 if((memFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0)
380 {
381     VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
382     memRange.memory = allocInfo.deviceMemory;
383     memRange.offset = allocInfo.offset;
384     memRange.size   = allocInfo.size;
385     vkFlushMappedMemoryRanges(device, 1, &memRange);
386 }
387 \endcode
388 
389 Please note that memory allocated with #VMA_MEMORY_USAGE_CPU_ONLY is guaranteed to be host coherent.
390 
391 Also, Windows drivers from all 3 PC GPU vendors (AMD, Intel, NVIDIA)
392 currently provide `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag on all memory types that are
393 `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`, so on this platform you may not need to bother.
394 
395 \section memory_mapping_finding_if_memory_mappable Finding out if memory is mappable
396 
397 It may happen that your allocation ends up in memory that is `HOST_VISIBLE` (available for mapping)
398 despite it wasn't explicitly requested.
399 For example, application may work on integrated graphics with unified memory (like Intel) or
400 allocation from video memory might have failed, so the library chose system memory as fallback.
401 
402 You can detect this case and map such allocation to access its memory on CPU directly,
403 instead of launching a transfer operation.
404 In order to do that: inspect `allocInfo.memoryType`, call vmaGetMemoryTypeProperties(),
405 and look for `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag in properties of that memory type.
406 
407 \code
408 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
409 bufCreateInfo.size = sizeof(ConstantBuffer);
410 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
411 
412 VmaAllocationCreateInfo allocCreateInfo = {};
413 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
414 
415 VkBuffer buf;
416 VmaAllocation alloc;
417 VmaAllocationInfo allocInfo;
418 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
419 
420 VkMemoryPropertyFlags memFlags;
421 vmaGetMemoryTypeProperties(allocator, allocInfo.memoryType, &memFlags);
422 if((memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
423 {
424     // Allocation ended up in mappable memory. You can map it and access it directly.
425     void* mappedData;
426     vmaMapMemory(allocator, alloc, &mappedData);
427     memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
428     vmaUnmapMemory(allocator, alloc);
429 }
430 else
431 {
432     // Allocation ended up in non-mappable memory.
433     // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
434 }
435 \endcode
436 
437 You can even use #VMA_ALLOCATION_CREATE_MAPPED_BIT flag while creating allocations
438 that are not necessarily `HOST_VISIBLE` (e.g. using #VMA_MEMORY_USAGE_GPU_ONLY).
439 If the allocation ends up in memory type that is `HOST_VISIBLE`, it will be persistently mapped and you can use it directly.
440 If not, the flag is just ignored.
441 Example:
442 
443 \code
444 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
445 bufCreateInfo.size = sizeof(ConstantBuffer);
446 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
447 
448 VmaAllocationCreateInfo allocCreateInfo = {};
449 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
450 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
451 
452 VkBuffer buf;
453 VmaAllocation alloc;
454 VmaAllocationInfo allocInfo;
455 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
456 
457 if(allocInfo.pUserData != nullptr)
458 {
459     // Allocation ended up in mappable memory.
460     // It's persistently mapped. You can access it directly.
461     memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
462 }
463 else
464 {
465     // Allocation ended up in non-mappable memory.
466     // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
467 }
468 \endcode
469 
470 
471 \page custom_memory_pools Custom memory pools
472 
473 A memory pool contains a number of `VkDeviceMemory` blocks.
474 The library automatically creates and manages default pool for each memory type available on the device.
475 Default memory pool automatically grows in size.
476 Size of allocated blocks is also variable and managed automatically.
477 
478 You can create custom pool and allocate memory out of it.
479 It can be useful if you want to:
480 
481 - Keep certain kind of allocations separate from others.
482 - Enforce particular, fixed size of Vulkan memory blocks.
483 - Limit maximum amount of Vulkan memory allocated for that pool.
484 - Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool.
485 
486 To use custom memory pools:
487 
488 -# Fill VmaPoolCreateInfo structure.
489 -# Call vmaCreatePool() to obtain #VmaPool handle.
490 -# When making an allocation, set VmaAllocationCreateInfo::pool to this handle.
491    You don't need to specify any other parameters of this structure, like usage.
492 
493 Example:
494 
495 \code
496 // Create a pool that can have at most 2 blocks, 128 MiB each.
497 VmaPoolCreateInfo poolCreateInfo = {};
498 poolCreateInfo.memoryTypeIndex = ...
499 poolCreateInfo.blockSize = 128ull * 1024 * 1024;
500 poolCreateInfo.maxBlockCount = 2;
501 
502 VmaPool pool;
503 vmaCreatePool(allocator, &poolCreateInfo, &pool);
504 
505 // Allocate a buffer out of it.
506 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
507 bufCreateInfo.size = 1024;
508 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
509 
510 VmaAllocationCreateInfo allocCreateInfo = {};
511 allocCreateInfo.pool = pool;
512 
513 VkBuffer buf;
514 VmaAllocation alloc;
515 VmaAllocationInfo allocInfo;
516 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
517 \endcode
518 
519 You have to free all allocations made from this pool before destroying it.
520 
521 \code
522 vmaDestroyBuffer(allocator, buf, alloc);
523 vmaDestroyPool(allocator, pool);
524 \endcode
525 
526 \section custom_memory_pools_MemTypeIndex Choosing memory type index
527 
528 When creating a pool, you must explicitly specify memory type index.
529 To find the one suitable for your buffers or images, you can use helper functions
530 vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo().
531 You need to provide structures with example parameters of buffers or images
532 that you are going to create in that pool.
533 
534 \code
535 VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
536 exampleBufCreateInfo.size = 1024; // Whatever.
537 exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; // Change if needed.
538 
539 VmaAllocationCreateInfo allocCreateInfo = {};
540 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; // Change if needed.
541 
542 uint32_t memTypeIndex;
543 vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex);
544 
545 VmaPoolCreateInfo poolCreateInfo = {};
546 poolCreateInfo.memoryTypeIndex = memTypeIndex;
547 // ...
548 \endcode
549 
550 When creating buffers/images allocated in that pool, provide following parameters:
551 
552 - `VkBufferCreateInfo`: Prefer to pass same parameters as above.
553   Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior.
554   Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers
555   or the other way around.
556 - VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member.
557   Other members are ignored anyway.
558 
559 
560 \page defragmentation Defragmentation
561 
562 Interleaved allocations and deallocations of many objects of varying size can
563 cause fragmentation, which can lead to a situation where the library is unable
564 to find a continuous range of free memory for a new allocation despite there is
565 enough free space, just scattered across many small free ranges between existing
566 allocations.
567 
568 To mitigate this problem, you can use vmaDefragment(). Given set of allocations,
569 this function can move them to compact used memory, ensure more continuous free
570 space and possibly also free some `VkDeviceMemory`. It can work only on
571 allocations made from memory type that is `HOST_VISIBLE`. Allocations are
572 modified to point to the new `VkDeviceMemory` and offset. Data in this memory is
573 also `memmove`-ed to the new place. However, if you have images or buffers bound
574 to these allocations (and you certainly do), you need to destroy, recreate, and
575 bind them to the new place in memory.
576 
577 For further details and example code, see documentation of function
578 vmaDefragment().
579 
580 \page lost_allocations Lost allocations
581 
582 If your game oversubscribes video memory, if may work OK in previous-generation
583 graphics APIs (DirectX 9, 10, 11, OpenGL) because resources are automatically
584 paged to system RAM. In Vulkan you can't do it because when you run out of
585 memory, an allocation just fails. If you have more data (e.g. textures) that can
586 fit into VRAM and you don't need it all at once, you may want to upload them to
587 GPU on demand and "push out" ones that are not used for a long time to make room
588 for the new ones, effectively using VRAM (or a cartain memory pool) as a form of
589 cache. Vulkan Memory Allocator can help you with that by supporting a concept of
590 "lost allocations".
591 
592 To create an allocation that can become lost, include #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT
593 flag in VmaAllocationCreateInfo::flags. Before using a buffer or image bound to
594 such allocation in every new frame, you need to query it if it's not lost.
595 To check it, call vmaTouchAllocation().
596 If the allocation is lost, you should not use it or buffer/image bound to it.
597 You mustn't forget to destroy this allocation and this buffer/image.
598 vmaGetAllocationInfo() can also be used for checking status of the allocation.
599 Allocation is lost when returned VmaAllocationInfo::deviceMemory == `VK_NULL_HANDLE`.
600 
601 To create an allocation that can make some other allocations lost to make room
602 for it, use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag. You will
603 usually use both flags #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT and
604 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT at the same time.
605 
606 Warning! Current implementation uses quite naive, brute force algorithm,
607 which can make allocation calls that use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT
608 flag quite slow. A new, more optimal algorithm and data structure to speed this
609 up is planned for the future.
610 
611 <b>Q: When interleaving creation of new allocations with usage of existing ones,
612 how do you make sure that an allocation won't become lost while it's used in the
613 current frame?</b>
614 
615 It is ensured because vmaTouchAllocation() / vmaGetAllocationInfo() not only returns allocation
616 status/parameters and checks whether it's not lost, but when it's not, it also
617 atomically marks it as used in the current frame, which makes it impossible to
618 become lost in that frame. It uses lockless algorithm, so it works fast and
619 doesn't involve locking any internal mutex.
620 
621 <b>Q: What if my allocation may still be in use by the GPU when it's rendering a
622 previous frame while I already submit new frame on the CPU?</b>
623 
624 You can make sure that allocations "touched" by vmaTouchAllocation() / vmaGetAllocationInfo() will not
625 become lost for a number of additional frames back from the current one by
626 specifying this number as VmaAllocatorCreateInfo::frameInUseCount (for default
627 memory pool) and VmaPoolCreateInfo::frameInUseCount (for custom pool).
628 
629 <b>Q: How do you inform the library when new frame starts?</b>
630 
631 You need to call function vmaSetCurrentFrameIndex().
632 
633 Example code:
634 
635 \code
636 struct MyBuffer
637 {
638     VkBuffer m_Buf = nullptr;
639     VmaAllocation m_Alloc = nullptr;
640 
641     // Called when the buffer is really needed in the current frame.
642     void EnsureBuffer();
643 };
644 
645 void MyBuffer::EnsureBuffer()
646 {
647     // Buffer has been created.
648     if(m_Buf != VK_NULL_HANDLE)
649     {
650         // Check if its allocation is not lost + mark it as used in current frame.
651         if(vmaTouchAllocation(allocator, m_Alloc))
652         {
653             // It's all OK - safe to use m_Buf.
654             return;
655         }
656     }
657 
658     // Buffer not yet exists or lost - destroy and recreate it.
659 
660     vmaDestroyBuffer(allocator, m_Buf, m_Alloc);
661 
662     VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
663     bufCreateInfo.size = 1024;
664     bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
665 
666     VmaAllocationCreateInfo allocCreateInfo = {};
667     allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
668     allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
669         VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
670 
671     vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &m_Buf, &m_Alloc, nullptr);
672 }
673 \endcode
674 
675 When using lost allocations, you may see some Vulkan validation layer warnings
676 about overlapping regions of memory bound to different kinds of buffers and
677 images. This is still valid as long as you implement proper handling of lost
678 allocations (like in the example above) and don't use them.
679 
680 You can create an allocation that is already in lost state from the beginning using function
681 vmaCreateLostAllocation(). It may be useful if you need a "dummy" allocation that is not null.
682 
683 You can call function vmaMakePoolAllocationsLost() to set all eligible allocations
684 in a specified custom pool to lost state.
685 Allocations that have been "touched" in current frame or VmaPoolCreateInfo::frameInUseCount frames back
686 cannot become lost.
687 
688 
689 \page statistics Statistics
690 
691 This library contains functions that return information about its internal state,
692 especially the amount of memory allocated from Vulkan.
693 Please keep in mind that these functions need to traverse all internal data structures
694 to gather these information, so they may be quite time-consuming.
695 Don't call them too often.
696 
697 \section statistics_numeric_statistics Numeric statistics
698 
699 You can query for overall statistics of the allocator using function vmaCalculateStats().
700 Information are returned using structure #VmaStats.
701 It contains #VmaStatInfo - number of allocated blocks, number of allocations
702 (occupied ranges in these blocks), number of unused (free) ranges in these blocks,
703 number of bytes used and unused (but still allocated from Vulkan) and other information.
704 They are summed across memory heaps, memory types and total for whole allocator.
705 
706 You can query for statistics of a custom pool using function vmaGetPoolStats().
707 Information are returned using structure #VmaPoolStats.
708 
709 You can query for information about specific allocation using function vmaGetAllocationInfo().
710 It fill structure #VmaAllocationInfo.
711 
712 \section statistics_json_dump JSON dump
713 
714 You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString().
715 The result is guaranteed to be correct JSON.
716 It uses ANSI encoding.
717 Any strings provided by user (see [Allocation names](@ref allocation_names))
718 are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding,
719 this JSON string can be treated as using this encoding.
720 It must be freed using function vmaFreeStatsString().
721 
722 The format of this JSON string is not part of official documentation of the library,
723 but it will not change in backward-incompatible way without increasing library major version number
724 and appropriate mention in changelog.
725 
726 The JSON string contains all the data that can be obtained using vmaCalculateStats().
727 It can also contain detailed map of allocated memory blocks and their regions -
728 free and occupied by allocations.
729 This allows e.g. to visualize the memory or assess fragmentation.
730 
731 
732 \page allocation_annotation Allocation names and user data
733 
734 \section allocation_user_data Allocation user data
735 
736 You can annotate allocations with your own information, e.g. for debugging purposes.
737 To do that, fill VmaAllocationCreateInfo::pUserData field when creating
738 an allocation. It's an opaque `void*` pointer. You can use it e.g. as a pointer,
739 some handle, index, key, ordinal number or any other value that would associate
740 the allocation with your custom metadata.
741 
742 \code
743 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
744 // Fill bufferInfo...
745 
746 MyBufferMetadata* pMetadata = CreateBufferMetadata();
747 
748 VmaAllocationCreateInfo allocCreateInfo = {};
749 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
750 allocCreateInfo.pUserData = pMetadata;
751 
752 VkBuffer buffer;
753 VmaAllocation allocation;
754 vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, nullptr);
755 \endcode
756 
757 The pointer may be later retrieved as VmaAllocationInfo::pUserData:
758 
759 \code
760 VmaAllocationInfo allocInfo;
761 vmaGetAllocationInfo(allocator, allocation, &allocInfo);
762 MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData;
763 \endcode
764 
765 It can also be changed using function vmaSetAllocationUserData().
766 
767 Values of (non-zero) allocations' `pUserData` are printed in JSON report created by
768 vmaBuildStatsString(), in hexadecimal form.
769 
770 \section allocation_names Allocation names
771 
772 There is alternative mode available where `pUserData` pointer is used to point to
773 a null-terminated string, giving a name to the allocation. To use this mode,
774 set #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT flag in VmaAllocationCreateInfo::flags.
775 Then `pUserData` passed as VmaAllocationCreateInfo::pUserData or argument to
776 vmaSetAllocationUserData() must be either null or pointer to a null-terminated string.
777 The library creates internal copy of the string, so the pointer you pass doesn't need
778 to be valid for whole lifetime of the allocation. You can free it after the call.
779 
780 \code
781 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
782 // Fill imageInfo...
783 
784 std::string imageName = "Texture: ";
785 imageName += fileName;
786 
787 VmaAllocationCreateInfo allocCreateInfo = {};
788 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
789 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
790 allocCreateInfo.pUserData = imageName.c_str();
791 
792 VkImage image;
793 VmaAllocation allocation;
794 vmaCreateImage(allocator, &imageInfo, &allocCreateInfo, &image, &allocation, nullptr);
795 \endcode
796 
797 The value of `pUserData` pointer of the allocation will be different than the one
798 you passed when setting allocation's name - pointing to a buffer managed
799 internally that holds copy of the string.
800 
801 \code
802 VmaAllocationInfo allocInfo;
803 vmaGetAllocationInfo(allocator, allocation, &allocInfo);
804 const char* imageName = (const char*)allocInfo.pUserData;
805 printf("Image name: %s\n", imageName);
806 \endcode
807 
808 That string is also printed in JSON report created by vmaBuildStatsString().
809 
810 
811 \page usage_patterns Recommended usage patterns
812 
813 \section usage_patterns_simple Simple patterns
814 
815 \subsection usage_patterns_simple_render_targets Render targets
816 
817 <b>When:</b>
818 Any resources that you frequently write and read on GPU,
819 e.g. images used as color attachments (aka "render targets"), depth-stencil attachments,
820 images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)").
821 
822 <b>What to do:</b>
823 Create them in video memory that is fastest to access from GPU using
824 #VMA_MEMORY_USAGE_GPU_ONLY.
825 
826 Consider using [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension
827 and/or manually creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
828 especially if they are large or if you plan to destroy and recreate them e.g. when
829 display resolution changes.
830 Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later.
831 
832 \subsection usage_patterns_simple_immutable_resources Immutable resources
833 
834 <b>When:</b>
835 Any resources that you fill on CPU only once (aka "immutable") or infrequently
836 and then read frequently on GPU,
837 e.g. textures, vertex and index buffers, constant buffers that don't change often.
838 
839 <b>What to do:</b>
840 Create them in video memory that is fastest to access from GPU using
841 #VMA_MEMORY_USAGE_GPU_ONLY.
842 
843 To initialize content of such resource, create a CPU-side (aka "staging") copy of it
844 in system memory - #VMA_MEMORY_USAGE_CPU_ONLY, map it, fill it,
845 and submit a transfer from it to the GPU resource.
846 You can keep the staging copy if you need it for another upload transfer in the future.
847 If you don't, you can destroy it or reuse this buffer for uploading different resource
848 after the transfer finishes.
849 
850 Prefer to create just buffers in system memory rather than images, even for uploading textures.
851 Use `vkCmdCopyBufferToImage()`.
852 Dont use images with `VK_IMAGE_TILING_LINEAR`.
853 
854 \subsection usage_patterns_dynamic_resources Dynamic resources
855 
856 <b>When:</b>
857 Any resources that change frequently (aka "dynamic"), e.g. every frame or every draw call,
858 written on CPU, read on GPU.
859 
860 <b>What to do:</b>
861 Create them using #VMA_MEMORY_USAGE_CPU_TO_GPU.
862 You can map it and write to it directly on CPU, as well as read from it on GPU.
863 
864 This is a more complex situation. Different solutions are possible,
865 and the best one depends on specific GPU type, but you can use this simple approach for the start.
866 Prefer to write to such resource sequentially (e.g. using `memcpy`).
867 Don't perform random access or any reads from it, as it may be very slow.
868 
869 \subsection usage_patterns_readback Readback
870 
871 <b>When:</b>
872 Resources that contain data written by GPU that you want to read back on CPU,
873 e.g. results of some computations.
874 
875 <b>What to do:</b>
876 Create them using #VMA_MEMORY_USAGE_GPU_TO_CPU.
877 You can write to them directly on GPU, as well as map and read them on CPU.
878 
879 \section usage_patterns_advanced Advanced patterns
880 
881 \subsection usage_patterns_integrated_graphics Detecting integrated graphics
882 
883 You can support integrated graphics (like Intel HD Graphics, AMD APU) better
884 by detecting it in Vulkan.
885 To do it, call `vkGetPhysicalDeviceProperties()`, inspect
886 `VkPhysicalDeviceProperties::deviceType` and look for `VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU`.
887 When you find it, you can assume that memory is unified and all memory types are equally fast
888 to access from GPU, regardless of `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
889 
890 You can then sum up sizes of all available memory heaps and treat them as useful for
891 your GPU resources, instead of only `DEVICE_LOCAL` ones.
892 You can also prefer to create your resources in memory types that are `HOST_VISIBLE` to map them
893 directly instead of submitting explicit transfer (see below).
894 
895 \subsection usage_patterns_direct_vs_transfer Direct access versus transfer
896 
897 For resources that you frequently write on CPU and read on GPU, many solutions are possible:
898 
899 -# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
900    second copy in system memory using #VMA_MEMORY_USAGE_CPU_ONLY and submit explicit tranfer each time.
901 -# Create just single copy using #VMA_MEMORY_USAGE_CPU_TO_GPU, map it and fill it on CPU,
902    read it directly on GPU.
903 -# Create just single copy using #VMA_MEMORY_USAGE_CPU_ONLY, map it and fill it on CPU,
904    read it directly on GPU.
905 
906 Which solution is the most efficient depends on your resource and especially on the GPU.
907 It is best to measure it and then make the decision.
908 Some general recommendations:
909 
910 - On integrated graphics use (2) or (3) to avoid unnecesary time and memory overhead
911   related to using a second copy.
912 - For small resources (e.g. constant buffers) use (2).
913   Discrete AMD cards have special 256 MiB pool of video memory that is directly mappable.
914   Even if the resource ends up in system memory, its data may be cached on GPU after first
915   fetch over PCIe bus.
916 - For larger resources (e.g. textures), decide between (1) and (2).
917   You may want to differentiate NVIDIA and AMD, e.g. by looking for memory type that is
918   both `DEVICE_LOCAL` and `HOST_VISIBLE`. When you find it, use (2), otherwise use (1).
919 
920 Similarly, for resources that you frequently write on GPU and read on CPU, multiple
921 solutions are possible:
922 
923 -# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
924    second copy in system memory using #VMA_MEMORY_USAGE_GPU_TO_CPU and submit explicit tranfer each time.
925 -# Create just single copy using #VMA_MEMORY_USAGE_GPU_TO_CPU, write to it directly on GPU,
926    map it and read it on CPU.
927 
928 You should take some measurements to decide which option is faster in case of your specific
929 resource.
930 
931 If you don't want to specialize your code for specific types of GPUs, yon can still make
932 an simple optimization for cases when your resource ends up in mappable memory to use it
933 directly in this case instead of creating CPU-side staging copy.
934 For details see [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable).
935 
936 
937 \page configuration Configuration
938 
939 Please check "CONFIGURATION SECTION" in the code to find macros that you can define
940 before each include of this file or change directly in this file to provide
941 your own implementation of basic facilities like assert, `min()` and `max()` functions,
942 mutex, atomic etc.
943 The library uses its own implementation of containers by default, but you can switch to using
944 STL containers instead.
945 
946 \section config_Vulkan_functions Pointers to Vulkan functions
947 
948 The library uses Vulkan functions straight from the `vulkan.h` header by default.
949 If you want to provide your own pointers to these functions, e.g. fetched using
950 `vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`:
951 
952 -# Define `VMA_STATIC_VULKAN_FUNCTIONS 0`.
953 -# Provide valid pointers through VmaAllocatorCreateInfo::pVulkanFunctions.
954 
955 \section custom_memory_allocator Custom host memory allocator
956 
957 If you use custom allocator for CPU memory rather than default operator `new`
958 and `delete` from C++, you can make this library using your allocator as well
959 by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These
960 functions will be passed to Vulkan, as well as used by the library itself to
961 make any CPU-side allocations.
962 
963 \section allocation_callbacks Device memory allocation callbacks
964 
965 The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally.
966 You can setup callbacks to be informed about these calls, e.g. for the purpose
967 of gathering some statistics. To do it, fill optional member
968 VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
969 
970 \section heap_memory_limit Device heap memory limit
971 
972 If you want to test how your program behaves with limited amount of Vulkan device
973 memory available without switching your graphics card to one that really has
974 smaller VRAM, you can use a feature of this library intended for this purpose.
975 To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit.
976 
977 
978 
979 \page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation
980 
981 VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve
982 performance on some GPUs. It augments Vulkan API with possibility to query
983 driver whether it prefers particular buffer or image to have its own, dedicated
984 allocation (separate `VkDeviceMemory` block) for better efficiency - to be able
985 to do some internal optimizations.
986 
987 The extension is supported by this library. It will be used automatically when
988 enabled. To enable it:
989 
990 1 . When creating Vulkan device, check if following 2 device extensions are
991 supported (call `vkEnumerateDeviceExtensionProperties()`).
992 If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`).
993 
994 - VK_KHR_get_memory_requirements2
995 - VK_KHR_dedicated_allocation
996 
997 If you enabled these extensions:
998 
999 2 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating
1000 your #VmaAllocator`to inform the library that you enabled required extensions
1001 and you want the library to use them.
1002 
1003 \code
1004 allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
1005 
1006 vmaCreateAllocator(&allocatorInfo, &allocator);
1007 \endcode
1008 
1009 That's all. The extension will be automatically used whenever you create a
1010 buffer using vmaCreateBuffer() or image using vmaCreateImage().
1011 
1012 When using the extension together with Vulkan Validation Layer, you will receive
1013 warnings like this:
1014 
1015     vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer.
1016 
1017 It is OK, you should just ignore it. It happens because you use function
1018 `vkGetBufferMemoryRequirements2KHR()` instead of standard
1019 `vkGetBufferMemoryRequirements()`, while the validation layer seems to be
1020 unaware of it.
1021 
1022 To learn more about this extension, see:
1023 
1024 - [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VK_KHR_dedicated_allocation)
1025 - [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5)
1026 
1027 
1028 
1029 \page general_considerations General considerations
1030 
1031 \section general_considerations_thread_safety Thread safety
1032 
1033 - The library has no global state, so separate #VmaAllocator objects can be used
1034   independently.
1035   There should be no need to create multiple such objects though - one per `VkDevice` is enough.
1036 - By default, all calls to functions that take #VmaAllocator as first parameter
1037   are safe to call from multiple threads simultaneously because they are
1038   synchronized internally when needed.
1039 - When the allocator is created with #VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT
1040   flag, calls to functions that take such #VmaAllocator object must be
1041   synchronized externally.
1042 - Access to a #VmaAllocation object must be externally synchronized. For example,
1043   you must not call vmaGetAllocationInfo() and vmaMapMemory() from different
1044   threads at the same time if you pass the same #VmaAllocation object to these
1045   functions.
1046 
1047 \section general_considerations_allocation_algorithm Allocation algorithm
1048 
1049 The library uses following algorithm for allocation, in order:
1050 
1051 -# Try to find free range of memory in existing blocks.
1052 -# If failed, try to create a new block of `VkDeviceMemory`, with preferred block size.
1053 -# If failed, try to create such block with size/2, size/4, size/8.
1054 -# If failed and #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag was
1055    specified, try to find space in existing blocks, possilby making some other
1056    allocations lost.
1057 -# If failed, try to allocate separate `VkDeviceMemory` for this allocation,
1058    just like when you use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
1059 -# If failed, choose other memory type that meets the requirements specified in
1060    VmaAllocationCreateInfo and go to point 1.
1061 -# If failed, return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
1062 
1063 \section general_considerations_features_not_supported Features not supported
1064 
1065 Features deliberately excluded from the scope of this library:
1066 
1067 - Data transfer - issuing commands that transfer data between buffers or images, any usage of
1068   `VkCommandList` or `VkCommandQueue` and related synchronization is responsibility of the user.
1069 - Support for any programming languages other than C/C++.
1070   Bindings to other languages are welcomed as external projects.
1071 
1072 */
1073 
1074 // For skia we don't include vulkan.h here. Before including this header we always include
1075 // GrVkDefines which has a user defined header. Additionally we don't require vulkan/vulkan.h to be
1076 // on the include path.
1077 // #include <vulkan/vulkan.h>
1078 
1079 /** \struct VmaAllocator
1080 \brief Represents main object of this library initialized.
1081 
1082 Fill structure VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it.
1083 Call function vmaDestroyAllocator() to destroy it.
1084 
1085 It is recommended to create just one object of this type per `VkDevice` object,
1086 right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed.
1087 */
1088 VK_DEFINE_HANDLE(VmaAllocator)
1089 
1090 /// Callback function called after successful vkAllocateMemory.
1091 typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)(
1092     VmaAllocator      allocator,
1093     uint32_t          memoryType,
1094     VkDeviceMemory    memory,
1095     VkDeviceSize      size);
1096 /// Callback function called before vkFreeMemory.
1097 typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)(
1098     VmaAllocator      allocator,
1099     uint32_t          memoryType,
1100     VkDeviceMemory    memory,
1101     VkDeviceSize      size);
1102 
1103 /** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`.
1104 
1105 Provided for informative purpose, e.g. to gather statistics about number of
1106 allocations or total amount of memory allocated in Vulkan.
1107 
1108 Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
1109 */
1110 typedef struct VmaDeviceMemoryCallbacks {
1111     /// Optional, can be null.
1112     PFN_vmaAllocateDeviceMemoryFunction pfnAllocate;
1113     /// Optional, can be null.
1114     PFN_vmaFreeDeviceMemoryFunction pfnFree;
1115 } VmaDeviceMemoryCallbacks;
1116 
1117 /// Flags for created #VmaAllocator.
1118 typedef enum VmaAllocatorCreateFlagBits {
1119     /** \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.
1120 
1121     Using this flag may increase performance because internal mutexes are not used.
1122     */
1123     VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001,
1124     /** \brief Enables usage of VK_KHR_dedicated_allocation extension.
1125 
1126     Using this extenion will automatically allocate dedicated blocks of memory for
1127     some buffers and images instead of suballocating place for them out of bigger
1128     memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
1129     flag) when it is recommended by the driver. It may improve performance on some
1130     GPUs.
1131 
1132     You may set this flag only if you found out that following device extensions are
1133     supported, you enabled them while creating Vulkan device passed as
1134     VmaAllocatorCreateInfo::device, and you want them to be used internally by this
1135     library:
1136 
1137     - VK_KHR_get_memory_requirements2
1138     - VK_KHR_dedicated_allocation
1139 
1140 When this flag is set, you can experience following warnings reported by Vulkan
1141 validation layer. You can ignore them.
1142 
1143 > vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer.
1144     */
1145     VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002,
1146 
1147     VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1148 } VmaAllocatorCreateFlagBits;
1149 typedef VkFlags VmaAllocatorCreateFlags;
1150 
1151 /** \brief Pointers to some Vulkan functions - a subset used by the library.
1152 
1153 Used in VmaAllocatorCreateInfo::pVulkanFunctions.
1154 */
1155 typedef struct VmaVulkanFunctions {
1156     PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
1157     PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
1158     PFN_vkAllocateMemory vkAllocateMemory;
1159     PFN_vkFreeMemory vkFreeMemory;
1160     PFN_vkMapMemory vkMapMemory;
1161     PFN_vkUnmapMemory vkUnmapMemory;
1162     PFN_vkBindBufferMemory vkBindBufferMemory;
1163     PFN_vkBindImageMemory vkBindImageMemory;
1164     PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
1165     PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
1166     PFN_vkCreateBuffer vkCreateBuffer;
1167     PFN_vkDestroyBuffer vkDestroyBuffer;
1168     PFN_vkCreateImage vkCreateImage;
1169     PFN_vkDestroyImage vkDestroyImage;
1170     PFN_vkGetBufferMemoryRequirements2KHR vkGetBufferMemoryRequirements2KHR;
1171     PFN_vkGetImageMemoryRequirements2KHR vkGetImageMemoryRequirements2KHR;
1172 } VmaVulkanFunctions;
1173 
1174 /// Description of a Allocator to be created.
1175 typedef struct VmaAllocatorCreateInfo
1176 {
1177     /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum.
1178     VmaAllocatorCreateFlags flags;
1179     /// Vulkan physical device.
1180     /** It must be valid throughout whole lifetime of created allocator. */
1181     VkPhysicalDevice physicalDevice;
1182     /// Vulkan device.
1183     /** It must be valid throughout whole lifetime of created allocator. */
1184     VkDevice device;
1185     /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional.
1186     /** Set to 0 to use default, which is currently 256 MiB. */
1187     VkDeviceSize preferredLargeHeapBlockSize;
1188     /// Custom CPU memory allocation callbacks. Optional.
1189     /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */
1190     const VkAllocationCallbacks* pAllocationCallbacks;
1191     /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional.
1192     /** Optional, can be null. */
1193     const VmaDeviceMemoryCallbacks* pDeviceMemoryCallbacks;
1194     /** \brief Maximum number of additional frames that are in use at the same time as current frame.
1195 
1196     This value is used only when you make allocations with
1197     VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
1198     lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
1199 
1200     For example, if you double-buffer your command buffers, so resources used for
1201     rendering in previous frame may still be in use by the GPU at the moment you
1202     allocate resources needed for the current frame, set this value to 1.
1203 
1204     If you want to allow any allocations other than used in the current frame to
1205     become lost, set this value to 0.
1206     */
1207     uint32_t frameInUseCount;
1208     /** \brief 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.
1209 
1210     If not NULL, it must be a pointer to an array of
1211     `VkPhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on
1212     maximum number of bytes that can be allocated out of particular Vulkan memory
1213     heap.
1214 
1215     Any of the elements may be equal to `VK_WHOLE_SIZE`, which means no limit on that
1216     heap. This is also the default in case of `pHeapSizeLimit` = NULL.
1217 
1218     If there is a limit defined for a heap:
1219 
1220     - If user tries to allocate more memory from that heap using this allocator,
1221       the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
1222     - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the
1223       value of this limit will be reported instead when using vmaGetMemoryProperties().
1224 
1225     Warning! Using this feature may not be equivalent to installing a GPU with
1226     smaller amount of memory, because graphics driver doesn't necessary fail new
1227     allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is
1228     exceeded. It may return success and just silently migrate some device memory
1229     blocks to system RAM.
1230     */
1231     const VkDeviceSize* pHeapSizeLimit;
1232     /** \brief Pointers to Vulkan functions. Can be null if you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1`.
1233 
1234     If you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1` in configuration section,
1235     you can pass null as this member, because the library will fetch pointers to
1236     Vulkan functions internally in a static way, like:
1237 
1238         vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
1239 
1240     Fill this member if you want to provide your own pointers to Vulkan functions,
1241     e.g. fetched using `vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`.
1242     */
1243     const VmaVulkanFunctions* pVulkanFunctions;
1244 } VmaAllocatorCreateInfo;
1245 
1246 /// Creates Allocator object.
1247 VkResult vmaCreateAllocator(
1248     const VmaAllocatorCreateInfo* pCreateInfo,
1249     VmaAllocator* pAllocator);
1250 
1251 /// Destroys allocator object.
1252 void vmaDestroyAllocator(
1253     VmaAllocator allocator);
1254 
1255 /**
1256 PhysicalDeviceProperties are fetched from physicalDevice by the allocator.
1257 You can access it here, without fetching it again on your own.
1258 */
1259 void vmaGetPhysicalDeviceProperties(
1260     VmaAllocator allocator,
1261     const VkPhysicalDeviceProperties** ppPhysicalDeviceProperties);
1262 
1263 /**
1264 PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator.
1265 You can access it here, without fetching it again on your own.
1266 */
1267 void vmaGetMemoryProperties(
1268     VmaAllocator allocator,
1269     const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties);
1270 
1271 /**
1272 \brief Given Memory Type Index, returns Property Flags of this memory type.
1273 
1274 This is just a convenience function. Same information can be obtained using
1275 vmaGetMemoryProperties().
1276 */
1277 void vmaGetMemoryTypeProperties(
1278     VmaAllocator allocator,
1279     uint32_t memoryTypeIndex,
1280     VkMemoryPropertyFlags* pFlags);
1281 
1282 /** \brief Sets index of the current frame.
1283 
1284 This function must be used if you make allocations with
1285 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT and
1286 #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flags to inform the allocator
1287 when a new frame begins. Allocations queried using vmaGetAllocationInfo() cannot
1288 become lost in the current frame.
1289 */
1290 void vmaSetCurrentFrameIndex(
1291     VmaAllocator allocator,
1292     uint32_t frameIndex);
1293 
1294 /** \brief Calculated statistics of memory usage in entire allocator.
1295 */
1296 typedef struct VmaStatInfo
1297 {
1298     /// Number of `VkDeviceMemory` Vulkan memory blocks allocated.
1299     uint32_t blockCount;
1300     /// Number of #VmaAllocation allocation objects allocated.
1301     uint32_t allocationCount;
1302     /// Number of free ranges of memory between allocations.
1303     uint32_t unusedRangeCount;
1304     /// Total number of bytes occupied by all allocations.
1305     VkDeviceSize usedBytes;
1306     /// Total number of bytes occupied by unused ranges.
1307     VkDeviceSize unusedBytes;
1308     VkDeviceSize allocationSizeMin, allocationSizeAvg, allocationSizeMax;
1309     VkDeviceSize unusedRangeSizeMin, unusedRangeSizeAvg, unusedRangeSizeMax;
1310 } VmaStatInfo;
1311 
1312 /// General statistics from current state of Allocator.
1313 typedef struct VmaStats
1314 {
1315     VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
1316     VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
1317     VmaStatInfo total;
1318 } VmaStats;
1319 
1320 /// Retrieves statistics from current state of the Allocator.
1321 void vmaCalculateStats(
1322     VmaAllocator allocator,
1323     VmaStats* pStats);
1324 
1325 #define VMA_STATS_STRING_ENABLED 1
1326 
1327 #if VMA_STATS_STRING_ENABLED
1328 
1329 /// Builds and returns statistics as string in JSON format.
1330 /** @param[out] ppStatsString Must be freed using vmaFreeStatsString() function.
1331 */
1332 void vmaBuildStatsString(
1333     VmaAllocator allocator,
1334     char** ppStatsString,
1335     VkBool32 detailedMap);
1336 
1337 void vmaFreeStatsString(
1338     VmaAllocator allocator,
1339     char* pStatsString);
1340 
1341 #endif // #if VMA_STATS_STRING_ENABLED
1342 
1343 /** \struct VmaPool
1344 \brief Represents custom memory pool
1345 
1346 Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it.
1347 Call function vmaDestroyPool() to destroy it.
1348 
1349 For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools).
1350 */
1351 VK_DEFINE_HANDLE(VmaPool)
1352 
1353 typedef enum VmaMemoryUsage
1354 {
1355     /** No intended memory usage specified.
1356     Use other members of VmaAllocationCreateInfo to specify your requirements.
1357     */
1358     VMA_MEMORY_USAGE_UNKNOWN = 0,
1359     /** Memory will be used on device only, so fast access from the device is preferred.
1360     It usually means device-local GPU (video) memory.
1361     No need to be mappable on host.
1362     It is roughly equivalent of `D3D12_HEAP_TYPE_DEFAULT`.
1363 
1364     Usage:
1365 
1366     - Resources written and read by device, e.g. images used as attachments.
1367     - Resources transferred from host once (immutable) or infrequently and read by
1368       device multiple times, e.g. textures to be sampled, vertex buffers, uniform
1369       (constant) buffers, and majority of other types of resources used by device.
1370 
1371     Allocation may still end up in `HOST_VISIBLE` memory on some implementations.
1372     In such case, you are free to map it.
1373     You can use #VMA_ALLOCATION_CREATE_MAPPED_BIT with this usage type.
1374     */
1375     VMA_MEMORY_USAGE_GPU_ONLY = 1,
1376     /** Memory will be mappable on host.
1377     It usually means CPU (system) memory.
1378     Resources created in this pool may still be accessible to the device, but access to them can be slower.
1379     Guarantees to be `HOST_VISIBLE` and `HOST_COHERENT`.
1380     CPU read may be uncached.
1381     It is roughly equivalent of `D3D12_HEAP_TYPE_UPLOAD`.
1382 
1383     Usage: Staging copy of resources used as transfer source.
1384     */
1385     VMA_MEMORY_USAGE_CPU_ONLY = 2,
1386     /**
1387     Memory that is both mappable on host (guarantees to be `HOST_VISIBLE`) and preferably fast to access by GPU.
1388     CPU reads may be uncached and very slow.
1389 
1390     Usage: Resources written frequently by host (dynamic), read by device. E.g. textures, vertex buffers, uniform buffers updated every frame or every draw call.
1391     */
1392     VMA_MEMORY_USAGE_CPU_TO_GPU = 3,
1393     /** Memory mappable on host (guarantees to be `HOST_VISIBLE`) and cached.
1394     It is roughly equivalent of `D3D12_HEAP_TYPE_READBACK`.
1395 
1396     Usage:
1397 
1398     - Resources written by device, read by host - results of some computations, e.g. screen capture, average scene luminance for HDR tone mapping.
1399     - 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.
1400     */
1401     VMA_MEMORY_USAGE_GPU_TO_CPU = 4,
1402     VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
1403 } VmaMemoryUsage;
1404 
1405 /// Flags to be passed as VmaAllocationCreateInfo::flags.
1406 typedef enum VmaAllocationCreateFlagBits {
1407     /** \brief Set this flag if the allocation should have its own memory block.
1408 
1409     Use it for special, big resources, like fullscreen images used as attachments.
1410 
1411     This flag must also be used for host visible resources that you want to map
1412     simultaneously because otherwise they might end up as regions of the same
1413     `VkDeviceMemory`, while mapping same `VkDeviceMemory` multiple times
1414     simultaneously is illegal.
1415 
1416     You should not use this flag if VmaAllocationCreateInfo::pool is not null.
1417     */
1418     VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001,
1419 
1420     /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block.
1421 
1422     If new allocation cannot be placed in any of the existing blocks, allocation
1423     fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
1424 
1425     You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and
1426     #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense.
1427 
1428     If VmaAllocationCreateInfo::pool is not null, this flag is implied and ignored. */
1429     VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002,
1430     /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it.
1431 
1432     Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData.
1433 
1434     Is it valid to use this flag for allocation made from memory type that is not
1435     `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is
1436     useful if you need an allocation that is efficient to use on GPU
1437     (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that
1438     support it (e.g. Intel GPU).
1439 
1440     You should not use this flag together with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT.
1441     */
1442     VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004,
1443     /** Allocation created with this flag can become lost as a result of another
1444     allocation with #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag, so you
1445     must check it before use.
1446 
1447     To check if allocation is not lost, call vmaGetAllocationInfo() and check if
1448     VmaAllocationInfo::deviceMemory is not `VK_NULL_HANDLE`.
1449 
1450     For details about supporting lost allocations, see Lost Allocations
1451     chapter of User Guide on Main Page.
1452 
1453     You should not use this flag together with #VMA_ALLOCATION_CREATE_MAPPED_BIT.
1454     */
1455     VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT = 0x00000008,
1456     /** While creating allocation using this flag, other allocations that were
1457     created with flag #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT can become lost.
1458 
1459     For details about supporting lost allocations, see Lost Allocations
1460     chapter of User Guide on Main Page.
1461     */
1462     VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT = 0x00000010,
1463     /** Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a
1464     null-terminated string. Instead of copying pointer value, a local copy of the
1465     string is made and stored in allocation's `pUserData`. The string is automatically
1466     freed together with the allocation. It is also used in vmaBuildStatsString().
1467     */
1468     VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020,
1469 
1470     VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1471 } VmaAllocationCreateFlagBits;
1472 typedef VkFlags VmaAllocationCreateFlags;
1473 
1474 typedef struct VmaAllocationCreateInfo
1475 {
1476     /// Use #VmaAllocationCreateFlagBits enum.
1477     VmaAllocationCreateFlags flags;
1478     /** \brief Intended usage of memory.
1479 
1480     You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n
1481     If `pool` is not null, this member is ignored.
1482     */
1483     VmaMemoryUsage usage;
1484     /** \brief Flags that must be set in a Memory Type chosen for an allocation.
1485 
1486     Leave 0 if you specify memory requirements in other way. \n
1487     If `pool` is not null, this member is ignored.*/
1488     VkMemoryPropertyFlags requiredFlags;
1489     /** \brief Flags that preferably should be set in a memory type chosen for an allocation.
1490 
1491     Set to 0 if no additional flags are prefered. \n
1492     If `pool` is not null, this member is ignored. */
1493     VkMemoryPropertyFlags preferredFlags;
1494     /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation.
1495 
1496     Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if
1497     it meets other requirements specified by this structure, with no further
1498     restrictions on memory type index. \n
1499     If `pool` is not null, this member is ignored.
1500     */
1501     uint32_t memoryTypeBits;
1502     /** \brief Pool that this allocation should be created in.
1503 
1504     Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members:
1505     `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.
1506     */
1507     VmaPool pool;
1508     /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().
1509 
1510     If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either
1511     null or pointer to a null-terminated string. The string will be then copied to
1512     internal buffer, so it doesn't need to be valid after allocation call.
1513     */
1514     void* pUserData;
1515 } VmaAllocationCreateInfo;
1516 
1517 /**
1518 \brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.
1519 
1520 This algorithm tries to find a memory type that:
1521 
1522 - Is allowed by memoryTypeBits.
1523 - Contains all the flags from pAllocationCreateInfo->requiredFlags.
1524 - Matches intended usage.
1525 - Has as many flags from pAllocationCreateInfo->preferredFlags as possible.
1526 
1527 \return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result
1528 from this function or any other allocating function probably means that your
1529 device doesn't support any memory type with requested features for the specific
1530 type of resource you want to use it for. Please check parameters of your
1531 resource, like image layout (OPTIMAL versus LINEAR) or mip level count.
1532 */
1533 VkResult vmaFindMemoryTypeIndex(
1534     VmaAllocator allocator,
1535     uint32_t memoryTypeBits,
1536     const VmaAllocationCreateInfo* pAllocationCreateInfo,
1537     uint32_t* pMemoryTypeIndex);
1538 
1539 /**
1540 \brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.
1541 
1542 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
1543 It internally creates a temporary, dummy buffer that never has memory bound.
1544 It is just a convenience function, equivalent to calling:
1545 
1546 - `vkCreateBuffer`
1547 - `vkGetBufferMemoryRequirements`
1548 - `vmaFindMemoryTypeIndex`
1549 - `vkDestroyBuffer`
1550 */
1551 VkResult vmaFindMemoryTypeIndexForBufferInfo(
1552     VmaAllocator allocator,
1553     const VkBufferCreateInfo* pBufferCreateInfo,
1554     const VmaAllocationCreateInfo* pAllocationCreateInfo,
1555     uint32_t* pMemoryTypeIndex);
1556 
1557 /**
1558 \brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.
1559 
1560 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
1561 It internally creates a temporary, dummy image that never has memory bound.
1562 It is just a convenience function, equivalent to calling:
1563 
1564 - `vkCreateImage`
1565 - `vkGetImageMemoryRequirements`
1566 - `vmaFindMemoryTypeIndex`
1567 - `vkDestroyImage`
1568 */
1569 VkResult vmaFindMemoryTypeIndexForImageInfo(
1570     VmaAllocator allocator,
1571     const VkImageCreateInfo* pImageCreateInfo,
1572     const VmaAllocationCreateInfo* pAllocationCreateInfo,
1573     uint32_t* pMemoryTypeIndex);
1574 
1575 /// Flags to be passed as VmaPoolCreateInfo::flags.
1576 typedef enum VmaPoolCreateFlagBits {
1577     /** \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.
1578 
1579     This is na optional optimization flag.
1580 
1581     If you always allocate using vmaCreateBuffer(), vmaCreateImage(),
1582     vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator
1583     knows exact type of your allocations so it can handle Buffer-Image Granularity
1584     in the optimal way.
1585 
1586     If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(),
1587     exact type of such allocations is not known, so allocator must be conservative
1588     in handling Buffer-Image Granularity, which can lead to suboptimal allocation
1589     (wasted memory). In that case, if you can make sure you always allocate only
1590     buffers and linear images or only optimal images out of this pool, use this flag
1591     to make allocator disregard Buffer-Image Granularity and so make allocations
1592     more optimal.
1593     */
1594     VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002,
1595 
1596     VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1597 } VmaPoolCreateFlagBits;
1598 typedef VkFlags VmaPoolCreateFlags;
1599 
1600 /** \brief Describes parameter of created #VmaPool.
1601 */
1602 typedef struct VmaPoolCreateInfo {
1603     /** \brief Vulkan memory type index to allocate this pool from.
1604     */
1605     uint32_t memoryTypeIndex;
1606     /** \brief Use combination of #VmaPoolCreateFlagBits.
1607     */
1608     VmaPoolCreateFlags flags;
1609     /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes.
1610 
1611     Optional. Leave 0 to use default.
1612     */
1613     VkDeviceSize blockSize;
1614     /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty.
1615 
1616     Set to 0 to have no preallocated blocks and let the pool be completely empty.
1617     */
1618     size_t minBlockCount;
1619     /** \brief Maximum number of blocks that can be allocated in this pool. Optional.
1620 
1621     Optional. Set to 0 to use `SIZE_MAX`, which means no limit.
1622 
1623     Set to same value as minBlockCount to have fixed amount of memory allocated
1624     throuout whole lifetime of this pool.
1625     */
1626     size_t maxBlockCount;
1627     /** \brief Maximum number of additional frames that are in use at the same time as current frame.
1628 
1629     This value is used only when you make allocations with
1630     #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
1631     lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
1632 
1633     For example, if you double-buffer your command buffers, so resources used for
1634     rendering in previous frame may still be in use by the GPU at the moment you
1635     allocate resources needed for the current frame, set this value to 1.
1636 
1637     If you want to allow any allocations other than used in the current frame to
1638     become lost, set this value to 0.
1639     */
1640     uint32_t frameInUseCount;
1641 } VmaPoolCreateInfo;
1642 
1643 /** \brief Describes parameter of existing #VmaPool.
1644 */
1645 typedef struct VmaPoolStats {
1646     /** \brief Total amount of `VkDeviceMemory` allocated from Vulkan for this pool, in bytes.
1647     */
1648     VkDeviceSize size;
1649     /** \brief Total number of bytes in the pool not used by any #VmaAllocation.
1650     */
1651     VkDeviceSize unusedSize;
1652     /** \brief Number of #VmaAllocation objects created from this pool that were not destroyed or lost.
1653     */
1654     size_t allocationCount;
1655     /** \brief Number of continuous memory ranges in the pool not used by any #VmaAllocation.
1656     */
1657     size_t unusedRangeCount;
1658     /** \brief Size of the largest continuous free memory region.
1659 
1660     Making a new allocation of that size is not guaranteed to succeed because of
1661     possible additional margin required to respect alignment and buffer/image
1662     granularity.
1663     */
1664     VkDeviceSize unusedRangeSizeMax;
1665 } VmaPoolStats;
1666 
1667 /** \brief Allocates Vulkan device memory and creates #VmaPool object.
1668 
1669 @param allocator Allocator object.
1670 @param pCreateInfo Parameters of pool to create.
1671 @param[out] pPool Handle to created pool.
1672 */
1673 VkResult vmaCreatePool(
1674 	VmaAllocator allocator,
1675 	const VmaPoolCreateInfo* pCreateInfo,
1676 	VmaPool* pPool);
1677 
1678 /** \brief Destroys #VmaPool object and frees Vulkan device memory.
1679 */
1680 void vmaDestroyPool(
1681     VmaAllocator allocator,
1682     VmaPool pool);
1683 
1684 /** \brief Retrieves statistics of existing #VmaPool object.
1685 
1686 @param allocator Allocator object.
1687 @param pool Pool object.
1688 @param[out] pPoolStats Statistics of specified pool.
1689 */
1690 void vmaGetPoolStats(
1691     VmaAllocator allocator,
1692     VmaPool pool,
1693     VmaPoolStats* pPoolStats);
1694 
1695 /** \brief Marks all allocations in given pool as lost if they are not used in current frame or VmaPoolCreateInfo::frameInUseCount back from now.
1696 
1697 @param allocator Allocator object.
1698 @param pool Pool.
1699 @param[out] pLostAllocationCount Number of allocations marked as lost. Optional - pass null if you don't need this information.
1700 */
1701 void vmaMakePoolAllocationsLost(
1702     VmaAllocator allocator,
1703     VmaPool pool,
1704     size_t* pLostAllocationCount);
1705 
1706 /** \struct VmaAllocation
1707 \brief Represents single memory allocation.
1708 
1709 It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type
1710 plus unique offset.
1711 
1712 There are multiple ways to create such object.
1713 You need to fill structure VmaAllocationCreateInfo.
1714 For more information see [Choosing memory type](@ref choosing_memory_type).
1715 
1716 Although the library provides convenience functions that create Vulkan buffer or image,
1717 allocate memory for it and bind them together,
1718 binding of the allocation to a buffer or an image is out of scope of the allocation itself.
1719 Allocation object can exist without buffer/image bound,
1720 binding can be done manually by the user, and destruction of it can be done
1721 independently of destruction of the allocation.
1722 
1723 The object also remembers its size and some other information.
1724 To retrieve this information, use function vmaGetAllocationInfo() and inspect
1725 returned structure VmaAllocationInfo.
1726 
1727 Some kinds allocations can be in lost state.
1728 For more information, see [Lost allocations](@ref lost_allocations).
1729 */
1730 VK_DEFINE_HANDLE(VmaAllocation)
1731 
1732 /** \brief Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
1733 */
1734 typedef struct VmaAllocationInfo {
1735     /** \brief Memory type index that this allocation was allocated from.
1736 
1737     It never changes.
1738     */
1739     uint32_t memoryType;
1740     /** \brief Handle to Vulkan memory object.
1741 
1742     Same memory object can be shared by multiple allocations.
1743 
1744     It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
1745 
1746     If the allocation is lost, it is equal to `VK_NULL_HANDLE`.
1747     */
1748     VkDeviceMemory deviceMemory;
1749     /** \brief Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation.
1750 
1751     It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
1752     */
1753     VkDeviceSize offset;
1754     /** \brief Size of this allocation, in bytes.
1755 
1756     It never changes, unless allocation is lost.
1757     */
1758     VkDeviceSize size;
1759     /** \brief Pointer to the beginning of this allocation as mapped data.
1760 
1761     If the allocation hasn't been mapped using vmaMapMemory() and hasn't been
1762     created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value null.
1763 
1764     It can change after call to vmaMapMemory(), vmaUnmapMemory().
1765     It can also change after call to vmaDefragment() if this allocation is passed to the function.
1766     */
1767     void* pMappedData;
1768     /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData().
1769 
1770     It can change after call to vmaSetAllocationUserData() for this allocation.
1771     */
1772     void* pUserData;
1773 } VmaAllocationInfo;
1774 
1775 /** \brief General purpose memory allocation.
1776 
1777 @param[out] pAllocation Handle to allocated memory.
1778 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
1779 
1780 You should free the memory using vmaFreeMemory().
1781 
1782 It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(),
1783 vmaCreateBuffer(), vmaCreateImage() instead whenever possible.
1784 */
1785 VkResult vmaAllocateMemory(
1786     VmaAllocator allocator,
1787     const VkMemoryRequirements* pVkMemoryRequirements,
1788     const VmaAllocationCreateInfo* pCreateInfo,
1789     VmaAllocation* pAllocation,
1790     VmaAllocationInfo* pAllocationInfo);
1791 
1792 /**
1793 @param[out] pAllocation Handle to allocated memory.
1794 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
1795 
1796 You should free the memory using vmaFreeMemory().
1797 */
1798 VkResult vmaAllocateMemoryForBuffer(
1799     VmaAllocator allocator,
1800     VkBuffer buffer,
1801     const VmaAllocationCreateInfo* pCreateInfo,
1802     VmaAllocation* pAllocation,
1803     VmaAllocationInfo* pAllocationInfo);
1804 
1805 /// Function similar to vmaAllocateMemoryForBuffer().
1806 VkResult vmaAllocateMemoryForImage(
1807     VmaAllocator allocator,
1808     VkImage image,
1809     const VmaAllocationCreateInfo* pCreateInfo,
1810     VmaAllocation* pAllocation,
1811     VmaAllocationInfo* pAllocationInfo);
1812 
1813 /// Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().
1814 void vmaFreeMemory(
1815     VmaAllocator allocator,
1816     VmaAllocation allocation);
1817 
1818 /** \brief Returns current information about specified allocation and atomically marks it as used in current frame.
1819 
1820 Current paramters of given allocation are returned in `pAllocationInfo`.
1821 
1822 This function also atomically "touches" allocation - marks it as used in current frame,
1823 just like vmaTouchAllocation().
1824 If the allocation is in lost state, `pAllocationInfo->deviceMemory == VK_NULL_HANDLE`.
1825 
1826 Although this function uses atomics and doesn't lock any mutex, so it should be quite efficient,
1827 you can avoid calling it too often.
1828 
1829 - You can retrieve same VmaAllocationInfo structure while creating your resource, from function
1830   vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change
1831   (e.g. due to defragmentation or allocation becoming lost).
1832 - If you just want to check if allocation is not lost, vmaTouchAllocation() will work faster.
1833 */
1834 void vmaGetAllocationInfo(
1835     VmaAllocator allocator,
1836     VmaAllocation allocation,
1837     VmaAllocationInfo* pAllocationInfo);
1838 
1839 /** \brief Returns `VK_TRUE` if allocation is not lost and atomically marks it as used in current frame.
1840 
1841 If the allocation has been created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
1842 this function returns `VK_TRUE` if it's not in lost state, so it can still be used.
1843 It then also atomically "touches" the allocation - marks it as used in current frame,
1844 so that you can be sure it won't become lost in current frame or next `frameInUseCount` frames.
1845 
1846 If the allocation is in lost state, the function returns `VK_FALSE`.
1847 Memory of such allocation, as well as buffer or image bound to it, should not be used.
1848 Lost allocation and the buffer/image still need to be destroyed.
1849 
1850 If the allocation has been created without #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
1851 this function always returns `VK_TRUE`.
1852 */
1853 VkBool32 vmaTouchAllocation(
1854     VmaAllocator allocator,
1855     VmaAllocation allocation);
1856 
1857 /** \brief Sets pUserData in given allocation to new value.
1858 
1859 If the allocation was created with VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT,
1860 pUserData must be either null, or pointer to a null-terminated string. The function
1861 makes local copy of the string and sets it as allocation's `pUserData`. String
1862 passed as pUserData doesn't need to be valid for whole lifetime of the allocation -
1863 you can free it after this call. String previously pointed by allocation's
1864 pUserData is freed from memory.
1865 
1866 If the flag was not used, the value of pointer `pUserData` is just copied to
1867 allocation's `pUserData`. It is opaque, so you can use it however you want - e.g.
1868 as a pointer, ordinal number or some handle to you own data.
1869 */
1870 void vmaSetAllocationUserData(
1871     VmaAllocator allocator,
1872     VmaAllocation allocation,
1873     void* pUserData);
1874 
1875 /** \brief Creates new allocation that is in lost state from the beginning.
1876 
1877 It can be useful if you need a dummy, non-null allocation.
1878 
1879 You still need to destroy created object using vmaFreeMemory().
1880 
1881 Returned allocation is not tied to any specific memory pool or memory type and
1882 not bound to any image or buffer. It has size = 0. It cannot be turned into
1883 a real, non-empty allocation.
1884 */
1885 void vmaCreateLostAllocation(
1886     VmaAllocator allocator,
1887     VmaAllocation* pAllocation);
1888 
1889 /** \brief Maps memory represented by given allocation and returns pointer to it.
1890 
1891 Maps memory represented by given allocation to make it accessible to CPU code.
1892 When succeeded, `*ppData` contains pointer to first byte of this memory.
1893 If the allocation is part of bigger `VkDeviceMemory` block, the pointer is
1894 correctly offseted to the beginning of region assigned to this particular
1895 allocation.
1896 
1897 Mapping is internally reference-counted and synchronized, so despite raw Vulkan
1898 function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory`
1899 multiple times simultaneously, it is safe to call this function on allocations
1900 assigned to the same memory block. Actual Vulkan memory will be mapped on first
1901 mapping and unmapped on last unmapping.
1902 
1903 If the function succeeded, you must call vmaUnmapMemory() to unmap the
1904 allocation when mapping is no longer needed or before freeing the allocation, at
1905 the latest.
1906 
1907 It also safe to call this function multiple times on the same allocation. You
1908 must call vmaUnmapMemory() same number of times as you called vmaMapMemory().
1909 
1910 It is also safe to call this function on allocation created with
1911 #VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time.
1912 You must still call vmaUnmapMemory() same number of times as you called
1913 vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the
1914 "0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag.
1915 
1916 This function fails when used on allocation made in memory type that is not
1917 `HOST_VISIBLE`.
1918 
1919 This function always fails when called for allocation that was created with
1920 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocations cannot be
1921 mapped.
1922 */
1923 VkResult vmaMapMemory(
1924     VmaAllocator allocator,
1925     VmaAllocation allocation,
1926     void** ppData);
1927 
1928 /** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
1929 
1930 For details, see description of vmaMapMemory().
1931 */
1932 void vmaUnmapMemory(
1933     VmaAllocator allocator,
1934     VmaAllocation allocation);
1935 
1936 /** \brief Optional configuration parameters to be passed to function vmaDefragment(). */
1937 typedef struct VmaDefragmentationInfo {
1938     /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places.
1939 
1940     Default is `VK_WHOLE_SIZE`, which means no limit.
1941     */
1942     VkDeviceSize maxBytesToMove;
1943     /** \brief Maximum number of allocations that can be moved to different place.
1944 
1945     Default is `UINT32_MAX`, which means no limit.
1946     */
1947     uint32_t maxAllocationsToMove;
1948 } VmaDefragmentationInfo;
1949 
1950 /** \brief Statistics returned by function vmaDefragment(). */
1951 typedef struct VmaDefragmentationStats {
1952     /// Total number of bytes that have been copied while moving allocations to different places.
1953     VkDeviceSize bytesMoved;
1954     /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects.
1955     VkDeviceSize bytesFreed;
1956     /// Number of allocations that have been moved to different places.
1957     uint32_t allocationsMoved;
1958     /// Number of empty `VkDeviceMemory` objects that have been released to the system.
1959     uint32_t deviceMemoryBlocksFreed;
1960 } VmaDefragmentationStats;
1961 
1962 /** \brief Compacts memory by moving allocations.
1963 
1964 @param pAllocations Array of allocations that can be moved during this compation.
1965 @param allocationCount Number of elements in pAllocations and pAllocationsChanged arrays.
1966 @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.
1967 @param pDefragmentationInfo Configuration parameters. Optional - pass null to use default values.
1968 @param[out] pDefragmentationStats Statistics returned by the function. Optional - pass null if you don't need this information.
1969 @return VK_SUCCESS if completed, VK_INCOMPLETE if succeeded but didn't make all possible optimizations because limits specified in pDefragmentationInfo have been reached, negative error code in case of error.
1970 
1971 This function works by moving allocations to different places (different
1972 `VkDeviceMemory` objects and/or different offsets) in order to optimize memory
1973 usage. Only allocations that are in pAllocations array can be moved. All other
1974 allocations are considered nonmovable in this call. Basic rules:
1975 
1976 - Only allocations made in memory types that have
1977   `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag can be compacted. You may pass other
1978   allocations but it makes no sense - these will never be moved.
1979 - You may pass allocations made with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT but
1980   it makes no sense - they will never be moved.
1981 - Both allocations made with or without #VMA_ALLOCATION_CREATE_MAPPED_BIT
1982   flag can be compacted. If not persistently mapped, memory will be mapped
1983   temporarily inside this function if needed.
1984 - You must not pass same #VmaAllocation object multiple times in pAllocations array.
1985 
1986 The function also frees empty `VkDeviceMemory` blocks.
1987 
1988 After allocation has been moved, its VmaAllocationInfo::deviceMemory and/or
1989 VmaAllocationInfo::offset changes. You must query them again using
1990 vmaGetAllocationInfo() if you need them.
1991 
1992 If an allocation has been moved, data in memory is copied to new place
1993 automatically, but if it was bound to a buffer or an image, you must destroy
1994 that object yourself, create new one and bind it to the new memory pointed by
1995 the allocation. You must use `vkDestroyBuffer()`, `vkDestroyImage()`,
1996 `vkCreateBuffer()`, `vkCreateImage()` for that purpose and NOT vmaDestroyBuffer(),
1997 vmaDestroyImage(), vmaCreateBuffer(), vmaCreateImage()! Example:
1998 
1999 \code
2000 VkDevice device = ...;
2001 VmaAllocator allocator = ...;
2002 std::vector<VkBuffer> buffers = ...;
2003 std::vector<VmaAllocation> allocations = ...;
2004 
2005 std::vector<VkBool32> allocationsChanged(allocations.size());
2006 vmaDefragment(allocator, allocations.data(), allocations.size(), allocationsChanged.data(), nullptr, nullptr);
2007 
2008 for(size_t i = 0; i < allocations.size(); ++i)
2009 {
2010     if(allocationsChanged[i])
2011     {
2012         VmaAllocationInfo allocInfo;
2013         vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
2014 
2015         vkDestroyBuffer(device, buffers[i], nullptr);
2016 
2017         VkBufferCreateInfo bufferInfo = ...;
2018         vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
2019 
2020         // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
2021 
2022         vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset);
2023     }
2024 }
2025 \endcode
2026 
2027 Note: Please don't expect memory to be fully compacted after this call.
2028 Algorithms inside are based on some heuristics that try to maximize number of Vulkan
2029 memory blocks to make totally empty to release them, as well as to maximimze continuous
2030 empty space inside remaining blocks, while minimizing the number and size of data that
2031 needs to be moved. Some fragmentation still remains after this call. This is normal.
2032 
2033 Warning: This function is not 100% correct according to Vulkan specification. Use it
2034 at your own risk. That's because Vulkan doesn't guarantee that memory
2035 requirements (size and alignment) for a new buffer or image are consistent. They
2036 may be different even for subsequent calls with the same parameters. It really
2037 does happen on some platforms, especially with images.
2038 
2039 Warning: This function may be time-consuming, so you shouldn't call it too often
2040 (like every frame or after every resource creation/destruction).
2041 You can call it on special occasions (like when reloading a game level or
2042 when you just destroyed a lot of objects).
2043 */
2044 VkResult vmaDefragment(
2045     VmaAllocator allocator,
2046     VmaAllocation* pAllocations,
2047     size_t allocationCount,
2048     VkBool32* pAllocationsChanged,
2049     const VmaDefragmentationInfo *pDefragmentationInfo,
2050     VmaDefragmentationStats* pDefragmentationStats);
2051 
2052 /** \brief Binds buffer to allocation.
2053 
2054 Binds specified buffer to region of memory represented by specified allocation.
2055 Gets `VkDeviceMemory` handle and offset from the allocation.
2056 If you want to create a buffer, allocate memory for it and bind them together separately,
2057 you should use this function for binding instead of standard `vkBindBufferMemory()`,
2058 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
2059 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
2060 (which is illegal in Vulkan).
2061 
2062 It is recommended to use function vmaCreateBuffer() instead of this one.
2063 */
2064 VkResult vmaBindBufferMemory(
2065     VmaAllocator allocator,
2066     VmaAllocation allocation,
2067     VkBuffer buffer);
2068 
2069 /** \brief Binds image to allocation.
2070 
2071 Binds specified image to region of memory represented by specified allocation.
2072 Gets `VkDeviceMemory` handle and offset from the allocation.
2073 If you want to create an image, allocate memory for it and bind them together separately,
2074 you should use this function for binding instead of standard `vkBindImageMemory()`,
2075 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
2076 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
2077 (which is illegal in Vulkan).
2078 
2079 It is recommended to use function vmaCreateImage() instead of this one.
2080 */
2081 VkResult vmaBindImageMemory(
2082     VmaAllocator allocator,
2083     VmaAllocation allocation,
2084     VkImage image);
2085 
2086 /**
2087 @param[out] pBuffer Buffer that was created.
2088 @param[out] pAllocation Allocation that was created.
2089 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
2090 
2091 This function automatically:
2092 
2093 -# Creates buffer.
2094 -# Allocates appropriate memory for it.
2095 -# Binds the buffer with the memory.
2096 
2097 If any of these operations fail, buffer and allocation are not created,
2098 returned value is negative error code, *pBuffer and *pAllocation are null.
2099 
2100 If the function succeeded, you must destroy both buffer and allocation when you
2101 no longer need them using either convenience function vmaDestroyBuffer() or
2102 separately, using `vkDestroyBuffer()` and vmaFreeMemory().
2103 
2104 If VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used,
2105 VK_KHR_dedicated_allocation extension is used internally to query driver whether
2106 it requires or prefers the new buffer to have dedicated allocation. If yes,
2107 and if dedicated allocation is possible (VmaAllocationCreateInfo::pool is null
2108 and VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated
2109 allocation for this buffer, just like when using
2110 VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
2111 */
2112 VkResult vmaCreateBuffer(
2113     VmaAllocator allocator,
2114     const VkBufferCreateInfo* pBufferCreateInfo,
2115     const VmaAllocationCreateInfo* pAllocationCreateInfo,
2116     VkBuffer* pBuffer,
2117     VmaAllocation* pAllocation,
2118     VmaAllocationInfo* pAllocationInfo);
2119 
2120 /** \brief Destroys Vulkan buffer and frees allocated memory.
2121 
2122 This is just a convenience function equivalent to:
2123 
2124 \code
2125 vkDestroyBuffer(device, buffer, allocationCallbacks);
2126 vmaFreeMemory(allocator, allocation);
2127 \endcode
2128 
2129 It it safe to pass null as buffer and/or allocation.
2130 */
2131 void vmaDestroyBuffer(
2132     VmaAllocator allocator,
2133     VkBuffer buffer,
2134     VmaAllocation allocation);
2135 
2136 /// Function similar to vmaCreateBuffer().
2137 VkResult vmaCreateImage(
2138     VmaAllocator allocator,
2139     const VkImageCreateInfo* pImageCreateInfo,
2140     const VmaAllocationCreateInfo* pAllocationCreateInfo,
2141     VkImage* pImage,
2142     VmaAllocation* pAllocation,
2143     VmaAllocationInfo* pAllocationInfo);
2144 
2145 /** \brief Destroys Vulkan image and frees allocated memory.
2146 
2147 This is just a convenience function equivalent to:
2148 
2149 \code
2150 vkDestroyImage(device, image, allocationCallbacks);
2151 vmaFreeMemory(allocator, allocation);
2152 \endcode
2153 
2154 It it safe to pass null as image and/or allocation.
2155 */
2156 void vmaDestroyImage(
2157     VmaAllocator allocator,
2158     VkImage image,
2159     VmaAllocation allocation);
2160 
2161 #ifdef __cplusplus
2162 }
2163 #endif
2164 
2165 #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
2166 
2167 // For Visual Studio IntelliSense.
2168 #ifdef __INTELLISENSE__
2169 #define VMA_IMPLEMENTATION
2170 #endif
2171 
2172 #ifdef VMA_IMPLEMENTATION
2173 #undef VMA_IMPLEMENTATION
2174 
2175 #include <cstdint>
2176 #include <cstdlib>
2177 #include <cstring>
2178 
2179 /*******************************************************************************
2180 CONFIGURATION SECTION
2181 
2182 Define some of these macros before each #include of this header or change them
2183 here if you need other then default behavior depending on your environment.
2184 */
2185 
2186 /*
2187 Define this macro to 1 to make the library fetch pointers to Vulkan functions
2188 internally, like:
2189 
2190     vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
2191 
2192 Define to 0 if you are going to provide you own pointers to Vulkan functions via
2193 VmaAllocatorCreateInfo::pVulkanFunctions.
2194 */
2195 #if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
2196 #define VMA_STATIC_VULKAN_FUNCTIONS 1
2197 #endif
2198 
2199 // Define this macro to 1 to make the library use STL containers instead of its own implementation.
2200 //#define VMA_USE_STL_CONTAINERS 1
2201 
2202 /* Set this macro to 1 to make the library including and using STL containers:
2203 std::pair, std::vector, std::list, std::unordered_map.
2204 
2205 Set it to 0 or undefined to make the library using its own implementation of
2206 the containers.
2207 */
2208 #if VMA_USE_STL_CONTAINERS
2209    #define VMA_USE_STL_VECTOR 1
2210    #define VMA_USE_STL_UNORDERED_MAP 1
2211    #define VMA_USE_STL_LIST 1
2212 #endif
2213 
2214 #if VMA_USE_STL_VECTOR
2215    #include <vector>
2216 #endif
2217 
2218 #if VMA_USE_STL_UNORDERED_MAP
2219    #include <unordered_map>
2220 #endif
2221 
2222 #if VMA_USE_STL_LIST
2223    #include <list>
2224 #endif
2225 
2226 /*
2227 Following headers are used in this CONFIGURATION section only, so feel free to
2228 remove them if not needed.
2229 */
2230 #include <cassert> // for assert
2231 #include <algorithm> // for min, max
2232 #include <mutex> // for std::mutex
2233 #include <atomic> // for std::atomic
2234 
2235 #if !defined(_WIN32) && !defined(__APPLE__)
2236     #include <malloc.h> // for aligned_alloc()
2237 #endif
2238 
2239 #ifndef VMA_NULL
2240    // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
2241    #define VMA_NULL   nullptr
2242 #endif
2243 
2244 #if defined(__APPLE__) || defined(__ANDROID__)
2245 #include <cstdlib>
aligned_alloc(size_t alignment,size_t size)2246 void *aligned_alloc(size_t alignment, size_t size)
2247 {
2248     // alignment must be >= sizeof(void*)
2249     if(alignment < sizeof(void*))
2250     {
2251         alignment = sizeof(void*);
2252     }
2253 
2254     void *pointer;
2255     if(posix_memalign(&pointer, alignment, size) == 0)
2256         return pointer;
2257     return VMA_NULL;
2258 }
2259 #endif
2260 
2261 // Normal assert to check for programmer's errors, especially in Debug configuration.
2262 #ifndef VMA_ASSERT
2263    #ifdef _DEBUG
2264        #define VMA_ASSERT(expr)         assert(expr)
2265    #else
2266        #define VMA_ASSERT(expr)
2267    #endif
2268 #endif
2269 
2270 // Assert that will be called very often, like inside data structures e.g. operator[].
2271 // Making it non-empty can make program slow.
2272 #ifndef VMA_HEAVY_ASSERT
2273    #ifdef _DEBUG
2274        #define VMA_HEAVY_ASSERT(expr)   //VMA_ASSERT(expr)
2275    #else
2276        #define VMA_HEAVY_ASSERT(expr)
2277    #endif
2278 #endif
2279 
2280 #ifndef VMA_ALIGN_OF
2281    #define VMA_ALIGN_OF(type)       (__alignof(type))
2282 #endif
2283 
2284 #ifndef VMA_SYSTEM_ALIGNED_MALLOC
2285    #if defined(_WIN32)
2286        #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment)   (_aligned_malloc((size), (alignment)))
2287    #else
2288        #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment)   (aligned_alloc((alignment), (size) ))
2289    #endif
2290 #endif
2291 
2292 #ifndef VMA_SYSTEM_FREE
2293    #if defined(_WIN32)
2294        #define VMA_SYSTEM_FREE(ptr)   _aligned_free(ptr)
2295    #else
2296        #define VMA_SYSTEM_FREE(ptr)   free(ptr)
2297    #endif
2298 #endif
2299 
2300 #ifndef VMA_MIN
2301    #define VMA_MIN(v1, v2)    (std::min((v1), (v2)))
2302 #endif
2303 
2304 #ifndef VMA_MAX
2305    #define VMA_MAX(v1, v2)    (std::max((v1), (v2)))
2306 #endif
2307 
2308 #ifndef VMA_SWAP
2309    #define VMA_SWAP(v1, v2)   std::swap((v1), (v2))
2310 #endif
2311 
2312 #ifndef VMA_SORT
2313    #define VMA_SORT(beg, end, cmp)  std::sort(beg, end, cmp)
2314 #endif
2315 
2316 #ifndef VMA_DEBUG_LOG
2317    #define VMA_DEBUG_LOG(format, ...)
2318    /*
2319    #define VMA_DEBUG_LOG(format, ...) do { \
2320        printf(format, __VA_ARGS__); \
2321        printf("\n"); \
2322    } while(false)
2323    */
2324 #endif
2325 
2326 // Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
2327 #if VMA_STATS_STRING_ENABLED
VmaUint32ToStr(char * outStr,size_t strLen,uint32_t num)2328    static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num)
2329    {
2330        snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
2331    }
VmaUint64ToStr(char * outStr,size_t strLen,uint64_t num)2332    static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
2333    {
2334        snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
2335    }
VmaPtrToStr(char * outStr,size_t strLen,const void * ptr)2336    static inline void VmaPtrToStr(char* outStr, size_t strLen, const void* ptr)
2337    {
2338        snprintf(outStr, strLen, "%p", ptr);
2339    }
2340 #endif
2341 
2342 #ifndef VMA_MUTEX
2343    class VmaMutex
2344    {
2345    public:
VmaMutex()2346        VmaMutex() { }
~VmaMutex()2347        ~VmaMutex() { }
Lock()2348        void Lock() { m_Mutex.lock(); }
Unlock()2349        void Unlock() { m_Mutex.unlock(); }
2350    private:
2351        std::mutex m_Mutex;
2352    };
2353    #define VMA_MUTEX VmaMutex
2354 #endif
2355 
2356 /*
2357 If providing your own implementation, you need to implement a subset of std::atomic:
2358 
2359 - Constructor(uint32_t desired)
2360 - uint32_t load() const
2361 - void store(uint32_t desired)
2362 - bool compare_exchange_weak(uint32_t& expected, uint32_t desired)
2363 */
2364 #ifndef VMA_ATOMIC_UINT32
2365    #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
2366 #endif
2367 
2368 #ifndef VMA_BEST_FIT
2369    /**
2370    Main parameter for function assessing how good is a free suballocation for a new
2371    allocation request.
2372 
2373    - Set to 1 to use Best-Fit algorithm - prefer smaller blocks, as close to the
2374      size of requested allocations as possible.
2375    - Set to 0 to use Worst-Fit algorithm - prefer larger blocks, as large as
2376      possible.
2377 
2378    Experiments in special testing environment showed that Best-Fit algorithm is
2379    better.
2380    */
2381    #define VMA_BEST_FIT (1)
2382 #endif
2383 
2384 #ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
2385    /**
2386    Every allocation will have its own memory block.
2387    Define to 1 for debugging purposes only.
2388    */
2389    #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
2390 #endif
2391 
2392 #ifndef VMA_DEBUG_ALIGNMENT
2393    /**
2394    Minimum alignment of all suballocations, in bytes.
2395    Set to more than 1 for debugging purposes only. Must be power of two.
2396    */
2397    #define VMA_DEBUG_ALIGNMENT (1)
2398 #endif
2399 
2400 #ifndef VMA_DEBUG_MARGIN
2401    /**
2402    Minimum margin between suballocations, in bytes.
2403    Set nonzero for debugging purposes only.
2404    */
2405    #define VMA_DEBUG_MARGIN (0)
2406 #endif
2407 
2408 #ifndef VMA_DEBUG_GLOBAL_MUTEX
2409    /**
2410    Set this to 1 for debugging purposes only, to enable single mutex protecting all
2411    entry calls to the library. Can be useful for debugging multithreading issues.
2412    */
2413    #define VMA_DEBUG_GLOBAL_MUTEX (0)
2414 #endif
2415 
2416 #ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
2417    /**
2418    Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.
2419    Set to more than 1 for debugging purposes only. Must be power of two.
2420    */
2421    #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
2422 #endif
2423 
2424 #ifndef VMA_SMALL_HEAP_MAX_SIZE
2425    /// Maximum size of a memory heap in Vulkan to consider it "small".
2426    #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
2427 #endif
2428 
2429 #ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
2430    /// Default size of a block allocated as single VkDeviceMemory from a "large" heap.
2431    #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
2432 #endif
2433 
2434 static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX;
2435 
2436 /*******************************************************************************
2437 END OF CONFIGURATION
2438 */
2439 
2440 static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
2441     VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
2442 
2443 // Returns number of bits set to 1 in (v).
VmaCountBitsSet(uint32_t v)2444 static inline uint32_t VmaCountBitsSet(uint32_t v)
2445 {
2446 	uint32_t c = v - ((v >> 1) & 0x55555555);
2447 	c = ((c >>  2) & 0x33333333) + (c & 0x33333333);
2448 	c = ((c >>  4) + c) & 0x0F0F0F0F;
2449 	c = ((c >>  8) + c) & 0x00FF00FF;
2450 	c = ((c >> 16) + c) & 0x0000FFFF;
2451 	return c;
2452 }
2453 
2454 // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
2455 // Use types like uint32_t, uint64_t as T.
2456 template <typename T>
VmaAlignUp(T val,T align)2457 static inline T VmaAlignUp(T val, T align)
2458 {
2459 	return (val + align - 1) / align * align;
2460 }
2461 
2462 // Division with mathematical rounding to nearest number.
2463 template <typename T>
VmaRoundDiv(T x,T y)2464 inline T VmaRoundDiv(T x, T y)
2465 {
2466 	return (x + (y / (T)2)) / y;
2467 }
2468 
2469 #ifndef VMA_SORT
2470 
2471 template<typename Iterator, typename Compare>
VmaQuickSortPartition(Iterator beg,Iterator end,Compare cmp)2472 Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
2473 {
2474     Iterator centerValue = end; --centerValue;
2475     Iterator insertIndex = beg;
2476     for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
2477     {
2478         if(cmp(*memTypeIndex, *centerValue))
2479         {
2480             if(insertIndex != memTypeIndex)
2481             {
2482                 VMA_SWAP(*memTypeIndex, *insertIndex);
2483             }
2484             ++insertIndex;
2485         }
2486     }
2487     if(insertIndex != centerValue)
2488     {
2489         VMA_SWAP(*insertIndex, *centerValue);
2490     }
2491     return insertIndex;
2492 }
2493 
2494 template<typename Iterator, typename Compare>
VmaQuickSort(Iterator beg,Iterator end,Compare cmp)2495 void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
2496 {
2497     if(beg < end)
2498     {
2499         Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
2500         VmaQuickSort<Iterator, Compare>(beg, it, cmp);
2501         VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
2502     }
2503 }
2504 
2505 #define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
2506 
2507 #endif // #ifndef VMA_SORT
2508 
2509 /*
2510 Returns true if two memory blocks occupy overlapping pages.
2511 ResourceA must be in less memory offset than ResourceB.
2512 
2513 Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
2514 chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
2515 */
VmaBlocksOnSamePage(VkDeviceSize resourceAOffset,VkDeviceSize resourceASize,VkDeviceSize resourceBOffset,VkDeviceSize pageSize)2516 static inline bool VmaBlocksOnSamePage(
2517     VkDeviceSize resourceAOffset,
2518     VkDeviceSize resourceASize,
2519     VkDeviceSize resourceBOffset,
2520     VkDeviceSize pageSize)
2521 {
2522     VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
2523     VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
2524     VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
2525     VkDeviceSize resourceBStart = resourceBOffset;
2526     VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
2527     return resourceAEndPage == resourceBStartPage;
2528 }
2529 
2530 enum VmaSuballocationType
2531 {
2532     VMA_SUBALLOCATION_TYPE_FREE = 0,
2533     VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
2534     VMA_SUBALLOCATION_TYPE_BUFFER = 2,
2535     VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
2536     VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
2537     VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
2538     VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
2539 };
2540 
2541 /*
2542 Returns true if given suballocation types could conflict and must respect
2543 VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
2544 or linear image and another one is optimal image. If type is unknown, behave
2545 conservatively.
2546 */
VmaIsBufferImageGranularityConflict(VmaSuballocationType suballocType1,VmaSuballocationType suballocType2)2547 static inline bool VmaIsBufferImageGranularityConflict(
2548     VmaSuballocationType suballocType1,
2549     VmaSuballocationType suballocType2)
2550 {
2551     if(suballocType1 > suballocType2)
2552     {
2553         VMA_SWAP(suballocType1, suballocType2);
2554     }
2555 
2556     switch(suballocType1)
2557     {
2558     case VMA_SUBALLOCATION_TYPE_FREE:
2559         return false;
2560     case VMA_SUBALLOCATION_TYPE_UNKNOWN:
2561         return true;
2562     case VMA_SUBALLOCATION_TYPE_BUFFER:
2563         return
2564             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
2565             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
2566     case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
2567         return
2568             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
2569             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
2570             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
2571     case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
2572         return
2573             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
2574     case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
2575         return false;
2576     default:
2577         VMA_ASSERT(0);
2578         return true;
2579     }
2580 }
2581 
2582 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
2583 struct VmaMutexLock
2584 {
2585 public:
VmaMutexLockVmaMutexLock2586     VmaMutexLock(VMA_MUTEX& mutex, bool useMutex) :
2587         m_pMutex(useMutex ? &mutex : VMA_NULL)
2588     {
2589         if(m_pMutex)
2590         {
2591             m_pMutex->Lock();
2592         }
2593     }
2594 
~VmaMutexLockVmaMutexLock2595     ~VmaMutexLock()
2596     {
2597         if(m_pMutex)
2598         {
2599             m_pMutex->Unlock();
2600         }
2601     }
2602 
2603 private:
2604     VMA_MUTEX* m_pMutex;
2605 };
2606 
2607 #if VMA_DEBUG_GLOBAL_MUTEX
2608     static VMA_MUTEX gDebugGlobalMutex;
2609     #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
2610 #else
2611     #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
2612 #endif
2613 
2614 // Minimum size of a free suballocation to register it in the free suballocation collection.
2615 static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
2616 
2617 /*
2618 Performs binary search and returns iterator to first element that is greater or
2619 equal to (key), according to comparison (cmp).
2620 
2621 Cmp should return true if first argument is less than second argument.
2622 
2623 Returned value is the found element, if present in the collection or place where
2624 new element with value (key) should be inserted.
2625 */
2626 template <typename IterT, typename KeyT, typename CmpT>
VmaBinaryFindFirstNotLess(IterT beg,IterT end,const KeyT & key,CmpT cmp)2627 static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, CmpT cmp)
2628 {
2629    size_t down = 0, up = (end - beg);
2630    while(down < up)
2631    {
2632       const size_t mid = (down + up) / 2;
2633       if(cmp(*(beg+mid), key))
2634       {
2635          down = mid + 1;
2636       }
2637       else
2638       {
2639          up = mid;
2640       }
2641    }
2642    return beg + down;
2643 }
2644 
2645 ////////////////////////////////////////////////////////////////////////////////
2646 // Memory allocation
2647 
VmaMalloc(const VkAllocationCallbacks * pAllocationCallbacks,size_t size,size_t alignment)2648 static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
2649 {
2650     if((pAllocationCallbacks != VMA_NULL) &&
2651         (pAllocationCallbacks->pfnAllocation != VMA_NULL))
2652     {
2653         return (*pAllocationCallbacks->pfnAllocation)(
2654             pAllocationCallbacks->pUserData,
2655             size,
2656             alignment,
2657             VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
2658     }
2659     else
2660     {
2661         return VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
2662     }
2663 }
2664 
VmaFree(const VkAllocationCallbacks * pAllocationCallbacks,void * ptr)2665 static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
2666 {
2667     if((pAllocationCallbacks != VMA_NULL) &&
2668         (pAllocationCallbacks->pfnFree != VMA_NULL))
2669     {
2670         (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
2671     }
2672     else
2673     {
2674         VMA_SYSTEM_FREE(ptr);
2675     }
2676 }
2677 
2678 template<typename T>
VmaAllocate(const VkAllocationCallbacks * pAllocationCallbacks)2679 static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
2680 {
2681     return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
2682 }
2683 
2684 template<typename T>
VmaAllocateArray(const VkAllocationCallbacks * pAllocationCallbacks,size_t count)2685 static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
2686 {
2687     return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
2688 }
2689 
2690 #define vma_new(allocator, type)   new(VmaAllocate<type>(allocator))(type)
2691 
2692 #define vma_new_array(allocator, type, count)   new(VmaAllocateArray<type>((allocator), (count)))(type)
2693 
2694 template<typename T>
vma_delete(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr)2695 static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
2696 {
2697     ptr->~T();
2698     VmaFree(pAllocationCallbacks, ptr);
2699 }
2700 
2701 template<typename T>
vma_delete_array(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr,size_t count)2702 static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
2703 {
2704     if(ptr != VMA_NULL)
2705     {
2706         for(size_t i = count; i--; )
2707         {
2708             ptr[i].~T();
2709         }
2710         VmaFree(pAllocationCallbacks, ptr);
2711     }
2712 }
2713 
2714 // STL-compatible allocator.
2715 template<typename T>
2716 class VmaStlAllocator
2717 {
2718 public:
2719     const VkAllocationCallbacks* const m_pCallbacks;
2720     typedef T value_type;
2721 
VmaStlAllocator(const VkAllocationCallbacks * pCallbacks)2722     VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
VmaStlAllocator(const VmaStlAllocator<U> & src)2723     template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
2724 
allocate(size_t n)2725     T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
deallocate(T * p,size_t n)2726     void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
2727 
2728     template<typename U>
2729     bool operator==(const VmaStlAllocator<U>& rhs) const
2730     {
2731         return m_pCallbacks == rhs.m_pCallbacks;
2732     }
2733     template<typename U>
2734     bool operator!=(const VmaStlAllocator<U>& rhs) const
2735     {
2736         return m_pCallbacks != rhs.m_pCallbacks;
2737     }
2738 
2739     VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
2740 };
2741 
2742 #if VMA_USE_STL_VECTOR
2743 
2744 #define VmaVector std::vector
2745 
2746 template<typename T, typename allocatorT>
VmaVectorInsert(std::vector<T,allocatorT> & vec,size_t index,const T & item)2747 static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
2748 {
2749     vec.insert(vec.begin() + index, item);
2750 }
2751 
2752 template<typename T, typename allocatorT>
VmaVectorRemove(std::vector<T,allocatorT> & vec,size_t index)2753 static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
2754 {
2755     vec.erase(vec.begin() + index);
2756 }
2757 
2758 #else // #if VMA_USE_STL_VECTOR
2759 
2760 /* Class with interface compatible with subset of std::vector.
2761 T must be POD because constructors and destructors are not called and memcpy is
2762 used for these objects. */
2763 template<typename T, typename AllocatorT>
2764 class VmaVector
2765 {
2766 public:
2767     typedef T value_type;
2768 
VmaVector(const AllocatorT & allocator)2769     VmaVector(const AllocatorT& allocator) :
2770         m_Allocator(allocator),
2771         m_pArray(VMA_NULL),
2772         m_Count(0),
2773         m_Capacity(0)
2774     {
2775     }
2776 
VmaVector(size_t count,const AllocatorT & allocator)2777     VmaVector(size_t count, const AllocatorT& allocator) :
2778         m_Allocator(allocator),
2779         m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
2780         m_Count(count),
2781         m_Capacity(count)
2782     {
2783     }
2784 
VmaVector(const VmaVector<T,AllocatorT> & src)2785     VmaVector(const VmaVector<T, AllocatorT>& src) :
2786         m_Allocator(src.m_Allocator),
2787         m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
2788         m_Count(src.m_Count),
2789         m_Capacity(src.m_Count)
2790     {
2791         if(m_Count != 0)
2792         {
2793             memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
2794         }
2795     }
2796 
~VmaVector()2797     ~VmaVector()
2798     {
2799         VmaFree(m_Allocator.m_pCallbacks, m_pArray);
2800     }
2801 
2802     VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
2803     {
2804         if(&rhs != this)
2805         {
2806             resize(rhs.m_Count);
2807             if(m_Count != 0)
2808             {
2809                 memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
2810             }
2811         }
2812         return *this;
2813     }
2814 
empty()2815     bool empty() const { return m_Count == 0; }
size()2816     size_t size() const { return m_Count; }
data()2817     T* data() { return m_pArray; }
data()2818     const T* data() const { return m_pArray; }
2819 
2820     T& operator[](size_t index)
2821     {
2822         VMA_HEAVY_ASSERT(index < m_Count);
2823         return m_pArray[index];
2824     }
2825     const T& operator[](size_t index) const
2826     {
2827         VMA_HEAVY_ASSERT(index < m_Count);
2828         return m_pArray[index];
2829     }
2830 
front()2831     T& front()
2832     {
2833         VMA_HEAVY_ASSERT(m_Count > 0);
2834         return m_pArray[0];
2835     }
front()2836     const T& front() const
2837     {
2838         VMA_HEAVY_ASSERT(m_Count > 0);
2839         return m_pArray[0];
2840     }
back()2841     T& back()
2842     {
2843         VMA_HEAVY_ASSERT(m_Count > 0);
2844         return m_pArray[m_Count - 1];
2845     }
back()2846     const T& back() const
2847     {
2848         VMA_HEAVY_ASSERT(m_Count > 0);
2849         return m_pArray[m_Count - 1];
2850     }
2851 
2852     void reserve(size_t newCapacity, bool freeMemory = false)
2853     {
2854         newCapacity = VMA_MAX(newCapacity, m_Count);
2855 
2856         if((newCapacity < m_Capacity) && !freeMemory)
2857         {
2858             newCapacity = m_Capacity;
2859         }
2860 
2861         if(newCapacity != m_Capacity)
2862         {
2863             T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
2864             if(m_Count != 0)
2865             {
2866                 memcpy(newArray, m_pArray, m_Count * sizeof(T));
2867             }
2868             VmaFree(m_Allocator.m_pCallbacks, m_pArray);
2869             m_Capacity = newCapacity;
2870             m_pArray = newArray;
2871         }
2872     }
2873 
2874     void resize(size_t newCount, bool freeMemory = false)
2875     {
2876         size_t newCapacity = m_Capacity;
2877         if(newCount > m_Capacity)
2878         {
2879             newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
2880         }
2881         else if(freeMemory)
2882         {
2883             newCapacity = newCount;
2884         }
2885 
2886         if(newCapacity != m_Capacity)
2887         {
2888             T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
2889             const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
2890             if(elementsToCopy != 0)
2891             {
2892                 memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
2893             }
2894             VmaFree(m_Allocator.m_pCallbacks, m_pArray);
2895             m_Capacity = newCapacity;
2896             m_pArray = newArray;
2897         }
2898 
2899         m_Count = newCount;
2900     }
2901 
2902     void clear(bool freeMemory = false)
2903     {
2904         resize(0, freeMemory);
2905     }
2906 
insert(size_t index,const T & src)2907     void insert(size_t index, const T& src)
2908     {
2909         VMA_HEAVY_ASSERT(index <= m_Count);
2910         const size_t oldCount = size();
2911         resize(oldCount + 1);
2912         if(index < oldCount)
2913         {
2914             memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
2915         }
2916         m_pArray[index] = src;
2917     }
2918 
remove(size_t index)2919     void remove(size_t index)
2920     {
2921         VMA_HEAVY_ASSERT(index < m_Count);
2922         const size_t oldCount = size();
2923         if(index < oldCount - 1)
2924         {
2925             memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
2926         }
2927         resize(oldCount - 1);
2928     }
2929 
push_back(const T & src)2930     void push_back(const T& src)
2931     {
2932         const size_t newIndex = size();
2933         resize(newIndex + 1);
2934         m_pArray[newIndex] = src;
2935     }
2936 
pop_back()2937     void pop_back()
2938     {
2939         VMA_HEAVY_ASSERT(m_Count > 0);
2940         resize(size() - 1);
2941     }
2942 
push_front(const T & src)2943     void push_front(const T& src)
2944     {
2945         insert(0, src);
2946     }
2947 
pop_front()2948     void pop_front()
2949     {
2950         VMA_HEAVY_ASSERT(m_Count > 0);
2951         remove(0);
2952     }
2953 
2954     typedef T* iterator;
2955 
begin()2956     iterator begin() { return m_pArray; }
end()2957     iterator end() { return m_pArray + m_Count; }
2958 
2959 private:
2960     AllocatorT m_Allocator;
2961     T* m_pArray;
2962     size_t m_Count;
2963     size_t m_Capacity;
2964 };
2965 
2966 template<typename T, typename allocatorT>
VmaVectorInsert(VmaVector<T,allocatorT> & vec,size_t index,const T & item)2967 static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
2968 {
2969     vec.insert(index, item);
2970 }
2971 
2972 template<typename T, typename allocatorT>
VmaVectorRemove(VmaVector<T,allocatorT> & vec,size_t index)2973 static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
2974 {
2975     vec.remove(index);
2976 }
2977 
2978 #endif // #if VMA_USE_STL_VECTOR
2979 
2980 template<typename CmpLess, typename VectorT>
VmaVectorInsertSorted(VectorT & vector,const typename VectorT::value_type & value)2981 size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
2982 {
2983     const size_t indexToInsert = VmaBinaryFindFirstNotLess(
2984         vector.data(),
2985         vector.data() + vector.size(),
2986         value,
2987         CmpLess()) - vector.data();
2988     VmaVectorInsert(vector, indexToInsert, value);
2989     return indexToInsert;
2990 }
2991 
2992 template<typename CmpLess, typename VectorT>
VmaVectorRemoveSorted(VectorT & vector,const typename VectorT::value_type & value)2993 bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
2994 {
2995     CmpLess comparator;
2996     typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
2997         vector.begin(),
2998         vector.end(),
2999         value,
3000         comparator);
3001     if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
3002     {
3003         size_t indexToRemove = it - vector.begin();
3004         VmaVectorRemove(vector, indexToRemove);
3005         return true;
3006     }
3007     return false;
3008 }
3009 
3010 template<typename CmpLess, typename VectorT>
VmaVectorFindSorted(const VectorT & vector,const typename VectorT::value_type & value)3011 size_t VmaVectorFindSorted(const VectorT& vector, const typename VectorT::value_type& value)
3012 {
3013     CmpLess comparator;
3014     typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
3015         vector.data(),
3016         vector.data() + vector.size(),
3017         value,
3018         comparator);
3019     if(it != vector.size() && !comparator(*it, value) && !comparator(value, *it))
3020     {
3021         return it - vector.begin();
3022     }
3023     else
3024     {
3025         return vector.size();
3026     }
3027 }
3028 
3029 ////////////////////////////////////////////////////////////////////////////////
3030 // class VmaPoolAllocator
3031 
3032 /*
3033 Allocator for objects of type T using a list of arrays (pools) to speed up
3034 allocation. Number of elements that can be allocated is not bounded because
3035 allocator can create multiple blocks.
3036 */
3037 template<typename T>
3038 class VmaPoolAllocator
3039 {
3040 public:
3041     VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock);
3042     ~VmaPoolAllocator();
3043     void Clear();
3044     T* Alloc();
3045     void Free(T* ptr);
3046 
3047 private:
3048     union Item
3049     {
3050         uint32_t NextFreeIndex;
3051         T Value;
3052     };
3053 
3054     struct ItemBlock
3055     {
3056         Item* pItems;
3057         uint32_t FirstFreeIndex;
3058     };
3059 
3060     const VkAllocationCallbacks* m_pAllocationCallbacks;
3061     size_t m_ItemsPerBlock;
3062     VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
3063 
3064     ItemBlock& CreateNewBlock();
3065 };
3066 
3067 template<typename T>
VmaPoolAllocator(const VkAllocationCallbacks * pAllocationCallbacks,size_t itemsPerBlock)3068 VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock) :
3069     m_pAllocationCallbacks(pAllocationCallbacks),
3070     m_ItemsPerBlock(itemsPerBlock),
3071     m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
3072 {
3073     VMA_ASSERT(itemsPerBlock > 0);
3074 }
3075 
3076 template<typename T>
~VmaPoolAllocator()3077 VmaPoolAllocator<T>::~VmaPoolAllocator()
3078 {
3079     Clear();
3080 }
3081 
3082 template<typename T>
Clear()3083 void VmaPoolAllocator<T>::Clear()
3084 {
3085     for(size_t i = m_ItemBlocks.size(); i--; )
3086         vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemsPerBlock);
3087     m_ItemBlocks.clear();
3088 }
3089 
3090 template<typename T>
Alloc()3091 T* VmaPoolAllocator<T>::Alloc()
3092 {
3093     for(size_t i = m_ItemBlocks.size(); i--; )
3094     {
3095         ItemBlock& block = m_ItemBlocks[i];
3096         // This block has some free items: Use first one.
3097         if(block.FirstFreeIndex != UINT32_MAX)
3098         {
3099             Item* const pItem = &block.pItems[block.FirstFreeIndex];
3100             block.FirstFreeIndex = pItem->NextFreeIndex;
3101             return &pItem->Value;
3102         }
3103     }
3104 
3105     // No block has free item: Create new one and use it.
3106     ItemBlock& newBlock = CreateNewBlock();
3107     Item* const pItem = &newBlock.pItems[0];
3108     newBlock.FirstFreeIndex = pItem->NextFreeIndex;
3109     return &pItem->Value;
3110 }
3111 
3112 template<typename T>
Free(T * ptr)3113 void VmaPoolAllocator<T>::Free(T* ptr)
3114 {
3115     // Search all memory blocks to find ptr.
3116     for(size_t i = 0; i < m_ItemBlocks.size(); ++i)
3117     {
3118         ItemBlock& block = m_ItemBlocks[i];
3119 
3120         // Casting to union.
3121         Item* pItemPtr;
3122         memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
3123 
3124         // Check if pItemPtr is in address range of this block.
3125         if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + m_ItemsPerBlock))
3126         {
3127             const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
3128             pItemPtr->NextFreeIndex = block.FirstFreeIndex;
3129             block.FirstFreeIndex = index;
3130             return;
3131         }
3132     }
3133     VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
3134 }
3135 
3136 template<typename T>
CreateNewBlock()3137 typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
3138 {
3139     ItemBlock newBlock = {
3140         vma_new_array(m_pAllocationCallbacks, Item, m_ItemsPerBlock), 0 };
3141 
3142     m_ItemBlocks.push_back(newBlock);
3143 
3144     // Setup singly-linked list of all free items in this block.
3145     for(uint32_t i = 0; i < m_ItemsPerBlock - 1; ++i)
3146         newBlock.pItems[i].NextFreeIndex = i + 1;
3147     newBlock.pItems[m_ItemsPerBlock - 1].NextFreeIndex = UINT32_MAX;
3148     return m_ItemBlocks.back();
3149 }
3150 
3151 ////////////////////////////////////////////////////////////////////////////////
3152 // class VmaRawList, VmaList
3153 
3154 #if VMA_USE_STL_LIST
3155 
3156 #define VmaList std::list
3157 
3158 #else // #if VMA_USE_STL_LIST
3159 
3160 template<typename T>
3161 struct VmaListItem
3162 {
3163     VmaListItem* pPrev;
3164     VmaListItem* pNext;
3165     T Value;
3166 };
3167 
3168 // Doubly linked list.
3169 template<typename T>
3170 class VmaRawList
3171 {
3172 public:
3173     typedef VmaListItem<T> ItemType;
3174 
3175     VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
3176     ~VmaRawList();
3177     void Clear();
3178 
GetCount()3179     size_t GetCount() const { return m_Count; }
IsEmpty()3180     bool IsEmpty() const { return m_Count == 0; }
3181 
Front()3182     ItemType* Front() { return m_pFront; }
Front()3183     const ItemType* Front() const { return m_pFront; }
Back()3184     ItemType* Back() { return m_pBack; }
Back()3185     const ItemType* Back() const { return m_pBack; }
3186 
3187     ItemType* PushBack();
3188     ItemType* PushFront();
3189     ItemType* PushBack(const T& value);
3190     ItemType* PushFront(const T& value);
3191     void PopBack();
3192     void PopFront();
3193 
3194     // Item can be null - it means PushBack.
3195     ItemType* InsertBefore(ItemType* pItem);
3196     // Item can be null - it means PushFront.
3197     ItemType* InsertAfter(ItemType* pItem);
3198 
3199     ItemType* InsertBefore(ItemType* pItem, const T& value);
3200     ItemType* InsertAfter(ItemType* pItem, const T& value);
3201 
3202     void Remove(ItemType* pItem);
3203 
3204 private:
3205     const VkAllocationCallbacks* const m_pAllocationCallbacks;
3206     VmaPoolAllocator<ItemType> m_ItemAllocator;
3207     ItemType* m_pFront;
3208     ItemType* m_pBack;
3209     size_t m_Count;
3210 
3211     // Declared not defined, to block copy constructor and assignment operator.
3212     VmaRawList(const VmaRawList<T>& src);
3213     VmaRawList<T>& operator=(const VmaRawList<T>& rhs);
3214 };
3215 
3216 template<typename T>
VmaRawList(const VkAllocationCallbacks * pAllocationCallbacks)3217 VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
3218     m_pAllocationCallbacks(pAllocationCallbacks),
3219     m_ItemAllocator(pAllocationCallbacks, 128),
3220     m_pFront(VMA_NULL),
3221     m_pBack(VMA_NULL),
3222     m_Count(0)
3223 {
3224 }
3225 
3226 template<typename T>
~VmaRawList()3227 VmaRawList<T>::~VmaRawList()
3228 {
3229     // Intentionally not calling Clear, because that would be unnecessary
3230     // computations to return all items to m_ItemAllocator as free.
3231 }
3232 
3233 template<typename T>
Clear()3234 void VmaRawList<T>::Clear()
3235 {
3236     if(IsEmpty() == false)
3237     {
3238         ItemType* pItem = m_pBack;
3239         while(pItem != VMA_NULL)
3240         {
3241             ItemType* const pPrevItem = pItem->pPrev;
3242             m_ItemAllocator.Free(pItem);
3243             pItem = pPrevItem;
3244         }
3245         m_pFront = VMA_NULL;
3246         m_pBack = VMA_NULL;
3247         m_Count = 0;
3248     }
3249 }
3250 
3251 template<typename T>
PushBack()3252 VmaListItem<T>* VmaRawList<T>::PushBack()
3253 {
3254     ItemType* const pNewItem = m_ItemAllocator.Alloc();
3255     pNewItem->pNext = VMA_NULL;
3256     if(IsEmpty())
3257     {
3258         pNewItem->pPrev = VMA_NULL;
3259         m_pFront = pNewItem;
3260         m_pBack = pNewItem;
3261         m_Count = 1;
3262     }
3263     else
3264     {
3265         pNewItem->pPrev = m_pBack;
3266         m_pBack->pNext = pNewItem;
3267         m_pBack = pNewItem;
3268         ++m_Count;
3269     }
3270     return pNewItem;
3271 }
3272 
3273 template<typename T>
PushFront()3274 VmaListItem<T>* VmaRawList<T>::PushFront()
3275 {
3276     ItemType* const pNewItem = m_ItemAllocator.Alloc();
3277     pNewItem->pPrev = VMA_NULL;
3278     if(IsEmpty())
3279     {
3280         pNewItem->pNext = VMA_NULL;
3281         m_pFront = pNewItem;
3282         m_pBack = pNewItem;
3283         m_Count = 1;
3284     }
3285     else
3286     {
3287         pNewItem->pNext = m_pFront;
3288         m_pFront->pPrev = pNewItem;
3289         m_pFront = pNewItem;
3290         ++m_Count;
3291     }
3292     return pNewItem;
3293 }
3294 
3295 template<typename T>
PushBack(const T & value)3296 VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
3297 {
3298     ItemType* const pNewItem = PushBack();
3299     pNewItem->Value = value;
3300     return pNewItem;
3301 }
3302 
3303 template<typename T>
PushFront(const T & value)3304 VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
3305 {
3306     ItemType* const pNewItem = PushFront();
3307     pNewItem->Value = value;
3308     return pNewItem;
3309 }
3310 
3311 template<typename T>
PopBack()3312 void VmaRawList<T>::PopBack()
3313 {
3314     VMA_HEAVY_ASSERT(m_Count > 0);
3315     ItemType* const pBackItem = m_pBack;
3316     ItemType* const pPrevItem = pBackItem->pPrev;
3317     if(pPrevItem != VMA_NULL)
3318     {
3319         pPrevItem->pNext = VMA_NULL;
3320     }
3321     m_pBack = pPrevItem;
3322     m_ItemAllocator.Free(pBackItem);
3323     --m_Count;
3324 }
3325 
3326 template<typename T>
PopFront()3327 void VmaRawList<T>::PopFront()
3328 {
3329     VMA_HEAVY_ASSERT(m_Count > 0);
3330     ItemType* const pFrontItem = m_pFront;
3331     ItemType* const pNextItem = pFrontItem->pNext;
3332     if(pNextItem != VMA_NULL)
3333     {
3334         pNextItem->pPrev = VMA_NULL;
3335     }
3336     m_pFront = pNextItem;
3337     m_ItemAllocator.Free(pFrontItem);
3338     --m_Count;
3339 }
3340 
3341 template<typename T>
Remove(ItemType * pItem)3342 void VmaRawList<T>::Remove(ItemType* pItem)
3343 {
3344     VMA_HEAVY_ASSERT(pItem != VMA_NULL);
3345     VMA_HEAVY_ASSERT(m_Count > 0);
3346 
3347     if(pItem->pPrev != VMA_NULL)
3348     {
3349         pItem->pPrev->pNext = pItem->pNext;
3350     }
3351     else
3352     {
3353         VMA_HEAVY_ASSERT(m_pFront == pItem);
3354         m_pFront = pItem->pNext;
3355     }
3356 
3357     if(pItem->pNext != VMA_NULL)
3358     {
3359         pItem->pNext->pPrev = pItem->pPrev;
3360     }
3361     else
3362     {
3363         VMA_HEAVY_ASSERT(m_pBack == pItem);
3364         m_pBack = pItem->pPrev;
3365     }
3366 
3367     m_ItemAllocator.Free(pItem);
3368     --m_Count;
3369 }
3370 
3371 template<typename T>
InsertBefore(ItemType * pItem)3372 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
3373 {
3374     if(pItem != VMA_NULL)
3375     {
3376         ItemType* const prevItem = pItem->pPrev;
3377         ItemType* const newItem = m_ItemAllocator.Alloc();
3378         newItem->pPrev = prevItem;
3379         newItem->pNext = pItem;
3380         pItem->pPrev = newItem;
3381         if(prevItem != VMA_NULL)
3382         {
3383             prevItem->pNext = newItem;
3384         }
3385         else
3386         {
3387             VMA_HEAVY_ASSERT(m_pFront == pItem);
3388             m_pFront = newItem;
3389         }
3390         ++m_Count;
3391         return newItem;
3392     }
3393     else
3394         return PushBack();
3395 }
3396 
3397 template<typename T>
InsertAfter(ItemType * pItem)3398 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
3399 {
3400     if(pItem != VMA_NULL)
3401     {
3402         ItemType* const nextItem = pItem->pNext;
3403         ItemType* const newItem = m_ItemAllocator.Alloc();
3404         newItem->pNext = nextItem;
3405         newItem->pPrev = pItem;
3406         pItem->pNext = newItem;
3407         if(nextItem != VMA_NULL)
3408         {
3409             nextItem->pPrev = newItem;
3410         }
3411         else
3412         {
3413             VMA_HEAVY_ASSERT(m_pBack == pItem);
3414             m_pBack = newItem;
3415         }
3416         ++m_Count;
3417         return newItem;
3418     }
3419     else
3420         return PushFront();
3421 }
3422 
3423 template<typename T>
InsertBefore(ItemType * pItem,const T & value)3424 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
3425 {
3426     ItemType* const newItem = InsertBefore(pItem);
3427     newItem->Value = value;
3428     return newItem;
3429 }
3430 
3431 template<typename T>
InsertAfter(ItemType * pItem,const T & value)3432 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
3433 {
3434     ItemType* const newItem = InsertAfter(pItem);
3435     newItem->Value = value;
3436     return newItem;
3437 }
3438 
3439 template<typename T, typename AllocatorT>
3440 class VmaList
3441 {
3442 public:
3443     class iterator
3444     {
3445     public:
iterator()3446         iterator() :
3447             m_pList(VMA_NULL),
3448             m_pItem(VMA_NULL)
3449         {
3450         }
3451 
3452         T& operator*() const
3453         {
3454             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
3455             return m_pItem->Value;
3456         }
3457         T* operator->() const
3458         {
3459             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
3460             return &m_pItem->Value;
3461         }
3462 
3463         iterator& operator++()
3464         {
3465             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
3466             m_pItem = m_pItem->pNext;
3467             return *this;
3468         }
3469         iterator& operator--()
3470         {
3471             if(m_pItem != VMA_NULL)
3472             {
3473                 m_pItem = m_pItem->pPrev;
3474             }
3475             else
3476             {
3477                 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
3478                 m_pItem = m_pList->Back();
3479             }
3480             return *this;
3481         }
3482 
3483         iterator operator++(int)
3484         {
3485             iterator result = *this;
3486             ++*this;
3487             return result;
3488         }
3489         iterator operator--(int)
3490         {
3491             iterator result = *this;
3492             --*this;
3493             return result;
3494         }
3495 
3496         bool operator==(const iterator& rhs) const
3497         {
3498             VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
3499             return m_pItem == rhs.m_pItem;
3500         }
3501         bool operator!=(const iterator& rhs) const
3502         {
3503             VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
3504             return m_pItem != rhs.m_pItem;
3505         }
3506 
3507     private:
3508         VmaRawList<T>* m_pList;
3509         VmaListItem<T>* m_pItem;
3510 
iterator(VmaRawList<T> * pList,VmaListItem<T> * pItem)3511         iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
3512             m_pList(pList),
3513             m_pItem(pItem)
3514         {
3515         }
3516 
3517         friend class VmaList<T, AllocatorT>;
3518     };
3519 
3520     class const_iterator
3521     {
3522     public:
const_iterator()3523         const_iterator() :
3524             m_pList(VMA_NULL),
3525             m_pItem(VMA_NULL)
3526         {
3527         }
3528 
const_iterator(const iterator & src)3529         const_iterator(const iterator& src) :
3530             m_pList(src.m_pList),
3531             m_pItem(src.m_pItem)
3532         {
3533         }
3534 
3535         const T& operator*() const
3536         {
3537             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
3538             return m_pItem->Value;
3539         }
3540         const T* operator->() const
3541         {
3542             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
3543             return &m_pItem->Value;
3544         }
3545 
3546         const_iterator& operator++()
3547         {
3548             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
3549             m_pItem = m_pItem->pNext;
3550             return *this;
3551         }
3552         const_iterator& operator--()
3553         {
3554             if(m_pItem != VMA_NULL)
3555             {
3556                 m_pItem = m_pItem->pPrev;
3557             }
3558             else
3559             {
3560                 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
3561                 m_pItem = m_pList->Back();
3562             }
3563             return *this;
3564         }
3565 
3566         const_iterator operator++(int)
3567         {
3568             const_iterator result = *this;
3569             ++*this;
3570             return result;
3571         }
3572         const_iterator operator--(int)
3573         {
3574             const_iterator result = *this;
3575             --*this;
3576             return result;
3577         }
3578 
3579         bool operator==(const const_iterator& rhs) const
3580         {
3581             VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
3582             return m_pItem == rhs.m_pItem;
3583         }
3584         bool operator!=(const const_iterator& rhs) const
3585         {
3586             VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
3587             return m_pItem != rhs.m_pItem;
3588         }
3589 
3590     private:
const_iterator(const VmaRawList<T> * pList,const VmaListItem<T> * pItem)3591         const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
3592             m_pList(pList),
3593             m_pItem(pItem)
3594         {
3595         }
3596 
3597         const VmaRawList<T>* m_pList;
3598         const VmaListItem<T>* m_pItem;
3599 
3600         friend class VmaList<T, AllocatorT>;
3601     };
3602 
VmaList(const AllocatorT & allocator)3603     VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
3604 
empty()3605     bool empty() const { return m_RawList.IsEmpty(); }
size()3606     size_t size() const { return m_RawList.GetCount(); }
3607 
begin()3608     iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
end()3609     iterator end() { return iterator(&m_RawList, VMA_NULL); }
3610 
cbegin()3611     const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
cend()3612     const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
3613 
clear()3614     void clear() { m_RawList.Clear(); }
push_back(const T & value)3615     void push_back(const T& value) { m_RawList.PushBack(value); }
erase(iterator it)3616     void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
insert(iterator it,const T & value)3617     iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
3618 
3619 private:
3620     VmaRawList<T> m_RawList;
3621 };
3622 
3623 #endif // #if VMA_USE_STL_LIST
3624 
3625 ////////////////////////////////////////////////////////////////////////////////
3626 // class VmaMap
3627 
3628 // Unused in this version.
3629 #if 0
3630 
3631 #if VMA_USE_STL_UNORDERED_MAP
3632 
3633 #define VmaPair std::pair
3634 
3635 #define VMA_MAP_TYPE(KeyT, ValueT) \
3636     std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
3637 
3638 #else // #if VMA_USE_STL_UNORDERED_MAP
3639 
3640 template<typename T1, typename T2>
3641 struct VmaPair
3642 {
3643     T1 first;
3644     T2 second;
3645 
3646     VmaPair() : first(), second() { }
3647     VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
3648 };
3649 
3650 /* Class compatible with subset of interface of std::unordered_map.
3651 KeyT, ValueT must be POD because they will be stored in VmaVector.
3652 */
3653 template<typename KeyT, typename ValueT>
3654 class VmaMap
3655 {
3656 public:
3657     typedef VmaPair<KeyT, ValueT> PairType;
3658     typedef PairType* iterator;
3659 
3660     VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
3661 
3662     iterator begin() { return m_Vector.begin(); }
3663     iterator end() { return m_Vector.end(); }
3664 
3665     void insert(const PairType& pair);
3666     iterator find(const KeyT& key);
3667     void erase(iterator it);
3668 
3669 private:
3670     VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
3671 };
3672 
3673 #define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
3674 
3675 template<typename FirstT, typename SecondT>
3676 struct VmaPairFirstLess
3677 {
3678     bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
3679     {
3680         return lhs.first < rhs.first;
3681     }
3682     bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
3683     {
3684         return lhs.first < rhsFirst;
3685     }
3686 };
3687 
3688 template<typename KeyT, typename ValueT>
3689 void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
3690 {
3691     const size_t indexToInsert = VmaBinaryFindFirstNotLess(
3692         m_Vector.data(),
3693         m_Vector.data() + m_Vector.size(),
3694         pair,
3695         VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
3696     VmaVectorInsert(m_Vector, indexToInsert, pair);
3697 }
3698 
3699 template<typename KeyT, typename ValueT>
3700 VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
3701 {
3702     PairType* it = VmaBinaryFindFirstNotLess(
3703         m_Vector.data(),
3704         m_Vector.data() + m_Vector.size(),
3705         key,
3706         VmaPairFirstLess<KeyT, ValueT>());
3707     if((it != m_Vector.end()) && (it->first == key))
3708     {
3709         return it;
3710     }
3711     else
3712     {
3713         return m_Vector.end();
3714     }
3715 }
3716 
3717 template<typename KeyT, typename ValueT>
3718 void VmaMap<KeyT, ValueT>::erase(iterator it)
3719 {
3720     VmaVectorRemove(m_Vector, it - m_Vector.begin());
3721 }
3722 
3723 #endif // #if VMA_USE_STL_UNORDERED_MAP
3724 
3725 #endif // #if 0
3726 
3727 ////////////////////////////////////////////////////////////////////////////////
3728 
3729 class VmaDeviceMemoryBlock;
3730 
3731 struct VmaAllocation_T
3732 {
3733 private:
3734     static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
3735 
3736     enum FLAGS
3737     {
3738         FLAG_USER_DATA_STRING = 0x01,
3739     };
3740 
3741 public:
3742     enum ALLOCATION_TYPE
3743     {
3744         ALLOCATION_TYPE_NONE,
3745         ALLOCATION_TYPE_BLOCK,
3746         ALLOCATION_TYPE_DEDICATED,
3747     };
3748 
VmaAllocation_TVmaAllocation_T3749     VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
3750         m_Alignment(1),
3751         m_Size(0),
3752         m_pUserData(VMA_NULL),
3753         m_LastUseFrameIndex(currentFrameIndex),
3754         m_Type((uint8_t)ALLOCATION_TYPE_NONE),
3755         m_SuballocationType((uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN),
3756         m_MapCount(0),
3757         m_Flags(userDataString ? (uint8_t)FLAG_USER_DATA_STRING : 0)
3758     {
3759     }
3760 
~VmaAllocation_TVmaAllocation_T3761     ~VmaAllocation_T()
3762     {
3763         VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
3764 
3765         // Check if owned string was freed.
3766         VMA_ASSERT(m_pUserData == VMA_NULL);
3767     }
3768 
InitBlockAllocationVmaAllocation_T3769     void InitBlockAllocation(
3770         VmaPool hPool,
3771         VmaDeviceMemoryBlock* block,
3772         VkDeviceSize offset,
3773         VkDeviceSize alignment,
3774         VkDeviceSize size,
3775         VmaSuballocationType suballocationType,
3776         bool mapped,
3777         bool canBecomeLost)
3778     {
3779         VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
3780         VMA_ASSERT(block != VMA_NULL);
3781         m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
3782         m_Alignment = alignment;
3783         m_Size = size;
3784         m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
3785         m_SuballocationType = (uint8_t)suballocationType;
3786         m_BlockAllocation.m_hPool = hPool;
3787         m_BlockAllocation.m_Block = block;
3788         m_BlockAllocation.m_Offset = offset;
3789         m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
3790     }
3791 
InitLostVmaAllocation_T3792     void InitLost()
3793     {
3794         VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
3795         VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
3796         m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
3797         m_BlockAllocation.m_hPool = VK_NULL_HANDLE;
3798         m_BlockAllocation.m_Block = VMA_NULL;
3799         m_BlockAllocation.m_Offset = 0;
3800         m_BlockAllocation.m_CanBecomeLost = true;
3801     }
3802 
3803     void ChangeBlockAllocation(
3804         VmaAllocator hAllocator,
3805         VmaDeviceMemoryBlock* block,
3806         VkDeviceSize offset);
3807 
3808     // pMappedData not null means allocation is created with MAPPED flag.
InitDedicatedAllocationVmaAllocation_T3809     void InitDedicatedAllocation(
3810         uint32_t memoryTypeIndex,
3811         VkDeviceMemory hMemory,
3812         VmaSuballocationType suballocationType,
3813         void* pMappedData,
3814         VkDeviceSize size)
3815     {
3816         VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
3817         VMA_ASSERT(hMemory != VK_NULL_HANDLE);
3818         m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
3819         m_Alignment = 0;
3820         m_Size = size;
3821         m_SuballocationType = (uint8_t)suballocationType;
3822         m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
3823         m_DedicatedAllocation.m_MemoryTypeIndex = memoryTypeIndex;
3824         m_DedicatedAllocation.m_hMemory = hMemory;
3825         m_DedicatedAllocation.m_pMappedData = pMappedData;
3826     }
3827 
GetTypeVmaAllocation_T3828     ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
GetAlignmentVmaAllocation_T3829     VkDeviceSize GetAlignment() const { return m_Alignment; }
GetSizeVmaAllocation_T3830     VkDeviceSize GetSize() const { return m_Size; }
IsUserDataStringVmaAllocation_T3831     bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
GetUserDataVmaAllocation_T3832     void* GetUserData() const { return m_pUserData; }
3833     void SetUserData(VmaAllocator hAllocator, void* pUserData);
GetSuballocationTypeVmaAllocation_T3834     VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
3835 
GetBlockVmaAllocation_T3836     VmaDeviceMemoryBlock* GetBlock() const
3837     {
3838         VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
3839         return m_BlockAllocation.m_Block;
3840     }
3841     VkDeviceSize GetOffset() const;
3842     VkDeviceMemory GetMemory() const;
3843     uint32_t GetMemoryTypeIndex() const;
IsPersistentMapVmaAllocation_T3844     bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
3845     void* GetMappedData() const;
3846     bool CanBecomeLost() const;
3847     VmaPool GetPool() const;
3848 
GetLastUseFrameIndexVmaAllocation_T3849     uint32_t GetLastUseFrameIndex() const
3850     {
3851         return m_LastUseFrameIndex.load();
3852     }
CompareExchangeLastUseFrameIndexVmaAllocation_T3853     bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
3854     {
3855         return m_LastUseFrameIndex.compare_exchange_weak(expected, desired);
3856     }
3857     /*
3858     - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
3859       makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
3860     - Else, returns false.
3861 
3862     If hAllocation is already lost, assert - you should not call it then.
3863     If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
3864     */
3865     bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
3866 
DedicatedAllocCalcStatsInfoVmaAllocation_T3867     void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
3868     {
3869         VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
3870         outInfo.blockCount = 1;
3871         outInfo.allocationCount = 1;
3872         outInfo.unusedRangeCount = 0;
3873         outInfo.usedBytes = m_Size;
3874         outInfo.unusedBytes = 0;
3875         outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
3876         outInfo.unusedRangeSizeMin = UINT64_MAX;
3877         outInfo.unusedRangeSizeMax = 0;
3878     }
3879 
3880     void BlockAllocMap();
3881     void BlockAllocUnmap();
3882     VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
3883     void DedicatedAllocUnmap(VmaAllocator hAllocator);
3884 
3885 private:
3886     VkDeviceSize m_Alignment;
3887     VkDeviceSize m_Size;
3888     void* m_pUserData;
3889     VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
3890     uint8_t m_Type; // ALLOCATION_TYPE
3891     uint8_t m_SuballocationType; // VmaSuballocationType
3892     // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
3893     // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
3894     uint8_t m_MapCount;
3895     uint8_t m_Flags; // enum FLAGS
3896 
3897     // Allocation out of VmaDeviceMemoryBlock.
3898     struct BlockAllocation
3899     {
3900         VmaPool m_hPool; // Null if belongs to general memory.
3901         VmaDeviceMemoryBlock* m_Block;
3902         VkDeviceSize m_Offset;
3903         bool m_CanBecomeLost;
3904     };
3905 
3906     // Allocation for an object that has its own private VkDeviceMemory.
3907     struct DedicatedAllocation
3908     {
3909         uint32_t m_MemoryTypeIndex;
3910         VkDeviceMemory m_hMemory;
3911         void* m_pMappedData; // Not null means memory is mapped.
3912     };
3913 
3914     union
3915     {
3916         // Allocation out of VmaDeviceMemoryBlock.
3917         BlockAllocation m_BlockAllocation;
3918         // Allocation for an object that has its own private VkDeviceMemory.
3919         DedicatedAllocation m_DedicatedAllocation;
3920     };
3921 
3922     void FreeUserDataString(VmaAllocator hAllocator);
3923 };
3924 
3925 /*
3926 Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
3927 allocated memory block or free.
3928 */
3929 struct VmaSuballocation
3930 {
3931     VkDeviceSize offset;
3932     VkDeviceSize size;
3933     VmaAllocation hAllocation;
3934     VmaSuballocationType type;
3935 };
3936 
3937 typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
3938 
3939 // Cost of one additional allocation lost, as equivalent in bytes.
3940 static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
3941 
3942 /*
3943 Parameters of planned allocation inside a VmaDeviceMemoryBlock.
3944 
3945 If canMakeOtherLost was false:
3946 - item points to a FREE suballocation.
3947 - itemsToMakeLostCount is 0.
3948 
3949 If canMakeOtherLost was true:
3950 - item points to first of sequence of suballocations, which are either FREE,
3951   or point to VmaAllocations that can become lost.
3952 - itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
3953   the requested allocation to succeed.
3954 */
3955 struct VmaAllocationRequest
3956 {
3957     VkDeviceSize offset;
3958     VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
3959     VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
3960     VmaSuballocationList::iterator item;
3961     size_t itemsToMakeLostCount;
3962 
CalcCostVmaAllocationRequest3963     VkDeviceSize CalcCost() const
3964     {
3965         return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
3966     }
3967 };
3968 
3969 /*
3970 Data structure used for bookkeeping of allocations and unused ranges of memory
3971 in a single VkDeviceMemory block.
3972 */
3973 class VmaBlockMetadata
3974 {
3975 public:
3976     VmaBlockMetadata(VmaAllocator hAllocator);
3977     ~VmaBlockMetadata();
3978     void Init(VkDeviceSize size);
3979 
3980     // Validates all data structures inside this object. If not valid, returns false.
3981     bool Validate() const;
GetSize()3982     VkDeviceSize GetSize() const { return m_Size; }
GetAllocationCount()3983     size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
GetSumFreeSize()3984     VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
3985     VkDeviceSize GetUnusedRangeSizeMax() const;
3986     // Returns true if this block is empty - contains only single free suballocation.
3987     bool IsEmpty() const;
3988 
3989     void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
3990     void AddPoolStats(VmaPoolStats& inoutStats) const;
3991 
3992 #if VMA_STATS_STRING_ENABLED
3993     void PrintDetailedMap(class VmaJsonWriter& json) const;
3994 #endif
3995 
3996     // Creates trivial request for case when block is empty.
3997     void CreateFirstAllocationRequest(VmaAllocationRequest* pAllocationRequest);
3998 
3999     // Tries to find a place for suballocation with given parameters inside this block.
4000     // If succeeded, fills pAllocationRequest and returns true.
4001     // If failed, returns false.
4002     bool CreateAllocationRequest(
4003         uint32_t currentFrameIndex,
4004         uint32_t frameInUseCount,
4005         VkDeviceSize bufferImageGranularity,
4006         VkDeviceSize allocSize,
4007         VkDeviceSize allocAlignment,
4008         VmaSuballocationType allocType,
4009         bool canMakeOtherLost,
4010         VmaAllocationRequest* pAllocationRequest);
4011 
4012     bool MakeRequestedAllocationsLost(
4013         uint32_t currentFrameIndex,
4014         uint32_t frameInUseCount,
4015         VmaAllocationRequest* pAllocationRequest);
4016 
4017     uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
4018 
4019     // Makes actual allocation based on request. Request must already be checked and valid.
4020     void Alloc(
4021         const VmaAllocationRequest& request,
4022         VmaSuballocationType type,
4023         VkDeviceSize allocSize,
4024         VmaAllocation hAllocation);
4025 
4026     // Frees suballocation assigned to given memory region.
4027     void Free(const VmaAllocation allocation);
4028     void FreeAtOffset(VkDeviceSize offset);
4029 
4030 private:
4031     VkDeviceSize m_Size;
4032     uint32_t m_FreeCount;
4033     VkDeviceSize m_SumFreeSize;
4034     VmaSuballocationList m_Suballocations;
4035     // Suballocations that are free and have size greater than certain threshold.
4036     // Sorted by size, ascending.
4037     VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
4038 
4039     bool ValidateFreeSuballocationList() const;
4040 
4041     // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
4042     // If yes, fills pOffset and returns true. If no, returns false.
4043     bool CheckAllocation(
4044         uint32_t currentFrameIndex,
4045         uint32_t frameInUseCount,
4046         VkDeviceSize bufferImageGranularity,
4047         VkDeviceSize allocSize,
4048         VkDeviceSize allocAlignment,
4049         VmaSuballocationType allocType,
4050         VmaSuballocationList::const_iterator suballocItem,
4051         bool canMakeOtherLost,
4052         VkDeviceSize* pOffset,
4053         size_t* itemsToMakeLostCount,
4054         VkDeviceSize* pSumFreeSize,
4055         VkDeviceSize* pSumItemSize) const;
4056     // Given free suballocation, it merges it with following one, which must also be free.
4057     void MergeFreeWithNext(VmaSuballocationList::iterator item);
4058     // Releases given suballocation, making it free.
4059     // Merges it with adjacent free suballocations if applicable.
4060     // Returns iterator to new free suballocation at this place.
4061     VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
4062     // Given free suballocation, it inserts it into sorted list of
4063     // m_FreeSuballocationsBySize if it's suitable.
4064     void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
4065     // Given free suballocation, it removes it from sorted list of
4066     // m_FreeSuballocationsBySize if it's suitable.
4067     void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
4068 };
4069 
4070 /*
4071 Represents a single block of device memory (`VkDeviceMemory`) with all the
4072 data about its regions (aka suballocations, #VmaAllocation), assigned and free.
4073 
4074 Thread-safety: This class must be externally synchronized.
4075 */
4076 class VmaDeviceMemoryBlock
4077 {
4078 public:
4079     VmaBlockMetadata m_Metadata;
4080 
4081     VmaDeviceMemoryBlock(VmaAllocator hAllocator);
4082 
~VmaDeviceMemoryBlock()4083     ~VmaDeviceMemoryBlock()
4084     {
4085         VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
4086         VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
4087     }
4088 
4089     // Always call after construction.
4090     void Init(
4091         uint32_t newMemoryTypeIndex,
4092         VkDeviceMemory newMemory,
4093         VkDeviceSize newSize);
4094     // Always call before destruction.
4095     void Destroy(VmaAllocator allocator);
4096 
GetDeviceMemory()4097     VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
GetMemoryTypeIndex()4098     uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetMappedData()4099     void* GetMappedData() const { return m_pMappedData; }
4100 
4101     // Validates all data structures inside this object. If not valid, returns false.
4102     bool Validate() const;
4103 
4104     // ppData can be null.
4105     VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
4106     void Unmap(VmaAllocator hAllocator, uint32_t count);
4107 
4108     VkResult BindBufferMemory(
4109         const VmaAllocator hAllocator,
4110         const VmaAllocation hAllocation,
4111         VkBuffer hBuffer);
4112     VkResult BindImageMemory(
4113         const VmaAllocator hAllocator,
4114         const VmaAllocation hAllocation,
4115         VkImage hImage);
4116 
4117 private:
4118     uint32_t m_MemoryTypeIndex;
4119     VkDeviceMemory m_hMemory;
4120 
4121     // Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
4122     // Also protects m_MapCount, m_pMappedData.
4123     VMA_MUTEX m_Mutex;
4124     uint32_t m_MapCount;
4125     void* m_pMappedData;
4126 };
4127 
4128 struct VmaPointerLess
4129 {
operatorVmaPointerLess4130     bool operator()(const void* lhs, const void* rhs) const
4131     {
4132         return lhs < rhs;
4133     }
4134 };
4135 
4136 class VmaDefragmentator;
4137 
4138 /*
4139 Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
4140 Vulkan memory type.
4141 
4142 Synchronized internally with a mutex.
4143 */
4144 struct VmaBlockVector
4145 {
4146     VmaBlockVector(
4147         VmaAllocator hAllocator,
4148         uint32_t memoryTypeIndex,
4149         VkDeviceSize preferredBlockSize,
4150         size_t minBlockCount,
4151         size_t maxBlockCount,
4152         VkDeviceSize bufferImageGranularity,
4153         uint32_t frameInUseCount,
4154         bool isCustomPool);
4155     ~VmaBlockVector();
4156 
4157     VkResult CreateMinBlocks();
4158 
GetMemoryTypeIndexVmaBlockVector4159     uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetPreferredBlockSizeVmaBlockVector4160     VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
GetBufferImageGranularityVmaBlockVector4161     VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
GetFrameInUseCountVmaBlockVector4162     uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
4163 
4164     void GetPoolStats(VmaPoolStats* pStats);
4165 
IsEmptyVmaBlockVector4166     bool IsEmpty() const { return m_Blocks.empty(); }
4167 
4168     VkResult Allocate(
4169         VmaPool hCurrentPool,
4170         uint32_t currentFrameIndex,
4171         const VkMemoryRequirements& vkMemReq,
4172         const VmaAllocationCreateInfo& createInfo,
4173         VmaSuballocationType suballocType,
4174         VmaAllocation* pAllocation);
4175 
4176     void Free(
4177         VmaAllocation hAllocation);
4178 
4179     // Adds statistics of this BlockVector to pStats.
4180     void AddStats(VmaStats* pStats);
4181 
4182 #if VMA_STATS_STRING_ENABLED
4183     void PrintDetailedMap(class VmaJsonWriter& json);
4184 #endif
4185 
4186     void MakePoolAllocationsLost(
4187         uint32_t currentFrameIndex,
4188         size_t* pLostAllocationCount);
4189 
4190     VmaDefragmentator* EnsureDefragmentator(
4191         VmaAllocator hAllocator,
4192         uint32_t currentFrameIndex);
4193 
4194     VkResult Defragment(
4195         VmaDefragmentationStats* pDefragmentationStats,
4196         VkDeviceSize& maxBytesToMove,
4197         uint32_t& maxAllocationsToMove);
4198 
4199     void DestroyDefragmentator();
4200 
4201 private:
4202     friend class VmaDefragmentator;
4203 
4204     const VmaAllocator m_hAllocator;
4205     const uint32_t m_MemoryTypeIndex;
4206     const VkDeviceSize m_PreferredBlockSize;
4207     const size_t m_MinBlockCount;
4208     const size_t m_MaxBlockCount;
4209     const VkDeviceSize m_BufferImageGranularity;
4210     const uint32_t m_FrameInUseCount;
4211     const bool m_IsCustomPool;
4212     VMA_MUTEX m_Mutex;
4213     // Incrementally sorted by sumFreeSize, ascending.
4214     VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
4215     /* There can be at most one allocation that is completely empty - a
4216     hysteresis to avoid pessimistic case of alternating creation and destruction
4217     of a VkDeviceMemory. */
4218     bool m_HasEmptyBlock;
4219     VmaDefragmentator* m_pDefragmentator;
4220 
4221     size_t CalcMaxBlockSize() const;
4222 
4223     // Finds and removes given block from vector.
4224     void Remove(VmaDeviceMemoryBlock* pBlock);
4225 
4226     // Performs single step in sorting m_Blocks. They may not be fully sorted
4227     // after this call.
4228     void IncrementallySortBlocks();
4229 
4230     VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
4231 };
4232 
4233 struct VmaPool_T
4234 {
4235 public:
4236     VmaBlockVector m_BlockVector;
4237 
4238     // Takes ownership.
4239     VmaPool_T(
4240         VmaAllocator hAllocator,
4241         const VmaPoolCreateInfo& createInfo);
4242     ~VmaPool_T();
4243 
GetBlockVectorVmaPool_T4244     VmaBlockVector& GetBlockVector() { return m_BlockVector; }
4245 
4246 #if VMA_STATS_STRING_ENABLED
4247     //void PrintDetailedMap(class VmaStringBuilder& sb);
4248 #endif
4249 };
4250 
4251 class VmaDefragmentator
4252 {
4253     const VmaAllocator m_hAllocator;
4254     VmaBlockVector* const m_pBlockVector;
4255     uint32_t m_CurrentFrameIndex;
4256     VkDeviceSize m_BytesMoved;
4257     uint32_t m_AllocationsMoved;
4258 
4259     struct AllocationInfo
4260     {
4261         VmaAllocation m_hAllocation;
4262         VkBool32* m_pChanged;
4263 
AllocationInfoAllocationInfo4264         AllocationInfo() :
4265             m_hAllocation(VK_NULL_HANDLE),
4266             m_pChanged(VMA_NULL)
4267         {
4268         }
4269     };
4270 
4271     struct AllocationInfoSizeGreater
4272     {
operatorAllocationInfoSizeGreater4273         bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
4274         {
4275             return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
4276         }
4277     };
4278 
4279     // Used between AddAllocation and Defragment.
4280     VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
4281 
4282     struct BlockInfo
4283     {
4284         VmaDeviceMemoryBlock* m_pBlock;
4285         bool m_HasNonMovableAllocations;
4286         VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
4287 
BlockInfoBlockInfo4288         BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
4289             m_pBlock(VMA_NULL),
4290             m_HasNonMovableAllocations(true),
4291             m_Allocations(pAllocationCallbacks),
4292             m_pMappedDataForDefragmentation(VMA_NULL)
4293         {
4294         }
4295 
CalcHasNonMovableAllocationsBlockInfo4296         void CalcHasNonMovableAllocations()
4297         {
4298             const size_t blockAllocCount = m_pBlock->m_Metadata.GetAllocationCount();
4299             const size_t defragmentAllocCount = m_Allocations.size();
4300             m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
4301         }
4302 
SortAllocationsBySizeDescecndingBlockInfo4303         void SortAllocationsBySizeDescecnding()
4304         {
4305             VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
4306         }
4307 
4308         VkResult EnsureMapping(VmaAllocator hAllocator, void** ppMappedData);
4309         void Unmap(VmaAllocator hAllocator);
4310 
4311     private:
4312         // Not null if mapped for defragmentation only, not originally mapped.
4313         void* m_pMappedDataForDefragmentation;
4314     };
4315 
4316     struct BlockPointerLess
4317     {
operatorBlockPointerLess4318         bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
4319         {
4320             return pLhsBlockInfo->m_pBlock < pRhsBlock;
4321         }
operatorBlockPointerLess4322         bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
4323         {
4324             return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
4325         }
4326     };
4327 
4328     // 1. Blocks with some non-movable allocations go first.
4329     // 2. Blocks with smaller sumFreeSize go first.
4330     struct BlockInfoCompareMoveDestination
4331     {
operatorBlockInfoCompareMoveDestination4332         bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
4333         {
4334             if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
4335             {
4336                 return true;
4337             }
4338             if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
4339             {
4340                 return false;
4341             }
4342             if(pLhsBlockInfo->m_pBlock->m_Metadata.GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_Metadata.GetSumFreeSize())
4343             {
4344                 return true;
4345             }
4346             return false;
4347         }
4348     };
4349 
4350     typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
4351     BlockInfoVector m_Blocks;
4352 
4353     VkResult DefragmentRound(
4354         VkDeviceSize maxBytesToMove,
4355         uint32_t maxAllocationsToMove);
4356 
4357     static bool MoveMakesSense(
4358         size_t dstBlockIndex, VkDeviceSize dstOffset,
4359         size_t srcBlockIndex, VkDeviceSize srcOffset);
4360 
4361 public:
4362     VmaDefragmentator(
4363         VmaAllocator hAllocator,
4364         VmaBlockVector* pBlockVector,
4365         uint32_t currentFrameIndex);
4366 
4367     ~VmaDefragmentator();
4368 
GetBytesMoved()4369     VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
GetAllocationsMoved()4370     uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
4371 
4372     void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
4373 
4374     VkResult Defragment(
4375         VkDeviceSize maxBytesToMove,
4376         uint32_t maxAllocationsToMove);
4377 };
4378 
4379 // Main allocator object.
4380 struct VmaAllocator_T
4381 {
4382     bool m_UseMutex;
4383     bool m_UseKhrDedicatedAllocation;
4384     VkDevice m_hDevice;
4385     bool m_AllocationCallbacksSpecified;
4386     VkAllocationCallbacks m_AllocationCallbacks;
4387     VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
4388 
4389     // Number of bytes free out of limit, or VK_WHOLE_SIZE if not limit for that heap.
4390     VkDeviceSize m_HeapSizeLimit[VK_MAX_MEMORY_HEAPS];
4391     VMA_MUTEX m_HeapSizeLimitMutex;
4392 
4393     VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
4394     VkPhysicalDeviceMemoryProperties m_MemProps;
4395 
4396     // Default pools.
4397     VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
4398 
4399     // Each vector is sorted by memory (handle value).
4400     typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
4401     AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES];
4402     VMA_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
4403 
4404     VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
4405     ~VmaAllocator_T();
4406 
GetAllocationCallbacksVmaAllocator_T4407     const VkAllocationCallbacks* GetAllocationCallbacks() const
4408     {
4409         return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
4410     }
GetVulkanFunctionsVmaAllocator_T4411     const VmaVulkanFunctions& GetVulkanFunctions() const
4412     {
4413         return m_VulkanFunctions;
4414     }
4415 
GetBufferImageGranularityVmaAllocator_T4416     VkDeviceSize GetBufferImageGranularity() const
4417     {
4418         return VMA_MAX(
4419             static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
4420             m_PhysicalDeviceProperties.limits.bufferImageGranularity);
4421     }
4422 
GetMemoryHeapCountVmaAllocator_T4423     uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
GetMemoryTypeCountVmaAllocator_T4424     uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
4425 
MemoryTypeIndexToHeapIndexVmaAllocator_T4426     uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
4427     {
4428         VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
4429         return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
4430     }
4431 
4432     void GetBufferMemoryRequirements(
4433         VkBuffer hBuffer,
4434         VkMemoryRequirements& memReq,
4435         bool& requiresDedicatedAllocation,
4436         bool& prefersDedicatedAllocation) const;
4437     void GetImageMemoryRequirements(
4438         VkImage hImage,
4439         VkMemoryRequirements& memReq,
4440         bool& requiresDedicatedAllocation,
4441         bool& prefersDedicatedAllocation) const;
4442 
4443     // Main allocation function.
4444     VkResult AllocateMemory(
4445         const VkMemoryRequirements& vkMemReq,
4446         bool requiresDedicatedAllocation,
4447         bool prefersDedicatedAllocation,
4448         VkBuffer dedicatedBuffer,
4449         VkImage dedicatedImage,
4450         const VmaAllocationCreateInfo& createInfo,
4451         VmaSuballocationType suballocType,
4452         VmaAllocation* pAllocation);
4453 
4454     // Main deallocation function.
4455     void FreeMemory(const VmaAllocation allocation);
4456 
4457     void CalculateStats(VmaStats* pStats);
4458 
4459 #if VMA_STATS_STRING_ENABLED
4460     void PrintDetailedMap(class VmaJsonWriter& json);
4461 #endif
4462 
4463     VkResult Defragment(
4464         VmaAllocation* pAllocations,
4465         size_t allocationCount,
4466         VkBool32* pAllocationsChanged,
4467         const VmaDefragmentationInfo* pDefragmentationInfo,
4468         VmaDefragmentationStats* pDefragmentationStats);
4469 
4470     void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
4471     bool TouchAllocation(VmaAllocation hAllocation);
4472 
4473     VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
4474     void DestroyPool(VmaPool pool);
4475     void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
4476 
4477     void SetCurrentFrameIndex(uint32_t frameIndex);
4478 
4479     void MakePoolAllocationsLost(
4480         VmaPool hPool,
4481         size_t* pLostAllocationCount);
4482 
4483     void CreateLostAllocation(VmaAllocation* pAllocation);
4484 
4485     VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
4486     void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
4487 
4488     VkResult Map(VmaAllocation hAllocation, void** ppData);
4489     void Unmap(VmaAllocation hAllocation);
4490 
4491     VkResult BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer);
4492     VkResult BindImageMemory(VmaAllocation hAllocation, VkImage hImage);
4493 
4494 private:
4495     VkDeviceSize m_PreferredLargeHeapBlockSize;
4496 
4497     VkPhysicalDevice m_PhysicalDevice;
4498     VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
4499 
4500     VMA_MUTEX m_PoolsMutex;
4501     // Protected by m_PoolsMutex. Sorted by pointer value.
4502     VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools;
4503 
4504     VmaVulkanFunctions m_VulkanFunctions;
4505 
4506     void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
4507 
4508     VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
4509 
4510     VkResult AllocateMemoryOfType(
4511         const VkMemoryRequirements& vkMemReq,
4512         bool dedicatedAllocation,
4513         VkBuffer dedicatedBuffer,
4514         VkImage dedicatedImage,
4515         const VmaAllocationCreateInfo& createInfo,
4516         uint32_t memTypeIndex,
4517         VmaSuballocationType suballocType,
4518         VmaAllocation* pAllocation);
4519 
4520     // Allocates and registers new VkDeviceMemory specifically for single allocation.
4521     VkResult AllocateDedicatedMemory(
4522         VkDeviceSize size,
4523         VmaSuballocationType suballocType,
4524         uint32_t memTypeIndex,
4525         bool map,
4526         bool isUserDataString,
4527         void* pUserData,
4528         VkBuffer dedicatedBuffer,
4529         VkImage dedicatedImage,
4530         VmaAllocation* pAllocation);
4531 
4532     // Tries to free pMemory as Dedicated Memory. Returns true if found and freed.
4533     void FreeDedicatedMemory(VmaAllocation allocation);
4534 };
4535 
4536 ////////////////////////////////////////////////////////////////////////////////
4537 // Memory allocation #2 after VmaAllocator_T definition
4538 
VmaMalloc(VmaAllocator hAllocator,size_t size,size_t alignment)4539 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
4540 {
4541     return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
4542 }
4543 
VmaFree(VmaAllocator hAllocator,void * ptr)4544 static void VmaFree(VmaAllocator hAllocator, void* ptr)
4545 {
4546     VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
4547 }
4548 
4549 template<typename T>
VmaAllocate(VmaAllocator hAllocator)4550 static T* VmaAllocate(VmaAllocator hAllocator)
4551 {
4552     return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
4553 }
4554 
4555 template<typename T>
VmaAllocateArray(VmaAllocator hAllocator,size_t count)4556 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
4557 {
4558     return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
4559 }
4560 
4561 template<typename T>
vma_delete(VmaAllocator hAllocator,T * ptr)4562 static void vma_delete(VmaAllocator hAllocator, T* ptr)
4563 {
4564     if(ptr != VMA_NULL)
4565     {
4566         ptr->~T();
4567         VmaFree(hAllocator, ptr);
4568     }
4569 }
4570 
4571 template<typename T>
vma_delete_array(VmaAllocator hAllocator,T * ptr,size_t count)4572 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
4573 {
4574     if(ptr != VMA_NULL)
4575     {
4576         for(size_t i = count; i--; )
4577             ptr[i].~T();
4578         VmaFree(hAllocator, ptr);
4579     }
4580 }
4581 
4582 ////////////////////////////////////////////////////////////////////////////////
4583 // VmaStringBuilder
4584 
4585 #if VMA_STATS_STRING_ENABLED
4586 
4587 class VmaStringBuilder
4588 {
4589 public:
VmaStringBuilder(VmaAllocator alloc)4590     VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
GetLength()4591     size_t GetLength() const { return m_Data.size(); }
GetData()4592     const char* GetData() const { return m_Data.data(); }
4593 
Add(char ch)4594     void Add(char ch) { m_Data.push_back(ch); }
4595     void Add(const char* pStr);
AddNewLine()4596     void AddNewLine() { Add('\n'); }
4597     void AddNumber(uint32_t num);
4598     void AddNumber(uint64_t num);
4599     void AddPointer(const void* ptr);
4600 
4601 private:
4602     VmaVector< char, VmaStlAllocator<char> > m_Data;
4603 };
4604 
Add(const char * pStr)4605 void VmaStringBuilder::Add(const char* pStr)
4606 {
4607     const size_t strLen = strlen(pStr);
4608     if(strLen > 0)
4609     {
4610         const size_t oldCount = m_Data.size();
4611         m_Data.resize(oldCount + strLen);
4612         memcpy(m_Data.data() + oldCount, pStr, strLen);
4613     }
4614 }
4615 
AddNumber(uint32_t num)4616 void VmaStringBuilder::AddNumber(uint32_t num)
4617 {
4618     char buf[11];
4619     VmaUint32ToStr(buf, sizeof(buf), num);
4620     Add(buf);
4621 }
4622 
AddNumber(uint64_t num)4623 void VmaStringBuilder::AddNumber(uint64_t num)
4624 {
4625     char buf[21];
4626     VmaUint64ToStr(buf, sizeof(buf), num);
4627     Add(buf);
4628 }
4629 
AddPointer(const void * ptr)4630 void VmaStringBuilder::AddPointer(const void* ptr)
4631 {
4632     char buf[21];
4633     VmaPtrToStr(buf, sizeof(buf), ptr);
4634     Add(buf);
4635 }
4636 
4637 #endif // #if VMA_STATS_STRING_ENABLED
4638 
4639 ////////////////////////////////////////////////////////////////////////////////
4640 // VmaJsonWriter
4641 
4642 #if VMA_STATS_STRING_ENABLED
4643 
4644 class VmaJsonWriter
4645 {
4646 public:
4647     VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
4648     ~VmaJsonWriter();
4649 
4650     void BeginObject(bool singleLine = false);
4651     void EndObject();
4652 
4653     void BeginArray(bool singleLine = false);
4654     void EndArray();
4655 
4656     void WriteString(const char* pStr);
4657     void BeginString(const char* pStr = VMA_NULL);
4658     void ContinueString(const char* pStr);
4659     void ContinueString(uint32_t n);
4660     void ContinueString(uint64_t n);
4661     void ContinueString_Pointer(const void* ptr);
4662     void EndString(const char* pStr = VMA_NULL);
4663 
4664     void WriteNumber(uint32_t n);
4665     void WriteNumber(uint64_t n);
4666     void WriteBool(bool b);
4667     void WriteNull();
4668 
4669 private:
4670     static const char* const INDENT;
4671 
4672     enum COLLECTION_TYPE
4673     {
4674         COLLECTION_TYPE_OBJECT,
4675         COLLECTION_TYPE_ARRAY,
4676     };
4677     struct StackItem
4678     {
4679         COLLECTION_TYPE type;
4680         uint32_t valueCount;
4681         bool singleLineMode;
4682     };
4683 
4684     VmaStringBuilder& m_SB;
4685     VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
4686     bool m_InsideString;
4687 
4688     void BeginValue(bool isString);
4689     void WriteIndent(bool oneLess = false);
4690 };
4691 
4692 const char* const VmaJsonWriter::INDENT = "  ";
4693 
VmaJsonWriter(const VkAllocationCallbacks * pAllocationCallbacks,VmaStringBuilder & sb)4694 VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
4695     m_SB(sb),
4696     m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
4697     m_InsideString(false)
4698 {
4699 }
4700 
~VmaJsonWriter()4701 VmaJsonWriter::~VmaJsonWriter()
4702 {
4703     VMA_ASSERT(!m_InsideString);
4704     VMA_ASSERT(m_Stack.empty());
4705 }
4706 
BeginObject(bool singleLine)4707 void VmaJsonWriter::BeginObject(bool singleLine)
4708 {
4709     VMA_ASSERT(!m_InsideString);
4710 
4711     BeginValue(false);
4712     m_SB.Add('{');
4713 
4714     StackItem item;
4715     item.type = COLLECTION_TYPE_OBJECT;
4716     item.valueCount = 0;
4717     item.singleLineMode = singleLine;
4718     m_Stack.push_back(item);
4719 }
4720 
EndObject()4721 void VmaJsonWriter::EndObject()
4722 {
4723     VMA_ASSERT(!m_InsideString);
4724 
4725     WriteIndent(true);
4726     m_SB.Add('}');
4727 
4728     VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
4729     m_Stack.pop_back();
4730 }
4731 
BeginArray(bool singleLine)4732 void VmaJsonWriter::BeginArray(bool singleLine)
4733 {
4734     VMA_ASSERT(!m_InsideString);
4735 
4736     BeginValue(false);
4737     m_SB.Add('[');
4738 
4739     StackItem item;
4740     item.type = COLLECTION_TYPE_ARRAY;
4741     item.valueCount = 0;
4742     item.singleLineMode = singleLine;
4743     m_Stack.push_back(item);
4744 }
4745 
EndArray()4746 void VmaJsonWriter::EndArray()
4747 {
4748     VMA_ASSERT(!m_InsideString);
4749 
4750     WriteIndent(true);
4751     m_SB.Add(']');
4752 
4753     VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
4754     m_Stack.pop_back();
4755 }
4756 
WriteString(const char * pStr)4757 void VmaJsonWriter::WriteString(const char* pStr)
4758 {
4759     BeginString(pStr);
4760     EndString();
4761 }
4762 
BeginString(const char * pStr)4763 void VmaJsonWriter::BeginString(const char* pStr)
4764 {
4765     VMA_ASSERT(!m_InsideString);
4766 
4767     BeginValue(true);
4768     m_SB.Add('"');
4769     m_InsideString = true;
4770     if(pStr != VMA_NULL && pStr[0] != '\0')
4771     {
4772         ContinueString(pStr);
4773     }
4774 }
4775 
ContinueString(const char * pStr)4776 void VmaJsonWriter::ContinueString(const char* pStr)
4777 {
4778     VMA_ASSERT(m_InsideString);
4779 
4780     const size_t strLen = strlen(pStr);
4781     for(size_t i = 0; i < strLen; ++i)
4782     {
4783         char ch = pStr[i];
4784         if(ch == '\'')
4785         {
4786             m_SB.Add("\\\\");
4787         }
4788         else if(ch == '"')
4789         {
4790             m_SB.Add("\\\"");
4791         }
4792         else if(ch >= 32)
4793         {
4794             m_SB.Add(ch);
4795         }
4796         else switch(ch)
4797         {
4798         case '\b':
4799             m_SB.Add("\\b");
4800             break;
4801         case '\f':
4802             m_SB.Add("\\f");
4803             break;
4804         case '\n':
4805             m_SB.Add("\\n");
4806             break;
4807         case '\r':
4808             m_SB.Add("\\r");
4809             break;
4810         case '\t':
4811             m_SB.Add("\\t");
4812             break;
4813         default:
4814             VMA_ASSERT(0 && "Character not currently supported.");
4815             break;
4816         }
4817     }
4818 }
4819 
ContinueString(uint32_t n)4820 void VmaJsonWriter::ContinueString(uint32_t n)
4821 {
4822     VMA_ASSERT(m_InsideString);
4823     m_SB.AddNumber(n);
4824 }
4825 
ContinueString(uint64_t n)4826 void VmaJsonWriter::ContinueString(uint64_t n)
4827 {
4828     VMA_ASSERT(m_InsideString);
4829     m_SB.AddNumber(n);
4830 }
4831 
ContinueString_Pointer(const void * ptr)4832 void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
4833 {
4834     VMA_ASSERT(m_InsideString);
4835     m_SB.AddPointer(ptr);
4836 }
4837 
EndString(const char * pStr)4838 void VmaJsonWriter::EndString(const char* pStr)
4839 {
4840     VMA_ASSERT(m_InsideString);
4841     if(pStr != VMA_NULL && pStr[0] != '\0')
4842     {
4843         ContinueString(pStr);
4844     }
4845     m_SB.Add('"');
4846     m_InsideString = false;
4847 }
4848 
WriteNumber(uint32_t n)4849 void VmaJsonWriter::WriteNumber(uint32_t n)
4850 {
4851     VMA_ASSERT(!m_InsideString);
4852     BeginValue(false);
4853     m_SB.AddNumber(n);
4854 }
4855 
WriteNumber(uint64_t n)4856 void VmaJsonWriter::WriteNumber(uint64_t n)
4857 {
4858     VMA_ASSERT(!m_InsideString);
4859     BeginValue(false);
4860     m_SB.AddNumber(n);
4861 }
4862 
WriteBool(bool b)4863 void VmaJsonWriter::WriteBool(bool b)
4864 {
4865     VMA_ASSERT(!m_InsideString);
4866     BeginValue(false);
4867     m_SB.Add(b ? "true" : "false");
4868 }
4869 
WriteNull()4870 void VmaJsonWriter::WriteNull()
4871 {
4872     VMA_ASSERT(!m_InsideString);
4873     BeginValue(false);
4874     m_SB.Add("null");
4875 }
4876 
BeginValue(bool isString)4877 void VmaJsonWriter::BeginValue(bool isString)
4878 {
4879     if(!m_Stack.empty())
4880     {
4881         StackItem& currItem = m_Stack.back();
4882         if(currItem.type == COLLECTION_TYPE_OBJECT &&
4883             currItem.valueCount % 2 == 0)
4884         {
4885             VMA_ASSERT(isString);
4886         }
4887 
4888         if(currItem.type == COLLECTION_TYPE_OBJECT &&
4889             currItem.valueCount % 2 != 0)
4890         {
4891             m_SB.Add(": ");
4892         }
4893         else if(currItem.valueCount > 0)
4894         {
4895             m_SB.Add(", ");
4896             WriteIndent();
4897         }
4898         else
4899         {
4900             WriteIndent();
4901         }
4902         ++currItem.valueCount;
4903     }
4904 }
4905 
WriteIndent(bool oneLess)4906 void VmaJsonWriter::WriteIndent(bool oneLess)
4907 {
4908     if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
4909     {
4910         m_SB.AddNewLine();
4911 
4912         size_t count = m_Stack.size();
4913         if(count > 0 && oneLess)
4914         {
4915             --count;
4916         }
4917         for(size_t i = 0; i < count; ++i)
4918         {
4919             m_SB.Add(INDENT);
4920         }
4921     }
4922 }
4923 
4924 #endif // #if VMA_STATS_STRING_ENABLED
4925 
4926 ////////////////////////////////////////////////////////////////////////////////
4927 
SetUserData(VmaAllocator hAllocator,void * pUserData)4928 void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
4929 {
4930     if(IsUserDataString())
4931     {
4932         VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
4933 
4934         FreeUserDataString(hAllocator);
4935 
4936         if(pUserData != VMA_NULL)
4937         {
4938             const char* const newStrSrc = (char*)pUserData;
4939             const size_t newStrLen = strlen(newStrSrc);
4940             char* const newStrDst = vma_new_array(hAllocator, char, newStrLen + 1);
4941             memcpy(newStrDst, newStrSrc, newStrLen + 1);
4942             m_pUserData = newStrDst;
4943         }
4944     }
4945     else
4946     {
4947         m_pUserData = pUserData;
4948     }
4949 }
4950 
ChangeBlockAllocation(VmaAllocator hAllocator,VmaDeviceMemoryBlock * block,VkDeviceSize offset)4951 void VmaAllocation_T::ChangeBlockAllocation(
4952     VmaAllocator hAllocator,
4953     VmaDeviceMemoryBlock* block,
4954     VkDeviceSize offset)
4955 {
4956     VMA_ASSERT(block != VMA_NULL);
4957     VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
4958 
4959     // Move mapping reference counter from old block to new block.
4960     if(block != m_BlockAllocation.m_Block)
4961     {
4962         uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
4963         if(IsPersistentMap())
4964             ++mapRefCount;
4965         m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
4966         block->Map(hAllocator, mapRefCount, VMA_NULL);
4967     }
4968 
4969     m_BlockAllocation.m_Block = block;
4970     m_BlockAllocation.m_Offset = offset;
4971 }
4972 
GetOffset()4973 VkDeviceSize VmaAllocation_T::GetOffset() const
4974 {
4975     switch(m_Type)
4976     {
4977     case ALLOCATION_TYPE_BLOCK:
4978         return m_BlockAllocation.m_Offset;
4979     case ALLOCATION_TYPE_DEDICATED:
4980         return 0;
4981     default:
4982         VMA_ASSERT(0);
4983         return 0;
4984     }
4985 }
4986 
GetMemory()4987 VkDeviceMemory VmaAllocation_T::GetMemory() const
4988 {
4989     switch(m_Type)
4990     {
4991     case ALLOCATION_TYPE_BLOCK:
4992         return m_BlockAllocation.m_Block->GetDeviceMemory();
4993     case ALLOCATION_TYPE_DEDICATED:
4994         return m_DedicatedAllocation.m_hMemory;
4995     default:
4996         VMA_ASSERT(0);
4997         return VK_NULL_HANDLE;
4998     }
4999 }
5000 
GetMemoryTypeIndex()5001 uint32_t VmaAllocation_T::GetMemoryTypeIndex() const
5002 {
5003     switch(m_Type)
5004     {
5005     case ALLOCATION_TYPE_BLOCK:
5006         return m_BlockAllocation.m_Block->GetMemoryTypeIndex();
5007     case ALLOCATION_TYPE_DEDICATED:
5008         return m_DedicatedAllocation.m_MemoryTypeIndex;
5009     default:
5010         VMA_ASSERT(0);
5011         return UINT32_MAX;
5012     }
5013 }
5014 
GetMappedData()5015 void* VmaAllocation_T::GetMappedData() const
5016 {
5017     switch(m_Type)
5018     {
5019     case ALLOCATION_TYPE_BLOCK:
5020         if(m_MapCount != 0)
5021         {
5022             void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
5023             VMA_ASSERT(pBlockData != VMA_NULL);
5024             return (char*)pBlockData + m_BlockAllocation.m_Offset;
5025         }
5026         else
5027         {
5028             return VMA_NULL;
5029         }
5030         break;
5031     case ALLOCATION_TYPE_DEDICATED:
5032         VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
5033         return m_DedicatedAllocation.m_pMappedData;
5034     default:
5035         VMA_ASSERT(0);
5036         return VMA_NULL;
5037     }
5038 }
5039 
CanBecomeLost()5040 bool VmaAllocation_T::CanBecomeLost() const
5041 {
5042     switch(m_Type)
5043     {
5044     case ALLOCATION_TYPE_BLOCK:
5045         return m_BlockAllocation.m_CanBecomeLost;
5046     case ALLOCATION_TYPE_DEDICATED:
5047         return false;
5048     default:
5049         VMA_ASSERT(0);
5050         return false;
5051     }
5052 }
5053 
GetPool()5054 VmaPool VmaAllocation_T::GetPool() const
5055 {
5056     VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
5057     return m_BlockAllocation.m_hPool;
5058 }
5059 
MakeLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)5060 bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
5061 {
5062     VMA_ASSERT(CanBecomeLost());
5063 
5064     /*
5065     Warning: This is a carefully designed algorithm.
5066     Do not modify unless you really know what you're doing :)
5067     */
5068     uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
5069     for(;;)
5070     {
5071         if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
5072         {
5073             VMA_ASSERT(0);
5074             return false;
5075         }
5076         else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
5077         {
5078             return false;
5079         }
5080         else // Last use time earlier than current time.
5081         {
5082             if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
5083             {
5084                 // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
5085                 // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
5086                 return true;
5087             }
5088         }
5089     }
5090 }
5091 
FreeUserDataString(VmaAllocator hAllocator)5092 void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
5093 {
5094     VMA_ASSERT(IsUserDataString());
5095     if(m_pUserData != VMA_NULL)
5096     {
5097         char* const oldStr = (char*)m_pUserData;
5098         const size_t oldStrLen = strlen(oldStr);
5099         vma_delete_array(hAllocator, oldStr, oldStrLen + 1);
5100         m_pUserData = VMA_NULL;
5101     }
5102 }
5103 
BlockAllocMap()5104 void VmaAllocation_T::BlockAllocMap()
5105 {
5106     VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
5107 
5108     if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
5109     {
5110         ++m_MapCount;
5111     }
5112     else
5113     {
5114         VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
5115     }
5116 }
5117 
BlockAllocUnmap()5118 void VmaAllocation_T::BlockAllocUnmap()
5119 {
5120     VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
5121 
5122     if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
5123     {
5124         --m_MapCount;
5125     }
5126     else
5127     {
5128         VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
5129     }
5130 }
5131 
DedicatedAllocMap(VmaAllocator hAllocator,void ** ppData)5132 VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
5133 {
5134     VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
5135 
5136     if(m_MapCount != 0)
5137     {
5138         if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
5139         {
5140             VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
5141             *ppData = m_DedicatedAllocation.m_pMappedData;
5142             ++m_MapCount;
5143             return VK_SUCCESS;
5144         }
5145         else
5146         {
5147             VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
5148             return VK_ERROR_MEMORY_MAP_FAILED;
5149         }
5150     }
5151     else
5152     {
5153         VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
5154             hAllocator->m_hDevice,
5155             m_DedicatedAllocation.m_hMemory,
5156             0, // offset
5157             VK_WHOLE_SIZE,
5158             0, // flags
5159             ppData);
5160         if(result == VK_SUCCESS)
5161         {
5162             m_DedicatedAllocation.m_pMappedData = *ppData;
5163             m_MapCount = 1;
5164         }
5165         return result;
5166     }
5167 }
5168 
DedicatedAllocUnmap(VmaAllocator hAllocator)5169 void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
5170 {
5171     VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
5172 
5173     if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
5174     {
5175         --m_MapCount;
5176         if(m_MapCount == 0)
5177         {
5178             m_DedicatedAllocation.m_pMappedData = VMA_NULL;
5179             (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
5180                 hAllocator->m_hDevice,
5181                 m_DedicatedAllocation.m_hMemory);
5182         }
5183     }
5184     else
5185     {
5186         VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
5187     }
5188 }
5189 
5190 #if VMA_STATS_STRING_ENABLED
5191 
5192 // Correspond to values of enum VmaSuballocationType.
5193 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
5194     "FREE",
5195     "UNKNOWN",
5196     "BUFFER",
5197     "IMAGE_UNKNOWN",
5198     "IMAGE_LINEAR",
5199     "IMAGE_OPTIMAL",
5200 };
5201 
VmaPrintStatInfo(VmaJsonWriter & json,const VmaStatInfo & stat)5202 static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
5203 {
5204     json.BeginObject();
5205 
5206     json.WriteString("Blocks");
5207     json.WriteNumber(stat.blockCount);
5208 
5209     json.WriteString("Allocations");
5210     json.WriteNumber(stat.allocationCount);
5211 
5212     json.WriteString("UnusedRanges");
5213     json.WriteNumber(stat.unusedRangeCount);
5214 
5215     json.WriteString("UsedBytes");
5216     json.WriteNumber(stat.usedBytes);
5217 
5218     json.WriteString("UnusedBytes");
5219     json.WriteNumber(stat.unusedBytes);
5220 
5221     if(stat.allocationCount > 1)
5222     {
5223         json.WriteString("AllocationSize");
5224         json.BeginObject(true);
5225         json.WriteString("Min");
5226         json.WriteNumber(stat.allocationSizeMin);
5227         json.WriteString("Avg");
5228         json.WriteNumber(stat.allocationSizeAvg);
5229         json.WriteString("Max");
5230         json.WriteNumber(stat.allocationSizeMax);
5231         json.EndObject();
5232     }
5233 
5234     if(stat.unusedRangeCount > 1)
5235     {
5236         json.WriteString("UnusedRangeSize");
5237         json.BeginObject(true);
5238         json.WriteString("Min");
5239         json.WriteNumber(stat.unusedRangeSizeMin);
5240         json.WriteString("Avg");
5241         json.WriteNumber(stat.unusedRangeSizeAvg);
5242         json.WriteString("Max");
5243         json.WriteNumber(stat.unusedRangeSizeMax);
5244         json.EndObject();
5245     }
5246 
5247     json.EndObject();
5248 }
5249 
5250 #endif // #if VMA_STATS_STRING_ENABLED
5251 
5252 struct VmaSuballocationItemSizeLess
5253 {
operatorVmaSuballocationItemSizeLess5254     bool operator()(
5255         const VmaSuballocationList::iterator lhs,
5256         const VmaSuballocationList::iterator rhs) const
5257     {
5258         return lhs->size < rhs->size;
5259     }
operatorVmaSuballocationItemSizeLess5260     bool operator()(
5261         const VmaSuballocationList::iterator lhs,
5262         VkDeviceSize rhsSize) const
5263     {
5264         return lhs->size < rhsSize;
5265     }
5266 };
5267 
5268 ////////////////////////////////////////////////////////////////////////////////
5269 // class VmaBlockMetadata
5270 
VmaBlockMetadata(VmaAllocator hAllocator)5271 VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
5272     m_Size(0),
5273     m_FreeCount(0),
5274     m_SumFreeSize(0),
5275     m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
5276     m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
5277 {
5278 }
5279 
~VmaBlockMetadata()5280 VmaBlockMetadata::~VmaBlockMetadata()
5281 {
5282 }
5283 
Init(VkDeviceSize size)5284 void VmaBlockMetadata::Init(VkDeviceSize size)
5285 {
5286     m_Size = size;
5287     m_FreeCount = 1;
5288     m_SumFreeSize = size;
5289 
5290     VmaSuballocation suballoc = {};
5291     suballoc.offset = 0;
5292     suballoc.size = size;
5293     suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
5294     suballoc.hAllocation = VK_NULL_HANDLE;
5295 
5296     m_Suballocations.push_back(suballoc);
5297     VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
5298     --suballocItem;
5299     m_FreeSuballocationsBySize.push_back(suballocItem);
5300 }
5301 
Validate()5302 bool VmaBlockMetadata::Validate() const
5303 {
5304     if(m_Suballocations.empty())
5305     {
5306         return false;
5307     }
5308 
5309     // Expected offset of new suballocation as calculates from previous ones.
5310     VkDeviceSize calculatedOffset = 0;
5311     // Expected number of free suballocations as calculated from traversing their list.
5312     uint32_t calculatedFreeCount = 0;
5313     // Expected sum size of free suballocations as calculated from traversing their list.
5314     VkDeviceSize calculatedSumFreeSize = 0;
5315     // Expected number of free suballocations that should be registered in
5316     // m_FreeSuballocationsBySize calculated from traversing their list.
5317     size_t freeSuballocationsToRegister = 0;
5318     // True if previous visisted suballocation was free.
5319     bool prevFree = false;
5320 
5321     for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
5322         suballocItem != m_Suballocations.cend();
5323         ++suballocItem)
5324     {
5325         const VmaSuballocation& subAlloc = *suballocItem;
5326 
5327         // Actual offset of this suballocation doesn't match expected one.
5328         if(subAlloc.offset != calculatedOffset)
5329         {
5330             return false;
5331         }
5332 
5333         const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
5334         // Two adjacent free suballocations are invalid. They should be merged.
5335         if(prevFree && currFree)
5336         {
5337             return false;
5338         }
5339 
5340         if(currFree != (subAlloc.hAllocation == VK_NULL_HANDLE))
5341         {
5342             return false;
5343         }
5344 
5345         if(currFree)
5346         {
5347             calculatedSumFreeSize += subAlloc.size;
5348             ++calculatedFreeCount;
5349             if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
5350             {
5351                 ++freeSuballocationsToRegister;
5352             }
5353         }
5354         else
5355         {
5356             if(subAlloc.hAllocation->GetOffset() != subAlloc.offset)
5357             {
5358                 return false;
5359             }
5360             if(subAlloc.hAllocation->GetSize() != subAlloc.size)
5361             {
5362                 return false;
5363             }
5364         }
5365 
5366         calculatedOffset += subAlloc.size;
5367         prevFree = currFree;
5368     }
5369 
5370     // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
5371     // match expected one.
5372     if(m_FreeSuballocationsBySize.size() != freeSuballocationsToRegister)
5373     {
5374         return false;
5375     }
5376 
5377     VkDeviceSize lastSize = 0;
5378     for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
5379     {
5380         VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
5381 
5382         // Only free suballocations can be registered in m_FreeSuballocationsBySize.
5383         if(suballocItem->type != VMA_SUBALLOCATION_TYPE_FREE)
5384         {
5385             return false;
5386         }
5387         // They must be sorted by size ascending.
5388         if(suballocItem->size < lastSize)
5389         {
5390             return false;
5391         }
5392 
5393         lastSize = suballocItem->size;
5394     }
5395 
5396     // Check if totals match calculacted values.
5397     if(!ValidateFreeSuballocationList() ||
5398         (calculatedOffset != m_Size) ||
5399         (calculatedSumFreeSize != m_SumFreeSize) ||
5400         (calculatedFreeCount != m_FreeCount))
5401     {
5402         return false;
5403     }
5404 
5405     return true;
5406 }
5407 
GetUnusedRangeSizeMax()5408 VkDeviceSize VmaBlockMetadata::GetUnusedRangeSizeMax() const
5409 {
5410     if(!m_FreeSuballocationsBySize.empty())
5411     {
5412         return m_FreeSuballocationsBySize.back()->size;
5413     }
5414     else
5415     {
5416         return 0;
5417     }
5418 }
5419 
IsEmpty()5420 bool VmaBlockMetadata::IsEmpty() const
5421 {
5422     return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
5423 }
5424 
CalcAllocationStatInfo(VmaStatInfo & outInfo)5425 void VmaBlockMetadata::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
5426 {
5427     outInfo.blockCount = 1;
5428 
5429     const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
5430     outInfo.allocationCount = rangeCount - m_FreeCount;
5431     outInfo.unusedRangeCount = m_FreeCount;
5432 
5433     outInfo.unusedBytes = m_SumFreeSize;
5434     outInfo.usedBytes = m_Size - outInfo.unusedBytes;
5435 
5436     outInfo.allocationSizeMin = UINT64_MAX;
5437     outInfo.allocationSizeMax = 0;
5438     outInfo.unusedRangeSizeMin = UINT64_MAX;
5439     outInfo.unusedRangeSizeMax = 0;
5440 
5441     for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
5442         suballocItem != m_Suballocations.cend();
5443         ++suballocItem)
5444     {
5445         const VmaSuballocation& suballoc = *suballocItem;
5446         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
5447         {
5448             outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
5449             outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
5450         }
5451         else
5452         {
5453             outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
5454             outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
5455         }
5456     }
5457 }
5458 
AddPoolStats(VmaPoolStats & inoutStats)5459 void VmaBlockMetadata::AddPoolStats(VmaPoolStats& inoutStats) const
5460 {
5461     const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
5462 
5463     inoutStats.size += m_Size;
5464     inoutStats.unusedSize += m_SumFreeSize;
5465     inoutStats.allocationCount += rangeCount - m_FreeCount;
5466     inoutStats.unusedRangeCount += m_FreeCount;
5467     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
5468 }
5469 
5470 #if VMA_STATS_STRING_ENABLED
5471 
PrintDetailedMap(class VmaJsonWriter & json)5472 void VmaBlockMetadata::PrintDetailedMap(class VmaJsonWriter& json) const
5473 {
5474     json.BeginObject();
5475 
5476     json.WriteString("TotalBytes");
5477     json.WriteNumber(m_Size);
5478 
5479     json.WriteString("UnusedBytes");
5480     json.WriteNumber(m_SumFreeSize);
5481 
5482     json.WriteString("Allocations");
5483     json.WriteNumber((uint64_t)m_Suballocations.size() - m_FreeCount);
5484 
5485     json.WriteString("UnusedRanges");
5486     json.WriteNumber(m_FreeCount);
5487 
5488     json.WriteString("Suballocations");
5489     json.BeginArray();
5490     size_t i = 0;
5491     for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
5492         suballocItem != m_Suballocations.cend();
5493         ++suballocItem, ++i)
5494     {
5495         json.BeginObject(true);
5496 
5497         json.WriteString("Type");
5498         json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[suballocItem->type]);
5499 
5500         json.WriteString("Size");
5501         json.WriteNumber(suballocItem->size);
5502 
5503         json.WriteString("Offset");
5504         json.WriteNumber(suballocItem->offset);
5505 
5506         if(suballocItem->type != VMA_SUBALLOCATION_TYPE_FREE)
5507         {
5508             const void* pUserData = suballocItem->hAllocation->GetUserData();
5509             if(pUserData != VMA_NULL)
5510             {
5511                 json.WriteString("UserData");
5512                 if(suballocItem->hAllocation->IsUserDataString())
5513                 {
5514                     json.WriteString((const char*)pUserData);
5515                 }
5516                 else
5517                 {
5518                     json.BeginString();
5519                     json.ContinueString_Pointer(pUserData);
5520                     json.EndString();
5521                 }
5522             }
5523         }
5524 
5525         json.EndObject();
5526     }
5527     json.EndArray();
5528 
5529     json.EndObject();
5530 }
5531 
5532 #endif // #if VMA_STATS_STRING_ENABLED
5533 
5534 /*
5535 How many suitable free suballocations to analyze before choosing best one.
5536 - Set to 1 to use First-Fit algorithm - first suitable free suballocation will
5537   be chosen.
5538 - Set to UINT32_MAX to use Best-Fit/Worst-Fit algorithm - all suitable free
5539   suballocations will be analized and best one will be chosen.
5540 - Any other value is also acceptable.
5541 */
5542 //static const uint32_t MAX_SUITABLE_SUBALLOCATIONS_TO_CHECK = 8;
5543 
CreateFirstAllocationRequest(VmaAllocationRequest * pAllocationRequest)5544 void VmaBlockMetadata::CreateFirstAllocationRequest(VmaAllocationRequest* pAllocationRequest)
5545 {
5546     VMA_ASSERT(IsEmpty());
5547     pAllocationRequest->offset = 0;
5548     pAllocationRequest->sumFreeSize = m_SumFreeSize;
5549     pAllocationRequest->sumItemSize = 0;
5550     pAllocationRequest->item = m_Suballocations.begin();
5551     pAllocationRequest->itemsToMakeLostCount = 0;
5552 }
5553 
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,bool canMakeOtherLost,VmaAllocationRequest * pAllocationRequest)5554 bool VmaBlockMetadata::CreateAllocationRequest(
5555     uint32_t currentFrameIndex,
5556     uint32_t frameInUseCount,
5557     VkDeviceSize bufferImageGranularity,
5558     VkDeviceSize allocSize,
5559     VkDeviceSize allocAlignment,
5560     VmaSuballocationType allocType,
5561     bool canMakeOtherLost,
5562     VmaAllocationRequest* pAllocationRequest)
5563 {
5564     VMA_ASSERT(allocSize > 0);
5565     VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
5566     VMA_ASSERT(pAllocationRequest != VMA_NULL);
5567     VMA_HEAVY_ASSERT(Validate());
5568 
5569     // There is not enough total free space in this block to fullfill the request: Early return.
5570     if(canMakeOtherLost == false && m_SumFreeSize < allocSize)
5571     {
5572         return false;
5573     }
5574 
5575     // New algorithm, efficiently searching freeSuballocationsBySize.
5576     const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
5577     if(freeSuballocCount > 0)
5578     {
5579         if(VMA_BEST_FIT)
5580         {
5581             // Find first free suballocation with size not less than allocSize.
5582             VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
5583                 m_FreeSuballocationsBySize.data(),
5584                 m_FreeSuballocationsBySize.data() + freeSuballocCount,
5585                 allocSize,
5586                 VmaSuballocationItemSizeLess());
5587             size_t index = it - m_FreeSuballocationsBySize.data();
5588             for(; index < freeSuballocCount; ++index)
5589             {
5590                 if(CheckAllocation(
5591                     currentFrameIndex,
5592                     frameInUseCount,
5593                     bufferImageGranularity,
5594                     allocSize,
5595                     allocAlignment,
5596                     allocType,
5597                     m_FreeSuballocationsBySize[index],
5598                     false, // canMakeOtherLost
5599                     &pAllocationRequest->offset,
5600                     &pAllocationRequest->itemsToMakeLostCount,
5601                     &pAllocationRequest->sumFreeSize,
5602                     &pAllocationRequest->sumItemSize))
5603                 {
5604                     pAllocationRequest->item = m_FreeSuballocationsBySize[index];
5605                     return true;
5606                 }
5607             }
5608         }
5609         else
5610         {
5611             // Search staring from biggest suballocations.
5612             for(size_t index = freeSuballocCount; index--; )
5613             {
5614                 if(CheckAllocation(
5615                     currentFrameIndex,
5616                     frameInUseCount,
5617                     bufferImageGranularity,
5618                     allocSize,
5619                     allocAlignment,
5620                     allocType,
5621                     m_FreeSuballocationsBySize[index],
5622                     false, // canMakeOtherLost
5623                     &pAllocationRequest->offset,
5624                     &pAllocationRequest->itemsToMakeLostCount,
5625                     &pAllocationRequest->sumFreeSize,
5626                     &pAllocationRequest->sumItemSize))
5627                 {
5628                     pAllocationRequest->item = m_FreeSuballocationsBySize[index];
5629                     return true;
5630                 }
5631             }
5632         }
5633     }
5634 
5635     if(canMakeOtherLost)
5636     {
5637         // Brute-force algorithm. TODO: Come up with something better.
5638 
5639         pAllocationRequest->sumFreeSize = VK_WHOLE_SIZE;
5640         pAllocationRequest->sumItemSize = VK_WHOLE_SIZE;
5641 
5642         VmaAllocationRequest tmpAllocRequest = {};
5643         for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
5644             suballocIt != m_Suballocations.end();
5645             ++suballocIt)
5646         {
5647             if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
5648                 suballocIt->hAllocation->CanBecomeLost())
5649             {
5650                 if(CheckAllocation(
5651                     currentFrameIndex,
5652                     frameInUseCount,
5653                     bufferImageGranularity,
5654                     allocSize,
5655                     allocAlignment,
5656                     allocType,
5657                     suballocIt,
5658                     canMakeOtherLost,
5659                     &tmpAllocRequest.offset,
5660                     &tmpAllocRequest.itemsToMakeLostCount,
5661                     &tmpAllocRequest.sumFreeSize,
5662                     &tmpAllocRequest.sumItemSize))
5663                 {
5664                     tmpAllocRequest.item = suballocIt;
5665 
5666                     if(tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost())
5667                     {
5668                         *pAllocationRequest = tmpAllocRequest;
5669                     }
5670                 }
5671             }
5672         }
5673 
5674         if(pAllocationRequest->sumItemSize != VK_WHOLE_SIZE)
5675         {
5676             return true;
5677         }
5678     }
5679 
5680     return false;
5681 }
5682 
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)5683 bool VmaBlockMetadata::MakeRequestedAllocationsLost(
5684     uint32_t currentFrameIndex,
5685     uint32_t frameInUseCount,
5686     VmaAllocationRequest* pAllocationRequest)
5687 {
5688     while(pAllocationRequest->itemsToMakeLostCount > 0)
5689     {
5690         if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
5691         {
5692             ++pAllocationRequest->item;
5693         }
5694         VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
5695         VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
5696         VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
5697         if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
5698         {
5699             pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
5700             --pAllocationRequest->itemsToMakeLostCount;
5701         }
5702         else
5703         {
5704             return false;
5705         }
5706     }
5707 
5708     VMA_HEAVY_ASSERT(Validate());
5709     VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
5710     VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
5711 
5712     return true;
5713 }
5714 
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)5715 uint32_t VmaBlockMetadata::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
5716 {
5717     uint32_t lostAllocationCount = 0;
5718     for(VmaSuballocationList::iterator it = m_Suballocations.begin();
5719         it != m_Suballocations.end();
5720         ++it)
5721     {
5722         if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
5723             it->hAllocation->CanBecomeLost() &&
5724             it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
5725         {
5726             it = FreeSuballocation(it);
5727             ++lostAllocationCount;
5728         }
5729     }
5730     return lostAllocationCount;
5731 }
5732 
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,VmaAllocation hAllocation)5733 void VmaBlockMetadata::Alloc(
5734     const VmaAllocationRequest& request,
5735     VmaSuballocationType type,
5736     VkDeviceSize allocSize,
5737     VmaAllocation hAllocation)
5738 {
5739     VMA_ASSERT(request.item != m_Suballocations.end());
5740     VmaSuballocation& suballoc = *request.item;
5741     // Given suballocation is a free block.
5742     VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
5743     // Given offset is inside this suballocation.
5744     VMA_ASSERT(request.offset >= suballoc.offset);
5745     const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
5746     VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
5747     const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
5748 
5749     // Unregister this free suballocation from m_FreeSuballocationsBySize and update
5750     // it to become used.
5751     UnregisterFreeSuballocation(request.item);
5752 
5753     suballoc.offset = request.offset;
5754     suballoc.size = allocSize;
5755     suballoc.type = type;
5756     suballoc.hAllocation = hAllocation;
5757 
5758     // If there are any free bytes remaining at the end, insert new free suballocation after current one.
5759     if(paddingEnd)
5760     {
5761         VmaSuballocation paddingSuballoc = {};
5762         paddingSuballoc.offset = request.offset + allocSize;
5763         paddingSuballoc.size = paddingEnd;
5764         paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
5765         VmaSuballocationList::iterator next = request.item;
5766         ++next;
5767         const VmaSuballocationList::iterator paddingEndItem =
5768             m_Suballocations.insert(next, paddingSuballoc);
5769         RegisterFreeSuballocation(paddingEndItem);
5770     }
5771 
5772     // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
5773     if(paddingBegin)
5774     {
5775         VmaSuballocation paddingSuballoc = {};
5776         paddingSuballoc.offset = request.offset - paddingBegin;
5777         paddingSuballoc.size = paddingBegin;
5778         paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
5779         const VmaSuballocationList::iterator paddingBeginItem =
5780             m_Suballocations.insert(request.item, paddingSuballoc);
5781         RegisterFreeSuballocation(paddingBeginItem);
5782     }
5783 
5784     // Update totals.
5785     m_FreeCount = m_FreeCount - 1;
5786     if(paddingBegin > 0)
5787     {
5788         ++m_FreeCount;
5789     }
5790     if(paddingEnd > 0)
5791     {
5792         ++m_FreeCount;
5793     }
5794     m_SumFreeSize -= allocSize;
5795 }
5796 
Free(const VmaAllocation allocation)5797 void VmaBlockMetadata::Free(const VmaAllocation allocation)
5798 {
5799     for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
5800         suballocItem != m_Suballocations.end();
5801         ++suballocItem)
5802     {
5803         VmaSuballocation& suballoc = *suballocItem;
5804         if(suballoc.hAllocation == allocation)
5805         {
5806             FreeSuballocation(suballocItem);
5807             VMA_HEAVY_ASSERT(Validate());
5808             return;
5809         }
5810     }
5811     VMA_ASSERT(0 && "Not found!");
5812 }
5813 
FreeAtOffset(VkDeviceSize offset)5814 void VmaBlockMetadata::FreeAtOffset(VkDeviceSize offset)
5815 {
5816     for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
5817         suballocItem != m_Suballocations.end();
5818         ++suballocItem)
5819     {
5820         VmaSuballocation& suballoc = *suballocItem;
5821         if(suballoc.offset == offset)
5822         {
5823             FreeSuballocation(suballocItem);
5824             return;
5825         }
5826     }
5827     VMA_ASSERT(0 && "Not found!");
5828 }
5829 
ValidateFreeSuballocationList()5830 bool VmaBlockMetadata::ValidateFreeSuballocationList() const
5831 {
5832     VkDeviceSize lastSize = 0;
5833     for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
5834     {
5835         const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
5836 
5837         if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
5838         {
5839             VMA_ASSERT(0);
5840             return false;
5841         }
5842         if(it->size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
5843         {
5844             VMA_ASSERT(0);
5845             return false;
5846         }
5847         if(it->size < lastSize)
5848         {
5849             VMA_ASSERT(0);
5850             return false;
5851         }
5852 
5853         lastSize = it->size;
5854     }
5855     return true;
5856 }
5857 
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)5858 bool VmaBlockMetadata::CheckAllocation(
5859     uint32_t currentFrameIndex,
5860     uint32_t frameInUseCount,
5861     VkDeviceSize bufferImageGranularity,
5862     VkDeviceSize allocSize,
5863     VkDeviceSize allocAlignment,
5864     VmaSuballocationType allocType,
5865     VmaSuballocationList::const_iterator suballocItem,
5866     bool canMakeOtherLost,
5867     VkDeviceSize* pOffset,
5868     size_t* itemsToMakeLostCount,
5869     VkDeviceSize* pSumFreeSize,
5870     VkDeviceSize* pSumItemSize) const
5871 {
5872     VMA_ASSERT(allocSize > 0);
5873     VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
5874     VMA_ASSERT(suballocItem != m_Suballocations.cend());
5875     VMA_ASSERT(pOffset != VMA_NULL);
5876 
5877     *itemsToMakeLostCount = 0;
5878     *pSumFreeSize = 0;
5879     *pSumItemSize = 0;
5880 
5881     if(canMakeOtherLost)
5882     {
5883         if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
5884         {
5885             *pSumFreeSize = suballocItem->size;
5886         }
5887         else
5888         {
5889             if(suballocItem->hAllocation->CanBecomeLost() &&
5890                 suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
5891             {
5892                 ++*itemsToMakeLostCount;
5893                 *pSumItemSize = suballocItem->size;
5894             }
5895             else
5896             {
5897                 return false;
5898             }
5899         }
5900 
5901         // Remaining size is too small for this request: Early return.
5902         if(m_Size - suballocItem->offset < allocSize)
5903         {
5904             return false;
5905         }
5906 
5907         // Start from offset equal to beginning of this suballocation.
5908         *pOffset = suballocItem->offset;
5909 
5910         // Apply VMA_DEBUG_MARGIN at the beginning.
5911         if((VMA_DEBUG_MARGIN > 0) && suballocItem != m_Suballocations.cbegin())
5912         {
5913             *pOffset += VMA_DEBUG_MARGIN;
5914         }
5915 
5916         // Apply alignment.
5917         const VkDeviceSize alignment = VMA_MAX(allocAlignment, static_cast<VkDeviceSize>(VMA_DEBUG_ALIGNMENT));
5918         *pOffset = VmaAlignUp(*pOffset, alignment);
5919 
5920         // Check previous suballocations for BufferImageGranularity conflicts.
5921         // Make bigger alignment if necessary.
5922         if(bufferImageGranularity > 1)
5923         {
5924             bool bufferImageGranularityConflict = false;
5925             VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
5926             while(prevSuballocItem != m_Suballocations.cbegin())
5927             {
5928                 --prevSuballocItem;
5929                 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
5930                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
5931                 {
5932                     if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
5933                     {
5934                         bufferImageGranularityConflict = true;
5935                         break;
5936                     }
5937                 }
5938                 else
5939                     // Already on previous page.
5940                     break;
5941             }
5942             if(bufferImageGranularityConflict)
5943             {
5944                 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
5945             }
5946         }
5947 
5948         // Now that we have final *pOffset, check if we are past suballocItem.
5949         // If yes, return false - this function should be called for another suballocItem as starting point.
5950         if(*pOffset >= suballocItem->offset + suballocItem->size)
5951         {
5952             return false;
5953         }
5954 
5955         // Calculate padding at the beginning based on current offset.
5956         const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
5957 
5958         // Calculate required margin at the end if this is not last suballocation.
5959         VmaSuballocationList::const_iterator next = suballocItem;
5960         ++next;
5961         const VkDeviceSize requiredEndMargin =
5962             (next != m_Suballocations.cend()) ? VMA_DEBUG_MARGIN : 0;
5963 
5964         const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
5965         // Another early return check.
5966         if(suballocItem->offset + totalSize > m_Size)
5967         {
5968             return false;
5969         }
5970 
5971         // Advance lastSuballocItem until desired size is reached.
5972         // Update itemsToMakeLostCount.
5973         VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
5974         if(totalSize > suballocItem->size)
5975         {
5976             VkDeviceSize remainingSize = totalSize - suballocItem->size;
5977             while(remainingSize > 0)
5978             {
5979                 ++lastSuballocItem;
5980                 if(lastSuballocItem == m_Suballocations.cend())
5981                 {
5982                     return false;
5983                 }
5984                 if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
5985                 {
5986                     *pSumFreeSize += lastSuballocItem->size;
5987                 }
5988                 else
5989                 {
5990                     VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
5991                     if(lastSuballocItem->hAllocation->CanBecomeLost() &&
5992                         lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
5993                     {
5994                         ++*itemsToMakeLostCount;
5995                         *pSumItemSize += lastSuballocItem->size;
5996                     }
5997                     else
5998                     {
5999                         return false;
6000                     }
6001                 }
6002                 remainingSize = (lastSuballocItem->size < remainingSize) ?
6003                     remainingSize - lastSuballocItem->size : 0;
6004             }
6005         }
6006 
6007         // Check next suballocations for BufferImageGranularity conflicts.
6008         // If conflict exists, we must mark more allocations lost or fail.
6009         if(bufferImageGranularity > 1)
6010         {
6011             VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
6012             ++nextSuballocItem;
6013             while(nextSuballocItem != m_Suballocations.cend())
6014             {
6015                 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
6016                 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
6017                 {
6018                     if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
6019                     {
6020                         VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
6021                         if(nextSuballoc.hAllocation->CanBecomeLost() &&
6022                             nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
6023                         {
6024                             ++*itemsToMakeLostCount;
6025                         }
6026                         else
6027                         {
6028                             return false;
6029                         }
6030                     }
6031                 }
6032                 else
6033                 {
6034                     // Already on next page.
6035                     break;
6036                 }
6037                 ++nextSuballocItem;
6038             }
6039         }
6040     }
6041     else
6042     {
6043         const VmaSuballocation& suballoc = *suballocItem;
6044         VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
6045 
6046         *pSumFreeSize = suballoc.size;
6047 
6048         // Size of this suballocation is too small for this request: Early return.
6049         if(suballoc.size < allocSize)
6050         {
6051             return false;
6052         }
6053 
6054         // Start from offset equal to beginning of this suballocation.
6055         *pOffset = suballoc.offset;
6056 
6057         // Apply VMA_DEBUG_MARGIN at the beginning.
6058         if((VMA_DEBUG_MARGIN > 0) && suballocItem != m_Suballocations.cbegin())
6059         {
6060             *pOffset += VMA_DEBUG_MARGIN;
6061         }
6062 
6063         // Apply alignment.
6064         const VkDeviceSize alignment = VMA_MAX(allocAlignment, static_cast<VkDeviceSize>(VMA_DEBUG_ALIGNMENT));
6065         *pOffset = VmaAlignUp(*pOffset, alignment);
6066 
6067         // Check previous suballocations for BufferImageGranularity conflicts.
6068         // Make bigger alignment if necessary.
6069         if(bufferImageGranularity > 1)
6070         {
6071             bool bufferImageGranularityConflict = false;
6072             VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
6073             while(prevSuballocItem != m_Suballocations.cbegin())
6074             {
6075                 --prevSuballocItem;
6076                 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
6077                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
6078                 {
6079                     if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
6080                     {
6081                         bufferImageGranularityConflict = true;
6082                         break;
6083                     }
6084                 }
6085                 else
6086                     // Already on previous page.
6087                     break;
6088             }
6089             if(bufferImageGranularityConflict)
6090             {
6091                 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
6092             }
6093         }
6094 
6095         // Calculate padding at the beginning based on current offset.
6096         const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
6097 
6098         // Calculate required margin at the end if this is not last suballocation.
6099         VmaSuballocationList::const_iterator next = suballocItem;
6100         ++next;
6101         const VkDeviceSize requiredEndMargin =
6102             (next != m_Suballocations.cend()) ? VMA_DEBUG_MARGIN : 0;
6103 
6104         // Fail if requested size plus margin before and after is bigger than size of this suballocation.
6105         if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
6106         {
6107             return false;
6108         }
6109 
6110         // Check next suballocations for BufferImageGranularity conflicts.
6111         // If conflict exists, allocation cannot be made here.
6112         if(bufferImageGranularity > 1)
6113         {
6114             VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
6115             ++nextSuballocItem;
6116             while(nextSuballocItem != m_Suballocations.cend())
6117             {
6118                 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
6119                 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
6120                 {
6121                     if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
6122                     {
6123                         return false;
6124                     }
6125                 }
6126                 else
6127                 {
6128                     // Already on next page.
6129                     break;
6130                 }
6131                 ++nextSuballocItem;
6132             }
6133         }
6134     }
6135 
6136     // All tests passed: Success. pOffset is already filled.
6137     return true;
6138 }
6139 
MergeFreeWithNext(VmaSuballocationList::iterator item)6140 void VmaBlockMetadata::MergeFreeWithNext(VmaSuballocationList::iterator item)
6141 {
6142     VMA_ASSERT(item != m_Suballocations.end());
6143     VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
6144 
6145     VmaSuballocationList::iterator nextItem = item;
6146     ++nextItem;
6147     VMA_ASSERT(nextItem != m_Suballocations.end());
6148     VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
6149 
6150     item->size += nextItem->size;
6151     --m_FreeCount;
6152     m_Suballocations.erase(nextItem);
6153 }
6154 
FreeSuballocation(VmaSuballocationList::iterator suballocItem)6155 VmaSuballocationList::iterator VmaBlockMetadata::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
6156 {
6157     // Change this suballocation to be marked as free.
6158     VmaSuballocation& suballoc = *suballocItem;
6159     suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
6160     suballoc.hAllocation = VK_NULL_HANDLE;
6161 
6162     // Update totals.
6163     ++m_FreeCount;
6164     m_SumFreeSize += suballoc.size;
6165 
6166     // Merge with previous and/or next suballocation if it's also free.
6167     bool mergeWithNext = false;
6168     bool mergeWithPrev = false;
6169 
6170     VmaSuballocationList::iterator nextItem = suballocItem;
6171     ++nextItem;
6172     if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
6173     {
6174         mergeWithNext = true;
6175     }
6176 
6177     VmaSuballocationList::iterator prevItem = suballocItem;
6178     if(suballocItem != m_Suballocations.begin())
6179     {
6180         --prevItem;
6181         if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
6182         {
6183             mergeWithPrev = true;
6184         }
6185     }
6186 
6187     if(mergeWithNext)
6188     {
6189         UnregisterFreeSuballocation(nextItem);
6190         MergeFreeWithNext(suballocItem);
6191     }
6192 
6193     if(mergeWithPrev)
6194     {
6195         UnregisterFreeSuballocation(prevItem);
6196         MergeFreeWithNext(prevItem);
6197         RegisterFreeSuballocation(prevItem);
6198         return prevItem;
6199     }
6200     else
6201     {
6202         RegisterFreeSuballocation(suballocItem);
6203         return suballocItem;
6204     }
6205 }
6206 
RegisterFreeSuballocation(VmaSuballocationList::iterator item)6207 void VmaBlockMetadata::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
6208 {
6209     VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
6210     VMA_ASSERT(item->size > 0);
6211 
6212     // You may want to enable this validation at the beginning or at the end of
6213     // this function, depending on what do you want to check.
6214     VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
6215 
6216     if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
6217     {
6218         if(m_FreeSuballocationsBySize.empty())
6219         {
6220             m_FreeSuballocationsBySize.push_back(item);
6221         }
6222         else
6223         {
6224             VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
6225         }
6226     }
6227 
6228     //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
6229 }
6230 
6231 
UnregisterFreeSuballocation(VmaSuballocationList::iterator item)6232 void VmaBlockMetadata::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
6233 {
6234     VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
6235     VMA_ASSERT(item->size > 0);
6236 
6237     // You may want to enable this validation at the beginning or at the end of
6238     // this function, depending on what do you want to check.
6239     VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
6240 
6241     if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
6242     {
6243         VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
6244             m_FreeSuballocationsBySize.data(),
6245             m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
6246             item,
6247             VmaSuballocationItemSizeLess());
6248         for(size_t index = it - m_FreeSuballocationsBySize.data();
6249             index < m_FreeSuballocationsBySize.size();
6250             ++index)
6251         {
6252             if(m_FreeSuballocationsBySize[index] == item)
6253             {
6254                 VmaVectorRemove(m_FreeSuballocationsBySize, index);
6255                 return;
6256             }
6257             VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
6258         }
6259         VMA_ASSERT(0 && "Not found.");
6260     }
6261 
6262     //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
6263 }
6264 
6265 ////////////////////////////////////////////////////////////////////////////////
6266 // class VmaDeviceMemoryBlock
6267 
VmaDeviceMemoryBlock(VmaAllocator hAllocator)6268 VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) :
6269     m_Metadata(hAllocator),
6270     m_MemoryTypeIndex(UINT32_MAX),
6271     m_hMemory(VK_NULL_HANDLE),
6272     m_MapCount(0),
6273     m_pMappedData(VMA_NULL)
6274 {
6275 }
6276 
Init(uint32_t newMemoryTypeIndex,VkDeviceMemory newMemory,VkDeviceSize newSize)6277 void VmaDeviceMemoryBlock::Init(
6278     uint32_t newMemoryTypeIndex,
6279     VkDeviceMemory newMemory,
6280     VkDeviceSize newSize)
6281 {
6282     VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
6283 
6284     m_MemoryTypeIndex = newMemoryTypeIndex;
6285     m_hMemory = newMemory;
6286 
6287     m_Metadata.Init(newSize);
6288 }
6289 
Destroy(VmaAllocator allocator)6290 void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
6291 {
6292     // This is the most important assert in the entire library.
6293     // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
6294     VMA_ASSERT(m_Metadata.IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
6295 
6296     VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
6297     allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_Metadata.GetSize(), m_hMemory);
6298     m_hMemory = VK_NULL_HANDLE;
6299 }
6300 
Validate()6301 bool VmaDeviceMemoryBlock::Validate() const
6302 {
6303     if((m_hMemory == VK_NULL_HANDLE) ||
6304         (m_Metadata.GetSize() == 0))
6305     {
6306         return false;
6307     }
6308 
6309     return m_Metadata.Validate();
6310 }
6311 
Map(VmaAllocator hAllocator,uint32_t count,void ** ppData)6312 VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
6313 {
6314     if(count == 0)
6315     {
6316         return VK_SUCCESS;
6317     }
6318 
6319     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
6320     if(m_MapCount != 0)
6321     {
6322         m_MapCount += count;
6323         VMA_ASSERT(m_pMappedData != VMA_NULL);
6324         if(ppData != VMA_NULL)
6325         {
6326             *ppData = m_pMappedData;
6327         }
6328         return VK_SUCCESS;
6329     }
6330     else
6331     {
6332         VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
6333             hAllocator->m_hDevice,
6334             m_hMemory,
6335             0, // offset
6336             VK_WHOLE_SIZE,
6337             0, // flags
6338             &m_pMappedData);
6339         if(result == VK_SUCCESS)
6340         {
6341             if(ppData != VMA_NULL)
6342             {
6343                 *ppData = m_pMappedData;
6344             }
6345             m_MapCount = count;
6346         }
6347         return result;
6348     }
6349 }
6350 
Unmap(VmaAllocator hAllocator,uint32_t count)6351 void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
6352 {
6353     if(count == 0)
6354     {
6355         return;
6356     }
6357 
6358     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
6359     if(m_MapCount >= count)
6360     {
6361         m_MapCount -= count;
6362         if(m_MapCount == 0)
6363         {
6364             m_pMappedData = VMA_NULL;
6365             (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
6366         }
6367     }
6368     else
6369     {
6370         VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
6371     }
6372 }
6373 
BindBufferMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkBuffer hBuffer)6374 VkResult VmaDeviceMemoryBlock::BindBufferMemory(
6375     const VmaAllocator hAllocator,
6376     const VmaAllocation hAllocation,
6377     VkBuffer hBuffer)
6378 {
6379     VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
6380         hAllocation->GetBlock() == this);
6381     // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
6382     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
6383     return hAllocator->GetVulkanFunctions().vkBindBufferMemory(
6384         hAllocator->m_hDevice,
6385         hBuffer,
6386         m_hMemory,
6387         hAllocation->GetOffset());
6388 }
6389 
BindImageMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkImage hImage)6390 VkResult VmaDeviceMemoryBlock::BindImageMemory(
6391     const VmaAllocator hAllocator,
6392     const VmaAllocation hAllocation,
6393     VkImage hImage)
6394 {
6395     VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
6396         hAllocation->GetBlock() == this);
6397     // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
6398     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
6399     return hAllocator->GetVulkanFunctions().vkBindImageMemory(
6400         hAllocator->m_hDevice,
6401         hImage,
6402         m_hMemory,
6403         hAllocation->GetOffset());
6404 }
6405 
InitStatInfo(VmaStatInfo & outInfo)6406 static void InitStatInfo(VmaStatInfo& outInfo)
6407 {
6408     memset(&outInfo, 0, sizeof(outInfo));
6409     outInfo.allocationSizeMin = UINT64_MAX;
6410     outInfo.unusedRangeSizeMin = UINT64_MAX;
6411 }
6412 
6413 // Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
VmaAddStatInfo(VmaStatInfo & inoutInfo,const VmaStatInfo & srcInfo)6414 static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
6415 {
6416     inoutInfo.blockCount += srcInfo.blockCount;
6417     inoutInfo.allocationCount += srcInfo.allocationCount;
6418     inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
6419     inoutInfo.usedBytes += srcInfo.usedBytes;
6420     inoutInfo.unusedBytes += srcInfo.unusedBytes;
6421     inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
6422     inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
6423     inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
6424     inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
6425 }
6426 
VmaPostprocessCalcStatInfo(VmaStatInfo & inoutInfo)6427 static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
6428 {
6429     inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
6430         VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
6431     inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
6432         VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
6433 }
6434 
VmaPool_T(VmaAllocator hAllocator,const VmaPoolCreateInfo & createInfo)6435 VmaPool_T::VmaPool_T(
6436     VmaAllocator hAllocator,
6437     const VmaPoolCreateInfo& createInfo) :
6438     m_BlockVector(
6439         hAllocator,
6440         createInfo.memoryTypeIndex,
6441         createInfo.blockSize,
6442         createInfo.minBlockCount,
6443         createInfo.maxBlockCount,
6444         (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
6445         createInfo.frameInUseCount,
6446         true) // isCustomPool
6447 {
6448 }
6449 
~VmaPool_T()6450 VmaPool_T::~VmaPool_T()
6451 {
6452 }
6453 
6454 #if VMA_STATS_STRING_ENABLED
6455 
6456 #endif // #if VMA_STATS_STRING_ENABLED
6457 
VmaBlockVector(VmaAllocator hAllocator,uint32_t memoryTypeIndex,VkDeviceSize preferredBlockSize,size_t minBlockCount,size_t maxBlockCount,VkDeviceSize bufferImageGranularity,uint32_t frameInUseCount,bool isCustomPool)6458 VmaBlockVector::VmaBlockVector(
6459     VmaAllocator hAllocator,
6460     uint32_t memoryTypeIndex,
6461     VkDeviceSize preferredBlockSize,
6462     size_t minBlockCount,
6463     size_t maxBlockCount,
6464     VkDeviceSize bufferImageGranularity,
6465     uint32_t frameInUseCount,
6466     bool isCustomPool) :
6467     m_hAllocator(hAllocator),
6468     m_MemoryTypeIndex(memoryTypeIndex),
6469     m_PreferredBlockSize(preferredBlockSize),
6470     m_MinBlockCount(minBlockCount),
6471     m_MaxBlockCount(maxBlockCount),
6472     m_BufferImageGranularity(bufferImageGranularity),
6473     m_FrameInUseCount(frameInUseCount),
6474     m_IsCustomPool(isCustomPool),
6475     m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
6476     m_HasEmptyBlock(false),
6477     m_pDefragmentator(VMA_NULL)
6478 {
6479 }
6480 
~VmaBlockVector()6481 VmaBlockVector::~VmaBlockVector()
6482 {
6483     VMA_ASSERT(m_pDefragmentator == VMA_NULL);
6484 
6485     for(size_t i = m_Blocks.size(); i--; )
6486     {
6487         m_Blocks[i]->Destroy(m_hAllocator);
6488         vma_delete(m_hAllocator, m_Blocks[i]);
6489     }
6490 }
6491 
CreateMinBlocks()6492 VkResult VmaBlockVector::CreateMinBlocks()
6493 {
6494     for(size_t i = 0; i < m_MinBlockCount; ++i)
6495     {
6496         VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
6497         if(res != VK_SUCCESS)
6498         {
6499             return res;
6500         }
6501     }
6502     return VK_SUCCESS;
6503 }
6504 
GetPoolStats(VmaPoolStats * pStats)6505 void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
6506 {
6507     pStats->size = 0;
6508     pStats->unusedSize = 0;
6509     pStats->allocationCount = 0;
6510     pStats->unusedRangeCount = 0;
6511     pStats->unusedRangeSizeMax = 0;
6512 
6513     VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
6514 
6515     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
6516     {
6517         const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
6518         VMA_ASSERT(pBlock);
6519         VMA_HEAVY_ASSERT(pBlock->Validate());
6520         pBlock->m_Metadata.AddPoolStats(*pStats);
6521     }
6522 }
6523 
6524 static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
6525 
Allocate(VmaPool hCurrentPool,uint32_t currentFrameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)6526 VkResult VmaBlockVector::Allocate(
6527     VmaPool hCurrentPool,
6528     uint32_t currentFrameIndex,
6529     const VkMemoryRequirements& vkMemReq,
6530     const VmaAllocationCreateInfo& createInfo,
6531     VmaSuballocationType suballocType,
6532     VmaAllocation* pAllocation)
6533 {
6534     const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
6535     const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
6536 
6537     VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
6538 
6539     // 1. Search existing allocations. Try to allocate without making other allocations lost.
6540     // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
6541     for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
6542     {
6543         VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
6544         VMA_ASSERT(pCurrBlock);
6545         VmaAllocationRequest currRequest = {};
6546         if(pCurrBlock->m_Metadata.CreateAllocationRequest(
6547             currentFrameIndex,
6548             m_FrameInUseCount,
6549             m_BufferImageGranularity,
6550             vkMemReq.size,
6551             vkMemReq.alignment,
6552             suballocType,
6553             false, // canMakeOtherLost
6554             &currRequest))
6555         {
6556             // Allocate from pCurrBlock.
6557             VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
6558 
6559             if(mapped)
6560             {
6561                 VkResult res = pCurrBlock->Map(m_hAllocator, 1, VMA_NULL);
6562                 if(res != VK_SUCCESS)
6563                 {
6564                     return res;
6565                 }
6566             }
6567 
6568             // We no longer have an empty Allocation.
6569             if(pCurrBlock->m_Metadata.IsEmpty())
6570             {
6571                 m_HasEmptyBlock = false;
6572             }
6573 
6574             *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
6575             pCurrBlock->m_Metadata.Alloc(currRequest, suballocType, vkMemReq.size, *pAllocation);
6576             (*pAllocation)->InitBlockAllocation(
6577                 hCurrentPool,
6578                 pCurrBlock,
6579                 currRequest.offset,
6580                 vkMemReq.alignment,
6581                 vkMemReq.size,
6582                 suballocType,
6583                 mapped,
6584                 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
6585             VMA_HEAVY_ASSERT(pCurrBlock->Validate());
6586             VMA_DEBUG_LOG("    Returned from existing allocation #%u", (uint32_t)blockIndex);
6587             (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
6588             return VK_SUCCESS;
6589         }
6590     }
6591 
6592     const bool canCreateNewBlock =
6593         ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
6594         (m_Blocks.size() < m_MaxBlockCount);
6595 
6596     // 2. Try to create new block.
6597     if(canCreateNewBlock)
6598     {
6599         // Calculate optimal size for new block.
6600         VkDeviceSize newBlockSize = m_PreferredBlockSize;
6601         uint32_t newBlockSizeShift = 0;
6602         const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
6603 
6604         // Allocating blocks of other sizes is allowed only in default pools.
6605         // In custom pools block size is fixed.
6606         if(m_IsCustomPool == false)
6607         {
6608             // Allocate 1/8, 1/4, 1/2 as first blocks.
6609             const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
6610             for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
6611             {
6612                 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
6613                 if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= vkMemReq.size * 2)
6614                 {
6615                     newBlockSize = smallerNewBlockSize;
6616                     ++newBlockSizeShift;
6617                 }
6618                 else
6619                 {
6620                     break;
6621                 }
6622             }
6623         }
6624 
6625         size_t newBlockIndex = 0;
6626         VkResult res = CreateBlock(newBlockSize, &newBlockIndex);
6627         // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
6628         if(m_IsCustomPool == false)
6629         {
6630             while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
6631             {
6632                 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
6633                 if(smallerNewBlockSize >= vkMemReq.size)
6634                 {
6635                     newBlockSize = smallerNewBlockSize;
6636                     ++newBlockSizeShift;
6637                     res = CreateBlock(newBlockSize, &newBlockIndex);
6638                 }
6639                 else
6640                 {
6641                     break;
6642                 }
6643             }
6644         }
6645 
6646         if(res == VK_SUCCESS)
6647         {
6648             VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
6649             VMA_ASSERT(pBlock->m_Metadata.GetSize() >= vkMemReq.size);
6650 
6651             if(mapped)
6652             {
6653                 res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
6654                 if(res != VK_SUCCESS)
6655                 {
6656                     return res;
6657                 }
6658             }
6659 
6660             // Allocate from pBlock. Because it is empty, dstAllocRequest can be trivially filled.
6661             VmaAllocationRequest allocRequest;
6662             pBlock->m_Metadata.CreateFirstAllocationRequest(&allocRequest);
6663             *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
6664             pBlock->m_Metadata.Alloc(allocRequest, suballocType, vkMemReq.size, *pAllocation);
6665             (*pAllocation)->InitBlockAllocation(
6666                 hCurrentPool,
6667                 pBlock,
6668                 allocRequest.offset,
6669                 vkMemReq.alignment,
6670                 vkMemReq.size,
6671                 suballocType,
6672                 mapped,
6673                 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
6674             VMA_HEAVY_ASSERT(pBlock->Validate());
6675             VMA_DEBUG_LOG("    Created new allocation Size=%llu", allocInfo.allocationSize);
6676             (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
6677             return VK_SUCCESS;
6678         }
6679     }
6680 
6681     const bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
6682 
6683     // 3. Try to allocate from existing blocks with making other allocations lost.
6684     if(canMakeOtherLost)
6685     {
6686         uint32_t tryIndex = 0;
6687         for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
6688         {
6689             VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
6690             VmaAllocationRequest bestRequest = {};
6691             VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
6692 
6693             // 1. Search existing allocations.
6694             // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
6695             for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
6696             {
6697                 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
6698                 VMA_ASSERT(pCurrBlock);
6699                 VmaAllocationRequest currRequest = {};
6700                 if(pCurrBlock->m_Metadata.CreateAllocationRequest(
6701                     currentFrameIndex,
6702                     m_FrameInUseCount,
6703                     m_BufferImageGranularity,
6704                     vkMemReq.size,
6705                     vkMemReq.alignment,
6706                     suballocType,
6707                     canMakeOtherLost,
6708                     &currRequest))
6709                 {
6710                     const VkDeviceSize currRequestCost = currRequest.CalcCost();
6711                     if(pBestRequestBlock == VMA_NULL ||
6712                         currRequestCost < bestRequestCost)
6713                     {
6714                         pBestRequestBlock = pCurrBlock;
6715                         bestRequest = currRequest;
6716                         bestRequestCost = currRequestCost;
6717 
6718                         if(bestRequestCost == 0)
6719                         {
6720                             break;
6721                         }
6722                     }
6723                 }
6724             }
6725 
6726             if(pBestRequestBlock != VMA_NULL)
6727             {
6728                 if(mapped)
6729                 {
6730                     VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
6731                     if(res != VK_SUCCESS)
6732                     {
6733                         return res;
6734                     }
6735                 }
6736 
6737                 if(pBestRequestBlock->m_Metadata.MakeRequestedAllocationsLost(
6738                     currentFrameIndex,
6739                     m_FrameInUseCount,
6740                     &bestRequest))
6741                 {
6742                     // We no longer have an empty Allocation.
6743                     if(pBestRequestBlock->m_Metadata.IsEmpty())
6744                     {
6745                         m_HasEmptyBlock = false;
6746                     }
6747                     // Allocate from this pBlock.
6748                     *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
6749                     pBestRequestBlock->m_Metadata.Alloc(bestRequest, suballocType, vkMemReq.size, *pAllocation);
6750                     (*pAllocation)->InitBlockAllocation(
6751                         hCurrentPool,
6752                         pBestRequestBlock,
6753                         bestRequest.offset,
6754                         vkMemReq.alignment,
6755                         vkMemReq.size,
6756                         suballocType,
6757                         mapped,
6758                         (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
6759                     VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
6760                     VMA_DEBUG_LOG("    Returned from existing allocation #%u", (uint32_t)blockIndex);
6761                     (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
6762                     return VK_SUCCESS;
6763                 }
6764                 // else: Some allocations must have been touched while we are here. Next try.
6765             }
6766             else
6767             {
6768                 // Could not find place in any of the blocks - break outer loop.
6769                 break;
6770             }
6771         }
6772         /* Maximum number of tries exceeded - a very unlike event when many other
6773         threads are simultaneously touching allocations making it impossible to make
6774         lost at the same time as we try to allocate. */
6775         if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
6776         {
6777             return VK_ERROR_TOO_MANY_OBJECTS;
6778         }
6779     }
6780 
6781     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
6782 }
6783 
Free(VmaAllocation hAllocation)6784 void VmaBlockVector::Free(
6785     VmaAllocation hAllocation)
6786 {
6787     VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
6788 
6789     // Scope for lock.
6790     {
6791         VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
6792 
6793         VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
6794 
6795         if(hAllocation->IsPersistentMap())
6796         {
6797             pBlock->Unmap(m_hAllocator, 1);
6798         }
6799 
6800         pBlock->m_Metadata.Free(hAllocation);
6801         VMA_HEAVY_ASSERT(pBlock->Validate());
6802 
6803         VMA_DEBUG_LOG("  Freed from MemoryTypeIndex=%u", memTypeIndex);
6804 
6805         // pBlock became empty after this deallocation.
6806         if(pBlock->m_Metadata.IsEmpty())
6807         {
6808             // Already has empty Allocation. We don't want to have two, so delete this one.
6809             if(m_HasEmptyBlock && m_Blocks.size() > m_MinBlockCount)
6810             {
6811                 pBlockToDelete = pBlock;
6812                 Remove(pBlock);
6813             }
6814             // We now have first empty Allocation.
6815             else
6816             {
6817                 m_HasEmptyBlock = true;
6818             }
6819         }
6820         // pBlock didn't become empty, but we have another empty block - find and free that one.
6821         // (This is optional, heuristics.)
6822         else if(m_HasEmptyBlock)
6823         {
6824             VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
6825             if(pLastBlock->m_Metadata.IsEmpty() && m_Blocks.size() > m_MinBlockCount)
6826             {
6827                 pBlockToDelete = pLastBlock;
6828                 m_Blocks.pop_back();
6829                 m_HasEmptyBlock = false;
6830             }
6831         }
6832 
6833         IncrementallySortBlocks();
6834     }
6835 
6836     // Destruction of a free Allocation. Deferred until this point, outside of mutex
6837     // lock, for performance reason.
6838     if(pBlockToDelete != VMA_NULL)
6839     {
6840         VMA_DEBUG_LOG("    Deleted empty allocation");
6841         pBlockToDelete->Destroy(m_hAllocator);
6842         vma_delete(m_hAllocator, pBlockToDelete);
6843     }
6844 }
6845 
CalcMaxBlockSize()6846 size_t VmaBlockVector::CalcMaxBlockSize() const
6847 {
6848     size_t result = 0;
6849     for(size_t i = m_Blocks.size(); i--; )
6850     {
6851         result = VMA_MAX((uint64_t)result, (uint64_t)m_Blocks[i]->m_Metadata.GetSize());
6852         if(result >= m_PreferredBlockSize)
6853         {
6854             break;
6855         }
6856     }
6857     return result;
6858 }
6859 
Remove(VmaDeviceMemoryBlock * pBlock)6860 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
6861 {
6862     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
6863     {
6864         if(m_Blocks[blockIndex] == pBlock)
6865         {
6866             VmaVectorRemove(m_Blocks, blockIndex);
6867             return;
6868         }
6869     }
6870     VMA_ASSERT(0);
6871 }
6872 
IncrementallySortBlocks()6873 void VmaBlockVector::IncrementallySortBlocks()
6874 {
6875     // Bubble sort only until first swap.
6876     for(size_t i = 1; i < m_Blocks.size(); ++i)
6877     {
6878         if(m_Blocks[i - 1]->m_Metadata.GetSumFreeSize() > m_Blocks[i]->m_Metadata.GetSumFreeSize())
6879         {
6880             VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
6881             return;
6882         }
6883     }
6884 }
6885 
CreateBlock(VkDeviceSize blockSize,size_t * pNewBlockIndex)6886 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
6887 {
6888     VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
6889     allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
6890     allocInfo.allocationSize = blockSize;
6891     VkDeviceMemory mem = VK_NULL_HANDLE;
6892     VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
6893     if(res < 0)
6894     {
6895         return res;
6896     }
6897 
6898     // New VkDeviceMemory successfully created.
6899 
6900     // Create new Allocation for it.
6901     VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
6902     pBlock->Init(
6903         m_MemoryTypeIndex,
6904         mem,
6905         allocInfo.allocationSize);
6906 
6907     m_Blocks.push_back(pBlock);
6908     if(pNewBlockIndex != VMA_NULL)
6909     {
6910         *pNewBlockIndex = m_Blocks.size() - 1;
6911     }
6912 
6913     return VK_SUCCESS;
6914 }
6915 
6916 #if VMA_STATS_STRING_ENABLED
6917 
PrintDetailedMap(class VmaJsonWriter & json)6918 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
6919 {
6920     VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
6921 
6922     json.BeginObject();
6923 
6924     if(m_IsCustomPool)
6925     {
6926         json.WriteString("MemoryTypeIndex");
6927         json.WriteNumber(m_MemoryTypeIndex);
6928 
6929         json.WriteString("BlockSize");
6930         json.WriteNumber(m_PreferredBlockSize);
6931 
6932         json.WriteString("BlockCount");
6933         json.BeginObject(true);
6934         if(m_MinBlockCount > 0)
6935         {
6936             json.WriteString("Min");
6937             json.WriteNumber((uint64_t)m_MinBlockCount);
6938         }
6939         if(m_MaxBlockCount < SIZE_MAX)
6940         {
6941             json.WriteString("Max");
6942             json.WriteNumber((uint64_t)m_MaxBlockCount);
6943         }
6944         json.WriteString("Cur");
6945         json.WriteNumber((uint64_t)m_Blocks.size());
6946         json.EndObject();
6947 
6948         if(m_FrameInUseCount > 0)
6949         {
6950             json.WriteString("FrameInUseCount");
6951             json.WriteNumber(m_FrameInUseCount);
6952         }
6953     }
6954     else
6955     {
6956         json.WriteString("PreferredBlockSize");
6957         json.WriteNumber(m_PreferredBlockSize);
6958     }
6959 
6960     json.WriteString("Blocks");
6961     json.BeginArray();
6962     for(size_t i = 0; i < m_Blocks.size(); ++i)
6963     {
6964         m_Blocks[i]->m_Metadata.PrintDetailedMap(json);
6965     }
6966     json.EndArray();
6967 
6968     json.EndObject();
6969 }
6970 
6971 #endif // #if VMA_STATS_STRING_ENABLED
6972 
EnsureDefragmentator(VmaAllocator hAllocator,uint32_t currentFrameIndex)6973 VmaDefragmentator* VmaBlockVector::EnsureDefragmentator(
6974     VmaAllocator hAllocator,
6975     uint32_t currentFrameIndex)
6976 {
6977     if(m_pDefragmentator == VMA_NULL)
6978     {
6979         m_pDefragmentator = vma_new(m_hAllocator, VmaDefragmentator)(
6980             hAllocator,
6981             this,
6982             currentFrameIndex);
6983     }
6984 
6985     return m_pDefragmentator;
6986 }
6987 
Defragment(VmaDefragmentationStats * pDefragmentationStats,VkDeviceSize & maxBytesToMove,uint32_t & maxAllocationsToMove)6988 VkResult VmaBlockVector::Defragment(
6989     VmaDefragmentationStats* pDefragmentationStats,
6990     VkDeviceSize& maxBytesToMove,
6991     uint32_t& maxAllocationsToMove)
6992 {
6993     if(m_pDefragmentator == VMA_NULL)
6994     {
6995         return VK_SUCCESS;
6996     }
6997 
6998     VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
6999 
7000     // Defragment.
7001     VkResult result = m_pDefragmentator->Defragment(maxBytesToMove, maxAllocationsToMove);
7002 
7003     // Accumulate statistics.
7004     if(pDefragmentationStats != VMA_NULL)
7005     {
7006         const VkDeviceSize bytesMoved = m_pDefragmentator->GetBytesMoved();
7007         const uint32_t allocationsMoved = m_pDefragmentator->GetAllocationsMoved();
7008         pDefragmentationStats->bytesMoved += bytesMoved;
7009         pDefragmentationStats->allocationsMoved += allocationsMoved;
7010         VMA_ASSERT(bytesMoved <= maxBytesToMove);
7011         VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
7012         maxBytesToMove -= bytesMoved;
7013         maxAllocationsToMove -= allocationsMoved;
7014     }
7015 
7016     // Free empty blocks.
7017     m_HasEmptyBlock = false;
7018     for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
7019     {
7020         VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
7021         if(pBlock->m_Metadata.IsEmpty())
7022         {
7023             if(m_Blocks.size() > m_MinBlockCount)
7024             {
7025                 if(pDefragmentationStats != VMA_NULL)
7026                 {
7027                     ++pDefragmentationStats->deviceMemoryBlocksFreed;
7028                     pDefragmentationStats->bytesFreed += pBlock->m_Metadata.GetSize();
7029                 }
7030 
7031                 VmaVectorRemove(m_Blocks, blockIndex);
7032                 pBlock->Destroy(m_hAllocator);
7033                 vma_delete(m_hAllocator, pBlock);
7034             }
7035             else
7036             {
7037                 m_HasEmptyBlock = true;
7038             }
7039         }
7040     }
7041 
7042     return result;
7043 }
7044 
DestroyDefragmentator()7045 void VmaBlockVector::DestroyDefragmentator()
7046 {
7047     if(m_pDefragmentator != VMA_NULL)
7048     {
7049         vma_delete(m_hAllocator, m_pDefragmentator);
7050         m_pDefragmentator = VMA_NULL;
7051     }
7052 }
7053 
MakePoolAllocationsLost(uint32_t currentFrameIndex,size_t * pLostAllocationCount)7054 void VmaBlockVector::MakePoolAllocationsLost(
7055     uint32_t currentFrameIndex,
7056     size_t* pLostAllocationCount)
7057 {
7058     VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
7059     size_t lostAllocationCount = 0;
7060     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
7061     {
7062         VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
7063         VMA_ASSERT(pBlock);
7064         lostAllocationCount += pBlock->m_Metadata.MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
7065     }
7066     if(pLostAllocationCount != VMA_NULL)
7067     {
7068         *pLostAllocationCount = lostAllocationCount;
7069     }
7070 }
7071 
AddStats(VmaStats * pStats)7072 void VmaBlockVector::AddStats(VmaStats* pStats)
7073 {
7074     const uint32_t memTypeIndex = m_MemoryTypeIndex;
7075     const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
7076 
7077     VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
7078 
7079     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
7080     {
7081         const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
7082         VMA_ASSERT(pBlock);
7083         VMA_HEAVY_ASSERT(pBlock->Validate());
7084         VmaStatInfo allocationStatInfo;
7085         pBlock->m_Metadata.CalcAllocationStatInfo(allocationStatInfo);
7086         VmaAddStatInfo(pStats->total, allocationStatInfo);
7087         VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
7088         VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
7089     }
7090 }
7091 
7092 ////////////////////////////////////////////////////////////////////////////////
7093 // VmaDefragmentator members definition
7094 
VmaDefragmentator(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex)7095 VmaDefragmentator::VmaDefragmentator(
7096     VmaAllocator hAllocator,
7097     VmaBlockVector* pBlockVector,
7098     uint32_t currentFrameIndex) :
7099     m_hAllocator(hAllocator),
7100     m_pBlockVector(pBlockVector),
7101     m_CurrentFrameIndex(currentFrameIndex),
7102     m_BytesMoved(0),
7103     m_AllocationsMoved(0),
7104     m_Allocations(VmaStlAllocator<AllocationInfo>(hAllocator->GetAllocationCallbacks())),
7105     m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
7106 {
7107 }
7108 
~VmaDefragmentator()7109 VmaDefragmentator::~VmaDefragmentator()
7110 {
7111     for(size_t i = m_Blocks.size(); i--; )
7112     {
7113         vma_delete(m_hAllocator, m_Blocks[i]);
7114     }
7115 }
7116 
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)7117 void VmaDefragmentator::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
7118 {
7119     AllocationInfo allocInfo;
7120     allocInfo.m_hAllocation = hAlloc;
7121     allocInfo.m_pChanged = pChanged;
7122     m_Allocations.push_back(allocInfo);
7123 }
7124 
EnsureMapping(VmaAllocator hAllocator,void ** ppMappedData)7125 VkResult VmaDefragmentator::BlockInfo::EnsureMapping(VmaAllocator hAllocator, void** ppMappedData)
7126 {
7127     // It has already been mapped for defragmentation.
7128     if(m_pMappedDataForDefragmentation)
7129     {
7130         *ppMappedData = m_pMappedDataForDefragmentation;
7131         return VK_SUCCESS;
7132     }
7133 
7134     // It is originally mapped.
7135     if(m_pBlock->GetMappedData())
7136     {
7137         *ppMappedData = m_pBlock->GetMappedData();
7138         return VK_SUCCESS;
7139     }
7140 
7141     // Map on first usage.
7142     VkResult res = m_pBlock->Map(hAllocator, 1, &m_pMappedDataForDefragmentation);
7143     *ppMappedData = m_pMappedDataForDefragmentation;
7144     return res;
7145 }
7146 
Unmap(VmaAllocator hAllocator)7147 void VmaDefragmentator::BlockInfo::Unmap(VmaAllocator hAllocator)
7148 {
7149     if(m_pMappedDataForDefragmentation != VMA_NULL)
7150     {
7151         m_pBlock->Unmap(hAllocator, 1);
7152     }
7153 }
7154 
DefragmentRound(VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove)7155 VkResult VmaDefragmentator::DefragmentRound(
7156     VkDeviceSize maxBytesToMove,
7157     uint32_t maxAllocationsToMove)
7158 {
7159     if(m_Blocks.empty())
7160     {
7161         return VK_SUCCESS;
7162     }
7163 
7164     size_t srcBlockIndex = m_Blocks.size() - 1;
7165     size_t srcAllocIndex = SIZE_MAX;
7166     for(;;)
7167     {
7168         // 1. Find next allocation to move.
7169         // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
7170         // 1.2. Then start from last to first m_Allocations - they are sorted from largest to smallest.
7171         while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
7172         {
7173             if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
7174             {
7175                 // Finished: no more allocations to process.
7176                 if(srcBlockIndex == 0)
7177                 {
7178                     return VK_SUCCESS;
7179                 }
7180                 else
7181                 {
7182                     --srcBlockIndex;
7183                     srcAllocIndex = SIZE_MAX;
7184                 }
7185             }
7186             else
7187             {
7188                 srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
7189             }
7190         }
7191 
7192         BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
7193         AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
7194 
7195         const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
7196         const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
7197         const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
7198         const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
7199 
7200         // 2. Try to find new place for this allocation in preceding or current block.
7201         for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
7202         {
7203             BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
7204             VmaAllocationRequest dstAllocRequest;
7205             if(pDstBlockInfo->m_pBlock->m_Metadata.CreateAllocationRequest(
7206                 m_CurrentFrameIndex,
7207                 m_pBlockVector->GetFrameInUseCount(),
7208                 m_pBlockVector->GetBufferImageGranularity(),
7209                 size,
7210                 alignment,
7211                 suballocType,
7212                 false, // canMakeOtherLost
7213                 &dstAllocRequest) &&
7214             MoveMakesSense(
7215                 dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
7216             {
7217                 VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
7218 
7219                 // Reached limit on number of allocations or bytes to move.
7220                 if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
7221                     (m_BytesMoved + size > maxBytesToMove))
7222                 {
7223                     return VK_INCOMPLETE;
7224                 }
7225 
7226                 void* pDstMappedData = VMA_NULL;
7227                 VkResult res = pDstBlockInfo->EnsureMapping(m_hAllocator, &pDstMappedData);
7228                 if(res != VK_SUCCESS)
7229                 {
7230                     return res;
7231                 }
7232 
7233                 void* pSrcMappedData = VMA_NULL;
7234                 res = pSrcBlockInfo->EnsureMapping(m_hAllocator, &pSrcMappedData);
7235                 if(res != VK_SUCCESS)
7236                 {
7237                     return res;
7238                 }
7239 
7240                 // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
7241                 memcpy(
7242                     reinterpret_cast<char*>(pDstMappedData) + dstAllocRequest.offset,
7243                     reinterpret_cast<char*>(pSrcMappedData) + srcOffset,
7244                     static_cast<size_t>(size));
7245 
7246                 pDstBlockInfo->m_pBlock->m_Metadata.Alloc(dstAllocRequest, suballocType, size, allocInfo.m_hAllocation);
7247                 pSrcBlockInfo->m_pBlock->m_Metadata.FreeAtOffset(srcOffset);
7248 
7249                 allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
7250 
7251                 if(allocInfo.m_pChanged != VMA_NULL)
7252                 {
7253                     *allocInfo.m_pChanged = VK_TRUE;
7254                 }
7255 
7256                 ++m_AllocationsMoved;
7257                 m_BytesMoved += size;
7258 
7259                 VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
7260 
7261                 break;
7262             }
7263         }
7264 
7265         // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
7266 
7267         if(srcAllocIndex > 0)
7268         {
7269             --srcAllocIndex;
7270         }
7271         else
7272         {
7273             if(srcBlockIndex > 0)
7274             {
7275                 --srcBlockIndex;
7276                 srcAllocIndex = SIZE_MAX;
7277             }
7278             else
7279             {
7280                 return VK_SUCCESS;
7281             }
7282         }
7283     }
7284 }
7285 
Defragment(VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove)7286 VkResult VmaDefragmentator::Defragment(
7287     VkDeviceSize maxBytesToMove,
7288     uint32_t maxAllocationsToMove)
7289 {
7290     if(m_Allocations.empty())
7291     {
7292         return VK_SUCCESS;
7293     }
7294 
7295     // Create block info for each block.
7296     const size_t blockCount = m_pBlockVector->m_Blocks.size();
7297     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
7298     {
7299         BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
7300         pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
7301         m_Blocks.push_back(pBlockInfo);
7302     }
7303 
7304     // Sort them by m_pBlock pointer value.
7305     VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
7306 
7307     // Move allocation infos from m_Allocations to appropriate m_Blocks[memTypeIndex].m_Allocations.
7308     for(size_t blockIndex = 0, allocCount = m_Allocations.size(); blockIndex < allocCount; ++blockIndex)
7309     {
7310         AllocationInfo& allocInfo = m_Allocations[blockIndex];
7311         // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
7312         if(allocInfo.m_hAllocation->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
7313         {
7314             VmaDeviceMemoryBlock* pBlock = allocInfo.m_hAllocation->GetBlock();
7315             BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
7316             if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
7317             {
7318                 (*it)->m_Allocations.push_back(allocInfo);
7319             }
7320             else
7321             {
7322                 VMA_ASSERT(0);
7323             }
7324         }
7325     }
7326     m_Allocations.clear();
7327 
7328     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
7329     {
7330         BlockInfo* pBlockInfo = m_Blocks[blockIndex];
7331         pBlockInfo->CalcHasNonMovableAllocations();
7332         pBlockInfo->SortAllocationsBySizeDescecnding();
7333     }
7334 
7335     // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
7336     VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
7337 
7338     // Execute defragmentation rounds (the main part).
7339     VkResult result = VK_SUCCESS;
7340     for(size_t round = 0; (round < 2) && (result == VK_SUCCESS); ++round)
7341     {
7342         result = DefragmentRound(maxBytesToMove, maxAllocationsToMove);
7343     }
7344 
7345     // Unmap blocks that were mapped for defragmentation.
7346     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
7347     {
7348         m_Blocks[blockIndex]->Unmap(m_hAllocator);
7349     }
7350 
7351     return result;
7352 }
7353 
MoveMakesSense(size_t dstBlockIndex,VkDeviceSize dstOffset,size_t srcBlockIndex,VkDeviceSize srcOffset)7354 bool VmaDefragmentator::MoveMakesSense(
7355         size_t dstBlockIndex, VkDeviceSize dstOffset,
7356         size_t srcBlockIndex, VkDeviceSize srcOffset)
7357 {
7358     if(dstBlockIndex < srcBlockIndex)
7359     {
7360         return true;
7361     }
7362     if(dstBlockIndex > srcBlockIndex)
7363     {
7364         return false;
7365     }
7366     if(dstOffset < srcOffset)
7367     {
7368         return true;
7369     }
7370     return false;
7371 }
7372 
7373 ////////////////////////////////////////////////////////////////////////////////
7374 // VmaAllocator_T
7375 
VmaAllocator_T(const VmaAllocatorCreateInfo * pCreateInfo)7376 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
7377     m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
7378     m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
7379     m_hDevice(pCreateInfo->device),
7380     m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
7381     m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
7382         *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
7383     m_PreferredLargeHeapBlockSize(0),
7384     m_PhysicalDevice(pCreateInfo->physicalDevice),
7385     m_CurrentFrameIndex(0),
7386     m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks()))
7387 {
7388     VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device);
7389 
7390     memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
7391     memset(&m_MemProps, 0, sizeof(m_MemProps));
7392     memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
7393 
7394     memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
7395     memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations));
7396 
7397     for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
7398     {
7399         m_HeapSizeLimit[i] = VK_WHOLE_SIZE;
7400     }
7401 
7402     if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
7403     {
7404         m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
7405         m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
7406     }
7407 
7408     ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
7409 
7410     (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
7411     (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
7412 
7413     m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
7414         pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
7415 
7416     if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
7417     {
7418         for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
7419         {
7420             const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
7421             if(limit != VK_WHOLE_SIZE)
7422             {
7423                 m_HeapSizeLimit[heapIndex] = limit;
7424                 if(limit < m_MemProps.memoryHeaps[heapIndex].size)
7425                 {
7426                     m_MemProps.memoryHeaps[heapIndex].size = limit;
7427                 }
7428             }
7429         }
7430     }
7431 
7432     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
7433     {
7434         const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
7435 
7436         m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
7437             this,
7438             memTypeIndex,
7439             preferredBlockSize,
7440             0,
7441             SIZE_MAX,
7442             GetBufferImageGranularity(),
7443             pCreateInfo->frameInUseCount,
7444             false); // isCustomPool
7445         // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
7446         // becase minBlockCount is 0.
7447         m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
7448     }
7449 }
7450 
~VmaAllocator_T()7451 VmaAllocator_T::~VmaAllocator_T()
7452 {
7453     VMA_ASSERT(m_Pools.empty());
7454 
7455     for(size_t i = GetMemoryTypeCount(); i--; )
7456     {
7457         vma_delete(this, m_pDedicatedAllocations[i]);
7458         vma_delete(this, m_pBlockVectors[i]);
7459     }
7460 }
7461 
ImportVulkanFunctions(const VmaVulkanFunctions * pVulkanFunctions)7462 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
7463 {
7464 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
7465     m_VulkanFunctions.vkGetPhysicalDeviceProperties = &vkGetPhysicalDeviceProperties;
7466     m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = &vkGetPhysicalDeviceMemoryProperties;
7467     m_VulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
7468     m_VulkanFunctions.vkFreeMemory = &vkFreeMemory;
7469     m_VulkanFunctions.vkMapMemory = &vkMapMemory;
7470     m_VulkanFunctions.vkUnmapMemory = &vkUnmapMemory;
7471     m_VulkanFunctions.vkBindBufferMemory = &vkBindBufferMemory;
7472     m_VulkanFunctions.vkBindImageMemory = &vkBindImageMemory;
7473     m_VulkanFunctions.vkGetBufferMemoryRequirements = &vkGetBufferMemoryRequirements;
7474     m_VulkanFunctions.vkGetImageMemoryRequirements = &vkGetImageMemoryRequirements;
7475     m_VulkanFunctions.vkCreateBuffer = &vkCreateBuffer;
7476     m_VulkanFunctions.vkDestroyBuffer = &vkDestroyBuffer;
7477     m_VulkanFunctions.vkCreateImage = &vkCreateImage;
7478     m_VulkanFunctions.vkDestroyImage = &vkDestroyImage;
7479     if(m_UseKhrDedicatedAllocation)
7480     {
7481         m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR =
7482             (PFN_vkGetBufferMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetBufferMemoryRequirements2KHR");
7483         m_VulkanFunctions.vkGetImageMemoryRequirements2KHR =
7484             (PFN_vkGetImageMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetImageMemoryRequirements2KHR");
7485     }
7486 #endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
7487 
7488 #define VMA_COPY_IF_NOT_NULL(funcName) \
7489     if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
7490 
7491     if(pVulkanFunctions != VMA_NULL)
7492     {
7493         VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
7494         VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
7495         VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
7496         VMA_COPY_IF_NOT_NULL(vkFreeMemory);
7497         VMA_COPY_IF_NOT_NULL(vkMapMemory);
7498         VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
7499         VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
7500         VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
7501         VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
7502         VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
7503         VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
7504         VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
7505         VMA_COPY_IF_NOT_NULL(vkCreateImage);
7506         VMA_COPY_IF_NOT_NULL(vkDestroyImage);
7507         VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
7508         VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
7509     }
7510 
7511 #undef VMA_COPY_IF_NOT_NULL
7512 
7513     // If these asserts are hit, you must either #define VMA_STATIC_VULKAN_FUNCTIONS 1
7514     // or pass valid pointers as VmaAllocatorCreateInfo::pVulkanFunctions.
7515     VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
7516     VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
7517     VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
7518     VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
7519     VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
7520     VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
7521     VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
7522     VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
7523     VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
7524     VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
7525     VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
7526     VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
7527     VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
7528     VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
7529     if(m_UseKhrDedicatedAllocation)
7530     {
7531         VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
7532         VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
7533     }
7534 }
7535 
CalcPreferredBlockSize(uint32_t memTypeIndex)7536 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
7537 {
7538     const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
7539     const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
7540     const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
7541     return isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize;
7542 }
7543 
AllocateMemoryOfType(const VkMemoryRequirements & vkMemReq,bool dedicatedAllocation,VkBuffer dedicatedBuffer,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,uint32_t memTypeIndex,VmaSuballocationType suballocType,VmaAllocation * pAllocation)7544 VkResult VmaAllocator_T::AllocateMemoryOfType(
7545     const VkMemoryRequirements& vkMemReq,
7546     bool dedicatedAllocation,
7547     VkBuffer dedicatedBuffer,
7548     VkImage dedicatedImage,
7549     const VmaAllocationCreateInfo& createInfo,
7550     uint32_t memTypeIndex,
7551     VmaSuballocationType suballocType,
7552     VmaAllocation* pAllocation)
7553 {
7554     VMA_ASSERT(pAllocation != VMA_NULL);
7555     VMA_DEBUG_LOG("  AllocateMemory: MemoryTypeIndex=%u, Size=%llu", memTypeIndex, vkMemReq.size);
7556 
7557     VmaAllocationCreateInfo finalCreateInfo = createInfo;
7558 
7559     // If memory type is not HOST_VISIBLE, disable MAPPED.
7560     if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
7561         (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
7562     {
7563         finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
7564     }
7565 
7566     VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
7567     VMA_ASSERT(blockVector);
7568 
7569     const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
7570     bool preferDedicatedMemory =
7571         VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
7572         dedicatedAllocation ||
7573         // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
7574         vkMemReq.size > preferredBlockSize / 2;
7575 
7576     if(preferDedicatedMemory &&
7577         (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
7578         finalCreateInfo.pool == VK_NULL_HANDLE)
7579     {
7580         finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
7581     }
7582 
7583     if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
7584     {
7585         if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
7586         {
7587             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
7588         }
7589         else
7590         {
7591             return AllocateDedicatedMemory(
7592                 vkMemReq.size,
7593                 suballocType,
7594                 memTypeIndex,
7595                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
7596                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
7597                 finalCreateInfo.pUserData,
7598                 dedicatedBuffer,
7599                 dedicatedImage,
7600                 pAllocation);
7601         }
7602     }
7603     else
7604     {
7605         VkResult res = blockVector->Allocate(
7606             VK_NULL_HANDLE, // hCurrentPool
7607             m_CurrentFrameIndex.load(),
7608             vkMemReq,
7609             finalCreateInfo,
7610             suballocType,
7611             pAllocation);
7612         if(res == VK_SUCCESS)
7613         {
7614             return res;
7615         }
7616 
7617         // 5. Try dedicated memory.
7618         if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
7619         {
7620             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
7621         }
7622         else
7623         {
7624             res = AllocateDedicatedMemory(
7625                 vkMemReq.size,
7626                 suballocType,
7627                 memTypeIndex,
7628                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
7629                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
7630                 finalCreateInfo.pUserData,
7631                 dedicatedBuffer,
7632                 dedicatedImage,
7633                 pAllocation);
7634             if(res == VK_SUCCESS)
7635             {
7636                 // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
7637                 VMA_DEBUG_LOG("    Allocated as DedicatedMemory");
7638                 return VK_SUCCESS;
7639             }
7640             else
7641             {
7642                 // Everything failed: Return error code.
7643                 VMA_DEBUG_LOG("    vkAllocateMemory FAILED");
7644                 return res;
7645             }
7646         }
7647     }
7648 }
7649 
AllocateDedicatedMemory(VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,bool map,bool isUserDataString,void * pUserData,VkBuffer dedicatedBuffer,VkImage dedicatedImage,VmaAllocation * pAllocation)7650 VkResult VmaAllocator_T::AllocateDedicatedMemory(
7651     VkDeviceSize size,
7652     VmaSuballocationType suballocType,
7653     uint32_t memTypeIndex,
7654     bool map,
7655     bool isUserDataString,
7656     void* pUserData,
7657     VkBuffer dedicatedBuffer,
7658     VkImage dedicatedImage,
7659     VmaAllocation* pAllocation)
7660 {
7661     VMA_ASSERT(pAllocation);
7662 
7663     VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
7664     allocInfo.memoryTypeIndex = memTypeIndex;
7665     allocInfo.allocationSize = size;
7666 
7667     VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
7668     if(m_UseKhrDedicatedAllocation)
7669     {
7670         if(dedicatedBuffer != VK_NULL_HANDLE)
7671         {
7672             VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
7673             dedicatedAllocInfo.buffer = dedicatedBuffer;
7674             allocInfo.pNext = &dedicatedAllocInfo;
7675         }
7676         else if(dedicatedImage != VK_NULL_HANDLE)
7677         {
7678             dedicatedAllocInfo.image = dedicatedImage;
7679             allocInfo.pNext = &dedicatedAllocInfo;
7680         }
7681     }
7682 
7683     // Allocate VkDeviceMemory.
7684     VkDeviceMemory hMemory = VK_NULL_HANDLE;
7685     VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
7686     if(res < 0)
7687     {
7688         VMA_DEBUG_LOG("    vkAllocateMemory FAILED");
7689         return res;
7690     }
7691 
7692     void* pMappedData = VMA_NULL;
7693     if(map)
7694     {
7695         res = (*m_VulkanFunctions.vkMapMemory)(
7696             m_hDevice,
7697             hMemory,
7698             0,
7699             VK_WHOLE_SIZE,
7700             0,
7701             &pMappedData);
7702         if(res < 0)
7703         {
7704             VMA_DEBUG_LOG("    vkMapMemory FAILED");
7705             FreeVulkanMemory(memTypeIndex, size, hMemory);
7706             return res;
7707         }
7708     }
7709 
7710     *pAllocation = vma_new(this, VmaAllocation_T)(m_CurrentFrameIndex.load(), isUserDataString);
7711     (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
7712     (*pAllocation)->SetUserData(this, pUserData);
7713 
7714     // Register it in m_pDedicatedAllocations.
7715     {
7716         VmaMutexLock lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
7717         AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
7718         VMA_ASSERT(pDedicatedAllocations);
7719         VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, *pAllocation);
7720     }
7721 
7722     VMA_DEBUG_LOG("    Allocated DedicatedMemory MemoryTypeIndex=#%u", memTypeIndex);
7723 
7724     return VK_SUCCESS;
7725 }
7726 
GetBufferMemoryRequirements(VkBuffer hBuffer,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)7727 void VmaAllocator_T::GetBufferMemoryRequirements(
7728     VkBuffer hBuffer,
7729     VkMemoryRequirements& memReq,
7730     bool& requiresDedicatedAllocation,
7731     bool& prefersDedicatedAllocation) const
7732 {
7733     if(m_UseKhrDedicatedAllocation)
7734     {
7735         VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
7736         memReqInfo.buffer = hBuffer;
7737 
7738         VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
7739 
7740         VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
7741         memReq2.pNext = &memDedicatedReq;
7742 
7743         (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
7744 
7745         memReq = memReq2.memoryRequirements;
7746         requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
7747         prefersDedicatedAllocation  = (memDedicatedReq.prefersDedicatedAllocation  != VK_FALSE);
7748     }
7749     else
7750     {
7751         (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
7752         requiresDedicatedAllocation = false;
7753         prefersDedicatedAllocation  = false;
7754     }
7755 }
7756 
GetImageMemoryRequirements(VkImage hImage,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)7757 void VmaAllocator_T::GetImageMemoryRequirements(
7758     VkImage hImage,
7759     VkMemoryRequirements& memReq,
7760     bool& requiresDedicatedAllocation,
7761     bool& prefersDedicatedAllocation) const
7762 {
7763     if(m_UseKhrDedicatedAllocation)
7764     {
7765         VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
7766         memReqInfo.image = hImage;
7767 
7768         VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
7769 
7770         VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
7771         memReq2.pNext = &memDedicatedReq;
7772 
7773         (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
7774 
7775         memReq = memReq2.memoryRequirements;
7776         requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
7777         prefersDedicatedAllocation  = (memDedicatedReq.prefersDedicatedAllocation  != VK_FALSE);
7778     }
7779     else
7780     {
7781         (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
7782         requiresDedicatedAllocation = false;
7783         prefersDedicatedAllocation  = false;
7784     }
7785 }
7786 
AllocateMemory(const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,VkBuffer dedicatedBuffer,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)7787 VkResult VmaAllocator_T::AllocateMemory(
7788     const VkMemoryRequirements& vkMemReq,
7789     bool requiresDedicatedAllocation,
7790     bool prefersDedicatedAllocation,
7791     VkBuffer dedicatedBuffer,
7792     VkImage dedicatedImage,
7793     const VmaAllocationCreateInfo& createInfo,
7794     VmaSuballocationType suballocType,
7795     VmaAllocation* pAllocation)
7796 {
7797     if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
7798         (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
7799     {
7800         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
7801         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
7802     }
7803     if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
7804         (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
7805     {
7806         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
7807         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
7808     }
7809     if(requiresDedicatedAllocation)
7810     {
7811         if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
7812         {
7813             VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
7814             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
7815         }
7816         if(createInfo.pool != VK_NULL_HANDLE)
7817         {
7818             VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
7819             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
7820         }
7821     }
7822     if((createInfo.pool != VK_NULL_HANDLE) &&
7823         ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
7824     {
7825         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
7826         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
7827     }
7828 
7829     if(createInfo.pool != VK_NULL_HANDLE)
7830     {
7831         return createInfo.pool->m_BlockVector.Allocate(
7832             createInfo.pool,
7833             m_CurrentFrameIndex.load(),
7834             vkMemReq,
7835             createInfo,
7836             suballocType,
7837             pAllocation);
7838     }
7839     else
7840     {
7841         // Bit mask of memory Vulkan types acceptable for this allocation.
7842         uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
7843         uint32_t memTypeIndex = UINT32_MAX;
7844         VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
7845         if(res == VK_SUCCESS)
7846         {
7847             res = AllocateMemoryOfType(
7848                 vkMemReq,
7849                 requiresDedicatedAllocation || prefersDedicatedAllocation,
7850                 dedicatedBuffer,
7851                 dedicatedImage,
7852                 createInfo,
7853                 memTypeIndex,
7854                 suballocType,
7855                 pAllocation);
7856             // Succeeded on first try.
7857             if(res == VK_SUCCESS)
7858             {
7859                 return res;
7860             }
7861             // Allocation from this memory type failed. Try other compatible memory types.
7862             else
7863             {
7864                 for(;;)
7865                 {
7866                     // Remove old memTypeIndex from list of possibilities.
7867                     memoryTypeBits &= ~(1u << memTypeIndex);
7868                     // Find alternative memTypeIndex.
7869                     res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
7870                     if(res == VK_SUCCESS)
7871                     {
7872                         res = AllocateMemoryOfType(
7873                             vkMemReq,
7874                             requiresDedicatedAllocation || prefersDedicatedAllocation,
7875                             dedicatedBuffer,
7876                             dedicatedImage,
7877                             createInfo,
7878                             memTypeIndex,
7879                             suballocType,
7880                             pAllocation);
7881                         // Allocation from this alternative memory type succeeded.
7882                         if(res == VK_SUCCESS)
7883                         {
7884                             return res;
7885                         }
7886                         // else: Allocation from this memory type failed. Try next one - next loop iteration.
7887                     }
7888                     // No other matching memory type index could be found.
7889                     else
7890                     {
7891                         // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
7892                         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
7893                     }
7894                 }
7895             }
7896         }
7897         // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
7898         else
7899             return res;
7900     }
7901 }
7902 
FreeMemory(const VmaAllocation allocation)7903 void VmaAllocator_T::FreeMemory(const VmaAllocation allocation)
7904 {
7905     VMA_ASSERT(allocation);
7906 
7907     if(allocation->CanBecomeLost() == false ||
7908         allocation->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
7909     {
7910         switch(allocation->GetType())
7911         {
7912         case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
7913             {
7914                 VmaBlockVector* pBlockVector = VMA_NULL;
7915                 VmaPool hPool = allocation->GetPool();
7916                 if(hPool != VK_NULL_HANDLE)
7917                 {
7918                     pBlockVector = &hPool->m_BlockVector;
7919                 }
7920                 else
7921                 {
7922                     const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
7923                     pBlockVector = m_pBlockVectors[memTypeIndex];
7924                 }
7925                 pBlockVector->Free(allocation);
7926             }
7927             break;
7928         case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
7929             FreeDedicatedMemory(allocation);
7930             break;
7931         default:
7932             VMA_ASSERT(0);
7933         }
7934     }
7935 
7936     allocation->SetUserData(this, VMA_NULL);
7937     vma_delete(this, allocation);
7938 }
7939 
CalculateStats(VmaStats * pStats)7940 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
7941 {
7942     // Initialize.
7943     InitStatInfo(pStats->total);
7944     for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
7945         InitStatInfo(pStats->memoryType[i]);
7946     for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
7947         InitStatInfo(pStats->memoryHeap[i]);
7948 
7949     // Process default pools.
7950     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
7951     {
7952         VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
7953         VMA_ASSERT(pBlockVector);
7954         pBlockVector->AddStats(pStats);
7955     }
7956 
7957     // Process custom pools.
7958     {
7959         VmaMutexLock lock(m_PoolsMutex, m_UseMutex);
7960         for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
7961         {
7962             m_Pools[poolIndex]->GetBlockVector().AddStats(pStats);
7963         }
7964     }
7965 
7966     // Process dedicated allocations.
7967     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
7968     {
7969         const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
7970         VmaMutexLock dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
7971         AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
7972         VMA_ASSERT(pDedicatedAllocVector);
7973         for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
7974         {
7975             VmaStatInfo allocationStatInfo;
7976             (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo);
7977             VmaAddStatInfo(pStats->total, allocationStatInfo);
7978             VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
7979             VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
7980         }
7981     }
7982 
7983     // Postprocess.
7984     VmaPostprocessCalcStatInfo(pStats->total);
7985     for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
7986         VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
7987     for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
7988         VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
7989 }
7990 
7991 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
7992 
Defragment(VmaAllocation * pAllocations,size_t allocationCount,VkBool32 * pAllocationsChanged,const VmaDefragmentationInfo * pDefragmentationInfo,VmaDefragmentationStats * pDefragmentationStats)7993 VkResult VmaAllocator_T::Defragment(
7994     VmaAllocation* pAllocations,
7995     size_t allocationCount,
7996     VkBool32* pAllocationsChanged,
7997     const VmaDefragmentationInfo* pDefragmentationInfo,
7998     VmaDefragmentationStats* pDefragmentationStats)
7999 {
8000     if(pAllocationsChanged != VMA_NULL)
8001     {
8002         memset(pAllocationsChanged, 0, sizeof(*pAllocationsChanged));
8003     }
8004     if(pDefragmentationStats != VMA_NULL)
8005     {
8006         memset(pDefragmentationStats, 0, sizeof(*pDefragmentationStats));
8007     }
8008 
8009     const uint32_t currentFrameIndex = m_CurrentFrameIndex.load();
8010 
8011     VmaMutexLock poolsLock(m_PoolsMutex, m_UseMutex);
8012 
8013     const size_t poolCount = m_Pools.size();
8014 
8015     // Dispatch pAllocations among defragmentators. Create them in BlockVectors when necessary.
8016     for(size_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
8017     {
8018         VmaAllocation hAlloc = pAllocations[allocIndex];
8019         VMA_ASSERT(hAlloc);
8020         const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
8021         // DedicatedAlloc cannot be defragmented.
8022         if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
8023             // Only HOST_VISIBLE memory types can be defragmented.
8024             ((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0) &&
8025             // Lost allocation cannot be defragmented.
8026             (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
8027         {
8028             VmaBlockVector* pAllocBlockVector = VMA_NULL;
8029 
8030             const VmaPool hAllocPool = hAlloc->GetPool();
8031             // This allocation belongs to custom pool.
8032             if(hAllocPool != VK_NULL_HANDLE)
8033             {
8034                 pAllocBlockVector = &hAllocPool->GetBlockVector();
8035             }
8036             // This allocation belongs to general pool.
8037             else
8038             {
8039                 pAllocBlockVector = m_pBlockVectors[memTypeIndex];
8040             }
8041 
8042             VmaDefragmentator* const pDefragmentator = pAllocBlockVector->EnsureDefragmentator(this, currentFrameIndex);
8043 
8044             VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
8045                 &pAllocationsChanged[allocIndex] : VMA_NULL;
8046             pDefragmentator->AddAllocation(hAlloc, pChanged);
8047         }
8048     }
8049 
8050     VkResult result = VK_SUCCESS;
8051 
8052     // ======== Main processing.
8053 
8054     VkDeviceSize maxBytesToMove = SIZE_MAX;
8055     uint32_t maxAllocationsToMove = UINT32_MAX;
8056     if(pDefragmentationInfo != VMA_NULL)
8057     {
8058         maxBytesToMove = pDefragmentationInfo->maxBytesToMove;
8059         maxAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
8060     }
8061 
8062     // Process standard memory.
8063     for(uint32_t memTypeIndex = 0;
8064         (memTypeIndex < GetMemoryTypeCount()) && (result == VK_SUCCESS);
8065         ++memTypeIndex)
8066     {
8067         // Only HOST_VISIBLE memory types can be defragmented.
8068         if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
8069         {
8070             result = m_pBlockVectors[memTypeIndex]->Defragment(
8071                 pDefragmentationStats,
8072                 maxBytesToMove,
8073                 maxAllocationsToMove);
8074         }
8075     }
8076 
8077     // Process custom pools.
8078     for(size_t poolIndex = 0; (poolIndex < poolCount) && (result == VK_SUCCESS); ++poolIndex)
8079     {
8080         result = m_Pools[poolIndex]->GetBlockVector().Defragment(
8081             pDefragmentationStats,
8082             maxBytesToMove,
8083             maxAllocationsToMove);
8084     }
8085 
8086     // ========  Destroy defragmentators.
8087 
8088     // Process custom pools.
8089     for(size_t poolIndex = poolCount; poolIndex--; )
8090     {
8091         m_Pools[poolIndex]->GetBlockVector().DestroyDefragmentator();
8092     }
8093 
8094     // Process standard memory.
8095     for(uint32_t memTypeIndex = GetMemoryTypeCount(); memTypeIndex--; )
8096     {
8097         if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
8098         {
8099             m_pBlockVectors[memTypeIndex]->DestroyDefragmentator();
8100         }
8101     }
8102 
8103     return result;
8104 }
8105 
GetAllocationInfo(VmaAllocation hAllocation,VmaAllocationInfo * pAllocationInfo)8106 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
8107 {
8108     if(hAllocation->CanBecomeLost())
8109     {
8110         /*
8111         Warning: This is a carefully designed algorithm.
8112         Do not modify unless you really know what you're doing :)
8113         */
8114         uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
8115         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
8116         for(;;)
8117         {
8118             if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
8119             {
8120                 pAllocationInfo->memoryType = UINT32_MAX;
8121                 pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
8122                 pAllocationInfo->offset = 0;
8123                 pAllocationInfo->size = hAllocation->GetSize();
8124                 pAllocationInfo->pMappedData = VMA_NULL;
8125                 pAllocationInfo->pUserData = hAllocation->GetUserData();
8126                 return;
8127             }
8128             else if(localLastUseFrameIndex == localCurrFrameIndex)
8129             {
8130                 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
8131                 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
8132                 pAllocationInfo->offset = hAllocation->GetOffset();
8133                 pAllocationInfo->size = hAllocation->GetSize();
8134                 pAllocationInfo->pMappedData = VMA_NULL;
8135                 pAllocationInfo->pUserData = hAllocation->GetUserData();
8136                 return;
8137             }
8138             else // Last use time earlier than current time.
8139             {
8140                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
8141                 {
8142                     localLastUseFrameIndex = localCurrFrameIndex;
8143                 }
8144             }
8145         }
8146     }
8147     else
8148     {
8149         pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
8150         pAllocationInfo->deviceMemory = hAllocation->GetMemory();
8151         pAllocationInfo->offset = hAllocation->GetOffset();
8152         pAllocationInfo->size = hAllocation->GetSize();
8153         pAllocationInfo->pMappedData = hAllocation->GetMappedData();
8154         pAllocationInfo->pUserData = hAllocation->GetUserData();
8155     }
8156 }
8157 
TouchAllocation(VmaAllocation hAllocation)8158 bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
8159 {
8160     // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
8161     if(hAllocation->CanBecomeLost())
8162     {
8163         uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
8164         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
8165         for(;;)
8166         {
8167             if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
8168             {
8169                 return false;
8170             }
8171             else if(localLastUseFrameIndex == localCurrFrameIndex)
8172             {
8173                 return true;
8174             }
8175             else // Last use time earlier than current time.
8176             {
8177                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
8178                 {
8179                     localLastUseFrameIndex = localCurrFrameIndex;
8180                 }
8181             }
8182         }
8183     }
8184     else
8185     {
8186         return true;
8187     }
8188 }
8189 
CreatePool(const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)8190 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
8191 {
8192     VMA_DEBUG_LOG("  CreatePool: MemoryTypeIndex=%u", pCreateInfo->memoryTypeIndex);
8193 
8194     VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
8195 
8196     if(newCreateInfo.maxBlockCount == 0)
8197     {
8198         newCreateInfo.maxBlockCount = SIZE_MAX;
8199     }
8200     if(newCreateInfo.blockSize == 0)
8201     {
8202         newCreateInfo.blockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
8203     }
8204 
8205     *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo);
8206 
8207     VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
8208     if(res != VK_SUCCESS)
8209     {
8210         vma_delete(this, *pPool);
8211         *pPool = VMA_NULL;
8212         return res;
8213     }
8214 
8215     // Add to m_Pools.
8216     {
8217         VmaMutexLock lock(m_PoolsMutex, m_UseMutex);
8218         VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool);
8219     }
8220 
8221     return VK_SUCCESS;
8222 }
8223 
DestroyPool(VmaPool pool)8224 void VmaAllocator_T::DestroyPool(VmaPool pool)
8225 {
8226     // Remove from m_Pools.
8227     {
8228         VmaMutexLock lock(m_PoolsMutex, m_UseMutex);
8229         bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
8230         VMA_ASSERT(success && "Pool not found in Allocator.");
8231     }
8232 
8233     vma_delete(this, pool);
8234 }
8235 
GetPoolStats(VmaPool pool,VmaPoolStats * pPoolStats)8236 void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
8237 {
8238     pool->m_BlockVector.GetPoolStats(pPoolStats);
8239 }
8240 
SetCurrentFrameIndex(uint32_t frameIndex)8241 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
8242 {
8243     m_CurrentFrameIndex.store(frameIndex);
8244 }
8245 
MakePoolAllocationsLost(VmaPool hPool,size_t * pLostAllocationCount)8246 void VmaAllocator_T::MakePoolAllocationsLost(
8247     VmaPool hPool,
8248     size_t* pLostAllocationCount)
8249 {
8250     hPool->m_BlockVector.MakePoolAllocationsLost(
8251         m_CurrentFrameIndex.load(),
8252         pLostAllocationCount);
8253 }
8254 
CreateLostAllocation(VmaAllocation * pAllocation)8255 void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
8256 {
8257     *pAllocation = vma_new(this, VmaAllocation_T)(VMA_FRAME_INDEX_LOST, false);
8258     (*pAllocation)->InitLost();
8259 }
8260 
AllocateVulkanMemory(const VkMemoryAllocateInfo * pAllocateInfo,VkDeviceMemory * pMemory)8261 VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
8262 {
8263     const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
8264 
8265     VkResult res;
8266     if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
8267     {
8268         VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
8269         if(m_HeapSizeLimit[heapIndex] >= pAllocateInfo->allocationSize)
8270         {
8271             res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
8272             if(res == VK_SUCCESS)
8273             {
8274                 m_HeapSizeLimit[heapIndex] -= pAllocateInfo->allocationSize;
8275             }
8276         }
8277         else
8278         {
8279             res = VK_ERROR_OUT_OF_DEVICE_MEMORY;
8280         }
8281     }
8282     else
8283     {
8284         res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
8285     }
8286 
8287     if(res == VK_SUCCESS && m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
8288     {
8289         (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize);
8290     }
8291 
8292     return res;
8293 }
8294 
FreeVulkanMemory(uint32_t memoryType,VkDeviceSize size,VkDeviceMemory hMemory)8295 void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
8296 {
8297     if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
8298     {
8299         (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size);
8300     }
8301 
8302     (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
8303 
8304     const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memoryType);
8305     if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
8306     {
8307         VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
8308         m_HeapSizeLimit[heapIndex] += size;
8309     }
8310 }
8311 
Map(VmaAllocation hAllocation,void ** ppData)8312 VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
8313 {
8314     if(hAllocation->CanBecomeLost())
8315     {
8316         return VK_ERROR_MEMORY_MAP_FAILED;
8317     }
8318 
8319     switch(hAllocation->GetType())
8320     {
8321     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
8322         {
8323             VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
8324             char *pBytes = VMA_NULL;
8325             VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
8326             if(res == VK_SUCCESS)
8327             {
8328                 *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
8329                 hAllocation->BlockAllocMap();
8330             }
8331             return res;
8332         }
8333     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
8334         return hAllocation->DedicatedAllocMap(this, ppData);
8335     default:
8336         VMA_ASSERT(0);
8337         return VK_ERROR_MEMORY_MAP_FAILED;
8338     }
8339 }
8340 
Unmap(VmaAllocation hAllocation)8341 void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
8342 {
8343     switch(hAllocation->GetType())
8344     {
8345     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
8346         {
8347             VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
8348             hAllocation->BlockAllocUnmap();
8349             pBlock->Unmap(this, 1);
8350         }
8351         break;
8352     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
8353         hAllocation->DedicatedAllocUnmap(this);
8354         break;
8355     default:
8356         VMA_ASSERT(0);
8357     }
8358 }
8359 
BindBufferMemory(VmaAllocation hAllocation,VkBuffer hBuffer)8360 VkResult VmaAllocator_T::BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer)
8361 {
8362     VkResult res = VK_SUCCESS;
8363     switch(hAllocation->GetType())
8364     {
8365     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
8366         res = GetVulkanFunctions().vkBindBufferMemory(
8367             m_hDevice,
8368             hBuffer,
8369             hAllocation->GetMemory(),
8370             0); //memoryOffset
8371         break;
8372     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
8373     {
8374         VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
8375         VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
8376         res = pBlock->BindBufferMemory(this, hAllocation, hBuffer);
8377         break;
8378     }
8379     default:
8380         VMA_ASSERT(0);
8381     }
8382     return res;
8383 }
8384 
BindImageMemory(VmaAllocation hAllocation,VkImage hImage)8385 VkResult VmaAllocator_T::BindImageMemory(VmaAllocation hAllocation, VkImage hImage)
8386 {
8387     VkResult res = VK_SUCCESS;
8388     switch(hAllocation->GetType())
8389     {
8390     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
8391         res = GetVulkanFunctions().vkBindImageMemory(
8392             m_hDevice,
8393             hImage,
8394             hAllocation->GetMemory(),
8395             0); //memoryOffset
8396         break;
8397     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
8398     {
8399         VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
8400         VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
8401         res = pBlock->BindImageMemory(this, hAllocation, hImage);
8402         break;
8403     }
8404     default:
8405         VMA_ASSERT(0);
8406     }
8407     return res;
8408 }
8409 
FreeDedicatedMemory(VmaAllocation allocation)8410 void VmaAllocator_T::FreeDedicatedMemory(VmaAllocation allocation)
8411 {
8412     VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
8413 
8414     const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
8415     {
8416         VmaMutexLock lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
8417         AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
8418         VMA_ASSERT(pDedicatedAllocations);
8419         bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
8420         VMA_ASSERT(success);
8421     }
8422 
8423     VkDeviceMemory hMemory = allocation->GetMemory();
8424 
8425     if(allocation->GetMappedData() != VMA_NULL)
8426     {
8427         (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
8428     }
8429 
8430     FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
8431 
8432     VMA_DEBUG_LOG("    Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
8433 }
8434 
8435 #if VMA_STATS_STRING_ENABLED
8436 
PrintDetailedMap(VmaJsonWriter & json)8437 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
8438 {
8439     bool dedicatedAllocationsStarted = false;
8440     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
8441     {
8442         VmaMutexLock dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
8443         AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
8444         VMA_ASSERT(pDedicatedAllocVector);
8445         if(pDedicatedAllocVector->empty() == false)
8446         {
8447             if(dedicatedAllocationsStarted == false)
8448             {
8449                 dedicatedAllocationsStarted = true;
8450                 json.WriteString("DedicatedAllocations");
8451                 json.BeginObject();
8452             }
8453 
8454             json.BeginString("Type ");
8455             json.ContinueString(memTypeIndex);
8456             json.EndString();
8457 
8458             json.BeginArray();
8459 
8460             for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
8461             {
8462                 const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
8463                 json.BeginObject(true);
8464 
8465                 json.WriteString("Type");
8466                 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[hAlloc->GetSuballocationType()]);
8467 
8468                 json.WriteString("Size");
8469                 json.WriteNumber(hAlloc->GetSize());
8470 
8471                 const void* pUserData = hAlloc->GetUserData();
8472                 if(pUserData != VMA_NULL)
8473                 {
8474                     json.WriteString("UserData");
8475                     if(hAlloc->IsUserDataString())
8476                     {
8477                         json.WriteString((const char*)pUserData);
8478                     }
8479                     else
8480                     {
8481                         json.BeginString();
8482                         json.ContinueString_Pointer(pUserData);
8483                         json.EndString();
8484                     }
8485                 }
8486 
8487                 json.EndObject();
8488             }
8489 
8490             json.EndArray();
8491         }
8492     }
8493     if(dedicatedAllocationsStarted)
8494     {
8495         json.EndObject();
8496     }
8497 
8498     {
8499         bool allocationsStarted = false;
8500         for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
8501         {
8502             if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
8503             {
8504                 if(allocationsStarted == false)
8505                 {
8506                     allocationsStarted = true;
8507                     json.WriteString("DefaultPools");
8508                     json.BeginObject();
8509                 }
8510 
8511                 json.BeginString("Type ");
8512                 json.ContinueString(memTypeIndex);
8513                 json.EndString();
8514 
8515                 m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
8516             }
8517         }
8518         if(allocationsStarted)
8519         {
8520             json.EndObject();
8521         }
8522     }
8523 
8524     {
8525         VmaMutexLock lock(m_PoolsMutex, m_UseMutex);
8526         const size_t poolCount = m_Pools.size();
8527         if(poolCount > 0)
8528         {
8529             json.WriteString("Pools");
8530             json.BeginArray();
8531             for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
8532             {
8533                 m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
8534             }
8535             json.EndArray();
8536         }
8537     }
8538 }
8539 
8540 #endif // #if VMA_STATS_STRING_ENABLED
8541 
AllocateMemoryForImage(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pAllocationCreateInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)8542 static VkResult AllocateMemoryForImage(
8543     VmaAllocator allocator,
8544     VkImage image,
8545     const VmaAllocationCreateInfo* pAllocationCreateInfo,
8546     VmaSuballocationType suballocType,
8547     VmaAllocation* pAllocation)
8548 {
8549     VMA_ASSERT(allocator && (image != VK_NULL_HANDLE) && pAllocationCreateInfo && pAllocation);
8550 
8551     VkMemoryRequirements vkMemReq = {};
8552     bool requiresDedicatedAllocation = false;
8553     bool prefersDedicatedAllocation  = false;
8554     allocator->GetImageMemoryRequirements(image, vkMemReq,
8555         requiresDedicatedAllocation, prefersDedicatedAllocation);
8556 
8557     return allocator->AllocateMemory(
8558         vkMemReq,
8559         requiresDedicatedAllocation,
8560         prefersDedicatedAllocation,
8561         VK_NULL_HANDLE, // dedicatedBuffer
8562         image, // dedicatedImage
8563         *pAllocationCreateInfo,
8564         suballocType,
8565         pAllocation);
8566 }
8567 
8568 ////////////////////////////////////////////////////////////////////////////////
8569 // Public interface
8570 
vmaCreateAllocator(const VmaAllocatorCreateInfo * pCreateInfo,VmaAllocator * pAllocator)8571 VkResult vmaCreateAllocator(
8572     const VmaAllocatorCreateInfo* pCreateInfo,
8573     VmaAllocator* pAllocator)
8574 {
8575     VMA_ASSERT(pCreateInfo && pAllocator);
8576     VMA_DEBUG_LOG("vmaCreateAllocator");
8577     *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
8578     return VK_SUCCESS;
8579 }
8580 
vmaDestroyAllocator(VmaAllocator allocator)8581 void vmaDestroyAllocator(
8582     VmaAllocator allocator)
8583 {
8584     if(allocator != VK_NULL_HANDLE)
8585     {
8586         VMA_DEBUG_LOG("vmaDestroyAllocator");
8587         VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
8588         vma_delete(&allocationCallbacks, allocator);
8589     }
8590 }
8591 
vmaGetPhysicalDeviceProperties(VmaAllocator allocator,const VkPhysicalDeviceProperties ** ppPhysicalDeviceProperties)8592 void vmaGetPhysicalDeviceProperties(
8593     VmaAllocator allocator,
8594     const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
8595 {
8596     VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
8597     *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
8598 }
8599 
vmaGetMemoryProperties(VmaAllocator allocator,const VkPhysicalDeviceMemoryProperties ** ppPhysicalDeviceMemoryProperties)8600 void vmaGetMemoryProperties(
8601     VmaAllocator allocator,
8602     const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
8603 {
8604     VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
8605     *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
8606 }
8607 
vmaGetMemoryTypeProperties(VmaAllocator allocator,uint32_t memoryTypeIndex,VkMemoryPropertyFlags * pFlags)8608 void vmaGetMemoryTypeProperties(
8609     VmaAllocator allocator,
8610     uint32_t memoryTypeIndex,
8611     VkMemoryPropertyFlags* pFlags)
8612 {
8613     VMA_ASSERT(allocator && pFlags);
8614     VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
8615     *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
8616 }
8617 
vmaSetCurrentFrameIndex(VmaAllocator allocator,uint32_t frameIndex)8618 void vmaSetCurrentFrameIndex(
8619     VmaAllocator allocator,
8620     uint32_t frameIndex)
8621 {
8622     VMA_ASSERT(allocator);
8623     VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
8624 
8625     VMA_DEBUG_GLOBAL_MUTEX_LOCK
8626 
8627     allocator->SetCurrentFrameIndex(frameIndex);
8628 }
8629 
vmaCalculateStats(VmaAllocator allocator,VmaStats * pStats)8630 void vmaCalculateStats(
8631     VmaAllocator allocator,
8632     VmaStats* pStats)
8633 {
8634     VMA_ASSERT(allocator && pStats);
8635     VMA_DEBUG_GLOBAL_MUTEX_LOCK
8636     allocator->CalculateStats(pStats);
8637 }
8638 
8639 #if VMA_STATS_STRING_ENABLED
8640 
vmaBuildStatsString(VmaAllocator allocator,char ** ppStatsString,VkBool32 detailedMap)8641 void vmaBuildStatsString(
8642     VmaAllocator allocator,
8643     char** ppStatsString,
8644     VkBool32 detailedMap)
8645 {
8646     VMA_ASSERT(allocator && ppStatsString);
8647     VMA_DEBUG_GLOBAL_MUTEX_LOCK
8648 
8649     VmaStringBuilder sb(allocator);
8650     {
8651         VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
8652         json.BeginObject();
8653 
8654         VmaStats stats;
8655         allocator->CalculateStats(&stats);
8656 
8657         json.WriteString("Total");
8658         VmaPrintStatInfo(json, stats.total);
8659 
8660         for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
8661         {
8662             json.BeginString("Heap ");
8663             json.ContinueString(heapIndex);
8664             json.EndString();
8665             json.BeginObject();
8666 
8667             json.WriteString("Size");
8668             json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
8669 
8670             json.WriteString("Flags");
8671             json.BeginArray(true);
8672             if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
8673             {
8674                 json.WriteString("DEVICE_LOCAL");
8675             }
8676             json.EndArray();
8677 
8678             if(stats.memoryHeap[heapIndex].blockCount > 0)
8679             {
8680                 json.WriteString("Stats");
8681                 VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
8682             }
8683 
8684             for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
8685             {
8686                 if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
8687                 {
8688                     json.BeginString("Type ");
8689                     json.ContinueString(typeIndex);
8690                     json.EndString();
8691 
8692                     json.BeginObject();
8693 
8694                     json.WriteString("Flags");
8695                     json.BeginArray(true);
8696                     VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
8697                     if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
8698                     {
8699                         json.WriteString("DEVICE_LOCAL");
8700                     }
8701                     if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
8702                     {
8703                         json.WriteString("HOST_VISIBLE");
8704                     }
8705                     if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
8706                     {
8707                         json.WriteString("HOST_COHERENT");
8708                     }
8709                     if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
8710                     {
8711                         json.WriteString("HOST_CACHED");
8712                     }
8713                     if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
8714                     {
8715                         json.WriteString("LAZILY_ALLOCATED");
8716                     }
8717                     json.EndArray();
8718 
8719                     if(stats.memoryType[typeIndex].blockCount > 0)
8720                     {
8721                         json.WriteString("Stats");
8722                         VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
8723                     }
8724 
8725                     json.EndObject();
8726                 }
8727             }
8728 
8729             json.EndObject();
8730         }
8731         if(detailedMap == VK_TRUE)
8732         {
8733             allocator->PrintDetailedMap(json);
8734         }
8735 
8736         json.EndObject();
8737     }
8738 
8739     const size_t len = sb.GetLength();
8740     char* const pChars = vma_new_array(allocator, char, len + 1);
8741     if(len > 0)
8742     {
8743         memcpy(pChars, sb.GetData(), len);
8744     }
8745     pChars[len] = '\0';
8746     *ppStatsString = pChars;
8747 }
8748 
vmaFreeStatsString(VmaAllocator allocator,char * pStatsString)8749 void vmaFreeStatsString(
8750     VmaAllocator allocator,
8751     char* pStatsString)
8752 {
8753     if(pStatsString != VMA_NULL)
8754     {
8755         VMA_ASSERT(allocator);
8756         size_t len = strlen(pStatsString);
8757         vma_delete_array(allocator, pStatsString, len + 1);
8758     }
8759 }
8760 
8761 #endif // #if VMA_STATS_STRING_ENABLED
8762 
8763 /*
8764 This function is not protected by any mutex because it just reads immutable data.
8765 */
vmaFindMemoryTypeIndex(VmaAllocator allocator,uint32_t memoryTypeBits,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)8766 VkResult vmaFindMemoryTypeIndex(
8767     VmaAllocator allocator,
8768     uint32_t memoryTypeBits,
8769     const VmaAllocationCreateInfo* pAllocationCreateInfo,
8770     uint32_t* pMemoryTypeIndex)
8771 {
8772     VMA_ASSERT(allocator != VK_NULL_HANDLE);
8773     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
8774     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
8775 
8776     if(pAllocationCreateInfo->memoryTypeBits != 0)
8777     {
8778         memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
8779     }
8780 
8781     uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
8782     uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
8783 
8784     // Convert usage to requiredFlags and preferredFlags.
8785     switch(pAllocationCreateInfo->usage)
8786     {
8787     case VMA_MEMORY_USAGE_UNKNOWN:
8788         break;
8789     case VMA_MEMORY_USAGE_GPU_ONLY:
8790         preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
8791         break;
8792     case VMA_MEMORY_USAGE_CPU_ONLY:
8793         requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
8794         break;
8795     case VMA_MEMORY_USAGE_CPU_TO_GPU:
8796         requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
8797         preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
8798         break;
8799     case VMA_MEMORY_USAGE_GPU_TO_CPU:
8800         requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
8801         preferredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
8802         break;
8803     default:
8804         break;
8805     }
8806 
8807     *pMemoryTypeIndex = UINT32_MAX;
8808     uint32_t minCost = UINT32_MAX;
8809     for(uint32_t memTypeIndex = 0, memTypeBit = 1;
8810         memTypeIndex < allocator->GetMemoryTypeCount();
8811         ++memTypeIndex, memTypeBit <<= 1)
8812     {
8813         // This memory type is acceptable according to memoryTypeBits bitmask.
8814         if((memTypeBit & memoryTypeBits) != 0)
8815         {
8816             const VkMemoryPropertyFlags currFlags =
8817                 allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
8818             // This memory type contains requiredFlags.
8819             if((requiredFlags & ~currFlags) == 0)
8820             {
8821                 // Calculate cost as number of bits from preferredFlags not present in this memory type.
8822                 uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags);
8823                 // Remember memory type with lowest cost.
8824                 if(currCost < minCost)
8825                 {
8826                     *pMemoryTypeIndex = memTypeIndex;
8827                     if(currCost == 0)
8828                     {
8829                         return VK_SUCCESS;
8830                     }
8831                     minCost = currCost;
8832                 }
8833             }
8834         }
8835     }
8836     return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
8837 }
8838 
vmaFindMemoryTypeIndexForBufferInfo(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)8839 VkResult vmaFindMemoryTypeIndexForBufferInfo(
8840     VmaAllocator allocator,
8841     const VkBufferCreateInfo* pBufferCreateInfo,
8842     const VmaAllocationCreateInfo* pAllocationCreateInfo,
8843     uint32_t* pMemoryTypeIndex)
8844 {
8845     VMA_ASSERT(allocator != VK_NULL_HANDLE);
8846     VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
8847     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
8848     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
8849 
8850     const VkDevice hDev = allocator->m_hDevice;
8851     VkBuffer hBuffer = VK_NULL_HANDLE;
8852     VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
8853         hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
8854     if(res == VK_SUCCESS)
8855     {
8856         VkMemoryRequirements memReq = {};
8857         allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
8858             hDev, hBuffer, &memReq);
8859 
8860         res = vmaFindMemoryTypeIndex(
8861             allocator,
8862             memReq.memoryTypeBits,
8863             pAllocationCreateInfo,
8864             pMemoryTypeIndex);
8865 
8866         allocator->GetVulkanFunctions().vkDestroyBuffer(
8867             hDev, hBuffer, allocator->GetAllocationCallbacks());
8868     }
8869     return res;
8870 }
8871 
vmaFindMemoryTypeIndexForImageInfo(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)8872 VkResult vmaFindMemoryTypeIndexForImageInfo(
8873     VmaAllocator allocator,
8874     const VkImageCreateInfo* pImageCreateInfo,
8875     const VmaAllocationCreateInfo* pAllocationCreateInfo,
8876     uint32_t* pMemoryTypeIndex)
8877 {
8878     VMA_ASSERT(allocator != VK_NULL_HANDLE);
8879     VMA_ASSERT(pImageCreateInfo != VMA_NULL);
8880     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
8881     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
8882 
8883     const VkDevice hDev = allocator->m_hDevice;
8884     VkImage hImage = VK_NULL_HANDLE;
8885     VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
8886         hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
8887     if(res == VK_SUCCESS)
8888     {
8889         VkMemoryRequirements memReq = {};
8890         allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
8891             hDev, hImage, &memReq);
8892 
8893         res = vmaFindMemoryTypeIndex(
8894             allocator,
8895             memReq.memoryTypeBits,
8896             pAllocationCreateInfo,
8897             pMemoryTypeIndex);
8898 
8899         allocator->GetVulkanFunctions().vkDestroyImage(
8900             hDev, hImage, allocator->GetAllocationCallbacks());
8901     }
8902     return res;
8903 }
8904 
vmaCreatePool(VmaAllocator allocator,const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)8905 VkResult vmaCreatePool(
8906 	VmaAllocator allocator,
8907 	const VmaPoolCreateInfo* pCreateInfo,
8908 	VmaPool* pPool)
8909 {
8910     VMA_ASSERT(allocator && pCreateInfo && pPool);
8911 
8912     VMA_DEBUG_LOG("vmaCreatePool");
8913 
8914     VMA_DEBUG_GLOBAL_MUTEX_LOCK
8915 
8916     return allocator->CreatePool(pCreateInfo, pPool);
8917 }
8918 
vmaDestroyPool(VmaAllocator allocator,VmaPool pool)8919 void vmaDestroyPool(
8920     VmaAllocator allocator,
8921     VmaPool pool)
8922 {
8923     VMA_ASSERT(allocator);
8924 
8925     if(pool == VK_NULL_HANDLE)
8926     {
8927         return;
8928     }
8929 
8930     VMA_DEBUG_LOG("vmaDestroyPool");
8931 
8932     VMA_DEBUG_GLOBAL_MUTEX_LOCK
8933 
8934     allocator->DestroyPool(pool);
8935 }
8936 
vmaGetPoolStats(VmaAllocator allocator,VmaPool pool,VmaPoolStats * pPoolStats)8937 void vmaGetPoolStats(
8938     VmaAllocator allocator,
8939     VmaPool pool,
8940     VmaPoolStats* pPoolStats)
8941 {
8942     VMA_ASSERT(allocator && pool && pPoolStats);
8943 
8944     VMA_DEBUG_GLOBAL_MUTEX_LOCK
8945 
8946     allocator->GetPoolStats(pool, pPoolStats);
8947 }
8948 
vmaMakePoolAllocationsLost(VmaAllocator allocator,VmaPool pool,size_t * pLostAllocationCount)8949 void vmaMakePoolAllocationsLost(
8950     VmaAllocator allocator,
8951     VmaPool pool,
8952     size_t* pLostAllocationCount)
8953 {
8954     VMA_ASSERT(allocator && pool);
8955 
8956     VMA_DEBUG_GLOBAL_MUTEX_LOCK
8957 
8958     allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
8959 }
8960 
vmaAllocateMemory(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)8961 VkResult vmaAllocateMemory(
8962     VmaAllocator allocator,
8963     const VkMemoryRequirements* pVkMemoryRequirements,
8964     const VmaAllocationCreateInfo* pCreateInfo,
8965     VmaAllocation* pAllocation,
8966     VmaAllocationInfo* pAllocationInfo)
8967 {
8968     VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
8969 
8970     VMA_DEBUG_LOG("vmaAllocateMemory");
8971 
8972     VMA_DEBUG_GLOBAL_MUTEX_LOCK
8973 
8974 	VkResult result = allocator->AllocateMemory(
8975         *pVkMemoryRequirements,
8976         false, // requiresDedicatedAllocation
8977         false, // prefersDedicatedAllocation
8978         VK_NULL_HANDLE, // dedicatedBuffer
8979         VK_NULL_HANDLE, // dedicatedImage
8980         *pCreateInfo,
8981         VMA_SUBALLOCATION_TYPE_UNKNOWN,
8982         pAllocation);
8983 
8984     if(pAllocationInfo && result == VK_SUCCESS)
8985     {
8986         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
8987     }
8988 
8989 	return result;
8990 }
8991 
vmaAllocateMemoryForBuffer(VmaAllocator allocator,VkBuffer buffer,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)8992 VkResult vmaAllocateMemoryForBuffer(
8993     VmaAllocator allocator,
8994     VkBuffer buffer,
8995     const VmaAllocationCreateInfo* pCreateInfo,
8996     VmaAllocation* pAllocation,
8997     VmaAllocationInfo* pAllocationInfo)
8998 {
8999     VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
9000 
9001     VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
9002 
9003     VMA_DEBUG_GLOBAL_MUTEX_LOCK
9004 
9005     VkMemoryRequirements vkMemReq = {};
9006     bool requiresDedicatedAllocation = false;
9007     bool prefersDedicatedAllocation = false;
9008     allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
9009         requiresDedicatedAllocation,
9010         prefersDedicatedAllocation);
9011 
9012     VkResult result = allocator->AllocateMemory(
9013         vkMemReq,
9014         requiresDedicatedAllocation,
9015         prefersDedicatedAllocation,
9016         buffer, // dedicatedBuffer
9017         VK_NULL_HANDLE, // dedicatedImage
9018         *pCreateInfo,
9019         VMA_SUBALLOCATION_TYPE_BUFFER,
9020         pAllocation);
9021 
9022     if(pAllocationInfo && result == VK_SUCCESS)
9023     {
9024         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
9025     }
9026 
9027 	return result;
9028 }
9029 
vmaAllocateMemoryForImage(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)9030 VkResult vmaAllocateMemoryForImage(
9031     VmaAllocator allocator,
9032     VkImage image,
9033     const VmaAllocationCreateInfo* pCreateInfo,
9034     VmaAllocation* pAllocation,
9035     VmaAllocationInfo* pAllocationInfo)
9036 {
9037     VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
9038 
9039     VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
9040 
9041     VMA_DEBUG_GLOBAL_MUTEX_LOCK
9042 
9043     VkResult result = AllocateMemoryForImage(
9044         allocator,
9045         image,
9046         pCreateInfo,
9047         VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
9048         pAllocation);
9049 
9050     if(pAllocationInfo && result == VK_SUCCESS)
9051     {
9052         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
9053     }
9054 
9055 	return result;
9056 }
9057 
vmaFreeMemory(VmaAllocator allocator,VmaAllocation allocation)9058 void vmaFreeMemory(
9059     VmaAllocator allocator,
9060     VmaAllocation allocation)
9061 {
9062     VMA_ASSERT(allocator && allocation);
9063 
9064     VMA_DEBUG_LOG("vmaFreeMemory");
9065 
9066     VMA_DEBUG_GLOBAL_MUTEX_LOCK
9067 
9068     allocator->FreeMemory(allocation);
9069 }
9070 
vmaGetAllocationInfo(VmaAllocator allocator,VmaAllocation allocation,VmaAllocationInfo * pAllocationInfo)9071 void vmaGetAllocationInfo(
9072     VmaAllocator allocator,
9073     VmaAllocation allocation,
9074     VmaAllocationInfo* pAllocationInfo)
9075 {
9076     VMA_ASSERT(allocator && allocation && pAllocationInfo);
9077 
9078     VMA_DEBUG_GLOBAL_MUTEX_LOCK
9079 
9080     allocator->GetAllocationInfo(allocation, pAllocationInfo);
9081 }
9082 
vmaTouchAllocation(VmaAllocator allocator,VmaAllocation allocation)9083 VkBool32 vmaTouchAllocation(
9084     VmaAllocator allocator,
9085     VmaAllocation allocation)
9086 {
9087     VMA_ASSERT(allocator && allocation);
9088 
9089     VMA_DEBUG_GLOBAL_MUTEX_LOCK
9090 
9091     return allocator->TouchAllocation(allocation);
9092 }
9093 
vmaSetAllocationUserData(VmaAllocator allocator,VmaAllocation allocation,void * pUserData)9094 void vmaSetAllocationUserData(
9095     VmaAllocator allocator,
9096     VmaAllocation allocation,
9097     void* pUserData)
9098 {
9099     VMA_ASSERT(allocator && allocation);
9100 
9101     VMA_DEBUG_GLOBAL_MUTEX_LOCK
9102 
9103     allocation->SetUserData(allocator, pUserData);
9104 }
9105 
vmaCreateLostAllocation(VmaAllocator allocator,VmaAllocation * pAllocation)9106 void vmaCreateLostAllocation(
9107     VmaAllocator allocator,
9108     VmaAllocation* pAllocation)
9109 {
9110     VMA_ASSERT(allocator && pAllocation);
9111 
9112     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
9113 
9114     allocator->CreateLostAllocation(pAllocation);
9115 }
9116 
vmaMapMemory(VmaAllocator allocator,VmaAllocation allocation,void ** ppData)9117 VkResult vmaMapMemory(
9118     VmaAllocator allocator,
9119     VmaAllocation allocation,
9120     void** ppData)
9121 {
9122     VMA_ASSERT(allocator && allocation && ppData);
9123 
9124     VMA_DEBUG_GLOBAL_MUTEX_LOCK
9125 
9126     return allocator->Map(allocation, ppData);
9127 }
9128 
vmaUnmapMemory(VmaAllocator allocator,VmaAllocation allocation)9129 void vmaUnmapMemory(
9130     VmaAllocator allocator,
9131     VmaAllocation allocation)
9132 {
9133     VMA_ASSERT(allocator && allocation);
9134 
9135     VMA_DEBUG_GLOBAL_MUTEX_LOCK
9136 
9137     allocator->Unmap(allocation);
9138 }
9139 
vmaDefragment(VmaAllocator allocator,VmaAllocation * pAllocations,size_t allocationCount,VkBool32 * pAllocationsChanged,const VmaDefragmentationInfo * pDefragmentationInfo,VmaDefragmentationStats * pDefragmentationStats)9140 VkResult vmaDefragment(
9141     VmaAllocator allocator,
9142     VmaAllocation* pAllocations,
9143     size_t allocationCount,
9144     VkBool32* pAllocationsChanged,
9145     const VmaDefragmentationInfo *pDefragmentationInfo,
9146     VmaDefragmentationStats* pDefragmentationStats)
9147 {
9148     VMA_ASSERT(allocator && pAllocations);
9149 
9150     VMA_DEBUG_LOG("vmaDefragment");
9151 
9152     VMA_DEBUG_GLOBAL_MUTEX_LOCK
9153 
9154     return allocator->Defragment(pAllocations, allocationCount, pAllocationsChanged, pDefragmentationInfo, pDefragmentationStats);
9155 }
9156 
vmaBindBufferMemory(VmaAllocator allocator,VmaAllocation allocation,VkBuffer buffer)9157 VkResult vmaBindBufferMemory(
9158     VmaAllocator allocator,
9159     VmaAllocation allocation,
9160     VkBuffer buffer)
9161 {
9162     VMA_ASSERT(allocator && allocation && buffer);
9163 
9164     VMA_DEBUG_LOG("vmaBindBufferMemory");
9165 
9166     VMA_DEBUG_GLOBAL_MUTEX_LOCK
9167 
9168     return allocator->BindBufferMemory(allocation, buffer);
9169 }
9170 
vmaBindImageMemory(VmaAllocator allocator,VmaAllocation allocation,VkImage image)9171 VkResult vmaBindImageMemory(
9172     VmaAllocator allocator,
9173     VmaAllocation allocation,
9174     VkImage image)
9175 {
9176     VMA_ASSERT(allocator && allocation && image);
9177 
9178     VMA_DEBUG_LOG("vmaBindImageMemory");
9179 
9180     VMA_DEBUG_GLOBAL_MUTEX_LOCK
9181 
9182     return allocator->BindImageMemory(allocation, image);
9183 }
9184 
vmaCreateBuffer(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkBuffer * pBuffer,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)9185 VkResult vmaCreateBuffer(
9186     VmaAllocator allocator,
9187     const VkBufferCreateInfo* pBufferCreateInfo,
9188     const VmaAllocationCreateInfo* pAllocationCreateInfo,
9189     VkBuffer* pBuffer,
9190     VmaAllocation* pAllocation,
9191     VmaAllocationInfo* pAllocationInfo)
9192 {
9193     VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
9194 
9195     VMA_DEBUG_LOG("vmaCreateBuffer");
9196 
9197     VMA_DEBUG_GLOBAL_MUTEX_LOCK
9198 
9199     *pBuffer = VK_NULL_HANDLE;
9200     *pAllocation = VK_NULL_HANDLE;
9201 
9202     // 1. Create VkBuffer.
9203     VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
9204         allocator->m_hDevice,
9205         pBufferCreateInfo,
9206         allocator->GetAllocationCallbacks(),
9207         pBuffer);
9208     if(res >= 0)
9209     {
9210         // 2. vkGetBufferMemoryRequirements.
9211         VkMemoryRequirements vkMemReq = {};
9212         bool requiresDedicatedAllocation = false;
9213         bool prefersDedicatedAllocation  = false;
9214         allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
9215             requiresDedicatedAllocation, prefersDedicatedAllocation);
9216 
9217          // Make sure alignment requirements for specific buffer usages reported
9218          // in Physical Device Properties are included in alignment reported by memory requirements.
9219         if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT) != 0)
9220         {
9221            VMA_ASSERT(vkMemReq.alignment %
9222               allocator->m_PhysicalDeviceProperties.limits.minTexelBufferOffsetAlignment == 0);
9223         }
9224         if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) != 0)
9225         {
9226            VMA_ASSERT(vkMemReq.alignment %
9227               allocator->m_PhysicalDeviceProperties.limits.minUniformBufferOffsetAlignment == 0);
9228         }
9229         if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) != 0)
9230         {
9231            VMA_ASSERT(vkMemReq.alignment %
9232               allocator->m_PhysicalDeviceProperties.limits.minStorageBufferOffsetAlignment == 0);
9233         }
9234 
9235         // 3. Allocate memory using allocator.
9236         res = allocator->AllocateMemory(
9237             vkMemReq,
9238             requiresDedicatedAllocation,
9239             prefersDedicatedAllocation,
9240             *pBuffer, // dedicatedBuffer
9241             VK_NULL_HANDLE, // dedicatedImage
9242             *pAllocationCreateInfo,
9243             VMA_SUBALLOCATION_TYPE_BUFFER,
9244             pAllocation);
9245         if(res >= 0)
9246         {
9247             // 3. Bind buffer with memory.
9248             res = allocator->BindBufferMemory(*pAllocation, *pBuffer);
9249             if(res >= 0)
9250             {
9251                 // All steps succeeded.
9252                 if(pAllocationInfo != VMA_NULL)
9253                 {
9254                     allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
9255                 }
9256                 return VK_SUCCESS;
9257             }
9258             allocator->FreeMemory(*pAllocation);
9259             *pAllocation = VK_NULL_HANDLE;
9260             (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
9261             *pBuffer = VK_NULL_HANDLE;
9262             return res;
9263         }
9264         (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
9265         *pBuffer = VK_NULL_HANDLE;
9266         return res;
9267     }
9268     return res;
9269 }
9270 
vmaDestroyBuffer(VmaAllocator allocator,VkBuffer buffer,VmaAllocation allocation)9271 void vmaDestroyBuffer(
9272     VmaAllocator allocator,
9273     VkBuffer buffer,
9274     VmaAllocation allocation)
9275 {
9276     if(buffer != VK_NULL_HANDLE)
9277     {
9278         VMA_ASSERT(allocator);
9279 
9280         VMA_DEBUG_LOG("vmaDestroyBuffer");
9281 
9282         VMA_DEBUG_GLOBAL_MUTEX_LOCK
9283 
9284         (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
9285 
9286         allocator->FreeMemory(allocation);
9287     }
9288 }
9289 
vmaCreateImage(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkImage * pImage,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)9290 VkResult vmaCreateImage(
9291     VmaAllocator allocator,
9292     const VkImageCreateInfo* pImageCreateInfo,
9293     const VmaAllocationCreateInfo* pAllocationCreateInfo,
9294     VkImage* pImage,
9295     VmaAllocation* pAllocation,
9296     VmaAllocationInfo* pAllocationInfo)
9297 {
9298     VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
9299 
9300     VMA_DEBUG_LOG("vmaCreateImage");
9301 
9302     VMA_DEBUG_GLOBAL_MUTEX_LOCK
9303 
9304     *pImage = VK_NULL_HANDLE;
9305     *pAllocation = VK_NULL_HANDLE;
9306 
9307     // 1. Create VkImage.
9308     VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
9309         allocator->m_hDevice,
9310         pImageCreateInfo,
9311         allocator->GetAllocationCallbacks(),
9312         pImage);
9313     if(res >= 0)
9314     {
9315         VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
9316             VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
9317             VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
9318 
9319         // 2. Allocate memory using allocator.
9320         res = AllocateMemoryForImage(allocator, *pImage, pAllocationCreateInfo, suballocType, pAllocation);
9321         if(res >= 0)
9322         {
9323             // 3. Bind image with memory.
9324             res = allocator->BindImageMemory(*pAllocation, *pImage);
9325             if(res >= 0)
9326             {
9327                 // All steps succeeded.
9328                 if(pAllocationInfo != VMA_NULL)
9329                 {
9330                     allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
9331                 }
9332                 return VK_SUCCESS;
9333             }
9334             allocator->FreeMemory(*pAllocation);
9335             *pAllocation = VK_NULL_HANDLE;
9336             (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
9337             *pImage = VK_NULL_HANDLE;
9338             return res;
9339         }
9340         (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
9341         *pImage = VK_NULL_HANDLE;
9342         return res;
9343     }
9344     return res;
9345 }
9346 
vmaDestroyImage(VmaAllocator allocator,VkImage image,VmaAllocation allocation)9347 void vmaDestroyImage(
9348     VmaAllocator allocator,
9349     VkImage image,
9350     VmaAllocation allocation)
9351 {
9352     if(image != VK_NULL_HANDLE)
9353     {
9354         VMA_ASSERT(allocator);
9355 
9356         VMA_DEBUG_LOG("vmaDestroyImage");
9357 
9358         VMA_DEBUG_GLOBAL_MUTEX_LOCK
9359 
9360         (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
9361 
9362         allocator->FreeMemory(allocation);
9363     }
9364 }
9365 
9366 #endif // #ifdef VMA_IMPLEMENTATION
9367