1 //
2 // Copyright (c) 2017-2020 Advanced Micro Devices, Inc. All rights reserved.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22 
23 #ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H
24 #define AMD_VULKAN_MEMORY_ALLOCATOR_H
25 
26 /** \mainpage Vulkan Memory Allocator
27 
28 <b>Version 3.0.0-development</b> (2020-03-23)
29 
30 Copyright (c) 2017-2020 Advanced Micro Devices, Inc. All rights reserved. \n
31 License: MIT
32 
33 Documentation of all members: vk_mem_alloc.h
34 
35 \section main_table_of_contents Table of contents
36 
37 - <b>User guide</b>
38   - \subpage quick_start
39     - [Project setup](@ref quick_start_project_setup)
40     - [Initialization](@ref quick_start_initialization)
41     - [Resource allocation](@ref quick_start_resource_allocation)
42   - \subpage choosing_memory_type
43     - [Usage](@ref choosing_memory_type_usage)
44     - [Required and preferred flags](@ref choosing_memory_type_required_preferred_flags)
45     - [Explicit memory types](@ref choosing_memory_type_explicit_memory_types)
46     - [Custom memory pools](@ref choosing_memory_type_custom_memory_pools)
47     - [Dedicated allocations](@ref choosing_memory_type_dedicated_allocations)
48   - \subpage memory_mapping
49     - [Mapping functions](@ref memory_mapping_mapping_functions)
50     - [Persistently mapped memory](@ref memory_mapping_persistently_mapped_memory)
51     - [Cache flush and invalidate](@ref memory_mapping_cache_control)
52     - [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable)
53   - \subpage staying_within_budget
54     - [Querying for budget](@ref staying_within_budget_querying_for_budget)
55     - [Controlling memory usage](@ref staying_within_budget_controlling_memory_usage)
56   - \subpage custom_memory_pools
57     - [Choosing memory type index](@ref custom_memory_pools_MemTypeIndex)
58     - [Linear allocation algorithm](@ref linear_algorithm)
59       - [Free-at-once](@ref linear_algorithm_free_at_once)
60       - [Stack](@ref linear_algorithm_stack)
61       - [Double stack](@ref linear_algorithm_double_stack)
62       - [Ring buffer](@ref linear_algorithm_ring_buffer)
63     - [Buddy allocation algorithm](@ref buddy_algorithm)
64   - \subpage defragmentation
65       - [Defragmenting CPU memory](@ref defragmentation_cpu)
66       - [Defragmenting GPU memory](@ref defragmentation_gpu)
67       - [Additional notes](@ref defragmentation_additional_notes)
68       - [Writing custom allocation algorithm](@ref defragmentation_custom_algorithm)
69   - \subpage lost_allocations
70   - \subpage statistics
71     - [Numeric statistics](@ref statistics_numeric_statistics)
72     - [JSON dump](@ref statistics_json_dump)
73   - \subpage allocation_annotation
74     - [Allocation user data](@ref allocation_user_data)
75     - [Allocation names](@ref allocation_names)
76   - \subpage debugging_memory_usage
77     - [Memory initialization](@ref debugging_memory_usage_initialization)
78     - [Margins](@ref debugging_memory_usage_margins)
79     - [Corruption detection](@ref debugging_memory_usage_corruption_detection)
80   - \subpage record_and_replay
81 - \subpage usage_patterns
82   - [Common mistakes](@ref usage_patterns_common_mistakes)
83   - [Simple patterns](@ref usage_patterns_simple)
84   - [Advanced patterns](@ref usage_patterns_advanced)
85 - \subpage configuration
86   - [Pointers to Vulkan functions](@ref config_Vulkan_functions)
87   - [Custom host memory allocator](@ref custom_memory_allocator)
88   - [Device memory allocation callbacks](@ref allocation_callbacks)
89   - [Device heap memory limit](@ref heap_memory_limit)
90   - \subpage vk_khr_dedicated_allocation
91   - \subpage enabling_buffer_device_address
92   - \subpage vk_amd_device_coherent_memory
93 - \subpage general_considerations
94   - [Thread safety](@ref general_considerations_thread_safety)
95   - [Validation layer warnings](@ref general_considerations_validation_layer_warnings)
96   - [Allocation algorithm](@ref general_considerations_allocation_algorithm)
97   - [Features not supported](@ref general_considerations_features_not_supported)
98 
99 \section main_see_also See also
100 
101 - [Product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-memory-allocator/)
102 - [Source repository on GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)
103 
104 
105 
106 
107 \page quick_start Quick start
108 
109 \section quick_start_project_setup Project setup
110 
111 Vulkan Memory Allocator comes in form of a "stb-style" single header file.
112 You don't need to build it as a separate library project.
113 You can add this file directly to your project and submit it to code repository next to your other source files.
114 
115 "Single header" doesn't mean that everything is contained in C/C++ declarations,
116 like it tends to be in case of inline functions or C++ templates.
117 It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro.
118 If you don't do it properly, you will get linker errors.
119 
120 To do it properly:
121 
122 -# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library.
123    This includes declarations of all members of the library.
124 -# In exacly one CPP file define following macro before this include.
125    It enables also internal definitions.
126 
127 \code
128 #define VMA_IMPLEMENTATION
129 #include "vk_mem_alloc.h"
130 \endcode
131 
132 It may be a good idea to create dedicated CPP file just for this purpose.
133 
134 Note on language: This library is written in C++, but has C-compatible interface.
135 Thus you can include and use vk_mem_alloc.h in C or C++ code, but full
136 implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C.
137 
138 Please note that this library includes header `<vulkan/vulkan.h>`, which in turn
139 includes `<windows.h>` on Windows. If you need some specific macros defined
140 before including these headers (like `WIN32_LEAN_AND_MEAN` or
141 `WINVER` for Windows, `VK_USE_PLATFORM_WIN32_KHR` for Vulkan), you must define
142 them before every `#include` of this library.
143 
144 
145 \section quick_start_initialization Initialization
146 
147 At program startup:
148 
149 -# Initialize Vulkan to have `VkPhysicalDevice`, `VkDevice` and `VkInstance` object.
150 -# Fill VmaAllocatorCreateInfo structure and create #VmaAllocator object by
151    calling vmaCreateAllocator().
152 
153 \code
154 VmaAllocatorCreateInfo allocatorInfo = {};
155 allocatorInfo.physicalDevice = physicalDevice;
156 allocatorInfo.device = device;
157 allocatorInfo.instance = instance;
158 
159 VmaAllocator allocator;
160 vmaCreateAllocator(&allocatorInfo, &allocator);
161 \endcode
162 
163 \section quick_start_resource_allocation Resource allocation
164 
165 When you want to create a buffer or image:
166 
167 -# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure.
168 -# Fill VmaAllocationCreateInfo structure.
169 -# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory
170    already allocated and bound to it.
171 
172 \code
173 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
174 bufferInfo.size = 65536;
175 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
176 
177 VmaAllocationCreateInfo allocInfo = {};
178 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
179 
180 VkBuffer buffer;
181 VmaAllocation allocation;
182 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
183 \endcode
184 
185 Don't forget to destroy your objects when no longer needed:
186 
187 \code
188 vmaDestroyBuffer(allocator, buffer, allocation);
189 vmaDestroyAllocator(allocator);
190 \endcode
191 
192 
193 \page choosing_memory_type Choosing memory type
194 
195 Physical devices in Vulkan support various combinations of memory heaps and
196 types. Help with choosing correct and optimal memory type for your specific
197 resource is one of the key features of this library. You can use it by filling
198 appropriate members of VmaAllocationCreateInfo structure, as described below.
199 You can also combine multiple methods.
200 
201 -# If you just want to find memory type index that meets your requirements, you
202    can use function: vmaFindMemoryTypeIndex(), vmaFindMemoryTypeIndexForBufferInfo(),
203    vmaFindMemoryTypeIndexForImageInfo().
204 -# If you want to allocate a region of device memory without association with any
205    specific image or buffer, you can use function vmaAllocateMemory(). Usage of
206    this function is not recommended and usually not needed.
207    vmaAllocateMemoryPages() function is also provided for creating multiple allocations at once,
208    which may be useful for sparse binding.
209 -# If you already have a buffer or an image created, you want to allocate memory
210    for it and then you will bind it yourself, you can use function
211    vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage().
212    For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory()
213    or their extended versions: vmaBindBufferMemory2(), vmaBindImageMemory2().
214 -# If you want to create a buffer or an image, allocate memory for it and bind
215    them together, all in one call, you can use function vmaCreateBuffer(),
216    vmaCreateImage(). This is the easiest and recommended way to use this library.
217 
218 When using 3. or 4., the library internally queries Vulkan for memory types
219 supported for that buffer or image (function `vkGetBufferMemoryRequirements()`)
220 and uses only one of these types.
221 
222 If no memory type can be found that meets all the requirements, these functions
223 return `VK_ERROR_FEATURE_NOT_PRESENT`.
224 
225 You can leave VmaAllocationCreateInfo structure completely filled with zeros.
226 It means no requirements are specified for memory type.
227 It is valid, although not very useful.
228 
229 \section choosing_memory_type_usage Usage
230 
231 The easiest way to specify memory requirements is to fill member
232 VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage.
233 It defines high level, common usage types.
234 For more details, see description of this enum.
235 
236 For example, if you want to create a uniform buffer that will be filled using
237 transfer only once or infrequently and used for rendering every frame, you can
238 do it using following code:
239 
240 \code
241 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
242 bufferInfo.size = 65536;
243 bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
244 
245 VmaAllocationCreateInfo allocInfo = {};
246 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
247 
248 VkBuffer buffer;
249 VmaAllocation allocation;
250 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
251 \endcode
252 
253 \section choosing_memory_type_required_preferred_flags Required and preferred flags
254 
255 You can specify more detailed requirements by filling members
256 VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags
257 with a combination of bits from enum `VkMemoryPropertyFlags`. For example,
258 if you want to create a buffer that will be persistently mapped on host (so it
259 must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`,
260 use following code:
261 
262 \code
263 VmaAllocationCreateInfo allocInfo = {};
264 allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
265 allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
266 allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
267 
268 VkBuffer buffer;
269 VmaAllocation allocation;
270 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
271 \endcode
272 
273 A memory type is chosen that has all the required flags and as many preferred
274 flags set as possible.
275 
276 If you use VmaAllocationCreateInfo::usage, it is just internally converted to
277 a set of required and preferred flags.
278 
279 \section choosing_memory_type_explicit_memory_types Explicit memory types
280 
281 If you inspected memory types available on the physical device and you have
282 a preference for memory types that you want to use, you can fill member
283 VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set
284 means that a memory type with that index is allowed to be used for the
285 allocation. Special value 0, just like `UINT32_MAX`, means there are no
286 restrictions to memory type index.
287 
288 Please note that this member is NOT just a memory type index.
289 Still you can use it to choose just one, specific memory type.
290 For example, if you already determined that your buffer should be created in
291 memory type 2, use following code:
292 
293 \code
294 uint32_t memoryTypeIndex = 2;
295 
296 VmaAllocationCreateInfo allocInfo = {};
297 allocInfo.memoryTypeBits = 1u << memoryTypeIndex;
298 
299 VkBuffer buffer;
300 VmaAllocation allocation;
301 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
302 \endcode
303 
304 \section choosing_memory_type_custom_memory_pools Custom memory pools
305 
306 If you allocate from custom memory pool, all the ways of specifying memory
307 requirements described above are not applicable and the aforementioned members
308 of VmaAllocationCreateInfo structure are ignored. Memory type is selected
309 explicitly when creating the pool and then used to make all the allocations from
310 that pool. For further details, see \ref custom_memory_pools.
311 
312 \section choosing_memory_type_dedicated_allocations Dedicated allocations
313 
314 Memory for allocations is reserved out of larger block of `VkDeviceMemory`
315 allocated from Vulkan internally. That's the main feature of this whole library.
316 You can still request a separate memory block to be created for an allocation,
317 just like you would do in a trivial solution without using any allocator.
318 In that case, a buffer or image is always bound to that memory at offset 0.
319 This is called a "dedicated allocation".
320 You can explicitly request it by using flag #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
321 The library can also internally decide to use dedicated allocation in some cases, e.g.:
322 
323 - When the size of the allocation is large.
324 - When [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension is enabled
325   and it reports that dedicated allocation is required or recommended for the resource.
326 - When allocation of next big memory block fails due to not enough device memory,
327   but allocation with the exact requested size succeeds.
328 
329 
330 \page memory_mapping Memory mapping
331 
332 To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`,
333 to be able to read from it or write to it in CPU code.
334 Mapping is possible only of memory allocated from a memory type that has
335 `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag.
336 Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose.
337 You can use them directly with memory allocated by this library,
338 but it is not recommended because of following issue:
339 Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed.
340 This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan.
341 Because of this, Vulkan Memory Allocator provides following facilities:
342 
343 \section memory_mapping_mapping_functions Mapping functions
344 
345 The library provides following functions for mapping of a specific #VmaAllocation: vmaMapMemory(), vmaUnmapMemory().
346 They are safer and more convenient to use than standard Vulkan functions.
347 You can map an allocation multiple times simultaneously - mapping is reference-counted internally.
348 You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block.
349 The way it's implemented is that the library always maps entire memory block, not just region of the allocation.
350 For further details, see description of vmaMapMemory() function.
351 Example:
352 
353 \code
354 // Having these objects initialized:
355 
356 struct ConstantBuffer
357 {
358     ...
359 };
360 ConstantBuffer constantBufferData;
361 
362 VmaAllocator allocator;
363 VkBuffer constantBuffer;
364 VmaAllocation constantBufferAllocation;
365 
366 // You can map and fill your buffer using following code:
367 
368 void* mappedData;
369 vmaMapMemory(allocator, constantBufferAllocation, &mappedData);
370 memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
371 vmaUnmapMemory(allocator, constantBufferAllocation);
372 \endcode
373 
374 When mapping, you may see a warning from Vulkan validation layer similar to this one:
375 
376 <i>Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.</i>
377 
378 It happens because the library maps entire `VkDeviceMemory` block, where different
379 types of images and buffers may end up together, especially on GPUs with unified memory like Intel.
380 You can safely ignore it if you are sure you access only memory of the intended
381 object that you wanted to map.
382 
383 
384 \section memory_mapping_persistently_mapped_memory Persistently mapped memory
385 
386 Kepping your memory persistently mapped is generally OK in Vulkan.
387 You don't need to unmap it before using its data on the GPU.
388 The library provides a special feature designed for that:
389 Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in
390 VmaAllocationCreateInfo::flags stay mapped all the time,
391 so you can just access CPU pointer to it any time
392 without a need to call any "map" or "unmap" function.
393 Example:
394 
395 \code
396 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
397 bufCreateInfo.size = sizeof(ConstantBuffer);
398 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
399 
400 VmaAllocationCreateInfo allocCreateInfo = {};
401 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
402 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
403 
404 VkBuffer buf;
405 VmaAllocation alloc;
406 VmaAllocationInfo allocInfo;
407 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
408 
409 // Buffer is already mapped. You can access its memory.
410 memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
411 \endcode
412 
413 There are some exceptions though, when you should consider mapping memory only for a short period of time:
414 
415 - When operating system is Windows 7 or 8.x (Windows 10 is not affected because it uses WDDM2),
416   device is discrete AMD GPU,
417   and memory type is the special 256 MiB pool of `DEVICE_LOCAL + HOST_VISIBLE` memory
418   (selected when you use #VMA_MEMORY_USAGE_CPU_TO_GPU),
419   then whenever a memory block allocated from this memory type stays mapped
420   for the time of any call to `vkQueueSubmit()` or `vkQueuePresentKHR()`, this
421   block is migrated by WDDM to system RAM, which degrades performance. It doesn't
422   matter if that particular memory block is actually used by the command buffer
423   being submitted.
424 - On Mac/MoltenVK there is a known bug - [Issue #175](https://github.com/KhronosGroup/MoltenVK/issues/175)
425   which requires unmapping before GPU can see updated texture.
426 - Keeping many large memory blocks mapped may impact performance or stability of some debugging tools.
427 
428 \section memory_mapping_cache_control Cache flush and invalidate
429 
430 Memory in Vulkan doesn't need to be unmapped before using it on GPU,
431 but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set,
432 you need to manually **invalidate** cache before reading of mapped pointer
433 and **flush** cache after writing to mapped pointer.
434 Map/unmap operations don't do that automatically.
435 Vulkan provides following functions for this purpose `vkFlushMappedMemoryRanges()`,
436 `vkInvalidateMappedMemoryRanges()`, but this library provides more convenient
437 functions that refer to given allocation object: vmaFlushAllocation(),
438 vmaInvalidateAllocation(),
439 or multiple objects at once: vmaFlushAllocations(), vmaInvalidateAllocations().
440 
441 Regions of memory specified for flush/invalidate must be aligned to
442 `VkPhysicalDeviceLimits::nonCoherentAtomSize`. This is automatically ensured by the library.
443 In any memory type that is `HOST_VISIBLE` but not `HOST_COHERENT`, all allocations
444 within blocks are aligned to this value, so their offsets are always multiply of
445 `nonCoherentAtomSize` and two different allocations never share same "line" of this size.
446 
447 Please note that memory allocated with #VMA_MEMORY_USAGE_CPU_ONLY is guaranteed to be `HOST_COHERENT`.
448 
449 Also, Windows drivers from all 3 **PC** GPU vendors (AMD, Intel, NVIDIA)
450 currently provide `HOST_COHERENT` flag on all memory types that are
451 `HOST_VISIBLE`, so on this platform you may not need to bother.
452 
453 \section memory_mapping_finding_if_memory_mappable Finding out if memory is mappable
454 
455 It may happen that your allocation ends up in memory that is `HOST_VISIBLE` (available for mapping)
456 despite it wasn't explicitly requested.
457 For example, application may work on integrated graphics with unified memory (like Intel) or
458 allocation from video memory might have failed, so the library chose system memory as fallback.
459 
460 You can detect this case and map such allocation to access its memory on CPU directly,
461 instead of launching a transfer operation.
462 In order to do that: inspect `allocInfo.memoryType`, call vmaGetMemoryTypeProperties(),
463 and look for `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag in properties of that memory type.
464 
465 \code
466 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
467 bufCreateInfo.size = sizeof(ConstantBuffer);
468 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
469 
470 VmaAllocationCreateInfo allocCreateInfo = {};
471 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
472 allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
473 
474 VkBuffer buf;
475 VmaAllocation alloc;
476 VmaAllocationInfo allocInfo;
477 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
478 
479 VkMemoryPropertyFlags memFlags;
480 vmaGetMemoryTypeProperties(allocator, allocInfo.memoryType, &memFlags);
481 if((memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
482 {
483     // Allocation ended up in mappable memory. You can map it and access it directly.
484     void* mappedData;
485     vmaMapMemory(allocator, alloc, &mappedData);
486     memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
487     vmaUnmapMemory(allocator, alloc);
488 }
489 else
490 {
491     // Allocation ended up in non-mappable memory.
492     // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
493 }
494 \endcode
495 
496 You can even use #VMA_ALLOCATION_CREATE_MAPPED_BIT flag while creating allocations
497 that are not necessarily `HOST_VISIBLE` (e.g. using #VMA_MEMORY_USAGE_GPU_ONLY).
498 If the allocation ends up in memory type that is `HOST_VISIBLE`, it will be persistently mapped and you can use it directly.
499 If not, the flag is just ignored.
500 Example:
501 
502 \code
503 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
504 bufCreateInfo.size = sizeof(ConstantBuffer);
505 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
506 
507 VmaAllocationCreateInfo allocCreateInfo = {};
508 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
509 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
510 
511 VkBuffer buf;
512 VmaAllocation alloc;
513 VmaAllocationInfo allocInfo;
514 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
515 
516 if(allocInfo.pUserData != nullptr)
517 {
518     // Allocation ended up in mappable memory.
519     // It's persistently mapped. You can access it directly.
520     memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
521 }
522 else
523 {
524     // Allocation ended up in non-mappable memory.
525     // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
526 }
527 \endcode
528 
529 
530 \page staying_within_budget Staying within budget
531 
532 When developing a graphics-intensive game or program, it is important to avoid allocating
533 more GPU memory than it's physically available. When the memory is over-committed,
534 various bad things can happen, depending on the specific GPU, graphics driver, and
535 operating system:
536 
537 - It may just work without any problems.
538 - The application may slow down because some memory blocks are moved to system RAM
539   and the GPU has to access them through PCI Express bus.
540 - A new allocation may take very long time to complete, even few seconds, and possibly
541   freeze entire system.
542 - The new allocation may fail with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
543 - It may even result in GPU crash (TDR), observed as `VK_ERROR_DEVICE_LOST`
544   returned somewhere later.
545 
546 \section staying_within_budget_querying_for_budget Querying for budget
547 
548 To query for current memory usage and available budget, use function vmaGetBudget().
549 Returned structure #VmaBudget contains quantities expressed in bytes, per Vulkan memory heap.
550 
551 Please note that this function returns different information and works faster than
552 vmaCalculateStats(). vmaGetBudget() can be called every frame or even before every
553 allocation, while vmaCalculateStats() is intended to be used rarely,
554 only to obtain statistical information, e.g. for debugging purposes.
555 
556 It is recommended to use <b>VK_EXT_memory_budget</b> device extension to obtain information
557 about the budget from Vulkan device. VMA is able to use this extension automatically.
558 When not enabled, the allocator behaves same way, but then it estimates current usage
559 and available budget based on its internal information and Vulkan memory heap sizes,
560 which may be less precise. In order to use this extension:
561 
562 1. Make sure extensions VK_EXT_memory_budget and VK_KHR_get_physical_device_properties2
563    required by it are available and enable them. Please note that the first is a device
564    extension and the second is instance extension!
565 2. Use flag #VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT when creating #VmaAllocator object.
566 3. Make sure to call vmaSetCurrentFrameIndex() every frame. Budget is queried from
567    Vulkan inside of it to avoid overhead of querying it with every allocation.
568 
569 \section staying_within_budget_controlling_memory_usage Controlling memory usage
570 
571 There are many ways in which you can try to stay within the budget.
572 
573 First, when making new allocation requires allocating a new memory block, the library
574 tries not to exceed the budget automatically. If a block with default recommended size
575 (e.g. 256 MB) would go over budget, a smaller block is allocated, possibly even
576 dedicated memory for just this resource.
577 
578 If the size of the requested resource plus current memory usage is more than the
579 budget, by default the library still tries to create it, leaving it to the Vulkan
580 implementation whether the allocation succeeds or fails. You can change this behavior
581 by using #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag. With it, the allocation is
582 not made if it would exceed the budget or if the budget is already exceeded.
583 Some other allocations become lost instead to make room for it, if the mechanism of
584 [lost allocations](@ref lost_allocations) is used.
585 If that is not possible, the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
586 Example usage pattern may be to pass the #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag
587 when creating resources that are not essential for the application (e.g. the texture
588 of a specific object) and not to pass it when creating critically important resources
589 (e.g. render targets).
590 
591 Finally, you can also use #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT flag to make sure
592 a new allocation is created only when it fits inside one of the existing memory blocks.
593 If it would require to allocate a new block, if fails instead with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
594 This also ensures that the function call is very fast because it never goes to Vulkan
595 to obtain a new block.
596 
597 Please note that creating \ref custom_memory_pools with VmaPoolCreateInfo::minBlockCount
598 set to more than 0 will try to allocate memory blocks without checking whether they
599 fit within budget.
600 
601 
602 \page custom_memory_pools Custom memory pools
603 
604 A memory pool contains a number of `VkDeviceMemory` blocks.
605 The library automatically creates and manages default pool for each memory type available on the device.
606 Default memory pool automatically grows in size.
607 Size of allocated blocks is also variable and managed automatically.
608 
609 You can create custom pool and allocate memory out of it.
610 It can be useful if you want to:
611 
612 - Keep certain kind of allocations separate from others.
613 - Enforce particular, fixed size of Vulkan memory blocks.
614 - Limit maximum amount of Vulkan memory allocated for that pool.
615 - Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool.
616 
617 To use custom memory pools:
618 
619 -# Fill VmaPoolCreateInfo structure.
620 -# Call vmaCreatePool() to obtain #VmaPool handle.
621 -# When making an allocation, set VmaAllocationCreateInfo::pool to this handle.
622    You don't need to specify any other parameters of this structure, like `usage`.
623 
624 Example:
625 
626 \code
627 // Create a pool that can have at most 2 blocks, 128 MiB each.
628 VmaPoolCreateInfo poolCreateInfo = {};
629 poolCreateInfo.memoryTypeIndex = ...
630 poolCreateInfo.blockSize = 128ull * 1024 * 1024;
631 poolCreateInfo.maxBlockCount = 2;
632 
633 VmaPool pool;
634 vmaCreatePool(allocator, &poolCreateInfo, &pool);
635 
636 // Allocate a buffer out of it.
637 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
638 bufCreateInfo.size = 1024;
639 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
640 
641 VmaAllocationCreateInfo allocCreateInfo = {};
642 allocCreateInfo.pool = pool;
643 
644 VkBuffer buf;
645 VmaAllocation alloc;
646 VmaAllocationInfo allocInfo;
647 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
648 \endcode
649 
650 You have to free all allocations made from this pool before destroying it.
651 
652 \code
653 vmaDestroyBuffer(allocator, buf, alloc);
654 vmaDestroyPool(allocator, pool);
655 \endcode
656 
657 \section custom_memory_pools_MemTypeIndex Choosing memory type index
658 
659 When creating a pool, you must explicitly specify memory type index.
660 To find the one suitable for your buffers or images, you can use helper functions
661 vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo().
662 You need to provide structures with example parameters of buffers or images
663 that you are going to create in that pool.
664 
665 \code
666 VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
667 exampleBufCreateInfo.size = 1024; // Whatever.
668 exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; // Change if needed.
669 
670 VmaAllocationCreateInfo allocCreateInfo = {};
671 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; // Change if needed.
672 
673 uint32_t memTypeIndex;
674 vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex);
675 
676 VmaPoolCreateInfo poolCreateInfo = {};
677 poolCreateInfo.memoryTypeIndex = memTypeIndex;
678 // ...
679 \endcode
680 
681 When creating buffers/images allocated in that pool, provide following parameters:
682 
683 - `VkBufferCreateInfo`: Prefer to pass same parameters as above.
684   Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior.
685   Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers
686   or the other way around.
687 - VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member.
688   Other members are ignored anyway.
689 
690 \section linear_algorithm Linear allocation algorithm
691 
692 Each Vulkan memory block managed by this library has accompanying metadata that
693 keeps track of used and unused regions. By default, the metadata structure and
694 algorithm tries to find best place for new allocations among free regions to
695 optimize memory usage. This way you can allocate and free objects in any order.
696 
697 ![Default allocation algorithm](../gfx/Linear_allocator_1_algo_default.png)
698 
699 Sometimes there is a need to use simpler, linear allocation algorithm. You can
700 create custom pool that uses such algorithm by adding flag
701 #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
702 #VmaPool object. Then an alternative metadata management is used. It always
703 creates new allocations after last one and doesn't reuse free regions after
704 allocations freed in the middle. It results in better allocation performance and
705 less memory consumed by metadata.
706 
707 ![Linear allocation algorithm](../gfx/Linear_allocator_2_algo_linear.png)
708 
709 With this one flag, you can create a custom pool that can be used in many ways:
710 free-at-once, stack, double stack, and ring buffer. See below for details.
711 
712 \subsection linear_algorithm_free_at_once Free-at-once
713 
714 In a pool that uses linear algorithm, you still need to free all the allocations
715 individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free
716 them in any order. New allocations are always made after last one - free space
717 in the middle is not reused. However, when you release all the allocation and
718 the pool becomes empty, allocation starts from the beginning again. This way you
719 can use linear algorithm to speed up creation of allocations that you are going
720 to release all at once.
721 
722 ![Free-at-once](../gfx/Linear_allocator_3_free_at_once.png)
723 
724 This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
725 value that allows multiple memory blocks.
726 
727 \subsection linear_algorithm_stack Stack
728 
729 When you free an allocation that was created last, its space can be reused.
730 Thanks to this, if you always release allocations in the order opposite to their
731 creation (LIFO - Last In First Out), you can achieve behavior of a stack.
732 
733 ![Stack](../gfx/Linear_allocator_4_stack.png)
734 
735 This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
736 value that allows multiple memory blocks.
737 
738 \subsection linear_algorithm_double_stack Double stack
739 
740 The space reserved by a custom pool with linear algorithm may be used by two
741 stacks:
742 
743 - First, default one, growing up from offset 0.
744 - Second, "upper" one, growing down from the end towards lower offsets.
745 
746 To make allocation from upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT
747 to VmaAllocationCreateInfo::flags.
748 
749 ![Double stack](../gfx/Linear_allocator_7_double_stack.png)
750 
751 Double stack is available only in pools with one memory block -
752 VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
753 
754 When the two stacks' ends meet so there is not enough space between them for a
755 new allocation, such allocation fails with usual
756 `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
757 
758 \subsection linear_algorithm_ring_buffer Ring buffer
759 
760 When you free some allocations from the beginning and there is not enough free space
761 for a new one at the end of a pool, allocator's "cursor" wraps around to the
762 beginning and starts allocation there. Thanks to this, if you always release
763 allocations in the same order as you created them (FIFO - First In First Out),
764 you can achieve behavior of a ring buffer / queue.
765 
766 ![Ring buffer](../gfx/Linear_allocator_5_ring_buffer.png)
767 
768 Pools with linear algorithm support [lost allocations](@ref lost_allocations) when used as ring buffer.
769 If there is not enough free space for a new allocation, but existing allocations
770 from the front of the queue can become lost, they become lost and the allocation
771 succeeds.
772 
773 ![Ring buffer with lost allocations](../gfx/Linear_allocator_6_ring_buffer_lost.png)
774 
775 Ring buffer is available only in pools with one memory block -
776 VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
777 
778 \section buddy_algorithm Buddy allocation algorithm
779 
780 There is another allocation algorithm that can be used with custom pools, called
781 "buddy". Its internal data structure is based on a tree of blocks, each having
782 size that is a power of two and a half of its parent's size. When you want to
783 allocate memory of certain size, a free node in the tree is located. If it's too
784 large, it is recursively split into two halves (called "buddies"). However, if
785 requested allocation size is not a power of two, the size of a tree node is
786 aligned up to the nearest power of two and the remaining space is wasted. When
787 two buddy nodes become free, they are merged back into one larger node.
788 
789 ![Buddy allocator](../gfx/Buddy_allocator.png)
790 
791 The advantage of buddy allocation algorithm over default algorithm is faster
792 allocation and deallocation, as well as smaller external fragmentation. The
793 disadvantage is more wasted space (internal fragmentation).
794 
795 For more information, please read ["Buddy memory allocation" on Wikipedia](https://en.wikipedia.org/wiki/Buddy_memory_allocation)
796 or other sources that describe this concept in general.
797 
798 To use buddy allocation algorithm with a custom pool, add flag
799 #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
800 #VmaPool object.
801 
802 Several limitations apply to pools that use buddy algorithm:
803 
804 - It is recommended to use VmaPoolCreateInfo::blockSize that is a power of two.
805   Otherwise, only largest power of two smaller than the size is used for
806   allocations. The remaining space always stays unused.
807 - [Margins](@ref debugging_memory_usage_margins) and
808   [corruption detection](@ref debugging_memory_usage_corruption_detection)
809   don't work in such pools.
810 - [Lost allocations](@ref lost_allocations) don't work in such pools. You can
811   use them, but they never become lost. Support may be added in the future.
812 - [Defragmentation](@ref defragmentation) doesn't work with allocations made from
813   such pool.
814 
815 \page defragmentation Defragmentation
816 
817 Interleaved allocations and deallocations of many objects of varying size can
818 cause fragmentation over time, which can lead to a situation where the library is unable
819 to find a continuous range of free memory for a new allocation despite there is
820 enough free space, just scattered across many small free ranges between existing
821 allocations.
822 
823 To mitigate this problem, you can use defragmentation feature:
824 structure #VmaDefragmentationInfo2, function vmaDefragmentationBegin(), vmaDefragmentationEnd().
825 Given set of allocations,
826 this function can move them to compact used memory, ensure more continuous free
827 space and possibly also free some `VkDeviceMemory` blocks.
828 
829 What the defragmentation does is:
830 
831 - Updates #VmaAllocation objects to point to new `VkDeviceMemory` and offset.
832   After allocation has been moved, its VmaAllocationInfo::deviceMemory and/or
833   VmaAllocationInfo::offset changes. You must query them again using
834   vmaGetAllocationInfo() if you need them.
835 - Moves actual data in memory.
836 
837 What it doesn't do, so you need to do it yourself:
838 
839 - Recreate buffers and images that were bound to allocations that were defragmented and
840   bind them with their new places in memory.
841   You must use `vkDestroyBuffer()`, `vkDestroyImage()`,
842   `vkCreateBuffer()`, `vkCreateImage()`, vmaBindBufferMemory(), vmaBindImageMemory()
843   for that purpose and NOT vmaDestroyBuffer(),
844   vmaDestroyImage(), vmaCreateBuffer(), vmaCreateImage(), because you don't need to
845   destroy or create allocation objects!
846 - Recreate views and update descriptors that point to these buffers and images.
847 
848 \section defragmentation_cpu Defragmenting CPU memory
849 
850 Following example demonstrates how you can run defragmentation on CPU.
851 Only allocations created in memory types that are `HOST_VISIBLE` can be defragmented.
852 Others are ignored.
853 
854 The way it works is:
855 
856 - It temporarily maps entire memory blocks when necessary.
857 - It moves data using `memmove()` function.
858 
859 \code
860 // Given following variables already initialized:
861 VkDevice device;
862 VmaAllocator allocator;
863 std::vector<VkBuffer> buffers;
864 std::vector<VmaAllocation> allocations;
865 
866 
867 const uint32_t allocCount = (uint32_t)allocations.size();
868 std::vector<VkBool32> allocationsChanged(allocCount);
869 
870 VmaDefragmentationInfo2 defragInfo = {};
871 defragInfo.allocationCount = allocCount;
872 defragInfo.pAllocations = allocations.data();
873 defragInfo.pAllocationsChanged = allocationsChanged.data();
874 defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE; // No limit.
875 defragInfo.maxCpuAllocationsToMove = UINT32_MAX; // No limit.
876 
877 VmaDefragmentationContext defragCtx;
878 vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx);
879 vmaDefragmentationEnd(allocator, defragCtx);
880 
881 for(uint32_t i = 0; i < allocCount; ++i)
882 {
883     if(allocationsChanged[i])
884     {
885         // Destroy buffer that is immutably bound to memory region which is no longer valid.
886         vkDestroyBuffer(device, buffers[i], nullptr);
887 
888         // Create new buffer with same parameters.
889         VkBufferCreateInfo bufferInfo = ...;
890         vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
891 
892         // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
893 
894         // Bind new buffer to new memory region. Data contained in it is already moved.
895         VmaAllocationInfo allocInfo;
896         vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
897         vmaBindBufferMemory(allocator, allocations[i], buffers[i]);
898     }
899 }
900 \endcode
901 
902 Setting VmaDefragmentationInfo2::pAllocationsChanged is optional.
903 This output array tells whether particular allocation in VmaDefragmentationInfo2::pAllocations at the same index
904 has been modified during defragmentation.
905 You can pass null, but you then need to query every allocation passed to defragmentation
906 for new parameters using vmaGetAllocationInfo() if you might need to recreate and rebind a buffer or image associated with it.
907 
908 If you use [Custom memory pools](@ref choosing_memory_type_custom_memory_pools),
909 you can fill VmaDefragmentationInfo2::poolCount and VmaDefragmentationInfo2::pPools
910 instead of VmaDefragmentationInfo2::allocationCount and VmaDefragmentationInfo2::pAllocations
911 to defragment all allocations in given pools.
912 You cannot use VmaDefragmentationInfo2::pAllocationsChanged in that case.
913 You can also combine both methods.
914 
915 \section defragmentation_gpu Defragmenting GPU memory
916 
917 It is also possible to defragment allocations created in memory types that are not `HOST_VISIBLE`.
918 To do that, you need to pass a command buffer that meets requirements as described in
919 VmaDefragmentationInfo2::commandBuffer. The way it works is:
920 
921 - It creates temporary buffers and binds them to entire memory blocks when necessary.
922 - It issues `vkCmdCopyBuffer()` to passed command buffer.
923 
924 Example:
925 
926 \code
927 // Given following variables already initialized:
928 VkDevice device;
929 VmaAllocator allocator;
930 VkCommandBuffer commandBuffer;
931 std::vector<VkBuffer> buffers;
932 std::vector<VmaAllocation> allocations;
933 
934 
935 const uint32_t allocCount = (uint32_t)allocations.size();
936 std::vector<VkBool32> allocationsChanged(allocCount);
937 
938 VkCommandBufferBeginInfo cmdBufBeginInfo = ...;
939 vkBeginCommandBuffer(commandBuffer, &cmdBufBeginInfo);
940 
941 VmaDefragmentationInfo2 defragInfo = {};
942 defragInfo.allocationCount = allocCount;
943 defragInfo.pAllocations = allocations.data();
944 defragInfo.pAllocationsChanged = allocationsChanged.data();
945 defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE; // Notice it's "GPU" this time.
946 defragInfo.maxGpuAllocationsToMove = UINT32_MAX; // Notice it's "GPU" this time.
947 defragInfo.commandBuffer = commandBuffer;
948 
949 VmaDefragmentationContext defragCtx;
950 vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx);
951 
952 vkEndCommandBuffer(commandBuffer);
953 
954 // Submit commandBuffer.
955 // Wait for a fence that ensures commandBuffer execution finished.
956 
957 vmaDefragmentationEnd(allocator, defragCtx);
958 
959 for(uint32_t i = 0; i < allocCount; ++i)
960 {
961     if(allocationsChanged[i])
962     {
963         // Destroy buffer that is immutably bound to memory region which is no longer valid.
964         vkDestroyBuffer(device, buffers[i], nullptr);
965 
966         // Create new buffer with same parameters.
967         VkBufferCreateInfo bufferInfo = ...;
968         vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
969 
970         // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
971 
972         // Bind new buffer to new memory region. Data contained in it is already moved.
973         VmaAllocationInfo allocInfo;
974         vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
975         vmaBindBufferMemory(allocator, allocations[i], buffers[i]);
976     }
977 }
978 \endcode
979 
980 You can combine these two methods by specifying non-zero `maxGpu*` as well as `maxCpu*` parameters.
981 The library automatically chooses best method to defragment each memory pool.
982 
983 You may try not to block your entire program to wait until defragmentation finishes,
984 but do it in the background, as long as you carefully fullfill requirements described
985 in function vmaDefragmentationBegin().
986 
987 \section defragmentation_additional_notes Additional notes
988 
989 It is only legal to defragment allocations bound to:
990 
991 - buffers
992 - images created with `VK_IMAGE_CREATE_ALIAS_BIT`, `VK_IMAGE_TILING_LINEAR`, and
993   being currently in `VK_IMAGE_LAYOUT_GENERAL` or `VK_IMAGE_LAYOUT_PREINITIALIZED`.
994 
995 Defragmentation of images created with `VK_IMAGE_TILING_OPTIMAL` or in any other
996 layout may give undefined results.
997 
998 If you defragment allocations bound to images, new images to be bound to new
999 memory region after defragmentation should be created with `VK_IMAGE_LAYOUT_PREINITIALIZED`
1000 and then transitioned to their original layout from before defragmentation if
1001 needed using an image memory barrier.
1002 
1003 While using defragmentation, you may experience validation layer warnings, which you just need to ignore.
1004 See [Validation layer warnings](@ref general_considerations_validation_layer_warnings).
1005 
1006 Please don't expect memory to be fully compacted after defragmentation.
1007 Algorithms inside are based on some heuristics that try to maximize number of Vulkan
1008 memory blocks to make totally empty to release them, as well as to maximimze continuous
1009 empty space inside remaining blocks, while minimizing the number and size of allocations that
1010 need to be moved. Some fragmentation may still remain - this is normal.
1011 
1012 \section defragmentation_custom_algorithm Writing custom defragmentation algorithm
1013 
1014 If you want to implement your own, custom defragmentation algorithm,
1015 there is infrastructure prepared for that,
1016 but it is not exposed through the library API - you need to hack its source code.
1017 Here are steps needed to do this:
1018 
1019 -# Main thing you need to do is to define your own class derived from base abstract
1020    class `VmaDefragmentationAlgorithm` and implement your version of its pure virtual methods.
1021    See definition and comments of this class for details.
1022 -# Your code needs to interact with device memory block metadata.
1023    If you need more access to its data than it's provided by its public interface,
1024    declare your new class as a friend class e.g. in class `VmaBlockMetadata_Generic`.
1025 -# If you want to create a flag that would enable your algorithm or pass some additional
1026    flags to configure it, add them to `VmaDefragmentationFlagBits` and use them in
1027    VmaDefragmentationInfo2::flags.
1028 -# Modify function `VmaBlockVectorDefragmentationContext::Begin` to create object
1029    of your new class whenever needed.
1030 
1031 
1032 \page lost_allocations Lost allocations
1033 
1034 If your game oversubscribes video memory, if may work OK in previous-generation
1035 graphics APIs (DirectX 9, 10, 11, OpenGL) because resources are automatically
1036 paged to system RAM. In Vulkan you can't do it because when you run out of
1037 memory, an allocation just fails. If you have more data (e.g. textures) that can
1038 fit into VRAM and you don't need it all at once, you may want to upload them to
1039 GPU on demand and "push out" ones that are not used for a long time to make room
1040 for the new ones, effectively using VRAM (or a cartain memory pool) as a form of
1041 cache. Vulkan Memory Allocator can help you with that by supporting a concept of
1042 "lost allocations".
1043 
1044 To create an allocation that can become lost, include #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT
1045 flag in VmaAllocationCreateInfo::flags. Before using a buffer or image bound to
1046 such allocation in every new frame, you need to query it if it's not lost.
1047 To check it, call vmaTouchAllocation().
1048 If the allocation is lost, you should not use it or buffer/image bound to it.
1049 You mustn't forget to destroy this allocation and this buffer/image.
1050 vmaGetAllocationInfo() can also be used for checking status of the allocation.
1051 Allocation is lost when returned VmaAllocationInfo::deviceMemory == `VK_NULL_HANDLE`.
1052 
1053 To create an allocation that can make some other allocations lost to make room
1054 for it, use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag. You will
1055 usually use both flags #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT and
1056 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT at the same time.
1057 
1058 Warning! Current implementation uses quite naive, brute force algorithm,
1059 which can make allocation calls that use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT
1060 flag quite slow. A new, more optimal algorithm and data structure to speed this
1061 up is planned for the future.
1062 
1063 <b>Q: When interleaving creation of new allocations with usage of existing ones,
1064 how do you make sure that an allocation won't become lost while it's used in the
1065 current frame?</b>
1066 
1067 It is ensured because vmaTouchAllocation() / vmaGetAllocationInfo() not only returns allocation
1068 status/parameters and checks whether it's not lost, but when it's not, it also
1069 atomically marks it as used in the current frame, which makes it impossible to
1070 become lost in that frame. It uses lockless algorithm, so it works fast and
1071 doesn't involve locking any internal mutex.
1072 
1073 <b>Q: What if my allocation may still be in use by the GPU when it's rendering a
1074 previous frame while I already submit new frame on the CPU?</b>
1075 
1076 You can make sure that allocations "touched" by vmaTouchAllocation() / vmaGetAllocationInfo() will not
1077 become lost for a number of additional frames back from the current one by
1078 specifying this number as VmaAllocatorCreateInfo::frameInUseCount (for default
1079 memory pool) and VmaPoolCreateInfo::frameInUseCount (for custom pool).
1080 
1081 <b>Q: How do you inform the library when new frame starts?</b>
1082 
1083 You need to call function vmaSetCurrentFrameIndex().
1084 
1085 Example code:
1086 
1087 \code
1088 struct MyBuffer
1089 {
1090     VkBuffer m_Buf = nullptr;
1091     VmaAllocation m_Alloc = nullptr;
1092 
1093     // Called when the buffer is really needed in the current frame.
1094     void EnsureBuffer();
1095 };
1096 
1097 void MyBuffer::EnsureBuffer()
1098 {
1099     // Buffer has been created.
1100     if(m_Buf != VK_NULL_HANDLE)
1101     {
1102         // Check if its allocation is not lost + mark it as used in current frame.
1103         if(vmaTouchAllocation(allocator, m_Alloc))
1104         {
1105             // It's all OK - safe to use m_Buf.
1106             return;
1107         }
1108     }
1109 
1110     // Buffer not yet exists or lost - destroy and recreate it.
1111 
1112     vmaDestroyBuffer(allocator, m_Buf, m_Alloc);
1113 
1114     VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1115     bufCreateInfo.size = 1024;
1116     bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
1117 
1118     VmaAllocationCreateInfo allocCreateInfo = {};
1119     allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1120     allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
1121         VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
1122 
1123     vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &m_Buf, &m_Alloc, nullptr);
1124 }
1125 \endcode
1126 
1127 When using lost allocations, you may see some Vulkan validation layer warnings
1128 about overlapping regions of memory bound to different kinds of buffers and
1129 images. This is still valid as long as you implement proper handling of lost
1130 allocations (like in the example above) and don't use them.
1131 
1132 You can create an allocation that is already in lost state from the beginning using function
1133 vmaCreateLostAllocation(). It may be useful if you need a "dummy" allocation that is not null.
1134 
1135 You can call function vmaMakePoolAllocationsLost() to set all eligible allocations
1136 in a specified custom pool to lost state.
1137 Allocations that have been "touched" in current frame or VmaPoolCreateInfo::frameInUseCount frames back
1138 cannot become lost.
1139 
1140 <b>Q: Can I touch allocation that cannot become lost?</b>
1141 
1142 Yes, although it has no visible effect.
1143 Calls to vmaGetAllocationInfo() and vmaTouchAllocation() update last use frame index
1144 also for allocations that cannot become lost, but the only way to observe it is to dump
1145 internal allocator state using vmaBuildStatsString().
1146 You can use this feature for debugging purposes to explicitly mark allocations that you use
1147 in current frame and then analyze JSON dump to see for how long each allocation stays unused.
1148 
1149 
1150 \page statistics Statistics
1151 
1152 This library contains functions that return information about its internal state,
1153 especially the amount of memory allocated from Vulkan.
1154 Please keep in mind that these functions need to traverse all internal data structures
1155 to gather these information, so they may be quite time-consuming.
1156 Don't call them too often.
1157 
1158 \section statistics_numeric_statistics Numeric statistics
1159 
1160 You can query for overall statistics of the allocator using function vmaCalculateStats().
1161 Information are returned using structure #VmaStats.
1162 It contains #VmaStatInfo - number of allocated blocks, number of allocations
1163 (occupied ranges in these blocks), number of unused (free) ranges in these blocks,
1164 number of bytes used and unused (but still allocated from Vulkan) and other information.
1165 They are summed across memory heaps, memory types and total for whole allocator.
1166 
1167 You can query for statistics of a custom pool using function vmaGetPoolStats().
1168 Information are returned using structure #VmaPoolStats.
1169 
1170 You can query for information about specific allocation using function vmaGetAllocationInfo().
1171 It fill structure #VmaAllocationInfo.
1172 
1173 \section statistics_json_dump JSON dump
1174 
1175 You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString().
1176 The result is guaranteed to be correct JSON.
1177 It uses ANSI encoding.
1178 Any strings provided by user (see [Allocation names](@ref allocation_names))
1179 are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding,
1180 this JSON string can be treated as using this encoding.
1181 It must be freed using function vmaFreeStatsString().
1182 
1183 The format of this JSON string is not part of official documentation of the library,
1184 but it will not change in backward-incompatible way without increasing library major version number
1185 and appropriate mention in changelog.
1186 
1187 The JSON string contains all the data that can be obtained using vmaCalculateStats().
1188 It can also contain detailed map of allocated memory blocks and their regions -
1189 free and occupied by allocations.
1190 This allows e.g. to visualize the memory or assess fragmentation.
1191 
1192 
1193 \page allocation_annotation Allocation names and user data
1194 
1195 \section allocation_user_data Allocation user data
1196 
1197 You can annotate allocations with your own information, e.g. for debugging purposes.
1198 To do that, fill VmaAllocationCreateInfo::pUserData field when creating
1199 an allocation. It's an opaque `void*` pointer. You can use it e.g. as a pointer,
1200 some handle, index, key, ordinal number or any other value that would associate
1201 the allocation with your custom metadata.
1202 
1203 \code
1204 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1205 // Fill bufferInfo...
1206 
1207 MyBufferMetadata* pMetadata = CreateBufferMetadata();
1208 
1209 VmaAllocationCreateInfo allocCreateInfo = {};
1210 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1211 allocCreateInfo.pUserData = pMetadata;
1212 
1213 VkBuffer buffer;
1214 VmaAllocation allocation;
1215 vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, nullptr);
1216 \endcode
1217 
1218 The pointer may be later retrieved as VmaAllocationInfo::pUserData:
1219 
1220 \code
1221 VmaAllocationInfo allocInfo;
1222 vmaGetAllocationInfo(allocator, allocation, &allocInfo);
1223 MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData;
1224 \endcode
1225 
1226 It can also be changed using function vmaSetAllocationUserData().
1227 
1228 Values of (non-zero) allocations' `pUserData` are printed in JSON report created by
1229 vmaBuildStatsString(), in hexadecimal form.
1230 
1231 \section allocation_names Allocation names
1232 
1233 There is alternative mode available where `pUserData` pointer is used to point to
1234 a null-terminated string, giving a name to the allocation. To use this mode,
1235 set #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT flag in VmaAllocationCreateInfo::flags.
1236 Then `pUserData` passed as VmaAllocationCreateInfo::pUserData or argument to
1237 vmaSetAllocationUserData() must be either null or pointer to a null-terminated string.
1238 The library creates internal copy of the string, so the pointer you pass doesn't need
1239 to be valid for whole lifetime of the allocation. You can free it after the call.
1240 
1241 \code
1242 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
1243 // Fill imageInfo...
1244 
1245 std::string imageName = "Texture: ";
1246 imageName += fileName;
1247 
1248 VmaAllocationCreateInfo allocCreateInfo = {};
1249 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1250 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
1251 allocCreateInfo.pUserData = imageName.c_str();
1252 
1253 VkImage image;
1254 VmaAllocation allocation;
1255 vmaCreateImage(allocator, &imageInfo, &allocCreateInfo, &image, &allocation, nullptr);
1256 \endcode
1257 
1258 The value of `pUserData` pointer of the allocation will be different than the one
1259 you passed when setting allocation's name - pointing to a buffer managed
1260 internally that holds copy of the string.
1261 
1262 \code
1263 VmaAllocationInfo allocInfo;
1264 vmaGetAllocationInfo(allocator, allocation, &allocInfo);
1265 const char* imageName = (const char*)allocInfo.pUserData;
1266 printf("Image name: %s\n", imageName);
1267 \endcode
1268 
1269 That string is also printed in JSON report created by vmaBuildStatsString().
1270 
1271 \note Passing string name to VMA allocation doesn't automatically set it to the Vulkan buffer or image created with it.
1272 You must do it manually using an extension like VK_EXT_debug_utils, which is independent of this library.
1273 
1274 
1275 \page debugging_memory_usage Debugging incorrect memory usage
1276 
1277 If you suspect a bug with memory usage, like usage of uninitialized memory or
1278 memory being overwritten out of bounds of an allocation,
1279 you can use debug features of this library to verify this.
1280 
1281 \section debugging_memory_usage_initialization Memory initialization
1282 
1283 If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used,
1284 you can enable automatic memory initialization to verify this.
1285 To do it, define macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to 1.
1286 
1287 \code
1288 #define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1
1289 #include "vk_mem_alloc.h"
1290 \endcode
1291 
1292 It makes memory of all new allocations initialized to bit pattern `0xDCDCDCDC`.
1293 Before an allocation is destroyed, its memory is filled with bit pattern `0xEFEFEFEF`.
1294 Memory is automatically mapped and unmapped if necessary.
1295 
1296 If you find these values while debugging your program, good chances are that you incorrectly
1297 read Vulkan memory that is allocated but not initialized, or already freed, respectively.
1298 
1299 Memory initialization works only with memory types that are `HOST_VISIBLE`.
1300 It works also with dedicated allocations.
1301 It doesn't work with allocations created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
1302 as they cannot be mapped.
1303 
1304 \section debugging_memory_usage_margins Margins
1305 
1306 By default, allocations are laid out in memory blocks next to each other if possible
1307 (considering required alignment, `bufferImageGranularity`, and `nonCoherentAtomSize`).
1308 
1309 ![Allocations without margin](../gfx/Margins_1.png)
1310 
1311 Define macro `VMA_DEBUG_MARGIN` to some non-zero value (e.g. 16) to enforce specified
1312 number of bytes as a margin before and after every allocation.
1313 
1314 \code
1315 #define VMA_DEBUG_MARGIN 16
1316 #include "vk_mem_alloc.h"
1317 \endcode
1318 
1319 ![Allocations with margin](../gfx/Margins_2.png)
1320 
1321 If your bug goes away after enabling margins, it means it may be caused by memory
1322 being overwritten outside of allocation boundaries. It is not 100% certain though.
1323 Change in application behavior may also be caused by different order and distribution
1324 of allocations across memory blocks after margins are applied.
1325 
1326 The margin is applied also before first and after last allocation in a block.
1327 It may occur only once between two adjacent allocations.
1328 
1329 Margins work with all types of memory.
1330 
1331 Margin is applied only to allocations made out of memory blocks and not to dedicated
1332 allocations, which have their own memory block of specific size.
1333 It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag
1334 or those automatically decided to put into dedicated allocations, e.g. due to its
1335 large size or recommended by VK_KHR_dedicated_allocation extension.
1336 Margins are also not active in custom pools created with #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag.
1337 
1338 Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space.
1339 
1340 Note that enabling margins increases memory usage and fragmentation.
1341 
1342 \section debugging_memory_usage_corruption_detection Corruption detection
1343 
1344 You can additionally define macro `VMA_DEBUG_DETECT_CORRUPTION` to 1 to enable validation
1345 of contents of the margins.
1346 
1347 \code
1348 #define VMA_DEBUG_MARGIN 16
1349 #define VMA_DEBUG_DETECT_CORRUPTION 1
1350 #include "vk_mem_alloc.h"
1351 \endcode
1352 
1353 When this feature is enabled, number of bytes specified as `VMA_DEBUG_MARGIN`
1354 (it must be multiply of 4) before and after every allocation is filled with a magic number.
1355 This idea is also know as "canary".
1356 Memory is automatically mapped and unmapped if necessary.
1357 
1358 This number is validated automatically when the allocation is destroyed.
1359 If it's not equal to the expected value, `VMA_ASSERT()` is executed.
1360 It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation,
1361 which indicates a serious bug.
1362 
1363 You can also explicitly request checking margins of all allocations in all memory blocks
1364 that belong to specified memory types by using function vmaCheckCorruption(),
1365 or in memory blocks that belong to specified custom pool, by using function
1366 vmaCheckPoolCorruption().
1367 
1368 Margin validation (corruption detection) works only for memory types that are
1369 `HOST_VISIBLE` and `HOST_COHERENT`.
1370 
1371 
1372 \page record_and_replay Record and replay
1373 
1374 \section record_and_replay_introduction Introduction
1375 
1376 While using the library, sequence of calls to its functions together with their
1377 parameters can be recorded to a file and later replayed using standalone player
1378 application. It can be useful to:
1379 
1380 - Test correctness - check if same sequence of calls will not cause crash or
1381   failures on a target platform.
1382 - Gather statistics - see number of allocations, peak memory usage, number of
1383   calls etc.
1384 - Benchmark performance - see how much time it takes to replay the whole
1385   sequence.
1386 
1387 \section record_and_replay_usage Usage
1388 
1389 Recording functionality is disabled by default.
1390 To enable it, define following macro before every include of this library:
1391 
1392 \code
1393 #define VMA_RECORDING_ENABLED 1
1394 \endcode
1395 
1396 <b>To record sequence of calls to a file:</b> Fill in
1397 VmaAllocatorCreateInfo::pRecordSettings member while creating #VmaAllocator
1398 object. File is opened and written during whole lifetime of the allocator.
1399 
1400 <b>To replay file:</b> Use VmaReplay - standalone command-line program.
1401 Precompiled binary can be found in "bin" directory.
1402 Its source can be found in "src/VmaReplay" directory.
1403 Its project is generated by Premake.
1404 Command line syntax is printed when the program is launched without parameters.
1405 Basic usage:
1406 
1407     VmaReplay.exe MyRecording.csv
1408 
1409 <b>Documentation of file format</b> can be found in file: "docs/Recording file format.md".
1410 It's a human-readable, text file in CSV format (Comma Separated Values).
1411 
1412 \section record_and_replay_additional_considerations Additional considerations
1413 
1414 - Replaying file that was recorded on a different GPU (with different parameters
1415   like `bufferImageGranularity`, `nonCoherentAtomSize`, and especially different
1416   set of memory heaps and types) may give different performance and memory usage
1417   results, as well as issue some warnings and errors.
1418 - Current implementation of recording in VMA, as well as VmaReplay application, is
1419   coded and tested only on Windows. Inclusion of recording code is driven by
1420   `VMA_RECORDING_ENABLED` macro. Support for other platforms should be easy to
1421   add. Contributions are welcomed.
1422 
1423 
1424 \page usage_patterns Recommended usage patterns
1425 
1426 See also slides from talk:
1427 [Sawicki, Adam. Advanced Graphics Techniques Tutorial: Memory management in Vulkan and DX12. Game Developers Conference, 2018](https://www.gdcvault.com/play/1025458/Advanced-Graphics-Techniques-Tutorial-New)
1428 
1429 
1430 \section usage_patterns_common_mistakes Common mistakes
1431 
1432 <b>Use of CPU_TO_GPU instead of CPU_ONLY memory</b>
1433 
1434 #VMA_MEMORY_USAGE_CPU_TO_GPU is recommended only for resources that will be
1435 mapped and written by the CPU, as well as read directly by the GPU - like some
1436 buffers or textures updated every frame (dynamic). If you create a staging copy
1437 of a resource to be written by CPU and then used as a source of transfer to
1438 another resource placed in the GPU memory, that staging resource should be
1439 created with #VMA_MEMORY_USAGE_CPU_ONLY. Please read the descriptions of these
1440 enums carefully for details.
1441 
1442 <b>Unnecessary use of custom pools</b>
1443 
1444 \ref custom_memory_pools may be useful for special purposes - when you want to
1445 keep certain type of resources separate e.g. to reserve minimum amount of memory
1446 for them, limit maximum amount of memory they can occupy, or make some of them
1447 push out the other through the mechanism of \ref lost_allocations. For most
1448 resources this is not needed and so it is not recommended to create #VmaPool
1449 objects and allocations out of them. Allocating from the default pool is sufficient.
1450 
1451 \section usage_patterns_simple Simple patterns
1452 
1453 \subsection usage_patterns_simple_render_targets Render targets
1454 
1455 <b>When:</b>
1456 Any resources that you frequently write and read on GPU,
1457 e.g. images used as color attachments (aka "render targets"), depth-stencil attachments,
1458 images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)").
1459 
1460 <b>What to do:</b>
1461 Create them in video memory that is fastest to access from GPU using
1462 #VMA_MEMORY_USAGE_GPU_ONLY.
1463 
1464 Consider using [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension
1465 and/or manually creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
1466 especially if they are large or if you plan to destroy and recreate them e.g. when
1467 display resolution changes.
1468 Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later.
1469 
1470 \subsection usage_patterns_simple_immutable_resources Immutable resources
1471 
1472 <b>When:</b>
1473 Any resources that you fill on CPU only once (aka "immutable") or infrequently
1474 and then read frequently on GPU,
1475 e.g. textures, vertex and index buffers, constant buffers that don't change often.
1476 
1477 <b>What to do:</b>
1478 Create them in video memory that is fastest to access from GPU using
1479 #VMA_MEMORY_USAGE_GPU_ONLY.
1480 
1481 To initialize content of such resource, create a CPU-side (aka "staging") copy of it
1482 in system memory - #VMA_MEMORY_USAGE_CPU_ONLY, map it, fill it,
1483 and submit a transfer from it to the GPU resource.
1484 You can keep the staging copy if you need it for another upload transfer in the future.
1485 If you don't, you can destroy it or reuse this buffer for uploading different resource
1486 after the transfer finishes.
1487 
1488 Prefer to create just buffers in system memory rather than images, even for uploading textures.
1489 Use `vkCmdCopyBufferToImage()`.
1490 Dont use images with `VK_IMAGE_TILING_LINEAR`.
1491 
1492 \subsection usage_patterns_dynamic_resources Dynamic resources
1493 
1494 <b>When:</b>
1495 Any resources that change frequently (aka "dynamic"), e.g. every frame or every draw call,
1496 written on CPU, read on GPU.
1497 
1498 <b>What to do:</b>
1499 Create them using #VMA_MEMORY_USAGE_CPU_TO_GPU.
1500 You can map it and write to it directly on CPU, as well as read from it on GPU.
1501 
1502 This is a more complex situation. Different solutions are possible,
1503 and the best one depends on specific GPU type, but you can use this simple approach for the start.
1504 Prefer to write to such resource sequentially (e.g. using `memcpy`).
1505 Don't perform random access or any reads from it on CPU, as it may be very slow.
1506 Also note that textures written directly from the host through a mapped pointer need to be in LINEAR not OPTIMAL layout.
1507 
1508 \subsection usage_patterns_readback Readback
1509 
1510 <b>When:</b>
1511 Resources that contain data written by GPU that you want to read back on CPU,
1512 e.g. results of some computations.
1513 
1514 <b>What to do:</b>
1515 Create them using #VMA_MEMORY_USAGE_GPU_TO_CPU.
1516 You can write to them directly on GPU, as well as map and read them on CPU.
1517 
1518 \section usage_patterns_advanced Advanced patterns
1519 
1520 \subsection usage_patterns_integrated_graphics Detecting integrated graphics
1521 
1522 You can support integrated graphics (like Intel HD Graphics, AMD APU) better
1523 by detecting it in Vulkan.
1524 To do it, call `vkGetPhysicalDeviceProperties()`, inspect
1525 `VkPhysicalDeviceProperties::deviceType` and look for `VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU`.
1526 When you find it, you can assume that memory is unified and all memory types are comparably fast
1527 to access from GPU, regardless of `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
1528 
1529 You can then sum up sizes of all available memory heaps and treat them as useful for
1530 your GPU resources, instead of only `DEVICE_LOCAL` ones.
1531 You can also prefer to create your resources in memory types that are `HOST_VISIBLE` to map them
1532 directly instead of submitting explicit transfer (see below).
1533 
1534 \subsection usage_patterns_direct_vs_transfer Direct access versus transfer
1535 
1536 For resources that you frequently write on CPU and read on GPU, many solutions are possible:
1537 
1538 -# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
1539    second copy in system memory using #VMA_MEMORY_USAGE_CPU_ONLY and submit explicit transfer each time.
1540 -# Create just a single copy using #VMA_MEMORY_USAGE_CPU_TO_GPU, map it and fill it on CPU,
1541    read it directly on GPU.
1542 -# Create just a single copy using #VMA_MEMORY_USAGE_CPU_ONLY, map it and fill it on CPU,
1543    read it directly on GPU.
1544 
1545 Which solution is the most efficient depends on your resource and especially on the GPU.
1546 It is best to measure it and then make the decision.
1547 Some general recommendations:
1548 
1549 - On integrated graphics use (2) or (3) to avoid unnecesary time and memory overhead
1550   related to using a second copy and making transfer.
1551 - For small resources (e.g. constant buffers) use (2).
1552   Discrete AMD cards have special 256 MiB pool of video memory that is directly mappable.
1553   Even if the resource ends up in system memory, its data may be cached on GPU after first
1554   fetch over PCIe bus.
1555 - For larger resources (e.g. textures), decide between (1) and (2).
1556   You may want to differentiate NVIDIA and AMD, e.g. by looking for memory type that is
1557   both `DEVICE_LOCAL` and `HOST_VISIBLE`. When you find it, use (2), otherwise use (1).
1558 
1559 Similarly, for resources that you frequently write on GPU and read on CPU, multiple
1560 solutions are possible:
1561 
1562 -# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
1563    second copy in system memory using #VMA_MEMORY_USAGE_GPU_TO_CPU and submit explicit tranfer each time.
1564 -# Create just single copy using #VMA_MEMORY_USAGE_GPU_TO_CPU, write to it directly on GPU,
1565    map it and read it on CPU.
1566 
1567 You should take some measurements to decide which option is faster in case of your specific
1568 resource.
1569 
1570 Note that textures accessed directly from the host through a mapped pointer need to be in LINEAR layout,
1571 which may slow down their usage on the device.
1572 Textures accessed only by the device and transfer operations can use OPTIMAL layout.
1573 
1574 If you don't want to specialize your code for specific types of GPUs, you can still make
1575 an simple optimization for cases when your resource ends up in mappable memory to use it
1576 directly in this case instead of creating CPU-side staging copy.
1577 For details see [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable).
1578 
1579 
1580 \page configuration Configuration
1581 
1582 Please check "CONFIGURATION SECTION" in the code to find macros that you can define
1583 before each include of this file or change directly in this file to provide
1584 your own implementation of basic facilities like assert, `min()` and `max()` functions,
1585 mutex, atomic etc.
1586 The library uses its own implementation of containers by default, but you can switch to using
1587 STL containers instead.
1588 
1589 For example, define `VMA_ASSERT(expr)` before including the library to provide
1590 custom implementation of the assertion, compatible with your project.
1591 By default it is defined to standard C `assert(expr)` in `_DEBUG` configuration
1592 and empty otherwise.
1593 
1594 \section config_Vulkan_functions Pointers to Vulkan functions
1595 
1596 There are multiple ways to import pointers to Vulkan functions in the library.
1597 In the simplest case you don't need to do anything.
1598 If the compilation or linking of your program or the initialization of the #VmaAllocator
1599 doesn't work for you, you can try to reconfigure it.
1600 
1601 First, the allocator tries to fetch pointers to Vulkan functions linked statically,
1602 like this:
1603 
1604 \code
1605 m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
1606 \endcode
1607 
1608 If you want to disable this feature, set configuration macro: `#define VMA_STATIC_VULKAN_FUNCTIONS 0`.
1609 
1610 Second, you can provide the pointers yourself by setting member VmaAllocatorCreateInfo::pVulkanFunctions.
1611 You can fetch them e.g. using functions `vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` or
1612 by using a helper library like [volk](https://github.com/zeux/volk).
1613 
1614 Third, VMA tries to fetch remaining pointers that are still null by calling
1615 `vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` on its own.
1616 If you want to disable this feature, set configuration macro: `#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0`.
1617 
1618 Finally, all the function pointers required by the library (considering selected
1619 Vulkan version and enabled extensions) are checked with `VMA_ASSERT` if they are not null.
1620 
1621 
1622 \section custom_memory_allocator Custom host memory allocator
1623 
1624 If you use custom allocator for CPU memory rather than default operator `new`
1625 and `delete` from C++, you can make this library using your allocator as well
1626 by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These
1627 functions will be passed to Vulkan, as well as used by the library itself to
1628 make any CPU-side allocations.
1629 
1630 \section allocation_callbacks Device memory allocation callbacks
1631 
1632 The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally.
1633 You can setup callbacks to be informed about these calls, e.g. for the purpose
1634 of gathering some statistics. To do it, fill optional member
1635 VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
1636 
1637 \section heap_memory_limit Device heap memory limit
1638 
1639 When device memory of certain heap runs out of free space, new allocations may
1640 fail (returning error code) or they may succeed, silently pushing some existing
1641 memory blocks from GPU VRAM to system RAM (which degrades performance). This
1642 behavior is implementation-dependant - it depends on GPU vendor and graphics
1643 driver.
1644 
1645 On AMD cards it can be controlled while creating Vulkan device object by using
1646 VK_AMD_memory_overallocation_behavior extension, if available.
1647 
1648 Alternatively, if you want to test how your program behaves with limited amount of Vulkan device
1649 memory available without switching your graphics card to one that really has
1650 smaller VRAM, you can use a feature of this library intended for this purpose.
1651 To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit.
1652 
1653 
1654 
1655 \page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation
1656 
1657 VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve
1658 performance on some GPUs. It augments Vulkan API with possibility to query
1659 driver whether it prefers particular buffer or image to have its own, dedicated
1660 allocation (separate `VkDeviceMemory` block) for better efficiency - to be able
1661 to do some internal optimizations.
1662 
1663 The extension is supported by this library. It will be used automatically when
1664 enabled. To enable it:
1665 
1666 1 . When creating Vulkan device, check if following 2 device extensions are
1667 supported (call `vkEnumerateDeviceExtensionProperties()`).
1668 If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`).
1669 
1670 - VK_KHR_get_memory_requirements2
1671 - VK_KHR_dedicated_allocation
1672 
1673 If you enabled these extensions:
1674 
1675 2 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating
1676 your #VmaAllocator`to inform the library that you enabled required extensions
1677 and you want the library to use them.
1678 
1679 \code
1680 allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
1681 
1682 vmaCreateAllocator(&allocatorInfo, &allocator);
1683 \endcode
1684 
1685 That's all. The extension will be automatically used whenever you create a
1686 buffer using vmaCreateBuffer() or image using vmaCreateImage().
1687 
1688 When using the extension together with Vulkan Validation Layer, you will receive
1689 warnings like this:
1690 
1691     vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer.
1692 
1693 It is OK, you should just ignore it. It happens because you use function
1694 `vkGetBufferMemoryRequirements2KHR()` instead of standard
1695 `vkGetBufferMemoryRequirements()`, while the validation layer seems to be
1696 unaware of it.
1697 
1698 To learn more about this extension, see:
1699 
1700 - [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap44.html#VK_KHR_dedicated_allocation)
1701 - [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5)
1702 
1703 
1704 
1705 \page vk_amd_device_coherent_memory VK_AMD_device_coherent_memory
1706 
1707 VK_AMD_device_coherent_memory is a device extension that enables access to
1708 additional memory types with `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and
1709 `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flag. It is useful mostly for
1710 allocation of buffers intended for writing "breadcrumb markers" in between passes
1711 or draw calls, which in turn are useful for debugging GPU crash/hang/TDR cases.
1712 
1713 When the extension is available but has not been enabled, Vulkan physical device
1714 still exposes those memory types, but their usage is forbidden. VMA automatically
1715 takes care of that - it returns `VK_ERROR_FEATURE_NOT_PRESENT` when an attempt
1716 to allocate memory of such type is made.
1717 
1718 If you want to use this extension in connection with VMA, follow these steps:
1719 
1720 \section vk_amd_device_coherent_memory_initialization Initialization
1721 
1722 1) Call `vkEnumerateDeviceExtensionProperties` for the physical device.
1723 Check if the extension is supported - if returned array of `VkExtensionProperties` contains "VK_AMD_device_coherent_memory".
1724 
1725 2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`.
1726 Attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to `VkPhysicalDeviceFeatures2::pNext` to be returned.
1727 Check if the device feature is really supported - check if `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true.
1728 
1729 3) While creating device with `vkCreateDevice`, enable this extension - add "VK_AMD_device_coherent_memory"
1730 to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`.
1731 
1732 4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`.
1733 Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`.
1734 Enable this device feature - attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to
1735 `VkPhysicalDeviceFeatures2::pNext` and set its member `deviceCoherentMemory` to `VK_TRUE`.
1736 
1737 5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you
1738 have enabled this extension and feature - add #VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT
1739 to VmaAllocatorCreateInfo::flags.
1740 
1741 \section vk_amd_device_coherent_memory_usage Usage
1742 
1743 After following steps described above, you can create VMA allocations and custom pools
1744 out of the special `DEVICE_COHERENT` and `DEVICE_UNCACHED` memory types on eligible
1745 devices. There are multiple ways to do it, for example:
1746 
1747 - You can request or prefer to allocate out of such memory types by adding
1748   `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` to VmaAllocationCreateInfo::requiredFlags
1749   or VmaAllocationCreateInfo::preferredFlags. Those flags can be freely mixed with
1750   other ways of \ref choosing_memory_type, like setting VmaAllocationCreateInfo::usage.
1751 - If you manually found memory type index to use for this purpose, force allocation
1752   from this specific index by setting VmaAllocationCreateInfo::memoryTypeBits `= 1u << index`.
1753 
1754 \section vk_amd_device_coherent_memory_more_information More information
1755 
1756 To learn more about this extension, see [VK_AMD_device_coherent_memory in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap44.html#VK_AMD_device_coherent_memory)
1757 
1758 Example use of this extension can be found in the code of the sample and test suite
1759 accompanying this library.
1760 
1761 
1762 \page enabling_buffer_device_address Enabling buffer device address
1763 
1764 Device extension VK_KHR_buffer_device_address
1765 allow to fetch raw GPU pointer to a buffer and pass it for usage in a shader code.
1766 It is promoted to core Vulkan 1.2.
1767 
1768 If you want to use this feature in connection with VMA, follow these steps:
1769 
1770 \section enabling_buffer_device_address_initialization Initialization
1771 
1772 1) (For Vulkan version < 1.2) Call `vkEnumerateDeviceExtensionProperties` for the physical device.
1773 Check if the extension is supported - if returned array of `VkExtensionProperties` contains
1774 "VK_KHR_buffer_device_address".
1775 
1776 2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`.
1777 Attach additional structure `VkPhysicalDeviceBufferDeviceAddressFeatures*` to `VkPhysicalDeviceFeatures2::pNext` to be returned.
1778 Check if the device feature is really supported - check if `VkPhysicalDeviceBufferDeviceAddressFeatures*::bufferDeviceAddress` is true.
1779 
1780 3) (For Vulkan version < 1.2) While creating device with `vkCreateDevice`, enable this extension - add
1781 "VK_KHR_buffer_device_address" to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`.
1782 
1783 4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`.
1784 Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`.
1785 Enable this device feature - attach additional structure `VkPhysicalDeviceBufferDeviceAddressFeatures*` to
1786 `VkPhysicalDeviceFeatures2::pNext` and set its member `bufferDeviceAddress` to `VK_TRUE`.
1787 
1788 5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you
1789 have enabled this feature - add #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT
1790 to VmaAllocatorCreateInfo::flags.
1791 
1792 \section enabling_buffer_device_address_usage Usage
1793 
1794 After following steps described above, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*` using VMA.
1795 The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT*` to
1796 allocated memory blocks wherever it might be needed.
1797 
1798 Please note that the library supports only `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*`.
1799 The second part of this functionality related to "capture and replay" is not supported,
1800 as it is intended for usage in debugging tools like RenderDoc, not in everyday Vulkan usage.
1801 
1802 \section enabling_buffer_device_address_more_information More information
1803 
1804 To learn more about this extension, see [VK_KHR_buffer_device_address in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap46.html#VK_KHR_buffer_device_address)
1805 
1806 Example use of this extension can be found in the code of the sample and test suite
1807 accompanying this library.
1808 
1809 \page general_considerations General considerations
1810 
1811 \section general_considerations_thread_safety Thread safety
1812 
1813 - The library has no global state, so separate #VmaAllocator objects can be used
1814   independently.
1815   There should be no need to create multiple such objects though - one per `VkDevice` is enough.
1816 - By default, all calls to functions that take #VmaAllocator as first parameter
1817   are safe to call from multiple threads simultaneously because they are
1818   synchronized internally when needed.
1819 - When the allocator is created with #VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT
1820   flag, calls to functions that take such #VmaAllocator object must be
1821   synchronized externally.
1822 - Access to a #VmaAllocation object must be externally synchronized. For example,
1823   you must not call vmaGetAllocationInfo() and vmaMapMemory() from different
1824   threads at the same time if you pass the same #VmaAllocation object to these
1825   functions.
1826 
1827 \section general_considerations_validation_layer_warnings Validation layer warnings
1828 
1829 When using this library, you can meet following types of warnings issued by
1830 Vulkan validation layer. They don't necessarily indicate a bug, so you may need
1831 to just ignore them.
1832 
1833 - *vkBindBufferMemory(): Binding memory to buffer 0xeb8e4 but vkGetBufferMemoryRequirements() has not been called on that buffer.*
1834   - It happens when VK_KHR_dedicated_allocation extension is enabled.
1835     `vkGetBufferMemoryRequirements2KHR` function is used instead, while validation layer seems to be unaware of it.
1836 - *Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.*
1837   - It happens when you map a buffer or image, because the library maps entire
1838     `VkDeviceMemory` block, where different types of images and buffers may end
1839     up together, especially on GPUs with unified memory like Intel.
1840 - *Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug.*
1841   - It happens when you use lost allocations, and a new image or buffer is
1842     created in place of an existing object that bacame lost.
1843   - It may happen also when you use [defragmentation](@ref defragmentation).
1844 
1845 \section general_considerations_allocation_algorithm Allocation algorithm
1846 
1847 The library uses following algorithm for allocation, in order:
1848 
1849 -# Try to find free range of memory in existing blocks.
1850 -# If failed, try to create a new block of `VkDeviceMemory`, with preferred block size.
1851 -# If failed, try to create such block with size/2, size/4, size/8.
1852 -# If failed and #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag was
1853    specified, try to find space in existing blocks, possilby making some other
1854    allocations lost.
1855 -# If failed, try to allocate separate `VkDeviceMemory` for this allocation,
1856    just like when you use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
1857 -# If failed, choose other memory type that meets the requirements specified in
1858    VmaAllocationCreateInfo and go to point 1.
1859 -# If failed, return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
1860 
1861 \section general_considerations_features_not_supported Features not supported
1862 
1863 Features deliberately excluded from the scope of this library:
1864 
1865 - Data transfer. Uploading (straming) and downloading data of buffers and images
1866   between CPU and GPU memory and related synchronization is responsibility of the user.
1867   Defining some "texture" object that would automatically stream its data from a
1868   staging copy in CPU memory to GPU memory would rather be a feature of another,
1869   higher-level library implemented on top of VMA.
1870 - Allocations for imported/exported external memory. They tend to require
1871   explicit memory type index and dedicated allocation anyway, so they don't
1872   interact with main features of this library. Such special purpose allocations
1873   should be made manually, using `vkCreateBuffer()` and `vkAllocateMemory()`.
1874 - Recreation of buffers and images. Although the library has functions for
1875   buffer and image creation (vmaCreateBuffer(), vmaCreateImage()), you need to
1876   recreate these objects yourself after defragmentation. That's because the big
1877   structures `VkBufferCreateInfo`, `VkImageCreateInfo` are not stored in
1878   #VmaAllocation object.
1879 - Handling CPU memory allocation failures. When dynamically creating small C++
1880   objects in CPU memory (not Vulkan memory), allocation failures are not checked
1881   and handled gracefully, because that would complicate code significantly and
1882   is usually not needed in desktop PC applications anyway.
1883 - Code free of any compiler warnings. Maintaining the library to compile and
1884   work correctly on so many different platforms is hard enough. Being free of
1885   any warnings, on any version of any compiler, is simply not feasible.
1886 - This is a C++ library with C interface.
1887   Bindings or ports to any other programming languages are welcomed as external projects and
1888   are not going to be included into this repository.
1889 
1890 */
1891 
1892 #if VMA_RECORDING_ENABLED
1893     #include <chrono>
1894     #if defined(_WIN32)
1895         #include <windows.h>
1896     #else
1897         #include <sstream>
1898         #include <thread>
1899     #endif
1900 #endif
1901 
1902 #ifdef __cplusplus
1903 extern "C" {
1904 #endif
1905 
1906 /*
1907 Define this macro to 0/1 to disable/enable support for recording functionality,
1908 available through VmaAllocatorCreateInfo::pRecordSettings.
1909 */
1910 #ifndef VMA_RECORDING_ENABLED
1911     #define VMA_RECORDING_ENABLED 0
1912 #endif
1913 
1914 #ifndef NOMINMAX
1915     #define NOMINMAX // For windows.h
1916 #endif
1917 
1918 #if defined(__ANDROID__) && defined(VK_NO_PROTOTYPES) && VMA_STATIC_VULKAN_FUNCTIONS
1919     extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
1920     extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
1921     extern PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
1922     extern PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
1923     extern PFN_vkAllocateMemory vkAllocateMemory;
1924     extern PFN_vkFreeMemory vkFreeMemory;
1925     extern PFN_vkMapMemory vkMapMemory;
1926     extern PFN_vkUnmapMemory vkUnmapMemory;
1927     extern PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
1928     extern PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
1929     extern PFN_vkBindBufferMemory vkBindBufferMemory;
1930     extern PFN_vkBindImageMemory vkBindImageMemory;
1931     extern PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
1932     extern PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
1933     extern PFN_vkCreateBuffer vkCreateBuffer;
1934     extern PFN_vkDestroyBuffer vkDestroyBuffer;
1935     extern PFN_vkCreateImage vkCreateImage;
1936     extern PFN_vkDestroyImage vkDestroyImage;
1937     extern PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
1938     #if VMA_VULKAN_VERSION >= 1001000
1939         extern PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2;
1940         extern PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2;
1941         extern PFN_vkBindBufferMemory2 vkBindBufferMemory2;
1942         extern PFN_vkBindImageMemory2 vkBindImageMemory2;
1943         extern PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2;
1944     #endif // #if VMA_VULKAN_VERSION >= 1001000
1945 #endif // #if defined(__ANDROID__) && VMA_STATIC_VULKAN_FUNCTIONS && VK_NO_PROTOTYPES
1946 
1947 #ifndef VULKAN_H_
1948     // For skia we don't include vulkan.h here. Before including this header we always include
1949     // Skias internal vulkan header which should have everything we need. We can't depend on the
1950     // VULKAN_H_ guard casue skia just inclues vulkan_core.h which doesn't not set the general
1951     // vulkan guard.
1952     //#include <vulkan/vulkan.h>
1953 #endif
1954 
1955 // Define this macro to declare maximum supported Vulkan version in format AAABBBCCC,
1956 // where AAA = major, BBB = minor, CCC = patch.
1957 // If you want to use version > 1.0, it still needs to be enabled via VmaAllocatorCreateInfo::vulkanApiVersion.
1958 #if !defined(VMA_VULKAN_VERSION)
1959     #if defined(VK_VERSION_1_2)
1960         #define VMA_VULKAN_VERSION 1002000
1961     #elif defined(VK_VERSION_1_1)
1962         #define VMA_VULKAN_VERSION 1001000
1963     #else
1964         #define VMA_VULKAN_VERSION 1000000
1965     #endif
1966 #endif
1967 
1968 #if !defined(VMA_DEDICATED_ALLOCATION)
1969     #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation
1970         #define VMA_DEDICATED_ALLOCATION 1
1971     #else
1972         #define VMA_DEDICATED_ALLOCATION 0
1973     #endif
1974 #endif
1975 
1976 #if !defined(VMA_BIND_MEMORY2)
1977     #if VK_KHR_bind_memory2
1978         #define VMA_BIND_MEMORY2 1
1979     #else
1980         #define VMA_BIND_MEMORY2 0
1981     #endif
1982 #endif
1983 
1984 #if !defined(VMA_MEMORY_BUDGET)
1985     #if VK_EXT_memory_budget && (VK_KHR_get_physical_device_properties2 || VMA_VULKAN_VERSION >= 1001000)
1986         #define VMA_MEMORY_BUDGET 1
1987     #else
1988         #define VMA_MEMORY_BUDGET 0
1989     #endif
1990 #endif
1991 
1992 // Defined to 1 when VK_KHR_buffer_device_address device extension or equivalent core Vulkan 1.2 feature is defined in its headers.
1993 #if !defined(VMA_BUFFER_DEVICE_ADDRESS)
1994     #if VK_KHR_buffer_device_address || VMA_VULKAN_VERSION >= 1002000
1995         #define VMA_BUFFER_DEVICE_ADDRESS 1
1996     #else
1997         #define VMA_BUFFER_DEVICE_ADDRESS 0
1998     #endif
1999 #endif
2000 
2001 // Define these macros to decorate all public functions with additional code,
2002 // before and after returned type, appropriately. This may be useful for
2003 // exporing the functions when compiling VMA as a separate library. Example:
2004 // #define VMA_CALL_PRE  __declspec(dllexport)
2005 // #define VMA_CALL_POST __cdecl
2006 #ifndef VMA_CALL_PRE
2007     #define VMA_CALL_PRE
2008 #endif
2009 #ifndef VMA_CALL_POST
2010     #define VMA_CALL_POST
2011 #endif
2012 
2013 // Define this macro to decorate pointers with an attribute specifying the
2014 // length of the array they point to if they are not null.
2015 //
2016 // The length may be one of
2017 // - The name of another parameter in the argument list where the pointer is declared
2018 // - The name of another member in the struct where the pointer is declared
2019 // - The name of a member of a struct type, meaning the value of that member in
2020 //   the context of the call. For example
2021 //   VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount"),
2022 //   this means the number of memory heaps available in the device associated
2023 //   with the VmaAllocator being dealt with.
2024 #ifndef VMA_LEN_IF_NOT_NULL
2025     #define VMA_LEN_IF_NOT_NULL(len)
2026 #endif
2027 
2028 // The VMA_NULLABLE macro is defined to be _Nullable when compiling with Clang.
2029 // see: https://clang.llvm.org/docs/AttributeReference.html#nullable
2030 #ifndef VMA_NULLABLE
2031     #ifdef __clang__
2032         #define VMA_NULLABLE _Nullable
2033     #else
2034         #define VMA_NULLABLE
2035     #endif
2036 #endif
2037 
2038 // The VMA_NOT_NULL macro is defined to be _Nonnull when compiling with Clang.
2039 // see: https://clang.llvm.org/docs/AttributeReference.html#nonnull
2040 #ifndef VMA_NOT_NULL
2041     #ifdef __clang__
2042         #define VMA_NOT_NULL _Nonnull
2043     #else
2044         #define VMA_NOT_NULL
2045     #endif
2046 #endif
2047 
2048 // If non-dispatchable handles are represented as pointers then we can give
2049 // then nullability annotations
2050 #ifndef VMA_NOT_NULL_NON_DISPATCHABLE
2051     #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
2052         #define VMA_NOT_NULL_NON_DISPATCHABLE VMA_NOT_NULL
2053     #else
2054         #define VMA_NOT_NULL_NON_DISPATCHABLE
2055     #endif
2056 #endif
2057 
2058 #ifndef VMA_NULLABLE_NON_DISPATCHABLE
2059     #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
2060         #define VMA_NULLABLE_NON_DISPATCHABLE VMA_NULLABLE
2061     #else
2062         #define VMA_NULLABLE_NON_DISPATCHABLE
2063     #endif
2064 #endif
2065 
2066 /** \struct VmaAllocator
2067 \brief Represents main object of this library initialized.
2068 
2069 Fill structure #VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it.
2070 Call function vmaDestroyAllocator() to destroy it.
2071 
2072 It is recommended to create just one object of this type per `VkDevice` object,
2073 right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed.
2074 */
2075 VK_DEFINE_HANDLE(VmaAllocator)
2076 
2077 /// Callback function called after successful vkAllocateMemory.
2078 typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)(
2079     VmaAllocator VMA_NOT_NULL                    allocator,
2080     uint32_t                                     memoryType,
2081     VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,
2082     VkDeviceSize                                 size,
2083     void* VMA_NULLABLE                           pUserData);
2084 /// Callback function called before vkFreeMemory.
2085 typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)(
2086     VmaAllocator VMA_NOT_NULL                    allocator,
2087     uint32_t                                     memoryType,
2088     VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,
2089     VkDeviceSize                                 size,
2090     void* VMA_NULLABLE                           pUserData);
2091 
2092 /** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`.
2093 
2094 Provided for informative purpose, e.g. to gather statistics about number of
2095 allocations or total amount of memory allocated in Vulkan.
2096 
2097 Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
2098 */
2099 typedef struct VmaDeviceMemoryCallbacks {
2100     /// Optional, can be null.
2101     PFN_vmaAllocateDeviceMemoryFunction VMA_NULLABLE pfnAllocate;
2102     /// Optional, can be null.
2103     PFN_vmaFreeDeviceMemoryFunction VMA_NULLABLE pfnFree;
2104     /// Optional, can be null.
2105     void* VMA_NULLABLE pUserData;
2106 } VmaDeviceMemoryCallbacks;
2107 
2108 /// Flags for created #VmaAllocator.
2109 typedef enum VmaAllocatorCreateFlagBits {
2110     /** \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.
2111 
2112     Using this flag may increase performance because internal mutexes are not used.
2113     */
2114     VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001,
2115     /** \brief Enables usage of VK_KHR_dedicated_allocation extension.
2116 
2117     The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`.
2118     When it's `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1.
2119 
2120     Using this extenion will automatically allocate dedicated blocks of memory for
2121     some buffers and images instead of suballocating place for them out of bigger
2122     memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
2123     flag) when it is recommended by the driver. It may improve performance on some
2124     GPUs.
2125 
2126     You may set this flag only if you found out that following device extensions are
2127     supported, you enabled them while creating Vulkan device passed as
2128     VmaAllocatorCreateInfo::device, and you want them to be used internally by this
2129     library:
2130 
2131     - VK_KHR_get_memory_requirements2 (device extension)
2132     - VK_KHR_dedicated_allocation (device extension)
2133 
2134     When this flag is set, you can experience following warnings reported by Vulkan
2135     validation layer. You can ignore them.
2136 
2137     > vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer.
2138     */
2139     VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002,
2140     /**
2141     Enables usage of VK_KHR_bind_memory2 extension.
2142 
2143     The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`.
2144     When it's `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1.
2145 
2146     You may set this flag only if you found out that this device extension is supported,
2147     you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,
2148     and you want it to be used internally by this library.
2149 
2150     The extension provides functions `vkBindBufferMemory2KHR` and `vkBindImageMemory2KHR`,
2151     which allow to pass a chain of `pNext` structures while binding.
2152     This flag is required if you use `pNext` parameter in vmaBindBufferMemory2() or vmaBindImageMemory2().
2153     */
2154     VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT = 0x00000004,
2155     /**
2156     Enables usage of VK_EXT_memory_budget extension.
2157 
2158     You may set this flag only if you found out that this device extension is supported,
2159     you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,
2160     and you want it to be used internally by this library, along with another instance extension
2161     VK_KHR_get_physical_device_properties2, which is required by it (or Vulkan 1.1, where this extension is promoted).
2162 
2163     The extension provides query for current memory usage and budget, which will probably
2164     be more accurate than an estimation used by the library otherwise.
2165     */
2166     VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT = 0x00000008,
2167     /**
2168     Enables usage of VK_AMD_device_coherent_memory extension.
2169 
2170     You may set this flag only if you:
2171 
2172     - found out that this device extension is supported and enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,
2173     - checked that `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true and set it while creating the Vulkan device,
2174     - want it to be used internally by this library.
2175 
2176     The extension and accompanying device feature provide access to memory types with
2177     `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flags.
2178     They are useful mostly for writing breadcrumb markers - a common method for debugging GPU crash/hang/TDR.
2179 
2180     When the extension is not enabled, such memory types are still enumerated, but their usage is illegal.
2181     To protect from this error, if you don't create the allocator with this flag, it will refuse to allocate any memory or create a custom pool in such memory type,
2182     returning `VK_ERROR_FEATURE_NOT_PRESENT`.
2183     */
2184     VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT = 0x00000010,
2185     /**
2186     Enables usage of "buffer device address" feature, which allows you to use function
2187     `vkGetBufferDeviceAddress*` to get raw GPU pointer to a buffer and pass it for usage inside a shader.
2188 
2189     You may set this flag only if you:
2190 
2191     1. (For Vulkan version < 1.2) Found as available and enabled device extension
2192     VK_KHR_buffer_device_address.
2193     This extension is promoted to core Vulkan 1.2.
2194     2. Found as available and enabled device feature `VkPhysicalDeviceBufferDeviceAddressFeatures*::bufferDeviceAddress`.
2195 
2196     When this flag is set, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*` using VMA.
2197     The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT*` to
2198     allocated memory blocks wherever it might be needed.
2199 
2200     For more information, see documentation chapter \ref enabling_buffer_device_address.
2201     */
2202     VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT = 0x00000020,
2203 
2204     VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2205 } VmaAllocatorCreateFlagBits;
2206 typedef VkFlags VmaAllocatorCreateFlags;
2207 
2208 /** \brief Pointers to some Vulkan functions - a subset used by the library.
2209 
2210 Used in VmaAllocatorCreateInfo::pVulkanFunctions.
2211 */
2212 typedef struct VmaVulkanFunctions {
2213     PFN_vkGetPhysicalDeviceProperties VMA_NULLABLE vkGetPhysicalDeviceProperties;
2214     PFN_vkGetPhysicalDeviceMemoryProperties VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties;
2215     PFN_vkAllocateMemory VMA_NULLABLE vkAllocateMemory;
2216     PFN_vkFreeMemory VMA_NULLABLE vkFreeMemory;
2217     PFN_vkMapMemory VMA_NULLABLE vkMapMemory;
2218     PFN_vkUnmapMemory VMA_NULLABLE vkUnmapMemory;
2219     PFN_vkFlushMappedMemoryRanges VMA_NULLABLE vkFlushMappedMemoryRanges;
2220     PFN_vkInvalidateMappedMemoryRanges VMA_NULLABLE vkInvalidateMappedMemoryRanges;
2221     PFN_vkBindBufferMemory VMA_NULLABLE vkBindBufferMemory;
2222     PFN_vkBindImageMemory VMA_NULLABLE vkBindImageMemory;
2223     PFN_vkGetBufferMemoryRequirements VMA_NULLABLE vkGetBufferMemoryRequirements;
2224     PFN_vkGetImageMemoryRequirements VMA_NULLABLE vkGetImageMemoryRequirements;
2225     PFN_vkCreateBuffer VMA_NULLABLE vkCreateBuffer;
2226     PFN_vkDestroyBuffer VMA_NULLABLE vkDestroyBuffer;
2227     PFN_vkCreateImage VMA_NULLABLE vkCreateImage;
2228     PFN_vkDestroyImage VMA_NULLABLE vkDestroyImage;
2229     PFN_vkCmdCopyBuffer VMA_NULLABLE vkCmdCopyBuffer;
2230 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
2231     PFN_vkGetBufferMemoryRequirements2KHR VMA_NULLABLE vkGetBufferMemoryRequirements2KHR;
2232     PFN_vkGetImageMemoryRequirements2KHR VMA_NULLABLE vkGetImageMemoryRequirements2KHR;
2233 #endif
2234 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
2235     PFN_vkBindBufferMemory2KHR VMA_NULLABLE vkBindBufferMemory2KHR;
2236     PFN_vkBindImageMemory2KHR VMA_NULLABLE vkBindImageMemory2KHR;
2237 #endif
2238 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
2239     PFN_vkGetPhysicalDeviceMemoryProperties2KHR VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties2KHR;
2240 #endif
2241 } VmaVulkanFunctions;
2242 
2243 /// Flags to be used in VmaRecordSettings::flags.
2244 typedef enum VmaRecordFlagBits {
2245     /** \brief Enables flush after recording every function call.
2246 
2247     Enable it if you expect your application to crash, which may leave recording file truncated.
2248     It may degrade performance though.
2249     */
2250     VMA_RECORD_FLUSH_AFTER_CALL_BIT = 0x00000001,
2251 
2252     VMA_RECORD_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2253 } VmaRecordFlagBits;
2254 typedef VkFlags VmaRecordFlags;
2255 
2256 /// Parameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSettings.
2257 typedef struct VmaRecordSettings
2258 {
2259     /// Flags for recording. Use #VmaRecordFlagBits enum.
2260     VmaRecordFlags flags;
2261     /** \brief Path to the file that should be written by the recording.
2262 
2263     Suggested extension: "csv".
2264     If the file already exists, it will be overwritten.
2265     It will be opened for the whole time #VmaAllocator object is alive.
2266     If opening this file fails, creation of the whole allocator object fails.
2267     */
2268     const char* VMA_NOT_NULL pFilePath;
2269 } VmaRecordSettings;
2270 
2271 /// Description of a Allocator to be created.
2272 typedef struct VmaAllocatorCreateInfo
2273 {
2274     /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum.
2275     VmaAllocatorCreateFlags flags;
2276     /// Vulkan physical device.
2277     /** It must be valid throughout whole lifetime of created allocator. */
2278     VkPhysicalDevice VMA_NOT_NULL physicalDevice;
2279     /// Vulkan device.
2280     /** It must be valid throughout whole lifetime of created allocator. */
2281     VkDevice VMA_NOT_NULL device;
2282     /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional.
2283     /** Set to 0 to use default, which is currently 256 MiB. */
2284     VkDeviceSize preferredLargeHeapBlockSize;
2285     /// Custom CPU memory allocation callbacks. Optional.
2286     /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */
2287     const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks;
2288     /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional.
2289     /** Optional, can be null. */
2290     const VmaDeviceMemoryCallbacks* VMA_NULLABLE pDeviceMemoryCallbacks;
2291     /** \brief Maximum number of additional frames that are in use at the same time as current frame.
2292 
2293     This value is used only when you make allocations with
2294     VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
2295     lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
2296 
2297     For example, if you double-buffer your command buffers, so resources used for
2298     rendering in previous frame may still be in use by the GPU at the moment you
2299     allocate resources needed for the current frame, set this value to 1.
2300 
2301     If you want to allow any allocations other than used in the current frame to
2302     become lost, set this value to 0.
2303     */
2304     uint32_t frameInUseCount;
2305     /** \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.
2306 
2307     If not NULL, it must be a pointer to an array of
2308     `VkPhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on
2309     maximum number of bytes that can be allocated out of particular Vulkan memory
2310     heap.
2311 
2312     Any of the elements may be equal to `VK_WHOLE_SIZE`, which means no limit on that
2313     heap. This is also the default in case of `pHeapSizeLimit` = NULL.
2314 
2315     If there is a limit defined for a heap:
2316 
2317     - If user tries to allocate more memory from that heap using this allocator,
2318       the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
2319     - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the
2320       value of this limit will be reported instead when using vmaGetMemoryProperties().
2321 
2322     Warning! Using this feature may not be equivalent to installing a GPU with
2323     smaller amount of memory, because graphics driver doesn't necessary fail new
2324     allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is
2325     exceeded. It may return success and just silently migrate some device memory
2326     blocks to system RAM. This driver behavior can also be controlled using
2327     VK_AMD_memory_overallocation_behavior extension.
2328     */
2329     const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pHeapSizeLimit;
2330 
2331     /** \brief Pointers to Vulkan functions. Can be null.
2332 
2333     For details see [Pointers to Vulkan functions](@ref config_Vulkan_functions).
2334     */
2335     const VmaVulkanFunctions* VMA_NULLABLE pVulkanFunctions;
2336     /** \brief Parameters for recording of VMA calls. Can be null.
2337 
2338     If not null, it enables recording of calls to VMA functions to a file.
2339     If support for recording is not enabled using `VMA_RECORDING_ENABLED` macro,
2340     creation of the allocator object fails with `VK_ERROR_FEATURE_NOT_PRESENT`.
2341     */
2342     const VmaRecordSettings* VMA_NULLABLE pRecordSettings;
2343     /** \brief Handle to Vulkan instance object.
2344 
2345     Starting from version 3.0.0 this member is no longer optional, it must be set!
2346     */
2347     VkInstance VMA_NOT_NULL instance;
2348     /** \brief Optional. The highest version of Vulkan that the application is designed to use.
2349 
2350     It must be a value in the format as created by macro `VK_MAKE_VERSION` or a constant like: `VK_API_VERSION_1_1`, `VK_API_VERSION_1_0`.
2351     The patch version number specified is ignored. Only the major and minor versions are considered.
2352     It must be less or equal (preferably equal) to value as passed to `vkCreateInstance` as `VkApplicationInfo::apiVersion`.
2353     Only versions 1.0 and 1.1 are supported by the current implementation.
2354     Leaving it initialized to zero is equivalent to `VK_API_VERSION_1_0`.
2355     */
2356     uint32_t vulkanApiVersion;
2357 } VmaAllocatorCreateInfo;
2358 
2359 /// Creates Allocator object.
2360 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
2361     const VmaAllocatorCreateInfo* VMA_NOT_NULL pCreateInfo,
2362     VmaAllocator VMA_NULLABLE * VMA_NOT_NULL pAllocator);
2363 
2364 /// Destroys allocator object.
2365 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
2366     VmaAllocator VMA_NULLABLE allocator);
2367 
2368 /** \brief Information about existing #VmaAllocator object.
2369 */
2370 typedef struct VmaAllocatorInfo
2371 {
2372     /** \brief Handle to Vulkan instance object.
2373 
2374     This is the same value as has been passed through VmaAllocatorCreateInfo::instance.
2375     */
2376     VkInstance VMA_NOT_NULL instance;
2377     /** \brief Handle to Vulkan physical device object.
2378 
2379     This is the same value as has been passed through VmaAllocatorCreateInfo::physicalDevice.
2380     */
2381     VkPhysicalDevice VMA_NOT_NULL physicalDevice;
2382     /** \brief Handle to Vulkan device object.
2383 
2384     This is the same value as has been passed through VmaAllocatorCreateInfo::device.
2385     */
2386     VkDevice VMA_NOT_NULL device;
2387 } VmaAllocatorInfo;
2388 
2389 /** \brief Returns information about existing #VmaAllocator object - handle to Vulkan device etc.
2390 
2391 It might be useful if you want to keep just the #VmaAllocator handle and fetch other required handles to
2392 `VkPhysicalDevice`, `VkDevice` etc. every time using this function.
2393 */
2394 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator VMA_NOT_NULL allocator, VmaAllocatorInfo* VMA_NOT_NULL pAllocatorInfo);
2395 
2396 /**
2397 PhysicalDeviceProperties are fetched from physicalDevice by the allocator.
2398 You can access it here, without fetching it again on your own.
2399 */
2400 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
2401     VmaAllocator VMA_NOT_NULL allocator,
2402     const VkPhysicalDeviceProperties* VMA_NULLABLE * VMA_NOT_NULL ppPhysicalDeviceProperties);
2403 
2404 /**
2405 PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator.
2406 You can access it here, without fetching it again on your own.
2407 */
2408 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
2409     VmaAllocator VMA_NOT_NULL allocator,
2410     const VkPhysicalDeviceMemoryProperties* VMA_NULLABLE * VMA_NOT_NULL ppPhysicalDeviceMemoryProperties);
2411 
2412 /**
2413 \brief Given Memory Type Index, returns Property Flags of this memory type.
2414 
2415 This is just a convenience function. Same information can be obtained using
2416 vmaGetMemoryProperties().
2417 */
2418 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
2419     VmaAllocator VMA_NOT_NULL allocator,
2420     uint32_t memoryTypeIndex,
2421     VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);
2422 
2423 /** \brief Sets index of the current frame.
2424 
2425 This function must be used if you make allocations with
2426 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT and
2427 #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flags to inform the allocator
2428 when a new frame begins. Allocations queried using vmaGetAllocationInfo() cannot
2429 become lost in the current frame.
2430 */
2431 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
2432     VmaAllocator VMA_NOT_NULL allocator,
2433     uint32_t frameIndex);
2434 
2435 /** \brief Calculated statistics of memory usage in entire allocator.
2436 */
2437 typedef struct VmaStatInfo
2438 {
2439     /// Number of `VkDeviceMemory` Vulkan memory blocks allocated.
2440     uint32_t blockCount;
2441     /// Number of #VmaAllocation allocation objects allocated.
2442     uint32_t allocationCount;
2443     /// Number of free ranges of memory between allocations.
2444     uint32_t unusedRangeCount;
2445     /// Total number of bytes occupied by all allocations.
2446     VkDeviceSize usedBytes;
2447     /// Total number of bytes occupied by unused ranges.
2448     VkDeviceSize unusedBytes;
2449     VkDeviceSize allocationSizeMin, allocationSizeAvg, allocationSizeMax;
2450     VkDeviceSize unusedRangeSizeMin, unusedRangeSizeAvg, unusedRangeSizeMax;
2451 } VmaStatInfo;
2452 
2453 /// General statistics from current state of Allocator.
2454 typedef struct VmaStats
2455 {
2456     VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
2457     VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
2458     VmaStatInfo total;
2459 } VmaStats;
2460 
2461 /** \brief Retrieves statistics from current state of the Allocator.
2462 
2463 This function is called "calculate" not "get" because it has to traverse all
2464 internal data structures, so it may be quite slow. For faster but more brief statistics
2465 suitable to be called every frame or every allocation, use vmaGetBudget().
2466 
2467 Note that when using allocator from multiple threads, returned information may immediately
2468 become outdated.
2469 */
2470 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
2471     VmaAllocator VMA_NOT_NULL allocator,
2472     VmaStats* VMA_NOT_NULL pStats);
2473 
2474 /** \brief Statistics of current memory usage and available budget, in bytes, for specific memory heap.
2475 */
2476 typedef struct VmaBudget
2477 {
2478     /** \brief Sum size of all `VkDeviceMemory` blocks allocated from particular heap, in bytes.
2479     */
2480     VkDeviceSize blockBytes;
2481 
2482     /** \brief Sum size of all allocations created in particular heap, in bytes.
2483 
2484     Usually less or equal than `blockBytes`.
2485     Difference `blockBytes - allocationBytes` is the amount of memory allocated but unused -
2486     available for new allocations or wasted due to fragmentation.
2487 
2488     It might be greater than `blockBytes` if there are some allocations in lost state, as they account
2489     to this value as well.
2490     */
2491     VkDeviceSize allocationBytes;
2492 
2493     /** \brief Estimated current memory usage of the program, in bytes.
2494 
2495     Fetched from system using `VK_EXT_memory_budget` extension if enabled.
2496 
2497     It might be different than `blockBytes` (usually higher) due to additional implicit objects
2498     also occupying the memory, like swapchain, pipelines, descriptor heaps, command buffers, or
2499     `VkDeviceMemory` blocks allocated outside of this library, if any.
2500     */
2501     VkDeviceSize usage;
2502 
2503     /** \brief Estimated amount of memory available to the program, in bytes.
2504 
2505     Fetched from system using `VK_EXT_memory_budget` extension if enabled.
2506 
2507     It might be different (most probably smaller) than `VkMemoryHeap::size[heapIndex]` due to factors
2508     external to the program, like other programs also consuming system resources.
2509     Difference `budget - usage` is the amount of additional memory that can probably
2510     be allocated without problems. Exceeding the budget may result in various problems.
2511     */
2512     VkDeviceSize budget;
2513 } VmaBudget;
2514 
2515 /** \brief Retrieves information about current memory budget for all memory heaps.
2516 
2517 \param[out] pBudget Must point to array with number of elements at least equal to number of memory heaps in physical device used.
2518 
2519 This function is called "get" not "calculate" because it is very fast, suitable to be called
2520 every frame or every allocation. For more detailed statistics use vmaCalculateStats().
2521 
2522 Note that when using allocator from multiple threads, returned information may immediately
2523 become outdated.
2524 */
2525 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
2526     VmaAllocator VMA_NOT_NULL allocator,
2527     VmaBudget* VMA_NOT_NULL pBudget);
2528 
2529 #ifndef VMA_STATS_STRING_ENABLED
2530 #define VMA_STATS_STRING_ENABLED 1
2531 #endif
2532 
2533 #if VMA_STATS_STRING_ENABLED
2534 
2535 /// Builds and returns statistics as string in JSON format.
2536 /** @param[out] ppStatsString Must be freed using vmaFreeStatsString() function.
2537 */
2538 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
2539     VmaAllocator VMA_NOT_NULL allocator,
2540     char* VMA_NULLABLE * VMA_NOT_NULL ppStatsString,
2541     VkBool32 detailedMap);
2542 
2543 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
2544     VmaAllocator VMA_NOT_NULL allocator,
2545     char* VMA_NULLABLE pStatsString);
2546 
2547 #endif // #if VMA_STATS_STRING_ENABLED
2548 
2549 /** \struct VmaPool
2550 \brief Represents custom memory pool
2551 
2552 Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it.
2553 Call function vmaDestroyPool() to destroy it.
2554 
2555 For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools).
2556 */
2557 VK_DEFINE_HANDLE(VmaPool)
2558 
2559 typedef enum VmaMemoryUsage
2560 {
2561     /** No intended memory usage specified.
2562     Use other members of VmaAllocationCreateInfo to specify your requirements.
2563     */
2564     VMA_MEMORY_USAGE_UNKNOWN = 0,
2565     /** Memory will be used on device only, so fast access from the device is preferred.
2566     It usually means device-local GPU (video) memory.
2567     No need to be mappable on host.
2568     It is roughly equivalent of `D3D12_HEAP_TYPE_DEFAULT`.
2569 
2570     Usage:
2571 
2572     - Resources written and read by device, e.g. images used as attachments.
2573     - Resources transferred from host once (immutable) or infrequently and read by
2574       device multiple times, e.g. textures to be sampled, vertex buffers, uniform
2575       (constant) buffers, and majority of other types of resources used on GPU.
2576 
2577     Allocation may still end up in `HOST_VISIBLE` memory on some implementations.
2578     In such case, you are free to map it.
2579     You can use #VMA_ALLOCATION_CREATE_MAPPED_BIT with this usage type.
2580     */
2581     VMA_MEMORY_USAGE_GPU_ONLY = 1,
2582     /** Memory will be mappable on host.
2583     It usually means CPU (system) memory.
2584     Guarantees to be `HOST_VISIBLE` and `HOST_COHERENT`.
2585     CPU access is typically uncached. Writes may be write-combined.
2586     Resources created in this pool may still be accessible to the device, but access to them can be slow.
2587     It is roughly equivalent of `D3D12_HEAP_TYPE_UPLOAD`.
2588 
2589     Usage: Staging copy of resources used as transfer source.
2590     */
2591     VMA_MEMORY_USAGE_CPU_ONLY = 2,
2592     /**
2593     Memory that is both mappable on host (guarantees to be `HOST_VISIBLE`) and preferably fast to access by GPU.
2594     CPU access is typically uncached. Writes may be write-combined.
2595 
2596     Usage: Resources written frequently by host (dynamic), read by device. E.g. textures (with LINEAR layout), vertex buffers, uniform buffers updated every frame or every draw call.
2597     */
2598     VMA_MEMORY_USAGE_CPU_TO_GPU = 3,
2599     /** Memory mappable on host (guarantees to be `HOST_VISIBLE`) and cached.
2600     It is roughly equivalent of `D3D12_HEAP_TYPE_READBACK`.
2601 
2602     Usage:
2603 
2604     - Resources written by device, read by host - results of some computations, e.g. screen capture, average scene luminance for HDR tone mapping.
2605     - 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.
2606     */
2607     VMA_MEMORY_USAGE_GPU_TO_CPU = 4,
2608     /** CPU memory - memory that is preferably not `DEVICE_LOCAL`, but also not guaranteed to be `HOST_VISIBLE`.
2609 
2610     Usage: Staging copy of resources moved from GPU memory to CPU memory as part
2611     of custom paging/residency mechanism, to be moved back to GPU memory when needed.
2612     */
2613     VMA_MEMORY_USAGE_CPU_COPY = 5,
2614     /** Lazily allocated GPU memory having `VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT`.
2615     Exists mostly on mobile platforms. Using it on desktop PC or other GPUs with no such memory type present will fail the allocation.
2616 
2617     Usage: Memory for transient attachment images (color attachments, depth attachments etc.), created with `VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT`.
2618 
2619     Allocations with this usage are always created as dedicated - it implies #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
2620     */
2621     VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED = 6,
2622 
2623     VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
2624 } VmaMemoryUsage;
2625 
2626 /// Flags to be passed as VmaAllocationCreateInfo::flags.
2627 typedef enum VmaAllocationCreateFlagBits {
2628     /** \brief Set this flag if the allocation should have its own memory block.
2629 
2630     Use it for special, big resources, like fullscreen images used as attachments.
2631 
2632     You should not use this flag if VmaAllocationCreateInfo::pool is not null.
2633     */
2634     VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001,
2635 
2636     /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block.
2637 
2638     If new allocation cannot be placed in any of the existing blocks, allocation
2639     fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
2640 
2641     You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and
2642     #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense.
2643 
2644     If VmaAllocationCreateInfo::pool is not null, this flag is implied and ignored. */
2645     VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002,
2646     /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it.
2647 
2648     Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData.
2649 
2650     Is it valid to use this flag for allocation made from memory type that is not
2651     `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is
2652     useful if you need an allocation that is efficient to use on GPU
2653     (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that
2654     support it (e.g. Intel GPU).
2655 
2656     You should not use this flag together with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT.
2657     */
2658     VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004,
2659     /** Allocation created with this flag can become lost as a result of another
2660     allocation with #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag, so you
2661     must check it before use.
2662 
2663     To check if allocation is not lost, call vmaGetAllocationInfo() and check if
2664     VmaAllocationInfo::deviceMemory is not `VK_NULL_HANDLE`.
2665 
2666     For details about supporting lost allocations, see Lost Allocations
2667     chapter of User Guide on Main Page.
2668 
2669     You should not use this flag together with #VMA_ALLOCATION_CREATE_MAPPED_BIT.
2670     */
2671     VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT = 0x00000008,
2672     /** While creating allocation using this flag, other allocations that were
2673     created with flag #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT can become lost.
2674 
2675     For details about supporting lost allocations, see Lost Allocations
2676     chapter of User Guide on Main Page.
2677     */
2678     VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT = 0x00000010,
2679     /** Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a
2680     null-terminated string. Instead of copying pointer value, a local copy of the
2681     string is made and stored in allocation's `pUserData`. The string is automatically
2682     freed together with the allocation. It is also used in vmaBuildStatsString().
2683     */
2684     VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020,
2685     /** Allocation will be created from upper stack in a double stack pool.
2686 
2687     This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag.
2688     */
2689     VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040,
2690     /** Create both buffer/image and allocation, but don't bind them together.
2691     It is useful when you want to bind yourself to do some more advanced binding, e.g. using some extensions.
2692     The flag is meaningful only with functions that bind by default: vmaCreateBuffer(), vmaCreateImage().
2693     Otherwise it is ignored.
2694     */
2695     VMA_ALLOCATION_CREATE_DONT_BIND_BIT = 0x00000080,
2696     /** Create allocation only if additional device memory required for it, if any, won't exceed
2697     memory budget. Otherwise return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
2698     */
2699     VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT = 0x00000100,
2700 
2701     /** Allocation strategy that chooses smallest possible free range for the
2702     allocation.
2703     */
2704     VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT  = 0x00010000,
2705     /** Allocation strategy that chooses biggest possible free range for the
2706     allocation.
2707     */
2708     VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT = 0x00020000,
2709     /** Allocation strategy that chooses first suitable free range for the
2710     allocation.
2711 
2712     "First" doesn't necessarily means the one with smallest offset in memory,
2713     but rather the one that is easiest and fastest to find.
2714     */
2715     VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = 0x00040000,
2716 
2717     /** Allocation strategy that tries to minimize memory usage.
2718     */
2719     VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT,
2720     /** Allocation strategy that tries to minimize allocation time.
2721     */
2722     VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
2723     /** Allocation strategy that tries to minimize memory fragmentation.
2724     */
2725     VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT,
2726 
2727     /** A bit mask to extract only `STRATEGY` bits from entire set of flags.
2728     */
2729     VMA_ALLOCATION_CREATE_STRATEGY_MASK =
2730         VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT |
2731         VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT |
2732         VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
2733 
2734     VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2735 } VmaAllocationCreateFlagBits;
2736 typedef VkFlags VmaAllocationCreateFlags;
2737 
2738 typedef struct VmaAllocationCreateInfo
2739 {
2740     /// Use #VmaAllocationCreateFlagBits enum.
2741     VmaAllocationCreateFlags flags;
2742     /** \brief Intended usage of memory.
2743 
2744     You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n
2745     If `pool` is not null, this member is ignored.
2746     */
2747     VmaMemoryUsage usage;
2748     /** \brief Flags that must be set in a Memory Type chosen for an allocation.
2749 
2750     Leave 0 if you specify memory requirements in other way. \n
2751     If `pool` is not null, this member is ignored.*/
2752     VkMemoryPropertyFlags requiredFlags;
2753     /** \brief Flags that preferably should be set in a memory type chosen for an allocation.
2754 
2755     Set to 0 if no additional flags are prefered. \n
2756     If `pool` is not null, this member is ignored. */
2757     VkMemoryPropertyFlags preferredFlags;
2758     /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation.
2759 
2760     Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if
2761     it meets other requirements specified by this structure, with no further
2762     restrictions on memory type index. \n
2763     If `pool` is not null, this member is ignored.
2764     */
2765     uint32_t memoryTypeBits;
2766     /** \brief Pool that this allocation should be created in.
2767 
2768     Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members:
2769     `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.
2770     */
2771     VmaPool VMA_NULLABLE pool;
2772     /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().
2773 
2774     If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either
2775     null or pointer to a null-terminated string. The string will be then copied to
2776     internal buffer, so it doesn't need to be valid after allocation call.
2777     */
2778     void* VMA_NULLABLE pUserData;
2779 } VmaAllocationCreateInfo;
2780 
2781 /**
2782 \brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.
2783 
2784 This algorithm tries to find a memory type that:
2785 
2786 - Is allowed by memoryTypeBits.
2787 - Contains all the flags from pAllocationCreateInfo->requiredFlags.
2788 - Matches intended usage.
2789 - Has as many flags from pAllocationCreateInfo->preferredFlags as possible.
2790 
2791 \return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result
2792 from this function or any other allocating function probably means that your
2793 device doesn't support any memory type with requested features for the specific
2794 type of resource you want to use it for. Please check parameters of your
2795 resource, like image layout (OPTIMAL versus LINEAR) or mip level count.
2796 */
2797 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
2798     VmaAllocator VMA_NOT_NULL allocator,
2799     uint32_t memoryTypeBits,
2800     const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2801     uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2802 
2803 /**
2804 \brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.
2805 
2806 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
2807 It internally creates a temporary, dummy buffer that never has memory bound.
2808 It is just a convenience function, equivalent to calling:
2809 
2810 - `vkCreateBuffer`
2811 - `vkGetBufferMemoryRequirements`
2812 - `vmaFindMemoryTypeIndex`
2813 - `vkDestroyBuffer`
2814 */
2815 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
2816     VmaAllocator VMA_NOT_NULL allocator,
2817     const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
2818     const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2819     uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2820 
2821 /**
2822 \brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.
2823 
2824 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
2825 It internally creates a temporary, dummy image that never has memory bound.
2826 It is just a convenience function, equivalent to calling:
2827 
2828 - `vkCreateImage`
2829 - `vkGetImageMemoryRequirements`
2830 - `vmaFindMemoryTypeIndex`
2831 - `vkDestroyImage`
2832 */
2833 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
2834     VmaAllocator VMA_NOT_NULL allocator,
2835     const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
2836     const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2837     uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2838 
2839 /// Flags to be passed as VmaPoolCreateInfo::flags.
2840 typedef enum VmaPoolCreateFlagBits {
2841     /** \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.
2842 
2843     This is an optional optimization flag.
2844 
2845     If you always allocate using vmaCreateBuffer(), vmaCreateImage(),
2846     vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator
2847     knows exact type of your allocations so it can handle Buffer-Image Granularity
2848     in the optimal way.
2849 
2850     If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(),
2851     exact type of such allocations is not known, so allocator must be conservative
2852     in handling Buffer-Image Granularity, which can lead to suboptimal allocation
2853     (wasted memory). In that case, if you can make sure you always allocate only
2854     buffers and linear images or only optimal images out of this pool, use this flag
2855     to make allocator disregard Buffer-Image Granularity and so make allocations
2856     faster and more optimal.
2857     */
2858     VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002,
2859 
2860     /** \brief Enables alternative, linear allocation algorithm in this pool.
2861 
2862     Specify this flag to enable linear allocation algorithm, which always creates
2863     new allocations after last one and doesn't reuse space from allocations freed in
2864     between. It trades memory consumption for simplified algorithm and data
2865     structure, which has better performance and uses less memory for metadata.
2866 
2867     By using this flag, you can achieve behavior of free-at-once, stack,
2868     ring buffer, and double stack. For details, see documentation chapter
2869     \ref linear_algorithm.
2870 
2871     When using this flag, you must specify VmaPoolCreateInfo::maxBlockCount == 1 (or 0 for default).
2872 
2873     For more details, see [Linear allocation algorithm](@ref linear_algorithm).
2874     */
2875     VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004,
2876 
2877     /** \brief Enables alternative, buddy allocation algorithm in this pool.
2878 
2879     It operates on a tree of blocks, each having size that is a power of two and
2880     a half of its parent's size. Comparing to default algorithm, this one provides
2881     faster allocation and deallocation and decreased external fragmentation,
2882     at the expense of more memory wasted (internal fragmentation).
2883 
2884     For more details, see [Buddy allocation algorithm](@ref buddy_algorithm).
2885     */
2886     VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT = 0x00000008,
2887 
2888     /** Bit mask to extract only `ALGORITHM` bits from entire set of flags.
2889     */
2890     VMA_POOL_CREATE_ALGORITHM_MASK =
2891         VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT |
2892         VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT,
2893 
2894     VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2895 } VmaPoolCreateFlagBits;
2896 typedef VkFlags VmaPoolCreateFlags;
2897 
2898 /** \brief Describes parameter of created #VmaPool.
2899 */
2900 typedef struct VmaPoolCreateInfo {
2901     /** \brief Vulkan memory type index to allocate this pool from.
2902     */
2903     uint32_t memoryTypeIndex;
2904     /** \brief Use combination of #VmaPoolCreateFlagBits.
2905     */
2906     VmaPoolCreateFlags flags;
2907     /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. Optional.
2908 
2909     Specify nonzero to set explicit, constant size of memory blocks used by this
2910     pool.
2911 
2912     Leave 0 to use default and let the library manage block sizes automatically.
2913     Sizes of particular blocks may vary.
2914     */
2915     VkDeviceSize blockSize;
2916     /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty.
2917 
2918     Set to 0 to have no preallocated blocks and allow the pool be completely empty.
2919     */
2920     size_t minBlockCount;
2921     /** \brief Maximum number of blocks that can be allocated in this pool. Optional.
2922 
2923     Set to 0 to use default, which is `SIZE_MAX`, which means no limit.
2924 
2925     Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated
2926     throughout whole lifetime of this pool.
2927     */
2928     size_t maxBlockCount;
2929     /** \brief Maximum number of additional frames that are in use at the same time as current frame.
2930 
2931     This value is used only when you make allocations with
2932     #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
2933     lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
2934 
2935     For example, if you double-buffer your command buffers, so resources used for
2936     rendering in previous frame may still be in use by the GPU at the moment you
2937     allocate resources needed for the current frame, set this value to 1.
2938 
2939     If you want to allow any allocations other than used in the current frame to
2940     become lost, set this value to 0.
2941     */
2942     uint32_t frameInUseCount;
2943 } VmaPoolCreateInfo;
2944 
2945 /** \brief Describes parameter of existing #VmaPool.
2946 */
2947 typedef struct VmaPoolStats {
2948     /** \brief Total amount of `VkDeviceMemory` allocated from Vulkan for this pool, in bytes.
2949     */
2950     VkDeviceSize size;
2951     /** \brief Total number of bytes in the pool not used by any #VmaAllocation.
2952     */
2953     VkDeviceSize unusedSize;
2954     /** \brief Number of #VmaAllocation objects created from this pool that were not destroyed or lost.
2955     */
2956     size_t allocationCount;
2957     /** \brief Number of continuous memory ranges in the pool not used by any #VmaAllocation.
2958     */
2959     size_t unusedRangeCount;
2960     /** \brief Size of the largest continuous free memory region available for new allocation.
2961 
2962     Making a new allocation of that size is not guaranteed to succeed because of
2963     possible additional margin required to respect alignment and buffer/image
2964     granularity.
2965     */
2966     VkDeviceSize unusedRangeSizeMax;
2967     /** \brief Number of `VkDeviceMemory` blocks allocated for this pool.
2968     */
2969     size_t blockCount;
2970 } VmaPoolStats;
2971 
2972 /** \brief Allocates Vulkan device memory and creates #VmaPool object.
2973 
2974 @param allocator Allocator object.
2975 @param pCreateInfo Parameters of pool to create.
2976 @param[out] pPool Handle to created pool.
2977 */
2978 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
2979     VmaAllocator VMA_NOT_NULL allocator,
2980     const VmaPoolCreateInfo* VMA_NOT_NULL pCreateInfo,
2981     VmaPool VMA_NULLABLE * VMA_NOT_NULL pPool);
2982 
2983 /** \brief Destroys #VmaPool object and frees Vulkan device memory.
2984 */
2985 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
2986     VmaAllocator VMA_NOT_NULL allocator,
2987     VmaPool VMA_NULLABLE pool);
2988 
2989 /** \brief Retrieves statistics of existing #VmaPool object.
2990 
2991 @param allocator Allocator object.
2992 @param pool Pool object.
2993 @param[out] pPoolStats Statistics of specified pool.
2994 */
2995 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
2996     VmaAllocator VMA_NOT_NULL allocator,
2997     VmaPool VMA_NOT_NULL pool,
2998     VmaPoolStats* VMA_NOT_NULL pPoolStats);
2999 
3000 /** \brief Marks all allocations in given pool as lost if they are not used in current frame or VmaPoolCreateInfo::frameInUseCount back from now.
3001 
3002 @param allocator Allocator object.
3003 @param pool Pool.
3004 @param[out] pLostAllocationCount Number of allocations marked as lost. Optional - pass null if you don't need this information.
3005 */
3006 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
3007     VmaAllocator VMA_NOT_NULL allocator,
3008     VmaPool VMA_NOT_NULL pool,
3009     size_t* VMA_NULLABLE pLostAllocationCount);
3010 
3011 /** \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions.
3012 
3013 Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
3014 `VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is
3015 `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
3016 
3017 Possible return values:
3018 
3019 - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool.
3020 - `VK_SUCCESS` - corruption detection has been performed and succeeded.
3021 - `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
3022   `VMA_ASSERT` is also fired in that case.
3023 - Other value: Error returned by Vulkan, e.g. memory mapping failure.
3024 */
3025 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator VMA_NOT_NULL allocator, VmaPool VMA_NOT_NULL pool);
3026 
3027 /** \brief Retrieves name of a custom pool.
3028 
3029 After the call `ppName` is either null or points to an internally-owned null-terminated string
3030 containing name of the pool that was previously set. The pointer becomes invalid when the pool is
3031 destroyed or its name is changed using vmaSetPoolName().
3032 */
3033 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
3034     VmaAllocator VMA_NOT_NULL allocator,
3035     VmaPool VMA_NOT_NULL pool,
3036     const char* VMA_NULLABLE * VMA_NOT_NULL ppName);
3037 
3038 /** \brief Sets name of a custom pool.
3039 
3040 `pName` can be either null or pointer to a null-terminated string with new name for the pool.
3041 Function makes internal copy of the string, so it can be changed or freed immediately after this call.
3042 */
3043 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
3044     VmaAllocator VMA_NOT_NULL allocator,
3045     VmaPool VMA_NOT_NULL pool,
3046     const char* VMA_NULLABLE pName);
3047 
3048 /** \struct VmaAllocation
3049 \brief Represents single memory allocation.
3050 
3051 It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type
3052 plus unique offset.
3053 
3054 There are multiple ways to create such object.
3055 You need to fill structure VmaAllocationCreateInfo.
3056 For more information see [Choosing memory type](@ref choosing_memory_type).
3057 
3058 Although the library provides convenience functions that create Vulkan buffer or image,
3059 allocate memory for it and bind them together,
3060 binding of the allocation to a buffer or an image is out of scope of the allocation itself.
3061 Allocation object can exist without buffer/image bound,
3062 binding can be done manually by the user, and destruction of it can be done
3063 independently of destruction of the allocation.
3064 
3065 The object also remembers its size and some other information.
3066 To retrieve this information, use function vmaGetAllocationInfo() and inspect
3067 returned structure VmaAllocationInfo.
3068 
3069 Some kinds allocations can be in lost state.
3070 For more information, see [Lost allocations](@ref lost_allocations).
3071 */
3072 VK_DEFINE_HANDLE(VmaAllocation)
3073 
3074 /** \brief Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
3075 */
3076 typedef struct VmaAllocationInfo {
3077     /** \brief Memory type index that this allocation was allocated from.
3078 
3079     It never changes.
3080     */
3081     uint32_t memoryType;
3082     /** \brief Handle to Vulkan memory object.
3083 
3084     Same memory object can be shared by multiple allocations.
3085 
3086     It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
3087 
3088     If the allocation is lost, it is equal to `VK_NULL_HANDLE`.
3089     */
3090     VkDeviceMemory VMA_NULLABLE_NON_DISPATCHABLE deviceMemory;
3091     /** \brief Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation.
3092 
3093     It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
3094     */
3095     VkDeviceSize offset;
3096     /** \brief Size of this allocation, in bytes.
3097 
3098     It never changes, unless allocation is lost.
3099     */
3100     VkDeviceSize size;
3101     /** \brief Pointer to the beginning of this allocation as mapped data.
3102 
3103     If the allocation hasn't been mapped using vmaMapMemory() and hasn't been
3104     created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value is null.
3105 
3106     It can change after call to vmaMapMemory(), vmaUnmapMemory().
3107     It can also change after call to vmaDefragment() if this allocation is passed to the function.
3108     */
3109     void* VMA_NULLABLE pMappedData;
3110     /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData().
3111 
3112     It can change after call to vmaSetAllocationUserData() for this allocation.
3113     */
3114     void* VMA_NULLABLE pUserData;
3115 } VmaAllocationInfo;
3116 
3117 /** \brief General purpose memory allocation.
3118 
3119 @param[out] pAllocation Handle to allocated memory.
3120 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
3121 
3122 You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
3123 
3124 It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(),
3125 vmaCreateBuffer(), vmaCreateImage() instead whenever possible.
3126 */
3127 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
3128     VmaAllocator VMA_NOT_NULL allocator,
3129     const VkMemoryRequirements* VMA_NOT_NULL pVkMemoryRequirements,
3130     const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3131     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3132     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3133 
3134 /** \brief General purpose memory allocation for multiple allocation objects at once.
3135 
3136 @param allocator Allocator object.
3137 @param pVkMemoryRequirements Memory requirements for each allocation.
3138 @param pCreateInfo Creation parameters for each alloction.
3139 @param allocationCount Number of allocations to make.
3140 @param[out] pAllocations Pointer to array that will be filled with handles to created allocations.
3141 @param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations.
3142 
3143 You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
3144 
3145 Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding.
3146 It is just a general purpose allocation function able to make multiple allocations at once.
3147 It may be internally optimized to be more efficient than calling vmaAllocateMemory() `allocationCount` times.
3148 
3149 All allocations are made using same parameters. All of them are created out of the same memory pool and type.
3150 If any allocation fails, all allocations already made within this function call are also freed, so that when
3151 returned result is not `VK_SUCCESS`, `pAllocation` array is always entirely filled with `VK_NULL_HANDLE`.
3152 */
3153 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
3154     VmaAllocator VMA_NOT_NULL allocator,
3155     const VkMemoryRequirements* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pVkMemoryRequirements,
3156     const VmaAllocationCreateInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pCreateInfo,
3157     size_t allocationCount,
3158     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
3159     VmaAllocationInfo* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationInfo);
3160 
3161 /**
3162 @param[out] pAllocation Handle to allocated memory.
3163 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
3164 
3165 You should free the memory using vmaFreeMemory().
3166 */
3167 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
3168     VmaAllocator VMA_NOT_NULL allocator,
3169     VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
3170     const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3171     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3172     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3173 
3174 /// Function similar to vmaAllocateMemoryForBuffer().
3175 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
3176     VmaAllocator VMA_NOT_NULL allocator,
3177     VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
3178     const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3179     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3180     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3181 
3182 /** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().
3183 
3184 Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped.
3185 */
3186 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
3187     VmaAllocator VMA_NOT_NULL allocator,
3188     const VmaAllocation VMA_NULLABLE allocation);
3189 
3190 /** \brief Frees memory and destroys multiple allocations.
3191 
3192 Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding.
3193 It is just a general purpose function to free memory and destroy allocations made using e.g. vmaAllocateMemory(),
3194 vmaAllocateMemoryPages() and other functions.
3195 It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times.
3196 
3197 Allocations in `pAllocations` array can come from any memory pools and types.
3198 Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped.
3199 */
3200 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
3201     VmaAllocator VMA_NOT_NULL allocator,
3202     size_t allocationCount,
3203     const VmaAllocation VMA_NULLABLE * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations);
3204 
3205 /** \brief Deprecated.
3206 
3207 \deprecated
3208 In version 2.2.0 it used to try to change allocation's size without moving or reallocating it.
3209 In current version it returns `VK_SUCCESS` only if `newSize` equals current allocation's size.
3210 Otherwise returns `VK_ERROR_OUT_OF_POOL_MEMORY`, indicating that allocation's size could not be changed.
3211 */
3212 VMA_CALL_PRE VkResult VMA_CALL_POST vmaResizeAllocation(
3213     VmaAllocator VMA_NOT_NULL allocator,
3214     VmaAllocation VMA_NOT_NULL allocation,
3215     VkDeviceSize newSize);
3216 
3217 /** \brief Returns current information about specified allocation and atomically marks it as used in current frame.
3218 
3219 Current paramters of given allocation are returned in `pAllocationInfo`.
3220 
3221 This function also atomically "touches" allocation - marks it as used in current frame,
3222 just like vmaTouchAllocation().
3223 If the allocation is in lost state, `pAllocationInfo->deviceMemory == VK_NULL_HANDLE`.
3224 
3225 Although this function uses atomics and doesn't lock any mutex, so it should be quite efficient,
3226 you can avoid calling it too often.
3227 
3228 - You can retrieve same VmaAllocationInfo structure while creating your resource, from function
3229   vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change
3230   (e.g. due to defragmentation or allocation becoming lost).
3231 - If you just want to check if allocation is not lost, vmaTouchAllocation() will work faster.
3232 */
3233 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
3234     VmaAllocator VMA_NOT_NULL allocator,
3235     VmaAllocation VMA_NOT_NULL allocation,
3236     VmaAllocationInfo* VMA_NOT_NULL pAllocationInfo);
3237 
3238 /** \brief Returns `VK_TRUE` if allocation is not lost and atomically marks it as used in current frame.
3239 
3240 If the allocation has been created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
3241 this function returns `VK_TRUE` if it's not in lost state, so it can still be used.
3242 It then also atomically "touches" the allocation - marks it as used in current frame,
3243 so that you can be sure it won't become lost in current frame or next `frameInUseCount` frames.
3244 
3245 If the allocation is in lost state, the function returns `VK_FALSE`.
3246 Memory of such allocation, as well as buffer or image bound to it, should not be used.
3247 Lost allocation and the buffer/image still need to be destroyed.
3248 
3249 If the allocation has been created without #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
3250 this function always returns `VK_TRUE`.
3251 */
3252 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
3253     VmaAllocator VMA_NOT_NULL allocator,
3254     VmaAllocation VMA_NOT_NULL allocation);
3255 
3256 /** \brief Sets pUserData in given allocation to new value.
3257 
3258 If the allocation was created with VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT,
3259 pUserData must be either null, or pointer to a null-terminated string. The function
3260 makes local copy of the string and sets it as allocation's `pUserData`. String
3261 passed as pUserData doesn't need to be valid for whole lifetime of the allocation -
3262 you can free it after this call. String previously pointed by allocation's
3263 pUserData is freed from memory.
3264 
3265 If the flag was not used, the value of pointer `pUserData` is just copied to
3266 allocation's `pUserData`. It is opaque, so you can use it however you want - e.g.
3267 as a pointer, ordinal number or some handle to you own data.
3268 */
3269 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
3270     VmaAllocator VMA_NOT_NULL allocator,
3271     VmaAllocation VMA_NOT_NULL allocation,
3272     void* VMA_NULLABLE pUserData);
3273 
3274 /** \brief Creates new allocation that is in lost state from the beginning.
3275 
3276 It can be useful if you need a dummy, non-null allocation.
3277 
3278 You still need to destroy created object using vmaFreeMemory().
3279 
3280 Returned allocation is not tied to any specific memory pool or memory type and
3281 not bound to any image or buffer. It has size = 0. It cannot be turned into
3282 a real, non-empty allocation.
3283 */
3284 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
3285     VmaAllocator VMA_NOT_NULL allocator,
3286     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation);
3287 
3288 /** \brief Maps memory represented by given allocation and returns pointer to it.
3289 
3290 Maps memory represented by given allocation to make it accessible to CPU code.
3291 When succeeded, `*ppData` contains pointer to first byte of this memory.
3292 If the allocation is part of bigger `VkDeviceMemory` block, the pointer is
3293 correctly offseted to the beginning of region assigned to this particular
3294 allocation.
3295 
3296 Mapping is internally reference-counted and synchronized, so despite raw Vulkan
3297 function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory`
3298 multiple times simultaneously, it is safe to call this function on allocations
3299 assigned to the same memory block. Actual Vulkan memory will be mapped on first
3300 mapping and unmapped on last unmapping.
3301 
3302 If the function succeeded, you must call vmaUnmapMemory() to unmap the
3303 allocation when mapping is no longer needed or before freeing the allocation, at
3304 the latest.
3305 
3306 It also safe to call this function multiple times on the same allocation. You
3307 must call vmaUnmapMemory() same number of times as you called vmaMapMemory().
3308 
3309 It is also safe to call this function on allocation created with
3310 #VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time.
3311 You must still call vmaUnmapMemory() same number of times as you called
3312 vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the
3313 "0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag.
3314 
3315 This function fails when used on allocation made in memory type that is not
3316 `HOST_VISIBLE`.
3317 
3318 This function always fails when called for allocation that was created with
3319 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocations cannot be
3320 mapped.
3321 
3322 This function doesn't automatically flush or invalidate caches.
3323 If the allocation is made from a memory types that is not `HOST_COHERENT`,
3324 you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification.
3325 */
3326 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
3327     VmaAllocator VMA_NOT_NULL allocator,
3328     VmaAllocation VMA_NOT_NULL allocation,
3329     void* VMA_NULLABLE * VMA_NOT_NULL ppData);
3330 
3331 /** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
3332 
3333 For details, see description of vmaMapMemory().
3334 
3335 This function doesn't automatically flush or invalidate caches.
3336 If the allocation is made from a memory types that is not `HOST_COHERENT`,
3337 you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification.
3338 */
3339 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
3340     VmaAllocator VMA_NOT_NULL allocator,
3341     VmaAllocation VMA_NOT_NULL allocation);
3342 
3343 /** \brief Flushes memory of given allocation.
3344 
3345 Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation.
3346 It needs to be called after writing to a mapped memory for memory types that are not `HOST_COHERENT`.
3347 Unmap operation doesn't do that automatically.
3348 
3349 - `offset` must be relative to the beginning of allocation.
3350 - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
3351 - `offset` and `size` don't have to be aligned.
3352   They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
3353 - If `size` is 0, this call is ignored.
3354 - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
3355   this call is ignored.
3356 
3357 Warning! `offset` and `size` are relative to the contents of given `allocation`.
3358 If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively.
3359 Do not pass allocation's offset as `offset`!!!
3360 
3361 This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is
3362 called, otherwise `VK_SUCCESS`.
3363 */
3364 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(
3365     VmaAllocator VMA_NOT_NULL allocator,
3366     VmaAllocation VMA_NOT_NULL allocation,
3367     VkDeviceSize offset,
3368     VkDeviceSize size);
3369 
3370 /** \brief Invalidates memory of given allocation.
3371 
3372 Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation.
3373 It needs to be called before reading from a mapped memory for memory types that are not `HOST_COHERENT`.
3374 Map operation doesn't do that automatically.
3375 
3376 - `offset` must be relative to the beginning of allocation.
3377 - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
3378 - `offset` and `size` don't have to be aligned.
3379   They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
3380 - If `size` is 0, this call is ignored.
3381 - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
3382   this call is ignored.
3383 
3384 Warning! `offset` and `size` are relative to the contents of given `allocation`.
3385 If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively.
3386 Do not pass allocation's offset as `offset`!!!
3387 
3388 This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if
3389 it is called, otherwise `VK_SUCCESS`.
3390 */
3391 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(
3392     VmaAllocator VMA_NOT_NULL allocator,
3393     VmaAllocation VMA_NOT_NULL allocation,
3394     VkDeviceSize offset,
3395     VkDeviceSize size);
3396 
3397 /** \brief Flushes memory of given set of allocations.
3398 
3399 Calls `vkFlushMappedMemoryRanges()` for memory associated with given ranges of given allocations.
3400 For more information, see documentation of vmaFlushAllocation().
3401 
3402 \param allocator
3403 \param allocationCount
3404 \param allocations
3405 \param offsets If not null, it must point to an array of offsets of regions to flush, relative to the beginning of respective allocations. Null means all ofsets are zero.
3406 \param sizes If not null, it must point to an array of sizes of regions to flush in respective allocations. Null means `VK_WHOLE_SIZE` for all allocations.
3407 
3408 This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is
3409 called, otherwise `VK_SUCCESS`.
3410 */
3411 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
3412     VmaAllocator VMA_NOT_NULL allocator,
3413     uint32_t allocationCount,
3414     const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
3415     const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
3416     const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
3417 
3418 /** \brief Invalidates memory of given set of allocations.
3419 
3420 Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given ranges of given allocations.
3421 For more information, see documentation of vmaInvalidateAllocation().
3422 
3423 \param allocator
3424 \param allocationCount
3425 \param allocations
3426 \param offsets If not null, it must point to an array of offsets of regions to flush, relative to the beginning of respective allocations. Null means all ofsets are zero.
3427 \param sizes If not null, it must point to an array of sizes of regions to flush in respective allocations. Null means `VK_WHOLE_SIZE` for all allocations.
3428 
3429 This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if it is
3430 called, otherwise `VK_SUCCESS`.
3431 */
3432 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
3433     VmaAllocator VMA_NOT_NULL allocator,
3434     uint32_t allocationCount,
3435     const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
3436     const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
3437     const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
3438 
3439 /** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions.
3440 
3441 @param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked.
3442 
3443 Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
3444 `VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are
3445 `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
3446 
3447 Possible return values:
3448 
3449 - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types.
3450 - `VK_SUCCESS` - corruption detection has been performed and succeeded.
3451 - `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
3452   `VMA_ASSERT` is also fired in that case.
3453 - Other value: Error returned by Vulkan, e.g. memory mapping failure.
3454 */
3455 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator VMA_NOT_NULL allocator, uint32_t memoryTypeBits);
3456 
3457 /** \struct VmaDefragmentationContext
3458 \brief Represents Opaque object that represents started defragmentation process.
3459 
3460 Fill structure #VmaDefragmentationInfo2 and call function vmaDefragmentationBegin() to create it.
3461 Call function vmaDefragmentationEnd() to destroy it.
3462 */
3463 VK_DEFINE_HANDLE(VmaDefragmentationContext)
3464 
3465 /// Flags to be used in vmaDefragmentationBegin(). None at the moment. Reserved for future use.
3466 typedef enum VmaDefragmentationFlagBits {
3467     VMA_DEFRAGMENTATION_FLAG_INCREMENTAL = 0x1,
3468     VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
3469 } VmaDefragmentationFlagBits;
3470 typedef VkFlags VmaDefragmentationFlags;
3471 
3472 /** \brief Parameters for defragmentation.
3473 
3474 To be used with function vmaDefragmentationBegin().
3475 */
3476 typedef struct VmaDefragmentationInfo2 {
3477     /** \brief Reserved for future use. Should be 0.
3478     */
3479     VmaDefragmentationFlags flags;
3480     /** \brief Number of allocations in `pAllocations` array.
3481     */
3482     uint32_t allocationCount;
3483     /** \brief Pointer to array of allocations that can be defragmented.
3484 
3485     The array should have `allocationCount` elements.
3486     The array should not contain nulls.
3487     Elements in the array should be unique - same allocation cannot occur twice.
3488     It is safe to pass allocations that are in the lost state - they are ignored.
3489     All allocations not present in this array are considered non-moveable during this defragmentation.
3490     */
3491     const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations;
3492     /** \brief Optional, output. Pointer to array that will be filled with information whether the allocation at certain index has been changed during defragmentation.
3493 
3494     The array should have `allocationCount` elements.
3495     You can pass null if you are not interested in this information.
3496     */
3497     VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged;
3498     /** \brief Numer of pools in `pPools` array.
3499     */
3500     uint32_t poolCount;
3501     /** \brief Either null or pointer to array of pools to be defragmented.
3502 
3503     All the allocations in the specified pools can be moved during defragmentation
3504     and there is no way to check if they were really moved as in `pAllocationsChanged`,
3505     so you must query all the allocations in all these pools for new `VkDeviceMemory`
3506     and offset using vmaGetAllocationInfo() if you might need to recreate buffers
3507     and images bound to them.
3508 
3509     The array should have `poolCount` elements.
3510     The array should not contain nulls.
3511     Elements in the array should be unique - same pool cannot occur twice.
3512 
3513     Using this array is equivalent to specifying all allocations from the pools in `pAllocations`.
3514     It might be more efficient.
3515     */
3516     const VmaPool VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(poolCount) pPools;
3517     /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on CPU side, like `memcpy()`, `memmove()`.
3518 
3519     `VK_WHOLE_SIZE` means no limit.
3520     */
3521     VkDeviceSize maxCpuBytesToMove;
3522     /** \brief Maximum number of allocations that can be moved to a different place using transfers on CPU side, like `memcpy()`, `memmove()`.
3523 
3524     `UINT32_MAX` means no limit.
3525     */
3526     uint32_t maxCpuAllocationsToMove;
3527     /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on GPU side, posted to `commandBuffer`.
3528 
3529     `VK_WHOLE_SIZE` means no limit.
3530     */
3531     VkDeviceSize maxGpuBytesToMove;
3532     /** \brief Maximum number of allocations that can be moved to a different place using transfers on GPU side, posted to `commandBuffer`.
3533 
3534     `UINT32_MAX` means no limit.
3535     */
3536     uint32_t maxGpuAllocationsToMove;
3537     /** \brief Optional. Command buffer where GPU copy commands will be posted.
3538 
3539     If not null, it must be a valid command buffer handle that supports Transfer queue type.
3540     It must be in the recording state and outside of a render pass instance.
3541     You need to submit it and make sure it finished execution before calling vmaDefragmentationEnd().
3542 
3543     Passing null means that only CPU defragmentation will be performed.
3544     */
3545     VkCommandBuffer VMA_NULLABLE commandBuffer;
3546 } VmaDefragmentationInfo2;
3547 
3548 typedef struct VmaDefragmentationPassMoveInfo {
3549     VmaAllocation VMA_NOT_NULL allocation;
3550     VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory;
3551     VkDeviceSize offset;
3552 } VmaDefragmentationPassMoveInfo;
3553 
3554 /** \brief Parameters for incremental defragmentation steps.
3555 
3556 To be used with function vmaBeginDefragmentationPass().
3557 */
3558 typedef struct VmaDefragmentationPassInfo {
3559     uint32_t moveCount;
3560     VmaDefragmentationPassMoveInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(moveCount) pMoves;
3561 } VmaDefragmentationPassInfo;
3562 
3563 /** \brief Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
3564 
3565 \deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
3566 */
3567 typedef struct VmaDefragmentationInfo {
3568     /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places.
3569 
3570     Default is `VK_WHOLE_SIZE`, which means no limit.
3571     */
3572     VkDeviceSize maxBytesToMove;
3573     /** \brief Maximum number of allocations that can be moved to different place.
3574 
3575     Default is `UINT32_MAX`, which means no limit.
3576     */
3577     uint32_t maxAllocationsToMove;
3578 } VmaDefragmentationInfo;
3579 
3580 /** \brief Statistics returned by function vmaDefragment(). */
3581 typedef struct VmaDefragmentationStats {
3582     /// Total number of bytes that have been copied while moving allocations to different places.
3583     VkDeviceSize bytesMoved;
3584     /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects.
3585     VkDeviceSize bytesFreed;
3586     /// Number of allocations that have been moved to different places.
3587     uint32_t allocationsMoved;
3588     /// Number of empty `VkDeviceMemory` objects that have been released to the system.
3589     uint32_t deviceMemoryBlocksFreed;
3590 } VmaDefragmentationStats;
3591 
3592 /** \brief Begins defragmentation process.
3593 
3594 @param allocator Allocator object.
3595 @param pInfo Structure filled with parameters of defragmentation.
3596 @param[out] pStats Optional. Statistics of defragmentation. You can pass null if you are not interested in this information.
3597 @param[out] pContext Context object that must be passed to vmaDefragmentationEnd() to finish defragmentation.
3598 @return `VK_SUCCESS` and `*pContext == null` if defragmentation finished within this function call. `VK_NOT_READY` and `*pContext != null` if defragmentation has been started and you need to call vmaDefragmentationEnd() to finish it. Negative value in case of error.
3599 
3600 Use this function instead of old, deprecated vmaDefragment().
3601 
3602 Warning! Between the call to vmaDefragmentationBegin() and vmaDefragmentationEnd():
3603 
3604 - You should not use any of allocations passed as `pInfo->pAllocations` or
3605   any allocations that belong to pools passed as `pInfo->pPools`,
3606   including calling vmaGetAllocationInfo(), vmaTouchAllocation(), or access
3607   their data.
3608 - Some mutexes protecting internal data structures may be locked, so trying to
3609   make or free any allocations, bind buffers or images, map memory, or launch
3610   another simultaneous defragmentation in between may cause stall (when done on
3611   another thread) or deadlock (when done on the same thread), unless you are
3612   100% sure that defragmented allocations are in different pools.
3613 - Information returned via `pStats` and `pInfo->pAllocationsChanged` are undefined.
3614   They become valid after call to vmaDefragmentationEnd().
3615 - If `pInfo->commandBuffer` is not null, you must submit that command buffer
3616   and make sure it finished execution before calling vmaDefragmentationEnd().
3617 
3618 For more information and important limitations regarding defragmentation, see documentation chapter:
3619 [Defragmentation](@ref defragmentation).
3620 */
3621 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
3622     VmaAllocator VMA_NOT_NULL allocator,
3623     const VmaDefragmentationInfo2* VMA_NOT_NULL pInfo,
3624     VmaDefragmentationStats* VMA_NULLABLE pStats,
3625     VmaDefragmentationContext VMA_NULLABLE * VMA_NOT_NULL pContext);
3626 
3627 /** \brief Ends defragmentation process.
3628 
3629 Use this function to finish defragmentation started by vmaDefragmentationBegin().
3630 It is safe to pass `context == null`. The function then does nothing.
3631 */
3632 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
3633     VmaAllocator VMA_NOT_NULL allocator,
3634     VmaDefragmentationContext VMA_NULLABLE context);
3635 
3636 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
3637     VmaAllocator VMA_NOT_NULL allocator,
3638     VmaDefragmentationContext VMA_NULLABLE context,
3639     VmaDefragmentationPassInfo* VMA_NOT_NULL pInfo
3640 );
3641 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
3642     VmaAllocator VMA_NOT_NULL allocator,
3643     VmaDefragmentationContext VMA_NULLABLE context
3644 );
3645 
3646 /** \brief Deprecated. Compacts memory by moving allocations.
3647 
3648 @param pAllocations Array of allocations that can be moved during this compation.
3649 @param allocationCount Number of elements in pAllocations and pAllocationsChanged arrays.
3650 @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.
3651 @param pDefragmentationInfo Configuration parameters. Optional - pass null to use default values.
3652 @param[out] pDefragmentationStats Statistics returned by the function. Optional - pass null if you don't need this information.
3653 @return `VK_SUCCESS` if completed, negative error code in case of error.
3654 
3655 \deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
3656 
3657 This function works by moving allocations to different places (different
3658 `VkDeviceMemory` objects and/or different offsets) in order to optimize memory
3659 usage. Only allocations that are in `pAllocations` array can be moved. All other
3660 allocations are considered nonmovable in this call. Basic rules:
3661 
3662 - Only allocations made in memory types that have
3663   `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`
3664   flags can be compacted. You may pass other allocations but it makes no sense -
3665   these will never be moved.
3666 - Custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT or
3667   #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag are not defragmented. Allocations
3668   passed to this function that come from such pools are ignored.
3669 - Allocations created with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT or
3670   created as dedicated allocations for any other reason are also ignored.
3671 - Both allocations made with or without #VMA_ALLOCATION_CREATE_MAPPED_BIT
3672   flag can be compacted. If not persistently mapped, memory will be mapped
3673   temporarily inside this function if needed.
3674 - You must not pass same #VmaAllocation object multiple times in `pAllocations` array.
3675 
3676 The function also frees empty `VkDeviceMemory` blocks.
3677 
3678 Warning: This function may be time-consuming, so you shouldn't call it too often
3679 (like after every resource creation/destruction).
3680 You can call it on special occasions (like when reloading a game level or
3681 when you just destroyed a lot of objects). Calling it every frame may be OK, but
3682 you should measure that on your platform.
3683 
3684 For more information, see [Defragmentation](@ref defragmentation) chapter.
3685 */
3686 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
3687     VmaAllocator VMA_NOT_NULL allocator,
3688     const VmaAllocation VMA_NOT_NULL * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
3689     size_t allocationCount,
3690     VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged,
3691     const VmaDefragmentationInfo* VMA_NULLABLE pDefragmentationInfo,
3692     VmaDefragmentationStats* VMA_NULLABLE pDefragmentationStats);
3693 
3694 /** \brief Binds buffer to allocation.
3695 
3696 Binds specified buffer to region of memory represented by specified allocation.
3697 Gets `VkDeviceMemory` handle and offset from the allocation.
3698 If you want to create a buffer, allocate memory for it and bind them together separately,
3699 you should use this function for binding instead of standard `vkBindBufferMemory()`,
3700 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
3701 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
3702 (which is illegal in Vulkan).
3703 
3704 It is recommended to use function vmaCreateBuffer() instead of this one.
3705 */
3706 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
3707     VmaAllocator VMA_NOT_NULL allocator,
3708     VmaAllocation VMA_NOT_NULL allocation,
3709     VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer);
3710 
3711 /** \brief Binds buffer to allocation with additional parameters.
3712 
3713 @param allocationLocalOffset Additional offset to be added while binding, relative to the beginnig of the `allocation`. Normally it should be 0.
3714 @param pNext A chain of structures to be attached to `VkBindBufferMemoryInfoKHR` structure used internally. Normally it should be null.
3715 
3716 This function is similar to vmaBindBufferMemory(), but it provides additional parameters.
3717 
3718 If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag
3719 or with VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_1`. Otherwise the call fails.
3720 */
3721 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
3722     VmaAllocator VMA_NOT_NULL allocator,
3723     VmaAllocation VMA_NOT_NULL allocation,
3724     VkDeviceSize allocationLocalOffset,
3725     VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
3726     const void* VMA_NULLABLE pNext);
3727 
3728 /** \brief Binds image to allocation.
3729 
3730 Binds specified image to region of memory represented by specified allocation.
3731 Gets `VkDeviceMemory` handle and offset from the allocation.
3732 If you want to create an image, allocate memory for it and bind them together separately,
3733 you should use this function for binding instead of standard `vkBindImageMemory()`,
3734 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
3735 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
3736 (which is illegal in Vulkan).
3737 
3738 It is recommended to use function vmaCreateImage() instead of this one.
3739 */
3740 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
3741     VmaAllocator VMA_NOT_NULL allocator,
3742     VmaAllocation VMA_NOT_NULL allocation,
3743     VkImage VMA_NOT_NULL_NON_DISPATCHABLE image);
3744 
3745 /** \brief Binds image to allocation with additional parameters.
3746 
3747 @param allocationLocalOffset Additional offset to be added while binding, relative to the beginnig of the `allocation`. Normally it should be 0.
3748 @param pNext A chain of structures to be attached to `VkBindImageMemoryInfoKHR` structure used internally. Normally it should be null.
3749 
3750 This function is similar to vmaBindImageMemory(), but it provides additional parameters.
3751 
3752 If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag
3753 or with VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_1`. Otherwise the call fails.
3754 */
3755 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
3756     VmaAllocator VMA_NOT_NULL allocator,
3757     VmaAllocation VMA_NOT_NULL allocation,
3758     VkDeviceSize allocationLocalOffset,
3759     VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
3760     const void* VMA_NULLABLE pNext);
3761 
3762 /**
3763 @param[out] pBuffer Buffer that was created.
3764 @param[out] pAllocation Allocation that was created.
3765 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
3766 
3767 This function automatically:
3768 
3769 -# Creates buffer.
3770 -# Allocates appropriate memory for it.
3771 -# Binds the buffer with the memory.
3772 
3773 If any of these operations fail, buffer and allocation are not created,
3774 returned value is negative error code, *pBuffer and *pAllocation are null.
3775 
3776 If the function succeeded, you must destroy both buffer and allocation when you
3777 no longer need them using either convenience function vmaDestroyBuffer() or
3778 separately, using `vkDestroyBuffer()` and vmaFreeMemory().
3779 
3780 If VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used,
3781 VK_KHR_dedicated_allocation extension is used internally to query driver whether
3782 it requires or prefers the new buffer to have dedicated allocation. If yes,
3783 and if dedicated allocation is possible (VmaAllocationCreateInfo::pool is null
3784 and VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated
3785 allocation for this buffer, just like when using
3786 VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
3787 */
3788 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
3789     VmaAllocator VMA_NOT_NULL allocator,
3790     const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
3791     const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
3792     VkBuffer VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pBuffer,
3793     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3794     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3795 
3796 /** \brief Destroys Vulkan buffer and frees allocated memory.
3797 
3798 This is just a convenience function equivalent to:
3799 
3800 \code
3801 vkDestroyBuffer(device, buffer, allocationCallbacks);
3802 vmaFreeMemory(allocator, allocation);
3803 \endcode
3804 
3805 It it safe to pass null as buffer and/or allocation.
3806 */
3807 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
3808     VmaAllocator VMA_NOT_NULL allocator,
3809     VkBuffer VMA_NULLABLE_NON_DISPATCHABLE buffer,
3810     VmaAllocation VMA_NULLABLE allocation);
3811 
3812 /// Function similar to vmaCreateBuffer().
3813 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
3814     VmaAllocator VMA_NOT_NULL allocator,
3815     const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
3816     const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
3817     VkImage VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pImage,
3818     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3819     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3820 
3821 /** \brief Destroys Vulkan image and frees allocated memory.
3822 
3823 This is just a convenience function equivalent to:
3824 
3825 \code
3826 vkDestroyImage(device, image, allocationCallbacks);
3827 vmaFreeMemory(allocator, allocation);
3828 \endcode
3829 
3830 It it safe to pass null as image and/or allocation.
3831 */
3832 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
3833     VmaAllocator VMA_NOT_NULL allocator,
3834     VkImage VMA_NULLABLE_NON_DISPATCHABLE image,
3835     VmaAllocation VMA_NULLABLE allocation);
3836 
3837 #ifdef __cplusplus
3838 }
3839 #endif
3840 
3841 #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
3842 
3843 // For Visual Studio IntelliSense.
3844 #if defined(__cplusplus) && defined(__INTELLISENSE__)
3845 #define VMA_IMPLEMENTATION
3846 #endif
3847 
3848 #ifdef VMA_IMPLEMENTATION
3849 #undef VMA_IMPLEMENTATION
3850 
3851 #include <cstdint>
3852 #include <cstdlib>
3853 #include <cstring>
3854 #include <utility>
3855 
3856 /*******************************************************************************
3857 CONFIGURATION SECTION
3858 
3859 Define some of these macros before each #include of this header or change them
3860 here if you need other then default behavior depending on your environment.
3861 */
3862 
3863 /*
3864 Define this macro to 1 to make the library fetch pointers to Vulkan functions
3865 internally, like:
3866 
3867     vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
3868 */
3869 #if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
3870     #define VMA_STATIC_VULKAN_FUNCTIONS 1
3871 #endif
3872 
3873 /*
3874 Define this macro to 1 to make the library fetch pointers to Vulkan functions
3875 internally, like:
3876 
3877     vulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkGetDeviceProcAddr(m_hDevice, vkAllocateMemory);
3878 */
3879 #if !defined(VMA_DYNAMIC_VULKAN_FUNCTIONS)
3880     #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
3881 #endif
3882 
3883 // Define this macro to 1 to make the library use STL containers instead of its own implementation.
3884 //#define VMA_USE_STL_CONTAINERS 1
3885 
3886 /* Set this macro to 1 to make the library including and using STL containers:
3887 std::pair, std::vector, std::list, std::unordered_map.
3888 
3889 Set it to 0 or undefined to make the library using its own implementation of
3890 the containers.
3891 */
3892 #if VMA_USE_STL_CONTAINERS
3893    #define VMA_USE_STL_VECTOR 1
3894    #define VMA_USE_STL_UNORDERED_MAP 1
3895    #define VMA_USE_STL_LIST 1
3896 #endif
3897 
3898 #ifndef VMA_USE_STL_SHARED_MUTEX
3899     // Compiler conforms to C++17.
3900     #if __cplusplus >= 201703L
3901         #define VMA_USE_STL_SHARED_MUTEX 1
3902     // Visual studio defines __cplusplus properly only when passed additional parameter: /Zc:__cplusplus
3903     // Otherwise it's always 199711L, despite shared_mutex works since Visual Studio 2015 Update 2.
3904     // See: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/
3905     #elif defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && __cplusplus == 199711L && _MSVC_LANG >= 201703L
3906         #define VMA_USE_STL_SHARED_MUTEX 1
3907     #else
3908         #define VMA_USE_STL_SHARED_MUTEX 0
3909     #endif
3910 #endif
3911 
3912 /*
3913 THESE INCLUDES ARE NOT ENABLED BY DEFAULT.
3914 Library has its own container implementation.
3915 */
3916 #if VMA_USE_STL_VECTOR
3917    #include <vector>
3918 #endif
3919 
3920 #if VMA_USE_STL_UNORDERED_MAP
3921    #include <unordered_map>
3922 #endif
3923 
3924 #if VMA_USE_STL_LIST
3925    #include <list>
3926 #endif
3927 
3928 /*
3929 Following headers are used in this CONFIGURATION section only, so feel free to
3930 remove them if not needed.
3931 */
3932 #include <cassert> // for assert
3933 #include <algorithm> // for min, max
3934 #include <mutex>
3935 
3936 #ifndef VMA_NULL
3937    // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
3938    #define VMA_NULL   nullptr
3939 #endif
3940 
3941 #if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
3942 #include <cstdlib>
aligned_alloc(size_t alignment,size_t size)3943 void *aligned_alloc(size_t alignment, size_t size)
3944 {
3945     // alignment must be >= sizeof(void*)
3946     if(alignment < sizeof(void*))
3947     {
3948         alignment = sizeof(void*);
3949     }
3950 
3951     return memalign(alignment, size);
3952 }
3953 #elif defined(__APPLE__) || defined(__ANDROID__) || (defined(__linux__) && defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC))
3954 #include <cstdlib>
aligned_alloc(size_t alignment,size_t size)3955 void *aligned_alloc(size_t alignment, size_t size)
3956 {
3957     // alignment must be >= sizeof(void*)
3958     if(alignment < sizeof(void*))
3959     {
3960         alignment = sizeof(void*);
3961     }
3962 
3963     void *pointer;
3964     if(posix_memalign(&pointer, alignment, size) == 0)
3965         return pointer;
3966     return VMA_NULL;
3967 }
3968 #endif
3969 
3970 // If your compiler is not compatible with C++11 and definition of
3971 // aligned_alloc() function is missing, uncommeting following line may help:
3972 
3973 //#include <malloc.h>
3974 
3975 // Normal assert to check for programmer's errors, especially in Debug configuration.
3976 #ifndef VMA_ASSERT
3977    #ifdef NDEBUG
3978        #define VMA_ASSERT(expr)
3979    #else
3980        #define VMA_ASSERT(expr)         assert(expr)
3981    #endif
3982 #endif
3983 
3984 // Assert that will be called very often, like inside data structures e.g. operator[].
3985 // Making it non-empty can make program slow.
3986 #ifndef VMA_HEAVY_ASSERT
3987    #ifdef NDEBUG
3988        #define VMA_HEAVY_ASSERT(expr)
3989    #else
3990        #define VMA_HEAVY_ASSERT(expr)   //VMA_ASSERT(expr)
3991    #endif
3992 #endif
3993 
3994 #ifndef VMA_ALIGN_OF
3995    #define VMA_ALIGN_OF(type)       (__alignof(type))
3996 #endif
3997 
3998 #ifndef VMA_SYSTEM_ALIGNED_MALLOC
3999    #if defined(_WIN32)
4000        #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment)   (_aligned_malloc((size), (alignment)))
4001    #else
4002        #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment)   (aligned_alloc((alignment), (size) ))
4003    #endif
4004 #endif
4005 
4006 #ifndef VMA_SYSTEM_FREE
4007    #if defined(_WIN32)
4008        #define VMA_SYSTEM_FREE(ptr)   _aligned_free(ptr)
4009    #else
4010        #define VMA_SYSTEM_FREE(ptr)   free(ptr)
4011    #endif
4012 #endif
4013 
4014 #ifndef VMA_MIN
4015    #define VMA_MIN(v1, v2)    (std::min((v1), (v2)))
4016 #endif
4017 
4018 #ifndef VMA_MAX
4019    #define VMA_MAX(v1, v2)    (std::max((v1), (v2)))
4020 #endif
4021 
4022 #ifndef VMA_SWAP
4023    #define VMA_SWAP(v1, v2)   std::swap((v1), (v2))
4024 #endif
4025 
4026 #ifndef VMA_SORT
4027    #define VMA_SORT(beg, end, cmp)  std::sort(beg, end, cmp)
4028 #endif
4029 
4030 #ifndef VMA_DEBUG_LOG
4031    #define VMA_DEBUG_LOG(format, ...)
4032    /*
4033    #define VMA_DEBUG_LOG(format, ...) do { \
4034        printf(format, __VA_ARGS__); \
4035        printf("\n"); \
4036    } while(false)
4037    */
4038 #endif
4039 
4040 // Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
4041 #if VMA_STATS_STRING_ENABLED
VmaUint32ToStr(char * outStr,size_t strLen,uint32_t num)4042     static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num)
4043     {
4044         snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
4045     }
VmaUint64ToStr(char * outStr,size_t strLen,uint64_t num)4046     static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
4047     {
4048         snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
4049     }
VmaPtrToStr(char * outStr,size_t strLen,const void * ptr)4050     static inline void VmaPtrToStr(char* outStr, size_t strLen, const void* ptr)
4051     {
4052         snprintf(outStr, strLen, "%p", ptr);
4053     }
4054 #endif
4055 
4056 #ifndef VMA_MUTEX
4057     class VmaMutex
4058     {
4059     public:
Lock()4060         void Lock() { m_Mutex.lock(); }
Unlock()4061         void Unlock() { m_Mutex.unlock(); }
TryLock()4062         bool TryLock() { return m_Mutex.try_lock(); }
4063     private:
4064         std::mutex m_Mutex;
4065     };
4066     #define VMA_MUTEX VmaMutex
4067 #endif
4068 
4069 // Read-write mutex, where "read" is shared access, "write" is exclusive access.
4070 #ifndef VMA_RW_MUTEX
4071     #if VMA_USE_STL_SHARED_MUTEX
4072         // Use std::shared_mutex from C++17.
4073         #include <shared_mutex>
4074         class VmaRWMutex
4075         {
4076         public:
LockRead()4077             void LockRead() { m_Mutex.lock_shared(); }
UnlockRead()4078             void UnlockRead() { m_Mutex.unlock_shared(); }
TryLockRead()4079             bool TryLockRead() { return m_Mutex.try_lock_shared(); }
LockWrite()4080             void LockWrite() { m_Mutex.lock(); }
UnlockWrite()4081             void UnlockWrite() { m_Mutex.unlock(); }
TryLockWrite()4082             bool TryLockWrite() { return m_Mutex.try_lock(); }
4083         private:
4084             std::shared_mutex m_Mutex;
4085         };
4086         #define VMA_RW_MUTEX VmaRWMutex
4087     #elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600
4088         // Use SRWLOCK from WinAPI.
4089         // Minimum supported client = Windows Vista, server = Windows Server 2008.
4090         class VmaRWMutex
4091         {
4092         public:
VmaRWMutex()4093             VmaRWMutex() { InitializeSRWLock(&m_Lock); }
LockRead()4094             void LockRead() { AcquireSRWLockShared(&m_Lock); }
UnlockRead()4095             void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
TryLockRead()4096             bool TryLockRead() { return TryAcquireSRWLockShared(&m_Lock) != FALSE; }
LockWrite()4097             void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
UnlockWrite()4098             void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
TryLockWrite()4099             bool TryLockWrite() { return TryAcquireSRWLockExclusive(&m_Lock) != FALSE; }
4100         private:
4101             SRWLOCK m_Lock;
4102         };
4103         #define VMA_RW_MUTEX VmaRWMutex
4104     #else
4105         // Less efficient fallback: Use normal mutex.
4106         class VmaRWMutex
4107         {
4108         public:
LockRead()4109             void LockRead() { m_Mutex.Lock(); }
UnlockRead()4110             void UnlockRead() { m_Mutex.Unlock(); }
TryLockRead()4111             bool TryLockRead() { return m_Mutex.TryLock(); }
LockWrite()4112             void LockWrite() { m_Mutex.Lock(); }
UnlockWrite()4113             void UnlockWrite() { m_Mutex.Unlock(); }
TryLockWrite()4114             bool TryLockWrite() { return m_Mutex.TryLock(); }
4115         private:
4116             VMA_MUTEX m_Mutex;
4117         };
4118         #define VMA_RW_MUTEX VmaRWMutex
4119     #endif // #if VMA_USE_STL_SHARED_MUTEX
4120 #endif // #ifndef VMA_RW_MUTEX
4121 
4122 /*
4123 If providing your own implementation, you need to implement a subset of std::atomic.
4124 */
4125 #ifndef VMA_ATOMIC_UINT32
4126     #include <atomic>
4127     #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
4128 #endif
4129 
4130 #ifndef VMA_ATOMIC_UINT64
4131     #include <atomic>
4132     #define VMA_ATOMIC_UINT64 std::atomic<uint64_t>
4133 #endif
4134 
4135 #ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
4136     /**
4137     Every allocation will have its own memory block.
4138     Define to 1 for debugging purposes only.
4139     */
4140     #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
4141 #endif
4142 
4143 #ifndef VMA_DEBUG_ALIGNMENT
4144     /**
4145     Minimum alignment of all allocations, in bytes.
4146     Set to more than 1 for debugging purposes only. Must be power of two.
4147     */
4148     #define VMA_DEBUG_ALIGNMENT (1)
4149 #endif
4150 
4151 #ifndef VMA_DEBUG_MARGIN
4152     /**
4153     Minimum margin before and after every allocation, in bytes.
4154     Set nonzero for debugging purposes only.
4155     */
4156     #define VMA_DEBUG_MARGIN (0)
4157 #endif
4158 
4159 #ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
4160     /**
4161     Define this macro to 1 to automatically fill new allocations and destroyed
4162     allocations with some bit pattern.
4163     */
4164     #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)
4165 #endif
4166 
4167 #ifndef VMA_DEBUG_DETECT_CORRUPTION
4168     /**
4169     Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to
4170     enable writing magic value to the margin before and after every allocation and
4171     validating it, so that memory corruptions (out-of-bounds writes) are detected.
4172     */
4173     #define VMA_DEBUG_DETECT_CORRUPTION (0)
4174 #endif
4175 
4176 #ifndef VMA_DEBUG_GLOBAL_MUTEX
4177     /**
4178     Set this to 1 for debugging purposes only, to enable single mutex protecting all
4179     entry calls to the library. Can be useful for debugging multithreading issues.
4180     */
4181     #define VMA_DEBUG_GLOBAL_MUTEX (0)
4182 #endif
4183 
4184 #ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
4185     /**
4186     Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.
4187     Set to more than 1 for debugging purposes only. Must be power of two.
4188     */
4189     #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
4190 #endif
4191 
4192 #ifndef VMA_SMALL_HEAP_MAX_SIZE
4193    /// Maximum size of a memory heap in Vulkan to consider it "small".
4194    #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
4195 #endif
4196 
4197 #ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
4198    /// Default size of a block allocated as single VkDeviceMemory from a "large" heap.
4199    #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
4200 #endif
4201 
4202 #ifndef VMA_CLASS_NO_COPY
4203     #define VMA_CLASS_NO_COPY(className) \
4204         private: \
4205             className(const className&) = delete; \
4206             className& operator=(const className&) = delete;
4207 #endif
4208 
4209 static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX;
4210 
4211 // Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.
4212 static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
4213 
4214 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED   = 0xDC;
4215 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
4216 
4217 /*******************************************************************************
4218 END OF CONFIGURATION
4219 */
4220 
4221 // # Copy of some Vulkan definitions so we don't need to check their existence just to handle few constants.
4222 
4223 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY = 0x00000040;
4224 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY = 0x00000080;
4225 static const uint32_t VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY = 0x00020000;
4226 
4227 static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
4228 
4229 static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
4230     VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
4231 
4232 // Returns number of bits set to 1 in (v).
VmaCountBitsSet(uint32_t v)4233 static inline uint32_t VmaCountBitsSet(uint32_t v)
4234 {
4235     uint32_t c = v - ((v >> 1) & 0x55555555);
4236     c = ((c >>  2) & 0x33333333) + (c & 0x33333333);
4237     c = ((c >>  4) + c) & 0x0F0F0F0F;
4238     c = ((c >>  8) + c) & 0x00FF00FF;
4239     c = ((c >> 16) + c) & 0x0000FFFF;
4240     return c;
4241 }
4242 
4243 // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
4244 // Use types like uint32_t, uint64_t as T.
4245 template <typename T>
VmaAlignUp(T val,T align)4246 static inline T VmaAlignUp(T val, T align)
4247 {
4248     return (val + align - 1) / align * align;
4249 }
4250 // Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8.
4251 // Use types like uint32_t, uint64_t as T.
4252 template <typename T>
VmaAlignDown(T val,T align)4253 static inline T VmaAlignDown(T val, T align)
4254 {
4255     return val / align * align;
4256 }
4257 
4258 // Division with mathematical rounding to nearest number.
4259 template <typename T>
VmaRoundDiv(T x,T y)4260 static inline T VmaRoundDiv(T x, T y)
4261 {
4262     return (x + (y / (T)2)) / y;
4263 }
4264 
4265 /*
4266 Returns true if given number is a power of two.
4267 T must be unsigned integer number or signed integer but always nonnegative.
4268 For 0 returns true.
4269 */
4270 template <typename T>
VmaIsPow2(T x)4271 inline bool VmaIsPow2(T x)
4272 {
4273     return (x & (x-1)) == 0;
4274 }
4275 
4276 // Returns smallest power of 2 greater or equal to v.
VmaNextPow2(uint32_t v)4277 static inline uint32_t VmaNextPow2(uint32_t v)
4278 {
4279     v--;
4280     v |= v >> 1;
4281     v |= v >> 2;
4282     v |= v >> 4;
4283     v |= v >> 8;
4284     v |= v >> 16;
4285     v++;
4286     return v;
4287 }
VmaNextPow2(uint64_t v)4288 static inline uint64_t VmaNextPow2(uint64_t v)
4289 {
4290     v--;
4291     v |= v >> 1;
4292     v |= v >> 2;
4293     v |= v >> 4;
4294     v |= v >> 8;
4295     v |= v >> 16;
4296     v |= v >> 32;
4297     v++;
4298     return v;
4299 }
4300 
4301 // Returns largest power of 2 less or equal to v.
VmaPrevPow2(uint32_t v)4302 static inline uint32_t VmaPrevPow2(uint32_t v)
4303 {
4304     v |= v >> 1;
4305     v |= v >> 2;
4306     v |= v >> 4;
4307     v |= v >> 8;
4308     v |= v >> 16;
4309     v = v ^ (v >> 1);
4310     return v;
4311 }
VmaPrevPow2(uint64_t v)4312 static inline uint64_t VmaPrevPow2(uint64_t v)
4313 {
4314     v |= v >> 1;
4315     v |= v >> 2;
4316     v |= v >> 4;
4317     v |= v >> 8;
4318     v |= v >> 16;
4319     v |= v >> 32;
4320     v = v ^ (v >> 1);
4321     return v;
4322 }
4323 
VmaStrIsEmpty(const char * pStr)4324 static inline bool VmaStrIsEmpty(const char* pStr)
4325 {
4326     return pStr == VMA_NULL || *pStr == '\0';
4327 }
4328 
4329 #if VMA_STATS_STRING_ENABLED
4330 
VmaAlgorithmToStr(uint32_t algorithm)4331 static const char* VmaAlgorithmToStr(uint32_t algorithm)
4332 {
4333     switch(algorithm)
4334     {
4335     case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
4336         return "Linear";
4337     case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
4338         return "Buddy";
4339     case 0:
4340         return "Default";
4341     default:
4342         VMA_ASSERT(0);
4343         return "";
4344     }
4345 }
4346 
4347 #endif // #if VMA_STATS_STRING_ENABLED
4348 
4349 #ifndef VMA_SORT
4350 
4351 template<typename Iterator, typename Compare>
VmaQuickSortPartition(Iterator beg,Iterator end,Compare cmp)4352 Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
4353 {
4354     Iterator centerValue = end; --centerValue;
4355     Iterator insertIndex = beg;
4356     for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
4357     {
4358         if(cmp(*memTypeIndex, *centerValue))
4359         {
4360             if(insertIndex != memTypeIndex)
4361             {
4362                 VMA_SWAP(*memTypeIndex, *insertIndex);
4363             }
4364             ++insertIndex;
4365         }
4366     }
4367     if(insertIndex != centerValue)
4368     {
4369         VMA_SWAP(*insertIndex, *centerValue);
4370     }
4371     return insertIndex;
4372 }
4373 
4374 template<typename Iterator, typename Compare>
VmaQuickSort(Iterator beg,Iterator end,Compare cmp)4375 void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
4376 {
4377     if(beg < end)
4378     {
4379         Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
4380         VmaQuickSort<Iterator, Compare>(beg, it, cmp);
4381         VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
4382     }
4383 }
4384 
4385 #define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
4386 
4387 #endif // #ifndef VMA_SORT
4388 
4389 /*
4390 Returns true if two memory blocks occupy overlapping pages.
4391 ResourceA must be in less memory offset than ResourceB.
4392 
4393 Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
4394 chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
4395 */
VmaBlocksOnSamePage(VkDeviceSize resourceAOffset,VkDeviceSize resourceASize,VkDeviceSize resourceBOffset,VkDeviceSize pageSize)4396 static inline bool VmaBlocksOnSamePage(
4397     VkDeviceSize resourceAOffset,
4398     VkDeviceSize resourceASize,
4399     VkDeviceSize resourceBOffset,
4400     VkDeviceSize pageSize)
4401 {
4402     VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
4403     VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
4404     VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
4405     VkDeviceSize resourceBStart = resourceBOffset;
4406     VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
4407     return resourceAEndPage == resourceBStartPage;
4408 }
4409 
4410 enum VmaSuballocationType
4411 {
4412     VMA_SUBALLOCATION_TYPE_FREE = 0,
4413     VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
4414     VMA_SUBALLOCATION_TYPE_BUFFER = 2,
4415     VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
4416     VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
4417     VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
4418     VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
4419 };
4420 
4421 /*
4422 Returns true if given suballocation types could conflict and must respect
4423 VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
4424 or linear image and another one is optimal image. If type is unknown, behave
4425 conservatively.
4426 */
VmaIsBufferImageGranularityConflict(VmaSuballocationType suballocType1,VmaSuballocationType suballocType2)4427 static inline bool VmaIsBufferImageGranularityConflict(
4428     VmaSuballocationType suballocType1,
4429     VmaSuballocationType suballocType2)
4430 {
4431     if(suballocType1 > suballocType2)
4432     {
4433         VMA_SWAP(suballocType1, suballocType2);
4434     }
4435 
4436     switch(suballocType1)
4437     {
4438     case VMA_SUBALLOCATION_TYPE_FREE:
4439         return false;
4440     case VMA_SUBALLOCATION_TYPE_UNKNOWN:
4441         return true;
4442     case VMA_SUBALLOCATION_TYPE_BUFFER:
4443         return
4444             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
4445             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4446     case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
4447         return
4448             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
4449             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
4450             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4451     case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
4452         return
4453             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4454     case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
4455         return false;
4456     default:
4457         VMA_ASSERT(0);
4458         return true;
4459     }
4460 }
4461 
VmaWriteMagicValue(void * pData,VkDeviceSize offset)4462 static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
4463 {
4464 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
4465     uint32_t* pDst = (uint32_t*)((char*)pData + offset);
4466     const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
4467     for(size_t i = 0; i < numberCount; ++i, ++pDst)
4468     {
4469         *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
4470     }
4471 #else
4472     // no-op
4473 #endif
4474 }
4475 
VmaValidateMagicValue(const void * pData,VkDeviceSize offset)4476 static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
4477 {
4478 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
4479     const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
4480     const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
4481     for(size_t i = 0; i < numberCount; ++i, ++pSrc)
4482     {
4483         if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
4484         {
4485             return false;
4486         }
4487     }
4488 #endif
4489     return true;
4490 }
4491 
4492 /*
4493 Fills structure with parameters of an example buffer to be used for transfers
4494 during GPU memory defragmentation.
4495 */
VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo & outBufCreateInfo)4496 static void VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo& outBufCreateInfo)
4497 {
4498     memset(&outBufCreateInfo, 0, sizeof(outBufCreateInfo));
4499     outBufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
4500     outBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
4501     outBufCreateInfo.size = (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE; // Example size.
4502 }
4503 
4504 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
4505 struct VmaMutexLock
4506 {
VMA_CLASS_NO_COPYVmaMutexLock4507     VMA_CLASS_NO_COPY(VmaMutexLock)
4508 public:
4509     VmaMutexLock(VMA_MUTEX& mutex, bool useMutex = true) :
4510         m_pMutex(useMutex ? &mutex : VMA_NULL)
4511     { if(m_pMutex) { m_pMutex->Lock(); } }
~VmaMutexLockVmaMutexLock4512     ~VmaMutexLock()
4513     { if(m_pMutex) { m_pMutex->Unlock(); } }
4514 private:
4515     VMA_MUTEX* m_pMutex;
4516 };
4517 
4518 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
4519 struct VmaMutexLockRead
4520 {
VMA_CLASS_NO_COPYVmaMutexLockRead4521     VMA_CLASS_NO_COPY(VmaMutexLockRead)
4522 public:
4523     VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
4524         m_pMutex(useMutex ? &mutex : VMA_NULL)
4525     { if(m_pMutex) { m_pMutex->LockRead(); } }
~VmaMutexLockReadVmaMutexLockRead4526     ~VmaMutexLockRead() { if(m_pMutex) { m_pMutex->UnlockRead(); } }
4527 private:
4528     VMA_RW_MUTEX* m_pMutex;
4529 };
4530 
4531 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
4532 struct VmaMutexLockWrite
4533 {
VMA_CLASS_NO_COPYVmaMutexLockWrite4534     VMA_CLASS_NO_COPY(VmaMutexLockWrite)
4535 public:
4536     VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) :
4537         m_pMutex(useMutex ? &mutex : VMA_NULL)
4538     { if(m_pMutex) { m_pMutex->LockWrite(); } }
~VmaMutexLockWriteVmaMutexLockWrite4539     ~VmaMutexLockWrite() { if(m_pMutex) { m_pMutex->UnlockWrite(); } }
4540 private:
4541     VMA_RW_MUTEX* m_pMutex;
4542 };
4543 
4544 #if VMA_DEBUG_GLOBAL_MUTEX
4545     static VMA_MUTEX gDebugGlobalMutex;
4546     #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
4547 #else
4548     #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
4549 #endif
4550 
4551 // Minimum size of a free suballocation to register it in the free suballocation collection.
4552 static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
4553 
4554 /*
4555 Performs binary search and returns iterator to first element that is greater or
4556 equal to (key), according to comparison (cmp).
4557 
4558 Cmp should return true if first argument is less than second argument.
4559 
4560 Returned value is the found element, if present in the collection or place where
4561 new element with value (key) should be inserted.
4562 */
4563 template <typename CmpLess, typename IterT, typename KeyT>
VmaBinaryFindFirstNotLess(IterT beg,IterT end,const KeyT & key,const CmpLess & cmp)4564 static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, const CmpLess& cmp)
4565 {
4566     size_t down = 0, up = (end - beg);
4567     while(down < up)
4568     {
4569         const size_t mid = (down + up) / 2;
4570         if(cmp(*(beg+mid), key))
4571         {
4572             down = mid + 1;
4573         }
4574         else
4575         {
4576             up = mid;
4577         }
4578     }
4579     return beg + down;
4580 }
4581 
4582 template<typename CmpLess, typename IterT, typename KeyT>
VmaBinaryFindSorted(const IterT & beg,const IterT & end,const KeyT & value,const CmpLess & cmp)4583 IterT VmaBinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp)
4584 {
4585     IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
4586         beg, end, value, cmp);
4587     if(it == end ||
4588         (!cmp(*it, value) && !cmp(value, *it)))
4589     {
4590         return it;
4591     }
4592     return end;
4593 }
4594 
4595 /*
4596 Returns true if all pointers in the array are not-null and unique.
4597 Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
4598 T must be pointer type, e.g. VmaAllocation, VmaPool.
4599 */
4600 template<typename T>
VmaValidatePointerArray(uint32_t count,const T * arr)4601 static bool VmaValidatePointerArray(uint32_t count, const T* arr)
4602 {
4603     for(uint32_t i = 0; i < count; ++i)
4604     {
4605         const T iPtr = arr[i];
4606         if(iPtr == VMA_NULL)
4607         {
4608             return false;
4609         }
4610         for(uint32_t j = i + 1; j < count; ++j)
4611         {
4612             if(iPtr == arr[j])
4613             {
4614                 return false;
4615             }
4616         }
4617     }
4618     return true;
4619 }
4620 
4621 template<typename MainT, typename NewT>
VmaPnextChainPushFront(MainT * mainStruct,NewT * newStruct)4622 static inline void VmaPnextChainPushFront(MainT* mainStruct, NewT* newStruct)
4623 {
4624     newStruct->pNext = mainStruct->pNext;
4625     mainStruct->pNext = newStruct;
4626 }
4627 
4628 ////////////////////////////////////////////////////////////////////////////////
4629 // Memory allocation
4630 
VmaMalloc(const VkAllocationCallbacks * pAllocationCallbacks,size_t size,size_t alignment)4631 static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
4632 {
4633     if((pAllocationCallbacks != VMA_NULL) &&
4634         (pAllocationCallbacks->pfnAllocation != VMA_NULL))
4635     {
4636         return (*pAllocationCallbacks->pfnAllocation)(
4637             pAllocationCallbacks->pUserData,
4638             size,
4639             alignment,
4640             VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
4641     }
4642     else
4643     {
4644         return VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
4645     }
4646 }
4647 
VmaFree(const VkAllocationCallbacks * pAllocationCallbacks,void * ptr)4648 static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
4649 {
4650     if((pAllocationCallbacks != VMA_NULL) &&
4651         (pAllocationCallbacks->pfnFree != VMA_NULL))
4652     {
4653         (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
4654     }
4655     else
4656     {
4657         VMA_SYSTEM_FREE(ptr);
4658     }
4659 }
4660 
4661 template<typename T>
VmaAllocate(const VkAllocationCallbacks * pAllocationCallbacks)4662 static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
4663 {
4664     return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
4665 }
4666 
4667 template<typename T>
VmaAllocateArray(const VkAllocationCallbacks * pAllocationCallbacks,size_t count)4668 static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
4669 {
4670     return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
4671 }
4672 
4673 #define vma_new(allocator, type)   new(VmaAllocate<type>(allocator))(type)
4674 
4675 #define vma_new_array(allocator, type, count)   new(VmaAllocateArray<type>((allocator), (count)))(type)
4676 
4677 template<typename T>
vma_delete(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr)4678 static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
4679 {
4680     ptr->~T();
4681     VmaFree(pAllocationCallbacks, ptr);
4682 }
4683 
4684 template<typename T>
vma_delete_array(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr,size_t count)4685 static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
4686 {
4687     if(ptr != VMA_NULL)
4688     {
4689         for(size_t i = count; i--; )
4690         {
4691             ptr[i].~T();
4692         }
4693         VmaFree(pAllocationCallbacks, ptr);
4694     }
4695 }
4696 
VmaCreateStringCopy(const VkAllocationCallbacks * allocs,const char * srcStr)4697 static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr)
4698 {
4699     if(srcStr != VMA_NULL)
4700     {
4701         const size_t len = strlen(srcStr);
4702         char* const result = vma_new_array(allocs, char, len + 1);
4703         memcpy(result, srcStr, len + 1);
4704         return result;
4705     }
4706     else
4707     {
4708         return VMA_NULL;
4709     }
4710 }
4711 
VmaFreeString(const VkAllocationCallbacks * allocs,char * str)4712 static void VmaFreeString(const VkAllocationCallbacks* allocs, char* str)
4713 {
4714     if(str != VMA_NULL)
4715     {
4716         const size_t len = strlen(str);
4717         vma_delete_array(allocs, str, len + 1);
4718     }
4719 }
4720 
4721 // STL-compatible allocator.
4722 template<typename T>
4723 class VmaStlAllocator
4724 {
4725 public:
4726     const VkAllocationCallbacks* const m_pCallbacks;
4727     typedef T value_type;
4728 
VmaStlAllocator(const VkAllocationCallbacks * pCallbacks)4729     VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
VmaStlAllocator(const VmaStlAllocator<U> & src)4730     template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
4731 
allocate(size_t n)4732     T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
deallocate(T * p,size_t n)4733     void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
4734 
4735     template<typename U>
4736     bool operator==(const VmaStlAllocator<U>& rhs) const
4737     {
4738         return m_pCallbacks == rhs.m_pCallbacks;
4739     }
4740     template<typename U>
4741     bool operator!=(const VmaStlAllocator<U>& rhs) const
4742     {
4743         return m_pCallbacks != rhs.m_pCallbacks;
4744     }
4745 
4746     VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
4747 };
4748 
4749 #if VMA_USE_STL_VECTOR
4750 
4751 #define VmaVector std::vector
4752 
4753 template<typename T, typename allocatorT>
VmaVectorInsert(std::vector<T,allocatorT> & vec,size_t index,const T & item)4754 static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
4755 {
4756     vec.insert(vec.begin() + index, item);
4757 }
4758 
4759 template<typename T, typename allocatorT>
VmaVectorRemove(std::vector<T,allocatorT> & vec,size_t index)4760 static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
4761 {
4762     vec.erase(vec.begin() + index);
4763 }
4764 
4765 #else // #if VMA_USE_STL_VECTOR
4766 
4767 /* Class with interface compatible with subset of std::vector.
4768 T must be POD because constructors and destructors are not called and memcpy is
4769 used for these objects. */
4770 template<typename T, typename AllocatorT>
4771 class VmaVector
4772 {
4773 public:
4774     typedef T value_type;
4775 
VmaVector(const AllocatorT & allocator)4776     VmaVector(const AllocatorT& allocator) :
4777         m_Allocator(allocator),
4778         m_pArray(VMA_NULL),
4779         m_Count(0),
4780         m_Capacity(0)
4781     {
4782     }
4783 
VmaVector(size_t count,const AllocatorT & allocator)4784     VmaVector(size_t count, const AllocatorT& allocator) :
4785         m_Allocator(allocator),
4786         m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
4787         m_Count(count),
4788         m_Capacity(count)
4789     {
4790     }
4791 
4792     // This version of the constructor is here for compatibility with pre-C++14 std::vector.
4793     // value is unused.
VmaVector(size_t count,const T & value,const AllocatorT & allocator)4794     VmaVector(size_t count, const T& value, const AllocatorT& allocator)
4795         : VmaVector(count, allocator) {}
4796 
VmaVector(const VmaVector<T,AllocatorT> & src)4797     VmaVector(const VmaVector<T, AllocatorT>& src) :
4798         m_Allocator(src.m_Allocator),
4799         m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
4800         m_Count(src.m_Count),
4801         m_Capacity(src.m_Count)
4802     {
4803         if(m_Count != 0)
4804         {
4805             memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
4806         }
4807     }
4808 
~VmaVector()4809     ~VmaVector()
4810     {
4811         VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4812     }
4813 
4814     VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
4815     {
4816         if(&rhs != this)
4817         {
4818             resize(rhs.m_Count);
4819             if(m_Count != 0)
4820             {
4821                 memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
4822             }
4823         }
4824         return *this;
4825     }
4826 
empty()4827     bool empty() const { return m_Count == 0; }
size()4828     size_t size() const { return m_Count; }
data()4829     T* data() { return m_pArray; }
data()4830     const T* data() const { return m_pArray; }
4831 
4832     T& operator[](size_t index)
4833     {
4834         VMA_HEAVY_ASSERT(index < m_Count);
4835         return m_pArray[index];
4836     }
4837     const T& operator[](size_t index) const
4838     {
4839         VMA_HEAVY_ASSERT(index < m_Count);
4840         return m_pArray[index];
4841     }
4842 
front()4843     T& front()
4844     {
4845         VMA_HEAVY_ASSERT(m_Count > 0);
4846         return m_pArray[0];
4847     }
front()4848     const T& front() const
4849     {
4850         VMA_HEAVY_ASSERT(m_Count > 0);
4851         return m_pArray[0];
4852     }
back()4853     T& back()
4854     {
4855         VMA_HEAVY_ASSERT(m_Count > 0);
4856         return m_pArray[m_Count - 1];
4857     }
back()4858     const T& back() const
4859     {
4860         VMA_HEAVY_ASSERT(m_Count > 0);
4861         return m_pArray[m_Count - 1];
4862     }
4863 
4864     void reserve(size_t newCapacity, bool freeMemory = false)
4865     {
4866         newCapacity = VMA_MAX(newCapacity, m_Count);
4867 
4868         if((newCapacity < m_Capacity) && !freeMemory)
4869         {
4870             newCapacity = m_Capacity;
4871         }
4872 
4873         if(newCapacity != m_Capacity)
4874         {
4875             T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
4876             if(m_Count != 0)
4877             {
4878                 memcpy(newArray, m_pArray, m_Count * sizeof(T));
4879             }
4880             VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4881             m_Capacity = newCapacity;
4882             m_pArray = newArray;
4883         }
4884     }
4885 
4886     void resize(size_t newCount, bool freeMemory = false)
4887     {
4888         size_t newCapacity = m_Capacity;
4889         if(newCount > m_Capacity)
4890         {
4891             newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
4892         }
4893         else if(freeMemory)
4894         {
4895             newCapacity = newCount;
4896         }
4897 
4898         if(newCapacity != m_Capacity)
4899         {
4900             T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
4901             const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
4902             if(elementsToCopy != 0)
4903             {
4904                 memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
4905             }
4906             VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4907             m_Capacity = newCapacity;
4908             m_pArray = newArray;
4909         }
4910 
4911         m_Count = newCount;
4912     }
4913 
4914     void clear(bool freeMemory = false)
4915     {
4916         resize(0, freeMemory);
4917     }
4918 
insert(size_t index,const T & src)4919     void insert(size_t index, const T& src)
4920     {
4921         VMA_HEAVY_ASSERT(index <= m_Count);
4922         const size_t oldCount = size();
4923         resize(oldCount + 1);
4924         if(index < oldCount)
4925         {
4926             memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
4927         }
4928         m_pArray[index] = src;
4929     }
4930 
remove(size_t index)4931     void remove(size_t index)
4932     {
4933         VMA_HEAVY_ASSERT(index < m_Count);
4934         const size_t oldCount = size();
4935         if(index < oldCount - 1)
4936         {
4937             memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
4938         }
4939         resize(oldCount - 1);
4940     }
4941 
push_back(const T & src)4942     void push_back(const T& src)
4943     {
4944         const size_t newIndex = size();
4945         resize(newIndex + 1);
4946         m_pArray[newIndex] = src;
4947     }
4948 
pop_back()4949     void pop_back()
4950     {
4951         VMA_HEAVY_ASSERT(m_Count > 0);
4952         resize(size() - 1);
4953     }
4954 
push_front(const T & src)4955     void push_front(const T& src)
4956     {
4957         insert(0, src);
4958     }
4959 
pop_front()4960     void pop_front()
4961     {
4962         VMA_HEAVY_ASSERT(m_Count > 0);
4963         remove(0);
4964     }
4965 
4966     typedef T* iterator;
4967 
begin()4968     iterator begin() { return m_pArray; }
end()4969     iterator end() { return m_pArray + m_Count; }
4970 
4971 private:
4972     AllocatorT m_Allocator;
4973     T* m_pArray;
4974     size_t m_Count;
4975     size_t m_Capacity;
4976 };
4977 
4978 template<typename T, typename allocatorT>
VmaVectorInsert(VmaVector<T,allocatorT> & vec,size_t index,const T & item)4979 static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
4980 {
4981     vec.insert(index, item);
4982 }
4983 
4984 template<typename T, typename allocatorT>
VmaVectorRemove(VmaVector<T,allocatorT> & vec,size_t index)4985 static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
4986 {
4987     vec.remove(index);
4988 }
4989 
4990 #endif // #if VMA_USE_STL_VECTOR
4991 
4992 template<typename CmpLess, typename VectorT>
VmaVectorInsertSorted(VectorT & vector,const typename VectorT::value_type & value)4993 size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
4994 {
4995     const size_t indexToInsert = VmaBinaryFindFirstNotLess(
4996         vector.data(),
4997         vector.data() + vector.size(),
4998         value,
4999         CmpLess()) - vector.data();
5000     VmaVectorInsert(vector, indexToInsert, value);
5001     return indexToInsert;
5002 }
5003 
5004 template<typename CmpLess, typename VectorT>
VmaVectorRemoveSorted(VectorT & vector,const typename VectorT::value_type & value)5005 bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
5006 {
5007     CmpLess comparator;
5008     typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
5009         vector.begin(),
5010         vector.end(),
5011         value,
5012         comparator);
5013     if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
5014     {
5015         size_t indexToRemove = it - vector.begin();
5016         VmaVectorRemove(vector, indexToRemove);
5017         return true;
5018     }
5019     return false;
5020 }
5021 
5022 ////////////////////////////////////////////////////////////////////////////////
5023 // class VmaSmallVector
5024 
5025 /*
5026 This is a vector (a variable-sized array), optimized for the case when the array is small.
5027 
5028 It contains some number of elements in-place, which allows it to avoid heap allocation
5029 when the actual number of elements is below that threshold. This allows normal "small"
5030 cases to be fast without losing generality for large inputs.
5031 */
5032 
5033 template<typename T, typename AllocatorT, size_t N>
5034 class VmaSmallVector
5035 {
5036 public:
5037     typedef T value_type;
5038 
VmaSmallVector(const AllocatorT & allocator)5039     VmaSmallVector(const AllocatorT& allocator) :
5040         m_Count(0),
5041         m_DynamicArray(allocator)
5042     {
5043     }
VmaSmallVector(size_t count,const AllocatorT & allocator)5044     VmaSmallVector(size_t count, const AllocatorT& allocator) :
5045         m_Count(count),
5046         m_DynamicArray(count > N ? count : 0, allocator)
5047     {
5048     }
5049     template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
5050     VmaSmallVector(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>& src) = delete;
5051     template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
5052     VmaSmallVector<T, AllocatorT, N>& operator=(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>& rhs) = delete;
5053 
empty()5054     bool empty() const { return m_Count == 0; }
size()5055     size_t size() const { return m_Count; }
data()5056     T* data() { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
data()5057     const T* data() const { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
5058 
5059     T& operator[](size_t index)
5060     {
5061         VMA_HEAVY_ASSERT(index < m_Count);
5062         return data()[index];
5063     }
5064     const T& operator[](size_t index) const
5065     {
5066         VMA_HEAVY_ASSERT(index < m_Count);
5067         return data()[index];
5068     }
5069 
front()5070     T& front()
5071     {
5072         VMA_HEAVY_ASSERT(m_Count > 0);
5073         return data()[0];
5074     }
front()5075     const T& front() const
5076     {
5077         VMA_HEAVY_ASSERT(m_Count > 0);
5078         return data()[0];
5079     }
back()5080     T& back()
5081     {
5082         VMA_HEAVY_ASSERT(m_Count > 0);
5083         return data()[m_Count - 1];
5084     }
back()5085     const T& back() const
5086     {
5087         VMA_HEAVY_ASSERT(m_Count > 0);
5088         return data()[m_Count - 1];
5089     }
5090 
5091     void resize(size_t newCount, bool freeMemory = false)
5092     {
5093         if(newCount > N && m_Count > N)
5094         {
5095             // Any direction, staying in m_DynamicArray
5096             m_DynamicArray.resize(newCount, freeMemory);
5097         }
5098         else if(newCount > N && m_Count <= N)
5099         {
5100             // Growing, moving from m_StaticArray to m_DynamicArray
5101             m_DynamicArray.resize(newCount, freeMemory);
5102             if(m_Count > 0)
5103             {
5104                 memcpy(m_DynamicArray.data(), m_StaticArray, m_Count * sizeof(T));
5105             }
5106         }
5107         else if(newCount <= N && m_Count > N)
5108         {
5109             // Shrinking, moving from m_DynamicArray to m_StaticArray
5110             if(newCount > 0)
5111             {
5112                 memcpy(m_StaticArray, m_DynamicArray.data(), newCount * sizeof(T));
5113             }
5114             m_DynamicArray.resize(0, freeMemory);
5115         }
5116         else
5117         {
5118             // Any direction, staying in m_StaticArray - nothing to do here
5119         }
5120         m_Count = newCount;
5121     }
5122 
5123     void clear(bool freeMemory = false)
5124     {
5125         m_DynamicArray.clear(freeMemory);
5126         m_Count = 0;
5127     }
5128 
insert(size_t index,const T & src)5129     void insert(size_t index, const T& src)
5130     {
5131         VMA_HEAVY_ASSERT(index <= m_Count);
5132         const size_t oldCount = size();
5133         resize(oldCount + 1);
5134         T* const dataPtr = data();
5135         if(index < oldCount)
5136         {
5137             //  I know, this could be more optimal for case where memmove can be memcpy directly from m_StaticArray to m_DynamicArray.
5138             memmove(dataPtr + (index + 1), dataPtr + index, (oldCount - index) * sizeof(T));
5139         }
5140         dataPtr[index] = src;
5141     }
5142 
remove(size_t index)5143     void remove(size_t index)
5144     {
5145         VMA_HEAVY_ASSERT(index < m_Count);
5146         const size_t oldCount = size();
5147         if(index < oldCount - 1)
5148         {
5149             //  I know, this could be more optimal for case where memmove can be memcpy directly from m_DynamicArray to m_StaticArray.
5150             T* const dataPtr = data();
5151             memmove(dataPtr + index, dataPtr + (index + 1), (oldCount - index - 1) * sizeof(T));
5152         }
5153         resize(oldCount - 1);
5154     }
5155 
push_back(const T & src)5156     void push_back(const T& src)
5157     {
5158         const size_t newIndex = size();
5159         resize(newIndex + 1);
5160         data()[newIndex] = src;
5161     }
5162 
pop_back()5163     void pop_back()
5164     {
5165         VMA_HEAVY_ASSERT(m_Count > 0);
5166         resize(size() - 1);
5167     }
5168 
push_front(const T & src)5169     void push_front(const T& src)
5170     {
5171         insert(0, src);
5172     }
5173 
pop_front()5174     void pop_front()
5175     {
5176         VMA_HEAVY_ASSERT(m_Count > 0);
5177         remove(0);
5178     }
5179 
5180     typedef T* iterator;
5181 
begin()5182     iterator begin() { return data(); }
end()5183     iterator end() { return data() + m_Count; }
5184 
5185 private:
5186     size_t m_Count;
5187     T m_StaticArray[N]; // Used when m_Size <= N
5188     VmaVector<T, AllocatorT> m_DynamicArray; // Used when m_Size > N
5189 };
5190 
5191 ////////////////////////////////////////////////////////////////////////////////
5192 // class VmaPoolAllocator
5193 
5194 /*
5195 Allocator for objects of type T using a list of arrays (pools) to speed up
5196 allocation. Number of elements that can be allocated is not bounded because
5197 allocator can create multiple blocks.
5198 */
5199 template<typename T>
5200 class VmaPoolAllocator
5201 {
5202     VMA_CLASS_NO_COPY(VmaPoolAllocator)
5203 public:
5204     VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity);
5205     ~VmaPoolAllocator();
5206     template<typename... Types> T* Alloc(Types... args);
5207     void Free(T* ptr);
5208 
5209 private:
5210     union Item
5211     {
5212         uint32_t NextFreeIndex;
5213         alignas(T) char Value[sizeof(T)];
5214     };
5215 
5216     struct ItemBlock
5217     {
5218         Item* pItems;
5219         uint32_t Capacity;
5220         uint32_t FirstFreeIndex;
5221     };
5222 
5223     const VkAllocationCallbacks* m_pAllocationCallbacks;
5224     const uint32_t m_FirstBlockCapacity;
5225     VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
5226 
5227     ItemBlock& CreateNewBlock();
5228 };
5229 
5230 template<typename T>
VmaPoolAllocator(const VkAllocationCallbacks * pAllocationCallbacks,uint32_t firstBlockCapacity)5231 VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity) :
5232     m_pAllocationCallbacks(pAllocationCallbacks),
5233     m_FirstBlockCapacity(firstBlockCapacity),
5234     m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
5235 {
5236     VMA_ASSERT(m_FirstBlockCapacity > 1);
5237 }
5238 
5239 template<typename T>
~VmaPoolAllocator()5240 VmaPoolAllocator<T>::~VmaPoolAllocator()
5241 {
5242     for(size_t i = m_ItemBlocks.size(); i--; )
5243         vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity);
5244     m_ItemBlocks.clear();
5245 }
5246 
5247 template<typename T>
Alloc(Types...args)5248 template<typename... Types> T* VmaPoolAllocator<T>::Alloc(Types... args)
5249 {
5250     for(size_t i = m_ItemBlocks.size(); i--; )
5251     {
5252         ItemBlock& block = m_ItemBlocks[i];
5253         // This block has some free items: Use first one.
5254         if(block.FirstFreeIndex != UINT32_MAX)
5255         {
5256             Item* const pItem = &block.pItems[block.FirstFreeIndex];
5257             block.FirstFreeIndex = pItem->NextFreeIndex;
5258             T* result = (T*)&pItem->Value;
5259             new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
5260             return result;
5261         }
5262     }
5263 
5264     // No block has free item: Create new one and use it.
5265     ItemBlock& newBlock = CreateNewBlock();
5266     Item* const pItem = &newBlock.pItems[0];
5267     newBlock.FirstFreeIndex = pItem->NextFreeIndex;
5268     T* result = (T*)&pItem->Value;
5269     new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
5270     return result;
5271 }
5272 
5273 template<typename T>
Free(T * ptr)5274 void VmaPoolAllocator<T>::Free(T* ptr)
5275 {
5276     // Search all memory blocks to find ptr.
5277     for(size_t i = m_ItemBlocks.size(); i--; )
5278     {
5279         ItemBlock& block = m_ItemBlocks[i];
5280 
5281         // Casting to union.
5282         Item* pItemPtr;
5283         memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
5284 
5285         // Check if pItemPtr is in address range of this block.
5286         if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity))
5287         {
5288             ptr->~T(); // Explicit destructor call.
5289             const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
5290             pItemPtr->NextFreeIndex = block.FirstFreeIndex;
5291             block.FirstFreeIndex = index;
5292             return;
5293         }
5294     }
5295     VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
5296 }
5297 
5298 template<typename T>
CreateNewBlock()5299 typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
5300 {
5301     const uint32_t newBlockCapacity = m_ItemBlocks.empty() ?
5302         m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2;
5303 
5304     const ItemBlock newBlock = {
5305         vma_new_array(m_pAllocationCallbacks, Item, newBlockCapacity),
5306         newBlockCapacity,
5307         0 };
5308 
5309     m_ItemBlocks.push_back(newBlock);
5310 
5311     // Setup singly-linked list of all free items in this block.
5312     for(uint32_t i = 0; i < newBlockCapacity - 1; ++i)
5313         newBlock.pItems[i].NextFreeIndex = i + 1;
5314     newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX;
5315     return m_ItemBlocks.back();
5316 }
5317 
5318 ////////////////////////////////////////////////////////////////////////////////
5319 // class VmaRawList, VmaList
5320 
5321 #if VMA_USE_STL_LIST
5322 
5323 #define VmaList std::list
5324 
5325 #else // #if VMA_USE_STL_LIST
5326 
5327 template<typename T>
5328 struct VmaListItem
5329 {
5330     VmaListItem* pPrev;
5331     VmaListItem* pNext;
5332     T Value;
5333 };
5334 
5335 // Doubly linked list.
5336 template<typename T>
5337 class VmaRawList
5338 {
5339     VMA_CLASS_NO_COPY(VmaRawList)
5340 public:
5341     typedef VmaListItem<T> ItemType;
5342 
5343     VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
5344     ~VmaRawList();
5345     void Clear();
5346 
GetCount()5347     size_t GetCount() const { return m_Count; }
IsEmpty()5348     bool IsEmpty() const { return m_Count == 0; }
5349 
Front()5350     ItemType* Front() { return m_pFront; }
Front()5351     const ItemType* Front() const { return m_pFront; }
Back()5352     ItemType* Back() { return m_pBack; }
Back()5353     const ItemType* Back() const { return m_pBack; }
5354 
5355     ItemType* PushBack();
5356     ItemType* PushFront();
5357     ItemType* PushBack(const T& value);
5358     ItemType* PushFront(const T& value);
5359     void PopBack();
5360     void PopFront();
5361 
5362     // Item can be null - it means PushBack.
5363     ItemType* InsertBefore(ItemType* pItem);
5364     // Item can be null - it means PushFront.
5365     ItemType* InsertAfter(ItemType* pItem);
5366 
5367     ItemType* InsertBefore(ItemType* pItem, const T& value);
5368     ItemType* InsertAfter(ItemType* pItem, const T& value);
5369 
5370     void Remove(ItemType* pItem);
5371 
5372 private:
5373     const VkAllocationCallbacks* const m_pAllocationCallbacks;
5374     VmaPoolAllocator<ItemType> m_ItemAllocator;
5375     ItemType* m_pFront;
5376     ItemType* m_pBack;
5377     size_t m_Count;
5378 };
5379 
5380 template<typename T>
VmaRawList(const VkAllocationCallbacks * pAllocationCallbacks)5381 VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
5382     m_pAllocationCallbacks(pAllocationCallbacks),
5383     m_ItemAllocator(pAllocationCallbacks, 128),
5384     m_pFront(VMA_NULL),
5385     m_pBack(VMA_NULL),
5386     m_Count(0)
5387 {
5388 }
5389 
5390 template<typename T>
~VmaRawList()5391 VmaRawList<T>::~VmaRawList()
5392 {
5393     // Intentionally not calling Clear, because that would be unnecessary
5394     // computations to return all items to m_ItemAllocator as free.
5395 }
5396 
5397 template<typename T>
Clear()5398 void VmaRawList<T>::Clear()
5399 {
5400     if(IsEmpty() == false)
5401     {
5402         ItemType* pItem = m_pBack;
5403         while(pItem != VMA_NULL)
5404         {
5405             ItemType* const pPrevItem = pItem->pPrev;
5406             m_ItemAllocator.Free(pItem);
5407             pItem = pPrevItem;
5408         }
5409         m_pFront = VMA_NULL;
5410         m_pBack = VMA_NULL;
5411         m_Count = 0;
5412     }
5413 }
5414 
5415 template<typename T>
PushBack()5416 VmaListItem<T>* VmaRawList<T>::PushBack()
5417 {
5418     ItemType* const pNewItem = m_ItemAllocator.Alloc();
5419     pNewItem->pNext = VMA_NULL;
5420     if(IsEmpty())
5421     {
5422         pNewItem->pPrev = VMA_NULL;
5423         m_pFront = pNewItem;
5424         m_pBack = pNewItem;
5425         m_Count = 1;
5426     }
5427     else
5428     {
5429         pNewItem->pPrev = m_pBack;
5430         m_pBack->pNext = pNewItem;
5431         m_pBack = pNewItem;
5432         ++m_Count;
5433     }
5434     return pNewItem;
5435 }
5436 
5437 template<typename T>
PushFront()5438 VmaListItem<T>* VmaRawList<T>::PushFront()
5439 {
5440     ItemType* const pNewItem = m_ItemAllocator.Alloc();
5441     pNewItem->pPrev = VMA_NULL;
5442     if(IsEmpty())
5443     {
5444         pNewItem->pNext = VMA_NULL;
5445         m_pFront = pNewItem;
5446         m_pBack = pNewItem;
5447         m_Count = 1;
5448     }
5449     else
5450     {
5451         pNewItem->pNext = m_pFront;
5452         m_pFront->pPrev = pNewItem;
5453         m_pFront = pNewItem;
5454         ++m_Count;
5455     }
5456     return pNewItem;
5457 }
5458 
5459 template<typename T>
PushBack(const T & value)5460 VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
5461 {
5462     ItemType* const pNewItem = PushBack();
5463     pNewItem->Value = value;
5464     return pNewItem;
5465 }
5466 
5467 template<typename T>
PushFront(const T & value)5468 VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
5469 {
5470     ItemType* const pNewItem = PushFront();
5471     pNewItem->Value = value;
5472     return pNewItem;
5473 }
5474 
5475 template<typename T>
PopBack()5476 void VmaRawList<T>::PopBack()
5477 {
5478     VMA_HEAVY_ASSERT(m_Count > 0);
5479     ItemType* const pBackItem = m_pBack;
5480     ItemType* const pPrevItem = pBackItem->pPrev;
5481     if(pPrevItem != VMA_NULL)
5482     {
5483         pPrevItem->pNext = VMA_NULL;
5484     }
5485     m_pBack = pPrevItem;
5486     m_ItemAllocator.Free(pBackItem);
5487     --m_Count;
5488 }
5489 
5490 template<typename T>
PopFront()5491 void VmaRawList<T>::PopFront()
5492 {
5493     VMA_HEAVY_ASSERT(m_Count > 0);
5494     ItemType* const pFrontItem = m_pFront;
5495     ItemType* const pNextItem = pFrontItem->pNext;
5496     if(pNextItem != VMA_NULL)
5497     {
5498         pNextItem->pPrev = VMA_NULL;
5499     }
5500     m_pFront = pNextItem;
5501     m_ItemAllocator.Free(pFrontItem);
5502     --m_Count;
5503 }
5504 
5505 template<typename T>
Remove(ItemType * pItem)5506 void VmaRawList<T>::Remove(ItemType* pItem)
5507 {
5508     VMA_HEAVY_ASSERT(pItem != VMA_NULL);
5509     VMA_HEAVY_ASSERT(m_Count > 0);
5510 
5511     if(pItem->pPrev != VMA_NULL)
5512     {
5513         pItem->pPrev->pNext = pItem->pNext;
5514     }
5515     else
5516     {
5517         VMA_HEAVY_ASSERT(m_pFront == pItem);
5518         m_pFront = pItem->pNext;
5519     }
5520 
5521     if(pItem->pNext != VMA_NULL)
5522     {
5523         pItem->pNext->pPrev = pItem->pPrev;
5524     }
5525     else
5526     {
5527         VMA_HEAVY_ASSERT(m_pBack == pItem);
5528         m_pBack = pItem->pPrev;
5529     }
5530 
5531     m_ItemAllocator.Free(pItem);
5532     --m_Count;
5533 }
5534 
5535 template<typename T>
InsertBefore(ItemType * pItem)5536 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
5537 {
5538     if(pItem != VMA_NULL)
5539     {
5540         ItemType* const prevItem = pItem->pPrev;
5541         ItemType* const newItem = m_ItemAllocator.Alloc();
5542         newItem->pPrev = prevItem;
5543         newItem->pNext = pItem;
5544         pItem->pPrev = newItem;
5545         if(prevItem != VMA_NULL)
5546         {
5547             prevItem->pNext = newItem;
5548         }
5549         else
5550         {
5551             VMA_HEAVY_ASSERT(m_pFront == pItem);
5552             m_pFront = newItem;
5553         }
5554         ++m_Count;
5555         return newItem;
5556     }
5557     else
5558         return PushBack();
5559 }
5560 
5561 template<typename T>
InsertAfter(ItemType * pItem)5562 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
5563 {
5564     if(pItem != VMA_NULL)
5565     {
5566         ItemType* const nextItem = pItem->pNext;
5567         ItemType* const newItem = m_ItemAllocator.Alloc();
5568         newItem->pNext = nextItem;
5569         newItem->pPrev = pItem;
5570         pItem->pNext = newItem;
5571         if(nextItem != VMA_NULL)
5572         {
5573             nextItem->pPrev = newItem;
5574         }
5575         else
5576         {
5577             VMA_HEAVY_ASSERT(m_pBack == pItem);
5578             m_pBack = newItem;
5579         }
5580         ++m_Count;
5581         return newItem;
5582     }
5583     else
5584         return PushFront();
5585 }
5586 
5587 template<typename T>
InsertBefore(ItemType * pItem,const T & value)5588 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
5589 {
5590     ItemType* const newItem = InsertBefore(pItem);
5591     newItem->Value = value;
5592     return newItem;
5593 }
5594 
5595 template<typename T>
InsertAfter(ItemType * pItem,const T & value)5596 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
5597 {
5598     ItemType* const newItem = InsertAfter(pItem);
5599     newItem->Value = value;
5600     return newItem;
5601 }
5602 
5603 template<typename T, typename AllocatorT>
5604 class VmaList
5605 {
VMA_CLASS_NO_COPY(VmaList)5606     VMA_CLASS_NO_COPY(VmaList)
5607 public:
5608     class iterator
5609     {
5610     public:
5611         iterator() :
5612             m_pList(VMA_NULL),
5613             m_pItem(VMA_NULL)
5614         {
5615         }
5616 
5617         T& operator*() const
5618         {
5619             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5620             return m_pItem->Value;
5621         }
5622         T* operator->() const
5623         {
5624             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5625             return &m_pItem->Value;
5626         }
5627 
5628         iterator& operator++()
5629         {
5630             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5631             m_pItem = m_pItem->pNext;
5632             return *this;
5633         }
5634         iterator& operator--()
5635         {
5636             if(m_pItem != VMA_NULL)
5637             {
5638                 m_pItem = m_pItem->pPrev;
5639             }
5640             else
5641             {
5642                 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
5643                 m_pItem = m_pList->Back();
5644             }
5645             return *this;
5646         }
5647 
5648         iterator operator++(int)
5649         {
5650             iterator result = *this;
5651             ++*this;
5652             return result;
5653         }
5654         iterator operator--(int)
5655         {
5656             iterator result = *this;
5657             --*this;
5658             return result;
5659         }
5660 
5661         bool operator==(const iterator& rhs) const
5662         {
5663             VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5664             return m_pItem == rhs.m_pItem;
5665         }
5666         bool operator!=(const iterator& rhs) const
5667         {
5668             VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5669             return m_pItem != rhs.m_pItem;
5670         }
5671 
5672     private:
5673         VmaRawList<T>* m_pList;
5674         VmaListItem<T>* m_pItem;
5675 
5676         iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
5677             m_pList(pList),
5678             m_pItem(pItem)
5679         {
5680         }
5681 
5682         friend class VmaList<T, AllocatorT>;
5683     };
5684 
5685     class const_iterator
5686     {
5687     public:
const_iterator()5688         const_iterator() :
5689             m_pList(VMA_NULL),
5690             m_pItem(VMA_NULL)
5691         {
5692         }
5693 
const_iterator(const iterator & src)5694         const_iterator(const iterator& src) :
5695             m_pList(src.m_pList),
5696             m_pItem(src.m_pItem)
5697         {
5698         }
5699 
5700         const T& operator*() const
5701         {
5702             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5703             return m_pItem->Value;
5704         }
5705         const T* operator->() const
5706         {
5707             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5708             return &m_pItem->Value;
5709         }
5710 
5711         const_iterator& operator++()
5712         {
5713             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5714             m_pItem = m_pItem->pNext;
5715             return *this;
5716         }
5717         const_iterator& operator--()
5718         {
5719             if(m_pItem != VMA_NULL)
5720             {
5721                 m_pItem = m_pItem->pPrev;
5722             }
5723             else
5724             {
5725                 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
5726                 m_pItem = m_pList->Back();
5727             }
5728             return *this;
5729         }
5730 
5731         const_iterator operator++(int)
5732         {
5733             const_iterator result = *this;
5734             ++*this;
5735             return result;
5736         }
5737         const_iterator operator--(int)
5738         {
5739             const_iterator result = *this;
5740             --*this;
5741             return result;
5742         }
5743 
5744         bool operator==(const const_iterator& rhs) const
5745         {
5746             VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5747             return m_pItem == rhs.m_pItem;
5748         }
5749         bool operator!=(const const_iterator& rhs) const
5750         {
5751             VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5752             return m_pItem != rhs.m_pItem;
5753         }
5754 
5755     private:
const_iterator(const VmaRawList<T> * pList,const VmaListItem<T> * pItem)5756         const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
5757             m_pList(pList),
5758             m_pItem(pItem)
5759         {
5760         }
5761 
5762         const VmaRawList<T>* m_pList;
5763         const VmaListItem<T>* m_pItem;
5764 
5765         friend class VmaList<T, AllocatorT>;
5766     };
5767 
VmaList(const AllocatorT & allocator)5768     VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
5769 
empty()5770     bool empty() const { return m_RawList.IsEmpty(); }
size()5771     size_t size() const { return m_RawList.GetCount(); }
5772 
begin()5773     iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
end()5774     iterator end() { return iterator(&m_RawList, VMA_NULL); }
5775 
cbegin()5776     const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
cend()5777     const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
5778 
clear()5779     void clear() { m_RawList.Clear(); }
push_back(const T & value)5780     void push_back(const T& value) { m_RawList.PushBack(value); }
erase(iterator it)5781     void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
insert(iterator it,const T & value)5782     iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
5783 
5784 private:
5785     VmaRawList<T> m_RawList;
5786 };
5787 
5788 #endif // #if VMA_USE_STL_LIST
5789 
5790 ////////////////////////////////////////////////////////////////////////////////
5791 // class VmaMap
5792 
5793 // Unused in this version.
5794 #if 0
5795 
5796 #if VMA_USE_STL_UNORDERED_MAP
5797 
5798 #define VmaPair std::pair
5799 
5800 #define VMA_MAP_TYPE(KeyT, ValueT) \
5801     std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
5802 
5803 #else // #if VMA_USE_STL_UNORDERED_MAP
5804 
5805 template<typename T1, typename T2>
5806 struct VmaPair
5807 {
5808     T1 first;
5809     T2 second;
5810 
5811     VmaPair() : first(), second() { }
5812     VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
5813 };
5814 
5815 /* Class compatible with subset of interface of std::unordered_map.
5816 KeyT, ValueT must be POD because they will be stored in VmaVector.
5817 */
5818 template<typename KeyT, typename ValueT>
5819 class VmaMap
5820 {
5821 public:
5822     typedef VmaPair<KeyT, ValueT> PairType;
5823     typedef PairType* iterator;
5824 
5825     VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
5826 
5827     iterator begin() { return m_Vector.begin(); }
5828     iterator end() { return m_Vector.end(); }
5829 
5830     void insert(const PairType& pair);
5831     iterator find(const KeyT& key);
5832     void erase(iterator it);
5833 
5834 private:
5835     VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
5836 };
5837 
5838 #define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
5839 
5840 template<typename FirstT, typename SecondT>
5841 struct VmaPairFirstLess
5842 {
5843     bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
5844     {
5845         return lhs.first < rhs.first;
5846     }
5847     bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
5848     {
5849         return lhs.first < rhsFirst;
5850     }
5851 };
5852 
5853 template<typename KeyT, typename ValueT>
5854 void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
5855 {
5856     const size_t indexToInsert = VmaBinaryFindFirstNotLess(
5857         m_Vector.data(),
5858         m_Vector.data() + m_Vector.size(),
5859         pair,
5860         VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
5861     VmaVectorInsert(m_Vector, indexToInsert, pair);
5862 }
5863 
5864 template<typename KeyT, typename ValueT>
5865 VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
5866 {
5867     PairType* it = VmaBinaryFindFirstNotLess(
5868         m_Vector.data(),
5869         m_Vector.data() + m_Vector.size(),
5870         key,
5871         VmaPairFirstLess<KeyT, ValueT>());
5872     if((it != m_Vector.end()) && (it->first == key))
5873     {
5874         return it;
5875     }
5876     else
5877     {
5878         return m_Vector.end();
5879     }
5880 }
5881 
5882 template<typename KeyT, typename ValueT>
5883 void VmaMap<KeyT, ValueT>::erase(iterator it)
5884 {
5885     VmaVectorRemove(m_Vector, it - m_Vector.begin());
5886 }
5887 
5888 #endif // #if VMA_USE_STL_UNORDERED_MAP
5889 
5890 #endif // #if 0
5891 
5892 ////////////////////////////////////////////////////////////////////////////////
5893 
5894 class VmaDeviceMemoryBlock;
5895 
5896 enum VMA_CACHE_OPERATION { VMA_CACHE_FLUSH, VMA_CACHE_INVALIDATE };
5897 
5898 struct VmaAllocation_T
5899 {
5900 private:
5901     static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
5902 
5903     enum FLAGS
5904     {
5905         FLAG_USER_DATA_STRING = 0x01,
5906     };
5907 
5908 public:
5909     enum ALLOCATION_TYPE
5910     {
5911         ALLOCATION_TYPE_NONE,
5912         ALLOCATION_TYPE_BLOCK,
5913         ALLOCATION_TYPE_DEDICATED,
5914     };
5915 
5916     /*
5917     This struct is allocated using VmaPoolAllocator.
5918     */
5919 
VmaAllocation_TVmaAllocation_T5920     VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
5921         m_Alignment{1},
5922         m_Size{0},
5923         m_pUserData{VMA_NULL},
5924         m_LastUseFrameIndex{currentFrameIndex},
5925         m_MemoryTypeIndex{0},
5926         m_Type{(uint8_t)ALLOCATION_TYPE_NONE},
5927         m_SuballocationType{(uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN},
5928         m_MapCount{0},
5929         m_Flags{userDataString ? (uint8_t)FLAG_USER_DATA_STRING : (uint8_t)0}
5930     {
5931 #if VMA_STATS_STRING_ENABLED
5932         m_CreationFrameIndex = currentFrameIndex;
5933         m_BufferImageUsage = 0;
5934 #endif
5935     }
5936 
~VmaAllocation_TVmaAllocation_T5937     ~VmaAllocation_T()
5938     {
5939         VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
5940 
5941         // Check if owned string was freed.
5942         VMA_ASSERT(m_pUserData == VMA_NULL);
5943     }
5944 
InitBlockAllocationVmaAllocation_T5945     void InitBlockAllocation(
5946         VmaDeviceMemoryBlock* block,
5947         VkDeviceSize offset,
5948         VkDeviceSize alignment,
5949         VkDeviceSize size,
5950         uint32_t memoryTypeIndex,
5951         VmaSuballocationType suballocationType,
5952         bool mapped,
5953         bool canBecomeLost)
5954     {
5955         VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
5956         VMA_ASSERT(block != VMA_NULL);
5957         m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
5958         m_Alignment = alignment;
5959         m_Size = size;
5960         m_MemoryTypeIndex = memoryTypeIndex;
5961         m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
5962         m_SuballocationType = (uint8_t)suballocationType;
5963         m_BlockAllocation.m_Block = block;
5964         m_BlockAllocation.m_Offset = offset;
5965         m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
5966     }
5967 
InitLostVmaAllocation_T5968     void InitLost()
5969     {
5970         VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
5971         VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
5972         m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
5973         m_MemoryTypeIndex = 0;
5974         m_BlockAllocation.m_Block = VMA_NULL;
5975         m_BlockAllocation.m_Offset = 0;
5976         m_BlockAllocation.m_CanBecomeLost = true;
5977     }
5978 
5979     void ChangeBlockAllocation(
5980         VmaAllocator hAllocator,
5981         VmaDeviceMemoryBlock* block,
5982         VkDeviceSize offset);
5983 
5984     void ChangeOffset(VkDeviceSize newOffset);
5985 
5986     // pMappedData not null means allocation is created with MAPPED flag.
InitDedicatedAllocationVmaAllocation_T5987     void InitDedicatedAllocation(
5988         uint32_t memoryTypeIndex,
5989         VkDeviceMemory hMemory,
5990         VmaSuballocationType suballocationType,
5991         void* pMappedData,
5992         VkDeviceSize size)
5993     {
5994         VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
5995         VMA_ASSERT(hMemory != VK_NULL_HANDLE);
5996         m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
5997         m_Alignment = 0;
5998         m_Size = size;
5999         m_MemoryTypeIndex = memoryTypeIndex;
6000         m_SuballocationType = (uint8_t)suballocationType;
6001         m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
6002         m_DedicatedAllocation.m_hMemory = hMemory;
6003         m_DedicatedAllocation.m_pMappedData = pMappedData;
6004     }
6005 
GetTypeVmaAllocation_T6006     ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
GetAlignmentVmaAllocation_T6007     VkDeviceSize GetAlignment() const { return m_Alignment; }
GetSizeVmaAllocation_T6008     VkDeviceSize GetSize() const { return m_Size; }
IsUserDataStringVmaAllocation_T6009     bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
GetUserDataVmaAllocation_T6010     void* GetUserData() const { return m_pUserData; }
6011     void SetUserData(VmaAllocator hAllocator, void* pUserData);
GetSuballocationTypeVmaAllocation_T6012     VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
6013 
GetBlockVmaAllocation_T6014     VmaDeviceMemoryBlock* GetBlock() const
6015     {
6016         VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
6017         return m_BlockAllocation.m_Block;
6018     }
6019     VkDeviceSize GetOffset() const;
6020     VkDeviceMemory GetMemory() const;
GetMemoryTypeIndexVmaAllocation_T6021     uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
IsPersistentMapVmaAllocation_T6022     bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
6023     void* GetMappedData() const;
6024     bool CanBecomeLost() const;
6025 
GetLastUseFrameIndexVmaAllocation_T6026     uint32_t GetLastUseFrameIndex() const
6027     {
6028         return m_LastUseFrameIndex.load();
6029     }
CompareExchangeLastUseFrameIndexVmaAllocation_T6030     bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
6031     {
6032         return m_LastUseFrameIndex.compare_exchange_weak(expected, desired);
6033     }
6034     /*
6035     - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
6036       makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
6037     - Else, returns false.
6038 
6039     If hAllocation is already lost, assert - you should not call it then.
6040     If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
6041     */
6042     bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6043 
DedicatedAllocCalcStatsInfoVmaAllocation_T6044     void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
6045     {
6046         VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
6047         outInfo.blockCount = 1;
6048         outInfo.allocationCount = 1;
6049         outInfo.unusedRangeCount = 0;
6050         outInfo.usedBytes = m_Size;
6051         outInfo.unusedBytes = 0;
6052         outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
6053         outInfo.unusedRangeSizeMin = UINT64_MAX;
6054         outInfo.unusedRangeSizeMax = 0;
6055     }
6056 
6057     void BlockAllocMap();
6058     void BlockAllocUnmap();
6059     VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
6060     void DedicatedAllocUnmap(VmaAllocator hAllocator);
6061 
6062 #if VMA_STATS_STRING_ENABLED
GetCreationFrameIndexVmaAllocation_T6063     uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; }
GetBufferImageUsageVmaAllocation_T6064     uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
6065 
InitBufferImageUsageVmaAllocation_T6066     void InitBufferImageUsage(uint32_t bufferImageUsage)
6067     {
6068         VMA_ASSERT(m_BufferImageUsage == 0);
6069         m_BufferImageUsage = bufferImageUsage;
6070     }
6071 
6072     void PrintParameters(class VmaJsonWriter& json) const;
6073 #endif
6074 
6075 private:
6076     VkDeviceSize m_Alignment;
6077     VkDeviceSize m_Size;
6078     void* m_pUserData;
6079     VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
6080     uint32_t m_MemoryTypeIndex;
6081     uint8_t m_Type; // ALLOCATION_TYPE
6082     uint8_t m_SuballocationType; // VmaSuballocationType
6083     // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
6084     // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
6085     uint8_t m_MapCount;
6086     uint8_t m_Flags; // enum FLAGS
6087 
6088     // Allocation out of VmaDeviceMemoryBlock.
6089     struct BlockAllocation
6090     {
6091         VmaDeviceMemoryBlock* m_Block;
6092         VkDeviceSize m_Offset;
6093         bool m_CanBecomeLost;
6094     };
6095 
6096     // Allocation for an object that has its own private VkDeviceMemory.
6097     struct DedicatedAllocation
6098     {
6099         VkDeviceMemory m_hMemory;
6100         void* m_pMappedData; // Not null means memory is mapped.
6101     };
6102 
6103     union
6104     {
6105         // Allocation out of VmaDeviceMemoryBlock.
6106         BlockAllocation m_BlockAllocation;
6107         // Allocation for an object that has its own private VkDeviceMemory.
6108         DedicatedAllocation m_DedicatedAllocation;
6109     };
6110 
6111 #if VMA_STATS_STRING_ENABLED
6112     uint32_t m_CreationFrameIndex;
6113     uint32_t m_BufferImageUsage; // 0 if unknown.
6114 #endif
6115 
6116     void FreeUserDataString(VmaAllocator hAllocator);
6117 };
6118 
6119 /*
6120 Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
6121 allocated memory block or free.
6122 */
6123 struct VmaSuballocation
6124 {
6125     VkDeviceSize offset;
6126     VkDeviceSize size;
6127     VmaAllocation hAllocation;
6128     VmaSuballocationType type;
6129 };
6130 
6131 // Comparator for offsets.
6132 struct VmaSuballocationOffsetLess
6133 {
operatorVmaSuballocationOffsetLess6134     bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6135     {
6136         return lhs.offset < rhs.offset;
6137     }
6138 };
6139 struct VmaSuballocationOffsetGreater
6140 {
operatorVmaSuballocationOffsetGreater6141     bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6142     {
6143         return lhs.offset > rhs.offset;
6144     }
6145 };
6146 
6147 typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
6148 
6149 // Cost of one additional allocation lost, as equivalent in bytes.
6150 static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
6151 
6152 enum class VmaAllocationRequestType
6153 {
6154     Normal,
6155     // Used by "Linear" algorithm.
6156     UpperAddress,
6157     EndOf1st,
6158     EndOf2nd,
6159 };
6160 
6161 /*
6162 Parameters of planned allocation inside a VmaDeviceMemoryBlock.
6163 
6164 If canMakeOtherLost was false:
6165 - item points to a FREE suballocation.
6166 - itemsToMakeLostCount is 0.
6167 
6168 If canMakeOtherLost was true:
6169 - item points to first of sequence of suballocations, which are either FREE,
6170   or point to VmaAllocations that can become lost.
6171 - itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
6172   the requested allocation to succeed.
6173 */
6174 struct VmaAllocationRequest
6175 {
6176     VkDeviceSize offset;
6177     VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
6178     VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
6179     VmaSuballocationList::iterator item;
6180     size_t itemsToMakeLostCount;
6181     void* customData;
6182     VmaAllocationRequestType type;
6183 
CalcCostVmaAllocationRequest6184     VkDeviceSize CalcCost() const
6185     {
6186         return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
6187     }
6188 };
6189 
6190 /*
6191 Data structure used for bookkeeping of allocations and unused ranges of memory
6192 in a single VkDeviceMemory block.
6193 */
6194 class VmaBlockMetadata
6195 {
6196 public:
6197     VmaBlockMetadata(VmaAllocator hAllocator);
~VmaBlockMetadata()6198     virtual ~VmaBlockMetadata() { }
Init(VkDeviceSize size)6199     virtual void Init(VkDeviceSize size) { m_Size = size; }
6200 
6201     // Validates all data structures inside this object. If not valid, returns false.
6202     virtual bool Validate() const = 0;
GetSize()6203     VkDeviceSize GetSize() const { return m_Size; }
6204     virtual size_t GetAllocationCount() const = 0;
6205     virtual VkDeviceSize GetSumFreeSize() const = 0;
6206     virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0;
6207     // Returns true if this block is empty - contains only single free suballocation.
6208     virtual bool IsEmpty() const = 0;
6209 
6210     virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
6211     // Shouldn't modify blockCount.
6212     virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
6213 
6214 #if VMA_STATS_STRING_ENABLED
6215     virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
6216 #endif
6217 
6218     // Tries to find a place for suballocation with given parameters inside this block.
6219     // If succeeded, fills pAllocationRequest and returns true.
6220     // If failed, returns false.
6221     virtual bool CreateAllocationRequest(
6222         uint32_t currentFrameIndex,
6223         uint32_t frameInUseCount,
6224         VkDeviceSize bufferImageGranularity,
6225         VkDeviceSize allocSize,
6226         VkDeviceSize allocAlignment,
6227         bool upperAddress,
6228         VmaSuballocationType allocType,
6229         bool canMakeOtherLost,
6230         // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
6231         uint32_t strategy,
6232         VmaAllocationRequest* pAllocationRequest) = 0;
6233 
6234     virtual bool MakeRequestedAllocationsLost(
6235         uint32_t currentFrameIndex,
6236         uint32_t frameInUseCount,
6237         VmaAllocationRequest* pAllocationRequest) = 0;
6238 
6239     virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0;
6240 
6241     virtual VkResult CheckCorruption(const void* pBlockData) = 0;
6242 
6243     // Makes actual allocation based on request. Request must already be checked and valid.
6244     virtual void Alloc(
6245         const VmaAllocationRequest& request,
6246         VmaSuballocationType type,
6247         VkDeviceSize allocSize,
6248         VmaAllocation hAllocation) = 0;
6249 
6250     // Frees suballocation assigned to given memory region.
6251     virtual void Free(const VmaAllocation allocation) = 0;
6252     virtual void FreeAtOffset(VkDeviceSize offset) = 0;
6253 
6254 protected:
GetAllocationCallbacks()6255     const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
6256 
6257 #if VMA_STATS_STRING_ENABLED
6258     void PrintDetailedMap_Begin(class VmaJsonWriter& json,
6259         VkDeviceSize unusedBytes,
6260         size_t allocationCount,
6261         size_t unusedRangeCount) const;
6262     void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
6263         VkDeviceSize offset,
6264         VmaAllocation hAllocation) const;
6265     void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
6266         VkDeviceSize offset,
6267         VkDeviceSize size) const;
6268     void PrintDetailedMap_End(class VmaJsonWriter& json) const;
6269 #endif
6270 
6271 private:
6272     VkDeviceSize m_Size;
6273     const VkAllocationCallbacks* m_pAllocationCallbacks;
6274 };
6275 
6276 #define VMA_VALIDATE(cond) do { if(!(cond)) { \
6277         VMA_ASSERT(0 && "Validation failed: " #cond); \
6278         return false; \
6279     } } while(false)
6280 
6281 class VmaBlockMetadata_Generic : public VmaBlockMetadata
6282 {
6283     VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
6284 public:
6285     VmaBlockMetadata_Generic(VmaAllocator hAllocator);
6286     virtual ~VmaBlockMetadata_Generic();
6287     virtual void Init(VkDeviceSize size);
6288 
6289     virtual bool Validate() const;
GetAllocationCount()6290     virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
GetSumFreeSize()6291     virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
6292     virtual VkDeviceSize GetUnusedRangeSizeMax() const;
6293     virtual bool IsEmpty() const;
6294 
6295     virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6296     virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6297 
6298 #if VMA_STATS_STRING_ENABLED
6299     virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6300 #endif
6301 
6302     virtual bool CreateAllocationRequest(
6303         uint32_t currentFrameIndex,
6304         uint32_t frameInUseCount,
6305         VkDeviceSize bufferImageGranularity,
6306         VkDeviceSize allocSize,
6307         VkDeviceSize allocAlignment,
6308         bool upperAddress,
6309         VmaSuballocationType allocType,
6310         bool canMakeOtherLost,
6311         uint32_t strategy,
6312         VmaAllocationRequest* pAllocationRequest);
6313 
6314     virtual bool MakeRequestedAllocationsLost(
6315         uint32_t currentFrameIndex,
6316         uint32_t frameInUseCount,
6317         VmaAllocationRequest* pAllocationRequest);
6318 
6319     virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6320 
6321     virtual VkResult CheckCorruption(const void* pBlockData);
6322 
6323     virtual void Alloc(
6324         const VmaAllocationRequest& request,
6325         VmaSuballocationType type,
6326         VkDeviceSize allocSize,
6327         VmaAllocation hAllocation);
6328 
6329     virtual void Free(const VmaAllocation allocation);
6330     virtual void FreeAtOffset(VkDeviceSize offset);
6331 
6332     ////////////////////////////////////////////////////////////////////////////////
6333     // For defragmentation
6334 
6335     bool IsBufferImageGranularityConflictPossible(
6336         VkDeviceSize bufferImageGranularity,
6337         VmaSuballocationType& inOutPrevSuballocType) const;
6338 
6339 private:
6340     friend class VmaDefragmentationAlgorithm_Generic;
6341     friend class VmaDefragmentationAlgorithm_Fast;
6342 
6343     uint32_t m_FreeCount;
6344     VkDeviceSize m_SumFreeSize;
6345     VmaSuballocationList m_Suballocations;
6346     // Suballocations that are free and have size greater than certain threshold.
6347     // Sorted by size, ascending.
6348     VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
6349 
6350     bool ValidateFreeSuballocationList() const;
6351 
6352     // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
6353     // If yes, fills pOffset and returns true. If no, returns false.
6354     bool CheckAllocation(
6355         uint32_t currentFrameIndex,
6356         uint32_t frameInUseCount,
6357         VkDeviceSize bufferImageGranularity,
6358         VkDeviceSize allocSize,
6359         VkDeviceSize allocAlignment,
6360         VmaSuballocationType allocType,
6361         VmaSuballocationList::const_iterator suballocItem,
6362         bool canMakeOtherLost,
6363         VkDeviceSize* pOffset,
6364         size_t* itemsToMakeLostCount,
6365         VkDeviceSize* pSumFreeSize,
6366         VkDeviceSize* pSumItemSize) const;
6367     // Given free suballocation, it merges it with following one, which must also be free.
6368     void MergeFreeWithNext(VmaSuballocationList::iterator item);
6369     // Releases given suballocation, making it free.
6370     // Merges it with adjacent free suballocations if applicable.
6371     // Returns iterator to new free suballocation at this place.
6372     VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
6373     // Given free suballocation, it inserts it into sorted list of
6374     // m_FreeSuballocationsBySize if it's suitable.
6375     void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
6376     // Given free suballocation, it removes it from sorted list of
6377     // m_FreeSuballocationsBySize if it's suitable.
6378     void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
6379 };
6380 
6381 /*
6382 Allocations and their references in internal data structure look like this:
6383 
6384 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
6385 
6386         0 +-------+
6387           |       |
6388           |       |
6389           |       |
6390           +-------+
6391           | Alloc |  1st[m_1stNullItemsBeginCount]
6392           +-------+
6393           | Alloc |  1st[m_1stNullItemsBeginCount + 1]
6394           +-------+
6395           |  ...  |
6396           +-------+
6397           | Alloc |  1st[1st.size() - 1]
6398           +-------+
6399           |       |
6400           |       |
6401           |       |
6402 GetSize() +-------+
6403 
6404 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
6405 
6406         0 +-------+
6407           | Alloc |  2nd[0]
6408           +-------+
6409           | Alloc |  2nd[1]
6410           +-------+
6411           |  ...  |
6412           +-------+
6413           | Alloc |  2nd[2nd.size() - 1]
6414           +-------+
6415           |       |
6416           |       |
6417           |       |
6418           +-------+
6419           | Alloc |  1st[m_1stNullItemsBeginCount]
6420           +-------+
6421           | Alloc |  1st[m_1stNullItemsBeginCount + 1]
6422           +-------+
6423           |  ...  |
6424           +-------+
6425           | Alloc |  1st[1st.size() - 1]
6426           +-------+
6427           |       |
6428 GetSize() +-------+
6429 
6430 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
6431 
6432         0 +-------+
6433           |       |
6434           |       |
6435           |       |
6436           +-------+
6437           | Alloc |  1st[m_1stNullItemsBeginCount]
6438           +-------+
6439           | Alloc |  1st[m_1stNullItemsBeginCount + 1]
6440           +-------+
6441           |  ...  |
6442           +-------+
6443           | Alloc |  1st[1st.size() - 1]
6444           +-------+
6445           |       |
6446           |       |
6447           |       |
6448           +-------+
6449           | Alloc |  2nd[2nd.size() - 1]
6450           +-------+
6451           |  ...  |
6452           +-------+
6453           | Alloc |  2nd[1]
6454           +-------+
6455           | Alloc |  2nd[0]
6456 GetSize() +-------+
6457 
6458 */
6459 class VmaBlockMetadata_Linear : public VmaBlockMetadata
6460 {
6461     VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
6462 public:
6463     VmaBlockMetadata_Linear(VmaAllocator hAllocator);
6464     virtual ~VmaBlockMetadata_Linear();
6465     virtual void Init(VkDeviceSize size);
6466 
6467     virtual bool Validate() const;
6468     virtual size_t GetAllocationCount() const;
GetSumFreeSize()6469     virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
6470     virtual VkDeviceSize GetUnusedRangeSizeMax() const;
IsEmpty()6471     virtual bool IsEmpty() const { return GetAllocationCount() == 0; }
6472 
6473     virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6474     virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6475 
6476 #if VMA_STATS_STRING_ENABLED
6477     virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6478 #endif
6479 
6480     virtual bool CreateAllocationRequest(
6481         uint32_t currentFrameIndex,
6482         uint32_t frameInUseCount,
6483         VkDeviceSize bufferImageGranularity,
6484         VkDeviceSize allocSize,
6485         VkDeviceSize allocAlignment,
6486         bool upperAddress,
6487         VmaSuballocationType allocType,
6488         bool canMakeOtherLost,
6489         uint32_t strategy,
6490         VmaAllocationRequest* pAllocationRequest);
6491 
6492     virtual bool MakeRequestedAllocationsLost(
6493         uint32_t currentFrameIndex,
6494         uint32_t frameInUseCount,
6495         VmaAllocationRequest* pAllocationRequest);
6496 
6497     virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6498 
6499     virtual VkResult CheckCorruption(const void* pBlockData);
6500 
6501     virtual void Alloc(
6502         const VmaAllocationRequest& request,
6503         VmaSuballocationType type,
6504         VkDeviceSize allocSize,
6505         VmaAllocation hAllocation);
6506 
6507     virtual void Free(const VmaAllocation allocation);
6508     virtual void FreeAtOffset(VkDeviceSize offset);
6509 
6510 private:
6511     /*
6512     There are two suballocation vectors, used in ping-pong way.
6513     The one with index m_1stVectorIndex is called 1st.
6514     The one with index (m_1stVectorIndex ^ 1) is called 2nd.
6515     2nd can be non-empty only when 1st is not empty.
6516     When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
6517     */
6518     typedef VmaVector< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > SuballocationVectorType;
6519 
6520     enum SECOND_VECTOR_MODE
6521     {
6522         SECOND_VECTOR_EMPTY,
6523         /*
6524         Suballocations in 2nd vector are created later than the ones in 1st, but they
6525         all have smaller offset.
6526         */
6527         SECOND_VECTOR_RING_BUFFER,
6528         /*
6529         Suballocations in 2nd vector are upper side of double stack.
6530         They all have offsets higher than those in 1st vector.
6531         Top of this stack means smaller offsets, but higher indices in this vector.
6532         */
6533         SECOND_VECTOR_DOUBLE_STACK,
6534     };
6535 
6536     VkDeviceSize m_SumFreeSize;
6537     SuballocationVectorType m_Suballocations0, m_Suballocations1;
6538     uint32_t m_1stVectorIndex;
6539     SECOND_VECTOR_MODE m_2ndVectorMode;
6540 
AccessSuballocations1st()6541     SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
AccessSuballocations2nd()6542     SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
AccessSuballocations1st()6543     const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
AccessSuballocations2nd()6544     const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
6545 
6546     // Number of items in 1st vector with hAllocation = null at the beginning.
6547     size_t m_1stNullItemsBeginCount;
6548     // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
6549     size_t m_1stNullItemsMiddleCount;
6550     // Number of items in 2nd vector with hAllocation = null.
6551     size_t m_2ndNullItemsCount;
6552 
6553     bool ShouldCompact1st() const;
6554     void CleanupAfterFree();
6555 
6556     bool CreateAllocationRequest_LowerAddress(
6557         uint32_t currentFrameIndex,
6558         uint32_t frameInUseCount,
6559         VkDeviceSize bufferImageGranularity,
6560         VkDeviceSize allocSize,
6561         VkDeviceSize allocAlignment,
6562         VmaSuballocationType allocType,
6563         bool canMakeOtherLost,
6564         uint32_t strategy,
6565         VmaAllocationRequest* pAllocationRequest);
6566     bool CreateAllocationRequest_UpperAddress(
6567         uint32_t currentFrameIndex,
6568         uint32_t frameInUseCount,
6569         VkDeviceSize bufferImageGranularity,
6570         VkDeviceSize allocSize,
6571         VkDeviceSize allocAlignment,
6572         VmaSuballocationType allocType,
6573         bool canMakeOtherLost,
6574         uint32_t strategy,
6575         VmaAllocationRequest* pAllocationRequest);
6576 };
6577 
6578 /*
6579 - GetSize() is the original size of allocated memory block.
6580 - m_UsableSize is this size aligned down to a power of two.
6581   All allocations and calculations happen relative to m_UsableSize.
6582 - GetUnusableSize() is the difference between them.
6583   It is repoted as separate, unused range, not available for allocations.
6584 
6585 Node at level 0 has size = m_UsableSize.
6586 Each next level contains nodes with size 2 times smaller than current level.
6587 m_LevelCount is the maximum number of levels to use in the current object.
6588 */
6589 class VmaBlockMetadata_Buddy : public VmaBlockMetadata
6590 {
6591     VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
6592 public:
6593     VmaBlockMetadata_Buddy(VmaAllocator hAllocator);
6594     virtual ~VmaBlockMetadata_Buddy();
6595     virtual void Init(VkDeviceSize size);
6596 
6597     virtual bool Validate() const;
GetAllocationCount()6598     virtual size_t GetAllocationCount() const { return m_AllocationCount; }
GetSumFreeSize()6599     virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); }
6600     virtual VkDeviceSize GetUnusedRangeSizeMax() const;
IsEmpty()6601     virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; }
6602 
6603     virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6604     virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6605 
6606 #if VMA_STATS_STRING_ENABLED
6607     virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6608 #endif
6609 
6610     virtual bool CreateAllocationRequest(
6611         uint32_t currentFrameIndex,
6612         uint32_t frameInUseCount,
6613         VkDeviceSize bufferImageGranularity,
6614         VkDeviceSize allocSize,
6615         VkDeviceSize allocAlignment,
6616         bool upperAddress,
6617         VmaSuballocationType allocType,
6618         bool canMakeOtherLost,
6619         uint32_t strategy,
6620         VmaAllocationRequest* pAllocationRequest);
6621 
6622     virtual bool MakeRequestedAllocationsLost(
6623         uint32_t currentFrameIndex,
6624         uint32_t frameInUseCount,
6625         VmaAllocationRequest* pAllocationRequest);
6626 
6627     virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6628 
CheckCorruption(const void * pBlockData)6629     virtual VkResult CheckCorruption(const void* pBlockData) { return VK_ERROR_FEATURE_NOT_PRESENT; }
6630 
6631     virtual void Alloc(
6632         const VmaAllocationRequest& request,
6633         VmaSuballocationType type,
6634         VkDeviceSize allocSize,
6635         VmaAllocation hAllocation);
6636 
Free(const VmaAllocation allocation)6637     virtual void Free(const VmaAllocation allocation) { FreeAtOffset(allocation, allocation->GetOffset()); }
FreeAtOffset(VkDeviceSize offset)6638     virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VMA_NULL, offset); }
6639 
6640 private:
6641     static const VkDeviceSize MIN_NODE_SIZE = 32;
6642     static const size_t MAX_LEVELS = 30;
6643 
6644     struct ValidationContext
6645     {
6646         size_t calculatedAllocationCount;
6647         size_t calculatedFreeCount;
6648         VkDeviceSize calculatedSumFreeSize;
6649 
ValidationContextValidationContext6650         ValidationContext() :
6651             calculatedAllocationCount(0),
6652             calculatedFreeCount(0),
6653             calculatedSumFreeSize(0) { }
6654     };
6655 
6656     struct Node
6657     {
6658         VkDeviceSize offset;
6659         enum TYPE
6660         {
6661             TYPE_FREE,
6662             TYPE_ALLOCATION,
6663             TYPE_SPLIT,
6664             TYPE_COUNT
6665         } type;
6666         Node* parent;
6667         Node* buddy;
6668 
6669         union
6670         {
6671             struct
6672             {
6673                 Node* prev;
6674                 Node* next;
6675             } free;
6676             struct
6677             {
6678                 VmaAllocation alloc;
6679             } allocation;
6680             struct
6681             {
6682                 Node* leftChild;
6683             } split;
6684         };
6685     };
6686 
6687     // Size of the memory block aligned down to a power of two.
6688     VkDeviceSize m_UsableSize;
6689     uint32_t m_LevelCount;
6690 
6691     Node* m_Root;
6692     struct {
6693         Node* front;
6694         Node* back;
6695     } m_FreeList[MAX_LEVELS];
6696     // Number of nodes in the tree with type == TYPE_ALLOCATION.
6697     size_t m_AllocationCount;
6698     // Number of nodes in the tree with type == TYPE_FREE.
6699     size_t m_FreeCount;
6700     // This includes space wasted due to internal fragmentation. Doesn't include unusable size.
6701     VkDeviceSize m_SumFreeSize;
6702 
GetUnusableSize()6703     VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
6704     void DeleteNode(Node* node);
6705     bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
6706     uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
LevelToNodeSize(uint32_t level)6707     inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
6708     // Alloc passed just for validation. Can be null.
6709     void FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset);
6710     void CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const;
6711     // Adds node to the front of FreeList at given level.
6712     // node->type must be FREE.
6713     // node->free.prev, next can be undefined.
6714     void AddToFreeListFront(uint32_t level, Node* node);
6715     // Removes node from FreeList at given level.
6716     // node->type must be FREE.
6717     // node->free.prev, next stay untouched.
6718     void RemoveFromFreeList(uint32_t level, Node* node);
6719 
6720 #if VMA_STATS_STRING_ENABLED
6721     void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
6722 #endif
6723 };
6724 
6725 /*
6726 Represents a single block of device memory (`VkDeviceMemory`) with all the
6727 data about its regions (aka suballocations, #VmaAllocation), assigned and free.
6728 
6729 Thread-safety: This class must be externally synchronized.
6730 */
6731 class VmaDeviceMemoryBlock
6732 {
6733     VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
6734 public:
6735     VmaBlockMetadata* m_pMetadata;
6736 
6737     VmaDeviceMemoryBlock(VmaAllocator hAllocator);
6738 
~VmaDeviceMemoryBlock()6739     ~VmaDeviceMemoryBlock()
6740     {
6741         VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
6742         VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
6743     }
6744 
6745     // Always call after construction.
6746     void Init(
6747         VmaAllocator hAllocator,
6748         VmaPool hParentPool,
6749         uint32_t newMemoryTypeIndex,
6750         VkDeviceMemory newMemory,
6751         VkDeviceSize newSize,
6752         uint32_t id,
6753         uint32_t algorithm);
6754     // Always call before destruction.
6755     void Destroy(VmaAllocator allocator);
6756 
GetParentPool()6757     VmaPool GetParentPool() const { return m_hParentPool; }
GetDeviceMemory()6758     VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
GetMemoryTypeIndex()6759     uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetId()6760     uint32_t GetId() const { return m_Id; }
GetMappedData()6761     void* GetMappedData() const { return m_pMappedData; }
6762 
6763     // Validates all data structures inside this object. If not valid, returns false.
6764     bool Validate() const;
6765 
6766     VkResult CheckCorruption(VmaAllocator hAllocator);
6767 
6768     // ppData can be null.
6769     VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
6770     void Unmap(VmaAllocator hAllocator, uint32_t count);
6771 
6772     VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
6773     VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
6774 
6775     VkResult BindBufferMemory(
6776         const VmaAllocator hAllocator,
6777         const VmaAllocation hAllocation,
6778         VkDeviceSize allocationLocalOffset,
6779         VkBuffer hBuffer,
6780         const void* pNext);
6781     VkResult BindImageMemory(
6782         const VmaAllocator hAllocator,
6783         const VmaAllocation hAllocation,
6784         VkDeviceSize allocationLocalOffset,
6785         VkImage hImage,
6786         const void* pNext);
6787 
6788 private:
6789     VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
6790     uint32_t m_MemoryTypeIndex;
6791     uint32_t m_Id;
6792     VkDeviceMemory m_hMemory;
6793 
6794     /*
6795     Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
6796     Also protects m_MapCount, m_pMappedData.
6797     Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
6798     */
6799     VMA_MUTEX m_Mutex;
6800     uint32_t m_MapCount;
6801     void* m_pMappedData;
6802 };
6803 
6804 struct VmaPointerLess
6805 {
operatorVmaPointerLess6806     bool operator()(const void* lhs, const void* rhs) const
6807     {
6808         return lhs < rhs;
6809     }
6810 };
6811 
6812 struct VmaDefragmentationMove
6813 {
6814     size_t srcBlockIndex;
6815     size_t dstBlockIndex;
6816     VkDeviceSize srcOffset;
6817     VkDeviceSize dstOffset;
6818     VkDeviceSize size;
6819     VmaAllocation hAllocation;
6820     VmaDeviceMemoryBlock* pSrcBlock;
6821     VmaDeviceMemoryBlock* pDstBlock;
6822 };
6823 
6824 class VmaDefragmentationAlgorithm;
6825 
6826 /*
6827 Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
6828 Vulkan memory type.
6829 
6830 Synchronized internally with a mutex.
6831 */
6832 struct VmaBlockVector
6833 {
6834     VMA_CLASS_NO_COPY(VmaBlockVector)
6835 public:
6836     VmaBlockVector(
6837         VmaAllocator hAllocator,
6838         VmaPool hParentPool,
6839         uint32_t memoryTypeIndex,
6840         VkDeviceSize preferredBlockSize,
6841         size_t minBlockCount,
6842         size_t maxBlockCount,
6843         VkDeviceSize bufferImageGranularity,
6844         uint32_t frameInUseCount,
6845         bool explicitBlockSize,
6846         uint32_t algorithm);
6847     ~VmaBlockVector();
6848 
6849     VkResult CreateMinBlocks();
6850 
GetAllocatorVmaBlockVector6851     VmaAllocator GetAllocator() const { return m_hAllocator; }
GetParentPoolVmaBlockVector6852     VmaPool GetParentPool() const { return m_hParentPool; }
IsCustomPoolVmaBlockVector6853     bool IsCustomPool() const { return m_hParentPool != VMA_NULL; }
GetMemoryTypeIndexVmaBlockVector6854     uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetPreferredBlockSizeVmaBlockVector6855     VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
GetBufferImageGranularityVmaBlockVector6856     VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
GetFrameInUseCountVmaBlockVector6857     uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
GetAlgorithmVmaBlockVector6858     uint32_t GetAlgorithm() const { return m_Algorithm; }
6859 
6860     void GetPoolStats(VmaPoolStats* pStats);
6861 
6862     bool IsEmpty();
6863     bool IsCorruptionDetectionEnabled() const;
6864 
6865     VkResult Allocate(
6866         uint32_t currentFrameIndex,
6867         VkDeviceSize size,
6868         VkDeviceSize alignment,
6869         const VmaAllocationCreateInfo& createInfo,
6870         VmaSuballocationType suballocType,
6871         size_t allocationCount,
6872         VmaAllocation* pAllocations);
6873 
6874     void Free(const VmaAllocation hAllocation);
6875 
6876     // Adds statistics of this BlockVector to pStats.
6877     void AddStats(VmaStats* pStats);
6878 
6879 #if VMA_STATS_STRING_ENABLED
6880     void PrintDetailedMap(class VmaJsonWriter& json);
6881 #endif
6882 
6883     void MakePoolAllocationsLost(
6884         uint32_t currentFrameIndex,
6885         size_t* pLostAllocationCount);
6886     VkResult CheckCorruption();
6887 
6888     // Saves results in pCtx->res.
6889     void Defragment(
6890         class VmaBlockVectorDefragmentationContext* pCtx,
6891         VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags,
6892         VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
6893         VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
6894         VkCommandBuffer commandBuffer);
6895     void DefragmentationEnd(
6896         class VmaBlockVectorDefragmentationContext* pCtx,
6897         uint32_t flags,
6898         VmaDefragmentationStats* pStats);
6899 
6900     uint32_t ProcessDefragmentations(
6901         class VmaBlockVectorDefragmentationContext *pCtx,
6902         VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves);
6903 
6904     void CommitDefragmentations(
6905         class VmaBlockVectorDefragmentationContext *pCtx,
6906         VmaDefragmentationStats* pStats);
6907 
6908     ////////////////////////////////////////////////////////////////////////////////
6909     // To be used only while the m_Mutex is locked. Used during defragmentation.
6910 
GetBlockCountVmaBlockVector6911     size_t GetBlockCount() const { return m_Blocks.size(); }
GetBlockVmaBlockVector6912     VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
6913     size_t CalcAllocationCount() const;
6914     bool IsBufferImageGranularityConflictPossible() const;
6915 
6916 private:
6917     friend class VmaDefragmentationAlgorithm_Generic;
6918 
6919     const VmaAllocator m_hAllocator;
6920     const VmaPool m_hParentPool;
6921     const uint32_t m_MemoryTypeIndex;
6922     const VkDeviceSize m_PreferredBlockSize;
6923     const size_t m_MinBlockCount;
6924     const size_t m_MaxBlockCount;
6925     const VkDeviceSize m_BufferImageGranularity;
6926     const uint32_t m_FrameInUseCount;
6927     const bool m_ExplicitBlockSize;
6928     const uint32_t m_Algorithm;
6929     VMA_RW_MUTEX m_Mutex;
6930 
6931     /* There can be at most one allocation that is completely empty (except when minBlockCount > 0) -
6932     a hysteresis to avoid pessimistic case of alternating creation and destruction of a VkDeviceMemory. */
6933     bool m_HasEmptyBlock;
6934     // Incrementally sorted by sumFreeSize, ascending.
6935     VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
6936     uint32_t m_NextBlockId;
6937 
6938     VkDeviceSize CalcMaxBlockSize() const;
6939 
6940     // Finds and removes given block from vector.
6941     void Remove(VmaDeviceMemoryBlock* pBlock);
6942 
6943     // Performs single step in sorting m_Blocks. They may not be fully sorted
6944     // after this call.
6945     void IncrementallySortBlocks();
6946 
6947     VkResult AllocatePage(
6948         uint32_t currentFrameIndex,
6949         VkDeviceSize size,
6950         VkDeviceSize alignment,
6951         const VmaAllocationCreateInfo& createInfo,
6952         VmaSuballocationType suballocType,
6953         VmaAllocation* pAllocation);
6954 
6955     // To be used only without CAN_MAKE_OTHER_LOST flag.
6956     VkResult AllocateFromBlock(
6957         VmaDeviceMemoryBlock* pBlock,
6958         uint32_t currentFrameIndex,
6959         VkDeviceSize size,
6960         VkDeviceSize alignment,
6961         VmaAllocationCreateFlags allocFlags,
6962         void* pUserData,
6963         VmaSuballocationType suballocType,
6964         uint32_t strategy,
6965         VmaAllocation* pAllocation);
6966 
6967     VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
6968 
6969     // Saves result to pCtx->res.
6970     void ApplyDefragmentationMovesCpu(
6971         class VmaBlockVectorDefragmentationContext* pDefragCtx,
6972         const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
6973     // Saves result to pCtx->res.
6974     void ApplyDefragmentationMovesGpu(
6975         class VmaBlockVectorDefragmentationContext* pDefragCtx,
6976         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6977         VkCommandBuffer commandBuffer);
6978 
6979     /*
6980     Used during defragmentation. pDefragmentationStats is optional. It's in/out
6981     - updated with new data.
6982     */
6983     void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
6984 
6985     void UpdateHasEmptyBlock();
6986 };
6987 
6988 struct VmaPool_T
6989 {
6990     VMA_CLASS_NO_COPY(VmaPool_T)
6991 public:
6992     VmaBlockVector m_BlockVector;
6993 
6994     VmaPool_T(
6995         VmaAllocator hAllocator,
6996         const VmaPoolCreateInfo& createInfo,
6997         VkDeviceSize preferredBlockSize);
6998     ~VmaPool_T();
6999 
GetIdVmaPool_T7000     uint32_t GetId() const { return m_Id; }
SetIdVmaPool_T7001     void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
7002 
GetNameVmaPool_T7003     const char* GetName() const { return m_Name; }
7004     void SetName(const char* pName);
7005 
7006 #if VMA_STATS_STRING_ENABLED
7007     //void PrintDetailedMap(class VmaStringBuilder& sb);
7008 #endif
7009 
7010 private:
7011     uint32_t m_Id;
7012     char* m_Name;
7013 };
7014 
7015 /*
7016 Performs defragmentation:
7017 
7018 - Updates `pBlockVector->m_pMetadata`.
7019 - Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
7020 - Does not move actual data, only returns requested moves as `moves`.
7021 */
7022 class VmaDefragmentationAlgorithm
7023 {
VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)7024     VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
7025 public:
7026     VmaDefragmentationAlgorithm(
7027         VmaAllocator hAllocator,
7028         VmaBlockVector* pBlockVector,
7029         uint32_t currentFrameIndex) :
7030         m_hAllocator(hAllocator),
7031         m_pBlockVector(pBlockVector),
7032         m_CurrentFrameIndex(currentFrameIndex)
7033     {
7034     }
~VmaDefragmentationAlgorithm()7035     virtual ~VmaDefragmentationAlgorithm()
7036     {
7037     }
7038 
7039     virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
7040     virtual void AddAll() = 0;
7041 
7042     virtual VkResult Defragment(
7043         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7044         VkDeviceSize maxBytesToMove,
7045         uint32_t maxAllocationsToMove,
7046         VmaDefragmentationFlags flags) = 0;
7047 
7048     virtual VkDeviceSize GetBytesMoved() const = 0;
7049     virtual uint32_t GetAllocationsMoved() const = 0;
7050 
7051 protected:
7052     VmaAllocator const m_hAllocator;
7053     VmaBlockVector* const m_pBlockVector;
7054     const uint32_t m_CurrentFrameIndex;
7055 
7056     struct AllocationInfo
7057     {
7058         VmaAllocation m_hAllocation;
7059         VkBool32* m_pChanged;
7060 
AllocationInfoAllocationInfo7061         AllocationInfo() :
7062             m_hAllocation(VK_NULL_HANDLE),
7063             m_pChanged(VMA_NULL)
7064         {
7065         }
AllocationInfoAllocationInfo7066         AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
7067             m_hAllocation(hAlloc),
7068             m_pChanged(pChanged)
7069         {
7070         }
7071     };
7072 };
7073 
7074 class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
7075 {
7076     VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
7077 public:
7078     VmaDefragmentationAlgorithm_Generic(
7079         VmaAllocator hAllocator,
7080         VmaBlockVector* pBlockVector,
7081         uint32_t currentFrameIndex,
7082         bool overlappingMoveSupported);
7083     virtual ~VmaDefragmentationAlgorithm_Generic();
7084 
7085     virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
AddAll()7086     virtual void AddAll() { m_AllAllocations = true; }
7087 
7088     virtual VkResult Defragment(
7089         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7090         VkDeviceSize maxBytesToMove,
7091         uint32_t maxAllocationsToMove,
7092         VmaDefragmentationFlags flags);
7093 
GetBytesMoved()7094     virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
GetAllocationsMoved()7095     virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
7096 
7097 private:
7098     uint32_t m_AllocationCount;
7099     bool m_AllAllocations;
7100 
7101     VkDeviceSize m_BytesMoved;
7102     uint32_t m_AllocationsMoved;
7103 
7104     struct AllocationInfoSizeGreater
7105     {
operatorAllocationInfoSizeGreater7106         bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
7107         {
7108             return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
7109         }
7110     };
7111 
7112     struct AllocationInfoOffsetGreater
7113     {
operatorAllocationInfoOffsetGreater7114         bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
7115         {
7116             return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
7117         }
7118     };
7119 
7120     struct BlockInfo
7121     {
7122         size_t m_OriginalBlockIndex;
7123         VmaDeviceMemoryBlock* m_pBlock;
7124         bool m_HasNonMovableAllocations;
7125         VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
7126 
BlockInfoBlockInfo7127         BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
7128             m_OriginalBlockIndex(SIZE_MAX),
7129             m_pBlock(VMA_NULL),
7130             m_HasNonMovableAllocations(true),
7131             m_Allocations(pAllocationCallbacks)
7132         {
7133         }
7134 
CalcHasNonMovableAllocationsBlockInfo7135         void CalcHasNonMovableAllocations()
7136         {
7137             const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
7138             const size_t defragmentAllocCount = m_Allocations.size();
7139             m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
7140         }
7141 
SortAllocationsBySizeDescendingBlockInfo7142         void SortAllocationsBySizeDescending()
7143         {
7144             VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
7145         }
7146 
SortAllocationsByOffsetDescendingBlockInfo7147         void SortAllocationsByOffsetDescending()
7148         {
7149             VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
7150         }
7151     };
7152 
7153     struct BlockPointerLess
7154     {
operatorBlockPointerLess7155         bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
7156         {
7157             return pLhsBlockInfo->m_pBlock < pRhsBlock;
7158         }
operatorBlockPointerLess7159         bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
7160         {
7161             return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
7162         }
7163     };
7164 
7165     // 1. Blocks with some non-movable allocations go first.
7166     // 2. Blocks with smaller sumFreeSize go first.
7167     struct BlockInfoCompareMoveDestination
7168     {
operatorBlockInfoCompareMoveDestination7169         bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
7170         {
7171             if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
7172             {
7173                 return true;
7174             }
7175             if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
7176             {
7177                 return false;
7178             }
7179             if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
7180             {
7181                 return true;
7182             }
7183             return false;
7184         }
7185     };
7186 
7187     typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
7188     BlockInfoVector m_Blocks;
7189 
7190     VkResult DefragmentRound(
7191         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7192         VkDeviceSize maxBytesToMove,
7193         uint32_t maxAllocationsToMove,
7194         bool freeOldAllocations);
7195 
7196     size_t CalcBlocksWithNonMovableCount() const;
7197 
7198     static bool MoveMakesSense(
7199         size_t dstBlockIndex, VkDeviceSize dstOffset,
7200         size_t srcBlockIndex, VkDeviceSize srcOffset);
7201 };
7202 
7203 class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
7204 {
7205     VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
7206 public:
7207     VmaDefragmentationAlgorithm_Fast(
7208         VmaAllocator hAllocator,
7209         VmaBlockVector* pBlockVector,
7210         uint32_t currentFrameIndex,
7211         bool overlappingMoveSupported);
7212     virtual ~VmaDefragmentationAlgorithm_Fast();
7213 
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)7214     virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { ++m_AllocationCount; }
AddAll()7215     virtual void AddAll() { m_AllAllocations = true; }
7216 
7217     virtual VkResult Defragment(
7218         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7219         VkDeviceSize maxBytesToMove,
7220         uint32_t maxAllocationsToMove,
7221         VmaDefragmentationFlags flags);
7222 
GetBytesMoved()7223     virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
GetAllocationsMoved()7224     virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
7225 
7226 private:
7227     struct BlockInfo
7228     {
7229         size_t origBlockIndex;
7230     };
7231 
7232     class FreeSpaceDatabase
7233     {
7234     public:
FreeSpaceDatabase()7235         FreeSpaceDatabase()
7236         {
7237             FreeSpace s = {};
7238             s.blockInfoIndex = SIZE_MAX;
7239             for(size_t i = 0; i < MAX_COUNT; ++i)
7240             {
7241                 m_FreeSpaces[i] = s;
7242             }
7243         }
7244 
Register(size_t blockInfoIndex,VkDeviceSize offset,VkDeviceSize size)7245         void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
7246         {
7247             if(size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7248             {
7249                 return;
7250             }
7251 
7252             // Find first invalid or the smallest structure.
7253             size_t bestIndex = SIZE_MAX;
7254             for(size_t i = 0; i < MAX_COUNT; ++i)
7255             {
7256                 // Empty structure.
7257                 if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
7258                 {
7259                     bestIndex = i;
7260                     break;
7261                 }
7262                 if(m_FreeSpaces[i].size < size &&
7263                     (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
7264                 {
7265                     bestIndex = i;
7266                 }
7267             }
7268 
7269             if(bestIndex != SIZE_MAX)
7270             {
7271                 m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
7272                 m_FreeSpaces[bestIndex].offset = offset;
7273                 m_FreeSpaces[bestIndex].size = size;
7274             }
7275         }
7276 
Fetch(VkDeviceSize alignment,VkDeviceSize size,size_t & outBlockInfoIndex,VkDeviceSize & outDstOffset)7277         bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
7278             size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
7279         {
7280             size_t bestIndex = SIZE_MAX;
7281             VkDeviceSize bestFreeSpaceAfter = 0;
7282             for(size_t i = 0; i < MAX_COUNT; ++i)
7283             {
7284                 // Structure is valid.
7285                 if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
7286                 {
7287                     const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment);
7288                     // Allocation fits into this structure.
7289                     if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
7290                     {
7291                         const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
7292                             (dstOffset + size);
7293                         if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
7294                         {
7295                             bestIndex = i;
7296                             bestFreeSpaceAfter = freeSpaceAfter;
7297                         }
7298                     }
7299                 }
7300             }
7301 
7302             if(bestIndex != SIZE_MAX)
7303             {
7304                 outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
7305                 outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment);
7306 
7307                 if(bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7308                 {
7309                     // Leave this structure for remaining empty space.
7310                     const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
7311                     m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
7312                     m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
7313                 }
7314                 else
7315                 {
7316                     // This structure becomes invalid.
7317                     m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX;
7318                 }
7319 
7320                 return true;
7321             }
7322 
7323             return false;
7324         }
7325 
7326     private:
7327         static const size_t MAX_COUNT = 4;
7328 
7329         struct FreeSpace
7330         {
7331             size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
7332             VkDeviceSize offset;
7333             VkDeviceSize size;
7334         } m_FreeSpaces[MAX_COUNT];
7335     };
7336 
7337     const bool m_OverlappingMoveSupported;
7338 
7339     uint32_t m_AllocationCount;
7340     bool m_AllAllocations;
7341 
7342     VkDeviceSize m_BytesMoved;
7343     uint32_t m_AllocationsMoved;
7344 
7345     VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
7346 
7347     void PreprocessMetadata();
7348     void PostprocessMetadata();
7349     void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
7350 };
7351 
7352 struct VmaBlockDefragmentationContext
7353 {
7354     enum BLOCK_FLAG
7355     {
7356         BLOCK_FLAG_USED = 0x00000001,
7357     };
7358     uint32_t flags;
7359     VkBuffer hBuffer;
7360 };
7361 
7362 class VmaBlockVectorDefragmentationContext
7363 {
7364     VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
7365 public:
7366     VkResult res;
7367     bool mutexLocked;
7368     VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
7369     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > defragmentationMoves;
7370     uint32_t defragmentationMovesProcessed;
7371     uint32_t defragmentationMovesCommitted;
7372     bool hasDefragmentationPlan;
7373 
7374     VmaBlockVectorDefragmentationContext(
7375         VmaAllocator hAllocator,
7376         VmaPool hCustomPool, // Optional.
7377         VmaBlockVector* pBlockVector,
7378         uint32_t currFrameIndex);
7379     ~VmaBlockVectorDefragmentationContext();
7380 
GetCustomPool()7381     VmaPool GetCustomPool() const { return m_hCustomPool; }
GetBlockVector()7382     VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
GetAlgorithm()7383     VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
7384 
7385     void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
AddAll()7386     void AddAll() { m_AllAllocations = true; }
7387 
7388     void Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags);
7389 
7390 private:
7391     const VmaAllocator m_hAllocator;
7392     // Null if not from custom pool.
7393     const VmaPool m_hCustomPool;
7394     // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
7395     VmaBlockVector* const m_pBlockVector;
7396     const uint32_t m_CurrFrameIndex;
7397     // Owner of this object.
7398     VmaDefragmentationAlgorithm* m_pAlgorithm;
7399 
7400     struct AllocInfo
7401     {
7402         VmaAllocation hAlloc;
7403         VkBool32* pChanged;
7404     };
7405     // Used between constructor and Begin.
7406     VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
7407     bool m_AllAllocations;
7408 };
7409 
7410 struct VmaDefragmentationContext_T
7411 {
7412 private:
7413     VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
7414 public:
7415     VmaDefragmentationContext_T(
7416         VmaAllocator hAllocator,
7417         uint32_t currFrameIndex,
7418         uint32_t flags,
7419         VmaDefragmentationStats* pStats);
7420     ~VmaDefragmentationContext_T();
7421 
7422     void AddPools(uint32_t poolCount, const VmaPool* pPools);
7423     void AddAllocations(
7424         uint32_t allocationCount,
7425         const VmaAllocation* pAllocations,
7426         VkBool32* pAllocationsChanged);
7427 
7428     /*
7429     Returns:
7430     - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
7431     - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
7432     - Negative value if error occured and object can be destroyed immediately.
7433     */
7434     VkResult Defragment(
7435         VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
7436         VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
7437         VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags);
7438 
7439     VkResult DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo);
7440     VkResult DefragmentPassEnd();
7441 
7442 private:
7443     const VmaAllocator m_hAllocator;
7444     const uint32_t m_CurrFrameIndex;
7445     const uint32_t m_Flags;
7446     VmaDefragmentationStats* const m_pStats;
7447 
7448     VkDeviceSize m_MaxCpuBytesToMove;
7449     uint32_t m_MaxCpuAllocationsToMove;
7450     VkDeviceSize m_MaxGpuBytesToMove;
7451     uint32_t m_MaxGpuAllocationsToMove;
7452 
7453     // Owner of these objects.
7454     VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
7455     // Owner of these objects.
7456     VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
7457 };
7458 
7459 #if VMA_RECORDING_ENABLED
7460 
7461 class VmaRecorder
7462 {
7463 public:
7464     VmaRecorder();
7465     VkResult Init(const VmaRecordSettings& settings, bool useMutex);
7466     void WriteConfiguration(
7467         const VkPhysicalDeviceProperties& devProps,
7468         const VkPhysicalDeviceMemoryProperties& memProps,
7469         uint32_t vulkanApiVersion,
7470         bool dedicatedAllocationExtensionEnabled,
7471         bool bindMemory2ExtensionEnabled,
7472         bool memoryBudgetExtensionEnabled,
7473         bool deviceCoherentMemoryExtensionEnabled);
7474     ~VmaRecorder();
7475 
7476     void RecordCreateAllocator(uint32_t frameIndex);
7477     void RecordDestroyAllocator(uint32_t frameIndex);
7478     void RecordCreatePool(uint32_t frameIndex,
7479         const VmaPoolCreateInfo& createInfo,
7480         VmaPool pool);
7481     void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
7482     void RecordAllocateMemory(uint32_t frameIndex,
7483         const VkMemoryRequirements& vkMemReq,
7484         const VmaAllocationCreateInfo& createInfo,
7485         VmaAllocation allocation);
7486     void RecordAllocateMemoryPages(uint32_t frameIndex,
7487         const VkMemoryRequirements& vkMemReq,
7488         const VmaAllocationCreateInfo& createInfo,
7489         uint64_t allocationCount,
7490         const VmaAllocation* pAllocations);
7491     void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
7492         const VkMemoryRequirements& vkMemReq,
7493         bool requiresDedicatedAllocation,
7494         bool prefersDedicatedAllocation,
7495         const VmaAllocationCreateInfo& createInfo,
7496         VmaAllocation allocation);
7497     void RecordAllocateMemoryForImage(uint32_t frameIndex,
7498         const VkMemoryRequirements& vkMemReq,
7499         bool requiresDedicatedAllocation,
7500         bool prefersDedicatedAllocation,
7501         const VmaAllocationCreateInfo& createInfo,
7502         VmaAllocation allocation);
7503     void RecordFreeMemory(uint32_t frameIndex,
7504         VmaAllocation allocation);
7505     void RecordFreeMemoryPages(uint32_t frameIndex,
7506         uint64_t allocationCount,
7507         const VmaAllocation* pAllocations);
7508     void RecordSetAllocationUserData(uint32_t frameIndex,
7509         VmaAllocation allocation,
7510         const void* pUserData);
7511     void RecordCreateLostAllocation(uint32_t frameIndex,
7512         VmaAllocation allocation);
7513     void RecordMapMemory(uint32_t frameIndex,
7514         VmaAllocation allocation);
7515     void RecordUnmapMemory(uint32_t frameIndex,
7516         VmaAllocation allocation);
7517     void RecordFlushAllocation(uint32_t frameIndex,
7518         VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
7519     void RecordInvalidateAllocation(uint32_t frameIndex,
7520         VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
7521     void RecordCreateBuffer(uint32_t frameIndex,
7522         const VkBufferCreateInfo& bufCreateInfo,
7523         const VmaAllocationCreateInfo& allocCreateInfo,
7524         VmaAllocation allocation);
7525     void RecordCreateImage(uint32_t frameIndex,
7526         const VkImageCreateInfo& imageCreateInfo,
7527         const VmaAllocationCreateInfo& allocCreateInfo,
7528         VmaAllocation allocation);
7529     void RecordDestroyBuffer(uint32_t frameIndex,
7530         VmaAllocation allocation);
7531     void RecordDestroyImage(uint32_t frameIndex,
7532         VmaAllocation allocation);
7533     void RecordTouchAllocation(uint32_t frameIndex,
7534         VmaAllocation allocation);
7535     void RecordGetAllocationInfo(uint32_t frameIndex,
7536         VmaAllocation allocation);
7537     void RecordMakePoolAllocationsLost(uint32_t frameIndex,
7538         VmaPool pool);
7539     void RecordDefragmentationBegin(uint32_t frameIndex,
7540         const VmaDefragmentationInfo2& info,
7541         VmaDefragmentationContext ctx);
7542     void RecordDefragmentationEnd(uint32_t frameIndex,
7543         VmaDefragmentationContext ctx);
7544     void RecordSetPoolName(uint32_t frameIndex,
7545         VmaPool pool,
7546         const char* name);
7547 
7548 private:
7549     struct CallParams
7550     {
7551         uint32_t threadId;
7552         double time;
7553     };
7554 
7555     class UserDataString
7556     {
7557     public:
7558         UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
GetString()7559         const char* GetString() const { return m_Str; }
7560 
7561     private:
7562         char m_PtrStr[17];
7563         const char* m_Str;
7564     };
7565 
7566     bool m_UseMutex;
7567     VmaRecordFlags m_Flags;
7568     FILE* m_File;
7569     VMA_MUTEX m_FileMutex;
7570     std::chrono::time_point<std::chrono::high_resolution_clock> m_RecordingStartTime;
7571 
7572     void GetBasicParams(CallParams& outParams);
7573 
7574     // T must be a pointer type, e.g. VmaAllocation, VmaPool.
7575     template<typename T>
PrintPointerList(uint64_t count,const T * pItems)7576     void PrintPointerList(uint64_t count, const T* pItems)
7577     {
7578         if(count)
7579         {
7580             fprintf(m_File, "%p", pItems[0]);
7581             for(uint64_t i = 1; i < count; ++i)
7582             {
7583                 fprintf(m_File, " %p", pItems[i]);
7584             }
7585         }
7586     }
7587 
7588     void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
7589     void Flush();
7590 };
7591 
7592 #endif // #if VMA_RECORDING_ENABLED
7593 
7594 /*
7595 Thread-safe wrapper over VmaPoolAllocator free list, for allocation of VmaAllocation_T objects.
7596 */
7597 class VmaAllocationObjectAllocator
7598 {
7599     VMA_CLASS_NO_COPY(VmaAllocationObjectAllocator)
7600 public:
7601     VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks);
7602 
7603     template<typename... Types> VmaAllocation Allocate(Types... args);
7604     void Free(VmaAllocation hAlloc);
7605 
7606 private:
7607     VMA_MUTEX m_Mutex;
7608     VmaPoolAllocator<VmaAllocation_T> m_Allocator;
7609 };
7610 
7611 struct VmaCurrentBudgetData
7612 {
7613     VMA_ATOMIC_UINT64 m_BlockBytes[VK_MAX_MEMORY_HEAPS];
7614     VMA_ATOMIC_UINT64 m_AllocationBytes[VK_MAX_MEMORY_HEAPS];
7615 
7616 #if VMA_MEMORY_BUDGET
7617     VMA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch;
7618     VMA_RW_MUTEX m_BudgetMutex;
7619     uint64_t m_VulkanUsage[VK_MAX_MEMORY_HEAPS];
7620     uint64_t m_VulkanBudget[VK_MAX_MEMORY_HEAPS];
7621     uint64_t m_BlockBytesAtBudgetFetch[VK_MAX_MEMORY_HEAPS];
7622 #endif // #if VMA_MEMORY_BUDGET
7623 
VmaCurrentBudgetDataVmaCurrentBudgetData7624     VmaCurrentBudgetData()
7625     {
7626         for(uint32_t heapIndex = 0; heapIndex < VK_MAX_MEMORY_HEAPS; ++heapIndex)
7627         {
7628             m_BlockBytes[heapIndex] = 0;
7629             m_AllocationBytes[heapIndex] = 0;
7630 #if VMA_MEMORY_BUDGET
7631             m_VulkanUsage[heapIndex] = 0;
7632             m_VulkanBudget[heapIndex] = 0;
7633             m_BlockBytesAtBudgetFetch[heapIndex] = 0;
7634 #endif
7635         }
7636 
7637 #if VMA_MEMORY_BUDGET
7638         m_OperationsSinceBudgetFetch = 0;
7639 #endif
7640     }
7641 
AddAllocationVmaCurrentBudgetData7642     void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
7643     {
7644         m_AllocationBytes[heapIndex] += allocationSize;
7645 #if VMA_MEMORY_BUDGET
7646         ++m_OperationsSinceBudgetFetch;
7647 #endif
7648     }
7649 
RemoveAllocationVmaCurrentBudgetData7650     void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
7651     {
7652         VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize); // DELME
7653         m_AllocationBytes[heapIndex] -= allocationSize;
7654 #if VMA_MEMORY_BUDGET
7655         ++m_OperationsSinceBudgetFetch;
7656 #endif
7657     }
7658 };
7659 
7660 // Main allocator object.
7661 struct VmaAllocator_T
7662 {
7663     VMA_CLASS_NO_COPY(VmaAllocator_T)
7664 public:
7665     bool m_UseMutex;
7666     uint32_t m_VulkanApiVersion;
7667     bool m_UseKhrDedicatedAllocation; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
7668     bool m_UseKhrBindMemory2; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
7669     bool m_UseExtMemoryBudget;
7670     bool m_UseAmdDeviceCoherentMemory;
7671     bool m_UseKhrBufferDeviceAddress;
7672     VkDevice m_hDevice;
7673     VkInstance m_hInstance;
7674     bool m_AllocationCallbacksSpecified;
7675     VkAllocationCallbacks m_AllocationCallbacks;
7676     VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
7677     VmaAllocationObjectAllocator m_AllocationObjectAllocator;
7678 
7679     // Each bit (1 << i) is set if HeapSizeLimit is enabled for that heap, so cannot allocate more than the heap size.
7680     uint32_t m_HeapSizeLimitMask;
7681 
7682     VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
7683     VkPhysicalDeviceMemoryProperties m_MemProps;
7684 
7685     // Default pools.
7686     VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
7687 
7688     // Each vector is sorted by memory (handle value).
7689     typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
7690     AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES];
7691     VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
7692 
7693     VmaCurrentBudgetData m_Budget;
7694 
7695     VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
7696     VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
7697     ~VmaAllocator_T();
7698 
GetAllocationCallbacksVmaAllocator_T7699     const VkAllocationCallbacks* GetAllocationCallbacks() const
7700     {
7701         return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
7702     }
GetVulkanFunctionsVmaAllocator_T7703     const VmaVulkanFunctions& GetVulkanFunctions() const
7704     {
7705         return m_VulkanFunctions;
7706     }
7707 
GetPhysicalDeviceVmaAllocator_T7708     VkPhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; }
7709 
GetBufferImageGranularityVmaAllocator_T7710     VkDeviceSize GetBufferImageGranularity() const
7711     {
7712         return VMA_MAX(
7713             static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
7714             m_PhysicalDeviceProperties.limits.bufferImageGranularity);
7715     }
7716 
GetMemoryHeapCountVmaAllocator_T7717     uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
GetMemoryTypeCountVmaAllocator_T7718     uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
7719 
MemoryTypeIndexToHeapIndexVmaAllocator_T7720     uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
7721     {
7722         VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
7723         return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
7724     }
7725     // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
IsMemoryTypeNonCoherentVmaAllocator_T7726     bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
7727     {
7728         return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
7729             VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
7730     }
7731     // Minimum alignment for all allocations in specific memory type.
GetMemoryTypeMinAlignmentVmaAllocator_T7732     VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
7733     {
7734         return IsMemoryTypeNonCoherent(memTypeIndex) ?
7735             VMA_MAX((VkDeviceSize)VMA_DEBUG_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
7736             (VkDeviceSize)VMA_DEBUG_ALIGNMENT;
7737     }
7738 
IsIntegratedGpuVmaAllocator_T7739     bool IsIntegratedGpu() const
7740     {
7741         return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
7742     }
7743 
GetGlobalMemoryTypeBitsVmaAllocator_T7744     uint32_t GetGlobalMemoryTypeBits() const { return m_GlobalMemoryTypeBits; }
7745 
7746 #if VMA_RECORDING_ENABLED
GetRecorderVmaAllocator_T7747     VmaRecorder* GetRecorder() const { return m_pRecorder; }
7748 #endif
7749 
7750     void GetBufferMemoryRequirements(
7751         VkBuffer hBuffer,
7752         VkMemoryRequirements& memReq,
7753         bool& requiresDedicatedAllocation,
7754         bool& prefersDedicatedAllocation) const;
7755     void GetImageMemoryRequirements(
7756         VkImage hImage,
7757         VkMemoryRequirements& memReq,
7758         bool& requiresDedicatedAllocation,
7759         bool& prefersDedicatedAllocation) const;
7760 
7761     // Main allocation function.
7762     VkResult AllocateMemory(
7763         const VkMemoryRequirements& vkMemReq,
7764         bool requiresDedicatedAllocation,
7765         bool prefersDedicatedAllocation,
7766         VkBuffer dedicatedBuffer,
7767         VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown.
7768         VkImage dedicatedImage,
7769         const VmaAllocationCreateInfo& createInfo,
7770         VmaSuballocationType suballocType,
7771         size_t allocationCount,
7772         VmaAllocation* pAllocations);
7773 
7774     // Main deallocation function.
7775     void FreeMemory(
7776         size_t allocationCount,
7777         const VmaAllocation* pAllocations);
7778 
7779     VkResult ResizeAllocation(
7780         const VmaAllocation alloc,
7781         VkDeviceSize newSize);
7782 
7783     void CalculateStats(VmaStats* pStats);
7784 
7785     void GetBudget(
7786         VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount);
7787 
7788 #if VMA_STATS_STRING_ENABLED
7789     void PrintDetailedMap(class VmaJsonWriter& json);
7790 #endif
7791 
7792     VkResult DefragmentationBegin(
7793         const VmaDefragmentationInfo2& info,
7794         VmaDefragmentationStats* pStats,
7795         VmaDefragmentationContext* pContext);
7796     VkResult DefragmentationEnd(
7797         VmaDefragmentationContext context);
7798 
7799     VkResult DefragmentationPassBegin(
7800         VmaDefragmentationPassInfo* pInfo,
7801         VmaDefragmentationContext context);
7802     VkResult DefragmentationPassEnd(
7803         VmaDefragmentationContext context);
7804 
7805     void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
7806     bool TouchAllocation(VmaAllocation hAllocation);
7807 
7808     VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
7809     void DestroyPool(VmaPool pool);
7810     void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
7811 
7812     void SetCurrentFrameIndex(uint32_t frameIndex);
GetCurrentFrameIndexVmaAllocator_T7813     uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
7814 
7815     void MakePoolAllocationsLost(
7816         VmaPool hPool,
7817         size_t* pLostAllocationCount);
7818     VkResult CheckPoolCorruption(VmaPool hPool);
7819     VkResult CheckCorruption(uint32_t memoryTypeBits);
7820 
7821     void CreateLostAllocation(VmaAllocation* pAllocation);
7822 
7823     // Call to Vulkan function vkAllocateMemory with accompanying bookkeeping.
7824     VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
7825     // Call to Vulkan function vkFreeMemory with accompanying bookkeeping.
7826     void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
7827     // Call to Vulkan function vkBindBufferMemory or vkBindBufferMemory2KHR.
7828     VkResult BindVulkanBuffer(
7829         VkDeviceMemory memory,
7830         VkDeviceSize memoryOffset,
7831         VkBuffer buffer,
7832         const void* pNext);
7833     // Call to Vulkan function vkBindImageMemory or vkBindImageMemory2KHR.
7834     VkResult BindVulkanImage(
7835         VkDeviceMemory memory,
7836         VkDeviceSize memoryOffset,
7837         VkImage image,
7838         const void* pNext);
7839 
7840     VkResult Map(VmaAllocation hAllocation, void** ppData);
7841     void Unmap(VmaAllocation hAllocation);
7842 
7843     VkResult BindBufferMemory(
7844         VmaAllocation hAllocation,
7845         VkDeviceSize allocationLocalOffset,
7846         VkBuffer hBuffer,
7847         const void* pNext);
7848     VkResult BindImageMemory(
7849         VmaAllocation hAllocation,
7850         VkDeviceSize allocationLocalOffset,
7851         VkImage hImage,
7852         const void* pNext);
7853 
7854     VkResult FlushOrInvalidateAllocation(
7855         VmaAllocation hAllocation,
7856         VkDeviceSize offset, VkDeviceSize size,
7857         VMA_CACHE_OPERATION op);
7858     VkResult FlushOrInvalidateAllocations(
7859         uint32_t allocationCount,
7860         const VmaAllocation* allocations,
7861         const VkDeviceSize* offsets, const VkDeviceSize* sizes,
7862         VMA_CACHE_OPERATION op);
7863 
7864     void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
7865 
7866     /*
7867     Returns bit mask of memory types that can support defragmentation on GPU as
7868     they support creation of required buffer for copy operations.
7869     */
7870     uint32_t GetGpuDefragmentationMemoryTypeBits();
7871 
7872 private:
7873     VkDeviceSize m_PreferredLargeHeapBlockSize;
7874 
7875     VkPhysicalDevice m_PhysicalDevice;
7876     VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
7877     VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized.
7878 
7879     VMA_RW_MUTEX m_PoolsMutex;
7880     // Protected by m_PoolsMutex. Sorted by pointer value.
7881     VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools;
7882     uint32_t m_NextPoolId;
7883 
7884     VmaVulkanFunctions m_VulkanFunctions;
7885 
7886     // Global bit mask AND-ed with any memoryTypeBits to disallow certain memory types.
7887     uint32_t m_GlobalMemoryTypeBits;
7888 
7889 #if VMA_RECORDING_ENABLED
7890     VmaRecorder* m_pRecorder;
7891 #endif
7892 
7893     void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
7894 
7895 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
7896     void ImportVulkanFunctions_Static();
7897 #endif
7898 
7899     void ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions);
7900 
7901 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
7902     void ImportVulkanFunctions_Dynamic();
7903 #endif
7904 
7905     void ValidateVulkanFunctions();
7906 
7907     VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
7908 
7909     VkResult AllocateMemoryOfType(
7910         VkDeviceSize size,
7911         VkDeviceSize alignment,
7912         bool dedicatedAllocation,
7913         VkBuffer dedicatedBuffer,
7914         VkBufferUsageFlags dedicatedBufferUsage,
7915         VkImage dedicatedImage,
7916         const VmaAllocationCreateInfo& createInfo,
7917         uint32_t memTypeIndex,
7918         VmaSuballocationType suballocType,
7919         size_t allocationCount,
7920         VmaAllocation* pAllocations);
7921 
7922     // Helper function only to be used inside AllocateDedicatedMemory.
7923     VkResult AllocateDedicatedMemoryPage(
7924         VkDeviceSize size,
7925         VmaSuballocationType suballocType,
7926         uint32_t memTypeIndex,
7927         const VkMemoryAllocateInfo& allocInfo,
7928         bool map,
7929         bool isUserDataString,
7930         void* pUserData,
7931         VmaAllocation* pAllocation);
7932 
7933     // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
7934     VkResult AllocateDedicatedMemory(
7935         VkDeviceSize size,
7936         VmaSuballocationType suballocType,
7937         uint32_t memTypeIndex,
7938         bool withinBudget,
7939         bool map,
7940         bool isUserDataString,
7941         void* pUserData,
7942         VkBuffer dedicatedBuffer,
7943         VkBufferUsageFlags dedicatedBufferUsage,
7944         VkImage dedicatedImage,
7945         size_t allocationCount,
7946         VmaAllocation* pAllocations);
7947 
7948     void FreeDedicatedMemory(const VmaAllocation allocation);
7949 
7950     /*
7951     Calculates and returns bit mask of memory types that can support defragmentation
7952     on GPU as they support creation of required buffer for copy operations.
7953     */
7954     uint32_t CalculateGpuDefragmentationMemoryTypeBits() const;
7955 
7956     uint32_t CalculateGlobalMemoryTypeBits() const;
7957 
7958     bool GetFlushOrInvalidateRange(
7959         VmaAllocation allocation,
7960         VkDeviceSize offset, VkDeviceSize size,
7961         VkMappedMemoryRange& outRange) const;
7962 
7963 #if VMA_MEMORY_BUDGET
7964     void UpdateVulkanBudget();
7965 #endif // #if VMA_MEMORY_BUDGET
7966 };
7967 
7968 ////////////////////////////////////////////////////////////////////////////////
7969 // Memory allocation #2 after VmaAllocator_T definition
7970 
VmaMalloc(VmaAllocator hAllocator,size_t size,size_t alignment)7971 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
7972 {
7973     return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
7974 }
7975 
VmaFree(VmaAllocator hAllocator,void * ptr)7976 static void VmaFree(VmaAllocator hAllocator, void* ptr)
7977 {
7978     VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
7979 }
7980 
7981 template<typename T>
VmaAllocate(VmaAllocator hAllocator)7982 static T* VmaAllocate(VmaAllocator hAllocator)
7983 {
7984     return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
7985 }
7986 
7987 template<typename T>
VmaAllocateArray(VmaAllocator hAllocator,size_t count)7988 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
7989 {
7990     return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
7991 }
7992 
7993 template<typename T>
vma_delete(VmaAllocator hAllocator,T * ptr)7994 static void vma_delete(VmaAllocator hAllocator, T* ptr)
7995 {
7996     if(ptr != VMA_NULL)
7997     {
7998         ptr->~T();
7999         VmaFree(hAllocator, ptr);
8000     }
8001 }
8002 
8003 template<typename T>
vma_delete_array(VmaAllocator hAllocator,T * ptr,size_t count)8004 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
8005 {
8006     if(ptr != VMA_NULL)
8007     {
8008         for(size_t i = count; i--; )
8009             ptr[i].~T();
8010         VmaFree(hAllocator, ptr);
8011     }
8012 }
8013 
8014 ////////////////////////////////////////////////////////////////////////////////
8015 // VmaStringBuilder
8016 
8017 #if VMA_STATS_STRING_ENABLED
8018 
8019 class VmaStringBuilder
8020 {
8021 public:
VmaStringBuilder(VmaAllocator alloc)8022     VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
GetLength()8023     size_t GetLength() const { return m_Data.size(); }
GetData()8024     const char* GetData() const { return m_Data.data(); }
8025 
Add(char ch)8026     void Add(char ch) { m_Data.push_back(ch); }
8027     void Add(const char* pStr);
AddNewLine()8028     void AddNewLine() { Add('\n'); }
8029     void AddNumber(uint32_t num);
8030     void AddNumber(uint64_t num);
8031     void AddPointer(const void* ptr);
8032 
8033 private:
8034     VmaVector< char, VmaStlAllocator<char> > m_Data;
8035 };
8036 
Add(const char * pStr)8037 void VmaStringBuilder::Add(const char* pStr)
8038 {
8039     const size_t strLen = strlen(pStr);
8040     if(strLen > 0)
8041     {
8042         const size_t oldCount = m_Data.size();
8043         m_Data.resize(oldCount + strLen);
8044         memcpy(m_Data.data() + oldCount, pStr, strLen);
8045     }
8046 }
8047 
AddNumber(uint32_t num)8048 void VmaStringBuilder::AddNumber(uint32_t num)
8049 {
8050     char buf[11];
8051     buf[10] = '\0';
8052     char *p = &buf[10];
8053     do
8054     {
8055         *--p = '0' + (num % 10);
8056         num /= 10;
8057     }
8058     while(num);
8059     Add(p);
8060 }
8061 
AddNumber(uint64_t num)8062 void VmaStringBuilder::AddNumber(uint64_t num)
8063 {
8064     char buf[21];
8065     buf[20] = '\0';
8066     char *p = &buf[20];
8067     do
8068     {
8069         *--p = '0' + (num % 10);
8070         num /= 10;
8071     }
8072     while(num);
8073     Add(p);
8074 }
8075 
AddPointer(const void * ptr)8076 void VmaStringBuilder::AddPointer(const void* ptr)
8077 {
8078     char buf[21];
8079     VmaPtrToStr(buf, sizeof(buf), ptr);
8080     Add(buf);
8081 }
8082 
8083 #endif // #if VMA_STATS_STRING_ENABLED
8084 
8085 ////////////////////////////////////////////////////////////////////////////////
8086 // VmaJsonWriter
8087 
8088 #if VMA_STATS_STRING_ENABLED
8089 
8090 class VmaJsonWriter
8091 {
8092     VMA_CLASS_NO_COPY(VmaJsonWriter)
8093 public:
8094     VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
8095     ~VmaJsonWriter();
8096 
8097     void BeginObject(bool singleLine = false);
8098     void EndObject();
8099 
8100     void BeginArray(bool singleLine = false);
8101     void EndArray();
8102 
8103     void WriteString(const char* pStr);
8104     void BeginString(const char* pStr = VMA_NULL);
8105     void ContinueString(const char* pStr);
8106     void ContinueString(uint32_t n);
8107     void ContinueString(uint64_t n);
8108     void ContinueString_Pointer(const void* ptr);
8109     void EndString(const char* pStr = VMA_NULL);
8110 
8111     void WriteNumber(uint32_t n);
8112     void WriteNumber(uint64_t n);
8113     void WriteBool(bool b);
8114     void WriteNull();
8115 
8116 private:
8117     static const char* const INDENT;
8118 
8119     enum COLLECTION_TYPE
8120     {
8121         COLLECTION_TYPE_OBJECT,
8122         COLLECTION_TYPE_ARRAY,
8123     };
8124     struct StackItem
8125     {
8126         COLLECTION_TYPE type;
8127         uint32_t valueCount;
8128         bool singleLineMode;
8129     };
8130 
8131     VmaStringBuilder& m_SB;
8132     VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
8133     bool m_InsideString;
8134 
8135     void BeginValue(bool isString);
8136     void WriteIndent(bool oneLess = false);
8137 };
8138 
8139 const char* const VmaJsonWriter::INDENT = "  ";
8140 
VmaJsonWriter(const VkAllocationCallbacks * pAllocationCallbacks,VmaStringBuilder & sb)8141 VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
8142     m_SB(sb),
8143     m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
8144     m_InsideString(false)
8145 {
8146 }
8147 
~VmaJsonWriter()8148 VmaJsonWriter::~VmaJsonWriter()
8149 {
8150     VMA_ASSERT(!m_InsideString);
8151     VMA_ASSERT(m_Stack.empty());
8152 }
8153 
BeginObject(bool singleLine)8154 void VmaJsonWriter::BeginObject(bool singleLine)
8155 {
8156     VMA_ASSERT(!m_InsideString);
8157 
8158     BeginValue(false);
8159     m_SB.Add('{');
8160 
8161     StackItem item;
8162     item.type = COLLECTION_TYPE_OBJECT;
8163     item.valueCount = 0;
8164     item.singleLineMode = singleLine;
8165     m_Stack.push_back(item);
8166 }
8167 
EndObject()8168 void VmaJsonWriter::EndObject()
8169 {
8170     VMA_ASSERT(!m_InsideString);
8171 
8172     WriteIndent(true);
8173     m_SB.Add('}');
8174 
8175     VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
8176     m_Stack.pop_back();
8177 }
8178 
BeginArray(bool singleLine)8179 void VmaJsonWriter::BeginArray(bool singleLine)
8180 {
8181     VMA_ASSERT(!m_InsideString);
8182 
8183     BeginValue(false);
8184     m_SB.Add('[');
8185 
8186     StackItem item;
8187     item.type = COLLECTION_TYPE_ARRAY;
8188     item.valueCount = 0;
8189     item.singleLineMode = singleLine;
8190     m_Stack.push_back(item);
8191 }
8192 
EndArray()8193 void VmaJsonWriter::EndArray()
8194 {
8195     VMA_ASSERT(!m_InsideString);
8196 
8197     WriteIndent(true);
8198     m_SB.Add(']');
8199 
8200     VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
8201     m_Stack.pop_back();
8202 }
8203 
WriteString(const char * pStr)8204 void VmaJsonWriter::WriteString(const char* pStr)
8205 {
8206     BeginString(pStr);
8207     EndString();
8208 }
8209 
BeginString(const char * pStr)8210 void VmaJsonWriter::BeginString(const char* pStr)
8211 {
8212     VMA_ASSERT(!m_InsideString);
8213 
8214     BeginValue(true);
8215     m_SB.Add('"');
8216     m_InsideString = true;
8217     if(pStr != VMA_NULL && pStr[0] != '\0')
8218     {
8219         ContinueString(pStr);
8220     }
8221 }
8222 
ContinueString(const char * pStr)8223 void VmaJsonWriter::ContinueString(const char* pStr)
8224 {
8225     VMA_ASSERT(m_InsideString);
8226 
8227     const size_t strLen = strlen(pStr);
8228     for(size_t i = 0; i < strLen; ++i)
8229     {
8230         char ch = pStr[i];
8231         if(ch == '\\')
8232         {
8233             m_SB.Add("\\\\");
8234         }
8235         else if(ch == '"')
8236         {
8237             m_SB.Add("\\\"");
8238         }
8239         else if(ch >= 32)
8240         {
8241             m_SB.Add(ch);
8242         }
8243         else switch(ch)
8244         {
8245         case '\b':
8246             m_SB.Add("\\b");
8247             break;
8248         case '\f':
8249             m_SB.Add("\\f");
8250             break;
8251         case '\n':
8252             m_SB.Add("\\n");
8253             break;
8254         case '\r':
8255             m_SB.Add("\\r");
8256             break;
8257         case '\t':
8258             m_SB.Add("\\t");
8259             break;
8260         default:
8261             VMA_ASSERT(0 && "Character not currently supported.");
8262             break;
8263         }
8264     }
8265 }
8266 
ContinueString(uint32_t n)8267 void VmaJsonWriter::ContinueString(uint32_t n)
8268 {
8269     VMA_ASSERT(m_InsideString);
8270     m_SB.AddNumber(n);
8271 }
8272 
ContinueString(uint64_t n)8273 void VmaJsonWriter::ContinueString(uint64_t n)
8274 {
8275     VMA_ASSERT(m_InsideString);
8276     m_SB.AddNumber(n);
8277 }
8278 
ContinueString_Pointer(const void * ptr)8279 void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
8280 {
8281     VMA_ASSERT(m_InsideString);
8282     m_SB.AddPointer(ptr);
8283 }
8284 
EndString(const char * pStr)8285 void VmaJsonWriter::EndString(const char* pStr)
8286 {
8287     VMA_ASSERT(m_InsideString);
8288     if(pStr != VMA_NULL && pStr[0] != '\0')
8289     {
8290         ContinueString(pStr);
8291     }
8292     m_SB.Add('"');
8293     m_InsideString = false;
8294 }
8295 
WriteNumber(uint32_t n)8296 void VmaJsonWriter::WriteNumber(uint32_t n)
8297 {
8298     VMA_ASSERT(!m_InsideString);
8299     BeginValue(false);
8300     m_SB.AddNumber(n);
8301 }
8302 
WriteNumber(uint64_t n)8303 void VmaJsonWriter::WriteNumber(uint64_t n)
8304 {
8305     VMA_ASSERT(!m_InsideString);
8306     BeginValue(false);
8307     m_SB.AddNumber(n);
8308 }
8309 
WriteBool(bool b)8310 void VmaJsonWriter::WriteBool(bool b)
8311 {
8312     VMA_ASSERT(!m_InsideString);
8313     BeginValue(false);
8314     m_SB.Add(b ? "true" : "false");
8315 }
8316 
WriteNull()8317 void VmaJsonWriter::WriteNull()
8318 {
8319     VMA_ASSERT(!m_InsideString);
8320     BeginValue(false);
8321     m_SB.Add("null");
8322 }
8323 
BeginValue(bool isString)8324 void VmaJsonWriter::BeginValue(bool isString)
8325 {
8326     if(!m_Stack.empty())
8327     {
8328         StackItem& currItem = m_Stack.back();
8329         if(currItem.type == COLLECTION_TYPE_OBJECT &&
8330             currItem.valueCount % 2 == 0)
8331         {
8332             VMA_ASSERT(isString);
8333         }
8334 
8335         if(currItem.type == COLLECTION_TYPE_OBJECT &&
8336             currItem.valueCount % 2 != 0)
8337         {
8338             m_SB.Add(": ");
8339         }
8340         else if(currItem.valueCount > 0)
8341         {
8342             m_SB.Add(", ");
8343             WriteIndent();
8344         }
8345         else
8346         {
8347             WriteIndent();
8348         }
8349         ++currItem.valueCount;
8350     }
8351 }
8352 
WriteIndent(bool oneLess)8353 void VmaJsonWriter::WriteIndent(bool oneLess)
8354 {
8355     if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
8356     {
8357         m_SB.AddNewLine();
8358 
8359         size_t count = m_Stack.size();
8360         if(count > 0 && oneLess)
8361         {
8362             --count;
8363         }
8364         for(size_t i = 0; i < count; ++i)
8365         {
8366             m_SB.Add(INDENT);
8367         }
8368     }
8369 }
8370 
8371 #endif // #if VMA_STATS_STRING_ENABLED
8372 
8373 ////////////////////////////////////////////////////////////////////////////////
8374 
SetUserData(VmaAllocator hAllocator,void * pUserData)8375 void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
8376 {
8377     if(IsUserDataString())
8378     {
8379         VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
8380 
8381         FreeUserDataString(hAllocator);
8382 
8383         if(pUserData != VMA_NULL)
8384         {
8385             m_pUserData = VmaCreateStringCopy(hAllocator->GetAllocationCallbacks(), (const char*)pUserData);
8386         }
8387     }
8388     else
8389     {
8390         m_pUserData = pUserData;
8391     }
8392 }
8393 
ChangeBlockAllocation(VmaAllocator hAllocator,VmaDeviceMemoryBlock * block,VkDeviceSize offset)8394 void VmaAllocation_T::ChangeBlockAllocation(
8395     VmaAllocator hAllocator,
8396     VmaDeviceMemoryBlock* block,
8397     VkDeviceSize offset)
8398 {
8399     VMA_ASSERT(block != VMA_NULL);
8400     VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
8401 
8402     // Move mapping reference counter from old block to new block.
8403     if(block != m_BlockAllocation.m_Block)
8404     {
8405         uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
8406         if(IsPersistentMap())
8407             ++mapRefCount;
8408         m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
8409         block->Map(hAllocator, mapRefCount, VMA_NULL);
8410     }
8411 
8412     m_BlockAllocation.m_Block = block;
8413     m_BlockAllocation.m_Offset = offset;
8414 }
8415 
ChangeOffset(VkDeviceSize newOffset)8416 void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
8417 {
8418     VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
8419     m_BlockAllocation.m_Offset = newOffset;
8420 }
8421 
GetOffset()8422 VkDeviceSize VmaAllocation_T::GetOffset() const
8423 {
8424     switch(m_Type)
8425     {
8426     case ALLOCATION_TYPE_BLOCK:
8427         return m_BlockAllocation.m_Offset;
8428     case ALLOCATION_TYPE_DEDICATED:
8429         return 0;
8430     default:
8431         VMA_ASSERT(0);
8432         return 0;
8433     }
8434 }
8435 
GetMemory()8436 VkDeviceMemory VmaAllocation_T::GetMemory() const
8437 {
8438     switch(m_Type)
8439     {
8440     case ALLOCATION_TYPE_BLOCK:
8441         return m_BlockAllocation.m_Block->GetDeviceMemory();
8442     case ALLOCATION_TYPE_DEDICATED:
8443         return m_DedicatedAllocation.m_hMemory;
8444     default:
8445         VMA_ASSERT(0);
8446         return VK_NULL_HANDLE;
8447     }
8448 }
8449 
GetMappedData()8450 void* VmaAllocation_T::GetMappedData() const
8451 {
8452     switch(m_Type)
8453     {
8454     case ALLOCATION_TYPE_BLOCK:
8455         if(m_MapCount != 0)
8456         {
8457             void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
8458             VMA_ASSERT(pBlockData != VMA_NULL);
8459             return (char*)pBlockData + m_BlockAllocation.m_Offset;
8460         }
8461         else
8462         {
8463             return VMA_NULL;
8464         }
8465         break;
8466     case ALLOCATION_TYPE_DEDICATED:
8467         VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
8468         return m_DedicatedAllocation.m_pMappedData;
8469     default:
8470         VMA_ASSERT(0);
8471         return VMA_NULL;
8472     }
8473 }
8474 
CanBecomeLost()8475 bool VmaAllocation_T::CanBecomeLost() const
8476 {
8477     switch(m_Type)
8478     {
8479     case ALLOCATION_TYPE_BLOCK:
8480         return m_BlockAllocation.m_CanBecomeLost;
8481     case ALLOCATION_TYPE_DEDICATED:
8482         return false;
8483     default:
8484         VMA_ASSERT(0);
8485         return false;
8486     }
8487 }
8488 
MakeLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)8489 bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
8490 {
8491     VMA_ASSERT(CanBecomeLost());
8492 
8493     /*
8494     Warning: This is a carefully designed algorithm.
8495     Do not modify unless you really know what you're doing :)
8496     */
8497     uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
8498     for(;;)
8499     {
8500         if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
8501         {
8502             VMA_ASSERT(0);
8503             return false;
8504         }
8505         else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
8506         {
8507             return false;
8508         }
8509         else // Last use time earlier than current time.
8510         {
8511             if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
8512             {
8513                 // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
8514                 // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
8515                 return true;
8516             }
8517         }
8518     }
8519 }
8520 
8521 #if VMA_STATS_STRING_ENABLED
8522 
8523 // Correspond to values of enum VmaSuballocationType.
8524 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
8525     "FREE",
8526     "UNKNOWN",
8527     "BUFFER",
8528     "IMAGE_UNKNOWN",
8529     "IMAGE_LINEAR",
8530     "IMAGE_OPTIMAL",
8531 };
8532 
PrintParameters(class VmaJsonWriter & json)8533 void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
8534 {
8535     json.WriteString("Type");
8536     json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
8537 
8538     json.WriteString("Size");
8539     json.WriteNumber(m_Size);
8540 
8541     if(m_pUserData != VMA_NULL)
8542     {
8543         json.WriteString("UserData");
8544         if(IsUserDataString())
8545         {
8546             json.WriteString((const char*)m_pUserData);
8547         }
8548         else
8549         {
8550             json.BeginString();
8551             json.ContinueString_Pointer(m_pUserData);
8552             json.EndString();
8553         }
8554     }
8555 
8556     json.WriteString("CreationFrameIndex");
8557     json.WriteNumber(m_CreationFrameIndex);
8558 
8559     json.WriteString("LastUseFrameIndex");
8560     json.WriteNumber(GetLastUseFrameIndex());
8561 
8562     if(m_BufferImageUsage != 0)
8563     {
8564         json.WriteString("Usage");
8565         json.WriteNumber(m_BufferImageUsage);
8566     }
8567 }
8568 
8569 #endif
8570 
FreeUserDataString(VmaAllocator hAllocator)8571 void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
8572 {
8573     VMA_ASSERT(IsUserDataString());
8574     VmaFreeString(hAllocator->GetAllocationCallbacks(), (char*)m_pUserData);
8575     m_pUserData = VMA_NULL;
8576 }
8577 
BlockAllocMap()8578 void VmaAllocation_T::BlockAllocMap()
8579 {
8580     VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
8581 
8582     if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
8583     {
8584         ++m_MapCount;
8585     }
8586     else
8587     {
8588         VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
8589     }
8590 }
8591 
BlockAllocUnmap()8592 void VmaAllocation_T::BlockAllocUnmap()
8593 {
8594     VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
8595 
8596     if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
8597     {
8598         --m_MapCount;
8599     }
8600     else
8601     {
8602         VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
8603     }
8604 }
8605 
DedicatedAllocMap(VmaAllocator hAllocator,void ** ppData)8606 VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
8607 {
8608     VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
8609 
8610     if(m_MapCount != 0)
8611     {
8612         if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
8613         {
8614             VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
8615             *ppData = m_DedicatedAllocation.m_pMappedData;
8616             ++m_MapCount;
8617             return VK_SUCCESS;
8618         }
8619         else
8620         {
8621             VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
8622             return VK_ERROR_MEMORY_MAP_FAILED;
8623         }
8624     }
8625     else
8626     {
8627         VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
8628             hAllocator->m_hDevice,
8629             m_DedicatedAllocation.m_hMemory,
8630             0, // offset
8631             VK_WHOLE_SIZE,
8632             0, // flags
8633             ppData);
8634         if(result == VK_SUCCESS)
8635         {
8636             m_DedicatedAllocation.m_pMappedData = *ppData;
8637             m_MapCount = 1;
8638         }
8639         return result;
8640     }
8641 }
8642 
DedicatedAllocUnmap(VmaAllocator hAllocator)8643 void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
8644 {
8645     VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
8646 
8647     if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
8648     {
8649         --m_MapCount;
8650         if(m_MapCount == 0)
8651         {
8652             m_DedicatedAllocation.m_pMappedData = VMA_NULL;
8653             (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
8654                 hAllocator->m_hDevice,
8655                 m_DedicatedAllocation.m_hMemory);
8656         }
8657     }
8658     else
8659     {
8660         VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
8661     }
8662 }
8663 
8664 #if VMA_STATS_STRING_ENABLED
8665 
VmaPrintStatInfo(VmaJsonWriter & json,const VmaStatInfo & stat)8666 static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
8667 {
8668     json.BeginObject();
8669 
8670     json.WriteString("Blocks");
8671     json.WriteNumber(stat.blockCount);
8672 
8673     json.WriteString("Allocations");
8674     json.WriteNumber(stat.allocationCount);
8675 
8676     json.WriteString("UnusedRanges");
8677     json.WriteNumber(stat.unusedRangeCount);
8678 
8679     json.WriteString("UsedBytes");
8680     json.WriteNumber(stat.usedBytes);
8681 
8682     json.WriteString("UnusedBytes");
8683     json.WriteNumber(stat.unusedBytes);
8684 
8685     if(stat.allocationCount > 1)
8686     {
8687         json.WriteString("AllocationSize");
8688         json.BeginObject(true);
8689         json.WriteString("Min");
8690         json.WriteNumber(stat.allocationSizeMin);
8691         json.WriteString("Avg");
8692         json.WriteNumber(stat.allocationSizeAvg);
8693         json.WriteString("Max");
8694         json.WriteNumber(stat.allocationSizeMax);
8695         json.EndObject();
8696     }
8697 
8698     if(stat.unusedRangeCount > 1)
8699     {
8700         json.WriteString("UnusedRangeSize");
8701         json.BeginObject(true);
8702         json.WriteString("Min");
8703         json.WriteNumber(stat.unusedRangeSizeMin);
8704         json.WriteString("Avg");
8705         json.WriteNumber(stat.unusedRangeSizeAvg);
8706         json.WriteString("Max");
8707         json.WriteNumber(stat.unusedRangeSizeMax);
8708         json.EndObject();
8709     }
8710 
8711     json.EndObject();
8712 }
8713 
8714 #endif // #if VMA_STATS_STRING_ENABLED
8715 
8716 struct VmaSuballocationItemSizeLess
8717 {
operatorVmaSuballocationItemSizeLess8718     bool operator()(
8719         const VmaSuballocationList::iterator lhs,
8720         const VmaSuballocationList::iterator rhs) const
8721     {
8722         return lhs->size < rhs->size;
8723     }
operatorVmaSuballocationItemSizeLess8724     bool operator()(
8725         const VmaSuballocationList::iterator lhs,
8726         VkDeviceSize rhsSize) const
8727     {
8728         return lhs->size < rhsSize;
8729     }
8730 };
8731 
8732 
8733 ////////////////////////////////////////////////////////////////////////////////
8734 // class VmaBlockMetadata
8735 
VmaBlockMetadata(VmaAllocator hAllocator)8736 VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
8737     m_Size(0),
8738     m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks())
8739 {
8740 }
8741 
8742 #if VMA_STATS_STRING_ENABLED
8743 
PrintDetailedMap_Begin(class VmaJsonWriter & json,VkDeviceSize unusedBytes,size_t allocationCount,size_t unusedRangeCount)8744 void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
8745     VkDeviceSize unusedBytes,
8746     size_t allocationCount,
8747     size_t unusedRangeCount) const
8748 {
8749     json.BeginObject();
8750 
8751     json.WriteString("TotalBytes");
8752     json.WriteNumber(GetSize());
8753 
8754     json.WriteString("UnusedBytes");
8755     json.WriteNumber(unusedBytes);
8756 
8757     json.WriteString("Allocations");
8758     json.WriteNumber((uint64_t)allocationCount);
8759 
8760     json.WriteString("UnusedRanges");
8761     json.WriteNumber((uint64_t)unusedRangeCount);
8762 
8763     json.WriteString("Suballocations");
8764     json.BeginArray();
8765 }
8766 
PrintDetailedMap_Allocation(class VmaJsonWriter & json,VkDeviceSize offset,VmaAllocation hAllocation)8767 void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
8768     VkDeviceSize offset,
8769     VmaAllocation hAllocation) const
8770 {
8771     json.BeginObject(true);
8772 
8773     json.WriteString("Offset");
8774     json.WriteNumber(offset);
8775 
8776     hAllocation->PrintParameters(json);
8777 
8778     json.EndObject();
8779 }
8780 
PrintDetailedMap_UnusedRange(class VmaJsonWriter & json,VkDeviceSize offset,VkDeviceSize size)8781 void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
8782     VkDeviceSize offset,
8783     VkDeviceSize size) const
8784 {
8785     json.BeginObject(true);
8786 
8787     json.WriteString("Offset");
8788     json.WriteNumber(offset);
8789 
8790     json.WriteString("Type");
8791     json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
8792 
8793     json.WriteString("Size");
8794     json.WriteNumber(size);
8795 
8796     json.EndObject();
8797 }
8798 
PrintDetailedMap_End(class VmaJsonWriter & json)8799 void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
8800 {
8801     json.EndArray();
8802     json.EndObject();
8803 }
8804 
8805 #endif // #if VMA_STATS_STRING_ENABLED
8806 
8807 ////////////////////////////////////////////////////////////////////////////////
8808 // class VmaBlockMetadata_Generic
8809 
VmaBlockMetadata_Generic(VmaAllocator hAllocator)8810 VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) :
8811     VmaBlockMetadata(hAllocator),
8812     m_FreeCount(0),
8813     m_SumFreeSize(0),
8814     m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8815     m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
8816 {
8817 }
8818 
~VmaBlockMetadata_Generic()8819 VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
8820 {
8821 }
8822 
Init(VkDeviceSize size)8823 void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
8824 {
8825     VmaBlockMetadata::Init(size);
8826 
8827     m_FreeCount = 1;
8828     m_SumFreeSize = size;
8829 
8830     VmaSuballocation suballoc = {};
8831     suballoc.offset = 0;
8832     suballoc.size = size;
8833     suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8834     suballoc.hAllocation = VK_NULL_HANDLE;
8835 
8836     VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
8837     m_Suballocations.push_back(suballoc);
8838     VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
8839     --suballocItem;
8840     m_FreeSuballocationsBySize.push_back(suballocItem);
8841 }
8842 
Validate()8843 bool VmaBlockMetadata_Generic::Validate() const
8844 {
8845     VMA_VALIDATE(!m_Suballocations.empty());
8846 
8847     // Expected offset of new suballocation as calculated from previous ones.
8848     VkDeviceSize calculatedOffset = 0;
8849     // Expected number of free suballocations as calculated from traversing their list.
8850     uint32_t calculatedFreeCount = 0;
8851     // Expected sum size of free suballocations as calculated from traversing their list.
8852     VkDeviceSize calculatedSumFreeSize = 0;
8853     // Expected number of free suballocations that should be registered in
8854     // m_FreeSuballocationsBySize calculated from traversing their list.
8855     size_t freeSuballocationsToRegister = 0;
8856     // True if previous visited suballocation was free.
8857     bool prevFree = false;
8858 
8859     for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
8860         suballocItem != m_Suballocations.cend();
8861         ++suballocItem)
8862     {
8863         const VmaSuballocation& subAlloc = *suballocItem;
8864 
8865         // Actual offset of this suballocation doesn't match expected one.
8866         VMA_VALIDATE(subAlloc.offset == calculatedOffset);
8867 
8868         const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
8869         // Two adjacent free suballocations are invalid. They should be merged.
8870         VMA_VALIDATE(!prevFree || !currFree);
8871 
8872         VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE));
8873 
8874         if(currFree)
8875         {
8876             calculatedSumFreeSize += subAlloc.size;
8877             ++calculatedFreeCount;
8878             if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8879             {
8880                 ++freeSuballocationsToRegister;
8881             }
8882 
8883             // Margin required between allocations - every free space must be at least that large.
8884             VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);
8885         }
8886         else
8887         {
8888             VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset);
8889             VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size);
8890 
8891             // Margin required between allocations - previous allocation must be free.
8892             VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);
8893         }
8894 
8895         calculatedOffset += subAlloc.size;
8896         prevFree = currFree;
8897     }
8898 
8899     // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
8900     // match expected one.
8901     VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
8902 
8903     VkDeviceSize lastSize = 0;
8904     for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
8905     {
8906         VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
8907 
8908         // Only free suballocations can be registered in m_FreeSuballocationsBySize.
8909         VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
8910         // They must be sorted by size ascending.
8911         VMA_VALIDATE(suballocItem->size >= lastSize);
8912 
8913         lastSize = suballocItem->size;
8914     }
8915 
8916     // Check if totals match calculacted values.
8917     VMA_VALIDATE(ValidateFreeSuballocationList());
8918     VMA_VALIDATE(calculatedOffset == GetSize());
8919     VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
8920     VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
8921 
8922     return true;
8923 }
8924 
GetUnusedRangeSizeMax()8925 VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
8926 {
8927     if(!m_FreeSuballocationsBySize.empty())
8928     {
8929         return m_FreeSuballocationsBySize.back()->size;
8930     }
8931     else
8932     {
8933         return 0;
8934     }
8935 }
8936 
IsEmpty()8937 bool VmaBlockMetadata_Generic::IsEmpty() const
8938 {
8939     return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
8940 }
8941 
CalcAllocationStatInfo(VmaStatInfo & outInfo)8942 void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
8943 {
8944     outInfo.blockCount = 1;
8945 
8946     const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
8947     outInfo.allocationCount = rangeCount - m_FreeCount;
8948     outInfo.unusedRangeCount = m_FreeCount;
8949 
8950     outInfo.unusedBytes = m_SumFreeSize;
8951     outInfo.usedBytes = GetSize() - outInfo.unusedBytes;
8952 
8953     outInfo.allocationSizeMin = UINT64_MAX;
8954     outInfo.allocationSizeMax = 0;
8955     outInfo.unusedRangeSizeMin = UINT64_MAX;
8956     outInfo.unusedRangeSizeMax = 0;
8957 
8958     for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
8959         suballocItem != m_Suballocations.cend();
8960         ++suballocItem)
8961     {
8962         const VmaSuballocation& suballoc = *suballocItem;
8963         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
8964         {
8965             outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
8966             outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
8967         }
8968         else
8969         {
8970             outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
8971             outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
8972         }
8973     }
8974 }
8975 
AddPoolStats(VmaPoolStats & inoutStats)8976 void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
8977 {
8978     const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
8979 
8980     inoutStats.size += GetSize();
8981     inoutStats.unusedSize += m_SumFreeSize;
8982     inoutStats.allocationCount += rangeCount - m_FreeCount;
8983     inoutStats.unusedRangeCount += m_FreeCount;
8984     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
8985 }
8986 
8987 #if VMA_STATS_STRING_ENABLED
8988 
PrintDetailedMap(class VmaJsonWriter & json)8989 void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
8990 {
8991     PrintDetailedMap_Begin(json,
8992         m_SumFreeSize, // unusedBytes
8993         m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
8994         m_FreeCount); // unusedRangeCount
8995 
8996     size_t i = 0;
8997     for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
8998         suballocItem != m_Suballocations.cend();
8999         ++suballocItem, ++i)
9000     {
9001         if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9002         {
9003             PrintDetailedMap_UnusedRange(json, suballocItem->offset, suballocItem->size);
9004         }
9005         else
9006         {
9007             PrintDetailedMap_Allocation(json, suballocItem->offset, suballocItem->hAllocation);
9008         }
9009     }
9010 
9011     PrintDetailedMap_End(json);
9012 }
9013 
9014 #endif // #if VMA_STATS_STRING_ENABLED
9015 
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)9016 bool VmaBlockMetadata_Generic::CreateAllocationRequest(
9017     uint32_t currentFrameIndex,
9018     uint32_t frameInUseCount,
9019     VkDeviceSize bufferImageGranularity,
9020     VkDeviceSize allocSize,
9021     VkDeviceSize allocAlignment,
9022     bool upperAddress,
9023     VmaSuballocationType allocType,
9024     bool canMakeOtherLost,
9025     uint32_t strategy,
9026     VmaAllocationRequest* pAllocationRequest)
9027 {
9028     VMA_ASSERT(allocSize > 0);
9029     VMA_ASSERT(!upperAddress);
9030     VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9031     VMA_ASSERT(pAllocationRequest != VMA_NULL);
9032     VMA_HEAVY_ASSERT(Validate());
9033 
9034     pAllocationRequest->type = VmaAllocationRequestType::Normal;
9035 
9036     // There is not enough total free space in this block to fullfill the request: Early return.
9037     if(canMakeOtherLost == false &&
9038         m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN)
9039     {
9040         return false;
9041     }
9042 
9043     // New algorithm, efficiently searching freeSuballocationsBySize.
9044     const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
9045     if(freeSuballocCount > 0)
9046     {
9047         if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
9048         {
9049             // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN.
9050             VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
9051                 m_FreeSuballocationsBySize.data(),
9052                 m_FreeSuballocationsBySize.data() + freeSuballocCount,
9053                 allocSize + 2 * VMA_DEBUG_MARGIN,
9054                 VmaSuballocationItemSizeLess());
9055             size_t index = it - m_FreeSuballocationsBySize.data();
9056             for(; index < freeSuballocCount; ++index)
9057             {
9058                 if(CheckAllocation(
9059                     currentFrameIndex,
9060                     frameInUseCount,
9061                     bufferImageGranularity,
9062                     allocSize,
9063                     allocAlignment,
9064                     allocType,
9065                     m_FreeSuballocationsBySize[index],
9066                     false, // canMakeOtherLost
9067                     &pAllocationRequest->offset,
9068                     &pAllocationRequest->itemsToMakeLostCount,
9069                     &pAllocationRequest->sumFreeSize,
9070                     &pAllocationRequest->sumItemSize))
9071                 {
9072                     pAllocationRequest->item = m_FreeSuballocationsBySize[index];
9073                     return true;
9074                 }
9075             }
9076         }
9077         else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
9078         {
9079             for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9080                 it != m_Suballocations.end();
9081                 ++it)
9082             {
9083                 if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
9084                     currentFrameIndex,
9085                     frameInUseCount,
9086                     bufferImageGranularity,
9087                     allocSize,
9088                     allocAlignment,
9089                     allocType,
9090                     it,
9091                     false, // canMakeOtherLost
9092                     &pAllocationRequest->offset,
9093                     &pAllocationRequest->itemsToMakeLostCount,
9094                     &pAllocationRequest->sumFreeSize,
9095                     &pAllocationRequest->sumItemSize))
9096                 {
9097                     pAllocationRequest->item = it;
9098                     return true;
9099                 }
9100             }
9101         }
9102         else // WORST_FIT, FIRST_FIT
9103         {
9104             // Search staring from biggest suballocations.
9105             for(size_t index = freeSuballocCount; index--; )
9106             {
9107                 if(CheckAllocation(
9108                     currentFrameIndex,
9109                     frameInUseCount,
9110                     bufferImageGranularity,
9111                     allocSize,
9112                     allocAlignment,
9113                     allocType,
9114                     m_FreeSuballocationsBySize[index],
9115                     false, // canMakeOtherLost
9116                     &pAllocationRequest->offset,
9117                     &pAllocationRequest->itemsToMakeLostCount,
9118                     &pAllocationRequest->sumFreeSize,
9119                     &pAllocationRequest->sumItemSize))
9120                 {
9121                     pAllocationRequest->item = m_FreeSuballocationsBySize[index];
9122                     return true;
9123                 }
9124             }
9125         }
9126     }
9127 
9128     if(canMakeOtherLost)
9129     {
9130         // Brute-force algorithm. TODO: Come up with something better.
9131 
9132         bool found = false;
9133         VmaAllocationRequest tmpAllocRequest = {};
9134         tmpAllocRequest.type = VmaAllocationRequestType::Normal;
9135         for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
9136             suballocIt != m_Suballocations.end();
9137             ++suballocIt)
9138         {
9139             if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
9140                 suballocIt->hAllocation->CanBecomeLost())
9141             {
9142                 if(CheckAllocation(
9143                     currentFrameIndex,
9144                     frameInUseCount,
9145                     bufferImageGranularity,
9146                     allocSize,
9147                     allocAlignment,
9148                     allocType,
9149                     suballocIt,
9150                     canMakeOtherLost,
9151                     &tmpAllocRequest.offset,
9152                     &tmpAllocRequest.itemsToMakeLostCount,
9153                     &tmpAllocRequest.sumFreeSize,
9154                     &tmpAllocRequest.sumItemSize))
9155                 {
9156                     if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
9157                     {
9158                         *pAllocationRequest = tmpAllocRequest;
9159                         pAllocationRequest->item = suballocIt;
9160                         break;
9161                     }
9162                     if(!found || tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost())
9163                     {
9164                         *pAllocationRequest = tmpAllocRequest;
9165                         pAllocationRequest->item = suballocIt;
9166                         found = true;
9167                     }
9168                 }
9169             }
9170         }
9171 
9172         return found;
9173     }
9174 
9175     return false;
9176 }
9177 
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)9178 bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
9179     uint32_t currentFrameIndex,
9180     uint32_t frameInUseCount,
9181     VmaAllocationRequest* pAllocationRequest)
9182 {
9183     VMA_ASSERT(pAllocationRequest && pAllocationRequest->type == VmaAllocationRequestType::Normal);
9184 
9185     while(pAllocationRequest->itemsToMakeLostCount > 0)
9186     {
9187         if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
9188         {
9189             ++pAllocationRequest->item;
9190         }
9191         VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
9192         VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
9193         VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
9194         if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
9195         {
9196             pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
9197             --pAllocationRequest->itemsToMakeLostCount;
9198         }
9199         else
9200         {
9201             return false;
9202         }
9203     }
9204 
9205     VMA_HEAVY_ASSERT(Validate());
9206     VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
9207     VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
9208 
9209     return true;
9210 }
9211 
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)9212 uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
9213 {
9214     uint32_t lostAllocationCount = 0;
9215     for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9216         it != m_Suballocations.end();
9217         ++it)
9218     {
9219         if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
9220             it->hAllocation->CanBecomeLost() &&
9221             it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
9222         {
9223             it = FreeSuballocation(it);
9224             ++lostAllocationCount;
9225         }
9226     }
9227     return lostAllocationCount;
9228 }
9229 
CheckCorruption(const void * pBlockData)9230 VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
9231 {
9232     for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9233         it != m_Suballocations.end();
9234         ++it)
9235     {
9236         if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
9237         {
9238             if(!VmaValidateMagicValue(pBlockData, it->offset - VMA_DEBUG_MARGIN))
9239             {
9240                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
9241                 return VK_ERROR_VALIDATION_FAILED_EXT;
9242             }
9243             if(!VmaValidateMagicValue(pBlockData, it->offset + it->size))
9244             {
9245                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
9246                 return VK_ERROR_VALIDATION_FAILED_EXT;
9247             }
9248         }
9249     }
9250 
9251     return VK_SUCCESS;
9252 }
9253 
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,VmaAllocation hAllocation)9254 void VmaBlockMetadata_Generic::Alloc(
9255     const VmaAllocationRequest& request,
9256     VmaSuballocationType type,
9257     VkDeviceSize allocSize,
9258     VmaAllocation hAllocation)
9259 {
9260     VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
9261     VMA_ASSERT(request.item != m_Suballocations.end());
9262     VmaSuballocation& suballoc = *request.item;
9263     // Given suballocation is a free block.
9264     VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9265     // Given offset is inside this suballocation.
9266     VMA_ASSERT(request.offset >= suballoc.offset);
9267     const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
9268     VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
9269     const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
9270 
9271     // Unregister this free suballocation from m_FreeSuballocationsBySize and update
9272     // it to become used.
9273     UnregisterFreeSuballocation(request.item);
9274 
9275     suballoc.offset = request.offset;
9276     suballoc.size = allocSize;
9277     suballoc.type = type;
9278     suballoc.hAllocation = hAllocation;
9279 
9280     // If there are any free bytes remaining at the end, insert new free suballocation after current one.
9281     if(paddingEnd)
9282     {
9283         VmaSuballocation paddingSuballoc = {};
9284         paddingSuballoc.offset = request.offset + allocSize;
9285         paddingSuballoc.size = paddingEnd;
9286         paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9287         VmaSuballocationList::iterator next = request.item;
9288         ++next;
9289         const VmaSuballocationList::iterator paddingEndItem =
9290             m_Suballocations.insert(next, paddingSuballoc);
9291         RegisterFreeSuballocation(paddingEndItem);
9292     }
9293 
9294     // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
9295     if(paddingBegin)
9296     {
9297         VmaSuballocation paddingSuballoc = {};
9298         paddingSuballoc.offset = request.offset - paddingBegin;
9299         paddingSuballoc.size = paddingBegin;
9300         paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9301         const VmaSuballocationList::iterator paddingBeginItem =
9302             m_Suballocations.insert(request.item, paddingSuballoc);
9303         RegisterFreeSuballocation(paddingBeginItem);
9304     }
9305 
9306     // Update totals.
9307     m_FreeCount = m_FreeCount - 1;
9308     if(paddingBegin > 0)
9309     {
9310         ++m_FreeCount;
9311     }
9312     if(paddingEnd > 0)
9313     {
9314         ++m_FreeCount;
9315     }
9316     m_SumFreeSize -= allocSize;
9317 }
9318 
Free(const VmaAllocation allocation)9319 void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation)
9320 {
9321     for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
9322         suballocItem != m_Suballocations.end();
9323         ++suballocItem)
9324     {
9325         VmaSuballocation& suballoc = *suballocItem;
9326         if(suballoc.hAllocation == allocation)
9327         {
9328             FreeSuballocation(suballocItem);
9329             VMA_HEAVY_ASSERT(Validate());
9330             return;
9331         }
9332     }
9333     VMA_ASSERT(0 && "Not found!");
9334 }
9335 
FreeAtOffset(VkDeviceSize offset)9336 void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
9337 {
9338     for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
9339         suballocItem != m_Suballocations.end();
9340         ++suballocItem)
9341     {
9342         VmaSuballocation& suballoc = *suballocItem;
9343         if(suballoc.offset == offset)
9344         {
9345             FreeSuballocation(suballocItem);
9346             return;
9347         }
9348     }
9349     VMA_ASSERT(0 && "Not found!");
9350 }
9351 
ValidateFreeSuballocationList()9352 bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
9353 {
9354     VkDeviceSize lastSize = 0;
9355     for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
9356     {
9357         const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
9358 
9359         VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
9360         VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
9361         VMA_VALIDATE(it->size >= lastSize);
9362         lastSize = it->size;
9363     }
9364     return true;
9365 }
9366 
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)9367 bool VmaBlockMetadata_Generic::CheckAllocation(
9368     uint32_t currentFrameIndex,
9369     uint32_t frameInUseCount,
9370     VkDeviceSize bufferImageGranularity,
9371     VkDeviceSize allocSize,
9372     VkDeviceSize allocAlignment,
9373     VmaSuballocationType allocType,
9374     VmaSuballocationList::const_iterator suballocItem,
9375     bool canMakeOtherLost,
9376     VkDeviceSize* pOffset,
9377     size_t* itemsToMakeLostCount,
9378     VkDeviceSize* pSumFreeSize,
9379     VkDeviceSize* pSumItemSize) const
9380 {
9381     VMA_ASSERT(allocSize > 0);
9382     VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9383     VMA_ASSERT(suballocItem != m_Suballocations.cend());
9384     VMA_ASSERT(pOffset != VMA_NULL);
9385 
9386     *itemsToMakeLostCount = 0;
9387     *pSumFreeSize = 0;
9388     *pSumItemSize = 0;
9389 
9390     if(canMakeOtherLost)
9391     {
9392         if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9393         {
9394             *pSumFreeSize = suballocItem->size;
9395         }
9396         else
9397         {
9398             if(suballocItem->hAllocation->CanBecomeLost() &&
9399                 suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9400             {
9401                 ++*itemsToMakeLostCount;
9402                 *pSumItemSize = suballocItem->size;
9403             }
9404             else
9405             {
9406                 return false;
9407             }
9408         }
9409 
9410         // Remaining size is too small for this request: Early return.
9411         if(GetSize() - suballocItem->offset < allocSize)
9412         {
9413             return false;
9414         }
9415 
9416         // Start from offset equal to beginning of this suballocation.
9417         *pOffset = suballocItem->offset;
9418 
9419         // Apply VMA_DEBUG_MARGIN at the beginning.
9420         if(VMA_DEBUG_MARGIN > 0)
9421         {
9422             *pOffset += VMA_DEBUG_MARGIN;
9423         }
9424 
9425         // Apply alignment.
9426         *pOffset = VmaAlignUp(*pOffset, allocAlignment);
9427 
9428         // Check previous suballocations for BufferImageGranularity conflicts.
9429         // Make bigger alignment if necessary.
9430         if(bufferImageGranularity > 1)
9431         {
9432             bool bufferImageGranularityConflict = false;
9433             VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
9434             while(prevSuballocItem != m_Suballocations.cbegin())
9435             {
9436                 --prevSuballocItem;
9437                 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
9438                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
9439                 {
9440                     if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9441                     {
9442                         bufferImageGranularityConflict = true;
9443                         break;
9444                     }
9445                 }
9446                 else
9447                     // Already on previous page.
9448                     break;
9449             }
9450             if(bufferImageGranularityConflict)
9451             {
9452                 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
9453             }
9454         }
9455 
9456         // Now that we have final *pOffset, check if we are past suballocItem.
9457         // If yes, return false - this function should be called for another suballocItem as starting point.
9458         if(*pOffset >= suballocItem->offset + suballocItem->size)
9459         {
9460             return false;
9461         }
9462 
9463         // Calculate padding at the beginning based on current offset.
9464         const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
9465 
9466         // Calculate required margin at the end.
9467         const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
9468 
9469         const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
9470         // Another early return check.
9471         if(suballocItem->offset + totalSize > GetSize())
9472         {
9473             return false;
9474         }
9475 
9476         // Advance lastSuballocItem until desired size is reached.
9477         // Update itemsToMakeLostCount.
9478         VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
9479         if(totalSize > suballocItem->size)
9480         {
9481             VkDeviceSize remainingSize = totalSize - suballocItem->size;
9482             while(remainingSize > 0)
9483             {
9484                 ++lastSuballocItem;
9485                 if(lastSuballocItem == m_Suballocations.cend())
9486                 {
9487                     return false;
9488                 }
9489                 if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9490                 {
9491                     *pSumFreeSize += lastSuballocItem->size;
9492                 }
9493                 else
9494                 {
9495                     VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
9496                     if(lastSuballocItem->hAllocation->CanBecomeLost() &&
9497                         lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9498                     {
9499                         ++*itemsToMakeLostCount;
9500                         *pSumItemSize += lastSuballocItem->size;
9501                     }
9502                     else
9503                     {
9504                         return false;
9505                     }
9506                 }
9507                 remainingSize = (lastSuballocItem->size < remainingSize) ?
9508                     remainingSize - lastSuballocItem->size : 0;
9509             }
9510         }
9511 
9512         // Check next suballocations for BufferImageGranularity conflicts.
9513         // If conflict exists, we must mark more allocations lost or fail.
9514         if(bufferImageGranularity > 1)
9515         {
9516             VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
9517             ++nextSuballocItem;
9518             while(nextSuballocItem != m_Suballocations.cend())
9519             {
9520                 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
9521                 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9522                 {
9523                     if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9524                     {
9525                         VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
9526                         if(nextSuballoc.hAllocation->CanBecomeLost() &&
9527                             nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9528                         {
9529                             ++*itemsToMakeLostCount;
9530                         }
9531                         else
9532                         {
9533                             return false;
9534                         }
9535                     }
9536                 }
9537                 else
9538                 {
9539                     // Already on next page.
9540                     break;
9541                 }
9542                 ++nextSuballocItem;
9543             }
9544         }
9545     }
9546     else
9547     {
9548         const VmaSuballocation& suballoc = *suballocItem;
9549         VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9550 
9551         *pSumFreeSize = suballoc.size;
9552 
9553         // Size of this suballocation is too small for this request: Early return.
9554         if(suballoc.size < allocSize)
9555         {
9556             return false;
9557         }
9558 
9559         // Start from offset equal to beginning of this suballocation.
9560         *pOffset = suballoc.offset;
9561 
9562         // Apply VMA_DEBUG_MARGIN at the beginning.
9563         if(VMA_DEBUG_MARGIN > 0)
9564         {
9565             *pOffset += VMA_DEBUG_MARGIN;
9566         }
9567 
9568         // Apply alignment.
9569         *pOffset = VmaAlignUp(*pOffset, allocAlignment);
9570 
9571         // Check previous suballocations for BufferImageGranularity conflicts.
9572         // Make bigger alignment if necessary.
9573         if(bufferImageGranularity > 1)
9574         {
9575             bool bufferImageGranularityConflict = false;
9576             VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
9577             while(prevSuballocItem != m_Suballocations.cbegin())
9578             {
9579                 --prevSuballocItem;
9580                 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
9581                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
9582                 {
9583                     if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9584                     {
9585                         bufferImageGranularityConflict = true;
9586                         break;
9587                     }
9588                 }
9589                 else
9590                     // Already on previous page.
9591                     break;
9592             }
9593             if(bufferImageGranularityConflict)
9594             {
9595                 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
9596             }
9597         }
9598 
9599         // Calculate padding at the beginning based on current offset.
9600         const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
9601 
9602         // Calculate required margin at the end.
9603         const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
9604 
9605         // Fail if requested size plus margin before and after is bigger than size of this suballocation.
9606         if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
9607         {
9608             return false;
9609         }
9610 
9611         // Check next suballocations for BufferImageGranularity conflicts.
9612         // If conflict exists, allocation cannot be made here.
9613         if(bufferImageGranularity > 1)
9614         {
9615             VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
9616             ++nextSuballocItem;
9617             while(nextSuballocItem != m_Suballocations.cend())
9618             {
9619                 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
9620                 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9621                 {
9622                     if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9623                     {
9624                         return false;
9625                     }
9626                 }
9627                 else
9628                 {
9629                     // Already on next page.
9630                     break;
9631                 }
9632                 ++nextSuballocItem;
9633             }
9634         }
9635     }
9636 
9637     // All tests passed: Success. pOffset is already filled.
9638     return true;
9639 }
9640 
MergeFreeWithNext(VmaSuballocationList::iterator item)9641 void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
9642 {
9643     VMA_ASSERT(item != m_Suballocations.end());
9644     VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9645 
9646     VmaSuballocationList::iterator nextItem = item;
9647     ++nextItem;
9648     VMA_ASSERT(nextItem != m_Suballocations.end());
9649     VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
9650 
9651     item->size += nextItem->size;
9652     --m_FreeCount;
9653     m_Suballocations.erase(nextItem);
9654 }
9655 
FreeSuballocation(VmaSuballocationList::iterator suballocItem)9656 VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
9657 {
9658     // Change this suballocation to be marked as free.
9659     VmaSuballocation& suballoc = *suballocItem;
9660     suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9661     suballoc.hAllocation = VK_NULL_HANDLE;
9662 
9663     // Update totals.
9664     ++m_FreeCount;
9665     m_SumFreeSize += suballoc.size;
9666 
9667     // Merge with previous and/or next suballocation if it's also free.
9668     bool mergeWithNext = false;
9669     bool mergeWithPrev = false;
9670 
9671     VmaSuballocationList::iterator nextItem = suballocItem;
9672     ++nextItem;
9673     if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
9674     {
9675         mergeWithNext = true;
9676     }
9677 
9678     VmaSuballocationList::iterator prevItem = suballocItem;
9679     if(suballocItem != m_Suballocations.begin())
9680     {
9681         --prevItem;
9682         if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9683         {
9684             mergeWithPrev = true;
9685         }
9686     }
9687 
9688     if(mergeWithNext)
9689     {
9690         UnregisterFreeSuballocation(nextItem);
9691         MergeFreeWithNext(suballocItem);
9692     }
9693 
9694     if(mergeWithPrev)
9695     {
9696         UnregisterFreeSuballocation(prevItem);
9697         MergeFreeWithNext(prevItem);
9698         RegisterFreeSuballocation(prevItem);
9699         return prevItem;
9700     }
9701     else
9702     {
9703         RegisterFreeSuballocation(suballocItem);
9704         return suballocItem;
9705     }
9706 }
9707 
RegisterFreeSuballocation(VmaSuballocationList::iterator item)9708 void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
9709 {
9710     VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9711     VMA_ASSERT(item->size > 0);
9712 
9713     // You may want to enable this validation at the beginning or at the end of
9714     // this function, depending on what do you want to check.
9715     VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9716 
9717     if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
9718     {
9719         if(m_FreeSuballocationsBySize.empty())
9720         {
9721             m_FreeSuballocationsBySize.push_back(item);
9722         }
9723         else
9724         {
9725             VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
9726         }
9727     }
9728 
9729     //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9730 }
9731 
9732 
UnregisterFreeSuballocation(VmaSuballocationList::iterator item)9733 void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
9734 {
9735     VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9736     VMA_ASSERT(item->size > 0);
9737 
9738     // You may want to enable this validation at the beginning or at the end of
9739     // this function, depending on what do you want to check.
9740     VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9741 
9742     if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
9743     {
9744         VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
9745             m_FreeSuballocationsBySize.data(),
9746             m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
9747             item,
9748             VmaSuballocationItemSizeLess());
9749         for(size_t index = it - m_FreeSuballocationsBySize.data();
9750             index < m_FreeSuballocationsBySize.size();
9751             ++index)
9752         {
9753             if(m_FreeSuballocationsBySize[index] == item)
9754             {
9755                 VmaVectorRemove(m_FreeSuballocationsBySize, index);
9756                 return;
9757             }
9758             VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
9759         }
9760         VMA_ASSERT(0 && "Not found.");
9761     }
9762 
9763     //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9764 }
9765 
IsBufferImageGranularityConflictPossible(VkDeviceSize bufferImageGranularity,VmaSuballocationType & inOutPrevSuballocType)9766 bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
9767     VkDeviceSize bufferImageGranularity,
9768     VmaSuballocationType& inOutPrevSuballocType) const
9769 {
9770     if(bufferImageGranularity == 1 || IsEmpty())
9771     {
9772         return false;
9773     }
9774 
9775     VkDeviceSize minAlignment = VK_WHOLE_SIZE;
9776     bool typeConflictFound = false;
9777     for(VmaSuballocationList::const_iterator it = m_Suballocations.cbegin();
9778         it != m_Suballocations.cend();
9779         ++it)
9780     {
9781         const VmaSuballocationType suballocType = it->type;
9782         if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)
9783         {
9784             minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment());
9785             if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))
9786             {
9787                 typeConflictFound = true;
9788             }
9789             inOutPrevSuballocType = suballocType;
9790         }
9791     }
9792 
9793     return typeConflictFound || minAlignment >= bufferImageGranularity;
9794 }
9795 
9796 ////////////////////////////////////////////////////////////////////////////////
9797 // class VmaBlockMetadata_Linear
9798 
VmaBlockMetadata_Linear(VmaAllocator hAllocator)9799 VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) :
9800     VmaBlockMetadata(hAllocator),
9801     m_SumFreeSize(0),
9802     m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
9803     m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
9804     m_1stVectorIndex(0),
9805     m_2ndVectorMode(SECOND_VECTOR_EMPTY),
9806     m_1stNullItemsBeginCount(0),
9807     m_1stNullItemsMiddleCount(0),
9808     m_2ndNullItemsCount(0)
9809 {
9810 }
9811 
~VmaBlockMetadata_Linear()9812 VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
9813 {
9814 }
9815 
Init(VkDeviceSize size)9816 void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
9817 {
9818     VmaBlockMetadata::Init(size);
9819     m_SumFreeSize = size;
9820 }
9821 
Validate()9822 bool VmaBlockMetadata_Linear::Validate() const
9823 {
9824     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9825     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9826 
9827     VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
9828     VMA_VALIDATE(!suballocations1st.empty() ||
9829         suballocations2nd.empty() ||
9830         m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
9831 
9832     if(!suballocations1st.empty())
9833     {
9834         // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
9835         VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE);
9836         // Null item at the end should be just pop_back().
9837         VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE);
9838     }
9839     if(!suballocations2nd.empty())
9840     {
9841         // Null item at the end should be just pop_back().
9842         VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE);
9843     }
9844 
9845     VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
9846     VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
9847 
9848     VkDeviceSize sumUsedSize = 0;
9849     const size_t suballoc1stCount = suballocations1st.size();
9850     VkDeviceSize offset = VMA_DEBUG_MARGIN;
9851 
9852     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9853     {
9854         const size_t suballoc2ndCount = suballocations2nd.size();
9855         size_t nullItem2ndCount = 0;
9856         for(size_t i = 0; i < suballoc2ndCount; ++i)
9857         {
9858             const VmaSuballocation& suballoc = suballocations2nd[i];
9859             const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9860 
9861             VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
9862             VMA_VALIDATE(suballoc.offset >= offset);
9863 
9864             if(!currFree)
9865             {
9866                 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
9867                 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
9868                 sumUsedSize += suballoc.size;
9869             }
9870             else
9871             {
9872                 ++nullItem2ndCount;
9873             }
9874 
9875             offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
9876         }
9877 
9878         VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
9879     }
9880 
9881     for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
9882     {
9883         const VmaSuballocation& suballoc = suballocations1st[i];
9884         VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
9885             suballoc.hAllocation == VK_NULL_HANDLE);
9886     }
9887 
9888     size_t nullItem1stCount = m_1stNullItemsBeginCount;
9889 
9890     for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
9891     {
9892         const VmaSuballocation& suballoc = suballocations1st[i];
9893         const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9894 
9895         VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
9896         VMA_VALIDATE(suballoc.offset >= offset);
9897         VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
9898 
9899         if(!currFree)
9900         {
9901             VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
9902             VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
9903             sumUsedSize += suballoc.size;
9904         }
9905         else
9906         {
9907             ++nullItem1stCount;
9908         }
9909 
9910         offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
9911     }
9912     VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
9913 
9914     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9915     {
9916         const size_t suballoc2ndCount = suballocations2nd.size();
9917         size_t nullItem2ndCount = 0;
9918         for(size_t i = suballoc2ndCount; i--; )
9919         {
9920             const VmaSuballocation& suballoc = suballocations2nd[i];
9921             const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9922 
9923             VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
9924             VMA_VALIDATE(suballoc.offset >= offset);
9925 
9926             if(!currFree)
9927             {
9928                 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
9929                 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
9930                 sumUsedSize += suballoc.size;
9931             }
9932             else
9933             {
9934                 ++nullItem2ndCount;
9935             }
9936 
9937             offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
9938         }
9939 
9940         VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
9941     }
9942 
9943     VMA_VALIDATE(offset <= GetSize());
9944     VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
9945 
9946     return true;
9947 }
9948 
GetAllocationCount()9949 size_t VmaBlockMetadata_Linear::GetAllocationCount() const
9950 {
9951     return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) +
9952         AccessSuballocations2nd().size() - m_2ndNullItemsCount;
9953 }
9954 
GetUnusedRangeSizeMax()9955 VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
9956 {
9957     const VkDeviceSize size = GetSize();
9958 
9959     /*
9960     We don't consider gaps inside allocation vectors with freed allocations because
9961     they are not suitable for reuse in linear allocator. We consider only space that
9962     is available for new allocations.
9963     */
9964     if(IsEmpty())
9965     {
9966         return size;
9967     }
9968 
9969     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9970 
9971     switch(m_2ndVectorMode)
9972     {
9973     case SECOND_VECTOR_EMPTY:
9974         /*
9975         Available space is after end of 1st, as well as before beginning of 1st (which
9976         whould make it a ring buffer).
9977         */
9978         {
9979             const size_t suballocations1stCount = suballocations1st.size();
9980             VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
9981             const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
9982             const VmaSuballocation& lastSuballoc  = suballocations1st[suballocations1stCount - 1];
9983             return VMA_MAX(
9984                 firstSuballoc.offset,
9985                 size - (lastSuballoc.offset + lastSuballoc.size));
9986         }
9987         break;
9988 
9989     case SECOND_VECTOR_RING_BUFFER:
9990         /*
9991         Available space is only between end of 2nd and beginning of 1st.
9992         */
9993         {
9994             const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9995             const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
9996             const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
9997             return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
9998         }
9999         break;
10000 
10001     case SECOND_VECTOR_DOUBLE_STACK:
10002         /*
10003         Available space is only between end of 1st and top of 2nd.
10004         */
10005         {
10006             const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10007             const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
10008             const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
10009             return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
10010         }
10011         break;
10012 
10013     default:
10014         VMA_ASSERT(0);
10015         return 0;
10016     }
10017 }
10018 
CalcAllocationStatInfo(VmaStatInfo & outInfo)10019 void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
10020 {
10021     const VkDeviceSize size = GetSize();
10022     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10023     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10024     const size_t suballoc1stCount = suballocations1st.size();
10025     const size_t suballoc2ndCount = suballocations2nd.size();
10026 
10027     outInfo.blockCount = 1;
10028     outInfo.allocationCount = (uint32_t)GetAllocationCount();
10029     outInfo.unusedRangeCount = 0;
10030     outInfo.usedBytes = 0;
10031     outInfo.allocationSizeMin = UINT64_MAX;
10032     outInfo.allocationSizeMax = 0;
10033     outInfo.unusedRangeSizeMin = UINT64_MAX;
10034     outInfo.unusedRangeSizeMax = 0;
10035 
10036     VkDeviceSize lastOffset = 0;
10037 
10038     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10039     {
10040         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10041         size_t nextAlloc2ndIndex = 0;
10042         while(lastOffset < freeSpace2ndTo1stEnd)
10043         {
10044             // Find next non-null allocation or move nextAllocIndex to the end.
10045             while(nextAlloc2ndIndex < suballoc2ndCount &&
10046                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10047             {
10048                 ++nextAlloc2ndIndex;
10049             }
10050 
10051             // Found non-null allocation.
10052             if(nextAlloc2ndIndex < suballoc2ndCount)
10053             {
10054                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10055 
10056                 // 1. Process free space before this allocation.
10057                 if(lastOffset < suballoc.offset)
10058                 {
10059                     // There is free space from lastOffset to suballoc.offset.
10060                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10061                     ++outInfo.unusedRangeCount;
10062                     outInfo.unusedBytes += unusedRangeSize;
10063                     outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10064                     outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10065                 }
10066 
10067                 // 2. Process this allocation.
10068                 // There is allocation with suballoc.offset, suballoc.size.
10069                 outInfo.usedBytes += suballoc.size;
10070                 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10071                 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10072 
10073                 // 3. Prepare for next iteration.
10074                 lastOffset = suballoc.offset + suballoc.size;
10075                 ++nextAlloc2ndIndex;
10076             }
10077             // We are at the end.
10078             else
10079             {
10080                 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10081                 if(lastOffset < freeSpace2ndTo1stEnd)
10082                 {
10083                     const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10084                     ++outInfo.unusedRangeCount;
10085                     outInfo.unusedBytes += unusedRangeSize;
10086                     outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10087                     outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10088                }
10089 
10090                 // End of loop.
10091                 lastOffset = freeSpace2ndTo1stEnd;
10092             }
10093         }
10094     }
10095 
10096     size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10097     const VkDeviceSize freeSpace1stTo2ndEnd =
10098         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10099     while(lastOffset < freeSpace1stTo2ndEnd)
10100     {
10101         // Find next non-null allocation or move nextAllocIndex to the end.
10102         while(nextAlloc1stIndex < suballoc1stCount &&
10103             suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10104         {
10105             ++nextAlloc1stIndex;
10106         }
10107 
10108         // Found non-null allocation.
10109         if(nextAlloc1stIndex < suballoc1stCount)
10110         {
10111             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10112 
10113             // 1. Process free space before this allocation.
10114             if(lastOffset < suballoc.offset)
10115             {
10116                 // There is free space from lastOffset to suballoc.offset.
10117                 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10118                 ++outInfo.unusedRangeCount;
10119                 outInfo.unusedBytes += unusedRangeSize;
10120                 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10121                 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10122             }
10123 
10124             // 2. Process this allocation.
10125             // There is allocation with suballoc.offset, suballoc.size.
10126             outInfo.usedBytes += suballoc.size;
10127             outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10128             outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10129 
10130             // 3. Prepare for next iteration.
10131             lastOffset = suballoc.offset + suballoc.size;
10132             ++nextAlloc1stIndex;
10133         }
10134         // We are at the end.
10135         else
10136         {
10137             // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10138             if(lastOffset < freeSpace1stTo2ndEnd)
10139             {
10140                 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10141                 ++outInfo.unusedRangeCount;
10142                 outInfo.unusedBytes += unusedRangeSize;
10143                 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10144                 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10145            }
10146 
10147             // End of loop.
10148             lastOffset = freeSpace1stTo2ndEnd;
10149         }
10150     }
10151 
10152     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10153     {
10154         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10155         while(lastOffset < size)
10156         {
10157             // Find next non-null allocation or move nextAllocIndex to the end.
10158             while(nextAlloc2ndIndex != SIZE_MAX &&
10159                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10160             {
10161                 --nextAlloc2ndIndex;
10162             }
10163 
10164             // Found non-null allocation.
10165             if(nextAlloc2ndIndex != SIZE_MAX)
10166             {
10167                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10168 
10169                 // 1. Process free space before this allocation.
10170                 if(lastOffset < suballoc.offset)
10171                 {
10172                     // There is free space from lastOffset to suballoc.offset.
10173                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10174                     ++outInfo.unusedRangeCount;
10175                     outInfo.unusedBytes += unusedRangeSize;
10176                     outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10177                     outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10178                 }
10179 
10180                 // 2. Process this allocation.
10181                 // There is allocation with suballoc.offset, suballoc.size.
10182                 outInfo.usedBytes += suballoc.size;
10183                 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10184                 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10185 
10186                 // 3. Prepare for next iteration.
10187                 lastOffset = suballoc.offset + suballoc.size;
10188                 --nextAlloc2ndIndex;
10189             }
10190             // We are at the end.
10191             else
10192             {
10193                 // There is free space from lastOffset to size.
10194                 if(lastOffset < size)
10195                 {
10196                     const VkDeviceSize unusedRangeSize = size - lastOffset;
10197                     ++outInfo.unusedRangeCount;
10198                     outInfo.unusedBytes += unusedRangeSize;
10199                     outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10200                     outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10201                }
10202 
10203                 // End of loop.
10204                 lastOffset = size;
10205             }
10206         }
10207     }
10208 
10209     outInfo.unusedBytes = size - outInfo.usedBytes;
10210 }
10211 
AddPoolStats(VmaPoolStats & inoutStats)10212 void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
10213 {
10214     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10215     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10216     const VkDeviceSize size = GetSize();
10217     const size_t suballoc1stCount = suballocations1st.size();
10218     const size_t suballoc2ndCount = suballocations2nd.size();
10219 
10220     inoutStats.size += size;
10221 
10222     VkDeviceSize lastOffset = 0;
10223 
10224     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10225     {
10226         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10227         size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
10228         while(lastOffset < freeSpace2ndTo1stEnd)
10229         {
10230             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10231             while(nextAlloc2ndIndex < suballoc2ndCount &&
10232                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10233             {
10234                 ++nextAlloc2ndIndex;
10235             }
10236 
10237             // Found non-null allocation.
10238             if(nextAlloc2ndIndex < suballoc2ndCount)
10239             {
10240                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10241 
10242                 // 1. Process free space before this allocation.
10243                 if(lastOffset < suballoc.offset)
10244                 {
10245                     // There is free space from lastOffset to suballoc.offset.
10246                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10247                     inoutStats.unusedSize += unusedRangeSize;
10248                     ++inoutStats.unusedRangeCount;
10249                     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10250                 }
10251 
10252                 // 2. Process this allocation.
10253                 // There is allocation with suballoc.offset, suballoc.size.
10254                 ++inoutStats.allocationCount;
10255 
10256                 // 3. Prepare for next iteration.
10257                 lastOffset = suballoc.offset + suballoc.size;
10258                 ++nextAlloc2ndIndex;
10259             }
10260             // We are at the end.
10261             else
10262             {
10263                 if(lastOffset < freeSpace2ndTo1stEnd)
10264                 {
10265                     // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10266                     const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10267                     inoutStats.unusedSize += unusedRangeSize;
10268                     ++inoutStats.unusedRangeCount;
10269                     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10270                 }
10271 
10272                 // End of loop.
10273                 lastOffset = freeSpace2ndTo1stEnd;
10274             }
10275         }
10276     }
10277 
10278     size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10279     const VkDeviceSize freeSpace1stTo2ndEnd =
10280         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10281     while(lastOffset < freeSpace1stTo2ndEnd)
10282     {
10283         // Find next non-null allocation or move nextAllocIndex to the end.
10284         while(nextAlloc1stIndex < suballoc1stCount &&
10285             suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10286         {
10287             ++nextAlloc1stIndex;
10288         }
10289 
10290         // Found non-null allocation.
10291         if(nextAlloc1stIndex < suballoc1stCount)
10292         {
10293             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10294 
10295             // 1. Process free space before this allocation.
10296             if(lastOffset < suballoc.offset)
10297             {
10298                 // There is free space from lastOffset to suballoc.offset.
10299                 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10300                 inoutStats.unusedSize += unusedRangeSize;
10301                 ++inoutStats.unusedRangeCount;
10302                 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10303             }
10304 
10305             // 2. Process this allocation.
10306             // There is allocation with suballoc.offset, suballoc.size.
10307             ++inoutStats.allocationCount;
10308 
10309             // 3. Prepare for next iteration.
10310             lastOffset = suballoc.offset + suballoc.size;
10311             ++nextAlloc1stIndex;
10312         }
10313         // We are at the end.
10314         else
10315         {
10316             if(lastOffset < freeSpace1stTo2ndEnd)
10317             {
10318                 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10319                 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10320                 inoutStats.unusedSize += unusedRangeSize;
10321                 ++inoutStats.unusedRangeCount;
10322                 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10323             }
10324 
10325             // End of loop.
10326             lastOffset = freeSpace1stTo2ndEnd;
10327         }
10328     }
10329 
10330     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10331     {
10332         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10333         while(lastOffset < size)
10334         {
10335             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10336             while(nextAlloc2ndIndex != SIZE_MAX &&
10337                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10338             {
10339                 --nextAlloc2ndIndex;
10340             }
10341 
10342             // Found non-null allocation.
10343             if(nextAlloc2ndIndex != SIZE_MAX)
10344             {
10345                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10346 
10347                 // 1. Process free space before this allocation.
10348                 if(lastOffset < suballoc.offset)
10349                 {
10350                     // There is free space from lastOffset to suballoc.offset.
10351                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10352                     inoutStats.unusedSize += unusedRangeSize;
10353                     ++inoutStats.unusedRangeCount;
10354                     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10355                 }
10356 
10357                 // 2. Process this allocation.
10358                 // There is allocation with suballoc.offset, suballoc.size.
10359                 ++inoutStats.allocationCount;
10360 
10361                 // 3. Prepare for next iteration.
10362                 lastOffset = suballoc.offset + suballoc.size;
10363                 --nextAlloc2ndIndex;
10364             }
10365             // We are at the end.
10366             else
10367             {
10368                 if(lastOffset < size)
10369                 {
10370                     // There is free space from lastOffset to size.
10371                     const VkDeviceSize unusedRangeSize = size - lastOffset;
10372                     inoutStats.unusedSize += unusedRangeSize;
10373                     ++inoutStats.unusedRangeCount;
10374                     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10375                 }
10376 
10377                 // End of loop.
10378                 lastOffset = size;
10379             }
10380         }
10381     }
10382 }
10383 
10384 #if VMA_STATS_STRING_ENABLED
PrintDetailedMap(class VmaJsonWriter & json)10385 void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
10386 {
10387     const VkDeviceSize size = GetSize();
10388     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10389     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10390     const size_t suballoc1stCount = suballocations1st.size();
10391     const size_t suballoc2ndCount = suballocations2nd.size();
10392 
10393     // FIRST PASS
10394 
10395     size_t unusedRangeCount = 0;
10396     VkDeviceSize usedBytes = 0;
10397 
10398     VkDeviceSize lastOffset = 0;
10399 
10400     size_t alloc2ndCount = 0;
10401     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10402     {
10403         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10404         size_t nextAlloc2ndIndex = 0;
10405         while(lastOffset < freeSpace2ndTo1stEnd)
10406         {
10407             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10408             while(nextAlloc2ndIndex < suballoc2ndCount &&
10409                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10410             {
10411                 ++nextAlloc2ndIndex;
10412             }
10413 
10414             // Found non-null allocation.
10415             if(nextAlloc2ndIndex < suballoc2ndCount)
10416             {
10417                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10418 
10419                 // 1. Process free space before this allocation.
10420                 if(lastOffset < suballoc.offset)
10421                 {
10422                     // There is free space from lastOffset to suballoc.offset.
10423                     ++unusedRangeCount;
10424                 }
10425 
10426                 // 2. Process this allocation.
10427                 // There is allocation with suballoc.offset, suballoc.size.
10428                 ++alloc2ndCount;
10429                 usedBytes += suballoc.size;
10430 
10431                 // 3. Prepare for next iteration.
10432                 lastOffset = suballoc.offset + suballoc.size;
10433                 ++nextAlloc2ndIndex;
10434             }
10435             // We are at the end.
10436             else
10437             {
10438                 if(lastOffset < freeSpace2ndTo1stEnd)
10439                 {
10440                     // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10441                     ++unusedRangeCount;
10442                 }
10443 
10444                 // End of loop.
10445                 lastOffset = freeSpace2ndTo1stEnd;
10446             }
10447         }
10448     }
10449 
10450     size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10451     size_t alloc1stCount = 0;
10452     const VkDeviceSize freeSpace1stTo2ndEnd =
10453         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10454     while(lastOffset < freeSpace1stTo2ndEnd)
10455     {
10456         // Find next non-null allocation or move nextAllocIndex to the end.
10457         while(nextAlloc1stIndex < suballoc1stCount &&
10458             suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10459         {
10460             ++nextAlloc1stIndex;
10461         }
10462 
10463         // Found non-null allocation.
10464         if(nextAlloc1stIndex < suballoc1stCount)
10465         {
10466             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10467 
10468             // 1. Process free space before this allocation.
10469             if(lastOffset < suballoc.offset)
10470             {
10471                 // There is free space from lastOffset to suballoc.offset.
10472                 ++unusedRangeCount;
10473             }
10474 
10475             // 2. Process this allocation.
10476             // There is allocation with suballoc.offset, suballoc.size.
10477             ++alloc1stCount;
10478             usedBytes += suballoc.size;
10479 
10480             // 3. Prepare for next iteration.
10481             lastOffset = suballoc.offset + suballoc.size;
10482             ++nextAlloc1stIndex;
10483         }
10484         // We are at the end.
10485         else
10486         {
10487             if(lastOffset < size)
10488             {
10489                 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10490                 ++unusedRangeCount;
10491             }
10492 
10493             // End of loop.
10494             lastOffset = freeSpace1stTo2ndEnd;
10495         }
10496     }
10497 
10498     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10499     {
10500         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10501         while(lastOffset < size)
10502         {
10503             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10504             while(nextAlloc2ndIndex != SIZE_MAX &&
10505                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10506             {
10507                 --nextAlloc2ndIndex;
10508             }
10509 
10510             // Found non-null allocation.
10511             if(nextAlloc2ndIndex != SIZE_MAX)
10512             {
10513                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10514 
10515                 // 1. Process free space before this allocation.
10516                 if(lastOffset < suballoc.offset)
10517                 {
10518                     // There is free space from lastOffset to suballoc.offset.
10519                     ++unusedRangeCount;
10520                 }
10521 
10522                 // 2. Process this allocation.
10523                 // There is allocation with suballoc.offset, suballoc.size.
10524                 ++alloc2ndCount;
10525                 usedBytes += suballoc.size;
10526 
10527                 // 3. Prepare for next iteration.
10528                 lastOffset = suballoc.offset + suballoc.size;
10529                 --nextAlloc2ndIndex;
10530             }
10531             // We are at the end.
10532             else
10533             {
10534                 if(lastOffset < size)
10535                 {
10536                     // There is free space from lastOffset to size.
10537                     ++unusedRangeCount;
10538                 }
10539 
10540                 // End of loop.
10541                 lastOffset = size;
10542             }
10543         }
10544     }
10545 
10546     const VkDeviceSize unusedBytes = size - usedBytes;
10547     PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
10548 
10549     // SECOND PASS
10550     lastOffset = 0;
10551 
10552     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10553     {
10554         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10555         size_t nextAlloc2ndIndex = 0;
10556         while(lastOffset < freeSpace2ndTo1stEnd)
10557         {
10558             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10559             while(nextAlloc2ndIndex < suballoc2ndCount &&
10560                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10561             {
10562                 ++nextAlloc2ndIndex;
10563             }
10564 
10565             // Found non-null allocation.
10566             if(nextAlloc2ndIndex < suballoc2ndCount)
10567             {
10568                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10569 
10570                 // 1. Process free space before this allocation.
10571                 if(lastOffset < suballoc.offset)
10572                 {
10573                     // There is free space from lastOffset to suballoc.offset.
10574                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10575                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10576                 }
10577 
10578                 // 2. Process this allocation.
10579                 // There is allocation with suballoc.offset, suballoc.size.
10580                 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10581 
10582                 // 3. Prepare for next iteration.
10583                 lastOffset = suballoc.offset + suballoc.size;
10584                 ++nextAlloc2ndIndex;
10585             }
10586             // We are at the end.
10587             else
10588             {
10589                 if(lastOffset < freeSpace2ndTo1stEnd)
10590                 {
10591                     // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10592                     const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10593                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10594                 }
10595 
10596                 // End of loop.
10597                 lastOffset = freeSpace2ndTo1stEnd;
10598             }
10599         }
10600     }
10601 
10602     nextAlloc1stIndex = m_1stNullItemsBeginCount;
10603     while(lastOffset < freeSpace1stTo2ndEnd)
10604     {
10605         // Find next non-null allocation or move nextAllocIndex to the end.
10606         while(nextAlloc1stIndex < suballoc1stCount &&
10607             suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10608         {
10609             ++nextAlloc1stIndex;
10610         }
10611 
10612         // Found non-null allocation.
10613         if(nextAlloc1stIndex < suballoc1stCount)
10614         {
10615             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10616 
10617             // 1. Process free space before this allocation.
10618             if(lastOffset < suballoc.offset)
10619             {
10620                 // There is free space from lastOffset to suballoc.offset.
10621                 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10622                 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10623             }
10624 
10625             // 2. Process this allocation.
10626             // There is allocation with suballoc.offset, suballoc.size.
10627             PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10628 
10629             // 3. Prepare for next iteration.
10630             lastOffset = suballoc.offset + suballoc.size;
10631             ++nextAlloc1stIndex;
10632         }
10633         // We are at the end.
10634         else
10635         {
10636             if(lastOffset < freeSpace1stTo2ndEnd)
10637             {
10638                 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10639                 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10640                 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10641             }
10642 
10643             // End of loop.
10644             lastOffset = freeSpace1stTo2ndEnd;
10645         }
10646     }
10647 
10648     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10649     {
10650         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10651         while(lastOffset < size)
10652         {
10653             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10654             while(nextAlloc2ndIndex != SIZE_MAX &&
10655                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10656             {
10657                 --nextAlloc2ndIndex;
10658             }
10659 
10660             // Found non-null allocation.
10661             if(nextAlloc2ndIndex != SIZE_MAX)
10662             {
10663                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10664 
10665                 // 1. Process free space before this allocation.
10666                 if(lastOffset < suballoc.offset)
10667                 {
10668                     // There is free space from lastOffset to suballoc.offset.
10669                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10670                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10671                 }
10672 
10673                 // 2. Process this allocation.
10674                 // There is allocation with suballoc.offset, suballoc.size.
10675                 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10676 
10677                 // 3. Prepare for next iteration.
10678                 lastOffset = suballoc.offset + suballoc.size;
10679                 --nextAlloc2ndIndex;
10680             }
10681             // We are at the end.
10682             else
10683             {
10684                 if(lastOffset < size)
10685                 {
10686                     // There is free space from lastOffset to size.
10687                     const VkDeviceSize unusedRangeSize = size - lastOffset;
10688                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10689                 }
10690 
10691                 // End of loop.
10692                 lastOffset = size;
10693             }
10694         }
10695     }
10696 
10697     PrintDetailedMap_End(json);
10698 }
10699 #endif // #if VMA_STATS_STRING_ENABLED
10700 
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)10701 bool VmaBlockMetadata_Linear::CreateAllocationRequest(
10702     uint32_t currentFrameIndex,
10703     uint32_t frameInUseCount,
10704     VkDeviceSize bufferImageGranularity,
10705     VkDeviceSize allocSize,
10706     VkDeviceSize allocAlignment,
10707     bool upperAddress,
10708     VmaSuballocationType allocType,
10709     bool canMakeOtherLost,
10710     uint32_t strategy,
10711     VmaAllocationRequest* pAllocationRequest)
10712 {
10713     VMA_ASSERT(allocSize > 0);
10714     VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
10715     VMA_ASSERT(pAllocationRequest != VMA_NULL);
10716     VMA_HEAVY_ASSERT(Validate());
10717     return upperAddress ?
10718         CreateAllocationRequest_UpperAddress(
10719             currentFrameIndex, frameInUseCount, bufferImageGranularity,
10720             allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest) :
10721         CreateAllocationRequest_LowerAddress(
10722             currentFrameIndex, frameInUseCount, bufferImageGranularity,
10723             allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest);
10724 }
10725 
CreateAllocationRequest_UpperAddress(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)10726 bool VmaBlockMetadata_Linear::CreateAllocationRequest_UpperAddress(
10727     uint32_t currentFrameIndex,
10728     uint32_t frameInUseCount,
10729     VkDeviceSize bufferImageGranularity,
10730     VkDeviceSize allocSize,
10731     VkDeviceSize allocAlignment,
10732     VmaSuballocationType allocType,
10733     bool canMakeOtherLost,
10734     uint32_t strategy,
10735     VmaAllocationRequest* pAllocationRequest)
10736 {
10737     const VkDeviceSize size = GetSize();
10738     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10739     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10740 
10741     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10742     {
10743         VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
10744         return false;
10745     }
10746 
10747     // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
10748     if(allocSize > size)
10749     {
10750         return false;
10751     }
10752     VkDeviceSize resultBaseOffset = size - allocSize;
10753     if(!suballocations2nd.empty())
10754     {
10755         const VmaSuballocation& lastSuballoc = suballocations2nd.back();
10756         resultBaseOffset = lastSuballoc.offset - allocSize;
10757         if(allocSize > lastSuballoc.offset)
10758         {
10759             return false;
10760         }
10761     }
10762 
10763     // Start from offset equal to end of free space.
10764     VkDeviceSize resultOffset = resultBaseOffset;
10765 
10766     // Apply VMA_DEBUG_MARGIN at the end.
10767     if(VMA_DEBUG_MARGIN > 0)
10768     {
10769         if(resultOffset < VMA_DEBUG_MARGIN)
10770         {
10771             return false;
10772         }
10773         resultOffset -= VMA_DEBUG_MARGIN;
10774     }
10775 
10776     // Apply alignment.
10777     resultOffset = VmaAlignDown(resultOffset, allocAlignment);
10778 
10779     // Check next suballocations from 2nd for BufferImageGranularity conflicts.
10780     // Make bigger alignment if necessary.
10781     if(bufferImageGranularity > 1 && !suballocations2nd.empty())
10782     {
10783         bool bufferImageGranularityConflict = false;
10784         for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
10785         {
10786             const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
10787             if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
10788             {
10789                 if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
10790                 {
10791                     bufferImageGranularityConflict = true;
10792                     break;
10793                 }
10794             }
10795             else
10796                 // Already on previous page.
10797                 break;
10798         }
10799         if(bufferImageGranularityConflict)
10800         {
10801             resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
10802         }
10803     }
10804 
10805     // There is enough free space.
10806     const VkDeviceSize endOf1st = !suballocations1st.empty() ?
10807         suballocations1st.back().offset + suballocations1st.back().size :
10808         0;
10809     if(endOf1st + VMA_DEBUG_MARGIN <= resultOffset)
10810     {
10811         // Check previous suballocations for BufferImageGranularity conflicts.
10812         // If conflict exists, allocation cannot be made here.
10813         if(bufferImageGranularity > 1)
10814         {
10815             for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
10816             {
10817                 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
10818                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
10819                 {
10820                     if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
10821                     {
10822                         return false;
10823                     }
10824                 }
10825                 else
10826                 {
10827                     // Already on next page.
10828                     break;
10829                 }
10830             }
10831         }
10832 
10833         // All tests passed: Success.
10834         pAllocationRequest->offset = resultOffset;
10835         pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
10836         pAllocationRequest->sumItemSize = 0;
10837         // pAllocationRequest->item unused.
10838         pAllocationRequest->itemsToMakeLostCount = 0;
10839         pAllocationRequest->type = VmaAllocationRequestType::UpperAddress;
10840         return true;
10841     }
10842 
10843     return false;
10844 }
10845 
CreateAllocationRequest_LowerAddress(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)10846 bool VmaBlockMetadata_Linear::CreateAllocationRequest_LowerAddress(
10847     uint32_t currentFrameIndex,
10848     uint32_t frameInUseCount,
10849     VkDeviceSize bufferImageGranularity,
10850     VkDeviceSize allocSize,
10851     VkDeviceSize allocAlignment,
10852     VmaSuballocationType allocType,
10853     bool canMakeOtherLost,
10854     uint32_t strategy,
10855     VmaAllocationRequest* pAllocationRequest)
10856 {
10857     const VkDeviceSize size = GetSize();
10858     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10859     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10860 
10861     if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10862     {
10863         // Try to allocate at the end of 1st vector.
10864 
10865         VkDeviceSize resultBaseOffset = 0;
10866         if(!suballocations1st.empty())
10867         {
10868             const VmaSuballocation& lastSuballoc = suballocations1st.back();
10869             resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
10870         }
10871 
10872         // Start from offset equal to beginning of free space.
10873         VkDeviceSize resultOffset = resultBaseOffset;
10874 
10875         // Apply VMA_DEBUG_MARGIN at the beginning.
10876         if(VMA_DEBUG_MARGIN > 0)
10877         {
10878             resultOffset += VMA_DEBUG_MARGIN;
10879         }
10880 
10881         // Apply alignment.
10882         resultOffset = VmaAlignUp(resultOffset, allocAlignment);
10883 
10884         // Check previous suballocations for BufferImageGranularity conflicts.
10885         // Make bigger alignment if necessary.
10886         if(bufferImageGranularity > 1 && !suballocations1st.empty())
10887         {
10888             bool bufferImageGranularityConflict = false;
10889             for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
10890             {
10891                 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
10892                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
10893                 {
10894                     if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
10895                     {
10896                         bufferImageGranularityConflict = true;
10897                         break;
10898                     }
10899                 }
10900                 else
10901                     // Already on previous page.
10902                     break;
10903             }
10904             if(bufferImageGranularityConflict)
10905             {
10906                 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
10907             }
10908         }
10909 
10910         const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
10911             suballocations2nd.back().offset : size;
10912 
10913         // There is enough free space at the end after alignment.
10914         if(resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd)
10915         {
10916             // Check next suballocations for BufferImageGranularity conflicts.
10917             // If conflict exists, allocation cannot be made here.
10918             if(bufferImageGranularity > 1 && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10919             {
10920                 for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
10921                 {
10922                     const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
10923                     if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
10924                     {
10925                         if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
10926                         {
10927                             return false;
10928                         }
10929                     }
10930                     else
10931                     {
10932                         // Already on previous page.
10933                         break;
10934                     }
10935                 }
10936             }
10937 
10938             // All tests passed: Success.
10939             pAllocationRequest->offset = resultOffset;
10940             pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
10941             pAllocationRequest->sumItemSize = 0;
10942             // pAllocationRequest->item, customData unused.
10943             pAllocationRequest->type = VmaAllocationRequestType::EndOf1st;
10944             pAllocationRequest->itemsToMakeLostCount = 0;
10945             return true;
10946         }
10947     }
10948 
10949     // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
10950     // beginning of 1st vector as the end of free space.
10951     if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10952     {
10953         VMA_ASSERT(!suballocations1st.empty());
10954 
10955         VkDeviceSize resultBaseOffset = 0;
10956         if(!suballocations2nd.empty())
10957         {
10958             const VmaSuballocation& lastSuballoc = suballocations2nd.back();
10959             resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
10960         }
10961 
10962         // Start from offset equal to beginning of free space.
10963         VkDeviceSize resultOffset = resultBaseOffset;
10964 
10965         // Apply VMA_DEBUG_MARGIN at the beginning.
10966         if(VMA_DEBUG_MARGIN > 0)
10967         {
10968             resultOffset += VMA_DEBUG_MARGIN;
10969         }
10970 
10971         // Apply alignment.
10972         resultOffset = VmaAlignUp(resultOffset, allocAlignment);
10973 
10974         // Check previous suballocations for BufferImageGranularity conflicts.
10975         // Make bigger alignment if necessary.
10976         if(bufferImageGranularity > 1 && !suballocations2nd.empty())
10977         {
10978             bool bufferImageGranularityConflict = false;
10979             for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
10980             {
10981                 const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
10982                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
10983                 {
10984                     if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
10985                     {
10986                         bufferImageGranularityConflict = true;
10987                         break;
10988                     }
10989                 }
10990                 else
10991                     // Already on previous page.
10992                     break;
10993             }
10994             if(bufferImageGranularityConflict)
10995             {
10996                 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
10997             }
10998         }
10999 
11000         pAllocationRequest->itemsToMakeLostCount = 0;
11001         pAllocationRequest->sumItemSize = 0;
11002         size_t index1st = m_1stNullItemsBeginCount;
11003 
11004         if(canMakeOtherLost)
11005         {
11006             while(index1st < suballocations1st.size() &&
11007                 resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset)
11008             {
11009                 // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
11010                 const VmaSuballocation& suballoc = suballocations1st[index1st];
11011                 if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
11012                 {
11013                     // No problem.
11014                 }
11015                 else
11016                 {
11017                     VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
11018                     if(suballoc.hAllocation->CanBecomeLost() &&
11019                         suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
11020                     {
11021                         ++pAllocationRequest->itemsToMakeLostCount;
11022                         pAllocationRequest->sumItemSize += suballoc.size;
11023                     }
11024                     else
11025                     {
11026                         return false;
11027                     }
11028                 }
11029                 ++index1st;
11030             }
11031 
11032             // Check next suballocations for BufferImageGranularity conflicts.
11033             // If conflict exists, we must mark more allocations lost or fail.
11034             if(bufferImageGranularity > 1)
11035             {
11036                 while(index1st < suballocations1st.size())
11037                 {
11038                     const VmaSuballocation& suballoc = suballocations1st[index1st];
11039                     if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))
11040                     {
11041                         if(suballoc.hAllocation != VK_NULL_HANDLE)
11042                         {
11043                             // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
11044                             if(suballoc.hAllocation->CanBecomeLost() &&
11045                                 suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
11046                             {
11047                                 ++pAllocationRequest->itemsToMakeLostCount;
11048                                 pAllocationRequest->sumItemSize += suballoc.size;
11049                             }
11050                             else
11051                             {
11052                                 return false;
11053                             }
11054                         }
11055                     }
11056                     else
11057                     {
11058                         // Already on next page.
11059                         break;
11060                     }
11061                     ++index1st;
11062                 }
11063             }
11064 
11065             // Special case: There is not enough room at the end for this allocation, even after making all from the 1st lost.
11066             if(index1st == suballocations1st.size() &&
11067                 resultOffset + allocSize + VMA_DEBUG_MARGIN > size)
11068             {
11069                 // TODO: This is a known bug that it's not yet implemented and the allocation is failing.
11070                 VMA_DEBUG_LOG("Unsupported special case in custom pool with linear allocation algorithm used as ring buffer with allocations that can be lost.");
11071             }
11072         }
11073 
11074         // There is enough free space at the end after alignment.
11075         if((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= size) ||
11076             (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset))
11077         {
11078             // Check next suballocations for BufferImageGranularity conflicts.
11079             // If conflict exists, allocation cannot be made here.
11080             if(bufferImageGranularity > 1)
11081             {
11082                 for(size_t nextSuballocIndex = index1st;
11083                     nextSuballocIndex < suballocations1st.size();
11084                     nextSuballocIndex++)
11085                 {
11086                     const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
11087                     if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
11088                     {
11089                         if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
11090                         {
11091                             return false;
11092                         }
11093                     }
11094                     else
11095                     {
11096                         // Already on next page.
11097                         break;
11098                     }
11099                 }
11100             }
11101 
11102             // All tests passed: Success.
11103             pAllocationRequest->offset = resultOffset;
11104             pAllocationRequest->sumFreeSize =
11105                 (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size)
11106                 - resultBaseOffset
11107                 - pAllocationRequest->sumItemSize;
11108             pAllocationRequest->type = VmaAllocationRequestType::EndOf2nd;
11109             // pAllocationRequest->item, customData unused.
11110             return true;
11111         }
11112     }
11113 
11114     return false;
11115 }
11116 
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)11117 bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
11118     uint32_t currentFrameIndex,
11119     uint32_t frameInUseCount,
11120     VmaAllocationRequest* pAllocationRequest)
11121 {
11122     if(pAllocationRequest->itemsToMakeLostCount == 0)
11123     {
11124         return true;
11125     }
11126 
11127     VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
11128 
11129     // We always start from 1st.
11130     SuballocationVectorType* suballocations = &AccessSuballocations1st();
11131     size_t index = m_1stNullItemsBeginCount;
11132     size_t madeLostCount = 0;
11133     while(madeLostCount < pAllocationRequest->itemsToMakeLostCount)
11134     {
11135         if(index == suballocations->size())
11136         {
11137             index = 0;
11138             // If we get to the end of 1st, we wrap around to beginning of 2nd of 1st.
11139             if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11140             {
11141                 suballocations = &AccessSuballocations2nd();
11142             }
11143             // else: m_2ndVectorMode == SECOND_VECTOR_EMPTY:
11144             // suballocations continues pointing at AccessSuballocations1st().
11145             VMA_ASSERT(!suballocations->empty());
11146         }
11147         VmaSuballocation& suballoc = (*suballocations)[index];
11148         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11149         {
11150             VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
11151             VMA_ASSERT(suballoc.hAllocation->CanBecomeLost());
11152             if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11153             {
11154                 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11155                 suballoc.hAllocation = VK_NULL_HANDLE;
11156                 m_SumFreeSize += suballoc.size;
11157                 if(suballocations == &AccessSuballocations1st())
11158                 {
11159                     ++m_1stNullItemsMiddleCount;
11160                 }
11161                 else
11162                 {
11163                     ++m_2ndNullItemsCount;
11164                 }
11165                 ++madeLostCount;
11166             }
11167             else
11168             {
11169                 return false;
11170             }
11171         }
11172         ++index;
11173     }
11174 
11175     CleanupAfterFree();
11176     //VMA_HEAVY_ASSERT(Validate()); // Already called by ClanupAfterFree().
11177 
11178     return true;
11179 }
11180 
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)11181 uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
11182 {
11183     uint32_t lostAllocationCount = 0;
11184 
11185     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11186     for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
11187     {
11188         VmaSuballocation& suballoc = suballocations1st[i];
11189         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
11190             suballoc.hAllocation->CanBecomeLost() &&
11191             suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11192         {
11193             suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11194             suballoc.hAllocation = VK_NULL_HANDLE;
11195             ++m_1stNullItemsMiddleCount;
11196             m_SumFreeSize += suballoc.size;
11197             ++lostAllocationCount;
11198         }
11199     }
11200 
11201     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11202     for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
11203     {
11204         VmaSuballocation& suballoc = suballocations2nd[i];
11205         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
11206             suballoc.hAllocation->CanBecomeLost() &&
11207             suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11208         {
11209             suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11210             suballoc.hAllocation = VK_NULL_HANDLE;
11211             ++m_2ndNullItemsCount;
11212             m_SumFreeSize += suballoc.size;
11213             ++lostAllocationCount;
11214         }
11215     }
11216 
11217     if(lostAllocationCount)
11218     {
11219         CleanupAfterFree();
11220     }
11221 
11222     return lostAllocationCount;
11223 }
11224 
CheckCorruption(const void * pBlockData)11225 VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
11226 {
11227     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11228     for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
11229     {
11230         const VmaSuballocation& suballoc = suballocations1st[i];
11231         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11232         {
11233             if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
11234             {
11235                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
11236                 return VK_ERROR_VALIDATION_FAILED_EXT;
11237             }
11238             if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
11239             {
11240                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
11241                 return VK_ERROR_VALIDATION_FAILED_EXT;
11242             }
11243         }
11244     }
11245 
11246     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11247     for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
11248     {
11249         const VmaSuballocation& suballoc = suballocations2nd[i];
11250         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11251         {
11252             if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
11253             {
11254                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
11255                 return VK_ERROR_VALIDATION_FAILED_EXT;
11256             }
11257             if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
11258             {
11259                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
11260                 return VK_ERROR_VALIDATION_FAILED_EXT;
11261             }
11262         }
11263     }
11264 
11265     return VK_SUCCESS;
11266 }
11267 
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,VmaAllocation hAllocation)11268 void VmaBlockMetadata_Linear::Alloc(
11269     const VmaAllocationRequest& request,
11270     VmaSuballocationType type,
11271     VkDeviceSize allocSize,
11272     VmaAllocation hAllocation)
11273 {
11274     const VmaSuballocation newSuballoc = { request.offset, allocSize, hAllocation, type };
11275 
11276     switch(request.type)
11277     {
11278     case VmaAllocationRequestType::UpperAddress:
11279         {
11280             VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
11281                 "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
11282             SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11283             suballocations2nd.push_back(newSuballoc);
11284             m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
11285         }
11286         break;
11287     case VmaAllocationRequestType::EndOf1st:
11288         {
11289             SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11290 
11291             VMA_ASSERT(suballocations1st.empty() ||
11292                 request.offset >= suballocations1st.back().offset + suballocations1st.back().size);
11293             // Check if it fits before the end of the block.
11294             VMA_ASSERT(request.offset + allocSize <= GetSize());
11295 
11296             suballocations1st.push_back(newSuballoc);
11297         }
11298         break;
11299     case VmaAllocationRequestType::EndOf2nd:
11300         {
11301             SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11302             // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
11303             VMA_ASSERT(!suballocations1st.empty() &&
11304                 request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset);
11305             SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11306 
11307             switch(m_2ndVectorMode)
11308             {
11309             case SECOND_VECTOR_EMPTY:
11310                 // First allocation from second part ring buffer.
11311                 VMA_ASSERT(suballocations2nd.empty());
11312                 m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
11313                 break;
11314             case SECOND_VECTOR_RING_BUFFER:
11315                 // 2-part ring buffer is already started.
11316                 VMA_ASSERT(!suballocations2nd.empty());
11317                 break;
11318             case SECOND_VECTOR_DOUBLE_STACK:
11319                 VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
11320                 break;
11321             default:
11322                 VMA_ASSERT(0);
11323             }
11324 
11325             suballocations2nd.push_back(newSuballoc);
11326         }
11327         break;
11328     default:
11329         VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
11330     }
11331 
11332     m_SumFreeSize -= newSuballoc.size;
11333 }
11334 
Free(const VmaAllocation allocation)11335 void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation)
11336 {
11337     FreeAtOffset(allocation->GetOffset());
11338 }
11339 
FreeAtOffset(VkDeviceSize offset)11340 void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
11341 {
11342     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11343     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11344 
11345     if(!suballocations1st.empty())
11346     {
11347         // First allocation: Mark it as next empty at the beginning.
11348         VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
11349         if(firstSuballoc.offset == offset)
11350         {
11351             firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11352             firstSuballoc.hAllocation = VK_NULL_HANDLE;
11353             m_SumFreeSize += firstSuballoc.size;
11354             ++m_1stNullItemsBeginCount;
11355             CleanupAfterFree();
11356             return;
11357         }
11358     }
11359 
11360     // Last allocation in 2-part ring buffer or top of upper stack (same logic).
11361     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
11362         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11363     {
11364         VmaSuballocation& lastSuballoc = suballocations2nd.back();
11365         if(lastSuballoc.offset == offset)
11366         {
11367             m_SumFreeSize += lastSuballoc.size;
11368             suballocations2nd.pop_back();
11369             CleanupAfterFree();
11370             return;
11371         }
11372     }
11373     // Last allocation in 1st vector.
11374     else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY)
11375     {
11376         VmaSuballocation& lastSuballoc = suballocations1st.back();
11377         if(lastSuballoc.offset == offset)
11378         {
11379             m_SumFreeSize += lastSuballoc.size;
11380             suballocations1st.pop_back();
11381             CleanupAfterFree();
11382             return;
11383         }
11384     }
11385 
11386     // Item from the middle of 1st vector.
11387     {
11388         VmaSuballocation refSuballoc;
11389         refSuballoc.offset = offset;
11390         // Rest of members stays uninitialized intentionally for better performance.
11391         SuballocationVectorType::iterator it = VmaBinaryFindSorted(
11392             suballocations1st.begin() + m_1stNullItemsBeginCount,
11393             suballocations1st.end(),
11394             refSuballoc,
11395             VmaSuballocationOffsetLess());
11396         if(it != suballocations1st.end())
11397         {
11398             it->type = VMA_SUBALLOCATION_TYPE_FREE;
11399             it->hAllocation = VK_NULL_HANDLE;
11400             ++m_1stNullItemsMiddleCount;
11401             m_SumFreeSize += it->size;
11402             CleanupAfterFree();
11403             return;
11404         }
11405     }
11406 
11407     if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
11408     {
11409         // Item from the middle of 2nd vector.
11410         VmaSuballocation refSuballoc;
11411         refSuballoc.offset = offset;
11412         // Rest of members stays uninitialized intentionally for better performance.
11413         SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
11414             VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :
11415             VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());
11416         if(it != suballocations2nd.end())
11417         {
11418             it->type = VMA_SUBALLOCATION_TYPE_FREE;
11419             it->hAllocation = VK_NULL_HANDLE;
11420             ++m_2ndNullItemsCount;
11421             m_SumFreeSize += it->size;
11422             CleanupAfterFree();
11423             return;
11424         }
11425     }
11426 
11427     VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
11428 }
11429 
ShouldCompact1st()11430 bool VmaBlockMetadata_Linear::ShouldCompact1st() const
11431 {
11432     const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
11433     const size_t suballocCount = AccessSuballocations1st().size();
11434     return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
11435 }
11436 
CleanupAfterFree()11437 void VmaBlockMetadata_Linear::CleanupAfterFree()
11438 {
11439     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11440     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11441 
11442     if(IsEmpty())
11443     {
11444         suballocations1st.clear();
11445         suballocations2nd.clear();
11446         m_1stNullItemsBeginCount = 0;
11447         m_1stNullItemsMiddleCount = 0;
11448         m_2ndNullItemsCount = 0;
11449         m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11450     }
11451     else
11452     {
11453         const size_t suballoc1stCount = suballocations1st.size();
11454         const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
11455         VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
11456 
11457         // Find more null items at the beginning of 1st vector.
11458         while(m_1stNullItemsBeginCount < suballoc1stCount &&
11459             suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
11460         {
11461             ++m_1stNullItemsBeginCount;
11462             --m_1stNullItemsMiddleCount;
11463         }
11464 
11465         // Find more null items at the end of 1st vector.
11466         while(m_1stNullItemsMiddleCount > 0 &&
11467             suballocations1st.back().hAllocation == VK_NULL_HANDLE)
11468         {
11469             --m_1stNullItemsMiddleCount;
11470             suballocations1st.pop_back();
11471         }
11472 
11473         // Find more null items at the end of 2nd vector.
11474         while(m_2ndNullItemsCount > 0 &&
11475             suballocations2nd.back().hAllocation == VK_NULL_HANDLE)
11476         {
11477             --m_2ndNullItemsCount;
11478             suballocations2nd.pop_back();
11479         }
11480 
11481         // Find more null items at the beginning of 2nd vector.
11482         while(m_2ndNullItemsCount > 0 &&
11483             suballocations2nd[0].hAllocation == VK_NULL_HANDLE)
11484         {
11485             --m_2ndNullItemsCount;
11486             VmaVectorRemove(suballocations2nd, 0);
11487         }
11488 
11489         if(ShouldCompact1st())
11490         {
11491             const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
11492             size_t srcIndex = m_1stNullItemsBeginCount;
11493             for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
11494             {
11495                 while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE)
11496                 {
11497                     ++srcIndex;
11498                 }
11499                 if(dstIndex != srcIndex)
11500                 {
11501                     suballocations1st[dstIndex] = suballocations1st[srcIndex];
11502                 }
11503                 ++srcIndex;
11504             }
11505             suballocations1st.resize(nonNullItemCount);
11506             m_1stNullItemsBeginCount = 0;
11507             m_1stNullItemsMiddleCount = 0;
11508         }
11509 
11510         // 2nd vector became empty.
11511         if(suballocations2nd.empty())
11512         {
11513             m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11514         }
11515 
11516         // 1st vector became empty.
11517         if(suballocations1st.size() - m_1stNullItemsBeginCount == 0)
11518         {
11519             suballocations1st.clear();
11520             m_1stNullItemsBeginCount = 0;
11521 
11522             if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11523             {
11524                 // Swap 1st with 2nd. Now 2nd is empty.
11525                 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11526                 m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
11527                 while(m_1stNullItemsBeginCount < suballocations2nd.size() &&
11528                     suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
11529                 {
11530                     ++m_1stNullItemsBeginCount;
11531                     --m_1stNullItemsMiddleCount;
11532                 }
11533                 m_2ndNullItemsCount = 0;
11534                 m_1stVectorIndex ^= 1;
11535             }
11536         }
11537     }
11538 
11539     VMA_HEAVY_ASSERT(Validate());
11540 }
11541 
11542 
11543 ////////////////////////////////////////////////////////////////////////////////
11544 // class VmaBlockMetadata_Buddy
11545 
VmaBlockMetadata_Buddy(VmaAllocator hAllocator)11546 VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :
11547     VmaBlockMetadata(hAllocator),
11548     m_Root(VMA_NULL),
11549     m_AllocationCount(0),
11550     m_FreeCount(1),
11551     m_SumFreeSize(0)
11552 {
11553     memset(m_FreeList, 0, sizeof(m_FreeList));
11554 }
11555 
~VmaBlockMetadata_Buddy()11556 VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
11557 {
11558     DeleteNode(m_Root);
11559 }
11560 
Init(VkDeviceSize size)11561 void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
11562 {
11563     VmaBlockMetadata::Init(size);
11564 
11565     m_UsableSize = VmaPrevPow2(size);
11566     m_SumFreeSize = m_UsableSize;
11567 
11568     // Calculate m_LevelCount.
11569     m_LevelCount = 1;
11570     while(m_LevelCount < MAX_LEVELS &&
11571         LevelToNodeSize(m_LevelCount) >= MIN_NODE_SIZE)
11572     {
11573         ++m_LevelCount;
11574     }
11575 
11576     Node* rootNode = vma_new(GetAllocationCallbacks(), Node)();
11577     rootNode->offset = 0;
11578     rootNode->type = Node::TYPE_FREE;
11579     rootNode->parent = VMA_NULL;
11580     rootNode->buddy = VMA_NULL;
11581 
11582     m_Root = rootNode;
11583     AddToFreeListFront(0, rootNode);
11584 }
11585 
Validate()11586 bool VmaBlockMetadata_Buddy::Validate() const
11587 {
11588     // Validate tree.
11589     ValidationContext ctx;
11590     if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
11591     {
11592         VMA_VALIDATE(false && "ValidateNode failed.");
11593     }
11594     VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
11595     VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
11596 
11597     // Validate free node lists.
11598     for(uint32_t level = 0; level < m_LevelCount; ++level)
11599     {
11600         VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
11601             m_FreeList[level].front->free.prev == VMA_NULL);
11602 
11603         for(Node* node = m_FreeList[level].front;
11604             node != VMA_NULL;
11605             node = node->free.next)
11606         {
11607             VMA_VALIDATE(node->type == Node::TYPE_FREE);
11608 
11609             if(node->free.next == VMA_NULL)
11610             {
11611                 VMA_VALIDATE(m_FreeList[level].back == node);
11612             }
11613             else
11614             {
11615                 VMA_VALIDATE(node->free.next->free.prev == node);
11616             }
11617         }
11618     }
11619 
11620     // Validate that free lists ar higher levels are empty.
11621     for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
11622     {
11623         VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
11624     }
11625 
11626     return true;
11627 }
11628 
GetUnusedRangeSizeMax()11629 VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
11630 {
11631     for(uint32_t level = 0; level < m_LevelCount; ++level)
11632     {
11633         if(m_FreeList[level].front != VMA_NULL)
11634         {
11635             return LevelToNodeSize(level);
11636         }
11637     }
11638     return 0;
11639 }
11640 
CalcAllocationStatInfo(VmaStatInfo & outInfo)11641 void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
11642 {
11643     const VkDeviceSize unusableSize = GetUnusableSize();
11644 
11645     outInfo.blockCount = 1;
11646 
11647     outInfo.allocationCount = outInfo.unusedRangeCount = 0;
11648     outInfo.usedBytes = outInfo.unusedBytes = 0;
11649 
11650     outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
11651     outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
11652     outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused.
11653 
11654     CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0));
11655 
11656     if(unusableSize > 0)
11657     {
11658         ++outInfo.unusedRangeCount;
11659         outInfo.unusedBytes += unusableSize;
11660         outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize);
11661         outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize);
11662     }
11663 }
11664 
AddPoolStats(VmaPoolStats & inoutStats)11665 void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
11666 {
11667     const VkDeviceSize unusableSize = GetUnusableSize();
11668 
11669     inoutStats.size += GetSize();
11670     inoutStats.unusedSize += m_SumFreeSize + unusableSize;
11671     inoutStats.allocationCount += m_AllocationCount;
11672     inoutStats.unusedRangeCount += m_FreeCount;
11673     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
11674 
11675     if(unusableSize > 0)
11676     {
11677         ++inoutStats.unusedRangeCount;
11678         // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
11679     }
11680 }
11681 
11682 #if VMA_STATS_STRING_ENABLED
11683 
PrintDetailedMap(class VmaJsonWriter & json)11684 void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
11685 {
11686     // TODO optimize
11687     VmaStatInfo stat;
11688     CalcAllocationStatInfo(stat);
11689 
11690     PrintDetailedMap_Begin(
11691         json,
11692         stat.unusedBytes,
11693         stat.allocationCount,
11694         stat.unusedRangeCount);
11695 
11696     PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
11697 
11698     const VkDeviceSize unusableSize = GetUnusableSize();
11699     if(unusableSize > 0)
11700     {
11701         PrintDetailedMap_UnusedRange(json,
11702             m_UsableSize, // offset
11703             unusableSize); // size
11704     }
11705 
11706     PrintDetailedMap_End(json);
11707 }
11708 
11709 #endif // #if VMA_STATS_STRING_ENABLED
11710 
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)11711 bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
11712     uint32_t currentFrameIndex,
11713     uint32_t frameInUseCount,
11714     VkDeviceSize bufferImageGranularity,
11715     VkDeviceSize allocSize,
11716     VkDeviceSize allocAlignment,
11717     bool upperAddress,
11718     VmaSuballocationType allocType,
11719     bool canMakeOtherLost,
11720     uint32_t strategy,
11721     VmaAllocationRequest* pAllocationRequest)
11722 {
11723     VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
11724 
11725     // Simple way to respect bufferImageGranularity. May be optimized some day.
11726     // Whenever it might be an OPTIMAL image...
11727     if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
11728         allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
11729         allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
11730     {
11731         allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity);
11732         allocSize = VMA_MAX(allocSize, bufferImageGranularity);
11733     }
11734 
11735     if(allocSize > m_UsableSize)
11736     {
11737         return false;
11738     }
11739 
11740     const uint32_t targetLevel = AllocSizeToLevel(allocSize);
11741     for(uint32_t level = targetLevel + 1; level--; )
11742     {
11743         for(Node* freeNode = m_FreeList[level].front;
11744             freeNode != VMA_NULL;
11745             freeNode = freeNode->free.next)
11746         {
11747             if(freeNode->offset % allocAlignment == 0)
11748             {
11749                 pAllocationRequest->type = VmaAllocationRequestType::Normal;
11750                 pAllocationRequest->offset = freeNode->offset;
11751                 pAllocationRequest->sumFreeSize = LevelToNodeSize(level);
11752                 pAllocationRequest->sumItemSize = 0;
11753                 pAllocationRequest->itemsToMakeLostCount = 0;
11754                 pAllocationRequest->customData = (void*)(uintptr_t)level;
11755                 return true;
11756             }
11757         }
11758     }
11759 
11760     return false;
11761 }
11762 
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)11763 bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
11764     uint32_t currentFrameIndex,
11765     uint32_t frameInUseCount,
11766     VmaAllocationRequest* pAllocationRequest)
11767 {
11768     /*
11769     Lost allocations are not supported in buddy allocator at the moment.
11770     Support might be added in the future.
11771     */
11772     return pAllocationRequest->itemsToMakeLostCount == 0;
11773 }
11774 
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)11775 uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
11776 {
11777     /*
11778     Lost allocations are not supported in buddy allocator at the moment.
11779     Support might be added in the future.
11780     */
11781     return 0;
11782 }
11783 
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,VmaAllocation hAllocation)11784 void VmaBlockMetadata_Buddy::Alloc(
11785     const VmaAllocationRequest& request,
11786     VmaSuballocationType type,
11787     VkDeviceSize allocSize,
11788     VmaAllocation hAllocation)
11789 {
11790     VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
11791 
11792     const uint32_t targetLevel = AllocSizeToLevel(allocSize);
11793     uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
11794 
11795     Node* currNode = m_FreeList[currLevel].front;
11796     VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
11797     while(currNode->offset != request.offset)
11798     {
11799         currNode = currNode->free.next;
11800         VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
11801     }
11802 
11803     // Go down, splitting free nodes.
11804     while(currLevel < targetLevel)
11805     {
11806         // currNode is already first free node at currLevel.
11807         // Remove it from list of free nodes at this currLevel.
11808         RemoveFromFreeList(currLevel, currNode);
11809 
11810         const uint32_t childrenLevel = currLevel + 1;
11811 
11812         // Create two free sub-nodes.
11813         Node* leftChild = vma_new(GetAllocationCallbacks(), Node)();
11814         Node* rightChild = vma_new(GetAllocationCallbacks(), Node)();
11815 
11816         leftChild->offset = currNode->offset;
11817         leftChild->type = Node::TYPE_FREE;
11818         leftChild->parent = currNode;
11819         leftChild->buddy = rightChild;
11820 
11821         rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
11822         rightChild->type = Node::TYPE_FREE;
11823         rightChild->parent = currNode;
11824         rightChild->buddy = leftChild;
11825 
11826         // Convert current currNode to split type.
11827         currNode->type = Node::TYPE_SPLIT;
11828         currNode->split.leftChild = leftChild;
11829 
11830         // Add child nodes to free list. Order is important!
11831         AddToFreeListFront(childrenLevel, rightChild);
11832         AddToFreeListFront(childrenLevel, leftChild);
11833 
11834         ++m_FreeCount;
11835         //m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2.
11836         ++currLevel;
11837         currNode = m_FreeList[currLevel].front;
11838 
11839         /*
11840         We can be sure that currNode, as left child of node previously split,
11841         also fullfills the alignment requirement.
11842         */
11843     }
11844 
11845     // Remove from free list.
11846     VMA_ASSERT(currLevel == targetLevel &&
11847         currNode != VMA_NULL &&
11848         currNode->type == Node::TYPE_FREE);
11849     RemoveFromFreeList(currLevel, currNode);
11850 
11851     // Convert to allocation node.
11852     currNode->type = Node::TYPE_ALLOCATION;
11853     currNode->allocation.alloc = hAllocation;
11854 
11855     ++m_AllocationCount;
11856     --m_FreeCount;
11857     m_SumFreeSize -= allocSize;
11858 }
11859 
DeleteNode(Node * node)11860 void VmaBlockMetadata_Buddy::DeleteNode(Node* node)
11861 {
11862     if(node->type == Node::TYPE_SPLIT)
11863     {
11864         DeleteNode(node->split.leftChild->buddy);
11865         DeleteNode(node->split.leftChild);
11866     }
11867 
11868     vma_delete(GetAllocationCallbacks(), node);
11869 }
11870 
ValidateNode(ValidationContext & ctx,const Node * parent,const Node * curr,uint32_t level,VkDeviceSize levelNodeSize)11871 bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
11872 {
11873     VMA_VALIDATE(level < m_LevelCount);
11874     VMA_VALIDATE(curr->parent == parent);
11875     VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
11876     VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
11877     switch(curr->type)
11878     {
11879     case Node::TYPE_FREE:
11880         // curr->free.prev, next are validated separately.
11881         ctx.calculatedSumFreeSize += levelNodeSize;
11882         ++ctx.calculatedFreeCount;
11883         break;
11884     case Node::TYPE_ALLOCATION:
11885         ++ctx.calculatedAllocationCount;
11886         ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize();
11887         VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);
11888         break;
11889     case Node::TYPE_SPLIT:
11890         {
11891             const uint32_t childrenLevel = level + 1;
11892             const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2;
11893             const Node* const leftChild = curr->split.leftChild;
11894             VMA_VALIDATE(leftChild != VMA_NULL);
11895             VMA_VALIDATE(leftChild->offset == curr->offset);
11896             if(!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
11897             {
11898                 VMA_VALIDATE(false && "ValidateNode for left child failed.");
11899             }
11900             const Node* const rightChild = leftChild->buddy;
11901             VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
11902             if(!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
11903             {
11904                 VMA_VALIDATE(false && "ValidateNode for right child failed.");
11905             }
11906         }
11907         break;
11908     default:
11909         return false;
11910     }
11911 
11912     return true;
11913 }
11914 
AllocSizeToLevel(VkDeviceSize allocSize)11915 uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
11916 {
11917     // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
11918     uint32_t level = 0;
11919     VkDeviceSize currLevelNodeSize = m_UsableSize;
11920     VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
11921     while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
11922     {
11923         ++level;
11924         currLevelNodeSize = nextLevelNodeSize;
11925         nextLevelNodeSize = currLevelNodeSize >> 1;
11926     }
11927     return level;
11928 }
11929 
FreeAtOffset(VmaAllocation alloc,VkDeviceSize offset)11930 void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset)
11931 {
11932     // Find node and level.
11933     Node* node = m_Root;
11934     VkDeviceSize nodeOffset = 0;
11935     uint32_t level = 0;
11936     VkDeviceSize levelNodeSize = LevelToNodeSize(0);
11937     while(node->type == Node::TYPE_SPLIT)
11938     {
11939         const VkDeviceSize nextLevelSize = levelNodeSize >> 1;
11940         if(offset < nodeOffset + nextLevelSize)
11941         {
11942             node = node->split.leftChild;
11943         }
11944         else
11945         {
11946             node = node->split.leftChild->buddy;
11947             nodeOffset += nextLevelSize;
11948         }
11949         ++level;
11950         levelNodeSize = nextLevelSize;
11951     }
11952 
11953     VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
11954     VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc);
11955 
11956     ++m_FreeCount;
11957     --m_AllocationCount;
11958     m_SumFreeSize += alloc->GetSize();
11959 
11960     node->type = Node::TYPE_FREE;
11961 
11962     // Join free nodes if possible.
11963     while(level > 0 && node->buddy->type == Node::TYPE_FREE)
11964     {
11965         RemoveFromFreeList(level, node->buddy);
11966         Node* const parent = node->parent;
11967 
11968         vma_delete(GetAllocationCallbacks(), node->buddy);
11969         vma_delete(GetAllocationCallbacks(), node);
11970         parent->type = Node::TYPE_FREE;
11971 
11972         node = parent;
11973         --level;
11974         //m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2.
11975         --m_FreeCount;
11976     }
11977 
11978     AddToFreeListFront(level, node);
11979 }
11980 
CalcAllocationStatInfoNode(VmaStatInfo & outInfo,const Node * node,VkDeviceSize levelNodeSize)11981 void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const
11982 {
11983     switch(node->type)
11984     {
11985     case Node::TYPE_FREE:
11986         ++outInfo.unusedRangeCount;
11987         outInfo.unusedBytes += levelNodeSize;
11988         outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, levelNodeSize);
11989         outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize);
11990         break;
11991     case Node::TYPE_ALLOCATION:
11992         {
11993             const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
11994             ++outInfo.allocationCount;
11995             outInfo.usedBytes += allocSize;
11996             outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize);
11997             outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize);
11998 
11999             const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize;
12000             if(unusedRangeSize > 0)
12001             {
12002                 ++outInfo.unusedRangeCount;
12003                 outInfo.unusedBytes += unusedRangeSize;
12004                 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize);
12005                 outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize);
12006             }
12007         }
12008         break;
12009     case Node::TYPE_SPLIT:
12010         {
12011             const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
12012             const Node* const leftChild = node->split.leftChild;
12013             CalcAllocationStatInfoNode(outInfo, leftChild, childrenNodeSize);
12014             const Node* const rightChild = leftChild->buddy;
12015             CalcAllocationStatInfoNode(outInfo, rightChild, childrenNodeSize);
12016         }
12017         break;
12018     default:
12019         VMA_ASSERT(0);
12020     }
12021 }
12022 
AddToFreeListFront(uint32_t level,Node * node)12023 void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
12024 {
12025     VMA_ASSERT(node->type == Node::TYPE_FREE);
12026 
12027     // List is empty.
12028     Node* const frontNode = m_FreeList[level].front;
12029     if(frontNode == VMA_NULL)
12030     {
12031         VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
12032         node->free.prev = node->free.next = VMA_NULL;
12033         m_FreeList[level].front = m_FreeList[level].back = node;
12034     }
12035     else
12036     {
12037         VMA_ASSERT(frontNode->free.prev == VMA_NULL);
12038         node->free.prev = VMA_NULL;
12039         node->free.next = frontNode;
12040         frontNode->free.prev = node;
12041         m_FreeList[level].front = node;
12042     }
12043 }
12044 
RemoveFromFreeList(uint32_t level,Node * node)12045 void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
12046 {
12047     VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
12048 
12049     // It is at the front.
12050     if(node->free.prev == VMA_NULL)
12051     {
12052         VMA_ASSERT(m_FreeList[level].front == node);
12053         m_FreeList[level].front = node->free.next;
12054     }
12055     else
12056     {
12057         Node* const prevFreeNode = node->free.prev;
12058         VMA_ASSERT(prevFreeNode->free.next == node);
12059         prevFreeNode->free.next = node->free.next;
12060     }
12061 
12062     // It is at the back.
12063     if(node->free.next == VMA_NULL)
12064     {
12065         VMA_ASSERT(m_FreeList[level].back == node);
12066         m_FreeList[level].back = node->free.prev;
12067     }
12068     else
12069     {
12070         Node* const nextFreeNode = node->free.next;
12071         VMA_ASSERT(nextFreeNode->free.prev == node);
12072         nextFreeNode->free.prev = node->free.prev;
12073     }
12074 }
12075 
12076 #if VMA_STATS_STRING_ENABLED
PrintDetailedMapNode(class VmaJsonWriter & json,const Node * node,VkDeviceSize levelNodeSize)12077 void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
12078 {
12079     switch(node->type)
12080     {
12081     case Node::TYPE_FREE:
12082         PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
12083         break;
12084     case Node::TYPE_ALLOCATION:
12085         {
12086             PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);
12087             const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
12088             if(allocSize < levelNodeSize)
12089             {
12090                 PrintDetailedMap_UnusedRange(json, node->offset + allocSize, levelNodeSize - allocSize);
12091             }
12092         }
12093         break;
12094     case Node::TYPE_SPLIT:
12095         {
12096             const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
12097             const Node* const leftChild = node->split.leftChild;
12098             PrintDetailedMapNode(json, leftChild, childrenNodeSize);
12099             const Node* const rightChild = leftChild->buddy;
12100             PrintDetailedMapNode(json, rightChild, childrenNodeSize);
12101         }
12102         break;
12103     default:
12104         VMA_ASSERT(0);
12105     }
12106 }
12107 #endif // #if VMA_STATS_STRING_ENABLED
12108 
12109 
12110 ////////////////////////////////////////////////////////////////////////////////
12111 // class VmaDeviceMemoryBlock
12112 
VmaDeviceMemoryBlock(VmaAllocator hAllocator)12113 VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) :
12114     m_pMetadata(VMA_NULL),
12115     m_MemoryTypeIndex(UINT32_MAX),
12116     m_Id(0),
12117     m_hMemory(VK_NULL_HANDLE),
12118     m_MapCount(0),
12119     m_pMappedData(VMA_NULL)
12120 {
12121 }
12122 
Init(VmaAllocator hAllocator,VmaPool hParentPool,uint32_t newMemoryTypeIndex,VkDeviceMemory newMemory,VkDeviceSize newSize,uint32_t id,uint32_t algorithm)12123 void VmaDeviceMemoryBlock::Init(
12124     VmaAllocator hAllocator,
12125     VmaPool hParentPool,
12126     uint32_t newMemoryTypeIndex,
12127     VkDeviceMemory newMemory,
12128     VkDeviceSize newSize,
12129     uint32_t id,
12130     uint32_t algorithm)
12131 {
12132     VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
12133 
12134     m_hParentPool = hParentPool;
12135     m_MemoryTypeIndex = newMemoryTypeIndex;
12136     m_Id = id;
12137     m_hMemory = newMemory;
12138 
12139     switch(algorithm)
12140     {
12141     case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
12142         m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator);
12143         break;
12144     case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
12145         m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator);
12146         break;
12147     default:
12148         VMA_ASSERT(0);
12149         // Fall-through.
12150     case 0:
12151         m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator);
12152     }
12153     m_pMetadata->Init(newSize);
12154 }
12155 
Destroy(VmaAllocator allocator)12156 void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
12157 {
12158     // This is the most important assert in the entire library.
12159     // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
12160     VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
12161 
12162     VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
12163     allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
12164     m_hMemory = VK_NULL_HANDLE;
12165 
12166     vma_delete(allocator, m_pMetadata);
12167     m_pMetadata = VMA_NULL;
12168 }
12169 
Validate()12170 bool VmaDeviceMemoryBlock::Validate() const
12171 {
12172     VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
12173         (m_pMetadata->GetSize() != 0));
12174 
12175     return m_pMetadata->Validate();
12176 }
12177 
CheckCorruption(VmaAllocator hAllocator)12178 VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
12179 {
12180     void* pData = nullptr;
12181     VkResult res = Map(hAllocator, 1, &pData);
12182     if(res != VK_SUCCESS)
12183     {
12184         return res;
12185     }
12186 
12187     res = m_pMetadata->CheckCorruption(pData);
12188 
12189     Unmap(hAllocator, 1);
12190 
12191     return res;
12192 }
12193 
Map(VmaAllocator hAllocator,uint32_t count,void ** ppData)12194 VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
12195 {
12196     if(count == 0)
12197     {
12198         return VK_SUCCESS;
12199     }
12200 
12201     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12202     if(m_MapCount != 0)
12203     {
12204         m_MapCount += count;
12205         VMA_ASSERT(m_pMappedData != VMA_NULL);
12206         if(ppData != VMA_NULL)
12207         {
12208             *ppData = m_pMappedData;
12209         }
12210         return VK_SUCCESS;
12211     }
12212     else
12213     {
12214         VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
12215             hAllocator->m_hDevice,
12216             m_hMemory,
12217             0, // offset
12218             VK_WHOLE_SIZE,
12219             0, // flags
12220             &m_pMappedData);
12221         if(result == VK_SUCCESS)
12222         {
12223             if(ppData != VMA_NULL)
12224             {
12225                 *ppData = m_pMappedData;
12226             }
12227             m_MapCount = count;
12228         }
12229         return result;
12230     }
12231 }
12232 
Unmap(VmaAllocator hAllocator,uint32_t count)12233 void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
12234 {
12235     if(count == 0)
12236     {
12237         return;
12238     }
12239 
12240     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12241     if(m_MapCount >= count)
12242     {
12243         m_MapCount -= count;
12244         if(m_MapCount == 0)
12245         {
12246             m_pMappedData = VMA_NULL;
12247             (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
12248         }
12249     }
12250     else
12251     {
12252         VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
12253     }
12254 }
12255 
WriteMagicValueAroundAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)12256 VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
12257 {
12258     VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
12259     VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
12260 
12261     void* pData;
12262     VkResult res = Map(hAllocator, 1, &pData);
12263     if(res != VK_SUCCESS)
12264     {
12265         return res;
12266     }
12267 
12268     VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN);
12269     VmaWriteMagicValue(pData, allocOffset + allocSize);
12270 
12271     Unmap(hAllocator, 1);
12272 
12273     return VK_SUCCESS;
12274 }
12275 
ValidateMagicValueAroundAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)12276 VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
12277 {
12278     VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
12279     VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
12280 
12281     void* pData;
12282     VkResult res = Map(hAllocator, 1, &pData);
12283     if(res != VK_SUCCESS)
12284     {
12285         return res;
12286     }
12287 
12288     if(!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN))
12289     {
12290         VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
12291     }
12292     else if(!VmaValidateMagicValue(pData, allocOffset + allocSize))
12293     {
12294         VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
12295     }
12296 
12297     Unmap(hAllocator, 1);
12298 
12299     return VK_SUCCESS;
12300 }
12301 
BindBufferMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkBuffer hBuffer,const void * pNext)12302 VkResult VmaDeviceMemoryBlock::BindBufferMemory(
12303     const VmaAllocator hAllocator,
12304     const VmaAllocation hAllocation,
12305     VkDeviceSize allocationLocalOffset,
12306     VkBuffer hBuffer,
12307     const void* pNext)
12308 {
12309     VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
12310         hAllocation->GetBlock() == this);
12311     VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
12312         "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
12313     const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
12314     // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
12315     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12316     return hAllocator->BindVulkanBuffer(m_hMemory, memoryOffset, hBuffer, pNext);
12317 }
12318 
BindImageMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkImage hImage,const void * pNext)12319 VkResult VmaDeviceMemoryBlock::BindImageMemory(
12320     const VmaAllocator hAllocator,
12321     const VmaAllocation hAllocation,
12322     VkDeviceSize allocationLocalOffset,
12323     VkImage hImage,
12324     const void* pNext)
12325 {
12326     VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
12327         hAllocation->GetBlock() == this);
12328     VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
12329         "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
12330     const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
12331     // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
12332     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12333     return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext);
12334 }
12335 
InitStatInfo(VmaStatInfo & outInfo)12336 static void InitStatInfo(VmaStatInfo& outInfo)
12337 {
12338     memset(&outInfo, 0, sizeof(outInfo));
12339     outInfo.allocationSizeMin = UINT64_MAX;
12340     outInfo.unusedRangeSizeMin = UINT64_MAX;
12341 }
12342 
12343 // Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
VmaAddStatInfo(VmaStatInfo & inoutInfo,const VmaStatInfo & srcInfo)12344 static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
12345 {
12346     inoutInfo.blockCount += srcInfo.blockCount;
12347     inoutInfo.allocationCount += srcInfo.allocationCount;
12348     inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
12349     inoutInfo.usedBytes += srcInfo.usedBytes;
12350     inoutInfo.unusedBytes += srcInfo.unusedBytes;
12351     inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
12352     inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
12353     inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
12354     inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
12355 }
12356 
VmaPostprocessCalcStatInfo(VmaStatInfo & inoutInfo)12357 static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
12358 {
12359     inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
12360         VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
12361     inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
12362         VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
12363 }
12364 
VmaPool_T(VmaAllocator hAllocator,const VmaPoolCreateInfo & createInfo,VkDeviceSize preferredBlockSize)12365 VmaPool_T::VmaPool_T(
12366     VmaAllocator hAllocator,
12367     const VmaPoolCreateInfo& createInfo,
12368     VkDeviceSize preferredBlockSize) :
12369     m_BlockVector(
12370         hAllocator,
12371         this, // hParentPool
12372         createInfo.memoryTypeIndex,
12373         createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
12374         createInfo.minBlockCount,
12375         createInfo.maxBlockCount,
12376         (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
12377         createInfo.frameInUseCount,
12378         createInfo.blockSize != 0, // explicitBlockSize
12379         createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK), // algorithm
12380     m_Id(0),
12381     m_Name(VMA_NULL)
12382 {
12383 }
12384 
~VmaPool_T()12385 VmaPool_T::~VmaPool_T()
12386 {
12387 }
12388 
SetName(const char * pName)12389 void VmaPool_T::SetName(const char* pName)
12390 {
12391     const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks();
12392     VmaFreeString(allocs, m_Name);
12393 
12394     if(pName != VMA_NULL)
12395     {
12396         m_Name = VmaCreateStringCopy(allocs, pName);
12397     }
12398     else
12399     {
12400         m_Name = VMA_NULL;
12401     }
12402 }
12403 
12404 #if VMA_STATS_STRING_ENABLED
12405 
12406 #endif // #if VMA_STATS_STRING_ENABLED
12407 
VmaBlockVector(VmaAllocator hAllocator,VmaPool hParentPool,uint32_t memoryTypeIndex,VkDeviceSize preferredBlockSize,size_t minBlockCount,size_t maxBlockCount,VkDeviceSize bufferImageGranularity,uint32_t frameInUseCount,bool explicitBlockSize,uint32_t algorithm)12408 VmaBlockVector::VmaBlockVector(
12409     VmaAllocator hAllocator,
12410     VmaPool hParentPool,
12411     uint32_t memoryTypeIndex,
12412     VkDeviceSize preferredBlockSize,
12413     size_t minBlockCount,
12414     size_t maxBlockCount,
12415     VkDeviceSize bufferImageGranularity,
12416     uint32_t frameInUseCount,
12417     bool explicitBlockSize,
12418     uint32_t algorithm) :
12419     m_hAllocator(hAllocator),
12420     m_hParentPool(hParentPool),
12421     m_MemoryTypeIndex(memoryTypeIndex),
12422     m_PreferredBlockSize(preferredBlockSize),
12423     m_MinBlockCount(minBlockCount),
12424     m_MaxBlockCount(maxBlockCount),
12425     m_BufferImageGranularity(bufferImageGranularity),
12426     m_FrameInUseCount(frameInUseCount),
12427     m_ExplicitBlockSize(explicitBlockSize),
12428     m_Algorithm(algorithm),
12429     m_HasEmptyBlock(false),
12430     m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
12431     m_NextBlockId(0)
12432 {
12433 }
12434 
~VmaBlockVector()12435 VmaBlockVector::~VmaBlockVector()
12436 {
12437     for(size_t i = m_Blocks.size(); i--; )
12438     {
12439         m_Blocks[i]->Destroy(m_hAllocator);
12440         vma_delete(m_hAllocator, m_Blocks[i]);
12441     }
12442 }
12443 
CreateMinBlocks()12444 VkResult VmaBlockVector::CreateMinBlocks()
12445 {
12446     for(size_t i = 0; i < m_MinBlockCount; ++i)
12447     {
12448         VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
12449         if(res != VK_SUCCESS)
12450         {
12451             return res;
12452         }
12453     }
12454     return VK_SUCCESS;
12455 }
12456 
GetPoolStats(VmaPoolStats * pStats)12457 void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
12458 {
12459     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12460 
12461     const size_t blockCount = m_Blocks.size();
12462 
12463     pStats->size = 0;
12464     pStats->unusedSize = 0;
12465     pStats->allocationCount = 0;
12466     pStats->unusedRangeCount = 0;
12467     pStats->unusedRangeSizeMax = 0;
12468     pStats->blockCount = blockCount;
12469 
12470     for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12471     {
12472         const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12473         VMA_ASSERT(pBlock);
12474         VMA_HEAVY_ASSERT(pBlock->Validate());
12475         pBlock->m_pMetadata->AddPoolStats(*pStats);
12476     }
12477 }
12478 
IsEmpty()12479 bool VmaBlockVector::IsEmpty()
12480 {
12481     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12482     return m_Blocks.empty();
12483 }
12484 
IsCorruptionDetectionEnabled()12485 bool VmaBlockVector::IsCorruptionDetectionEnabled() const
12486 {
12487     const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
12488     return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
12489         (VMA_DEBUG_MARGIN > 0) &&
12490         (m_Algorithm == 0 || m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) &&
12491         (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
12492 }
12493 
12494 static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
12495 
Allocate(uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)12496 VkResult VmaBlockVector::Allocate(
12497     uint32_t currentFrameIndex,
12498     VkDeviceSize size,
12499     VkDeviceSize alignment,
12500     const VmaAllocationCreateInfo& createInfo,
12501     VmaSuballocationType suballocType,
12502     size_t allocationCount,
12503     VmaAllocation* pAllocations)
12504 {
12505     size_t allocIndex;
12506     VkResult res = VK_SUCCESS;
12507 
12508     if(IsCorruptionDetectionEnabled())
12509     {
12510         size = VmaAlignUp<VkDeviceSize>(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12511         alignment = VmaAlignUp<VkDeviceSize>(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12512     }
12513 
12514     {
12515         VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12516         for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
12517         {
12518             res = AllocatePage(
12519                 currentFrameIndex,
12520                 size,
12521                 alignment,
12522                 createInfo,
12523                 suballocType,
12524                 pAllocations + allocIndex);
12525             if(res != VK_SUCCESS)
12526             {
12527                 break;
12528             }
12529         }
12530     }
12531 
12532     if(res != VK_SUCCESS)
12533     {
12534         // Free all already created allocations.
12535         while(allocIndex--)
12536         {
12537             Free(pAllocations[allocIndex]);
12538         }
12539         memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
12540     }
12541 
12542     return res;
12543 }
12544 
AllocatePage(uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)12545 VkResult VmaBlockVector::AllocatePage(
12546     uint32_t currentFrameIndex,
12547     VkDeviceSize size,
12548     VkDeviceSize alignment,
12549     const VmaAllocationCreateInfo& createInfo,
12550     VmaSuballocationType suballocType,
12551     VmaAllocation* pAllocation)
12552 {
12553     const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
12554     bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
12555     const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
12556     const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
12557 
12558     VkDeviceSize freeMemory;
12559     {
12560         const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
12561         VmaBudget heapBudget = {};
12562         m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
12563         freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0;
12564     }
12565 
12566     const bool canFallbackToDedicated = !IsCustomPool();
12567     const bool canCreateNewBlock =
12568         ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
12569         (m_Blocks.size() < m_MaxBlockCount) &&
12570         (freeMemory >= size || !canFallbackToDedicated);
12571     uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
12572 
12573     // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
12574     // Which in turn is available only when maxBlockCount = 1.
12575     if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
12576     {
12577         canMakeOtherLost = false;
12578     }
12579 
12580     // Upper address can only be used with linear allocator and within single memory block.
12581     if(isUpperAddress &&
12582         (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
12583     {
12584         return VK_ERROR_FEATURE_NOT_PRESENT;
12585     }
12586 
12587     // Validate strategy.
12588     switch(strategy)
12589     {
12590     case 0:
12591         strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
12592         break;
12593     case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
12594     case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
12595     case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
12596         break;
12597     default:
12598         return VK_ERROR_FEATURE_NOT_PRESENT;
12599     }
12600 
12601     // Early reject: requested allocation size is larger that maximum block size for this block vector.
12602     if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
12603     {
12604         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12605     }
12606 
12607     /*
12608     Under certain condition, this whole section can be skipped for optimization, so
12609     we move on directly to trying to allocate with canMakeOtherLost. That's the case
12610     e.g. for custom pools with linear algorithm.
12611     */
12612     if(!canMakeOtherLost || canCreateNewBlock)
12613     {
12614         // 1. Search existing allocations. Try to allocate without making other allocations lost.
12615         VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
12616         allocFlagsCopy &= ~VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
12617 
12618         if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
12619         {
12620             // Use only last block.
12621             if(!m_Blocks.empty())
12622             {
12623                 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
12624                 VMA_ASSERT(pCurrBlock);
12625                 VkResult res = AllocateFromBlock(
12626                     pCurrBlock,
12627                     currentFrameIndex,
12628                     size,
12629                     alignment,
12630                     allocFlagsCopy,
12631                     createInfo.pUserData,
12632                     suballocType,
12633                     strategy,
12634                     pAllocation);
12635                 if(res == VK_SUCCESS)
12636                 {
12637                     VMA_DEBUG_LOG("    Returned from last block #%u", pCurrBlock->GetId());
12638                     return VK_SUCCESS;
12639                 }
12640             }
12641         }
12642         else
12643         {
12644             if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
12645             {
12646                 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12647                 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
12648                 {
12649                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12650                     VMA_ASSERT(pCurrBlock);
12651                     VkResult res = AllocateFromBlock(
12652                         pCurrBlock,
12653                         currentFrameIndex,
12654                         size,
12655                         alignment,
12656                         allocFlagsCopy,
12657                         createInfo.pUserData,
12658                         suballocType,
12659                         strategy,
12660                         pAllocation);
12661                     if(res == VK_SUCCESS)
12662                     {
12663                         VMA_DEBUG_LOG("    Returned from existing block #%u", pCurrBlock->GetId());
12664                         return VK_SUCCESS;
12665                     }
12666                 }
12667             }
12668             else // WORST_FIT, FIRST_FIT
12669             {
12670                 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
12671                 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12672                 {
12673                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12674                     VMA_ASSERT(pCurrBlock);
12675                     VkResult res = AllocateFromBlock(
12676                         pCurrBlock,
12677                         currentFrameIndex,
12678                         size,
12679                         alignment,
12680                         allocFlagsCopy,
12681                         createInfo.pUserData,
12682                         suballocType,
12683                         strategy,
12684                         pAllocation);
12685                     if(res == VK_SUCCESS)
12686                     {
12687                         VMA_DEBUG_LOG("    Returned from existing block #%u", pCurrBlock->GetId());
12688                         return VK_SUCCESS;
12689                     }
12690                 }
12691             }
12692         }
12693 
12694         // 2. Try to create new block.
12695         if(canCreateNewBlock)
12696         {
12697             // Calculate optimal size for new block.
12698             VkDeviceSize newBlockSize = m_PreferredBlockSize;
12699             uint32_t newBlockSizeShift = 0;
12700             const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
12701 
12702             if(!m_ExplicitBlockSize)
12703             {
12704                 // Allocate 1/8, 1/4, 1/2 as first blocks.
12705                 const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
12706                 for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
12707                 {
12708                     const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12709                     if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
12710                     {
12711                         newBlockSize = smallerNewBlockSize;
12712                         ++newBlockSizeShift;
12713                     }
12714                     else
12715                     {
12716                         break;
12717                     }
12718                 }
12719             }
12720 
12721             size_t newBlockIndex = 0;
12722             VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12723                 CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12724             // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
12725             if(!m_ExplicitBlockSize)
12726             {
12727                 while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
12728                 {
12729                     const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12730                     if(smallerNewBlockSize >= size)
12731                     {
12732                         newBlockSize = smallerNewBlockSize;
12733                         ++newBlockSizeShift;
12734                         res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12735                             CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12736                     }
12737                     else
12738                     {
12739                         break;
12740                     }
12741                 }
12742             }
12743 
12744             if(res == VK_SUCCESS)
12745             {
12746                 VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
12747                 VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
12748 
12749                 res = AllocateFromBlock(
12750                     pBlock,
12751                     currentFrameIndex,
12752                     size,
12753                     alignment,
12754                     allocFlagsCopy,
12755                     createInfo.pUserData,
12756                     suballocType,
12757                     strategy,
12758                     pAllocation);
12759                 if(res == VK_SUCCESS)
12760                 {
12761                     VMA_DEBUG_LOG("    Created new block #%u Size=%llu", pBlock->GetId(), newBlockSize);
12762                     return VK_SUCCESS;
12763                 }
12764                 else
12765                 {
12766                     // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
12767                     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12768                 }
12769             }
12770         }
12771     }
12772 
12773     // 3. Try to allocate from existing blocks with making other allocations lost.
12774     if(canMakeOtherLost)
12775     {
12776         uint32_t tryIndex = 0;
12777         for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
12778         {
12779             VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
12780             VmaAllocationRequest bestRequest = {};
12781             VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
12782 
12783             // 1. Search existing allocations.
12784             if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
12785             {
12786                 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12787                 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
12788                 {
12789                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12790                     VMA_ASSERT(pCurrBlock);
12791                     VmaAllocationRequest currRequest = {};
12792                     if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
12793                         currentFrameIndex,
12794                         m_FrameInUseCount,
12795                         m_BufferImageGranularity,
12796                         size,
12797                         alignment,
12798                         (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
12799                         suballocType,
12800                         canMakeOtherLost,
12801                         strategy,
12802                         &currRequest))
12803                     {
12804                         const VkDeviceSize currRequestCost = currRequest.CalcCost();
12805                         if(pBestRequestBlock == VMA_NULL ||
12806                             currRequestCost < bestRequestCost)
12807                         {
12808                             pBestRequestBlock = pCurrBlock;
12809                             bestRequest = currRequest;
12810                             bestRequestCost = currRequestCost;
12811 
12812                             if(bestRequestCost == 0)
12813                             {
12814                                 break;
12815                             }
12816                         }
12817                     }
12818                 }
12819             }
12820             else // WORST_FIT, FIRST_FIT
12821             {
12822                 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
12823                 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12824                 {
12825                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12826                     VMA_ASSERT(pCurrBlock);
12827                     VmaAllocationRequest currRequest = {};
12828                     if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
12829                         currentFrameIndex,
12830                         m_FrameInUseCount,
12831                         m_BufferImageGranularity,
12832                         size,
12833                         alignment,
12834                         (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
12835                         suballocType,
12836                         canMakeOtherLost,
12837                         strategy,
12838                         &currRequest))
12839                     {
12840                         const VkDeviceSize currRequestCost = currRequest.CalcCost();
12841                         if(pBestRequestBlock == VMA_NULL ||
12842                             currRequestCost < bestRequestCost ||
12843                             strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
12844                         {
12845                             pBestRequestBlock = pCurrBlock;
12846                             bestRequest = currRequest;
12847                             bestRequestCost = currRequestCost;
12848 
12849                             if(bestRequestCost == 0 ||
12850                                 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
12851                             {
12852                                 break;
12853                             }
12854                         }
12855                     }
12856                 }
12857             }
12858 
12859             if(pBestRequestBlock != VMA_NULL)
12860             {
12861                 if(mapped)
12862                 {
12863                     VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
12864                     if(res != VK_SUCCESS)
12865                     {
12866                         return res;
12867                     }
12868                 }
12869 
12870                 if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
12871                     currentFrameIndex,
12872                     m_FrameInUseCount,
12873                     &bestRequest))
12874                 {
12875                     // Allocate from this pBlock.
12876                     *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
12877                     pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, *pAllocation);
12878                     UpdateHasEmptyBlock();
12879                     (*pAllocation)->InitBlockAllocation(
12880                         pBestRequestBlock,
12881                         bestRequest.offset,
12882                         alignment,
12883                         size,
12884                         m_MemoryTypeIndex,
12885                         suballocType,
12886                         mapped,
12887                         (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
12888                     VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
12889                     VMA_DEBUG_LOG("    Returned from existing block");
12890                     (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
12891                     m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
12892                     if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
12893                     {
12894                         m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
12895                     }
12896                     if(IsCorruptionDetectionEnabled())
12897                     {
12898                         VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
12899                         VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
12900                     }
12901                     return VK_SUCCESS;
12902                 }
12903                 // else: Some allocations must have been touched while we are here. Next try.
12904             }
12905             else
12906             {
12907                 // Could not find place in any of the blocks - break outer loop.
12908                 break;
12909             }
12910         }
12911         /* Maximum number of tries exceeded - a very unlike event when many other
12912         threads are simultaneously touching allocations making it impossible to make
12913         lost at the same time as we try to allocate. */
12914         if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
12915         {
12916             return VK_ERROR_TOO_MANY_OBJECTS;
12917         }
12918     }
12919 
12920     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12921 }
12922 
Free(const VmaAllocation hAllocation)12923 void VmaBlockVector::Free(
12924     const VmaAllocation hAllocation)
12925 {
12926     VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
12927 
12928     bool budgetExceeded = false;
12929     {
12930         const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
12931         VmaBudget heapBudget = {};
12932         m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
12933         budgetExceeded = heapBudget.usage >= heapBudget.budget;
12934     }
12935 
12936     // Scope for lock.
12937     {
12938         VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12939 
12940         VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
12941 
12942         if(IsCorruptionDetectionEnabled())
12943         {
12944             VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
12945             VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
12946         }
12947 
12948         if(hAllocation->IsPersistentMap())
12949         {
12950             pBlock->Unmap(m_hAllocator, 1);
12951         }
12952 
12953         pBlock->m_pMetadata->Free(hAllocation);
12954         VMA_HEAVY_ASSERT(pBlock->Validate());
12955 
12956         VMA_DEBUG_LOG("  Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
12957 
12958         const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount;
12959         // pBlock became empty after this deallocation.
12960         if(pBlock->m_pMetadata->IsEmpty())
12961         {
12962             // Already has empty block. We don't want to have two, so delete this one.
12963             if((m_HasEmptyBlock || budgetExceeded) && canDeleteBlock)
12964             {
12965                 pBlockToDelete = pBlock;
12966                 Remove(pBlock);
12967             }
12968             // else: We now have an empty block - leave it.
12969         }
12970         // pBlock didn't become empty, but we have another empty block - find and free that one.
12971         // (This is optional, heuristics.)
12972         else if(m_HasEmptyBlock && canDeleteBlock)
12973         {
12974             VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
12975             if(pLastBlock->m_pMetadata->IsEmpty())
12976             {
12977                 pBlockToDelete = pLastBlock;
12978                 m_Blocks.pop_back();
12979             }
12980         }
12981 
12982         UpdateHasEmptyBlock();
12983         IncrementallySortBlocks();
12984     }
12985 
12986     // Destruction of a free block. Deferred until this point, outside of mutex
12987     // lock, for performance reason.
12988     if(pBlockToDelete != VMA_NULL)
12989     {
12990         VMA_DEBUG_LOG("    Deleted empty block");
12991         pBlockToDelete->Destroy(m_hAllocator);
12992         vma_delete(m_hAllocator, pBlockToDelete);
12993     }
12994 }
12995 
CalcMaxBlockSize()12996 VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
12997 {
12998     VkDeviceSize result = 0;
12999     for(size_t i = m_Blocks.size(); i--; )
13000     {
13001         result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
13002         if(result >= m_PreferredBlockSize)
13003         {
13004             break;
13005         }
13006     }
13007     return result;
13008 }
13009 
Remove(VmaDeviceMemoryBlock * pBlock)13010 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
13011 {
13012     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13013     {
13014         if(m_Blocks[blockIndex] == pBlock)
13015         {
13016             VmaVectorRemove(m_Blocks, blockIndex);
13017             return;
13018         }
13019     }
13020     VMA_ASSERT(0);
13021 }
13022 
IncrementallySortBlocks()13023 void VmaBlockVector::IncrementallySortBlocks()
13024 {
13025     if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
13026     {
13027         // Bubble sort only until first swap.
13028         for(size_t i = 1; i < m_Blocks.size(); ++i)
13029         {
13030             if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
13031             {
13032                 VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
13033                 return;
13034             }
13035         }
13036     }
13037 }
13038 
AllocateFromBlock(VmaDeviceMemoryBlock * pBlock,uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,VmaAllocationCreateFlags allocFlags,void * pUserData,VmaSuballocationType suballocType,uint32_t strategy,VmaAllocation * pAllocation)13039 VkResult VmaBlockVector::AllocateFromBlock(
13040     VmaDeviceMemoryBlock* pBlock,
13041     uint32_t currentFrameIndex,
13042     VkDeviceSize size,
13043     VkDeviceSize alignment,
13044     VmaAllocationCreateFlags allocFlags,
13045     void* pUserData,
13046     VmaSuballocationType suballocType,
13047     uint32_t strategy,
13048     VmaAllocation* pAllocation)
13049 {
13050     VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
13051     const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
13052     const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
13053     const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
13054 
13055     VmaAllocationRequest currRequest = {};
13056     if(pBlock->m_pMetadata->CreateAllocationRequest(
13057         currentFrameIndex,
13058         m_FrameInUseCount,
13059         m_BufferImageGranularity,
13060         size,
13061         alignment,
13062         isUpperAddress,
13063         suballocType,
13064         false, // canMakeOtherLost
13065         strategy,
13066         &currRequest))
13067     {
13068         // Allocate from pCurrBlock.
13069         VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
13070 
13071         if(mapped)
13072         {
13073             VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
13074             if(res != VK_SUCCESS)
13075             {
13076                 return res;
13077             }
13078         }
13079 
13080         *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
13081         pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, *pAllocation);
13082         UpdateHasEmptyBlock();
13083         (*pAllocation)->InitBlockAllocation(
13084             pBlock,
13085             currRequest.offset,
13086             alignment,
13087             size,
13088             m_MemoryTypeIndex,
13089             suballocType,
13090             mapped,
13091             (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
13092         VMA_HEAVY_ASSERT(pBlock->Validate());
13093         (*pAllocation)->SetUserData(m_hAllocator, pUserData);
13094         m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
13095         if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13096         {
13097             m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13098         }
13099         if(IsCorruptionDetectionEnabled())
13100         {
13101             VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
13102             VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
13103         }
13104         return VK_SUCCESS;
13105     }
13106     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13107 }
13108 
CreateBlock(VkDeviceSize blockSize,size_t * pNewBlockIndex)13109 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
13110 {
13111     VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
13112     allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
13113     allocInfo.allocationSize = blockSize;
13114 
13115 #if VMA_BUFFER_DEVICE_ADDRESS
13116     // Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature.
13117     VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
13118     if(m_hAllocator->m_UseKhrBufferDeviceAddress)
13119     {
13120         allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
13121         VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
13122     }
13123 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
13124 
13125     VkDeviceMemory mem = VK_NULL_HANDLE;
13126     VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
13127     if(res < 0)
13128     {
13129         return res;
13130     }
13131 
13132     // New VkDeviceMemory successfully created.
13133 
13134     // Create new Allocation for it.
13135     VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
13136     pBlock->Init(
13137         m_hAllocator,
13138         m_hParentPool,
13139         m_MemoryTypeIndex,
13140         mem,
13141         allocInfo.allocationSize,
13142         m_NextBlockId++,
13143         m_Algorithm);
13144 
13145     m_Blocks.push_back(pBlock);
13146     if(pNewBlockIndex != VMA_NULL)
13147     {
13148         *pNewBlockIndex = m_Blocks.size() - 1;
13149     }
13150 
13151     return VK_SUCCESS;
13152 }
13153 
ApplyDefragmentationMovesCpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,const VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves)13154 void VmaBlockVector::ApplyDefragmentationMovesCpu(
13155     class VmaBlockVectorDefragmentationContext* pDefragCtx,
13156     const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
13157 {
13158     const size_t blockCount = m_Blocks.size();
13159     const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
13160 
13161     enum BLOCK_FLAG
13162     {
13163         BLOCK_FLAG_USED = 0x00000001,
13164         BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
13165     };
13166 
13167     struct BlockInfo
13168     {
13169         uint32_t flags;
13170         void* pMappedData;
13171     };
13172     VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
13173         blockInfo(blockCount, BlockInfo(), VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
13174     memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
13175 
13176     // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13177     const size_t moveCount = moves.size();
13178     for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13179     {
13180         const VmaDefragmentationMove& move = moves[moveIndex];
13181         blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
13182         blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
13183     }
13184 
13185     VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13186 
13187     // Go over all blocks. Get mapped pointer or map if necessary.
13188     for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13189     {
13190         BlockInfo& currBlockInfo = blockInfo[blockIndex];
13191         VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13192         if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
13193         {
13194             currBlockInfo.pMappedData = pBlock->GetMappedData();
13195             // It is not originally mapped - map it.
13196             if(currBlockInfo.pMappedData == VMA_NULL)
13197             {
13198                 pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
13199                 if(pDefragCtx->res == VK_SUCCESS)
13200                 {
13201                     currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
13202                 }
13203             }
13204         }
13205     }
13206 
13207     // Go over all moves. Do actual data transfer.
13208     if(pDefragCtx->res == VK_SUCCESS)
13209     {
13210         const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
13211         VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
13212 
13213         for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13214         {
13215             const VmaDefragmentationMove& move = moves[moveIndex];
13216 
13217             const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
13218             const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
13219 
13220             VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
13221 
13222             // Invalidate source.
13223             if(isNonCoherent)
13224             {
13225                 VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
13226                 memRange.memory = pSrcBlock->GetDeviceMemory();
13227                 memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
13228                 memRange.size = VMA_MIN(
13229                     VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
13230                     pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
13231                 (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13232             }
13233 
13234             // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
13235             memmove(
13236                 reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
13237                 reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
13238                 static_cast<size_t>(move.size));
13239 
13240             if(IsCorruptionDetectionEnabled())
13241             {
13242                 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
13243                 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
13244             }
13245 
13246             // Flush destination.
13247             if(isNonCoherent)
13248             {
13249                 VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
13250                 memRange.memory = pDstBlock->GetDeviceMemory();
13251                 memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
13252                 memRange.size = VMA_MIN(
13253                     VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
13254                     pDstBlock->m_pMetadata->GetSize() - memRange.offset);
13255                 (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13256             }
13257         }
13258     }
13259 
13260     // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
13261     // Regardless of pCtx->res == VK_SUCCESS.
13262     for(size_t blockIndex = blockCount; blockIndex--; )
13263     {
13264         const BlockInfo& currBlockInfo = blockInfo[blockIndex];
13265         if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
13266         {
13267             VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13268             pBlock->Unmap(m_hAllocator, 1);
13269         }
13270     }
13271 }
13272 
ApplyDefragmentationMovesGpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkCommandBuffer commandBuffer)13273 void VmaBlockVector::ApplyDefragmentationMovesGpu(
13274     class VmaBlockVectorDefragmentationContext* pDefragCtx,
13275     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13276     VkCommandBuffer commandBuffer)
13277 {
13278     const size_t blockCount = m_Blocks.size();
13279 
13280     pDefragCtx->blockContexts.resize(blockCount);
13281     memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext));
13282 
13283     // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13284     const size_t moveCount = moves.size();
13285     for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13286     {
13287         const VmaDefragmentationMove& move = moves[moveIndex];
13288 
13289         //if(move.type == VMA_ALLOCATION_TYPE_UNKNOWN)
13290         {
13291             // Old school move still require us to map the whole block
13292             pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13293             pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13294         }
13295     }
13296 
13297     VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13298 
13299     // Go over all blocks. Create and bind buffer for whole block if necessary.
13300     {
13301         VkBufferCreateInfo bufCreateInfo;
13302         VmaFillGpuDefragmentationBufferCreateInfo(bufCreateInfo);
13303 
13304         for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13305         {
13306             VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
13307             VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13308             if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
13309             {
13310                 bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
13311                 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
13312                     m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
13313                 if(pDefragCtx->res == VK_SUCCESS)
13314                 {
13315                     pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
13316                         m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
13317                 }
13318             }
13319         }
13320     }
13321 
13322     // Go over all moves. Post data transfer commands to command buffer.
13323     if(pDefragCtx->res == VK_SUCCESS)
13324     {
13325         for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13326         {
13327             const VmaDefragmentationMove& move = moves[moveIndex];
13328 
13329             const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
13330             const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
13331 
13332             VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
13333 
13334             VkBufferCopy region = {
13335                 move.srcOffset,
13336                 move.dstOffset,
13337                 move.size };
13338             (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
13339                 commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, &region);
13340         }
13341     }
13342 
13343     // Save buffers to defrag context for later destruction.
13344     if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
13345     {
13346         pDefragCtx->res = VK_NOT_READY;
13347     }
13348 }
13349 
FreeEmptyBlocks(VmaDefragmentationStats * pDefragmentationStats)13350 void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
13351 {
13352     for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
13353     {
13354         VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13355         if(pBlock->m_pMetadata->IsEmpty())
13356         {
13357             if(m_Blocks.size() > m_MinBlockCount)
13358             {
13359                 if(pDefragmentationStats != VMA_NULL)
13360                 {
13361                     ++pDefragmentationStats->deviceMemoryBlocksFreed;
13362                     pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
13363                 }
13364 
13365                 VmaVectorRemove(m_Blocks, blockIndex);
13366                 pBlock->Destroy(m_hAllocator);
13367                 vma_delete(m_hAllocator, pBlock);
13368             }
13369             else
13370             {
13371                 break;
13372             }
13373         }
13374     }
13375     UpdateHasEmptyBlock();
13376 }
13377 
UpdateHasEmptyBlock()13378 void VmaBlockVector::UpdateHasEmptyBlock()
13379 {
13380     m_HasEmptyBlock = false;
13381     for(size_t index = 0, count = m_Blocks.size(); index < count; ++index)
13382     {
13383         VmaDeviceMemoryBlock* const pBlock = m_Blocks[index];
13384         if(pBlock->m_pMetadata->IsEmpty())
13385         {
13386             m_HasEmptyBlock = true;
13387             break;
13388         }
13389     }
13390 }
13391 
13392 #if VMA_STATS_STRING_ENABLED
13393 
PrintDetailedMap(class VmaJsonWriter & json)13394 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
13395 {
13396     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13397 
13398     json.BeginObject();
13399 
13400     if(IsCustomPool())
13401     {
13402         const char* poolName = m_hParentPool->GetName();
13403         if(poolName != VMA_NULL && poolName[0] != '\0')
13404         {
13405             json.WriteString("Name");
13406             json.WriteString(poolName);
13407         }
13408 
13409         json.WriteString("MemoryTypeIndex");
13410         json.WriteNumber(m_MemoryTypeIndex);
13411 
13412         json.WriteString("BlockSize");
13413         json.WriteNumber(m_PreferredBlockSize);
13414 
13415         json.WriteString("BlockCount");
13416         json.BeginObject(true);
13417         if(m_MinBlockCount > 0)
13418         {
13419             json.WriteString("Min");
13420             json.WriteNumber((uint64_t)m_MinBlockCount);
13421         }
13422         if(m_MaxBlockCount < SIZE_MAX)
13423         {
13424             json.WriteString("Max");
13425             json.WriteNumber((uint64_t)m_MaxBlockCount);
13426         }
13427         json.WriteString("Cur");
13428         json.WriteNumber((uint64_t)m_Blocks.size());
13429         json.EndObject();
13430 
13431         if(m_FrameInUseCount > 0)
13432         {
13433             json.WriteString("FrameInUseCount");
13434             json.WriteNumber(m_FrameInUseCount);
13435         }
13436 
13437         if(m_Algorithm != 0)
13438         {
13439             json.WriteString("Algorithm");
13440             json.WriteString(VmaAlgorithmToStr(m_Algorithm));
13441         }
13442     }
13443     else
13444     {
13445         json.WriteString("PreferredBlockSize");
13446         json.WriteNumber(m_PreferredBlockSize);
13447     }
13448 
13449     json.WriteString("Blocks");
13450     json.BeginObject();
13451     for(size_t i = 0; i < m_Blocks.size(); ++i)
13452     {
13453         json.BeginString();
13454         json.ContinueString(m_Blocks[i]->GetId());
13455         json.EndString();
13456 
13457         m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
13458     }
13459     json.EndObject();
13460 
13461     json.EndObject();
13462 }
13463 
13464 #endif // #if VMA_STATS_STRING_ENABLED
13465 
Defragment(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats,VmaDefragmentationFlags flags,VkDeviceSize & maxCpuBytesToMove,uint32_t & maxCpuAllocationsToMove,VkDeviceSize & maxGpuBytesToMove,uint32_t & maxGpuAllocationsToMove,VkCommandBuffer commandBuffer)13466 void VmaBlockVector::Defragment(
13467     class VmaBlockVectorDefragmentationContext* pCtx,
13468     VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags,
13469     VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
13470     VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
13471     VkCommandBuffer commandBuffer)
13472 {
13473     pCtx->res = VK_SUCCESS;
13474 
13475     const VkMemoryPropertyFlags memPropFlags =
13476         m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
13477     const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
13478 
13479     const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
13480         isHostVisible;
13481     const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
13482         !IsCorruptionDetectionEnabled() &&
13483         ((1u << m_MemoryTypeIndex) & m_hAllocator->GetGpuDefragmentationMemoryTypeBits()) != 0;
13484 
13485     // There are options to defragment this memory type.
13486     if(canDefragmentOnCpu || canDefragmentOnGpu)
13487     {
13488         bool defragmentOnGpu;
13489         // There is only one option to defragment this memory type.
13490         if(canDefragmentOnGpu != canDefragmentOnCpu)
13491         {
13492             defragmentOnGpu = canDefragmentOnGpu;
13493         }
13494         // Both options are available: Heuristics to choose the best one.
13495         else
13496         {
13497             defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
13498                 m_hAllocator->IsIntegratedGpu();
13499         }
13500 
13501         bool overlappingMoveSupported = !defragmentOnGpu;
13502 
13503         if(m_hAllocator->m_UseMutex)
13504         {
13505             if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
13506             {
13507                 if(!m_Mutex.TryLockWrite())
13508                 {
13509                     pCtx->res = VK_ERROR_INITIALIZATION_FAILED;
13510                     return;
13511                 }
13512             }
13513             else
13514             {
13515                 m_Mutex.LockWrite();
13516                 pCtx->mutexLocked = true;
13517             }
13518         }
13519 
13520         pCtx->Begin(overlappingMoveSupported, flags);
13521 
13522         // Defragment.
13523 
13524         const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
13525         const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
13526         pCtx->res = pCtx->GetAlgorithm()->Defragment(pCtx->defragmentationMoves, maxBytesToMove, maxAllocationsToMove, flags);
13527 
13528         // Accumulate statistics.
13529         if(pStats != VMA_NULL)
13530         {
13531             const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
13532             const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
13533             pStats->bytesMoved += bytesMoved;
13534             pStats->allocationsMoved += allocationsMoved;
13535             VMA_ASSERT(bytesMoved <= maxBytesToMove);
13536             VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
13537             if(defragmentOnGpu)
13538             {
13539                 maxGpuBytesToMove -= bytesMoved;
13540                 maxGpuAllocationsToMove -= allocationsMoved;
13541             }
13542             else
13543             {
13544                 maxCpuBytesToMove -= bytesMoved;
13545                 maxCpuAllocationsToMove -= allocationsMoved;
13546             }
13547         }
13548 
13549         if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
13550         {
13551             if(m_hAllocator->m_UseMutex)
13552                 m_Mutex.UnlockWrite();
13553 
13554             if(pCtx->res >= VK_SUCCESS && !pCtx->defragmentationMoves.empty())
13555                 pCtx->res = VK_NOT_READY;
13556 
13557             return;
13558         }
13559 
13560         if(pCtx->res >= VK_SUCCESS)
13561         {
13562             if(defragmentOnGpu)
13563             {
13564                 ApplyDefragmentationMovesGpu(pCtx, pCtx->defragmentationMoves, commandBuffer);
13565             }
13566             else
13567             {
13568                 ApplyDefragmentationMovesCpu(pCtx, pCtx->defragmentationMoves);
13569             }
13570         }
13571     }
13572 }
13573 
DefragmentationEnd(class VmaBlockVectorDefragmentationContext * pCtx,uint32_t flags,VmaDefragmentationStats * pStats)13574 void VmaBlockVector::DefragmentationEnd(
13575     class VmaBlockVectorDefragmentationContext* pCtx,
13576     uint32_t flags,
13577     VmaDefragmentationStats* pStats)
13578 {
13579     if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL && m_hAllocator->m_UseMutex)
13580     {
13581         VMA_ASSERT(pCtx->mutexLocked == false);
13582 
13583         // Incremental defragmentation doesn't hold the lock, so when we enter here we don't actually have any
13584         // lock protecting us. Since we mutate state here, we have to take the lock out now
13585         m_Mutex.LockWrite();
13586         pCtx->mutexLocked = true;
13587     }
13588 
13589     // If the mutex isn't locked we didn't do any work and there is nothing to delete.
13590     if(pCtx->mutexLocked || !m_hAllocator->m_UseMutex)
13591     {
13592         // Destroy buffers.
13593         for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--;)
13594         {
13595             VmaBlockDefragmentationContext &blockCtx = pCtx->blockContexts[blockIndex];
13596             if(blockCtx.hBuffer)
13597             {
13598                 (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
13599             }
13600         }
13601 
13602         if(pCtx->res >= VK_SUCCESS)
13603         {
13604             FreeEmptyBlocks(pStats);
13605         }
13606     }
13607 
13608     if(pCtx->mutexLocked)
13609     {
13610         VMA_ASSERT(m_hAllocator->m_UseMutex);
13611         m_Mutex.UnlockWrite();
13612     }
13613 }
13614 
ProcessDefragmentations(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationPassMoveInfo * pMove,uint32_t maxMoves)13615 uint32_t VmaBlockVector::ProcessDefragmentations(
13616     class VmaBlockVectorDefragmentationContext *pCtx,
13617     VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves)
13618 {
13619     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13620 
13621     const uint32_t moveCount = std::min(uint32_t(pCtx->defragmentationMoves.size()) - pCtx->defragmentationMovesProcessed, maxMoves);
13622 
13623     for(uint32_t i = 0; i < moveCount; ++ i)
13624     {
13625         VmaDefragmentationMove& move = pCtx->defragmentationMoves[pCtx->defragmentationMovesProcessed + i];
13626 
13627         pMove->allocation = move.hAllocation;
13628         pMove->memory = move.pDstBlock->GetDeviceMemory();
13629         pMove->offset = move.dstOffset;
13630 
13631         ++ pMove;
13632     }
13633 
13634     pCtx->defragmentationMovesProcessed += moveCount;
13635 
13636     return moveCount;
13637 }
13638 
CommitDefragmentations(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats)13639 void VmaBlockVector::CommitDefragmentations(
13640     class VmaBlockVectorDefragmentationContext *pCtx,
13641     VmaDefragmentationStats* pStats)
13642 {
13643     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13644 
13645     for(uint32_t i = pCtx->defragmentationMovesCommitted; i < pCtx->defragmentationMovesProcessed; ++ i)
13646     {
13647         const VmaDefragmentationMove &move = pCtx->defragmentationMoves[i];
13648 
13649         move.pSrcBlock->m_pMetadata->FreeAtOffset(move.srcOffset);
13650         move.hAllocation->ChangeBlockAllocation(m_hAllocator, move.pDstBlock, move.dstOffset);
13651     }
13652 
13653     pCtx->defragmentationMovesCommitted = pCtx->defragmentationMovesProcessed;
13654     FreeEmptyBlocks(pStats);
13655 }
13656 
CalcAllocationCount()13657 size_t VmaBlockVector::CalcAllocationCount() const
13658 {
13659     size_t result = 0;
13660     for(size_t i = 0; i < m_Blocks.size(); ++i)
13661     {
13662         result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
13663     }
13664     return result;
13665 }
13666 
IsBufferImageGranularityConflictPossible()13667 bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
13668 {
13669     if(m_BufferImageGranularity == 1)
13670     {
13671         return false;
13672     }
13673     VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
13674     for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
13675     {
13676         VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
13677         VMA_ASSERT(m_Algorithm == 0);
13678         VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
13679         if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
13680         {
13681             return true;
13682         }
13683     }
13684     return false;
13685 }
13686 
MakePoolAllocationsLost(uint32_t currentFrameIndex,size_t * pLostAllocationCount)13687 void VmaBlockVector::MakePoolAllocationsLost(
13688     uint32_t currentFrameIndex,
13689     size_t* pLostAllocationCount)
13690 {
13691     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13692     size_t lostAllocationCount = 0;
13693     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13694     {
13695         VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13696         VMA_ASSERT(pBlock);
13697         lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
13698     }
13699     if(pLostAllocationCount != VMA_NULL)
13700     {
13701         *pLostAllocationCount = lostAllocationCount;
13702     }
13703 }
13704 
CheckCorruption()13705 VkResult VmaBlockVector::CheckCorruption()
13706 {
13707     if(!IsCorruptionDetectionEnabled())
13708     {
13709         return VK_ERROR_FEATURE_NOT_PRESENT;
13710     }
13711 
13712     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13713     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13714     {
13715         VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13716         VMA_ASSERT(pBlock);
13717         VkResult res = pBlock->CheckCorruption(m_hAllocator);
13718         if(res != VK_SUCCESS)
13719         {
13720             return res;
13721         }
13722     }
13723     return VK_SUCCESS;
13724 }
13725 
AddStats(VmaStats * pStats)13726 void VmaBlockVector::AddStats(VmaStats* pStats)
13727 {
13728     const uint32_t memTypeIndex = m_MemoryTypeIndex;
13729     const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
13730 
13731     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13732 
13733     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13734     {
13735         const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13736         VMA_ASSERT(pBlock);
13737         VMA_HEAVY_ASSERT(pBlock->Validate());
13738         VmaStatInfo allocationStatInfo;
13739         pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
13740         VmaAddStatInfo(pStats->total, allocationStatInfo);
13741         VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
13742         VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
13743     }
13744 }
13745 
13746 ////////////////////////////////////////////////////////////////////////////////
13747 // VmaDefragmentationAlgorithm_Generic members definition
13748 
VmaDefragmentationAlgorithm_Generic(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)13749 VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
13750     VmaAllocator hAllocator,
13751     VmaBlockVector* pBlockVector,
13752     uint32_t currentFrameIndex,
13753     bool overlappingMoveSupported) :
13754     VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
13755     m_AllocationCount(0),
13756     m_AllAllocations(false),
13757     m_BytesMoved(0),
13758     m_AllocationsMoved(0),
13759     m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
13760 {
13761     // Create block info for each block.
13762     const size_t blockCount = m_pBlockVector->m_Blocks.size();
13763     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13764     {
13765         BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
13766         pBlockInfo->m_OriginalBlockIndex = blockIndex;
13767         pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
13768         m_Blocks.push_back(pBlockInfo);
13769     }
13770 
13771     // Sort them by m_pBlock pointer value.
13772     VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
13773 }
13774 
~VmaDefragmentationAlgorithm_Generic()13775 VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
13776 {
13777     for(size_t i = m_Blocks.size(); i--; )
13778     {
13779         vma_delete(m_hAllocator, m_Blocks[i]);
13780     }
13781 }
13782 
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)13783 void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
13784 {
13785     // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
13786     if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
13787     {
13788         VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
13789         BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
13790         if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
13791         {
13792             AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
13793             (*it)->m_Allocations.push_back(allocInfo);
13794         }
13795         else
13796         {
13797             VMA_ASSERT(0);
13798         }
13799 
13800         ++m_AllocationCount;
13801     }
13802 }
13803 
DefragmentRound(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,bool freeOldAllocations)13804 VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
13805     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13806     VkDeviceSize maxBytesToMove,
13807     uint32_t maxAllocationsToMove,
13808     bool freeOldAllocations)
13809 {
13810     if(m_Blocks.empty())
13811     {
13812         return VK_SUCCESS;
13813     }
13814 
13815     // This is a choice based on research.
13816     // Option 1:
13817     uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
13818     // Option 2:
13819     //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
13820     // Option 3:
13821     //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
13822 
13823     size_t srcBlockMinIndex = 0;
13824     // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
13825     /*
13826     if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
13827     {
13828         const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
13829         if(blocksWithNonMovableCount > 0)
13830         {
13831             srcBlockMinIndex = blocksWithNonMovableCount - 1;
13832         }
13833     }
13834     */
13835 
13836     size_t srcBlockIndex = m_Blocks.size() - 1;
13837     size_t srcAllocIndex = SIZE_MAX;
13838     for(;;)
13839     {
13840         // 1. Find next allocation to move.
13841         // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
13842         // 1.2. Then start from last to first m_Allocations.
13843         while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
13844         {
13845             if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
13846             {
13847                 // Finished: no more allocations to process.
13848                 if(srcBlockIndex == srcBlockMinIndex)
13849                 {
13850                     return VK_SUCCESS;
13851                 }
13852                 else
13853                 {
13854                     --srcBlockIndex;
13855                     srcAllocIndex = SIZE_MAX;
13856                 }
13857             }
13858             else
13859             {
13860                 srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
13861             }
13862         }
13863 
13864         BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
13865         AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
13866 
13867         const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
13868         const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
13869         const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
13870         const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
13871 
13872         // 2. Try to find new place for this allocation in preceding or current block.
13873         for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
13874         {
13875             BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
13876             VmaAllocationRequest dstAllocRequest;
13877             if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
13878                 m_CurrentFrameIndex,
13879                 m_pBlockVector->GetFrameInUseCount(),
13880                 m_pBlockVector->GetBufferImageGranularity(),
13881                 size,
13882                 alignment,
13883                 false, // upperAddress
13884                 suballocType,
13885                 false, // canMakeOtherLost
13886                 strategy,
13887                 &dstAllocRequest) &&
13888             MoveMakesSense(
13889                 dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
13890             {
13891                 VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
13892 
13893                 // Reached limit on number of allocations or bytes to move.
13894                 if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
13895                     (m_BytesMoved + size > maxBytesToMove))
13896                 {
13897                     return VK_SUCCESS;
13898                 }
13899 
13900                 VmaDefragmentationMove move = {};
13901                 move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
13902                 move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
13903                 move.srcOffset = srcOffset;
13904                 move.dstOffset = dstAllocRequest.offset;
13905                 move.size = size;
13906                 move.hAllocation = allocInfo.m_hAllocation;
13907                 move.pSrcBlock = pSrcBlockInfo->m_pBlock;
13908                 move.pDstBlock = pDstBlockInfo->m_pBlock;
13909 
13910                 moves.push_back(move);
13911 
13912                 pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
13913                     dstAllocRequest,
13914                     suballocType,
13915                     size,
13916                     allocInfo.m_hAllocation);
13917 
13918                 if(freeOldAllocations)
13919                 {
13920                     pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
13921                     allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
13922                 }
13923 
13924                 if(allocInfo.m_pChanged != VMA_NULL)
13925                 {
13926                     *allocInfo.m_pChanged = VK_TRUE;
13927                 }
13928 
13929                 ++m_AllocationsMoved;
13930                 m_BytesMoved += size;
13931 
13932                 VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
13933 
13934                 break;
13935             }
13936         }
13937 
13938         // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
13939 
13940         if(srcAllocIndex > 0)
13941         {
13942             --srcAllocIndex;
13943         }
13944         else
13945         {
13946             if(srcBlockIndex > 0)
13947             {
13948                 --srcBlockIndex;
13949                 srcAllocIndex = SIZE_MAX;
13950             }
13951             else
13952             {
13953                 return VK_SUCCESS;
13954             }
13955         }
13956     }
13957 }
13958 
CalcBlocksWithNonMovableCount()13959 size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
13960 {
13961     size_t result = 0;
13962     for(size_t i = 0; i < m_Blocks.size(); ++i)
13963     {
13964         if(m_Blocks[i]->m_HasNonMovableAllocations)
13965         {
13966             ++result;
13967         }
13968     }
13969     return result;
13970 }
13971 
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,VmaDefragmentationFlags flags)13972 VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
13973     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13974     VkDeviceSize maxBytesToMove,
13975     uint32_t maxAllocationsToMove,
13976     VmaDefragmentationFlags flags)
13977 {
13978     if(!m_AllAllocations && m_AllocationCount == 0)
13979     {
13980         return VK_SUCCESS;
13981     }
13982 
13983     const size_t blockCount = m_Blocks.size();
13984     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13985     {
13986         BlockInfo* pBlockInfo = m_Blocks[blockIndex];
13987 
13988         if(m_AllAllocations)
13989         {
13990             VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
13991             for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
13992                 it != pMetadata->m_Suballocations.end();
13993                 ++it)
13994             {
13995                 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
13996                 {
13997                     AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
13998                     pBlockInfo->m_Allocations.push_back(allocInfo);
13999                 }
14000             }
14001         }
14002 
14003         pBlockInfo->CalcHasNonMovableAllocations();
14004 
14005         // This is a choice based on research.
14006         // Option 1:
14007         pBlockInfo->SortAllocationsByOffsetDescending();
14008         // Option 2:
14009         //pBlockInfo->SortAllocationsBySizeDescending();
14010     }
14011 
14012     // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
14013     VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
14014 
14015     // This is a choice based on research.
14016     const uint32_t roundCount = 2;
14017 
14018     // Execute defragmentation rounds (the main part).
14019     VkResult result = VK_SUCCESS;
14020     for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
14021     {
14022         result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove, !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL));
14023     }
14024 
14025     return result;
14026 }
14027 
MoveMakesSense(size_t dstBlockIndex,VkDeviceSize dstOffset,size_t srcBlockIndex,VkDeviceSize srcOffset)14028 bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
14029         size_t dstBlockIndex, VkDeviceSize dstOffset,
14030         size_t srcBlockIndex, VkDeviceSize srcOffset)
14031 {
14032     if(dstBlockIndex < srcBlockIndex)
14033     {
14034         return true;
14035     }
14036     if(dstBlockIndex > srcBlockIndex)
14037     {
14038         return false;
14039     }
14040     if(dstOffset < srcOffset)
14041     {
14042         return true;
14043     }
14044     return false;
14045 }
14046 
14047 ////////////////////////////////////////////////////////////////////////////////
14048 // VmaDefragmentationAlgorithm_Fast
14049 
VmaDefragmentationAlgorithm_Fast(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)14050 VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
14051     VmaAllocator hAllocator,
14052     VmaBlockVector* pBlockVector,
14053     uint32_t currentFrameIndex,
14054     bool overlappingMoveSupported) :
14055     VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
14056     m_OverlappingMoveSupported(overlappingMoveSupported),
14057     m_AllocationCount(0),
14058     m_AllAllocations(false),
14059     m_BytesMoved(0),
14060     m_AllocationsMoved(0),
14061     m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
14062 {
14063     VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
14064 
14065 }
14066 
~VmaDefragmentationAlgorithm_Fast()14067 VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
14068 {
14069 }
14070 
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,VmaDefragmentationFlags flags)14071 VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
14072     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14073     VkDeviceSize maxBytesToMove,
14074     uint32_t maxAllocationsToMove,
14075     VmaDefragmentationFlags flags)
14076 {
14077     VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
14078 
14079     const size_t blockCount = m_pBlockVector->GetBlockCount();
14080     if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
14081     {
14082         return VK_SUCCESS;
14083     }
14084 
14085     PreprocessMetadata();
14086 
14087     // Sort blocks in order from most destination.
14088 
14089     m_BlockInfos.resize(blockCount);
14090     for(size_t i = 0; i < blockCount; ++i)
14091     {
14092         m_BlockInfos[i].origBlockIndex = i;
14093     }
14094 
14095     VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
14096         return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
14097             m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
14098     });
14099 
14100     // THE MAIN ALGORITHM
14101 
14102     FreeSpaceDatabase freeSpaceDb;
14103 
14104     size_t dstBlockInfoIndex = 0;
14105     size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14106     VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14107     VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14108     VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
14109     VkDeviceSize dstOffset = 0;
14110 
14111     bool end = false;
14112     for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
14113     {
14114         const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
14115         VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
14116         VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
14117         for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
14118             !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
14119         {
14120             VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
14121             const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
14122             const VkDeviceSize srcAllocSize = srcSuballocIt->size;
14123             if(m_AllocationsMoved == maxAllocationsToMove ||
14124                 m_BytesMoved + srcAllocSize > maxBytesToMove)
14125             {
14126                 end = true;
14127                 break;
14128             }
14129             const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
14130 
14131             VmaDefragmentationMove move = {};
14132             // Try to place it in one of free spaces from the database.
14133             size_t freeSpaceInfoIndex;
14134             VkDeviceSize dstAllocOffset;
14135             if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
14136                 freeSpaceInfoIndex, dstAllocOffset))
14137             {
14138                 size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
14139                 VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
14140                 VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
14141 
14142                 // Same block
14143                 if(freeSpaceInfoIndex == srcBlockInfoIndex)
14144                 {
14145                     VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14146 
14147                     // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14148 
14149                     VmaSuballocation suballoc = *srcSuballocIt;
14150                     suballoc.offset = dstAllocOffset;
14151                     suballoc.hAllocation->ChangeOffset(dstAllocOffset);
14152                     m_BytesMoved += srcAllocSize;
14153                     ++m_AllocationsMoved;
14154 
14155                     VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14156                     ++nextSuballocIt;
14157                     pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14158                     srcSuballocIt = nextSuballocIt;
14159 
14160                     InsertSuballoc(pFreeSpaceMetadata, suballoc);
14161 
14162                     move.srcBlockIndex = srcOrigBlockIndex;
14163                     move.dstBlockIndex = freeSpaceOrigBlockIndex;
14164                     move.srcOffset = srcAllocOffset;
14165                     move.dstOffset = dstAllocOffset;
14166                     move.size = srcAllocSize;
14167 
14168                     moves.push_back(move);
14169                 }
14170                 // Different block
14171                 else
14172                 {
14173                     // MOVE OPTION 2: Move the allocation to a different block.
14174 
14175                     VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
14176 
14177                     VmaSuballocation suballoc = *srcSuballocIt;
14178                     suballoc.offset = dstAllocOffset;
14179                     suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
14180                     m_BytesMoved += srcAllocSize;
14181                     ++m_AllocationsMoved;
14182 
14183                     VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14184                     ++nextSuballocIt;
14185                     pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14186                     srcSuballocIt = nextSuballocIt;
14187 
14188                     InsertSuballoc(pFreeSpaceMetadata, suballoc);
14189 
14190                     move.srcBlockIndex = srcOrigBlockIndex;
14191                     move.dstBlockIndex = freeSpaceOrigBlockIndex;
14192                     move.srcOffset = srcAllocOffset;
14193                     move.dstOffset = dstAllocOffset;
14194                     move.size = srcAllocSize;
14195 
14196                     moves.push_back(move);
14197                 }
14198             }
14199             else
14200             {
14201                 dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
14202 
14203                 // If the allocation doesn't fit before the end of dstBlock, forward to next block.
14204                 while(dstBlockInfoIndex < srcBlockInfoIndex &&
14205                     dstAllocOffset + srcAllocSize > dstBlockSize)
14206                 {
14207                     // But before that, register remaining free space at the end of dst block.
14208                     freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
14209 
14210                     ++dstBlockInfoIndex;
14211                     dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14212                     pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14213                     pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14214                     dstBlockSize = pDstMetadata->GetSize();
14215                     dstOffset = 0;
14216                     dstAllocOffset = 0;
14217                 }
14218 
14219                 // Same block
14220                 if(dstBlockInfoIndex == srcBlockInfoIndex)
14221                 {
14222                     VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14223 
14224                     const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
14225 
14226                     bool skipOver = overlap;
14227                     if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
14228                     {
14229                         // If destination and source place overlap, skip if it would move it
14230                         // by only < 1/64 of its size.
14231                         skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
14232                     }
14233 
14234                     if(skipOver)
14235                     {
14236                         freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
14237 
14238                         dstOffset = srcAllocOffset + srcAllocSize;
14239                         ++srcSuballocIt;
14240                     }
14241                     // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14242                     else
14243                     {
14244                         srcSuballocIt->offset = dstAllocOffset;
14245                         srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);
14246                         dstOffset = dstAllocOffset + srcAllocSize;
14247                         m_BytesMoved += srcAllocSize;
14248                         ++m_AllocationsMoved;
14249                         ++srcSuballocIt;
14250 
14251                         move.srcBlockIndex = srcOrigBlockIndex;
14252                         move.dstBlockIndex = dstOrigBlockIndex;
14253                         move.srcOffset = srcAllocOffset;
14254                         move.dstOffset = dstAllocOffset;
14255                         move.size = srcAllocSize;
14256 
14257                         moves.push_back(move);
14258                     }
14259                 }
14260                 // Different block
14261                 else
14262                 {
14263                     // MOVE OPTION 2: Move the allocation to a different block.
14264 
14265                     VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
14266                     VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
14267 
14268                     VmaSuballocation suballoc = *srcSuballocIt;
14269                     suballoc.offset = dstAllocOffset;
14270                     suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
14271                     dstOffset = dstAllocOffset + srcAllocSize;
14272                     m_BytesMoved += srcAllocSize;
14273                     ++m_AllocationsMoved;
14274 
14275                     VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14276                     ++nextSuballocIt;
14277                     pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14278                     srcSuballocIt = nextSuballocIt;
14279 
14280                     pDstMetadata->m_Suballocations.push_back(suballoc);
14281 
14282                     move.srcBlockIndex = srcOrigBlockIndex;
14283                     move.dstBlockIndex = dstOrigBlockIndex;
14284                     move.srcOffset = srcAllocOffset;
14285                     move.dstOffset = dstAllocOffset;
14286                     move.size = srcAllocSize;
14287 
14288                     moves.push_back(move);
14289                 }
14290             }
14291         }
14292     }
14293 
14294     m_BlockInfos.clear();
14295 
14296     PostprocessMetadata();
14297 
14298     return VK_SUCCESS;
14299 }
14300 
PreprocessMetadata()14301 void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
14302 {
14303     const size_t blockCount = m_pBlockVector->GetBlockCount();
14304     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14305     {
14306         VmaBlockMetadata_Generic* const pMetadata =
14307             (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14308         pMetadata->m_FreeCount = 0;
14309         pMetadata->m_SumFreeSize = pMetadata->GetSize();
14310         pMetadata->m_FreeSuballocationsBySize.clear();
14311         for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14312             it != pMetadata->m_Suballocations.end(); )
14313         {
14314             if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
14315             {
14316                 VmaSuballocationList::iterator nextIt = it;
14317                 ++nextIt;
14318                 pMetadata->m_Suballocations.erase(it);
14319                 it = nextIt;
14320             }
14321             else
14322             {
14323                 ++it;
14324             }
14325         }
14326     }
14327 }
14328 
PostprocessMetadata()14329 void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
14330 {
14331     const size_t blockCount = m_pBlockVector->GetBlockCount();
14332     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14333     {
14334         VmaBlockMetadata_Generic* const pMetadata =
14335             (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14336         const VkDeviceSize blockSize = pMetadata->GetSize();
14337 
14338         // No allocations in this block - entire area is free.
14339         if(pMetadata->m_Suballocations.empty())
14340         {
14341             pMetadata->m_FreeCount = 1;
14342             //pMetadata->m_SumFreeSize is already set to blockSize.
14343             VmaSuballocation suballoc = {
14344                 0, // offset
14345                 blockSize, // size
14346                 VMA_NULL, // hAllocation
14347                 VMA_SUBALLOCATION_TYPE_FREE };
14348             pMetadata->m_Suballocations.push_back(suballoc);
14349             pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
14350         }
14351         // There are some allocations in this block.
14352         else
14353         {
14354             VkDeviceSize offset = 0;
14355             VmaSuballocationList::iterator it;
14356             for(it = pMetadata->m_Suballocations.begin();
14357                 it != pMetadata->m_Suballocations.end();
14358                 ++it)
14359             {
14360                 VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
14361                 VMA_ASSERT(it->offset >= offset);
14362 
14363                 // Need to insert preceding free space.
14364                 if(it->offset > offset)
14365                 {
14366                     ++pMetadata->m_FreeCount;
14367                     const VkDeviceSize freeSize = it->offset - offset;
14368                     VmaSuballocation suballoc = {
14369                         offset, // offset
14370                         freeSize, // size
14371                         VMA_NULL, // hAllocation
14372                         VMA_SUBALLOCATION_TYPE_FREE };
14373                     VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14374                     if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14375                     {
14376                         pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
14377                     }
14378                 }
14379 
14380                 pMetadata->m_SumFreeSize -= it->size;
14381                 offset = it->offset + it->size;
14382             }
14383 
14384             // Need to insert trailing free space.
14385             if(offset < blockSize)
14386             {
14387                 ++pMetadata->m_FreeCount;
14388                 const VkDeviceSize freeSize = blockSize - offset;
14389                 VmaSuballocation suballoc = {
14390                     offset, // offset
14391                     freeSize, // size
14392                     VMA_NULL, // hAllocation
14393                     VMA_SUBALLOCATION_TYPE_FREE };
14394                 VMA_ASSERT(it == pMetadata->m_Suballocations.end());
14395                 VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14396                 if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14397                 {
14398                     pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
14399                 }
14400             }
14401 
14402             VMA_SORT(
14403                 pMetadata->m_FreeSuballocationsBySize.begin(),
14404                 pMetadata->m_FreeSuballocationsBySize.end(),
14405                 VmaSuballocationItemSizeLess());
14406         }
14407 
14408         VMA_HEAVY_ASSERT(pMetadata->Validate());
14409     }
14410 }
14411 
InsertSuballoc(VmaBlockMetadata_Generic * pMetadata,const VmaSuballocation & suballoc)14412 void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
14413 {
14414     // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
14415     VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14416     while(it != pMetadata->m_Suballocations.end())
14417     {
14418         if(it->offset < suballoc.offset)
14419         {
14420             ++it;
14421         }
14422     }
14423     pMetadata->m_Suballocations.insert(it, suballoc);
14424 }
14425 
14426 ////////////////////////////////////////////////////////////////////////////////
14427 // VmaBlockVectorDefragmentationContext
14428 
VmaBlockVectorDefragmentationContext(VmaAllocator hAllocator,VmaPool hCustomPool,VmaBlockVector * pBlockVector,uint32_t currFrameIndex)14429 VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
14430     VmaAllocator hAllocator,
14431     VmaPool hCustomPool,
14432     VmaBlockVector* pBlockVector,
14433     uint32_t currFrameIndex) :
14434     res(VK_SUCCESS),
14435     mutexLocked(false),
14436     blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
14437     defragmentationMoves(VmaStlAllocator<VmaDefragmentationMove>(hAllocator->GetAllocationCallbacks())),
14438     defragmentationMovesProcessed(0),
14439     defragmentationMovesCommitted(0),
14440     hasDefragmentationPlan(0),
14441     m_hAllocator(hAllocator),
14442     m_hCustomPool(hCustomPool),
14443     m_pBlockVector(pBlockVector),
14444     m_CurrFrameIndex(currFrameIndex),
14445     m_pAlgorithm(VMA_NULL),
14446     m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
14447     m_AllAllocations(false)
14448 {
14449 }
14450 
~VmaBlockVectorDefragmentationContext()14451 VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
14452 {
14453     vma_delete(m_hAllocator, m_pAlgorithm);
14454 }
14455 
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)14456 void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
14457 {
14458     AllocInfo info = { hAlloc, pChanged };
14459     m_Allocations.push_back(info);
14460 }
14461 
Begin(bool overlappingMoveSupported,VmaDefragmentationFlags flags)14462 void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags)
14463 {
14464     const bool allAllocations = m_AllAllocations ||
14465         m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
14466 
14467     /********************************
14468     HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
14469     ********************************/
14470 
14471     /*
14472     Fast algorithm is supported only when certain criteria are met:
14473     - VMA_DEBUG_MARGIN is 0.
14474     - All allocations in this block vector are moveable.
14475     - There is no possibility of image/buffer granularity conflict.
14476     - The defragmentation is not incremental
14477     */
14478     if(VMA_DEBUG_MARGIN == 0 &&
14479         allAllocations &&
14480         !m_pBlockVector->IsBufferImageGranularityConflictPossible() &&
14481         !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL))
14482     {
14483         m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
14484             m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14485     }
14486     else
14487     {
14488         m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
14489             m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14490     }
14491 
14492     if(allAllocations)
14493     {
14494         m_pAlgorithm->AddAll();
14495     }
14496     else
14497     {
14498         for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
14499         {
14500             m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
14501         }
14502     }
14503 }
14504 
14505 ////////////////////////////////////////////////////////////////////////////////
14506 // VmaDefragmentationContext
14507 
VmaDefragmentationContext_T(VmaAllocator hAllocator,uint32_t currFrameIndex,uint32_t flags,VmaDefragmentationStats * pStats)14508 VmaDefragmentationContext_T::VmaDefragmentationContext_T(
14509     VmaAllocator hAllocator,
14510     uint32_t currFrameIndex,
14511     uint32_t flags,
14512     VmaDefragmentationStats* pStats) :
14513     m_hAllocator(hAllocator),
14514     m_CurrFrameIndex(currFrameIndex),
14515     m_Flags(flags),
14516     m_pStats(pStats),
14517     m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
14518 {
14519     memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
14520 }
14521 
~VmaDefragmentationContext_T()14522 VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
14523 {
14524     for(size_t i = m_CustomPoolContexts.size(); i--; )
14525     {
14526         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
14527         pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
14528         vma_delete(m_hAllocator, pBlockVectorCtx);
14529     }
14530     for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
14531     {
14532         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
14533         if(pBlockVectorCtx)
14534         {
14535             pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
14536             vma_delete(m_hAllocator, pBlockVectorCtx);
14537         }
14538     }
14539 }
14540 
AddPools(uint32_t poolCount,const VmaPool * pPools)14541 void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, const VmaPool* pPools)
14542 {
14543     for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
14544     {
14545         VmaPool pool = pPools[poolIndex];
14546         VMA_ASSERT(pool);
14547         // Pools with algorithm other than default are not defragmented.
14548         if(pool->m_BlockVector.GetAlgorithm() == 0)
14549         {
14550             VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
14551 
14552             for(size_t i = m_CustomPoolContexts.size(); i--; )
14553             {
14554                 if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
14555                 {
14556                     pBlockVectorDefragCtx = m_CustomPoolContexts[i];
14557                     break;
14558                 }
14559             }
14560 
14561             if(!pBlockVectorDefragCtx)
14562             {
14563                 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14564                     m_hAllocator,
14565                     pool,
14566                     &pool->m_BlockVector,
14567                     m_CurrFrameIndex);
14568                 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
14569             }
14570 
14571             pBlockVectorDefragCtx->AddAll();
14572         }
14573     }
14574 }
14575 
AddAllocations(uint32_t allocationCount,const VmaAllocation * pAllocations,VkBool32 * pAllocationsChanged)14576 void VmaDefragmentationContext_T::AddAllocations(
14577     uint32_t allocationCount,
14578     const VmaAllocation* pAllocations,
14579     VkBool32* pAllocationsChanged)
14580 {
14581     // Dispatch pAllocations among defragmentators. Create them when necessary.
14582     for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14583     {
14584         const VmaAllocation hAlloc = pAllocations[allocIndex];
14585         VMA_ASSERT(hAlloc);
14586         // DedicatedAlloc cannot be defragmented.
14587         if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
14588             // Lost allocation cannot be defragmented.
14589             (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
14590         {
14591             VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
14592 
14593             const VmaPool hAllocPool = hAlloc->GetBlock()->GetParentPool();
14594             // This allocation belongs to custom pool.
14595             if(hAllocPool != VK_NULL_HANDLE)
14596             {
14597                 // Pools with algorithm other than default are not defragmented.
14598                 if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
14599                 {
14600                     for(size_t i = m_CustomPoolContexts.size(); i--; )
14601                     {
14602                         if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
14603                         {
14604                             pBlockVectorDefragCtx = m_CustomPoolContexts[i];
14605                             break;
14606                         }
14607                     }
14608                     if(!pBlockVectorDefragCtx)
14609                     {
14610                         pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14611                             m_hAllocator,
14612                             hAllocPool,
14613                             &hAllocPool->m_BlockVector,
14614                             m_CurrFrameIndex);
14615                         m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
14616                     }
14617                 }
14618             }
14619             // This allocation belongs to default pool.
14620             else
14621             {
14622                 const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
14623                 pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
14624                 if(!pBlockVectorDefragCtx)
14625                 {
14626                     pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14627                         m_hAllocator,
14628                         VMA_NULL, // hCustomPool
14629                         m_hAllocator->m_pBlockVectors[memTypeIndex],
14630                         m_CurrFrameIndex);
14631                     m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
14632                 }
14633             }
14634 
14635             if(pBlockVectorDefragCtx)
14636             {
14637                 VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
14638                     &pAllocationsChanged[allocIndex] : VMA_NULL;
14639                 pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
14640             }
14641         }
14642     }
14643 }
14644 
Defragment(VkDeviceSize maxCpuBytesToMove,uint32_t maxCpuAllocationsToMove,VkDeviceSize maxGpuBytesToMove,uint32_t maxGpuAllocationsToMove,VkCommandBuffer commandBuffer,VmaDefragmentationStats * pStats,VmaDefragmentationFlags flags)14645 VkResult VmaDefragmentationContext_T::Defragment(
14646     VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
14647     VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
14648     VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags)
14649 {
14650     if(pStats)
14651     {
14652         memset(pStats, 0, sizeof(VmaDefragmentationStats));
14653     }
14654 
14655     if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
14656     {
14657         // For incremental defragmetnations, we just earmark how much we can move
14658         // The real meat is in the defragmentation steps
14659         m_MaxCpuBytesToMove = maxCpuBytesToMove;
14660         m_MaxCpuAllocationsToMove = maxCpuAllocationsToMove;
14661 
14662         m_MaxGpuBytesToMove = maxGpuBytesToMove;
14663         m_MaxGpuAllocationsToMove = maxGpuAllocationsToMove;
14664 
14665         if(m_MaxCpuBytesToMove == 0 && m_MaxCpuAllocationsToMove == 0 &&
14666             m_MaxGpuBytesToMove == 0 && m_MaxGpuAllocationsToMove == 0)
14667             return VK_SUCCESS;
14668 
14669         return VK_NOT_READY;
14670     }
14671 
14672     if(commandBuffer == VK_NULL_HANDLE)
14673     {
14674         maxGpuBytesToMove = 0;
14675         maxGpuAllocationsToMove = 0;
14676     }
14677 
14678     VkResult res = VK_SUCCESS;
14679 
14680     // Process default pools.
14681     for(uint32_t memTypeIndex = 0;
14682         memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
14683         ++memTypeIndex)
14684     {
14685         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14686         if(pBlockVectorCtx)
14687         {
14688             VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14689             pBlockVectorCtx->GetBlockVector()->Defragment(
14690                 pBlockVectorCtx,
14691                 pStats, flags,
14692                 maxCpuBytesToMove, maxCpuAllocationsToMove,
14693                 maxGpuBytesToMove, maxGpuAllocationsToMove,
14694                 commandBuffer);
14695             if(pBlockVectorCtx->res != VK_SUCCESS)
14696             {
14697                 res = pBlockVectorCtx->res;
14698             }
14699         }
14700     }
14701 
14702     // Process custom pools.
14703     for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14704         customCtxIndex < customCtxCount && res >= VK_SUCCESS;
14705         ++customCtxIndex)
14706     {
14707         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14708         VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14709         pBlockVectorCtx->GetBlockVector()->Defragment(
14710             pBlockVectorCtx,
14711             pStats, flags,
14712             maxCpuBytesToMove, maxCpuAllocationsToMove,
14713             maxGpuBytesToMove, maxGpuAllocationsToMove,
14714             commandBuffer);
14715         if(pBlockVectorCtx->res != VK_SUCCESS)
14716         {
14717             res = pBlockVectorCtx->res;
14718         }
14719     }
14720 
14721     return res;
14722 }
14723 
DefragmentPassBegin(VmaDefragmentationPassInfo * pInfo)14724 VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo)
14725 {
14726     VmaDefragmentationPassMoveInfo* pCurrentMove = pInfo->pMoves;
14727     uint32_t movesLeft = pInfo->moveCount;
14728 
14729     // Process default pools.
14730     for(uint32_t memTypeIndex = 0;
14731         memTypeIndex < m_hAllocator->GetMemoryTypeCount();
14732         ++memTypeIndex)
14733     {
14734         VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14735         if(pBlockVectorCtx)
14736         {
14737             VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14738 
14739             if(!pBlockVectorCtx->hasDefragmentationPlan)
14740             {
14741                 pBlockVectorCtx->GetBlockVector()->Defragment(
14742                     pBlockVectorCtx,
14743                     m_pStats, m_Flags,
14744                     m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
14745                     m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
14746                     VK_NULL_HANDLE);
14747 
14748                 if(pBlockVectorCtx->res < VK_SUCCESS)
14749                     continue;
14750 
14751                 pBlockVectorCtx->hasDefragmentationPlan = true;
14752             }
14753 
14754             const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
14755                 pBlockVectorCtx,
14756                 pCurrentMove, movesLeft);
14757 
14758             movesLeft -= processed;
14759             pCurrentMove += processed;
14760         }
14761     }
14762 
14763     // Process custom pools.
14764     for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14765         customCtxIndex < customCtxCount;
14766         ++customCtxIndex)
14767     {
14768         VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14769         VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14770 
14771         if(!pBlockVectorCtx->hasDefragmentationPlan)
14772         {
14773             pBlockVectorCtx->GetBlockVector()->Defragment(
14774                 pBlockVectorCtx,
14775                 m_pStats, m_Flags,
14776                 m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
14777                 m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
14778                 VK_NULL_HANDLE);
14779 
14780             if(pBlockVectorCtx->res < VK_SUCCESS)
14781                 continue;
14782 
14783             pBlockVectorCtx->hasDefragmentationPlan = true;
14784         }
14785 
14786         const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
14787             pBlockVectorCtx,
14788             pCurrentMove, movesLeft);
14789 
14790         movesLeft -= processed;
14791         pCurrentMove += processed;
14792     }
14793 
14794     pInfo->moveCount = pInfo->moveCount - movesLeft;
14795 
14796     return VK_SUCCESS;
14797 }
DefragmentPassEnd()14798 VkResult VmaDefragmentationContext_T::DefragmentPassEnd()
14799 {
14800     VkResult res = VK_SUCCESS;
14801 
14802     // Process default pools.
14803     for(uint32_t memTypeIndex = 0;
14804         memTypeIndex < m_hAllocator->GetMemoryTypeCount();
14805         ++memTypeIndex)
14806     {
14807         VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14808         if(pBlockVectorCtx)
14809         {
14810             VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14811 
14812             if(!pBlockVectorCtx->hasDefragmentationPlan)
14813             {
14814                 res = VK_NOT_READY;
14815                 continue;
14816             }
14817 
14818             pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
14819                 pBlockVectorCtx, m_pStats);
14820 
14821             if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
14822                 res = VK_NOT_READY;
14823         }
14824     }
14825 
14826     // Process custom pools.
14827     for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14828         customCtxIndex < customCtxCount;
14829         ++customCtxIndex)
14830     {
14831         VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14832         VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14833 
14834         if(!pBlockVectorCtx->hasDefragmentationPlan)
14835         {
14836             res = VK_NOT_READY;
14837             continue;
14838         }
14839 
14840         pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
14841             pBlockVectorCtx, m_pStats);
14842 
14843         if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
14844             res = VK_NOT_READY;
14845     }
14846 
14847     return res;
14848 }
14849 
14850 ////////////////////////////////////////////////////////////////////////////////
14851 // VmaRecorder
14852 
14853 #if VMA_RECORDING_ENABLED
14854 
VmaRecorder()14855 VmaRecorder::VmaRecorder() :
14856     m_UseMutex(true),
14857     m_Flags(0),
14858     m_File(VMA_NULL),
14859     m_RecordingStartTime(std::chrono::high_resolution_clock::now())
14860 {
14861 }
14862 
Init(const VmaRecordSettings & settings,bool useMutex)14863 VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
14864 {
14865     m_UseMutex = useMutex;
14866     m_Flags = settings.flags;
14867 
14868 #if defined(_WIN32)
14869     // Open file for writing.
14870     errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
14871 
14872     if(err != 0)
14873     {
14874         return VK_ERROR_INITIALIZATION_FAILED;
14875     }
14876 #else
14877     // Open file for writing.
14878     m_File = fopen(settings.pFilePath, "wb");
14879 
14880     if(m_File == 0)
14881     {
14882         return VK_ERROR_INITIALIZATION_FAILED;
14883     }
14884 #endif
14885 
14886     // Write header.
14887     fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
14888     fprintf(m_File, "%s\n", "1,8");
14889 
14890     return VK_SUCCESS;
14891 }
14892 
~VmaRecorder()14893 VmaRecorder::~VmaRecorder()
14894 {
14895     if(m_File != VMA_NULL)
14896     {
14897         fclose(m_File);
14898     }
14899 }
14900 
RecordCreateAllocator(uint32_t frameIndex)14901 void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
14902 {
14903     CallParams callParams;
14904     GetBasicParams(callParams);
14905 
14906     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14907     fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
14908     Flush();
14909 }
14910 
RecordDestroyAllocator(uint32_t frameIndex)14911 void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
14912 {
14913     CallParams callParams;
14914     GetBasicParams(callParams);
14915 
14916     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14917     fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
14918     Flush();
14919 }
14920 
RecordCreatePool(uint32_t frameIndex,const VmaPoolCreateInfo & createInfo,VmaPool pool)14921 void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
14922 {
14923     CallParams callParams;
14924     GetBasicParams(callParams);
14925 
14926     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14927     fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
14928         createInfo.memoryTypeIndex,
14929         createInfo.flags,
14930         createInfo.blockSize,
14931         (uint64_t)createInfo.minBlockCount,
14932         (uint64_t)createInfo.maxBlockCount,
14933         createInfo.frameInUseCount,
14934         pool);
14935     Flush();
14936 }
14937 
RecordDestroyPool(uint32_t frameIndex,VmaPool pool)14938 void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
14939 {
14940     CallParams callParams;
14941     GetBasicParams(callParams);
14942 
14943     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14944     fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
14945         pool);
14946     Flush();
14947 }
14948 
RecordAllocateMemory(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)14949 void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
14950         const VkMemoryRequirements& vkMemReq,
14951         const VmaAllocationCreateInfo& createInfo,
14952         VmaAllocation allocation)
14953 {
14954     CallParams callParams;
14955     GetBasicParams(callParams);
14956 
14957     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14958     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
14959     fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
14960         vkMemReq.size,
14961         vkMemReq.alignment,
14962         vkMemReq.memoryTypeBits,
14963         createInfo.flags,
14964         createInfo.usage,
14965         createInfo.requiredFlags,
14966         createInfo.preferredFlags,
14967         createInfo.memoryTypeBits,
14968         createInfo.pool,
14969         allocation,
14970         userDataStr.GetString());
14971     Flush();
14972 }
14973 
RecordAllocateMemoryPages(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,uint64_t allocationCount,const VmaAllocation * pAllocations)14974 void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
14975     const VkMemoryRequirements& vkMemReq,
14976     const VmaAllocationCreateInfo& createInfo,
14977     uint64_t allocationCount,
14978     const VmaAllocation* pAllocations)
14979 {
14980     CallParams callParams;
14981     GetBasicParams(callParams);
14982 
14983     VmaMutexLock lock(m_FileMutex, m_UseMutex);
14984     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
14985     fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
14986         vkMemReq.size,
14987         vkMemReq.alignment,
14988         vkMemReq.memoryTypeBits,
14989         createInfo.flags,
14990         createInfo.usage,
14991         createInfo.requiredFlags,
14992         createInfo.preferredFlags,
14993         createInfo.memoryTypeBits,
14994         createInfo.pool);
14995     PrintPointerList(allocationCount, pAllocations);
14996     fprintf(m_File, ",%s\n", userDataStr.GetString());
14997     Flush();
14998 }
14999 
RecordAllocateMemoryForBuffer(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)15000 void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
15001     const VkMemoryRequirements& vkMemReq,
15002     bool requiresDedicatedAllocation,
15003     bool prefersDedicatedAllocation,
15004     const VmaAllocationCreateInfo& createInfo,
15005     VmaAllocation allocation)
15006 {
15007     CallParams callParams;
15008     GetBasicParams(callParams);
15009 
15010     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15011     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15012     fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryForBuffer,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15013         vkMemReq.size,
15014         vkMemReq.alignment,
15015         vkMemReq.memoryTypeBits,
15016         requiresDedicatedAllocation ? 1 : 0,
15017         prefersDedicatedAllocation ? 1 : 0,
15018         createInfo.flags,
15019         createInfo.usage,
15020         createInfo.requiredFlags,
15021         createInfo.preferredFlags,
15022         createInfo.memoryTypeBits,
15023         createInfo.pool,
15024         allocation,
15025         userDataStr.GetString());
15026     Flush();
15027 }
15028 
RecordAllocateMemoryForImage(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)15029 void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
15030     const VkMemoryRequirements& vkMemReq,
15031     bool requiresDedicatedAllocation,
15032     bool prefersDedicatedAllocation,
15033     const VmaAllocationCreateInfo& createInfo,
15034     VmaAllocation allocation)
15035 {
15036     CallParams callParams;
15037     GetBasicParams(callParams);
15038 
15039     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15040     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15041     fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryForImage,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15042         vkMemReq.size,
15043         vkMemReq.alignment,
15044         vkMemReq.memoryTypeBits,
15045         requiresDedicatedAllocation ? 1 : 0,
15046         prefersDedicatedAllocation ? 1 : 0,
15047         createInfo.flags,
15048         createInfo.usage,
15049         createInfo.requiredFlags,
15050         createInfo.preferredFlags,
15051         createInfo.memoryTypeBits,
15052         createInfo.pool,
15053         allocation,
15054         userDataStr.GetString());
15055     Flush();
15056 }
15057 
RecordFreeMemory(uint32_t frameIndex,VmaAllocation allocation)15058 void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
15059     VmaAllocation allocation)
15060 {
15061     CallParams callParams;
15062     GetBasicParams(callParams);
15063 
15064     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15065     fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15066         allocation);
15067     Flush();
15068 }
15069 
RecordFreeMemoryPages(uint32_t frameIndex,uint64_t allocationCount,const VmaAllocation * pAllocations)15070 void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
15071     uint64_t allocationCount,
15072     const VmaAllocation* pAllocations)
15073 {
15074     CallParams callParams;
15075     GetBasicParams(callParams);
15076 
15077     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15078     fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
15079     PrintPointerList(allocationCount, pAllocations);
15080     fprintf(m_File, "\n");
15081     Flush();
15082 }
15083 
RecordSetAllocationUserData(uint32_t frameIndex,VmaAllocation allocation,const void * pUserData)15084 void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
15085     VmaAllocation allocation,
15086     const void* pUserData)
15087 {
15088     CallParams callParams;
15089     GetBasicParams(callParams);
15090 
15091     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15092     UserDataString userDataStr(
15093         allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
15094         pUserData);
15095     fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15096         allocation,
15097         userDataStr.GetString());
15098     Flush();
15099 }
15100 
RecordCreateLostAllocation(uint32_t frameIndex,VmaAllocation allocation)15101 void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
15102     VmaAllocation allocation)
15103 {
15104     CallParams callParams;
15105     GetBasicParams(callParams);
15106 
15107     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15108     fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15109         allocation);
15110     Flush();
15111 }
15112 
RecordMapMemory(uint32_t frameIndex,VmaAllocation allocation)15113 void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
15114     VmaAllocation allocation)
15115 {
15116     CallParams callParams;
15117     GetBasicParams(callParams);
15118 
15119     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15120     fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15121         allocation);
15122     Flush();
15123 }
15124 
RecordUnmapMemory(uint32_t frameIndex,VmaAllocation allocation)15125 void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
15126     VmaAllocation allocation)
15127 {
15128     CallParams callParams;
15129     GetBasicParams(callParams);
15130 
15131     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15132     fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15133         allocation);
15134     Flush();
15135 }
15136 
RecordFlushAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)15137 void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
15138     VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15139 {
15140     CallParams callParams;
15141     GetBasicParams(callParams);
15142 
15143     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15144     fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15145         allocation,
15146         offset,
15147         size);
15148     Flush();
15149 }
15150 
RecordInvalidateAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)15151 void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
15152     VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15153 {
15154     CallParams callParams;
15155     GetBasicParams(callParams);
15156 
15157     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15158     fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15159         allocation,
15160         offset,
15161         size);
15162     Flush();
15163 }
15164 
RecordCreateBuffer(uint32_t frameIndex,const VkBufferCreateInfo & bufCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)15165 void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
15166     const VkBufferCreateInfo& bufCreateInfo,
15167     const VmaAllocationCreateInfo& allocCreateInfo,
15168     VmaAllocation allocation)
15169 {
15170     CallParams callParams;
15171     GetBasicParams(callParams);
15172 
15173     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15174     UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15175     fprintf(m_File, "%u,%.3f,%u,vmaCreateBuffer,%u,%llu,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15176         bufCreateInfo.flags,
15177         bufCreateInfo.size,
15178         bufCreateInfo.usage,
15179         bufCreateInfo.sharingMode,
15180         allocCreateInfo.flags,
15181         allocCreateInfo.usage,
15182         allocCreateInfo.requiredFlags,
15183         allocCreateInfo.preferredFlags,
15184         allocCreateInfo.memoryTypeBits,
15185         allocCreateInfo.pool,
15186         allocation,
15187         userDataStr.GetString());
15188     Flush();
15189 }
15190 
RecordCreateImage(uint32_t frameIndex,const VkImageCreateInfo & imageCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)15191 void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
15192     const VkImageCreateInfo& imageCreateInfo,
15193     const VmaAllocationCreateInfo& allocCreateInfo,
15194     VmaAllocation allocation)
15195 {
15196     CallParams callParams;
15197     GetBasicParams(callParams);
15198 
15199     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15200     UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15201     fprintf(m_File, "%u,%.3f,%u,vmaCreateImage,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15202         imageCreateInfo.flags,
15203         imageCreateInfo.imageType,
15204         imageCreateInfo.format,
15205         imageCreateInfo.extent.width,
15206         imageCreateInfo.extent.height,
15207         imageCreateInfo.extent.depth,
15208         imageCreateInfo.mipLevels,
15209         imageCreateInfo.arrayLayers,
15210         imageCreateInfo.samples,
15211         imageCreateInfo.tiling,
15212         imageCreateInfo.usage,
15213         imageCreateInfo.sharingMode,
15214         imageCreateInfo.initialLayout,
15215         allocCreateInfo.flags,
15216         allocCreateInfo.usage,
15217         allocCreateInfo.requiredFlags,
15218         allocCreateInfo.preferredFlags,
15219         allocCreateInfo.memoryTypeBits,
15220         allocCreateInfo.pool,
15221         allocation,
15222         userDataStr.GetString());
15223     Flush();
15224 }
15225 
RecordDestroyBuffer(uint32_t frameIndex,VmaAllocation allocation)15226 void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
15227     VmaAllocation allocation)
15228 {
15229     CallParams callParams;
15230     GetBasicParams(callParams);
15231 
15232     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15233     fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
15234         allocation);
15235     Flush();
15236 }
15237 
RecordDestroyImage(uint32_t frameIndex,VmaAllocation allocation)15238 void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
15239     VmaAllocation allocation)
15240 {
15241     CallParams callParams;
15242     GetBasicParams(callParams);
15243 
15244     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15245     fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
15246         allocation);
15247     Flush();
15248 }
15249 
RecordTouchAllocation(uint32_t frameIndex,VmaAllocation allocation)15250 void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
15251     VmaAllocation allocation)
15252 {
15253     CallParams callParams;
15254     GetBasicParams(callParams);
15255 
15256     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15257     fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15258         allocation);
15259     Flush();
15260 }
15261 
RecordGetAllocationInfo(uint32_t frameIndex,VmaAllocation allocation)15262 void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
15263     VmaAllocation allocation)
15264 {
15265     CallParams callParams;
15266     GetBasicParams(callParams);
15267 
15268     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15269     fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
15270         allocation);
15271     Flush();
15272 }
15273 
RecordMakePoolAllocationsLost(uint32_t frameIndex,VmaPool pool)15274 void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
15275     VmaPool pool)
15276 {
15277     CallParams callParams;
15278     GetBasicParams(callParams);
15279 
15280     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15281     fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
15282         pool);
15283     Flush();
15284 }
15285 
RecordDefragmentationBegin(uint32_t frameIndex,const VmaDefragmentationInfo2 & info,VmaDefragmentationContext ctx)15286 void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
15287     const VmaDefragmentationInfo2& info,
15288     VmaDefragmentationContext ctx)
15289 {
15290     CallParams callParams;
15291     GetBasicParams(callParams);
15292 
15293     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15294     fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
15295         info.flags);
15296     PrintPointerList(info.allocationCount, info.pAllocations);
15297     fprintf(m_File, ",");
15298     PrintPointerList(info.poolCount, info.pPools);
15299     fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
15300         info.maxCpuBytesToMove,
15301         info.maxCpuAllocationsToMove,
15302         info.maxGpuBytesToMove,
15303         info.maxGpuAllocationsToMove,
15304         info.commandBuffer,
15305         ctx);
15306     Flush();
15307 }
15308 
RecordDefragmentationEnd(uint32_t frameIndex,VmaDefragmentationContext ctx)15309 void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
15310     VmaDefragmentationContext ctx)
15311 {
15312     CallParams callParams;
15313     GetBasicParams(callParams);
15314 
15315     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15316     fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
15317         ctx);
15318     Flush();
15319 }
15320 
RecordSetPoolName(uint32_t frameIndex,VmaPool pool,const char * name)15321 void VmaRecorder::RecordSetPoolName(uint32_t frameIndex,
15322     VmaPool pool,
15323     const char* name)
15324 {
15325     CallParams callParams;
15326     GetBasicParams(callParams);
15327 
15328     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15329     fprintf(m_File, "%u,%.3f,%u,vmaSetPoolName,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15330         pool, name != VMA_NULL ? name : "");
15331     Flush();
15332 }
15333 
UserDataString(VmaAllocationCreateFlags allocFlags,const void * pUserData)15334 VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
15335 {
15336     if(pUserData != VMA_NULL)
15337     {
15338         if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
15339         {
15340             m_Str = (const char*)pUserData;
15341         }
15342         else
15343         {
15344             // If VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is not specified, convert the string's memory address to a string and store it.
15345             snprintf(m_PtrStr, 17, "%p", pUserData);
15346             m_Str = m_PtrStr;
15347         }
15348     }
15349     else
15350     {
15351         m_Str = "";
15352     }
15353 }
15354 
WriteConfiguration(const VkPhysicalDeviceProperties & devProps,const VkPhysicalDeviceMemoryProperties & memProps,uint32_t vulkanApiVersion,bool dedicatedAllocationExtensionEnabled,bool bindMemory2ExtensionEnabled,bool memoryBudgetExtensionEnabled,bool deviceCoherentMemoryExtensionEnabled)15355 void VmaRecorder::WriteConfiguration(
15356     const VkPhysicalDeviceProperties& devProps,
15357     const VkPhysicalDeviceMemoryProperties& memProps,
15358     uint32_t vulkanApiVersion,
15359     bool dedicatedAllocationExtensionEnabled,
15360     bool bindMemory2ExtensionEnabled,
15361     bool memoryBudgetExtensionEnabled,
15362     bool deviceCoherentMemoryExtensionEnabled)
15363 {
15364     fprintf(m_File, "Config,Begin\n");
15365 
15366     fprintf(m_File, "VulkanApiVersion,%u,%u\n", VK_VERSION_MAJOR(vulkanApiVersion), VK_VERSION_MINOR(vulkanApiVersion));
15367 
15368     fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
15369     fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
15370     fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
15371     fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
15372     fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
15373     fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
15374 
15375     fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
15376     fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
15377     fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
15378 
15379     fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
15380     for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
15381     {
15382         fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
15383         fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
15384     }
15385     fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
15386     for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
15387     {
15388         fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
15389         fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
15390     }
15391 
15392     fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
15393     fprintf(m_File, "Extension,VK_KHR_bind_memory2,%u\n", bindMemory2ExtensionEnabled ? 1 : 0);
15394     fprintf(m_File, "Extension,VK_EXT_memory_budget,%u\n", memoryBudgetExtensionEnabled ? 1 : 0);
15395     fprintf(m_File, "Extension,VK_AMD_device_coherent_memory,%u\n", deviceCoherentMemoryExtensionEnabled ? 1 : 0);
15396 
15397     fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
15398     fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);
15399     fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
15400     fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
15401     fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
15402     fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
15403     fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
15404     fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
15405     fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15406 
15407     fprintf(m_File, "Config,End\n");
15408 }
15409 
GetBasicParams(CallParams & outParams)15410 void VmaRecorder::GetBasicParams(CallParams& outParams)
15411 {
15412     #if defined(_WIN32)
15413         outParams.threadId = GetCurrentThreadId();
15414     #else
15415         // Use C++11 features to get thread id and convert it to uint32_t.
15416         // There is room for optimization since sstream is quite slow.
15417         // Is there a better way to convert std::this_thread::get_id() to uint32_t?
15418         std::thread::id thread_id = std::this_thread::get_id();
15419         stringstream thread_id_to_string_converter;
15420         thread_id_to_string_converter << thread_id;
15421         string thread_id_as_string = thread_id_to_string_converter.str();
15422         outParams.threadId = static_cast<uint32_t>(std::stoi(thread_id_as_string.c_str()));
15423     #endif
15424 
15425     auto current_time = std::chrono::high_resolution_clock::now();
15426 
15427     outParams.time = std::chrono::duration<double, std::chrono::seconds::period>(current_time - m_RecordingStartTime).count();
15428 }
15429 
PrintPointerList(uint64_t count,const VmaAllocation * pItems)15430 void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
15431 {
15432     if(count)
15433     {
15434         fprintf(m_File, "%p", pItems[0]);
15435         for(uint64_t i = 1; i < count; ++i)
15436         {
15437             fprintf(m_File, " %p", pItems[i]);
15438         }
15439     }
15440 }
15441 
Flush()15442 void VmaRecorder::Flush()
15443 {
15444     if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
15445     {
15446         fflush(m_File);
15447     }
15448 }
15449 
15450 #endif // #if VMA_RECORDING_ENABLED
15451 
15452 ////////////////////////////////////////////////////////////////////////////////
15453 // VmaAllocationObjectAllocator
15454 
VmaAllocationObjectAllocator(const VkAllocationCallbacks * pAllocationCallbacks)15455 VmaAllocationObjectAllocator::VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks) :
15456     m_Allocator(pAllocationCallbacks, 1024)
15457 {
15458 }
15459 
Allocate(Types...args)15460 template<typename... Types> VmaAllocation VmaAllocationObjectAllocator::Allocate(Types... args)
15461 {
15462     VmaMutexLock mutexLock(m_Mutex);
15463     return m_Allocator.Alloc<Types...>(std::forward<Types>(args)...);
15464 }
15465 
Free(VmaAllocation hAlloc)15466 void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc)
15467 {
15468     VmaMutexLock mutexLock(m_Mutex);
15469     m_Allocator.Free(hAlloc);
15470 }
15471 
15472 ////////////////////////////////////////////////////////////////////////////////
15473 // VmaAllocator_T
15474 
VmaAllocator_T(const VmaAllocatorCreateInfo * pCreateInfo)15475 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
15476     m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
15477     m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0),
15478     m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
15479     m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0),
15480     m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0),
15481     m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0),
15482     m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0),
15483     m_hDevice(pCreateInfo->device),
15484     m_hInstance(pCreateInfo->instance),
15485     m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
15486     m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
15487         *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
15488     m_AllocationObjectAllocator(&m_AllocationCallbacks),
15489     m_HeapSizeLimitMask(0),
15490     m_PreferredLargeHeapBlockSize(0),
15491     m_PhysicalDevice(pCreateInfo->physicalDevice),
15492     m_CurrentFrameIndex(0),
15493     m_GpuDefragmentationMemoryTypeBits(UINT32_MAX),
15494     m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks())),
15495     m_NextPoolId(0),
15496     m_GlobalMemoryTypeBits(UINT32_MAX)
15497 #if VMA_RECORDING_ENABLED
15498     ,m_pRecorder(VMA_NULL)
15499 #endif
15500 {
15501     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15502     {
15503         m_UseKhrDedicatedAllocation = false;
15504         m_UseKhrBindMemory2 = false;
15505     }
15506 
15507     if(VMA_DEBUG_DETECT_CORRUPTION)
15508     {
15509         // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
15510         VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
15511     }
15512 
15513     VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance);
15514 
15515     if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
15516     {
15517 #if !(VMA_DEDICATED_ALLOCATION)
15518         if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
15519         {
15520             VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
15521         }
15522 #endif
15523 #if !(VMA_BIND_MEMORY2)
15524         if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0)
15525         {
15526             VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros.");
15527         }
15528 #endif
15529     }
15530 #if !(VMA_MEMORY_BUDGET)
15531     if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0)
15532     {
15533         VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros.");
15534     }
15535 #endif
15536 #if !(VMA_BUFFER_DEVICE_ADDRESS)
15537     if(m_UseKhrBufferDeviceAddress)
15538     {
15539         VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT is set but required extension or Vulkan 1.2 is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
15540     }
15541 #endif
15542 #if VMA_VULKAN_VERSION < 1002000
15543     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0))
15544     {
15545         VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros.");
15546     }
15547 #endif
15548 #if VMA_VULKAN_VERSION < 1001000
15549     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15550     {
15551         VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros.");
15552     }
15553 #endif
15554 
15555     memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
15556     memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
15557     memset(&m_MemProps, 0, sizeof(m_MemProps));
15558 
15559     memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
15560     memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations));
15561     memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions));
15562 
15563     if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
15564     {
15565         m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData;
15566         m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
15567         m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
15568     }
15569 
15570     ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
15571 
15572     (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
15573     (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
15574 
15575     VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT));
15576     VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
15577     VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
15578     VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
15579 
15580     m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
15581         pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15582 
15583     m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits();
15584 
15585     if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
15586     {
15587         for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
15588         {
15589             const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
15590             if(limit != VK_WHOLE_SIZE)
15591             {
15592                 m_HeapSizeLimitMask |= 1u << heapIndex;
15593                 if(limit < m_MemProps.memoryHeaps[heapIndex].size)
15594                 {
15595                     m_MemProps.memoryHeaps[heapIndex].size = limit;
15596                 }
15597             }
15598         }
15599     }
15600 
15601     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15602     {
15603         const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
15604 
15605         m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
15606             this,
15607             VK_NULL_HANDLE, // hParentPool
15608             memTypeIndex,
15609             preferredBlockSize,
15610             0,
15611             SIZE_MAX,
15612             GetBufferImageGranularity(),
15613             pCreateInfo->frameInUseCount,
15614             false, // explicitBlockSize
15615             false); // linearAlgorithm
15616         // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
15617         // becase minBlockCount is 0.
15618         m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
15619 
15620     }
15621 }
15622 
Init(const VmaAllocatorCreateInfo * pCreateInfo)15623 VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
15624 {
15625     VkResult res = VK_SUCCESS;
15626 
15627     if(pCreateInfo->pRecordSettings != VMA_NULL &&
15628         !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
15629     {
15630 #if VMA_RECORDING_ENABLED
15631         m_pRecorder = vma_new(this, VmaRecorder)();
15632         res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
15633         if(res != VK_SUCCESS)
15634         {
15635             return res;
15636         }
15637         m_pRecorder->WriteConfiguration(
15638             m_PhysicalDeviceProperties,
15639             m_MemProps,
15640             m_VulkanApiVersion,
15641             m_UseKhrDedicatedAllocation,
15642             m_UseKhrBindMemory2,
15643             m_UseExtMemoryBudget,
15644             m_UseAmdDeviceCoherentMemory);
15645         m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
15646 #else
15647         VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
15648         return VK_ERROR_FEATURE_NOT_PRESENT;
15649 #endif
15650     }
15651 
15652 #if VMA_MEMORY_BUDGET
15653     if(m_UseExtMemoryBudget)
15654     {
15655         UpdateVulkanBudget();
15656     }
15657 #endif // #if VMA_MEMORY_BUDGET
15658 
15659     return res;
15660 }
15661 
~VmaAllocator_T()15662 VmaAllocator_T::~VmaAllocator_T()
15663 {
15664 #if VMA_RECORDING_ENABLED
15665     if(m_pRecorder != VMA_NULL)
15666     {
15667         m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
15668         vma_delete(this, m_pRecorder);
15669     }
15670 #endif
15671 
15672     VMA_ASSERT(m_Pools.empty());
15673 
15674     for(size_t i = GetMemoryTypeCount(); i--; )
15675     {
15676         if(m_pDedicatedAllocations[i] != VMA_NULL && !m_pDedicatedAllocations[i]->empty())
15677         {
15678             VMA_ASSERT(0 && "Unfreed dedicated allocations found.");
15679         }
15680 
15681         vma_delete(this, m_pDedicatedAllocations[i]);
15682         vma_delete(this, m_pBlockVectors[i]);
15683     }
15684 }
15685 
ImportVulkanFunctions(const VmaVulkanFunctions * pVulkanFunctions)15686 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
15687 {
15688 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15689     ImportVulkanFunctions_Static();
15690 #endif
15691 
15692     if(pVulkanFunctions != VMA_NULL)
15693     {
15694         ImportVulkanFunctions_Custom(pVulkanFunctions);
15695     }
15696 
15697 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
15698     ImportVulkanFunctions_Dynamic();
15699 #endif
15700 
15701     ValidateVulkanFunctions();
15702 }
15703 
15704 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15705 
ImportVulkanFunctions_Static()15706 void VmaAllocator_T::ImportVulkanFunctions_Static()
15707 {
15708     // Vulkan 1.0
15709     m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties;
15710     m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties;
15711     m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
15712     m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory;
15713     m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory;
15714     m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory;
15715     m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges;
15716     m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges;
15717     m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory;
15718     m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory;
15719     m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements;
15720     m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements;
15721     m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer;
15722     m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer;
15723     m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage;
15724     m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage;
15725     m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer;
15726 
15727     // Vulkan 1.1
15728 #if VMA_VULKAN_VERSION >= 1001000
15729     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15730     {
15731         m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2;
15732         m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2;
15733         m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2;
15734         m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2;
15735         m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2;
15736     }
15737 #endif
15738 }
15739 
15740 #endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15741 
ImportVulkanFunctions_Custom(const VmaVulkanFunctions * pVulkanFunctions)15742 void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions)
15743 {
15744     VMA_ASSERT(pVulkanFunctions != VMA_NULL);
15745 
15746 #define VMA_COPY_IF_NOT_NULL(funcName) \
15747     if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
15748 
15749     VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
15750     VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
15751     VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
15752     VMA_COPY_IF_NOT_NULL(vkFreeMemory);
15753     VMA_COPY_IF_NOT_NULL(vkMapMemory);
15754     VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
15755     VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
15756     VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
15757     VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
15758     VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
15759     VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
15760     VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
15761     VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
15762     VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
15763     VMA_COPY_IF_NOT_NULL(vkCreateImage);
15764     VMA_COPY_IF_NOT_NULL(vkDestroyImage);
15765     VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
15766 
15767 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15768     VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
15769     VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
15770 #endif
15771 
15772 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
15773     VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR);
15774     VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR);
15775 #endif
15776 
15777 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
15778     VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR);
15779 #endif
15780 
15781 #undef VMA_COPY_IF_NOT_NULL
15782 }
15783 
15784 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
15785 
ImportVulkanFunctions_Dynamic()15786 void VmaAllocator_T::ImportVulkanFunctions_Dynamic()
15787 {
15788 #define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \
15789     if(m_VulkanFunctions.memberName == VMA_NULL) \
15790         m_VulkanFunctions.memberName = \
15791             (functionPointerType)vkGetInstanceProcAddr(m_hInstance, functionNameString);
15792 #define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \
15793     if(m_VulkanFunctions.memberName == VMA_NULL) \
15794         m_VulkanFunctions.memberName = \
15795             (functionPointerType)vkGetDeviceProcAddr(m_hDevice, functionNameString);
15796 
15797     VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");
15798     VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties");
15799     VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory");
15800     VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory");
15801     VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory");
15802     VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory");
15803     VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges");
15804     VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges");
15805     VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory");
15806     VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory");
15807     VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements");
15808     VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements");
15809     VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer");
15810     VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer");
15811     VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage");
15812     VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage");
15813     VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer");
15814 
15815 #if VMA_DEDICATED_ALLOCATION
15816     if(m_UseKhrDedicatedAllocation)
15817     {
15818         VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR");
15819         VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR");
15820     }
15821 #endif
15822 
15823 #if VMA_BIND_MEMORY2
15824     if(m_UseKhrBindMemory2)
15825     {
15826         VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR");
15827         VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR");
15828     }
15829 #endif // #if VMA_BIND_MEMORY2
15830 
15831 #if VMA_MEMORY_BUDGET
15832     if(m_UseExtMemoryBudget && m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
15833     {
15834         VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");
15835     }
15836 #endif // #if VMA_MEMORY_BUDGET
15837 
15838 #undef VMA_FETCH_DEVICE_FUNC
15839 #undef VMA_FETCH_INSTANCE_FUNC
15840 }
15841 
15842 #endif // #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
15843 
ValidateVulkanFunctions()15844 void VmaAllocator_T::ValidateVulkanFunctions()
15845 {
15846     VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
15847     VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
15848     VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
15849     VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
15850     VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
15851     VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
15852     VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
15853     VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
15854     VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
15855     VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
15856     VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
15857     VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
15858     VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
15859     VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
15860     VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
15861     VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
15862     VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
15863 
15864 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15865     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation)
15866     {
15867         VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
15868         VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
15869     }
15870 #endif
15871 
15872 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
15873     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2)
15874     {
15875         VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL);
15876         VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL);
15877     }
15878 #endif
15879 
15880 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
15881     if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15882     {
15883         VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);
15884     }
15885 #endif
15886 }
15887 
CalcPreferredBlockSize(uint32_t memTypeIndex)15888 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
15889 {
15890     const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
15891     const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
15892     const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
15893     return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32);
15894 }
15895 
AllocateMemoryOfType(VkDeviceSize size,VkDeviceSize alignment,bool dedicatedAllocation,VkBuffer dedicatedBuffer,VkBufferUsageFlags dedicatedBufferUsage,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,uint32_t memTypeIndex,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)15896 VkResult VmaAllocator_T::AllocateMemoryOfType(
15897     VkDeviceSize size,
15898     VkDeviceSize alignment,
15899     bool dedicatedAllocation,
15900     VkBuffer dedicatedBuffer,
15901     VkBufferUsageFlags dedicatedBufferUsage,
15902     VkImage dedicatedImage,
15903     const VmaAllocationCreateInfo& createInfo,
15904     uint32_t memTypeIndex,
15905     VmaSuballocationType suballocType,
15906     size_t allocationCount,
15907     VmaAllocation* pAllocations)
15908 {
15909     VMA_ASSERT(pAllocations != VMA_NULL);
15910     VMA_DEBUG_LOG("  AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size);
15911 
15912     VmaAllocationCreateInfo finalCreateInfo = createInfo;
15913 
15914     // If memory type is not HOST_VISIBLE, disable MAPPED.
15915     if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
15916         (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15917     {
15918         finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
15919     }
15920     // If memory is lazily allocated, it should be always dedicated.
15921     if(finalCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED)
15922     {
15923         finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
15924     }
15925 
15926     VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
15927     VMA_ASSERT(blockVector);
15928 
15929     const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
15930     bool preferDedicatedMemory =
15931         VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
15932         dedicatedAllocation ||
15933         // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
15934         size > preferredBlockSize / 2;
15935 
15936     if(preferDedicatedMemory &&
15937         (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
15938         finalCreateInfo.pool == VK_NULL_HANDLE)
15939     {
15940         finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
15941     }
15942 
15943     if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
15944     {
15945         if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
15946         {
15947             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15948         }
15949         else
15950         {
15951             return AllocateDedicatedMemory(
15952                 size,
15953                 suballocType,
15954                 memTypeIndex,
15955                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
15956                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
15957                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
15958                 finalCreateInfo.pUserData,
15959                 dedicatedBuffer,
15960                 dedicatedBufferUsage,
15961                 dedicatedImage,
15962                 allocationCount,
15963                 pAllocations);
15964         }
15965     }
15966     else
15967     {
15968         VkResult res = blockVector->Allocate(
15969             m_CurrentFrameIndex.load(),
15970             size,
15971             alignment,
15972             finalCreateInfo,
15973             suballocType,
15974             allocationCount,
15975             pAllocations);
15976         if(res == VK_SUCCESS)
15977         {
15978             return res;
15979         }
15980 
15981         // 5. Try dedicated memory.
15982         if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
15983         {
15984             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15985         }
15986         else
15987         {
15988             res = AllocateDedicatedMemory(
15989                 size,
15990                 suballocType,
15991                 memTypeIndex,
15992                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
15993                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
15994                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
15995                 finalCreateInfo.pUserData,
15996                 dedicatedBuffer,
15997                 dedicatedBufferUsage,
15998                 dedicatedImage,
15999                 allocationCount,
16000                 pAllocations);
16001             if(res == VK_SUCCESS)
16002             {
16003                 // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
16004                 VMA_DEBUG_LOG("    Allocated as DedicatedMemory");
16005                 return VK_SUCCESS;
16006             }
16007             else
16008             {
16009                 // Everything failed: Return error code.
16010                 VMA_DEBUG_LOG("    vkAllocateMemory FAILED");
16011                 return res;
16012             }
16013         }
16014     }
16015 }
16016 
AllocateDedicatedMemory(VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,bool withinBudget,bool map,bool isUserDataString,void * pUserData,VkBuffer dedicatedBuffer,VkBufferUsageFlags dedicatedBufferUsage,VkImage dedicatedImage,size_t allocationCount,VmaAllocation * pAllocations)16017 VkResult VmaAllocator_T::AllocateDedicatedMemory(
16018     VkDeviceSize size,
16019     VmaSuballocationType suballocType,
16020     uint32_t memTypeIndex,
16021     bool withinBudget,
16022     bool map,
16023     bool isUserDataString,
16024     void* pUserData,
16025     VkBuffer dedicatedBuffer,
16026     VkBufferUsageFlags dedicatedBufferUsage,
16027     VkImage dedicatedImage,
16028     size_t allocationCount,
16029     VmaAllocation* pAllocations)
16030 {
16031     VMA_ASSERT(allocationCount > 0 && pAllocations);
16032 
16033     if(withinBudget)
16034     {
16035         const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16036         VmaBudget heapBudget = {};
16037         GetBudget(&heapBudget, heapIndex, 1);
16038         if(heapBudget.usage + size * allocationCount > heapBudget.budget)
16039         {
16040             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16041         }
16042     }
16043 
16044     VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
16045     allocInfo.memoryTypeIndex = memTypeIndex;
16046     allocInfo.allocationSize = size;
16047 
16048 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16049     VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
16050     if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16051     {
16052         if(dedicatedBuffer != VK_NULL_HANDLE)
16053         {
16054             VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
16055             dedicatedAllocInfo.buffer = dedicatedBuffer;
16056             VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16057         }
16058         else if(dedicatedImage != VK_NULL_HANDLE)
16059         {
16060             dedicatedAllocInfo.image = dedicatedImage;
16061             VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16062         }
16063     }
16064 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16065 
16066 #if VMA_BUFFER_DEVICE_ADDRESS
16067     VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
16068     if(m_UseKhrBufferDeviceAddress)
16069     {
16070         bool canContainBufferWithDeviceAddress = true;
16071         if(dedicatedBuffer != VK_NULL_HANDLE)
16072         {
16073             canContainBufferWithDeviceAddress = dedicatedBufferUsage == UINT32_MAX || // Usage flags unknown
16074                 (dedicatedBufferUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) != 0;
16075         }
16076         else if(dedicatedImage != VK_NULL_HANDLE)
16077         {
16078             canContainBufferWithDeviceAddress = false;
16079         }
16080         if(canContainBufferWithDeviceAddress)
16081         {
16082             allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
16083             VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
16084         }
16085     }
16086 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
16087 
16088     size_t allocIndex;
16089     VkResult res = VK_SUCCESS;
16090     for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16091     {
16092         res = AllocateDedicatedMemoryPage(
16093             size,
16094             suballocType,
16095             memTypeIndex,
16096             allocInfo,
16097             map,
16098             isUserDataString,
16099             pUserData,
16100             pAllocations + allocIndex);
16101         if(res != VK_SUCCESS)
16102         {
16103             break;
16104         }
16105     }
16106 
16107     if(res == VK_SUCCESS)
16108     {
16109         // Register them in m_pDedicatedAllocations.
16110         {
16111             VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16112             AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
16113             VMA_ASSERT(pDedicatedAllocations);
16114             for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16115             {
16116                 VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, pAllocations[allocIndex]);
16117             }
16118         }
16119 
16120         VMA_DEBUG_LOG("    Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
16121     }
16122     else
16123     {
16124         // Free all already created allocations.
16125         while(allocIndex--)
16126         {
16127             VmaAllocation currAlloc = pAllocations[allocIndex];
16128             VkDeviceMemory hMemory = currAlloc->GetMemory();
16129 
16130             /*
16131             There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
16132             before vkFreeMemory.
16133 
16134             if(currAlloc->GetMappedData() != VMA_NULL)
16135             {
16136                 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
16137             }
16138             */
16139 
16140             FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
16141             m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize());
16142             currAlloc->SetUserData(this, VMA_NULL);
16143             m_AllocationObjectAllocator.Free(currAlloc);
16144         }
16145 
16146         memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16147     }
16148 
16149     return res;
16150 }
16151 
AllocateDedicatedMemoryPage(VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,const VkMemoryAllocateInfo & allocInfo,bool map,bool isUserDataString,void * pUserData,VmaAllocation * pAllocation)16152 VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
16153     VkDeviceSize size,
16154     VmaSuballocationType suballocType,
16155     uint32_t memTypeIndex,
16156     const VkMemoryAllocateInfo& allocInfo,
16157     bool map,
16158     bool isUserDataString,
16159     void* pUserData,
16160     VmaAllocation* pAllocation)
16161 {
16162     VkDeviceMemory hMemory = VK_NULL_HANDLE;
16163     VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
16164     if(res < 0)
16165     {
16166         VMA_DEBUG_LOG("    vkAllocateMemory FAILED");
16167         return res;
16168     }
16169 
16170     void* pMappedData = VMA_NULL;
16171     if(map)
16172     {
16173         res = (*m_VulkanFunctions.vkMapMemory)(
16174             m_hDevice,
16175             hMemory,
16176             0,
16177             VK_WHOLE_SIZE,
16178             0,
16179             &pMappedData);
16180         if(res < 0)
16181         {
16182             VMA_DEBUG_LOG("    vkMapMemory FAILED");
16183             FreeVulkanMemory(memTypeIndex, size, hMemory);
16184             return res;
16185         }
16186     }
16187 
16188     *pAllocation = m_AllocationObjectAllocator.Allocate(m_CurrentFrameIndex.load(), isUserDataString);
16189     (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
16190     (*pAllocation)->SetUserData(this, pUserData);
16191     m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size);
16192     if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16193     {
16194         FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
16195     }
16196 
16197     return VK_SUCCESS;
16198 }
16199 
GetBufferMemoryRequirements(VkBuffer hBuffer,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)16200 void VmaAllocator_T::GetBufferMemoryRequirements(
16201     VkBuffer hBuffer,
16202     VkMemoryRequirements& memReq,
16203     bool& requiresDedicatedAllocation,
16204     bool& prefersDedicatedAllocation) const
16205 {
16206 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16207     if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16208     {
16209         VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
16210         memReqInfo.buffer = hBuffer;
16211 
16212         VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16213 
16214         VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16215         VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16216 
16217         (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16218 
16219         memReq = memReq2.memoryRequirements;
16220         requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16221         prefersDedicatedAllocation  = (memDedicatedReq.prefersDedicatedAllocation  != VK_FALSE);
16222     }
16223     else
16224 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16225     {
16226         (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
16227         requiresDedicatedAllocation = false;
16228         prefersDedicatedAllocation  = false;
16229     }
16230 }
16231 
GetImageMemoryRequirements(VkImage hImage,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)16232 void VmaAllocator_T::GetImageMemoryRequirements(
16233     VkImage hImage,
16234     VkMemoryRequirements& memReq,
16235     bool& requiresDedicatedAllocation,
16236     bool& prefersDedicatedAllocation) const
16237 {
16238 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16239     if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16240     {
16241         VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
16242         memReqInfo.image = hImage;
16243 
16244         VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16245 
16246         VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16247         VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16248 
16249         (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16250 
16251         memReq = memReq2.memoryRequirements;
16252         requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16253         prefersDedicatedAllocation  = (memDedicatedReq.prefersDedicatedAllocation  != VK_FALSE);
16254     }
16255     else
16256 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16257     {
16258         (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
16259         requiresDedicatedAllocation = false;
16260         prefersDedicatedAllocation  = false;
16261     }
16262 }
16263 
AllocateMemory(const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,VkBuffer dedicatedBuffer,VkBufferUsageFlags dedicatedBufferUsage,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)16264 VkResult VmaAllocator_T::AllocateMemory(
16265     const VkMemoryRequirements& vkMemReq,
16266     bool requiresDedicatedAllocation,
16267     bool prefersDedicatedAllocation,
16268     VkBuffer dedicatedBuffer,
16269     VkBufferUsageFlags dedicatedBufferUsage,
16270     VkImage dedicatedImage,
16271     const VmaAllocationCreateInfo& createInfo,
16272     VmaSuballocationType suballocType,
16273     size_t allocationCount,
16274     VmaAllocation* pAllocations)
16275 {
16276     memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16277 
16278     VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
16279 
16280     if(vkMemReq.size == 0)
16281     {
16282         return VK_ERROR_VALIDATION_FAILED_EXT;
16283     }
16284     if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
16285         (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16286     {
16287         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
16288         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16289     }
16290     if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16291         (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
16292     {
16293         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
16294         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16295     }
16296     if(requiresDedicatedAllocation)
16297     {
16298         if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16299         {
16300             VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
16301             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16302         }
16303         if(createInfo.pool != VK_NULL_HANDLE)
16304         {
16305             VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
16306             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16307         }
16308     }
16309     if((createInfo.pool != VK_NULL_HANDLE) &&
16310         ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
16311     {
16312         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
16313         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16314     }
16315 
16316     if(createInfo.pool != VK_NULL_HANDLE)
16317     {
16318         const VkDeviceSize alignmentForPool = VMA_MAX(
16319             vkMemReq.alignment,
16320             GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex()));
16321 
16322         VmaAllocationCreateInfo createInfoForPool = createInfo;
16323         // If memory type is not HOST_VISIBLE, disable MAPPED.
16324         if((createInfoForPool.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16325             (m_MemProps.memoryTypes[createInfo.pool->m_BlockVector.GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
16326         {
16327             createInfoForPool.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
16328         }
16329 
16330         return createInfo.pool->m_BlockVector.Allocate(
16331             m_CurrentFrameIndex.load(),
16332             vkMemReq.size,
16333             alignmentForPool,
16334             createInfoForPool,
16335             suballocType,
16336             allocationCount,
16337             pAllocations);
16338     }
16339     else
16340     {
16341         // Bit mask of memory Vulkan types acceptable for this allocation.
16342         uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
16343         uint32_t memTypeIndex = UINT32_MAX;
16344         VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16345         if(res == VK_SUCCESS)
16346         {
16347             VkDeviceSize alignmentForMemType = VMA_MAX(
16348                 vkMemReq.alignment,
16349                 GetMemoryTypeMinAlignment(memTypeIndex));
16350 
16351             res = AllocateMemoryOfType(
16352                 vkMemReq.size,
16353                 alignmentForMemType,
16354                 requiresDedicatedAllocation || prefersDedicatedAllocation,
16355                 dedicatedBuffer,
16356                 dedicatedBufferUsage,
16357                 dedicatedImage,
16358                 createInfo,
16359                 memTypeIndex,
16360                 suballocType,
16361                 allocationCount,
16362                 pAllocations);
16363             // Succeeded on first try.
16364             if(res == VK_SUCCESS)
16365             {
16366                 return res;
16367             }
16368             // Allocation from this memory type failed. Try other compatible memory types.
16369             else
16370             {
16371                 for(;;)
16372                 {
16373                     // Remove old memTypeIndex from list of possibilities.
16374                     memoryTypeBits &= ~(1u << memTypeIndex);
16375                     // Find alternative memTypeIndex.
16376                     res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16377                     if(res == VK_SUCCESS)
16378                     {
16379                         alignmentForMemType = VMA_MAX(
16380                             vkMemReq.alignment,
16381                             GetMemoryTypeMinAlignment(memTypeIndex));
16382 
16383                         res = AllocateMemoryOfType(
16384                             vkMemReq.size,
16385                             alignmentForMemType,
16386                             requiresDedicatedAllocation || prefersDedicatedAllocation,
16387                             dedicatedBuffer,
16388                             dedicatedBufferUsage,
16389                             dedicatedImage,
16390                             createInfo,
16391                             memTypeIndex,
16392                             suballocType,
16393                             allocationCount,
16394                             pAllocations);
16395                         // Allocation from this alternative memory type succeeded.
16396                         if(res == VK_SUCCESS)
16397                         {
16398                             return res;
16399                         }
16400                         // else: Allocation from this memory type failed. Try next one - next loop iteration.
16401                     }
16402                     // No other matching memory type index could be found.
16403                     else
16404                     {
16405                         // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
16406                         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16407                     }
16408                 }
16409             }
16410         }
16411         // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
16412         else
16413             return res;
16414     }
16415 }
16416 
FreeMemory(size_t allocationCount,const VmaAllocation * pAllocations)16417 void VmaAllocator_T::FreeMemory(
16418     size_t allocationCount,
16419     const VmaAllocation* pAllocations)
16420 {
16421     VMA_ASSERT(pAllocations);
16422 
16423     for(size_t allocIndex = allocationCount; allocIndex--; )
16424     {
16425         VmaAllocation allocation = pAllocations[allocIndex];
16426 
16427         if(allocation != VK_NULL_HANDLE)
16428         {
16429             if(TouchAllocation(allocation))
16430             {
16431                 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16432                 {
16433                     FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
16434                 }
16435 
16436                 switch(allocation->GetType())
16437                 {
16438                 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16439                     {
16440                         VmaBlockVector* pBlockVector = VMA_NULL;
16441                         VmaPool hPool = allocation->GetBlock()->GetParentPool();
16442                         if(hPool != VK_NULL_HANDLE)
16443                         {
16444                             pBlockVector = &hPool->m_BlockVector;
16445                         }
16446                         else
16447                         {
16448                             const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16449                             pBlockVector = m_pBlockVectors[memTypeIndex];
16450                         }
16451                         pBlockVector->Free(allocation);
16452                     }
16453                     break;
16454                 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
16455                     FreeDedicatedMemory(allocation);
16456                     break;
16457                 default:
16458                     VMA_ASSERT(0);
16459                 }
16460             }
16461 
16462             // Do this regardless of whether the allocation is lost. Lost allocations still account to Budget.AllocationBytes.
16463             m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
16464             allocation->SetUserData(this, VMA_NULL);
16465             m_AllocationObjectAllocator.Free(allocation);
16466         }
16467     }
16468 }
16469 
ResizeAllocation(const VmaAllocation alloc,VkDeviceSize newSize)16470 VkResult VmaAllocator_T::ResizeAllocation(
16471     const VmaAllocation alloc,
16472     VkDeviceSize newSize)
16473 {
16474     // This function is deprecated and so it does nothing. It's left for backward compatibility.
16475     if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST)
16476     {
16477         return VK_ERROR_VALIDATION_FAILED_EXT;
16478     }
16479     if(newSize == alloc->GetSize())
16480     {
16481         return VK_SUCCESS;
16482     }
16483     return VK_ERROR_OUT_OF_POOL_MEMORY;
16484 }
16485 
CalculateStats(VmaStats * pStats)16486 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
16487 {
16488     // Initialize.
16489     InitStatInfo(pStats->total);
16490     for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
16491         InitStatInfo(pStats->memoryType[i]);
16492     for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
16493         InitStatInfo(pStats->memoryHeap[i]);
16494 
16495     // Process default pools.
16496     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16497     {
16498         VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
16499         VMA_ASSERT(pBlockVector);
16500         pBlockVector->AddStats(pStats);
16501     }
16502 
16503     // Process custom pools.
16504     {
16505         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
16506         for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
16507         {
16508             m_Pools[poolIndex]->m_BlockVector.AddStats(pStats);
16509         }
16510     }
16511 
16512     // Process dedicated allocations.
16513     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16514     {
16515         const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16516         VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16517         AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
16518         VMA_ASSERT(pDedicatedAllocVector);
16519         for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
16520         {
16521             VmaStatInfo allocationStatInfo;
16522             (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo);
16523             VmaAddStatInfo(pStats->total, allocationStatInfo);
16524             VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
16525             VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
16526         }
16527     }
16528 
16529     // Postprocess.
16530     VmaPostprocessCalcStatInfo(pStats->total);
16531     for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
16532         VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
16533     for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
16534         VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
16535 }
16536 
GetBudget(VmaBudget * outBudget,uint32_t firstHeap,uint32_t heapCount)16537 void VmaAllocator_T::GetBudget(VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount)
16538 {
16539 #if VMA_MEMORY_BUDGET
16540     if(m_UseExtMemoryBudget)
16541     {
16542         if(m_Budget.m_OperationsSinceBudgetFetch < 30)
16543         {
16544             VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex);
16545             for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
16546             {
16547                 const uint32_t heapIndex = firstHeap + i;
16548 
16549                 outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
16550                 outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
16551 
16552                 if(m_Budget.m_VulkanUsage[heapIndex] + outBudget->blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex])
16553                 {
16554                     outBudget->usage = m_Budget.m_VulkanUsage[heapIndex] +
16555                         outBudget->blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
16556                 }
16557                 else
16558                 {
16559                     outBudget->usage = 0;
16560                 }
16561 
16562                 // Have to take MIN with heap size because explicit HeapSizeLimit is included in it.
16563                 outBudget->budget = VMA_MIN(
16564                     m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size);
16565             }
16566         }
16567         else
16568         {
16569             UpdateVulkanBudget(); // Outside of mutex lock
16570             GetBudget(outBudget, firstHeap, heapCount); // Recursion
16571         }
16572     }
16573     else
16574 #endif
16575     {
16576         for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
16577         {
16578             const uint32_t heapIndex = firstHeap + i;
16579 
16580             outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
16581             outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
16582 
16583             outBudget->usage = outBudget->blockBytes;
16584             outBudget->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
16585         }
16586     }
16587 }
16588 
16589 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
16590 
DefragmentationBegin(const VmaDefragmentationInfo2 & info,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)16591 VkResult VmaAllocator_T::DefragmentationBegin(
16592     const VmaDefragmentationInfo2& info,
16593     VmaDefragmentationStats* pStats,
16594     VmaDefragmentationContext* pContext)
16595 {
16596     if(info.pAllocationsChanged != VMA_NULL)
16597     {
16598         memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
16599     }
16600 
16601     *pContext = vma_new(this, VmaDefragmentationContext_T)(
16602         this, m_CurrentFrameIndex.load(), info.flags, pStats);
16603 
16604     (*pContext)->AddPools(info.poolCount, info.pPools);
16605     (*pContext)->AddAllocations(
16606         info.allocationCount, info.pAllocations, info.pAllocationsChanged);
16607 
16608     VkResult res = (*pContext)->Defragment(
16609         info.maxCpuBytesToMove, info.maxCpuAllocationsToMove,
16610         info.maxGpuBytesToMove, info.maxGpuAllocationsToMove,
16611         info.commandBuffer, pStats, info.flags);
16612 
16613     if(res != VK_NOT_READY)
16614     {
16615         vma_delete(this, *pContext);
16616         *pContext = VMA_NULL;
16617     }
16618 
16619     return res;
16620 }
16621 
DefragmentationEnd(VmaDefragmentationContext context)16622 VkResult VmaAllocator_T::DefragmentationEnd(
16623     VmaDefragmentationContext context)
16624 {
16625     vma_delete(this, context);
16626     return VK_SUCCESS;
16627 }
16628 
DefragmentationPassBegin(VmaDefragmentationPassInfo * pInfo,VmaDefragmentationContext context)16629 VkResult VmaAllocator_T::DefragmentationPassBegin(
16630     VmaDefragmentationPassInfo* pInfo,
16631     VmaDefragmentationContext context)
16632 {
16633     return context->DefragmentPassBegin(pInfo);
16634 }
DefragmentationPassEnd(VmaDefragmentationContext context)16635 VkResult VmaAllocator_T::DefragmentationPassEnd(
16636     VmaDefragmentationContext context)
16637 {
16638     return context->DefragmentPassEnd();
16639 
16640 }
16641 
GetAllocationInfo(VmaAllocation hAllocation,VmaAllocationInfo * pAllocationInfo)16642 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
16643 {
16644     if(hAllocation->CanBecomeLost())
16645     {
16646         /*
16647         Warning: This is a carefully designed algorithm.
16648         Do not modify unless you really know what you're doing :)
16649         */
16650         const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
16651         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
16652         for(;;)
16653         {
16654             if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
16655             {
16656                 pAllocationInfo->memoryType = UINT32_MAX;
16657                 pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
16658                 pAllocationInfo->offset = 0;
16659                 pAllocationInfo->size = hAllocation->GetSize();
16660                 pAllocationInfo->pMappedData = VMA_NULL;
16661                 pAllocationInfo->pUserData = hAllocation->GetUserData();
16662                 return;
16663             }
16664             else if(localLastUseFrameIndex == localCurrFrameIndex)
16665             {
16666                 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
16667                 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
16668                 pAllocationInfo->offset = hAllocation->GetOffset();
16669                 pAllocationInfo->size = hAllocation->GetSize();
16670                 pAllocationInfo->pMappedData = VMA_NULL;
16671                 pAllocationInfo->pUserData = hAllocation->GetUserData();
16672                 return;
16673             }
16674             else // Last use time earlier than current time.
16675             {
16676                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
16677                 {
16678                     localLastUseFrameIndex = localCurrFrameIndex;
16679                 }
16680             }
16681         }
16682     }
16683     else
16684     {
16685 #if VMA_STATS_STRING_ENABLED
16686         uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
16687         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
16688         for(;;)
16689         {
16690             VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
16691             if(localLastUseFrameIndex == localCurrFrameIndex)
16692             {
16693                 break;
16694             }
16695             else // Last use time earlier than current time.
16696             {
16697                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
16698                 {
16699                     localLastUseFrameIndex = localCurrFrameIndex;
16700                 }
16701             }
16702         }
16703 #endif
16704 
16705         pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
16706         pAllocationInfo->deviceMemory = hAllocation->GetMemory();
16707         pAllocationInfo->offset = hAllocation->GetOffset();
16708         pAllocationInfo->size = hAllocation->GetSize();
16709         pAllocationInfo->pMappedData = hAllocation->GetMappedData();
16710         pAllocationInfo->pUserData = hAllocation->GetUserData();
16711     }
16712 }
16713 
TouchAllocation(VmaAllocation hAllocation)16714 bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
16715 {
16716     // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
16717     if(hAllocation->CanBecomeLost())
16718     {
16719         uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
16720         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
16721         for(;;)
16722         {
16723             if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
16724             {
16725                 return false;
16726             }
16727             else if(localLastUseFrameIndex == localCurrFrameIndex)
16728             {
16729                 return true;
16730             }
16731             else // Last use time earlier than current time.
16732             {
16733                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
16734                 {
16735                     localLastUseFrameIndex = localCurrFrameIndex;
16736                 }
16737             }
16738         }
16739     }
16740     else
16741     {
16742 #if VMA_STATS_STRING_ENABLED
16743         uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
16744         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
16745         for(;;)
16746         {
16747             VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
16748             if(localLastUseFrameIndex == localCurrFrameIndex)
16749             {
16750                 break;
16751             }
16752             else // Last use time earlier than current time.
16753             {
16754                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
16755                 {
16756                     localLastUseFrameIndex = localCurrFrameIndex;
16757                 }
16758             }
16759         }
16760 #endif
16761 
16762         return true;
16763     }
16764 }
16765 
CreatePool(const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)16766 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
16767 {
16768     VMA_DEBUG_LOG("  CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
16769 
16770     VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
16771 
16772     if(newCreateInfo.maxBlockCount == 0)
16773     {
16774         newCreateInfo.maxBlockCount = SIZE_MAX;
16775     }
16776     if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
16777     {
16778         return VK_ERROR_INITIALIZATION_FAILED;
16779     }
16780     // Memory type index out of range or forbidden.
16781     if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() ||
16782         ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0)
16783     {
16784         return VK_ERROR_FEATURE_NOT_PRESENT;
16785     }
16786 
16787     const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
16788 
16789     *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
16790 
16791     VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
16792     if(res != VK_SUCCESS)
16793     {
16794         vma_delete(this, *pPool);
16795         *pPool = VMA_NULL;
16796         return res;
16797     }
16798 
16799     // Add to m_Pools.
16800     {
16801         VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
16802         (*pPool)->SetId(m_NextPoolId++);
16803         VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool);
16804     }
16805 
16806     return VK_SUCCESS;
16807 }
16808 
DestroyPool(VmaPool pool)16809 void VmaAllocator_T::DestroyPool(VmaPool pool)
16810 {
16811     // Remove from m_Pools.
16812     {
16813         VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
16814         bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
16815         VMA_ASSERT(success && "Pool not found in Allocator.");
16816     }
16817 
16818     vma_delete(this, pool);
16819 }
16820 
GetPoolStats(VmaPool pool,VmaPoolStats * pPoolStats)16821 void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
16822 {
16823     pool->m_BlockVector.GetPoolStats(pPoolStats);
16824 }
16825 
SetCurrentFrameIndex(uint32_t frameIndex)16826 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
16827 {
16828     m_CurrentFrameIndex.store(frameIndex);
16829 
16830 #if VMA_MEMORY_BUDGET
16831     if(m_UseExtMemoryBudget)
16832     {
16833         UpdateVulkanBudget();
16834     }
16835 #endif // #if VMA_MEMORY_BUDGET
16836 }
16837 
MakePoolAllocationsLost(VmaPool hPool,size_t * pLostAllocationCount)16838 void VmaAllocator_T::MakePoolAllocationsLost(
16839     VmaPool hPool,
16840     size_t* pLostAllocationCount)
16841 {
16842     hPool->m_BlockVector.MakePoolAllocationsLost(
16843         m_CurrentFrameIndex.load(),
16844         pLostAllocationCount);
16845 }
16846 
CheckPoolCorruption(VmaPool hPool)16847 VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
16848 {
16849     return hPool->m_BlockVector.CheckCorruption();
16850 }
16851 
CheckCorruption(uint32_t memoryTypeBits)16852 VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
16853 {
16854     VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
16855 
16856     // Process default pools.
16857     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16858     {
16859         if(((1u << memTypeIndex) & memoryTypeBits) != 0)
16860         {
16861             VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
16862             VMA_ASSERT(pBlockVector);
16863             VkResult localRes = pBlockVector->CheckCorruption();
16864             switch(localRes)
16865             {
16866             case VK_ERROR_FEATURE_NOT_PRESENT:
16867                 break;
16868             case VK_SUCCESS:
16869                 finalRes = VK_SUCCESS;
16870                 break;
16871             default:
16872                 return localRes;
16873             }
16874         }
16875     }
16876 
16877     // Process custom pools.
16878     {
16879         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
16880         for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
16881         {
16882             if(((1u << m_Pools[poolIndex]->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
16883             {
16884                 VkResult localRes = m_Pools[poolIndex]->m_BlockVector.CheckCorruption();
16885                 switch(localRes)
16886                 {
16887                 case VK_ERROR_FEATURE_NOT_PRESENT:
16888                     break;
16889                 case VK_SUCCESS:
16890                     finalRes = VK_SUCCESS;
16891                     break;
16892                 default:
16893                     return localRes;
16894                 }
16895             }
16896         }
16897     }
16898 
16899     return finalRes;
16900 }
16901 
CreateLostAllocation(VmaAllocation * pAllocation)16902 void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
16903 {
16904     *pAllocation = m_AllocationObjectAllocator.Allocate(VMA_FRAME_INDEX_LOST, false);
16905     (*pAllocation)->InitLost();
16906 }
16907 
AllocateVulkanMemory(const VkMemoryAllocateInfo * pAllocateInfo,VkDeviceMemory * pMemory)16908 VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
16909 {
16910     const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
16911 
16912     // HeapSizeLimit is in effect for this heap.
16913     if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0)
16914     {
16915         const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
16916         VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex];
16917         for(;;)
16918         {
16919             const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize;
16920             if(blockBytesAfterAllocation > heapSize)
16921             {
16922                 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16923             }
16924             if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation))
16925             {
16926                 break;
16927             }
16928         }
16929     }
16930     else
16931     {
16932         m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize;
16933     }
16934 
16935     // VULKAN CALL vkAllocateMemory.
16936     VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
16937 
16938     if(res == VK_SUCCESS)
16939     {
16940 #if VMA_MEMORY_BUDGET
16941         ++m_Budget.m_OperationsSinceBudgetFetch;
16942 #endif
16943 
16944         // Informative callback.
16945         if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
16946         {
16947             (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData);
16948         }
16949     }
16950     else
16951     {
16952         m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize;
16953     }
16954 
16955     return res;
16956 }
16957 
FreeVulkanMemory(uint32_t memoryType,VkDeviceSize size,VkDeviceMemory hMemory)16958 void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
16959 {
16960     // Informative callback.
16961     if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
16962     {
16963         (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData);
16964     }
16965 
16966     // VULKAN CALL vkFreeMemory.
16967     (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
16968 
16969     m_Budget.m_BlockBytes[MemoryTypeIndexToHeapIndex(memoryType)] -= size;
16970 }
16971 
BindVulkanBuffer(VkDeviceMemory memory,VkDeviceSize memoryOffset,VkBuffer buffer,const void * pNext)16972 VkResult VmaAllocator_T::BindVulkanBuffer(
16973     VkDeviceMemory memory,
16974     VkDeviceSize memoryOffset,
16975     VkBuffer buffer,
16976     const void* pNext)
16977 {
16978     if(pNext != VMA_NULL)
16979     {
16980 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
16981         if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
16982             m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL)
16983         {
16984             VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR };
16985             bindBufferMemoryInfo.pNext = pNext;
16986             bindBufferMemoryInfo.buffer = buffer;
16987             bindBufferMemoryInfo.memory = memory;
16988             bindBufferMemoryInfo.memoryOffset = memoryOffset;
16989             return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
16990         }
16991         else
16992 #endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
16993         {
16994             return VK_ERROR_EXTENSION_NOT_PRESENT;
16995         }
16996     }
16997     else
16998     {
16999         return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset);
17000     }
17001 }
17002 
BindVulkanImage(VkDeviceMemory memory,VkDeviceSize memoryOffset,VkImage image,const void * pNext)17003 VkResult VmaAllocator_T::BindVulkanImage(
17004     VkDeviceMemory memory,
17005     VkDeviceSize memoryOffset,
17006     VkImage image,
17007     const void* pNext)
17008 {
17009     if(pNext != VMA_NULL)
17010     {
17011 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17012         if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
17013             m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL)
17014         {
17015             VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR };
17016             bindBufferMemoryInfo.pNext = pNext;
17017             bindBufferMemoryInfo.image = image;
17018             bindBufferMemoryInfo.memory = memory;
17019             bindBufferMemoryInfo.memoryOffset = memoryOffset;
17020             return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17021         }
17022         else
17023 #endif // #if VMA_BIND_MEMORY2
17024         {
17025             return VK_ERROR_EXTENSION_NOT_PRESENT;
17026         }
17027     }
17028     else
17029     {
17030         return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset);
17031     }
17032 }
17033 
Map(VmaAllocation hAllocation,void ** ppData)17034 VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
17035 {
17036     if(hAllocation->CanBecomeLost())
17037     {
17038         return VK_ERROR_MEMORY_MAP_FAILED;
17039     }
17040 
17041     switch(hAllocation->GetType())
17042     {
17043     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17044         {
17045             VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17046             char *pBytes = VMA_NULL;
17047             VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
17048             if(res == VK_SUCCESS)
17049             {
17050                 *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
17051                 hAllocation->BlockAllocMap();
17052             }
17053             return res;
17054         }
17055     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17056         return hAllocation->DedicatedAllocMap(this, ppData);
17057     default:
17058         VMA_ASSERT(0);
17059         return VK_ERROR_MEMORY_MAP_FAILED;
17060     }
17061 }
17062 
Unmap(VmaAllocation hAllocation)17063 void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
17064 {
17065     switch(hAllocation->GetType())
17066     {
17067     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17068         {
17069             VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17070             hAllocation->BlockAllocUnmap();
17071             pBlock->Unmap(this, 1);
17072         }
17073         break;
17074     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17075         hAllocation->DedicatedAllocUnmap(this);
17076         break;
17077     default:
17078         VMA_ASSERT(0);
17079     }
17080 }
17081 
BindBufferMemory(VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkBuffer hBuffer,const void * pNext)17082 VkResult VmaAllocator_T::BindBufferMemory(
17083     VmaAllocation hAllocation,
17084     VkDeviceSize allocationLocalOffset,
17085     VkBuffer hBuffer,
17086     const void* pNext)
17087 {
17088     VkResult res = VK_SUCCESS;
17089     switch(hAllocation->GetType())
17090     {
17091     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17092         res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext);
17093         break;
17094     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17095     {
17096         VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17097         VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
17098         res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext);
17099         break;
17100     }
17101     default:
17102         VMA_ASSERT(0);
17103     }
17104     return res;
17105 }
17106 
BindImageMemory(VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkImage hImage,const void * pNext)17107 VkResult VmaAllocator_T::BindImageMemory(
17108     VmaAllocation hAllocation,
17109     VkDeviceSize allocationLocalOffset,
17110     VkImage hImage,
17111     const void* pNext)
17112 {
17113     VkResult res = VK_SUCCESS;
17114     switch(hAllocation->GetType())
17115     {
17116     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17117         res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext);
17118         break;
17119     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17120     {
17121         VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
17122         VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
17123         res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext);
17124         break;
17125     }
17126     default:
17127         VMA_ASSERT(0);
17128     }
17129     return res;
17130 }
17131 
FlushOrInvalidateAllocation(VmaAllocation hAllocation,VkDeviceSize offset,VkDeviceSize size,VMA_CACHE_OPERATION op)17132 VkResult VmaAllocator_T::FlushOrInvalidateAllocation(
17133     VmaAllocation hAllocation,
17134     VkDeviceSize offset, VkDeviceSize size,
17135     VMA_CACHE_OPERATION op)
17136 {
17137     VkResult res = VK_SUCCESS;
17138 
17139     VkMappedMemoryRange memRange = {};
17140     if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange))
17141     {
17142         switch(op)
17143         {
17144         case VMA_CACHE_FLUSH:
17145             res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
17146             break;
17147         case VMA_CACHE_INVALIDATE:
17148             res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
17149             break;
17150         default:
17151             VMA_ASSERT(0);
17152         }
17153     }
17154     // else: Just ignore this call.
17155     return res;
17156 }
17157 
FlushOrInvalidateAllocations(uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes,VMA_CACHE_OPERATION op)17158 VkResult VmaAllocator_T::FlushOrInvalidateAllocations(
17159     uint32_t allocationCount,
17160     const VmaAllocation* allocations,
17161     const VkDeviceSize* offsets, const VkDeviceSize* sizes,
17162     VMA_CACHE_OPERATION op)
17163 {
17164     typedef VmaStlAllocator<VkMappedMemoryRange> RangeAllocator;
17165     typedef VmaSmallVector<VkMappedMemoryRange, RangeAllocator, 16> RangeVector;
17166     RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks()));
17167 
17168     for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
17169     {
17170         const VmaAllocation alloc = allocations[allocIndex];
17171         const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0;
17172         const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE;
17173         VkMappedMemoryRange newRange;
17174         if(GetFlushOrInvalidateRange(alloc, offset, size, newRange))
17175         {
17176             ranges.push_back(newRange);
17177         }
17178     }
17179 
17180     VkResult res = VK_SUCCESS;
17181     if(!ranges.empty())
17182     {
17183         switch(op)
17184         {
17185         case VMA_CACHE_FLUSH:
17186             res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17187             break;
17188         case VMA_CACHE_INVALIDATE:
17189             res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17190             break;
17191         default:
17192             VMA_ASSERT(0);
17193         }
17194     }
17195     // else: Just ignore this call.
17196     return res;
17197 }
17198 
FreeDedicatedMemory(const VmaAllocation allocation)17199 void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
17200 {
17201     VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
17202 
17203     const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17204     {
17205         VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17206         AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
17207         VMA_ASSERT(pDedicatedAllocations);
17208         bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
17209         VMA_ASSERT(success);
17210     }
17211 
17212     VkDeviceMemory hMemory = allocation->GetMemory();
17213 
17214     /*
17215     There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
17216     before vkFreeMemory.
17217 
17218     if(allocation->GetMappedData() != VMA_NULL)
17219     {
17220         (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
17221     }
17222     */
17223 
17224     FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
17225 
17226     VMA_DEBUG_LOG("    Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
17227 }
17228 
CalculateGpuDefragmentationMemoryTypeBits()17229 uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const
17230 {
17231     VkBufferCreateInfo dummyBufCreateInfo;
17232     VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo);
17233 
17234     uint32_t memoryTypeBits = 0;
17235 
17236     // Create buffer.
17237     VkBuffer buf = VK_NULL_HANDLE;
17238     VkResult res = (*GetVulkanFunctions().vkCreateBuffer)(
17239         m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf);
17240     if(res == VK_SUCCESS)
17241     {
17242         // Query for supported memory types.
17243         VkMemoryRequirements memReq;
17244         (*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq);
17245         memoryTypeBits = memReq.memoryTypeBits;
17246 
17247         // Destroy buffer.
17248         (*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks());
17249     }
17250 
17251     return memoryTypeBits;
17252 }
17253 
CalculateGlobalMemoryTypeBits()17254 uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const
17255 {
17256     // Make sure memory information is already fetched.
17257     VMA_ASSERT(GetMemoryTypeCount() > 0);
17258 
17259     uint32_t memoryTypeBits = UINT32_MAX;
17260 
17261     if(!m_UseAmdDeviceCoherentMemory)
17262     {
17263         // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD.
17264         for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17265         {
17266             if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
17267             {
17268                 memoryTypeBits &= ~(1u << memTypeIndex);
17269             }
17270         }
17271     }
17272 
17273     return memoryTypeBits;
17274 }
17275 
GetFlushOrInvalidateRange(VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size,VkMappedMemoryRange & outRange)17276 bool VmaAllocator_T::GetFlushOrInvalidateRange(
17277     VmaAllocation allocation,
17278     VkDeviceSize offset, VkDeviceSize size,
17279     VkMappedMemoryRange& outRange) const
17280 {
17281     const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17282     if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
17283     {
17284         const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
17285         const VkDeviceSize allocationSize = allocation->GetSize();
17286         VMA_ASSERT(offset <= allocationSize);
17287 
17288         outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
17289         outRange.pNext = VMA_NULL;
17290         outRange.memory = allocation->GetMemory();
17291 
17292         switch(allocation->GetType())
17293         {
17294         case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17295             outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17296             if(size == VK_WHOLE_SIZE)
17297             {
17298                 outRange.size = allocationSize - outRange.offset;
17299             }
17300             else
17301             {
17302                 VMA_ASSERT(offset + size <= allocationSize);
17303                 outRange.size = VMA_MIN(
17304                     VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize),
17305                     allocationSize - outRange.offset);
17306             }
17307             break;
17308         case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17309         {
17310             // 1. Still within this allocation.
17311             outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17312             if(size == VK_WHOLE_SIZE)
17313             {
17314                 size = allocationSize - offset;
17315             }
17316             else
17317             {
17318                 VMA_ASSERT(offset + size <= allocationSize);
17319             }
17320             outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize);
17321 
17322             // 2. Adjust to whole block.
17323             const VkDeviceSize allocationOffset = allocation->GetOffset();
17324             VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
17325             const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize();
17326             outRange.offset += allocationOffset;
17327             outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset);
17328 
17329             break;
17330         }
17331         default:
17332             VMA_ASSERT(0);
17333         }
17334         return true;
17335     }
17336     return false;
17337 }
17338 
17339 #if VMA_MEMORY_BUDGET
17340 
UpdateVulkanBudget()17341 void VmaAllocator_T::UpdateVulkanBudget()
17342 {
17343     VMA_ASSERT(m_UseExtMemoryBudget);
17344 
17345     VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR };
17346 
17347     VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };
17348     VmaPnextChainPushFront(&memProps, &budgetProps);
17349 
17350     GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps);
17351 
17352     {
17353         VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex);
17354 
17355         for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
17356         {
17357             m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex];
17358             m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex];
17359             m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load();
17360 
17361             // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size.
17362             if(m_Budget.m_VulkanBudget[heapIndex] == 0)
17363             {
17364                 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
17365             }
17366             else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size)
17367             {
17368                 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size;
17369             }
17370             if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0)
17371             {
17372                 m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
17373             }
17374         }
17375         m_Budget.m_OperationsSinceBudgetFetch = 0;
17376     }
17377 }
17378 
17379 #endif // #if VMA_MEMORY_BUDGET
17380 
FillAllocation(const VmaAllocation hAllocation,uint8_t pattern)17381 void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
17382 {
17383     if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
17384         !hAllocation->CanBecomeLost() &&
17385         (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
17386     {
17387         void* pData = VMA_NULL;
17388         VkResult res = Map(hAllocation, &pData);
17389         if(res == VK_SUCCESS)
17390         {
17391             memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
17392             FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
17393             Unmap(hAllocation);
17394         }
17395         else
17396         {
17397             VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
17398         }
17399     }
17400 }
17401 
GetGpuDefragmentationMemoryTypeBits()17402 uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits()
17403 {
17404     uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load();
17405     if(memoryTypeBits == UINT32_MAX)
17406     {
17407         memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits();
17408         m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits);
17409     }
17410     return memoryTypeBits;
17411 }
17412 
17413 #if VMA_STATS_STRING_ENABLED
17414 
PrintDetailedMap(VmaJsonWriter & json)17415 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
17416 {
17417     bool dedicatedAllocationsStarted = false;
17418     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17419     {
17420         VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17421         AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
17422         VMA_ASSERT(pDedicatedAllocVector);
17423         if(pDedicatedAllocVector->empty() == false)
17424         {
17425             if(dedicatedAllocationsStarted == false)
17426             {
17427                 dedicatedAllocationsStarted = true;
17428                 json.WriteString("DedicatedAllocations");
17429                 json.BeginObject();
17430             }
17431 
17432             json.BeginString("Type ");
17433             json.ContinueString(memTypeIndex);
17434             json.EndString();
17435 
17436             json.BeginArray();
17437 
17438             for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
17439             {
17440                 json.BeginObject(true);
17441                 const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
17442                 hAlloc->PrintParameters(json);
17443                 json.EndObject();
17444             }
17445 
17446             json.EndArray();
17447         }
17448     }
17449     if(dedicatedAllocationsStarted)
17450     {
17451         json.EndObject();
17452     }
17453 
17454     {
17455         bool allocationsStarted = false;
17456         for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17457         {
17458             if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
17459             {
17460                 if(allocationsStarted == false)
17461                 {
17462                     allocationsStarted = true;
17463                     json.WriteString("DefaultPools");
17464                     json.BeginObject();
17465                 }
17466 
17467                 json.BeginString("Type ");
17468                 json.ContinueString(memTypeIndex);
17469                 json.EndString();
17470 
17471                 m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
17472             }
17473         }
17474         if(allocationsStarted)
17475         {
17476             json.EndObject();
17477         }
17478     }
17479 
17480     // Custom pools
17481     {
17482         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
17483         const size_t poolCount = m_Pools.size();
17484         if(poolCount > 0)
17485         {
17486             json.WriteString("Pools");
17487             json.BeginObject();
17488             for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
17489             {
17490                 json.BeginString();
17491                 json.ContinueString(m_Pools[poolIndex]->GetId());
17492                 json.EndString();
17493 
17494                 m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
17495             }
17496             json.EndObject();
17497         }
17498     }
17499 }
17500 
17501 #endif // #if VMA_STATS_STRING_ENABLED
17502 
17503 ////////////////////////////////////////////////////////////////////////////////
17504 // Public interface
17505 
vmaCreateAllocator(const VmaAllocatorCreateInfo * pCreateInfo,VmaAllocator * pAllocator)17506 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
17507     const VmaAllocatorCreateInfo* pCreateInfo,
17508     VmaAllocator* pAllocator)
17509 {
17510     VMA_ASSERT(pCreateInfo && pAllocator);
17511     VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 ||
17512         (VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 2));
17513     VMA_DEBUG_LOG("vmaCreateAllocator");
17514     *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
17515     return (*pAllocator)->Init(pCreateInfo);
17516 }
17517 
vmaDestroyAllocator(VmaAllocator allocator)17518 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
17519     VmaAllocator allocator)
17520 {
17521     if(allocator != VK_NULL_HANDLE)
17522     {
17523         VMA_DEBUG_LOG("vmaDestroyAllocator");
17524         VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
17525         vma_delete(&allocationCallbacks, allocator);
17526     }
17527 }
17528 
vmaGetAllocatorInfo(VmaAllocator allocator,VmaAllocatorInfo * pAllocatorInfo)17529 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo)
17530 {
17531     VMA_ASSERT(allocator && pAllocatorInfo);
17532     pAllocatorInfo->instance = allocator->m_hInstance;
17533     pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice();
17534     pAllocatorInfo->device = allocator->m_hDevice;
17535 }
17536 
vmaGetPhysicalDeviceProperties(VmaAllocator allocator,const VkPhysicalDeviceProperties ** ppPhysicalDeviceProperties)17537 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
17538     VmaAllocator allocator,
17539     const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
17540 {
17541     VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
17542     *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
17543 }
17544 
vmaGetMemoryProperties(VmaAllocator allocator,const VkPhysicalDeviceMemoryProperties ** ppPhysicalDeviceMemoryProperties)17545 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
17546     VmaAllocator allocator,
17547     const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
17548 {
17549     VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
17550     *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
17551 }
17552 
vmaGetMemoryTypeProperties(VmaAllocator allocator,uint32_t memoryTypeIndex,VkMemoryPropertyFlags * pFlags)17553 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
17554     VmaAllocator allocator,
17555     uint32_t memoryTypeIndex,
17556     VkMemoryPropertyFlags* pFlags)
17557 {
17558     VMA_ASSERT(allocator && pFlags);
17559     VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
17560     *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
17561 }
17562 
vmaSetCurrentFrameIndex(VmaAllocator allocator,uint32_t frameIndex)17563 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
17564     VmaAllocator allocator,
17565     uint32_t frameIndex)
17566 {
17567     VMA_ASSERT(allocator);
17568     VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
17569 
17570     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17571 
17572     allocator->SetCurrentFrameIndex(frameIndex);
17573 }
17574 
vmaCalculateStats(VmaAllocator allocator,VmaStats * pStats)17575 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
17576     VmaAllocator allocator,
17577     VmaStats* pStats)
17578 {
17579     VMA_ASSERT(allocator && pStats);
17580     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17581     allocator->CalculateStats(pStats);
17582 }
17583 
vmaGetBudget(VmaAllocator allocator,VmaBudget * pBudget)17584 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
17585     VmaAllocator allocator,
17586     VmaBudget* pBudget)
17587 {
17588     VMA_ASSERT(allocator && pBudget);
17589     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17590     allocator->GetBudget(pBudget, 0, allocator->GetMemoryHeapCount());
17591 }
17592 
17593 #if VMA_STATS_STRING_ENABLED
17594 
vmaBuildStatsString(VmaAllocator allocator,char ** ppStatsString,VkBool32 detailedMap)17595 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
17596     VmaAllocator allocator,
17597     char** ppStatsString,
17598     VkBool32 detailedMap)
17599 {
17600     VMA_ASSERT(allocator && ppStatsString);
17601     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17602 
17603     VmaStringBuilder sb(allocator);
17604     {
17605         VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
17606         json.BeginObject();
17607 
17608         VmaBudget budget[VK_MAX_MEMORY_HEAPS];
17609         allocator->GetBudget(budget, 0, allocator->GetMemoryHeapCount());
17610 
17611         VmaStats stats;
17612         allocator->CalculateStats(&stats);
17613 
17614         json.WriteString("Total");
17615         VmaPrintStatInfo(json, stats.total);
17616 
17617         for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
17618         {
17619             json.BeginString("Heap ");
17620             json.ContinueString(heapIndex);
17621             json.EndString();
17622             json.BeginObject();
17623 
17624             json.WriteString("Size");
17625             json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
17626 
17627             json.WriteString("Flags");
17628             json.BeginArray(true);
17629             if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
17630             {
17631                 json.WriteString("DEVICE_LOCAL");
17632             }
17633             json.EndArray();
17634 
17635             json.WriteString("Budget");
17636             json.BeginObject();
17637             {
17638                 json.WriteString("BlockBytes");
17639                 json.WriteNumber(budget[heapIndex].blockBytes);
17640                 json.WriteString("AllocationBytes");
17641                 json.WriteNumber(budget[heapIndex].allocationBytes);
17642                 json.WriteString("Usage");
17643                 json.WriteNumber(budget[heapIndex].usage);
17644                 json.WriteString("Budget");
17645                 json.WriteNumber(budget[heapIndex].budget);
17646             }
17647             json.EndObject();
17648 
17649             if(stats.memoryHeap[heapIndex].blockCount > 0)
17650             {
17651                 json.WriteString("Stats");
17652                 VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
17653             }
17654 
17655             for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
17656             {
17657                 if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
17658                 {
17659                     json.BeginString("Type ");
17660                     json.ContinueString(typeIndex);
17661                     json.EndString();
17662 
17663                     json.BeginObject();
17664 
17665                     json.WriteString("Flags");
17666                     json.BeginArray(true);
17667                     VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
17668                     if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
17669                     {
17670                         json.WriteString("DEVICE_LOCAL");
17671                     }
17672                     if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
17673                     {
17674                         json.WriteString("HOST_VISIBLE");
17675                     }
17676                     if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
17677                     {
17678                         json.WriteString("HOST_COHERENT");
17679                     }
17680                     if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
17681                     {
17682                         json.WriteString("HOST_CACHED");
17683                     }
17684                     if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
17685                     {
17686                         json.WriteString("LAZILY_ALLOCATED");
17687                     }
17688                     if((flags & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0)
17689                     {
17690                         json.WriteString(" PROTECTED");
17691                     }
17692                     if((flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
17693                     {
17694                         json.WriteString(" DEVICE_COHERENT");
17695                     }
17696                     if((flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY) != 0)
17697                     {
17698                         json.WriteString(" DEVICE_UNCACHED");
17699                     }
17700                     json.EndArray();
17701 
17702                     if(stats.memoryType[typeIndex].blockCount > 0)
17703                     {
17704                         json.WriteString("Stats");
17705                         VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
17706                     }
17707 
17708                     json.EndObject();
17709                 }
17710             }
17711 
17712             json.EndObject();
17713         }
17714         if(detailedMap == VK_TRUE)
17715         {
17716             allocator->PrintDetailedMap(json);
17717         }
17718 
17719         json.EndObject();
17720     }
17721 
17722     const size_t len = sb.GetLength();
17723     char* const pChars = vma_new_array(allocator, char, len + 1);
17724     if(len > 0)
17725     {
17726         memcpy(pChars, sb.GetData(), len);
17727     }
17728     pChars[len] = '\0';
17729     *ppStatsString = pChars;
17730 }
17731 
vmaFreeStatsString(VmaAllocator allocator,char * pStatsString)17732 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
17733     VmaAllocator allocator,
17734     char* pStatsString)
17735 {
17736     if(pStatsString != VMA_NULL)
17737     {
17738         VMA_ASSERT(allocator);
17739         size_t len = strlen(pStatsString);
17740         vma_delete_array(allocator, pStatsString, len + 1);
17741     }
17742 }
17743 
17744 #endif // #if VMA_STATS_STRING_ENABLED
17745 
17746 /*
17747 This function is not protected by any mutex because it just reads immutable data.
17748 */
vmaFindMemoryTypeIndex(VmaAllocator allocator,uint32_t memoryTypeBits,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)17749 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
17750     VmaAllocator allocator,
17751     uint32_t memoryTypeBits,
17752     const VmaAllocationCreateInfo* pAllocationCreateInfo,
17753     uint32_t* pMemoryTypeIndex)
17754 {
17755     VMA_ASSERT(allocator != VK_NULL_HANDLE);
17756     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
17757     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
17758 
17759     memoryTypeBits &= allocator->GetGlobalMemoryTypeBits();
17760 
17761     if(pAllocationCreateInfo->memoryTypeBits != 0)
17762     {
17763         memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
17764     }
17765 
17766     uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
17767     uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
17768     uint32_t notPreferredFlags = 0;
17769 
17770     // Convert usage to requiredFlags and preferredFlags.
17771     switch(pAllocationCreateInfo->usage)
17772     {
17773     case VMA_MEMORY_USAGE_UNKNOWN:
17774         break;
17775     case VMA_MEMORY_USAGE_GPU_ONLY:
17776         if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
17777         {
17778             preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
17779         }
17780         break;
17781     case VMA_MEMORY_USAGE_CPU_ONLY:
17782         requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
17783         break;
17784     case VMA_MEMORY_USAGE_CPU_TO_GPU:
17785         requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
17786         if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
17787         {
17788             preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
17789         }
17790         break;
17791     case VMA_MEMORY_USAGE_GPU_TO_CPU:
17792         requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
17793         preferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
17794         break;
17795     case VMA_MEMORY_USAGE_CPU_COPY:
17796         notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
17797         break;
17798     case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED:
17799         requiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
17800         break;
17801     default:
17802         VMA_ASSERT(0);
17803         break;
17804     }
17805 
17806     // Avoid DEVICE_COHERENT unless explicitly requested.
17807     if(((pAllocationCreateInfo->requiredFlags | pAllocationCreateInfo->preferredFlags) &
17808         (VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0)
17809     {
17810         notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY;
17811     }
17812 
17813     *pMemoryTypeIndex = UINT32_MAX;
17814     uint32_t minCost = UINT32_MAX;
17815     for(uint32_t memTypeIndex = 0, memTypeBit = 1;
17816         memTypeIndex < allocator->GetMemoryTypeCount();
17817         ++memTypeIndex, memTypeBit <<= 1)
17818     {
17819         // This memory type is acceptable according to memoryTypeBits bitmask.
17820         if((memTypeBit & memoryTypeBits) != 0)
17821         {
17822             const VkMemoryPropertyFlags currFlags =
17823                 allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
17824             // This memory type contains requiredFlags.
17825             if((requiredFlags & ~currFlags) == 0)
17826             {
17827                 // Calculate cost as number of bits from preferredFlags not present in this memory type.
17828                 uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags) +
17829                     VmaCountBitsSet(currFlags & notPreferredFlags);
17830                 // Remember memory type with lowest cost.
17831                 if(currCost < minCost)
17832                 {
17833                     *pMemoryTypeIndex = memTypeIndex;
17834                     if(currCost == 0)
17835                     {
17836                         return VK_SUCCESS;
17837                     }
17838                     minCost = currCost;
17839                 }
17840             }
17841         }
17842     }
17843     return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
17844 }
17845 
vmaFindMemoryTypeIndexForBufferInfo(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)17846 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
17847     VmaAllocator allocator,
17848     const VkBufferCreateInfo* pBufferCreateInfo,
17849     const VmaAllocationCreateInfo* pAllocationCreateInfo,
17850     uint32_t* pMemoryTypeIndex)
17851 {
17852     VMA_ASSERT(allocator != VK_NULL_HANDLE);
17853     VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
17854     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
17855     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
17856 
17857     const VkDevice hDev = allocator->m_hDevice;
17858     VkBuffer hBuffer = VK_NULL_HANDLE;
17859     VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
17860         hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
17861     if(res == VK_SUCCESS)
17862     {
17863         VkMemoryRequirements memReq = {};
17864         allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
17865             hDev, hBuffer, &memReq);
17866 
17867         res = vmaFindMemoryTypeIndex(
17868             allocator,
17869             memReq.memoryTypeBits,
17870             pAllocationCreateInfo,
17871             pMemoryTypeIndex);
17872 
17873         allocator->GetVulkanFunctions().vkDestroyBuffer(
17874             hDev, hBuffer, allocator->GetAllocationCallbacks());
17875     }
17876     return res;
17877 }
17878 
vmaFindMemoryTypeIndexForImageInfo(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)17879 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
17880     VmaAllocator allocator,
17881     const VkImageCreateInfo* pImageCreateInfo,
17882     const VmaAllocationCreateInfo* pAllocationCreateInfo,
17883     uint32_t* pMemoryTypeIndex)
17884 {
17885     VMA_ASSERT(allocator != VK_NULL_HANDLE);
17886     VMA_ASSERT(pImageCreateInfo != VMA_NULL);
17887     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
17888     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
17889 
17890     const VkDevice hDev = allocator->m_hDevice;
17891     VkImage hImage = VK_NULL_HANDLE;
17892     VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
17893         hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
17894     if(res == VK_SUCCESS)
17895     {
17896         VkMemoryRequirements memReq = {};
17897         allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
17898             hDev, hImage, &memReq);
17899 
17900         res = vmaFindMemoryTypeIndex(
17901             allocator,
17902             memReq.memoryTypeBits,
17903             pAllocationCreateInfo,
17904             pMemoryTypeIndex);
17905 
17906         allocator->GetVulkanFunctions().vkDestroyImage(
17907             hDev, hImage, allocator->GetAllocationCallbacks());
17908     }
17909     return res;
17910 }
17911 
vmaCreatePool(VmaAllocator allocator,const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)17912 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
17913     VmaAllocator allocator,
17914     const VmaPoolCreateInfo* pCreateInfo,
17915     VmaPool* pPool)
17916 {
17917     VMA_ASSERT(allocator && pCreateInfo && pPool);
17918 
17919     VMA_DEBUG_LOG("vmaCreatePool");
17920 
17921     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17922 
17923     VkResult res = allocator->CreatePool(pCreateInfo, pPool);
17924 
17925 #if VMA_RECORDING_ENABLED
17926     if(allocator->GetRecorder() != VMA_NULL)
17927     {
17928         allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
17929     }
17930 #endif
17931 
17932     return res;
17933 }
17934 
vmaDestroyPool(VmaAllocator allocator,VmaPool pool)17935 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
17936     VmaAllocator allocator,
17937     VmaPool pool)
17938 {
17939     VMA_ASSERT(allocator);
17940 
17941     if(pool == VK_NULL_HANDLE)
17942     {
17943         return;
17944     }
17945 
17946     VMA_DEBUG_LOG("vmaDestroyPool");
17947 
17948     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17949 
17950 #if VMA_RECORDING_ENABLED
17951     if(allocator->GetRecorder() != VMA_NULL)
17952     {
17953         allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
17954     }
17955 #endif
17956 
17957     allocator->DestroyPool(pool);
17958 }
17959 
vmaGetPoolStats(VmaAllocator allocator,VmaPool pool,VmaPoolStats * pPoolStats)17960 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
17961     VmaAllocator allocator,
17962     VmaPool pool,
17963     VmaPoolStats* pPoolStats)
17964 {
17965     VMA_ASSERT(allocator && pool && pPoolStats);
17966 
17967     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17968 
17969     allocator->GetPoolStats(pool, pPoolStats);
17970 }
17971 
vmaMakePoolAllocationsLost(VmaAllocator allocator,VmaPool pool,size_t * pLostAllocationCount)17972 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
17973     VmaAllocator allocator,
17974     VmaPool pool,
17975     size_t* pLostAllocationCount)
17976 {
17977     VMA_ASSERT(allocator && pool);
17978 
17979     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17980 
17981 #if VMA_RECORDING_ENABLED
17982     if(allocator->GetRecorder() != VMA_NULL)
17983     {
17984         allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
17985     }
17986 #endif
17987 
17988     allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
17989 }
17990 
vmaCheckPoolCorruption(VmaAllocator allocator,VmaPool pool)17991 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
17992 {
17993     VMA_ASSERT(allocator && pool);
17994 
17995     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17996 
17997     VMA_DEBUG_LOG("vmaCheckPoolCorruption");
17998 
17999     return allocator->CheckPoolCorruption(pool);
18000 }
18001 
vmaGetPoolName(VmaAllocator allocator,VmaPool pool,const char ** ppName)18002 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
18003     VmaAllocator allocator,
18004     VmaPool pool,
18005     const char** ppName)
18006 {
18007     VMA_ASSERT(allocator && pool && ppName);
18008 
18009     VMA_DEBUG_LOG("vmaGetPoolName");
18010 
18011     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18012 
18013     *ppName = pool->GetName();
18014 }
18015 
vmaSetPoolName(VmaAllocator allocator,VmaPool pool,const char * pName)18016 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
18017     VmaAllocator allocator,
18018     VmaPool pool,
18019     const char* pName)
18020 {
18021     VMA_ASSERT(allocator && pool);
18022 
18023     VMA_DEBUG_LOG("vmaSetPoolName");
18024 
18025     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18026 
18027     pool->SetName(pName);
18028 
18029 #if VMA_RECORDING_ENABLED
18030     if(allocator->GetRecorder() != VMA_NULL)
18031     {
18032         allocator->GetRecorder()->RecordSetPoolName(allocator->GetCurrentFrameIndex(), pool, pName);
18033     }
18034 #endif
18035 }
18036 
vmaAllocateMemory(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18037 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
18038     VmaAllocator allocator,
18039     const VkMemoryRequirements* pVkMemoryRequirements,
18040     const VmaAllocationCreateInfo* pCreateInfo,
18041     VmaAllocation* pAllocation,
18042     VmaAllocationInfo* pAllocationInfo)
18043 {
18044     VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
18045 
18046     VMA_DEBUG_LOG("vmaAllocateMemory");
18047 
18048     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18049 
18050     VkResult result = allocator->AllocateMemory(
18051         *pVkMemoryRequirements,
18052         false, // requiresDedicatedAllocation
18053         false, // prefersDedicatedAllocation
18054         VK_NULL_HANDLE, // dedicatedBuffer
18055         UINT32_MAX, // dedicatedBufferUsage
18056         VK_NULL_HANDLE, // dedicatedImage
18057         *pCreateInfo,
18058         VMA_SUBALLOCATION_TYPE_UNKNOWN,
18059         1, // allocationCount
18060         pAllocation);
18061 
18062 #if VMA_RECORDING_ENABLED
18063     if(allocator->GetRecorder() != VMA_NULL)
18064     {
18065         allocator->GetRecorder()->RecordAllocateMemory(
18066             allocator->GetCurrentFrameIndex(),
18067             *pVkMemoryRequirements,
18068             *pCreateInfo,
18069             *pAllocation);
18070     }
18071 #endif
18072 
18073     if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18074     {
18075         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18076     }
18077 
18078     return result;
18079 }
18080 
vmaAllocateMemoryPages(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,size_t allocationCount,VmaAllocation * pAllocations,VmaAllocationInfo * pAllocationInfo)18081 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
18082     VmaAllocator allocator,
18083     const VkMemoryRequirements* pVkMemoryRequirements,
18084     const VmaAllocationCreateInfo* pCreateInfo,
18085     size_t allocationCount,
18086     VmaAllocation* pAllocations,
18087     VmaAllocationInfo* pAllocationInfo)
18088 {
18089     if(allocationCount == 0)
18090     {
18091         return VK_SUCCESS;
18092     }
18093 
18094     VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
18095 
18096     VMA_DEBUG_LOG("vmaAllocateMemoryPages");
18097 
18098     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18099 
18100     VkResult result = allocator->AllocateMemory(
18101         *pVkMemoryRequirements,
18102         false, // requiresDedicatedAllocation
18103         false, // prefersDedicatedAllocation
18104         VK_NULL_HANDLE, // dedicatedBuffer
18105         UINT32_MAX, // dedicatedBufferUsage
18106         VK_NULL_HANDLE, // dedicatedImage
18107         *pCreateInfo,
18108         VMA_SUBALLOCATION_TYPE_UNKNOWN,
18109         allocationCount,
18110         pAllocations);
18111 
18112 #if VMA_RECORDING_ENABLED
18113     if(allocator->GetRecorder() != VMA_NULL)
18114     {
18115         allocator->GetRecorder()->RecordAllocateMemoryPages(
18116             allocator->GetCurrentFrameIndex(),
18117             *pVkMemoryRequirements,
18118             *pCreateInfo,
18119             (uint64_t)allocationCount,
18120             pAllocations);
18121     }
18122 #endif
18123 
18124     if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18125     {
18126         for(size_t i = 0; i < allocationCount; ++i)
18127         {
18128             allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
18129         }
18130     }
18131 
18132     return result;
18133 }
18134 
vmaAllocateMemoryForBuffer(VmaAllocator allocator,VkBuffer buffer,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18135 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
18136     VmaAllocator allocator,
18137     VkBuffer buffer,
18138     const VmaAllocationCreateInfo* pCreateInfo,
18139     VmaAllocation* pAllocation,
18140     VmaAllocationInfo* pAllocationInfo)
18141 {
18142     VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18143 
18144     VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
18145 
18146     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18147 
18148     VkMemoryRequirements vkMemReq = {};
18149     bool requiresDedicatedAllocation = false;
18150     bool prefersDedicatedAllocation = false;
18151     allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
18152         requiresDedicatedAllocation,
18153         prefersDedicatedAllocation);
18154 
18155     VkResult result = allocator->AllocateMemory(
18156         vkMemReq,
18157         requiresDedicatedAllocation,
18158         prefersDedicatedAllocation,
18159         buffer, // dedicatedBuffer
18160         UINT32_MAX, // dedicatedBufferUsage
18161         VK_NULL_HANDLE, // dedicatedImage
18162         *pCreateInfo,
18163         VMA_SUBALLOCATION_TYPE_BUFFER,
18164         1, // allocationCount
18165         pAllocation);
18166 
18167 #if VMA_RECORDING_ENABLED
18168     if(allocator->GetRecorder() != VMA_NULL)
18169     {
18170         allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
18171             allocator->GetCurrentFrameIndex(),
18172             vkMemReq,
18173             requiresDedicatedAllocation,
18174             prefersDedicatedAllocation,
18175             *pCreateInfo,
18176             *pAllocation);
18177     }
18178 #endif
18179 
18180     if(pAllocationInfo && result == VK_SUCCESS)
18181     {
18182         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18183     }
18184 
18185     return result;
18186 }
18187 
vmaAllocateMemoryForImage(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18188 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
18189     VmaAllocator allocator,
18190     VkImage image,
18191     const VmaAllocationCreateInfo* pCreateInfo,
18192     VmaAllocation* pAllocation,
18193     VmaAllocationInfo* pAllocationInfo)
18194 {
18195     VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18196 
18197     VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
18198 
18199     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18200 
18201     VkMemoryRequirements vkMemReq = {};
18202     bool requiresDedicatedAllocation = false;
18203     bool prefersDedicatedAllocation  = false;
18204     allocator->GetImageMemoryRequirements(image, vkMemReq,
18205         requiresDedicatedAllocation, prefersDedicatedAllocation);
18206 
18207     VkResult result = allocator->AllocateMemory(
18208         vkMemReq,
18209         requiresDedicatedAllocation,
18210         prefersDedicatedAllocation,
18211         VK_NULL_HANDLE, // dedicatedBuffer
18212         UINT32_MAX, // dedicatedBufferUsage
18213         image, // dedicatedImage
18214         *pCreateInfo,
18215         VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
18216         1, // allocationCount
18217         pAllocation);
18218 
18219 #if VMA_RECORDING_ENABLED
18220     if(allocator->GetRecorder() != VMA_NULL)
18221     {
18222         allocator->GetRecorder()->RecordAllocateMemoryForImage(
18223             allocator->GetCurrentFrameIndex(),
18224             vkMemReq,
18225             requiresDedicatedAllocation,
18226             prefersDedicatedAllocation,
18227             *pCreateInfo,
18228             *pAllocation);
18229     }
18230 #endif
18231 
18232     if(pAllocationInfo && result == VK_SUCCESS)
18233     {
18234         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18235     }
18236 
18237     return result;
18238 }
18239 
vmaFreeMemory(VmaAllocator allocator,VmaAllocation allocation)18240 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
18241     VmaAllocator allocator,
18242     VmaAllocation allocation)
18243 {
18244     VMA_ASSERT(allocator);
18245 
18246     if(allocation == VK_NULL_HANDLE)
18247     {
18248         return;
18249     }
18250 
18251     VMA_DEBUG_LOG("vmaFreeMemory");
18252 
18253     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18254 
18255 #if VMA_RECORDING_ENABLED
18256     if(allocator->GetRecorder() != VMA_NULL)
18257     {
18258         allocator->GetRecorder()->RecordFreeMemory(
18259             allocator->GetCurrentFrameIndex(),
18260             allocation);
18261     }
18262 #endif
18263 
18264     allocator->FreeMemory(
18265         1, // allocationCount
18266         &allocation);
18267 }
18268 
vmaFreeMemoryPages(VmaAllocator allocator,size_t allocationCount,const VmaAllocation * pAllocations)18269 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
18270     VmaAllocator allocator,
18271     size_t allocationCount,
18272     const VmaAllocation* pAllocations)
18273 {
18274     if(allocationCount == 0)
18275     {
18276         return;
18277     }
18278 
18279     VMA_ASSERT(allocator);
18280 
18281     VMA_DEBUG_LOG("vmaFreeMemoryPages");
18282 
18283     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18284 
18285 #if VMA_RECORDING_ENABLED
18286     if(allocator->GetRecorder() != VMA_NULL)
18287     {
18288         allocator->GetRecorder()->RecordFreeMemoryPages(
18289             allocator->GetCurrentFrameIndex(),
18290             (uint64_t)allocationCount,
18291             pAllocations);
18292     }
18293 #endif
18294 
18295     allocator->FreeMemory(allocationCount, pAllocations);
18296 }
18297 
vmaResizeAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize newSize)18298 VMA_CALL_PRE VkResult VMA_CALL_POST vmaResizeAllocation(
18299     VmaAllocator allocator,
18300     VmaAllocation allocation,
18301     VkDeviceSize newSize)
18302 {
18303     VMA_ASSERT(allocator && allocation);
18304 
18305     VMA_DEBUG_LOG("vmaResizeAllocation");
18306 
18307     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18308 
18309     return allocator->ResizeAllocation(allocation, newSize);
18310 }
18311 
vmaGetAllocationInfo(VmaAllocator allocator,VmaAllocation allocation,VmaAllocationInfo * pAllocationInfo)18312 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
18313     VmaAllocator allocator,
18314     VmaAllocation allocation,
18315     VmaAllocationInfo* pAllocationInfo)
18316 {
18317     VMA_ASSERT(allocator && allocation && pAllocationInfo);
18318 
18319     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18320 
18321 #if VMA_RECORDING_ENABLED
18322     if(allocator->GetRecorder() != VMA_NULL)
18323     {
18324         allocator->GetRecorder()->RecordGetAllocationInfo(
18325             allocator->GetCurrentFrameIndex(),
18326             allocation);
18327     }
18328 #endif
18329 
18330     allocator->GetAllocationInfo(allocation, pAllocationInfo);
18331 }
18332 
vmaTouchAllocation(VmaAllocator allocator,VmaAllocation allocation)18333 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
18334     VmaAllocator allocator,
18335     VmaAllocation allocation)
18336 {
18337     VMA_ASSERT(allocator && allocation);
18338 
18339     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18340 
18341 #if VMA_RECORDING_ENABLED
18342     if(allocator->GetRecorder() != VMA_NULL)
18343     {
18344         allocator->GetRecorder()->RecordTouchAllocation(
18345             allocator->GetCurrentFrameIndex(),
18346             allocation);
18347     }
18348 #endif
18349 
18350     return allocator->TouchAllocation(allocation);
18351 }
18352 
vmaSetAllocationUserData(VmaAllocator allocator,VmaAllocation allocation,void * pUserData)18353 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
18354     VmaAllocator allocator,
18355     VmaAllocation allocation,
18356     void* pUserData)
18357 {
18358     VMA_ASSERT(allocator && allocation);
18359 
18360     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18361 
18362     allocation->SetUserData(allocator, pUserData);
18363 
18364 #if VMA_RECORDING_ENABLED
18365     if(allocator->GetRecorder() != VMA_NULL)
18366     {
18367         allocator->GetRecorder()->RecordSetAllocationUserData(
18368             allocator->GetCurrentFrameIndex(),
18369             allocation,
18370             pUserData);
18371     }
18372 #endif
18373 }
18374 
vmaCreateLostAllocation(VmaAllocator allocator,VmaAllocation * pAllocation)18375 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
18376     VmaAllocator allocator,
18377     VmaAllocation* pAllocation)
18378 {
18379     VMA_ASSERT(allocator && pAllocation);
18380 
18381     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18382 
18383     allocator->CreateLostAllocation(pAllocation);
18384 
18385 #if VMA_RECORDING_ENABLED
18386     if(allocator->GetRecorder() != VMA_NULL)
18387     {
18388         allocator->GetRecorder()->RecordCreateLostAllocation(
18389             allocator->GetCurrentFrameIndex(),
18390             *pAllocation);
18391     }
18392 #endif
18393 }
18394 
vmaMapMemory(VmaAllocator allocator,VmaAllocation allocation,void ** ppData)18395 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
18396     VmaAllocator allocator,
18397     VmaAllocation allocation,
18398     void** ppData)
18399 {
18400     VMA_ASSERT(allocator && allocation && ppData);
18401 
18402     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18403 
18404     VkResult res = allocator->Map(allocation, ppData);
18405 
18406 #if VMA_RECORDING_ENABLED
18407     if(allocator->GetRecorder() != VMA_NULL)
18408     {
18409         allocator->GetRecorder()->RecordMapMemory(
18410             allocator->GetCurrentFrameIndex(),
18411             allocation);
18412     }
18413 #endif
18414 
18415     return res;
18416 }
18417 
vmaUnmapMemory(VmaAllocator allocator,VmaAllocation allocation)18418 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
18419     VmaAllocator allocator,
18420     VmaAllocation allocation)
18421 {
18422     VMA_ASSERT(allocator && allocation);
18423 
18424     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18425 
18426 #if VMA_RECORDING_ENABLED
18427     if(allocator->GetRecorder() != VMA_NULL)
18428     {
18429         allocator->GetRecorder()->RecordUnmapMemory(
18430             allocator->GetCurrentFrameIndex(),
18431             allocation);
18432     }
18433 #endif
18434 
18435     allocator->Unmap(allocation);
18436 }
18437 
vmaFlushAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)18438 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18439 {
18440     VMA_ASSERT(allocator && allocation);
18441 
18442     VMA_DEBUG_LOG("vmaFlushAllocation");
18443 
18444     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18445 
18446     const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
18447 
18448 #if VMA_RECORDING_ENABLED
18449     if(allocator->GetRecorder() != VMA_NULL)
18450     {
18451         allocator->GetRecorder()->RecordFlushAllocation(
18452             allocator->GetCurrentFrameIndex(),
18453             allocation, offset, size);
18454     }
18455 #endif
18456 
18457     return res;
18458 }
18459 
vmaInvalidateAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)18460 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18461 {
18462     VMA_ASSERT(allocator && allocation);
18463 
18464     VMA_DEBUG_LOG("vmaInvalidateAllocation");
18465 
18466     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18467 
18468     const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
18469 
18470 #if VMA_RECORDING_ENABLED
18471     if(allocator->GetRecorder() != VMA_NULL)
18472     {
18473         allocator->GetRecorder()->RecordInvalidateAllocation(
18474             allocator->GetCurrentFrameIndex(),
18475             allocation, offset, size);
18476     }
18477 #endif
18478 
18479     return res;
18480 }
18481 
vmaFlushAllocations(VmaAllocator allocator,uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes)18482 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
18483     VmaAllocator allocator,
18484     uint32_t allocationCount,
18485     const VmaAllocation* allocations,
18486     const VkDeviceSize* offsets,
18487     const VkDeviceSize* sizes)
18488 {
18489     VMA_ASSERT(allocator);
18490 
18491     if(allocationCount == 0)
18492     {
18493         return VK_SUCCESS;
18494     }
18495 
18496     VMA_ASSERT(allocations);
18497 
18498     VMA_DEBUG_LOG("vmaFlushAllocations");
18499 
18500     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18501 
18502     const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH);
18503 
18504 #if VMA_RECORDING_ENABLED
18505     if(allocator->GetRecorder() != VMA_NULL)
18506     {
18507         //TODO
18508     }
18509 #endif
18510 
18511     return res;
18512 }
18513 
vmaInvalidateAllocations(VmaAllocator allocator,uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes)18514 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
18515     VmaAllocator allocator,
18516     uint32_t allocationCount,
18517     const VmaAllocation* allocations,
18518     const VkDeviceSize* offsets,
18519     const VkDeviceSize* sizes)
18520 {
18521     VMA_ASSERT(allocator);
18522 
18523     if(allocationCount == 0)
18524     {
18525         return VK_SUCCESS;
18526     }
18527 
18528     VMA_ASSERT(allocations);
18529 
18530     VMA_DEBUG_LOG("vmaInvalidateAllocations");
18531 
18532     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18533 
18534     const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE);
18535 
18536 #if VMA_RECORDING_ENABLED
18537     if(allocator->GetRecorder() != VMA_NULL)
18538     {
18539         //TODO
18540     }
18541 #endif
18542 
18543     return res;
18544 }
18545 
vmaCheckCorruption(VmaAllocator allocator,uint32_t memoryTypeBits)18546 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
18547 {
18548     VMA_ASSERT(allocator);
18549 
18550     VMA_DEBUG_LOG("vmaCheckCorruption");
18551 
18552     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18553 
18554     return allocator->CheckCorruption(memoryTypeBits);
18555 }
18556 
vmaDefragment(VmaAllocator allocator,const VmaAllocation * pAllocations,size_t allocationCount,VkBool32 * pAllocationsChanged,const VmaDefragmentationInfo * pDefragmentationInfo,VmaDefragmentationStats * pDefragmentationStats)18557 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
18558     VmaAllocator allocator,
18559     const VmaAllocation* pAllocations,
18560     size_t allocationCount,
18561     VkBool32* pAllocationsChanged,
18562     const VmaDefragmentationInfo *pDefragmentationInfo,
18563     VmaDefragmentationStats* pDefragmentationStats)
18564 {
18565     // Deprecated interface, reimplemented using new one.
18566 
18567     VmaDefragmentationInfo2 info2 = {};
18568     info2.allocationCount = (uint32_t)allocationCount;
18569     info2.pAllocations = pAllocations;
18570     info2.pAllocationsChanged = pAllocationsChanged;
18571     if(pDefragmentationInfo != VMA_NULL)
18572     {
18573         info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
18574         info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
18575     }
18576     else
18577     {
18578         info2.maxCpuAllocationsToMove = UINT32_MAX;
18579         info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
18580     }
18581     // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
18582 
18583     VmaDefragmentationContext ctx;
18584     VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
18585     if(res == VK_NOT_READY)
18586     {
18587         res = vmaDefragmentationEnd( allocator, ctx);
18588     }
18589     return res;
18590 }
18591 
vmaDefragmentationBegin(VmaAllocator allocator,const VmaDefragmentationInfo2 * pInfo,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)18592 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
18593     VmaAllocator allocator,
18594     const VmaDefragmentationInfo2* pInfo,
18595     VmaDefragmentationStats* pStats,
18596     VmaDefragmentationContext *pContext)
18597 {
18598     VMA_ASSERT(allocator && pInfo && pContext);
18599 
18600     // Degenerate case: Nothing to defragment.
18601     if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
18602     {
18603         return VK_SUCCESS;
18604     }
18605 
18606     VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
18607     VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
18608     VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
18609     VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
18610 
18611     VMA_DEBUG_LOG("vmaDefragmentationBegin");
18612 
18613     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18614 
18615     VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
18616 
18617 #if VMA_RECORDING_ENABLED
18618     if(allocator->GetRecorder() != VMA_NULL)
18619     {
18620         allocator->GetRecorder()->RecordDefragmentationBegin(
18621             allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
18622     }
18623 #endif
18624 
18625     return res;
18626 }
18627 
vmaDefragmentationEnd(VmaAllocator allocator,VmaDefragmentationContext context)18628 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
18629     VmaAllocator allocator,
18630     VmaDefragmentationContext context)
18631 {
18632     VMA_ASSERT(allocator);
18633 
18634     VMA_DEBUG_LOG("vmaDefragmentationEnd");
18635 
18636     if(context != VK_NULL_HANDLE)
18637     {
18638         VMA_DEBUG_GLOBAL_MUTEX_LOCK
18639 
18640 #if VMA_RECORDING_ENABLED
18641         if(allocator->GetRecorder() != VMA_NULL)
18642         {
18643             allocator->GetRecorder()->RecordDefragmentationEnd(
18644                 allocator->GetCurrentFrameIndex(), context);
18645         }
18646 #endif
18647 
18648         return allocator->DefragmentationEnd(context);
18649     }
18650     else
18651     {
18652         return VK_SUCCESS;
18653     }
18654 }
18655 
vmaBeginDefragmentationPass(VmaAllocator allocator,VmaDefragmentationContext context,VmaDefragmentationPassInfo * pInfo)18656 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
18657     VmaAllocator allocator,
18658     VmaDefragmentationContext context,
18659     VmaDefragmentationPassInfo* pInfo
18660     )
18661 {
18662     VMA_ASSERT(allocator);
18663     VMA_ASSERT(pInfo);
18664     VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->moveCount, pInfo->pMoves));
18665 
18666     VMA_DEBUG_LOG("vmaBeginDefragmentationPass");
18667 
18668     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18669 
18670     if(context == VK_NULL_HANDLE)
18671     {
18672         pInfo->moveCount = 0;
18673         return VK_SUCCESS;
18674     }
18675 
18676     return allocator->DefragmentationPassBegin(pInfo, context);
18677 }
vmaEndDefragmentationPass(VmaAllocator allocator,VmaDefragmentationContext context)18678 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
18679     VmaAllocator allocator,
18680     VmaDefragmentationContext context)
18681 {
18682     VMA_ASSERT(allocator);
18683 
18684     VMA_DEBUG_LOG("vmaEndDefragmentationPass");
18685     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18686 
18687     if(context == VK_NULL_HANDLE)
18688         return VK_SUCCESS;
18689 
18690     return allocator->DefragmentationPassEnd(context);
18691 }
18692 
vmaBindBufferMemory(VmaAllocator allocator,VmaAllocation allocation,VkBuffer buffer)18693 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
18694     VmaAllocator allocator,
18695     VmaAllocation allocation,
18696     VkBuffer buffer)
18697 {
18698     VMA_ASSERT(allocator && allocation && buffer);
18699 
18700     VMA_DEBUG_LOG("vmaBindBufferMemory");
18701 
18702     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18703 
18704     return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL);
18705 }
18706 
vmaBindBufferMemory2(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize allocationLocalOffset,VkBuffer buffer,const void * pNext)18707 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
18708     VmaAllocator allocator,
18709     VmaAllocation allocation,
18710     VkDeviceSize allocationLocalOffset,
18711     VkBuffer buffer,
18712     const void* pNext)
18713 {
18714     VMA_ASSERT(allocator && allocation && buffer);
18715 
18716     VMA_DEBUG_LOG("vmaBindBufferMemory2");
18717 
18718     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18719 
18720     return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext);
18721 }
18722 
vmaBindImageMemory(VmaAllocator allocator,VmaAllocation allocation,VkImage image)18723 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
18724     VmaAllocator allocator,
18725     VmaAllocation allocation,
18726     VkImage image)
18727 {
18728     VMA_ASSERT(allocator && allocation && image);
18729 
18730     VMA_DEBUG_LOG("vmaBindImageMemory");
18731 
18732     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18733 
18734     return allocator->BindImageMemory(allocation, 0, image, VMA_NULL);
18735 }
18736 
vmaBindImageMemory2(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize allocationLocalOffset,VkImage image,const void * pNext)18737 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
18738     VmaAllocator allocator,
18739     VmaAllocation allocation,
18740     VkDeviceSize allocationLocalOffset,
18741     VkImage image,
18742     const void* pNext)
18743 {
18744     VMA_ASSERT(allocator && allocation && image);
18745 
18746     VMA_DEBUG_LOG("vmaBindImageMemory2");
18747 
18748     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18749 
18750         return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext);
18751 }
18752 
vmaCreateBuffer(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkBuffer * pBuffer,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18753 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
18754     VmaAllocator allocator,
18755     const VkBufferCreateInfo* pBufferCreateInfo,
18756     const VmaAllocationCreateInfo* pAllocationCreateInfo,
18757     VkBuffer* pBuffer,
18758     VmaAllocation* pAllocation,
18759     VmaAllocationInfo* pAllocationInfo)
18760 {
18761     VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
18762 
18763     if(pBufferCreateInfo->size == 0)
18764     {
18765         return VK_ERROR_VALIDATION_FAILED_EXT;
18766     }
18767     if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
18768         !allocator->m_UseKhrBufferDeviceAddress)
18769     {
18770         VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used.");
18771         return VK_ERROR_VALIDATION_FAILED_EXT;
18772     }
18773 
18774     VMA_DEBUG_LOG("vmaCreateBuffer");
18775 
18776     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18777 
18778     *pBuffer = VK_NULL_HANDLE;
18779     *pAllocation = VK_NULL_HANDLE;
18780 
18781     // 1. Create VkBuffer.
18782     VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
18783         allocator->m_hDevice,
18784         pBufferCreateInfo,
18785         allocator->GetAllocationCallbacks(),
18786         pBuffer);
18787     if(res >= 0)
18788     {
18789         // 2. vkGetBufferMemoryRequirements.
18790         VkMemoryRequirements vkMemReq = {};
18791         bool requiresDedicatedAllocation = false;
18792         bool prefersDedicatedAllocation  = false;
18793         allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
18794             requiresDedicatedAllocation, prefersDedicatedAllocation);
18795 
18796         // 3. Allocate memory using allocator.
18797         res = allocator->AllocateMemory(
18798             vkMemReq,
18799             requiresDedicatedAllocation,
18800             prefersDedicatedAllocation,
18801             *pBuffer, // dedicatedBuffer
18802             pBufferCreateInfo->usage, // dedicatedBufferUsage
18803             VK_NULL_HANDLE, // dedicatedImage
18804             *pAllocationCreateInfo,
18805             VMA_SUBALLOCATION_TYPE_BUFFER,
18806             1, // allocationCount
18807             pAllocation);
18808 
18809 #if VMA_RECORDING_ENABLED
18810         if(allocator->GetRecorder() != VMA_NULL)
18811         {
18812             allocator->GetRecorder()->RecordCreateBuffer(
18813                 allocator->GetCurrentFrameIndex(),
18814                 *pBufferCreateInfo,
18815                 *pAllocationCreateInfo,
18816                 *pAllocation);
18817         }
18818 #endif
18819 
18820         if(res >= 0)
18821         {
18822             // 3. Bind buffer with memory.
18823             if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
18824             {
18825                 res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
18826             }
18827             if(res >= 0)
18828             {
18829                 // All steps succeeded.
18830                 #if VMA_STATS_STRING_ENABLED
18831                     (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
18832                 #endif
18833                 if(pAllocationInfo != VMA_NULL)
18834                 {
18835                     allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18836                 }
18837 
18838                 return VK_SUCCESS;
18839             }
18840             allocator->FreeMemory(
18841                 1, // allocationCount
18842                 pAllocation);
18843             *pAllocation = VK_NULL_HANDLE;
18844             (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
18845             *pBuffer = VK_NULL_HANDLE;
18846             return res;
18847         }
18848         (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
18849         *pBuffer = VK_NULL_HANDLE;
18850         return res;
18851     }
18852     return res;
18853 }
18854 
vmaDestroyBuffer(VmaAllocator allocator,VkBuffer buffer,VmaAllocation allocation)18855 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
18856     VmaAllocator allocator,
18857     VkBuffer buffer,
18858     VmaAllocation allocation)
18859 {
18860     VMA_ASSERT(allocator);
18861 
18862     if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
18863     {
18864         return;
18865     }
18866 
18867     VMA_DEBUG_LOG("vmaDestroyBuffer");
18868 
18869     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18870 
18871 #if VMA_RECORDING_ENABLED
18872     if(allocator->GetRecorder() != VMA_NULL)
18873     {
18874         allocator->GetRecorder()->RecordDestroyBuffer(
18875             allocator->GetCurrentFrameIndex(),
18876             allocation);
18877     }
18878 #endif
18879 
18880     if(buffer != VK_NULL_HANDLE)
18881     {
18882         (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
18883     }
18884 
18885     if(allocation != VK_NULL_HANDLE)
18886     {
18887         allocator->FreeMemory(
18888             1, // allocationCount
18889             &allocation);
18890     }
18891 }
18892 
vmaCreateImage(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkImage * pImage,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18893 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
18894     VmaAllocator allocator,
18895     const VkImageCreateInfo* pImageCreateInfo,
18896     const VmaAllocationCreateInfo* pAllocationCreateInfo,
18897     VkImage* pImage,
18898     VmaAllocation* pAllocation,
18899     VmaAllocationInfo* pAllocationInfo)
18900 {
18901     VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
18902 
18903     if(pImageCreateInfo->extent.width == 0 ||
18904         pImageCreateInfo->extent.height == 0 ||
18905         pImageCreateInfo->extent.depth == 0 ||
18906         pImageCreateInfo->mipLevels == 0 ||
18907         pImageCreateInfo->arrayLayers == 0)
18908     {
18909         return VK_ERROR_VALIDATION_FAILED_EXT;
18910     }
18911 
18912     VMA_DEBUG_LOG("vmaCreateImage");
18913 
18914     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18915 
18916     *pImage = VK_NULL_HANDLE;
18917     *pAllocation = VK_NULL_HANDLE;
18918 
18919     // 1. Create VkImage.
18920     VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
18921         allocator->m_hDevice,
18922         pImageCreateInfo,
18923         allocator->GetAllocationCallbacks(),
18924         pImage);
18925     if(res >= 0)
18926     {
18927         VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
18928             VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
18929             VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
18930 
18931         // 2. Allocate memory using allocator.
18932         VkMemoryRequirements vkMemReq = {};
18933         bool requiresDedicatedAllocation = false;
18934         bool prefersDedicatedAllocation  = false;
18935         allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
18936             requiresDedicatedAllocation, prefersDedicatedAllocation);
18937 
18938         res = allocator->AllocateMemory(
18939             vkMemReq,
18940             requiresDedicatedAllocation,
18941             prefersDedicatedAllocation,
18942             VK_NULL_HANDLE, // dedicatedBuffer
18943             UINT32_MAX, // dedicatedBufferUsage
18944             *pImage, // dedicatedImage
18945             *pAllocationCreateInfo,
18946             suballocType,
18947             1, // allocationCount
18948             pAllocation);
18949 
18950 #if VMA_RECORDING_ENABLED
18951         if(allocator->GetRecorder() != VMA_NULL)
18952         {
18953             allocator->GetRecorder()->RecordCreateImage(
18954                 allocator->GetCurrentFrameIndex(),
18955                 *pImageCreateInfo,
18956                 *pAllocationCreateInfo,
18957                 *pAllocation);
18958         }
18959 #endif
18960 
18961         if(res >= 0)
18962         {
18963             // 3. Bind image with memory.
18964             if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
18965             {
18966                 res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);
18967             }
18968             if(res >= 0)
18969             {
18970                 // All steps succeeded.
18971                 #if VMA_STATS_STRING_ENABLED
18972                     (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
18973                 #endif
18974                 if(pAllocationInfo != VMA_NULL)
18975                 {
18976                     allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18977                 }
18978 
18979                 return VK_SUCCESS;
18980             }
18981             allocator->FreeMemory(
18982                 1, // allocationCount
18983                 pAllocation);
18984             *pAllocation = VK_NULL_HANDLE;
18985             (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
18986             *pImage = VK_NULL_HANDLE;
18987             return res;
18988         }
18989         (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
18990         *pImage = VK_NULL_HANDLE;
18991         return res;
18992     }
18993     return res;
18994 }
18995 
vmaDestroyImage(VmaAllocator allocator,VkImage image,VmaAllocation allocation)18996 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
18997     VmaAllocator allocator,
18998     VkImage image,
18999     VmaAllocation allocation)
19000 {
19001     VMA_ASSERT(allocator);
19002 
19003     if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
19004     {
19005         return;
19006     }
19007 
19008     VMA_DEBUG_LOG("vmaDestroyImage");
19009 
19010     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19011 
19012 #if VMA_RECORDING_ENABLED
19013     if(allocator->GetRecorder() != VMA_NULL)
19014     {
19015         allocator->GetRecorder()->RecordDestroyImage(
19016             allocator->GetCurrentFrameIndex(),
19017             allocation);
19018     }
19019 #endif
19020 
19021     if(image != VK_NULL_HANDLE)
19022     {
19023         (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
19024     }
19025     if(allocation != VK_NULL_HANDLE)
19026     {
19027         allocator->FreeMemory(
19028             1, // allocationCount
19029             &allocation);
19030     }
19031 }
19032 
19033 #endif // #ifdef VMA_IMPLEMENTATION
19034