• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     size_t maxBlockCount = SIZE_MAX;
2358 } VmaAllocatorCreateInfo;
2359 
2360 /// Creates Allocator object.
2361 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
2362     const VmaAllocatorCreateInfo* VMA_NOT_NULL pCreateInfo,
2363     VmaAllocator VMA_NULLABLE * VMA_NOT_NULL pAllocator);
2364 
2365 /// Destroys allocator object.
2366 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
2367     VmaAllocator VMA_NULLABLE allocator);
2368 
2369 /** \brief Information about existing #VmaAllocator object.
2370 */
2371 typedef struct VmaAllocatorInfo
2372 {
2373     /** \brief Handle to Vulkan instance object.
2374 
2375     This is the same value as has been passed through VmaAllocatorCreateInfo::instance.
2376     */
2377     VkInstance VMA_NOT_NULL instance;
2378     /** \brief Handle to Vulkan physical device object.
2379 
2380     This is the same value as has been passed through VmaAllocatorCreateInfo::physicalDevice.
2381     */
2382     VkPhysicalDevice VMA_NOT_NULL physicalDevice;
2383     /** \brief Handle to Vulkan device object.
2384 
2385     This is the same value as has been passed through VmaAllocatorCreateInfo::device.
2386     */
2387     VkDevice VMA_NOT_NULL device;
2388 } VmaAllocatorInfo;
2389 
2390 /** \brief Returns information about existing #VmaAllocator object - handle to Vulkan device etc.
2391 
2392 It might be useful if you want to keep just the #VmaAllocator handle and fetch other required handles to
2393 `VkPhysicalDevice`, `VkDevice` etc. every time using this function.
2394 */
2395 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator VMA_NOT_NULL allocator, VmaAllocatorInfo* VMA_NOT_NULL pAllocatorInfo);
2396 
2397 /**
2398 PhysicalDeviceProperties are fetched from physicalDevice by the allocator.
2399 You can access it here, without fetching it again on your own.
2400 */
2401 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
2402     VmaAllocator VMA_NOT_NULL allocator,
2403     const VkPhysicalDeviceProperties* VMA_NULLABLE * VMA_NOT_NULL ppPhysicalDeviceProperties);
2404 
2405 /**
2406 PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator.
2407 You can access it here, without fetching it again on your own.
2408 */
2409 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
2410     VmaAllocator VMA_NOT_NULL allocator,
2411     const VkPhysicalDeviceMemoryProperties* VMA_NULLABLE * VMA_NOT_NULL ppPhysicalDeviceMemoryProperties);
2412 
2413 /**
2414 \brief Given Memory Type Index, returns Property Flags of this memory type.
2415 
2416 This is just a convenience function. Same information can be obtained using
2417 vmaGetMemoryProperties().
2418 */
2419 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
2420     VmaAllocator VMA_NOT_NULL allocator,
2421     uint32_t memoryTypeIndex,
2422     VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);
2423 
2424 /** \brief Sets index of the current frame.
2425 
2426 This function must be used if you make allocations with
2427 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT and
2428 #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flags to inform the allocator
2429 when a new frame begins. Allocations queried using vmaGetAllocationInfo() cannot
2430 become lost in the current frame.
2431 */
2432 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
2433     VmaAllocator VMA_NOT_NULL allocator,
2434     uint32_t frameIndex);
2435 
2436 /** \brief Calculated statistics of memory usage in entire allocator.
2437 */
2438 typedef struct VmaStatInfo
2439 {
2440     /// Number of `VkDeviceMemory` Vulkan memory blocks allocated.
2441     uint32_t blockCount;
2442     /// Number of #VmaAllocation allocation objects allocated.
2443     uint32_t allocationCount;
2444     /// Number of free ranges of memory between allocations.
2445     uint32_t unusedRangeCount;
2446     /// Total number of bytes occupied by all allocations.
2447     VkDeviceSize usedBytes;
2448     /// Total number of bytes occupied by unused ranges.
2449     VkDeviceSize unusedBytes;
2450     VkDeviceSize allocationSizeMin, allocationSizeAvg, allocationSizeMax;
2451     VkDeviceSize unusedRangeSizeMin, unusedRangeSizeAvg, unusedRangeSizeMax;
2452 } VmaStatInfo;
2453 
2454 /// General statistics from current state of Allocator.
2455 typedef struct VmaStats
2456 {
2457     VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
2458     VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
2459     VmaStatInfo total;
2460 } VmaStats;
2461 
2462 /** \brief Retrieves statistics from current state of the Allocator.
2463 
2464 This function is called "calculate" not "get" because it has to traverse all
2465 internal data structures, so it may be quite slow. For faster but more brief statistics
2466 suitable to be called every frame or every allocation, use vmaGetBudget().
2467 
2468 Note that when using allocator from multiple threads, returned information may immediately
2469 become outdated.
2470 */
2471 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
2472     VmaAllocator VMA_NOT_NULL allocator,
2473     VmaStats* VMA_NOT_NULL pStats);
2474 
2475 /** \brief Free empty block of the Allocator.
2476 */
2477 VMA_CALL_PRE void VMA_CALL_POST vmaFreeEmptyBlock(
2478     VmaAllocator VMA_NOT_NULL allocator);
2479 
2480 /** \brief Statistics of current memory usage and available budget, in bytes, for specific memory heap.
2481 */
2482 typedef struct VmaBudget
2483 {
2484     /** \brief Sum size of all `VkDeviceMemory` blocks allocated from particular heap, in bytes.
2485     */
2486     VkDeviceSize blockBytes;
2487 
2488     /** \brief Sum size of all allocations created in particular heap, in bytes.
2489 
2490     Usually less or equal than `blockBytes`.
2491     Difference `blockBytes - allocationBytes` is the amount of memory allocated but unused -
2492     available for new allocations or wasted due to fragmentation.
2493 
2494     It might be greater than `blockBytes` if there are some allocations in lost state, as they account
2495     to this value as well.
2496     */
2497     VkDeviceSize allocationBytes;
2498 
2499     /** \brief Estimated current memory usage of the program, in bytes.
2500 
2501     Fetched from system using `VK_EXT_memory_budget` extension if enabled.
2502 
2503     It might be different than `blockBytes` (usually higher) due to additional implicit objects
2504     also occupying the memory, like swapchain, pipelines, descriptor heaps, command buffers, or
2505     `VkDeviceMemory` blocks allocated outside of this library, if any.
2506     */
2507     VkDeviceSize usage;
2508 
2509     /** \brief Estimated amount of memory available to the program, in bytes.
2510 
2511     Fetched from system using `VK_EXT_memory_budget` extension if enabled.
2512 
2513     It might be different (most probably smaller) than `VkMemoryHeap::size[heapIndex]` due to factors
2514     external to the program, like other programs also consuming system resources.
2515     Difference `budget - usage` is the amount of additional memory that can probably
2516     be allocated without problems. Exceeding the budget may result in various problems.
2517     */
2518     VkDeviceSize budget;
2519 } VmaBudget;
2520 
2521 /** \brief Retrieves information about current memory budget for all memory heaps.
2522 
2523 \param[out] pBudget Must point to array with number of elements at least equal to number of memory heaps in physical device used.
2524 
2525 This function is called "get" not "calculate" because it is very fast, suitable to be called
2526 every frame or every allocation. For more detailed statistics use vmaCalculateStats().
2527 
2528 Note that when using allocator from multiple threads, returned information may immediately
2529 become outdated.
2530 */
2531 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
2532     VmaAllocator VMA_NOT_NULL allocator,
2533     VmaBudget* VMA_NOT_NULL pBudget);
2534 
2535 #ifndef VMA_STATS_STRING_ENABLED
2536 #define VMA_STATS_STRING_ENABLED 1
2537 #endif
2538 
2539 #if VMA_STATS_STRING_ENABLED
2540 
2541 /// Builds and returns statistics as string in JSON format.
2542 /** @param[out] ppStatsString Must be freed using vmaFreeStatsString() function.
2543 */
2544 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
2545     VmaAllocator VMA_NOT_NULL allocator,
2546     char* VMA_NULLABLE * VMA_NOT_NULL ppStatsString,
2547     VkBool32 detailedMap);
2548 
2549 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
2550     VmaAllocator VMA_NOT_NULL allocator,
2551     char* VMA_NULLABLE pStatsString);
2552 
2553 #endif // #if VMA_STATS_STRING_ENABLED
2554 
2555 /** \struct VmaPool
2556 \brief Represents custom memory pool
2557 
2558 Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it.
2559 Call function vmaDestroyPool() to destroy it.
2560 
2561 For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools).
2562 */
2563 VK_DEFINE_HANDLE(VmaPool)
2564 
2565 typedef enum VmaMemoryUsage
2566 {
2567     /** No intended memory usage specified.
2568     Use other members of VmaAllocationCreateInfo to specify your requirements.
2569     */
2570     VMA_MEMORY_USAGE_UNKNOWN = 0,
2571     /** Memory will be used on device only, so fast access from the device is preferred.
2572     It usually means device-local GPU (video) memory.
2573     No need to be mappable on host.
2574     It is roughly equivalent of `D3D12_HEAP_TYPE_DEFAULT`.
2575 
2576     Usage:
2577 
2578     - Resources written and read by device, e.g. images used as attachments.
2579     - Resources transferred from host once (immutable) or infrequently and read by
2580       device multiple times, e.g. textures to be sampled, vertex buffers, uniform
2581       (constant) buffers, and majority of other types of resources used on GPU.
2582 
2583     Allocation may still end up in `HOST_VISIBLE` memory on some implementations.
2584     In such case, you are free to map it.
2585     You can use #VMA_ALLOCATION_CREATE_MAPPED_BIT with this usage type.
2586     */
2587     VMA_MEMORY_USAGE_GPU_ONLY = 1,
2588     /** Memory will be mappable on host.
2589     It usually means CPU (system) memory.
2590     Guarantees to be `HOST_VISIBLE` and `HOST_COHERENT`.
2591     CPU access is typically uncached. Writes may be write-combined.
2592     Resources created in this pool may still be accessible to the device, but access to them can be slow.
2593     It is roughly equivalent of `D3D12_HEAP_TYPE_UPLOAD`.
2594 
2595     Usage: Staging copy of resources used as transfer source.
2596     */
2597     VMA_MEMORY_USAGE_CPU_ONLY = 2,
2598     /**
2599     Memory that is both mappable on host (guarantees to be `HOST_VISIBLE`) and preferably fast to access by GPU.
2600     CPU access is typically uncached. Writes may be write-combined.
2601 
2602     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.
2603     */
2604     VMA_MEMORY_USAGE_CPU_TO_GPU = 3,
2605     /** Memory mappable on host (guarantees to be `HOST_VISIBLE`) and cached.
2606     It is roughly equivalent of `D3D12_HEAP_TYPE_READBACK`.
2607 
2608     Usage:
2609 
2610     - Resources written by device, read by host - results of some computations, e.g. screen capture, average scene luminance for HDR tone mapping.
2611     - 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.
2612     */
2613     VMA_MEMORY_USAGE_GPU_TO_CPU = 4,
2614     /** CPU memory - memory that is preferably not `DEVICE_LOCAL`, but also not guaranteed to be `HOST_VISIBLE`.
2615 
2616     Usage: Staging copy of resources moved from GPU memory to CPU memory as part
2617     of custom paging/residency mechanism, to be moved back to GPU memory when needed.
2618     */
2619     VMA_MEMORY_USAGE_CPU_COPY = 5,
2620     /** Lazily allocated GPU memory having `VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT`.
2621     Exists mostly on mobile platforms. Using it on desktop PC or other GPUs with no such memory type present will fail the allocation.
2622 
2623     Usage: Memory for transient attachment images (color attachments, depth attachments etc.), created with `VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT`.
2624 
2625     Allocations with this usage are always created as dedicated - it implies #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
2626     */
2627     VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED = 6,
2628 
2629     VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
2630 } VmaMemoryUsage;
2631 
2632 /// Flags to be passed as VmaAllocationCreateInfo::flags.
2633 typedef enum VmaAllocationCreateFlagBits {
2634     /** \brief Set this flag if the allocation should have its own memory block.
2635 
2636     Use it for special, big resources, like fullscreen images used as attachments.
2637 
2638     You should not use this flag if VmaAllocationCreateInfo::pool is not null.
2639     */
2640     VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001,
2641 
2642     /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block.
2643 
2644     If new allocation cannot be placed in any of the existing blocks, allocation
2645     fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
2646 
2647     You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and
2648     #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense.
2649 
2650     If VmaAllocationCreateInfo::pool is not null, this flag is implied and ignored. */
2651     VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002,
2652     /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it.
2653 
2654     Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData.
2655 
2656     Is it valid to use this flag for allocation made from memory type that is not
2657     `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is
2658     useful if you need an allocation that is efficient to use on GPU
2659     (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that
2660     support it (e.g. Intel GPU).
2661 
2662     You should not use this flag together with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT.
2663     */
2664     VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004,
2665     /** Allocation created with this flag can become lost as a result of another
2666     allocation with #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag, so you
2667     must check it before use.
2668 
2669     To check if allocation is not lost, call vmaGetAllocationInfo() and check if
2670     VmaAllocationInfo::deviceMemory is not `VK_NULL_HANDLE`.
2671 
2672     For details about supporting lost allocations, see Lost Allocations
2673     chapter of User Guide on Main Page.
2674 
2675     You should not use this flag together with #VMA_ALLOCATION_CREATE_MAPPED_BIT.
2676     */
2677     VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT = 0x00000008,
2678     /** While creating allocation using this flag, other allocations that were
2679     created with flag #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT can become lost.
2680 
2681     For details about supporting lost allocations, see Lost Allocations
2682     chapter of User Guide on Main Page.
2683     */
2684     VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT = 0x00000010,
2685     /** Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a
2686     null-terminated string. Instead of copying pointer value, a local copy of the
2687     string is made and stored in allocation's `pUserData`. The string is automatically
2688     freed together with the allocation. It is also used in vmaBuildStatsString().
2689     */
2690     VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020,
2691     /** Allocation will be created from upper stack in a double stack pool.
2692 
2693     This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag.
2694     */
2695     VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040,
2696     /** Create both buffer/image and allocation, but don't bind them together.
2697     It is useful when you want to bind yourself to do some more advanced binding, e.g. using some extensions.
2698     The flag is meaningful only with functions that bind by default: vmaCreateBuffer(), vmaCreateImage().
2699     Otherwise it is ignored.
2700     */
2701     VMA_ALLOCATION_CREATE_DONT_BIND_BIT = 0x00000080,
2702     /** Create allocation only if additional device memory required for it, if any, won't exceed
2703     memory budget. Otherwise return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
2704     */
2705     VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT = 0x00000100,
2706 
2707     /** Allocation strategy that chooses smallest possible free range for the
2708     allocation.
2709     */
2710     VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT  = 0x00010000,
2711     /** Allocation strategy that chooses biggest possible free range for the
2712     allocation.
2713     */
2714     VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT = 0x00020000,
2715     /** Allocation strategy that chooses first suitable free range for the
2716     allocation.
2717 
2718     "First" doesn't necessarily means the one with smallest offset in memory,
2719     but rather the one that is easiest and fastest to find.
2720     */
2721     VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = 0x00040000,
2722 
2723     /** Allocation strategy that tries to minimize memory usage.
2724     */
2725     VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT,
2726     /** Allocation strategy that tries to minimize allocation time.
2727     */
2728     VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
2729     /** Allocation strategy that tries to minimize memory fragmentation.
2730     */
2731     VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT,
2732 
2733     /** A bit mask to extract only `STRATEGY` bits from entire set of flags.
2734     */
2735     VMA_ALLOCATION_CREATE_STRATEGY_MASK =
2736         VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT |
2737         VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT |
2738         VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
2739 
2740     VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2741 } VmaAllocationCreateFlagBits;
2742 typedef VkFlags VmaAllocationCreateFlags;
2743 
2744 typedef struct VmaAllocationCreateInfo
2745 {
2746     /// Use #VmaAllocationCreateFlagBits enum.
2747     VmaAllocationCreateFlags flags;
2748     /** \brief Intended usage of memory.
2749 
2750     You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n
2751     If `pool` is not null, this member is ignored.
2752     */
2753     VmaMemoryUsage usage;
2754     /** \brief Flags that must be set in a Memory Type chosen for an allocation.
2755 
2756     Leave 0 if you specify memory requirements in other way. \n
2757     If `pool` is not null, this member is ignored.*/
2758     VkMemoryPropertyFlags requiredFlags;
2759     /** \brief Flags that preferably should be set in a memory type chosen for an allocation.
2760 
2761     Set to 0 if no additional flags are prefered. \n
2762     If `pool` is not null, this member is ignored. */
2763     VkMemoryPropertyFlags preferredFlags;
2764     /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation.
2765 
2766     Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if
2767     it meets other requirements specified by this structure, with no further
2768     restrictions on memory type index. \n
2769     If `pool` is not null, this member is ignored.
2770     */
2771     uint32_t memoryTypeBits;
2772     /** \brief Pool that this allocation should be created in.
2773 
2774     Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members:
2775     `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.
2776     */
2777     VmaPool VMA_NULLABLE pool;
2778     /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().
2779 
2780     If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either
2781     null or pointer to a null-terminated string. The string will be then copied to
2782     internal buffer, so it doesn't need to be valid after allocation call.
2783     */
2784     void* VMA_NULLABLE pUserData;
2785 } VmaAllocationCreateInfo;
2786 
2787 /**
2788 \brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.
2789 
2790 This algorithm tries to find a memory type that:
2791 
2792 - Is allowed by memoryTypeBits.
2793 - Contains all the flags from pAllocationCreateInfo->requiredFlags.
2794 - Matches intended usage.
2795 - Has as many flags from pAllocationCreateInfo->preferredFlags as possible.
2796 
2797 \return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result
2798 from this function or any other allocating function probably means that your
2799 device doesn't support any memory type with requested features for the specific
2800 type of resource you want to use it for. Please check parameters of your
2801 resource, like image layout (OPTIMAL versus LINEAR) or mip level count.
2802 */
2803 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
2804     VmaAllocator VMA_NOT_NULL allocator,
2805     uint32_t memoryTypeBits,
2806     const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2807     uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2808 
2809 /**
2810 \brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.
2811 
2812 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
2813 It internally creates a temporary, dummy buffer that never has memory bound.
2814 It is just a convenience function, equivalent to calling:
2815 
2816 - `vkCreateBuffer`
2817 - `vkGetBufferMemoryRequirements`
2818 - `vmaFindMemoryTypeIndex`
2819 - `vkDestroyBuffer`
2820 */
2821 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
2822     VmaAllocator VMA_NOT_NULL allocator,
2823     const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
2824     const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2825     uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2826 
2827 /**
2828 \brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.
2829 
2830 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
2831 It internally creates a temporary, dummy image that never has memory bound.
2832 It is just a convenience function, equivalent to calling:
2833 
2834 - `vkCreateImage`
2835 - `vkGetImageMemoryRequirements`
2836 - `vmaFindMemoryTypeIndex`
2837 - `vkDestroyImage`
2838 */
2839 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
2840     VmaAllocator VMA_NOT_NULL allocator,
2841     const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
2842     const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2843     uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2844 
2845 /// Flags to be passed as VmaPoolCreateInfo::flags.
2846 typedef enum VmaPoolCreateFlagBits {
2847     /** \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.
2848 
2849     This is an optional optimization flag.
2850 
2851     If you always allocate using vmaCreateBuffer(), vmaCreateImage(),
2852     vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator
2853     knows exact type of your allocations so it can handle Buffer-Image Granularity
2854     in the optimal way.
2855 
2856     If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(),
2857     exact type of such allocations is not known, so allocator must be conservative
2858     in handling Buffer-Image Granularity, which can lead to suboptimal allocation
2859     (wasted memory). In that case, if you can make sure you always allocate only
2860     buffers and linear images or only optimal images out of this pool, use this flag
2861     to make allocator disregard Buffer-Image Granularity and so make allocations
2862     faster and more optimal.
2863     */
2864     VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002,
2865 
2866     /** \brief Enables alternative, linear allocation algorithm in this pool.
2867 
2868     Specify this flag to enable linear allocation algorithm, which always creates
2869     new allocations after last one and doesn't reuse space from allocations freed in
2870     between. It trades memory consumption for simplified algorithm and data
2871     structure, which has better performance and uses less memory for metadata.
2872 
2873     By using this flag, you can achieve behavior of free-at-once, stack,
2874     ring buffer, and double stack. For details, see documentation chapter
2875     \ref linear_algorithm.
2876 
2877     When using this flag, you must specify VmaPoolCreateInfo::maxBlockCount == 1 (or 0 for default).
2878 
2879     For more details, see [Linear allocation algorithm](@ref linear_algorithm).
2880     */
2881     VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004,
2882 
2883     /** \brief Enables alternative, buddy allocation algorithm in this pool.
2884 
2885     It operates on a tree of blocks, each having size that is a power of two and
2886     a half of its parent's size. Comparing to default algorithm, this one provides
2887     faster allocation and deallocation and decreased external fragmentation,
2888     at the expense of more memory wasted (internal fragmentation).
2889 
2890     For more details, see [Buddy allocation algorithm](@ref buddy_algorithm).
2891     */
2892     VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT = 0x00000008,
2893 
2894     /** Bit mask to extract only `ALGORITHM` bits from entire set of flags.
2895     */
2896     VMA_POOL_CREATE_ALGORITHM_MASK =
2897         VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT |
2898         VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT,
2899 
2900     VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2901 } VmaPoolCreateFlagBits;
2902 typedef VkFlags VmaPoolCreateFlags;
2903 
2904 /** \brief Describes parameter of created #VmaPool.
2905 */
2906 typedef struct VmaPoolCreateInfo {
2907     /** \brief Vulkan memory type index to allocate this pool from.
2908     */
2909     uint32_t memoryTypeIndex;
2910     /** \brief Use combination of #VmaPoolCreateFlagBits.
2911     */
2912     VmaPoolCreateFlags flags;
2913     /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. Optional.
2914 
2915     Specify nonzero to set explicit, constant size of memory blocks used by this
2916     pool.
2917 
2918     Leave 0 to use default and let the library manage block sizes automatically.
2919     Sizes of particular blocks may vary.
2920     */
2921     VkDeviceSize blockSize;
2922     /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty.
2923 
2924     Set to 0 to have no preallocated blocks and allow the pool be completely empty.
2925     */
2926     size_t minBlockCount;
2927     /** \brief Maximum number of blocks that can be allocated in this pool. Optional.
2928 
2929     Set to 0 to use default, which is `SIZE_MAX`, which means no limit.
2930 
2931     Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated
2932     throughout whole lifetime of this pool.
2933     */
2934     size_t maxBlockCount;
2935     /** \brief Maximum number of additional frames that are in use at the same time as current frame.
2936 
2937     This value is used only when you make allocations with
2938     #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
2939     lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
2940 
2941     For example, if you double-buffer your command buffers, so resources used for
2942     rendering in previous frame may still be in use by the GPU at the moment you
2943     allocate resources needed for the current frame, set this value to 1.
2944 
2945     If you want to allow any allocations other than used in the current frame to
2946     become lost, set this value to 0.
2947     */
2948     uint32_t frameInUseCount;
2949 } VmaPoolCreateInfo;
2950 
2951 /** \brief Describes parameter of existing #VmaPool.
2952 */
2953 typedef struct VmaPoolStats {
2954     /** \brief Total amount of `VkDeviceMemory` allocated from Vulkan for this pool, in bytes.
2955     */
2956     VkDeviceSize size;
2957     /** \brief Total number of bytes in the pool not used by any #VmaAllocation.
2958     */
2959     VkDeviceSize unusedSize;
2960     /** \brief Number of #VmaAllocation objects created from this pool that were not destroyed or lost.
2961     */
2962     size_t allocationCount;
2963     /** \brief Number of continuous memory ranges in the pool not used by any #VmaAllocation.
2964     */
2965     size_t unusedRangeCount;
2966     /** \brief Size of the largest continuous free memory region available for new allocation.
2967 
2968     Making a new allocation of that size is not guaranteed to succeed because of
2969     possible additional margin required to respect alignment and buffer/image
2970     granularity.
2971     */
2972     VkDeviceSize unusedRangeSizeMax;
2973     /** \brief Number of `VkDeviceMemory` blocks allocated for this pool.
2974     */
2975     size_t blockCount;
2976 } VmaPoolStats;
2977 
2978 /** \brief Allocates Vulkan device memory and creates #VmaPool object.
2979 
2980 @param allocator Allocator object.
2981 @param pCreateInfo Parameters of pool to create.
2982 @param[out] pPool Handle to created pool.
2983 */
2984 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
2985     VmaAllocator VMA_NOT_NULL allocator,
2986     const VmaPoolCreateInfo* VMA_NOT_NULL pCreateInfo,
2987     VmaPool VMA_NULLABLE * VMA_NOT_NULL pPool);
2988 
2989 /** \brief Destroys #VmaPool object and frees Vulkan device memory.
2990 */
2991 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
2992     VmaAllocator VMA_NOT_NULL allocator,
2993     VmaPool VMA_NULLABLE pool);
2994 
2995 /** \brief Retrieves statistics of existing #VmaPool object.
2996 
2997 @param allocator Allocator object.
2998 @param pool Pool object.
2999 @param[out] pPoolStats Statistics of specified pool.
3000 */
3001 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
3002     VmaAllocator VMA_NOT_NULL allocator,
3003     VmaPool VMA_NOT_NULL pool,
3004     VmaPoolStats* VMA_NOT_NULL pPoolStats);
3005 
3006 /** \brief Marks all allocations in given pool as lost if they are not used in current frame or VmaPoolCreateInfo::frameInUseCount back from now.
3007 
3008 @param allocator Allocator object.
3009 @param pool Pool.
3010 @param[out] pLostAllocationCount Number of allocations marked as lost. Optional - pass null if you don't need this information.
3011 */
3012 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
3013     VmaAllocator VMA_NOT_NULL allocator,
3014     VmaPool VMA_NOT_NULL pool,
3015     size_t* VMA_NULLABLE pLostAllocationCount);
3016 
3017 /** \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions.
3018 
3019 Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
3020 `VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is
3021 `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
3022 
3023 Possible return values:
3024 
3025 - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool.
3026 - `VK_SUCCESS` - corruption detection has been performed and succeeded.
3027 - `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
3028   `VMA_ASSERT` is also fired in that case.
3029 - Other value: Error returned by Vulkan, e.g. memory mapping failure.
3030 */
3031 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator VMA_NOT_NULL allocator, VmaPool VMA_NOT_NULL pool);
3032 
3033 /** \brief Retrieves name of a custom pool.
3034 
3035 After the call `ppName` is either null or points to an internally-owned null-terminated string
3036 containing name of the pool that was previously set. The pointer becomes invalid when the pool is
3037 destroyed or its name is changed using vmaSetPoolName().
3038 */
3039 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
3040     VmaAllocator VMA_NOT_NULL allocator,
3041     VmaPool VMA_NOT_NULL pool,
3042     const char* VMA_NULLABLE * VMA_NOT_NULL ppName);
3043 
3044 /** \brief Sets name of a custom pool.
3045 
3046 `pName` can be either null or pointer to a null-terminated string with new name for the pool.
3047 Function makes internal copy of the string, so it can be changed or freed immediately after this call.
3048 */
3049 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
3050     VmaAllocator VMA_NOT_NULL allocator,
3051     VmaPool VMA_NOT_NULL pool,
3052     const char* VMA_NULLABLE pName);
3053 
3054 /** \struct VmaAllocation
3055 \brief Represents single memory allocation.
3056 
3057 It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type
3058 plus unique offset.
3059 
3060 There are multiple ways to create such object.
3061 You need to fill structure VmaAllocationCreateInfo.
3062 For more information see [Choosing memory type](@ref choosing_memory_type).
3063 
3064 Although the library provides convenience functions that create Vulkan buffer or image,
3065 allocate memory for it and bind them together,
3066 binding of the allocation to a buffer or an image is out of scope of the allocation itself.
3067 Allocation object can exist without buffer/image bound,
3068 binding can be done manually by the user, and destruction of it can be done
3069 independently of destruction of the allocation.
3070 
3071 The object also remembers its size and some other information.
3072 To retrieve this information, use function vmaGetAllocationInfo() and inspect
3073 returned structure VmaAllocationInfo.
3074 
3075 Some kinds allocations can be in lost state.
3076 For more information, see [Lost allocations](@ref lost_allocations).
3077 */
3078 VK_DEFINE_HANDLE(VmaAllocation)
3079 
3080 /** \brief Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
3081 */
3082 typedef struct VmaAllocationInfo {
3083     /** \brief Memory type index that this allocation was allocated from.
3084 
3085     It never changes.
3086     */
3087     uint32_t memoryType;
3088     /** \brief Handle to Vulkan memory object.
3089 
3090     Same memory object can be shared by multiple allocations.
3091 
3092     It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
3093 
3094     If the allocation is lost, it is equal to `VK_NULL_HANDLE`.
3095     */
3096     VkDeviceMemory VMA_NULLABLE_NON_DISPATCHABLE deviceMemory;
3097     /** \brief Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation.
3098 
3099     It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
3100     */
3101     VkDeviceSize offset;
3102     /** \brief Size of this allocation, in bytes.
3103 
3104     It never changes, unless allocation is lost.
3105     */
3106     VkDeviceSize size;
3107     /** \brief Pointer to the beginning of this allocation as mapped data.
3108 
3109     If the allocation hasn't been mapped using vmaMapMemory() and hasn't been
3110     created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value is null.
3111 
3112     It can change after call to vmaMapMemory(), vmaUnmapMemory().
3113     It can also change after call to vmaDefragment() if this allocation is passed to the function.
3114     */
3115     void* VMA_NULLABLE pMappedData;
3116     /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData().
3117 
3118     It can change after call to vmaSetAllocationUserData() for this allocation.
3119     */
3120     void* VMA_NULLABLE pUserData;
3121 } VmaAllocationInfo;
3122 
3123 /** \brief General purpose memory allocation.
3124 
3125 @param[out] pAllocation Handle to allocated memory.
3126 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
3127 
3128 You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
3129 
3130 It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(),
3131 vmaCreateBuffer(), vmaCreateImage() instead whenever possible.
3132 */
3133 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
3134     VmaAllocator VMA_NOT_NULL allocator,
3135     const VkMemoryRequirements* VMA_NOT_NULL pVkMemoryRequirements,
3136     const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3137     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3138     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3139 
3140 /** \brief General purpose memory allocation for multiple allocation objects at once.
3141 
3142 @param allocator Allocator object.
3143 @param pVkMemoryRequirements Memory requirements for each allocation.
3144 @param pCreateInfo Creation parameters for each alloction.
3145 @param allocationCount Number of allocations to make.
3146 @param[out] pAllocations Pointer to array that will be filled with handles to created allocations.
3147 @param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations.
3148 
3149 You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
3150 
3151 Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding.
3152 It is just a general purpose allocation function able to make multiple allocations at once.
3153 It may be internally optimized to be more efficient than calling vmaAllocateMemory() `allocationCount` times.
3154 
3155 All allocations are made using same parameters. All of them are created out of the same memory pool and type.
3156 If any allocation fails, all allocations already made within this function call are also freed, so that when
3157 returned result is not `VK_SUCCESS`, `pAllocation` array is always entirely filled with `VK_NULL_HANDLE`.
3158 */
3159 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
3160     VmaAllocator VMA_NOT_NULL allocator,
3161     const VkMemoryRequirements* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pVkMemoryRequirements,
3162     const VmaAllocationCreateInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pCreateInfo,
3163     size_t allocationCount,
3164     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
3165     VmaAllocationInfo* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationInfo);
3166 
3167 /**
3168 @param[out] pAllocation Handle to allocated memory.
3169 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
3170 
3171 You should free the memory using vmaFreeMemory().
3172 */
3173 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
3174     VmaAllocator VMA_NOT_NULL allocator,
3175     VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
3176     const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3177     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3178     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3179 
3180 /// Function similar to vmaAllocateMemoryForBuffer().
3181 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
3182     VmaAllocator VMA_NOT_NULL allocator,
3183     VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
3184     const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3185     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3186     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3187 
3188 /** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().
3189 
3190 Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped.
3191 */
3192 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
3193     VmaAllocator VMA_NOT_NULL allocator,
3194     const VmaAllocation VMA_NULLABLE allocation);
3195 
3196 /** \brief Frees memory and destroys multiple allocations.
3197 
3198 Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding.
3199 It is just a general purpose function to free memory and destroy allocations made using e.g. vmaAllocateMemory(),
3200 vmaAllocateMemoryPages() and other functions.
3201 It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times.
3202 
3203 Allocations in `pAllocations` array can come from any memory pools and types.
3204 Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped.
3205 */
3206 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
3207     VmaAllocator VMA_NOT_NULL allocator,
3208     size_t allocationCount,
3209     const VmaAllocation VMA_NULLABLE * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations);
3210 
3211 // OH ISSUE: VMA preAlloc
3212 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateReservedMemoryForImage(
3213     VmaAllocator VMA_NOT_NULL allocator,
3214     VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
3215     const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3216     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3217     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3218 
3219 VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetNewBlockStats(
3220     VmaAllocation VMA_NOT_NULL allocation,
3221     bool* VMA_NULLABLE pStats);
3222 
3223 VMA_CALL_PRE VkResult VMA_CALL_POST vmaClearNewBlockStats(
3224     VmaAllocation VMA_NOT_NULL allocation);
3225 
3226 VMA_CALL_PRE VkResult VMA_CALL_POST vmaSwapReservedBlock(
3227     VmaAllocator VMA_NOT_NULL allocator,
3228     VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
3229     const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3230     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3231     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3232 
3233 VMA_CALL_PRE void VMA_CALL_POST vmaFreeReservedMemory(
3234     VmaAllocator VMA_NOT_NULL allocator,
3235     const VmaAllocation VMA_NULLABLE allocation);
3236 
3237 VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetPreAllocBlockSize(
3238     VmaAllocator VMA_NOT_NULL allocator,
3239     uint32_t* VMA_NULLABLE pStats);
3240 
3241 /** \brief Deprecated.
3242 
3243 \deprecated
3244 In version 2.2.0 it used to try to change allocation's size without moving or reallocating it.
3245 In current version it returns `VK_SUCCESS` only if `newSize` equals current allocation's size.
3246 Otherwise returns `VK_ERROR_OUT_OF_POOL_MEMORY`, indicating that allocation's size could not be changed.
3247 */
3248 VMA_CALL_PRE VkResult VMA_CALL_POST vmaResizeAllocation(
3249     VmaAllocator VMA_NOT_NULL allocator,
3250     VmaAllocation VMA_NOT_NULL allocation,
3251     VkDeviceSize newSize);
3252 
3253 /** \brief Returns current information about specified allocation and atomically marks it as used in current frame.
3254 
3255 Current paramters of given allocation are returned in `pAllocationInfo`.
3256 
3257 This function also atomically "touches" allocation - marks it as used in current frame,
3258 just like vmaTouchAllocation().
3259 If the allocation is in lost state, `pAllocationInfo->deviceMemory == VK_NULL_HANDLE`.
3260 
3261 Although this function uses atomics and doesn't lock any mutex, so it should be quite efficient,
3262 you can avoid calling it too often.
3263 
3264 - You can retrieve same VmaAllocationInfo structure while creating your resource, from function
3265   vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change
3266   (e.g. due to defragmentation or allocation becoming lost).
3267 - If you just want to check if allocation is not lost, vmaTouchAllocation() will work faster.
3268 */
3269 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
3270     VmaAllocator VMA_NOT_NULL allocator,
3271     VmaAllocation VMA_NOT_NULL allocation,
3272     VmaAllocationInfo* VMA_NOT_NULL pAllocationInfo);
3273 
3274 /** \brief Returns `VK_TRUE` if allocation is not lost and atomically marks it as used in current frame.
3275 
3276 If the allocation has been created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
3277 this function returns `VK_TRUE` if it's not in lost state, so it can still be used.
3278 It then also atomically "touches" the allocation - marks it as used in current frame,
3279 so that you can be sure it won't become lost in current frame or next `frameInUseCount` frames.
3280 
3281 If the allocation is in lost state, the function returns `VK_FALSE`.
3282 Memory of such allocation, as well as buffer or image bound to it, should not be used.
3283 Lost allocation and the buffer/image still need to be destroyed.
3284 
3285 If the allocation has been created without #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
3286 this function always returns `VK_TRUE`.
3287 */
3288 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
3289     VmaAllocator VMA_NOT_NULL allocator,
3290     VmaAllocation VMA_NOT_NULL allocation);
3291 
3292 /** \brief Sets pUserData in given allocation to new value.
3293 
3294 If the allocation was created with VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT,
3295 pUserData must be either null, or pointer to a null-terminated string. The function
3296 makes local copy of the string and sets it as allocation's `pUserData`. String
3297 passed as pUserData doesn't need to be valid for whole lifetime of the allocation -
3298 you can free it after this call. String previously pointed by allocation's
3299 pUserData is freed from memory.
3300 
3301 If the flag was not used, the value of pointer `pUserData` is just copied to
3302 allocation's `pUserData`. It is opaque, so you can use it however you want - e.g.
3303 as a pointer, ordinal number or some handle to you own data.
3304 */
3305 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
3306     VmaAllocator VMA_NOT_NULL allocator,
3307     VmaAllocation VMA_NOT_NULL allocation,
3308     void* VMA_NULLABLE pUserData);
3309 
3310 /** \brief Creates new allocation that is in lost state from the beginning.
3311 
3312 It can be useful if you need a dummy, non-null allocation.
3313 
3314 You still need to destroy created object using vmaFreeMemory().
3315 
3316 Returned allocation is not tied to any specific memory pool or memory type and
3317 not bound to any image or buffer. It has size = 0. It cannot be turned into
3318 a real, non-empty allocation.
3319 */
3320 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
3321     VmaAllocator VMA_NOT_NULL allocator,
3322     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation);
3323 
3324 /** \brief Maps memory represented by given allocation and returns pointer to it.
3325 
3326 Maps memory represented by given allocation to make it accessible to CPU code.
3327 When succeeded, `*ppData` contains pointer to first byte of this memory.
3328 If the allocation is part of bigger `VkDeviceMemory` block, the pointer is
3329 correctly offseted to the beginning of region assigned to this particular
3330 allocation.
3331 
3332 Mapping is internally reference-counted and synchronized, so despite raw Vulkan
3333 function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory`
3334 multiple times simultaneously, it is safe to call this function on allocations
3335 assigned to the same memory block. Actual Vulkan memory will be mapped on first
3336 mapping and unmapped on last unmapping.
3337 
3338 If the function succeeded, you must call vmaUnmapMemory() to unmap the
3339 allocation when mapping is no longer needed or before freeing the allocation, at
3340 the latest.
3341 
3342 It also safe to call this function multiple times on the same allocation. You
3343 must call vmaUnmapMemory() same number of times as you called vmaMapMemory().
3344 
3345 It is also safe to call this function on allocation created with
3346 #VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time.
3347 You must still call vmaUnmapMemory() same number of times as you called
3348 vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the
3349 "0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag.
3350 
3351 This function fails when used on allocation made in memory type that is not
3352 `HOST_VISIBLE`.
3353 
3354 This function always fails when called for allocation that was created with
3355 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocations cannot be
3356 mapped.
3357 
3358 This function doesn't automatically flush or invalidate caches.
3359 If the allocation is made from a memory types that is not `HOST_COHERENT`,
3360 you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification.
3361 */
3362 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
3363     VmaAllocator VMA_NOT_NULL allocator,
3364     VmaAllocation VMA_NOT_NULL allocation,
3365     void* VMA_NULLABLE * VMA_NOT_NULL ppData);
3366 
3367 /** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
3368 
3369 For details, see description of vmaMapMemory().
3370 
3371 This function doesn't automatically flush or invalidate caches.
3372 If the allocation is made from a memory types that is not `HOST_COHERENT`,
3373 you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification.
3374 */
3375 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
3376     VmaAllocator VMA_NOT_NULL allocator,
3377     VmaAllocation VMA_NOT_NULL allocation);
3378 
3379 /** \brief Flushes memory of given allocation.
3380 
3381 Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation.
3382 It needs to be called after writing to a mapped memory for memory types that are not `HOST_COHERENT`.
3383 Unmap operation doesn't do that automatically.
3384 
3385 - `offset` must be relative to the beginning of allocation.
3386 - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
3387 - `offset` and `size` don't have to be aligned.
3388   They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
3389 - If `size` is 0, this call is ignored.
3390 - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
3391   this call is ignored.
3392 
3393 Warning! `offset` and `size` are relative to the contents of given `allocation`.
3394 If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively.
3395 Do not pass allocation's offset as `offset`!!!
3396 
3397 This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is
3398 called, otherwise `VK_SUCCESS`.
3399 */
3400 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(
3401     VmaAllocator VMA_NOT_NULL allocator,
3402     VmaAllocation VMA_NOT_NULL allocation,
3403     VkDeviceSize offset,
3404     VkDeviceSize size);
3405 
3406 /** \brief Invalidates memory of given allocation.
3407 
3408 Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation.
3409 It needs to be called before reading from a mapped memory for memory types that are not `HOST_COHERENT`.
3410 Map operation doesn't do that automatically.
3411 
3412 - `offset` must be relative to the beginning of allocation.
3413 - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
3414 - `offset` and `size` don't have to be aligned.
3415   They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
3416 - If `size` is 0, this call is ignored.
3417 - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
3418   this call is ignored.
3419 
3420 Warning! `offset` and `size` are relative to the contents of given `allocation`.
3421 If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively.
3422 Do not pass allocation's offset as `offset`!!!
3423 
3424 This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if
3425 it is called, otherwise `VK_SUCCESS`.
3426 */
3427 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(
3428     VmaAllocator VMA_NOT_NULL allocator,
3429     VmaAllocation VMA_NOT_NULL allocation,
3430     VkDeviceSize offset,
3431     VkDeviceSize size);
3432 
3433 /** \brief Flushes memory of given set of allocations.
3434 
3435 Calls `vkFlushMappedMemoryRanges()` for memory associated with given ranges of given allocations.
3436 For more information, see documentation of vmaFlushAllocation().
3437 
3438 \param allocator
3439 \param allocationCount
3440 \param allocations
3441 \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.
3442 \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.
3443 
3444 This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is
3445 called, otherwise `VK_SUCCESS`.
3446 */
3447 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
3448     VmaAllocator VMA_NOT_NULL allocator,
3449     uint32_t allocationCount,
3450     const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
3451     const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
3452     const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
3453 
3454 /** \brief Invalidates memory of given set of allocations.
3455 
3456 Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given ranges of given allocations.
3457 For more information, see documentation of vmaInvalidateAllocation().
3458 
3459 \param allocator
3460 \param allocationCount
3461 \param allocations
3462 \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.
3463 \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.
3464 
3465 This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if it is
3466 called, otherwise `VK_SUCCESS`.
3467 */
3468 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
3469     VmaAllocator VMA_NOT_NULL allocator,
3470     uint32_t allocationCount,
3471     const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
3472     const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
3473     const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
3474 
3475 /** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions.
3476 
3477 @param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked.
3478 
3479 Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
3480 `VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are
3481 `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
3482 
3483 Possible return values:
3484 
3485 - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types.
3486 - `VK_SUCCESS` - corruption detection has been performed and succeeded.
3487 - `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
3488   `VMA_ASSERT` is also fired in that case.
3489 - Other value: Error returned by Vulkan, e.g. memory mapping failure.
3490 */
3491 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator VMA_NOT_NULL allocator, uint32_t memoryTypeBits);
3492 
3493 /** \struct VmaDefragmentationContext
3494 \brief Represents Opaque object that represents started defragmentation process.
3495 
3496 Fill structure #VmaDefragmentationInfo2 and call function vmaDefragmentationBegin() to create it.
3497 Call function vmaDefragmentationEnd() to destroy it.
3498 */
3499 VK_DEFINE_HANDLE(VmaDefragmentationContext)
3500 
3501 /// Flags to be used in vmaDefragmentationBegin(). None at the moment. Reserved for future use.
3502 typedef enum VmaDefragmentationFlagBits {
3503     VMA_DEFRAGMENTATION_FLAG_INCREMENTAL = 0x1,
3504     VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
3505 } VmaDefragmentationFlagBits;
3506 typedef VkFlags VmaDefragmentationFlags;
3507 
3508 /** \brief Parameters for defragmentation.
3509 
3510 To be used with function vmaDefragmentationBegin().
3511 */
3512 typedef struct VmaDefragmentationInfo2 {
3513     /** \brief Reserved for future use. Should be 0.
3514     */
3515     VmaDefragmentationFlags flags;
3516     /** \brief Number of allocations in `pAllocations` array.
3517     */
3518     uint32_t allocationCount;
3519     /** \brief Pointer to array of allocations that can be defragmented.
3520 
3521     The array should have `allocationCount` elements.
3522     The array should not contain nulls.
3523     Elements in the array should be unique - same allocation cannot occur twice.
3524     It is safe to pass allocations that are in the lost state - they are ignored.
3525     All allocations not present in this array are considered non-moveable during this defragmentation.
3526     */
3527     const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations;
3528     /** \brief Optional, output. Pointer to array that will be filled with information whether the allocation at certain index has been changed during defragmentation.
3529 
3530     The array should have `allocationCount` elements.
3531     You can pass null if you are not interested in this information.
3532     */
3533     VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged;
3534     /** \brief Numer of pools in `pPools` array.
3535     */
3536     uint32_t poolCount;
3537     /** \brief Either null or pointer to array of pools to be defragmented.
3538 
3539     All the allocations in the specified pools can be moved during defragmentation
3540     and there is no way to check if they were really moved as in `pAllocationsChanged`,
3541     so you must query all the allocations in all these pools for new `VkDeviceMemory`
3542     and offset using vmaGetAllocationInfo() if you might need to recreate buffers
3543     and images bound to them.
3544 
3545     The array should have `poolCount` elements.
3546     The array should not contain nulls.
3547     Elements in the array should be unique - same pool cannot occur twice.
3548 
3549     Using this array is equivalent to specifying all allocations from the pools in `pAllocations`.
3550     It might be more efficient.
3551     */
3552     const VmaPool VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(poolCount) pPools;
3553     /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on CPU side, like `memcpy()`, `memmove()`.
3554 
3555     `VK_WHOLE_SIZE` means no limit.
3556     */
3557     VkDeviceSize maxCpuBytesToMove;
3558     /** \brief Maximum number of allocations that can be moved to a different place using transfers on CPU side, like `memcpy()`, `memmove()`.
3559 
3560     `UINT32_MAX` means no limit.
3561     */
3562     uint32_t maxCpuAllocationsToMove;
3563     /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on GPU side, posted to `commandBuffer`.
3564 
3565     `VK_WHOLE_SIZE` means no limit.
3566     */
3567     VkDeviceSize maxGpuBytesToMove;
3568     /** \brief Maximum number of allocations that can be moved to a different place using transfers on GPU side, posted to `commandBuffer`.
3569 
3570     `UINT32_MAX` means no limit.
3571     */
3572     uint32_t maxGpuAllocationsToMove;
3573     /** \brief Optional. Command buffer where GPU copy commands will be posted.
3574 
3575     If not null, it must be a valid command buffer handle that supports Transfer queue type.
3576     It must be in the recording state and outside of a render pass instance.
3577     You need to submit it and make sure it finished execution before calling vmaDefragmentationEnd().
3578 
3579     Passing null means that only CPU defragmentation will be performed.
3580     */
3581     VkCommandBuffer VMA_NULLABLE commandBuffer;
3582 } VmaDefragmentationInfo2;
3583 
3584 typedef struct VmaDefragmentationPassMoveInfo {
3585     VmaAllocation VMA_NOT_NULL allocation;
3586     VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory;
3587     VkDeviceSize offset;
3588 } VmaDefragmentationPassMoveInfo;
3589 
3590 /** \brief Parameters for incremental defragmentation steps.
3591 
3592 To be used with function vmaBeginDefragmentationPass().
3593 */
3594 typedef struct VmaDefragmentationPassInfo {
3595     uint32_t moveCount;
3596     VmaDefragmentationPassMoveInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(moveCount) pMoves;
3597 } VmaDefragmentationPassInfo;
3598 
3599 /** \brief Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
3600 
3601 \deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
3602 */
3603 typedef struct VmaDefragmentationInfo {
3604     /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places.
3605 
3606     Default is `VK_WHOLE_SIZE`, which means no limit.
3607     */
3608     VkDeviceSize maxBytesToMove;
3609     /** \brief Maximum number of allocations that can be moved to different place.
3610 
3611     Default is `UINT32_MAX`, which means no limit.
3612     */
3613     uint32_t maxAllocationsToMove;
3614 } VmaDefragmentationInfo;
3615 
3616 /** \brief Statistics returned by function vmaDefragment(). */
3617 typedef struct VmaDefragmentationStats {
3618     /// Total number of bytes that have been copied while moving allocations to different places.
3619     VkDeviceSize bytesMoved;
3620     /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects.
3621     VkDeviceSize bytesFreed;
3622     /// Number of allocations that have been moved to different places.
3623     uint32_t allocationsMoved;
3624     /// Number of empty `VkDeviceMemory` objects that have been released to the system.
3625     uint32_t deviceMemoryBlocksFreed;
3626 } VmaDefragmentationStats;
3627 
3628 /** \brief Begins defragmentation process.
3629 
3630 @param allocator Allocator object.
3631 @param pInfo Structure filled with parameters of defragmentation.
3632 @param[out] pStats Optional. Statistics of defragmentation. You can pass null if you are not interested in this information.
3633 @param[out] pContext Context object that must be passed to vmaDefragmentationEnd() to finish defragmentation.
3634 @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.
3635 
3636 Use this function instead of old, deprecated vmaDefragment().
3637 
3638 Warning! Between the call to vmaDefragmentationBegin() and vmaDefragmentationEnd():
3639 
3640 - You should not use any of allocations passed as `pInfo->pAllocations` or
3641   any allocations that belong to pools passed as `pInfo->pPools`,
3642   including calling vmaGetAllocationInfo(), vmaTouchAllocation(), or access
3643   their data.
3644 - Some mutexes protecting internal data structures may be locked, so trying to
3645   make or free any allocations, bind buffers or images, map memory, or launch
3646   another simultaneous defragmentation in between may cause stall (when done on
3647   another thread) or deadlock (when done on the same thread), unless you are
3648   100% sure that defragmented allocations are in different pools.
3649 - Information returned via `pStats` and `pInfo->pAllocationsChanged` are undefined.
3650   They become valid after call to vmaDefragmentationEnd().
3651 - If `pInfo->commandBuffer` is not null, you must submit that command buffer
3652   and make sure it finished execution before calling vmaDefragmentationEnd().
3653 
3654 For more information and important limitations regarding defragmentation, see documentation chapter:
3655 [Defragmentation](@ref defragmentation).
3656 */
3657 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
3658     VmaAllocator VMA_NOT_NULL allocator,
3659     const VmaDefragmentationInfo2* VMA_NOT_NULL pInfo,
3660     VmaDefragmentationStats* VMA_NULLABLE pStats,
3661     VmaDefragmentationContext VMA_NULLABLE * VMA_NOT_NULL pContext);
3662 
3663 /** \brief Ends defragmentation process.
3664 
3665 Use this function to finish defragmentation started by vmaDefragmentationBegin().
3666 It is safe to pass `context == null`. The function then does nothing.
3667 */
3668 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
3669     VmaAllocator VMA_NOT_NULL allocator,
3670     VmaDefragmentationContext VMA_NULLABLE context);
3671 
3672 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
3673     VmaAllocator VMA_NOT_NULL allocator,
3674     VmaDefragmentationContext VMA_NULLABLE context,
3675     VmaDefragmentationPassInfo* VMA_NOT_NULL pInfo
3676 );
3677 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
3678     VmaAllocator VMA_NOT_NULL allocator,
3679     VmaDefragmentationContext VMA_NULLABLE context
3680 );
3681 
3682 /** \brief Deprecated. Compacts memory by moving allocations.
3683 
3684 @param pAllocations Array of allocations that can be moved during this compation.
3685 @param allocationCount Number of elements in pAllocations and pAllocationsChanged arrays.
3686 @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.
3687 @param pDefragmentationInfo Configuration parameters. Optional - pass null to use default values.
3688 @param[out] pDefragmentationStats Statistics returned by the function. Optional - pass null if you don't need this information.
3689 @return `VK_SUCCESS` if completed, negative error code in case of error.
3690 
3691 \deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
3692 
3693 This function works by moving allocations to different places (different
3694 `VkDeviceMemory` objects and/or different offsets) in order to optimize memory
3695 usage. Only allocations that are in `pAllocations` array can be moved. All other
3696 allocations are considered nonmovable in this call. Basic rules:
3697 
3698 - Only allocations made in memory types that have
3699   `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`
3700   flags can be compacted. You may pass other allocations but it makes no sense -
3701   these will never be moved.
3702 - Custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT or
3703   #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag are not defragmented. Allocations
3704   passed to this function that come from such pools are ignored.
3705 - Allocations created with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT or
3706   created as dedicated allocations for any other reason are also ignored.
3707 - Both allocations made with or without #VMA_ALLOCATION_CREATE_MAPPED_BIT
3708   flag can be compacted. If not persistently mapped, memory will be mapped
3709   temporarily inside this function if needed.
3710 - You must not pass same #VmaAllocation object multiple times in `pAllocations` array.
3711 
3712 The function also frees empty `VkDeviceMemory` blocks.
3713 
3714 Warning: This function may be time-consuming, so you shouldn't call it too often
3715 (like after every resource creation/destruction).
3716 You can call it on special occasions (like when reloading a game level or
3717 when you just destroyed a lot of objects). Calling it every frame may be OK, but
3718 you should measure that on your platform.
3719 
3720 For more information, see [Defragmentation](@ref defragmentation) chapter.
3721 */
3722 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
3723     VmaAllocator VMA_NOT_NULL allocator,
3724     const VmaAllocation VMA_NOT_NULL * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
3725     size_t allocationCount,
3726     VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged,
3727     const VmaDefragmentationInfo* VMA_NULLABLE pDefragmentationInfo,
3728     VmaDefragmentationStats* VMA_NULLABLE pDefragmentationStats);
3729 
3730 /** \brief Binds buffer to allocation.
3731 
3732 Binds specified buffer to region of memory represented by specified allocation.
3733 Gets `VkDeviceMemory` handle and offset from the allocation.
3734 If you want to create a buffer, allocate memory for it and bind them together separately,
3735 you should use this function for binding instead of standard `vkBindBufferMemory()`,
3736 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
3737 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
3738 (which is illegal in Vulkan).
3739 
3740 It is recommended to use function vmaCreateBuffer() instead of this one.
3741 */
3742 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
3743     VmaAllocator VMA_NOT_NULL allocator,
3744     VmaAllocation VMA_NOT_NULL allocation,
3745     VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer);
3746 
3747 /** \brief Binds buffer to allocation with additional parameters.
3748 
3749 @param allocationLocalOffset Additional offset to be added while binding, relative to the beginnig of the `allocation`. Normally it should be 0.
3750 @param pNext A chain of structures to be attached to `VkBindBufferMemoryInfoKHR` structure used internally. Normally it should be null.
3751 
3752 This function is similar to vmaBindBufferMemory(), but it provides additional parameters.
3753 
3754 If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag
3755 or with VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_1`. Otherwise the call fails.
3756 */
3757 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
3758     VmaAllocator VMA_NOT_NULL allocator,
3759     VmaAllocation VMA_NOT_NULL allocation,
3760     VkDeviceSize allocationLocalOffset,
3761     VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
3762     const void* VMA_NULLABLE pNext);
3763 
3764 /** \brief Binds image to allocation.
3765 
3766 Binds specified image to region of memory represented by specified allocation.
3767 Gets `VkDeviceMemory` handle and offset from the allocation.
3768 If you want to create an image, allocate memory for it and bind them together separately,
3769 you should use this function for binding instead of standard `vkBindImageMemory()`,
3770 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
3771 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
3772 (which is illegal in Vulkan).
3773 
3774 It is recommended to use function vmaCreateImage() instead of this one.
3775 */
3776 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
3777     VmaAllocator VMA_NOT_NULL allocator,
3778     VmaAllocation VMA_NOT_NULL allocation,
3779     VkImage VMA_NOT_NULL_NON_DISPATCHABLE image);
3780 
3781 /** \brief Binds image to allocation with additional parameters.
3782 
3783 @param allocationLocalOffset Additional offset to be added while binding, relative to the beginnig of the `allocation`. Normally it should be 0.
3784 @param pNext A chain of structures to be attached to `VkBindImageMemoryInfoKHR` structure used internally. Normally it should be null.
3785 
3786 This function is similar to vmaBindImageMemory(), but it provides additional parameters.
3787 
3788 If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag
3789 or with VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_1`. Otherwise the call fails.
3790 */
3791 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
3792     VmaAllocator VMA_NOT_NULL allocator,
3793     VmaAllocation VMA_NOT_NULL allocation,
3794     VkDeviceSize allocationLocalOffset,
3795     VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
3796     const void* VMA_NULLABLE pNext);
3797 
3798 /**
3799 @param[out] pBuffer Buffer that was created.
3800 @param[out] pAllocation Allocation that was created.
3801 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
3802 
3803 This function automatically:
3804 
3805 -# Creates buffer.
3806 -# Allocates appropriate memory for it.
3807 -# Binds the buffer with the memory.
3808 
3809 If any of these operations fail, buffer and allocation are not created,
3810 returned value is negative error code, *pBuffer and *pAllocation are null.
3811 
3812 If the function succeeded, you must destroy both buffer and allocation when you
3813 no longer need them using either convenience function vmaDestroyBuffer() or
3814 separately, using `vkDestroyBuffer()` and vmaFreeMemory().
3815 
3816 If VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used,
3817 VK_KHR_dedicated_allocation extension is used internally to query driver whether
3818 it requires or prefers the new buffer to have dedicated allocation. If yes,
3819 and if dedicated allocation is possible (VmaAllocationCreateInfo::pool is null
3820 and VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated
3821 allocation for this buffer, just like when using
3822 VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
3823 */
3824 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
3825     VmaAllocator VMA_NOT_NULL allocator,
3826     const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
3827     const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
3828     VkBuffer VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pBuffer,
3829     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3830     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3831 
3832 /** \brief Destroys Vulkan buffer and frees allocated memory.
3833 
3834 This is just a convenience function equivalent to:
3835 
3836 \code
3837 vkDestroyBuffer(device, buffer, allocationCallbacks);
3838 vmaFreeMemory(allocator, allocation);
3839 \endcode
3840 
3841 It it safe to pass null as buffer and/or allocation.
3842 */
3843 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
3844     VmaAllocator VMA_NOT_NULL allocator,
3845     VkBuffer VMA_NULLABLE_NON_DISPATCHABLE buffer,
3846     VmaAllocation VMA_NULLABLE allocation);
3847 
3848 /// Function similar to vmaCreateBuffer().
3849 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
3850     VmaAllocator VMA_NOT_NULL allocator,
3851     const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
3852     const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
3853     VkImage VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pImage,
3854     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3855     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3856 
3857 /** \brief Destroys Vulkan image and frees allocated memory.
3858 
3859 This is just a convenience function equivalent to:
3860 
3861 \code
3862 vkDestroyImage(device, image, allocationCallbacks);
3863 vmaFreeMemory(allocator, allocation);
3864 \endcode
3865 
3866 It it safe to pass null as image and/or allocation.
3867 */
3868 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
3869     VmaAllocator VMA_NOT_NULL allocator,
3870     VkImage VMA_NULLABLE_NON_DISPATCHABLE image,
3871     VmaAllocation VMA_NULLABLE allocation);
3872 
3873 // OH ISSUE: VMA preAlloc
3874 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateFakeImage(
3875     VmaAllocator VMA_NOT_NULL allocator,
3876     VkImage VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pImage);
3877 
3878 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyFakeImage(
3879     VmaAllocator VMA_NOT_NULL allocator,
3880     VkImage VMA_NULLABLE_NON_DISPATCHABLE image);
3881 
3882 #ifdef __cplusplus
3883 }
3884 #endif
3885 
3886 #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
3887 
3888 // For Visual Studio IntelliSense.
3889 #if defined(__cplusplus) && defined(__INTELLISENSE__)
3890 #define VMA_IMPLEMENTATION
3891 #endif
3892 
3893 #ifdef VMA_IMPLEMENTATION
3894 #undef VMA_IMPLEMENTATION
3895 
3896 #include <cstdint>
3897 #include <cstdlib>
3898 #include <cstring>
3899 #include <utility>
3900 
3901 /*******************************************************************************
3902 CONFIGURATION SECTION
3903 
3904 Define some of these macros before each #include of this header or change them
3905 here if you need other then default behavior depending on your environment.
3906 */
3907 
3908 /*
3909 Define this macro to 1 to make the library fetch pointers to Vulkan functions
3910 internally, like:
3911 
3912     vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
3913 */
3914 #if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
3915     #define VMA_STATIC_VULKAN_FUNCTIONS 1
3916 #endif
3917 
3918 /*
3919 Define this macro to 1 to make the library fetch pointers to Vulkan functions
3920 internally, like:
3921 
3922     vulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkGetDeviceProcAddr(m_hDevice, vkAllocateMemory);
3923 */
3924 #if !defined(VMA_DYNAMIC_VULKAN_FUNCTIONS)
3925     #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
3926 #endif
3927 
3928 // Define this macro to 1 to make the library use STL containers instead of its own implementation.
3929 //#define VMA_USE_STL_CONTAINERS 1
3930 
3931 /* Set this macro to 1 to make the library including and using STL containers:
3932 std::pair, std::vector, std::list, std::unordered_map.
3933 
3934 Set it to 0 or undefined to make the library using its own implementation of
3935 the containers.
3936 */
3937 #if VMA_USE_STL_CONTAINERS
3938    #define VMA_USE_STL_VECTOR 1
3939    #define VMA_USE_STL_UNORDERED_MAP 1
3940    #define VMA_USE_STL_LIST 1
3941 #endif
3942 
3943 #ifndef VMA_USE_STL_SHARED_MUTEX
3944     // Compiler conforms to C++17.
3945     #if __cplusplus >= 201703L
3946         #define VMA_USE_STL_SHARED_MUTEX 1
3947     // Visual studio defines __cplusplus properly only when passed additional parameter: /Zc:__cplusplus
3948     // Otherwise it's always 199711L, despite shared_mutex works since Visual Studio 2015 Update 2.
3949     // See: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/
3950     #elif defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && __cplusplus == 199711L && _MSVC_LANG >= 201703L
3951         #define VMA_USE_STL_SHARED_MUTEX 1
3952     #else
3953         #define VMA_USE_STL_SHARED_MUTEX 0
3954     #endif
3955 #endif
3956 
3957 /*
3958 THESE INCLUDES ARE NOT ENABLED BY DEFAULT.
3959 Library has its own container implementation.
3960 */
3961 #if VMA_USE_STL_VECTOR
3962    #include <vector>
3963 #endif
3964 
3965 #if VMA_USE_STL_UNORDERED_MAP
3966    #include <unordered_map>
3967 #endif
3968 
3969 #if VMA_USE_STL_LIST
3970    #include <list>
3971 #endif
3972 
3973 /*
3974 Following headers are used in this CONFIGURATION section only, so feel free to
3975 remove them if not needed.
3976 */
3977 #include <cassert> // for assert
3978 #include <algorithm> // for min, max
3979 #include <mutex>
3980 
3981 #ifndef VMA_NULL
3982    // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
3983    #define VMA_NULL   nullptr
3984 #endif
3985 
3986 #if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
3987 #include <cstdlib>
aligned_alloc(size_t alignment,size_t size)3988 void *aligned_alloc(size_t alignment, size_t size)
3989 {
3990     // alignment must be >= sizeof(void*)
3991     if(alignment < sizeof(void*))
3992     {
3993         alignment = sizeof(void*);
3994     }
3995 
3996     return memalign(alignment, size);
3997 }
3998 #elif defined(__APPLE__) || defined(__ANDROID__) || (defined(__linux__) && defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC))
3999 #include <cstdlib>
aligned_alloc(size_t alignment,size_t size)4000 void *aligned_alloc(size_t alignment, size_t size)
4001 {
4002     // alignment must be >= sizeof(void*)
4003     if(alignment < sizeof(void*))
4004     {
4005         alignment = sizeof(void*);
4006     }
4007 
4008     void *pointer;
4009     if(posix_memalign(&pointer, alignment, size) == 0)
4010         return pointer;
4011     return VMA_NULL;
4012 }
4013 #endif
4014 
4015 // If your compiler is not compatible with C++11 and definition of
4016 // aligned_alloc() function is missing, uncommeting following line may help:
4017 
4018 //#include <malloc.h>
4019 
4020 // Normal assert to check for programmer's errors, especially in Debug configuration.
4021 #ifndef VMA_ASSERT
4022    #ifdef NDEBUG
4023        #define VMA_ASSERT(expr)
4024    #else
4025        #define VMA_ASSERT(expr)         assert(expr)
4026    #endif
4027 #endif
4028 
4029 // Assert that will be called very often, like inside data structures e.g. operator[].
4030 // Making it non-empty can make program slow.
4031 #ifndef VMA_HEAVY_ASSERT
4032    #ifdef NDEBUG
4033        #define VMA_HEAVY_ASSERT(expr)
4034    #else
4035        #define VMA_HEAVY_ASSERT(expr)   //VMA_ASSERT(expr)
4036    #endif
4037 #endif
4038 
4039 #ifndef VMA_ALIGN_OF
4040    #define VMA_ALIGN_OF(type)       (__alignof(type))
4041 #endif
4042 
4043 #ifndef VMA_SYSTEM_ALIGNED_MALLOC
4044    #if defined(_WIN32)
4045        #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment)   (_aligned_malloc((size), (alignment)))
4046    #else
4047        #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment)   (aligned_alloc((alignment), (size) ))
4048    #endif
4049 #endif
4050 
4051 #ifndef VMA_SYSTEM_FREE
4052    #if defined(_WIN32)
4053        #define VMA_SYSTEM_FREE(ptr)   _aligned_free(ptr)
4054    #else
4055        #define VMA_SYSTEM_FREE(ptr)   free(ptr)
4056    #endif
4057 #endif
4058 
4059 #ifndef VMA_MIN
4060    #define VMA_MIN(v1, v2)    (std::min((v1), (v2)))
4061 #endif
4062 
4063 #ifndef VMA_MAX
4064    #define VMA_MAX(v1, v2)    (std::max((v1), (v2)))
4065 #endif
4066 
4067 #ifndef VMA_SWAP
4068    #define VMA_SWAP(v1, v2)   std::swap((v1), (v2))
4069 #endif
4070 
4071 #ifndef VMA_SORT
4072    #define VMA_SORT(beg, end, cmp)  std::sort(beg, end, cmp)
4073 #endif
4074 
4075 #ifndef VMA_DEBUG_LOG
4076    #define VMA_DEBUG_LOG(format, ...)
4077    /*
4078    #define VMA_DEBUG_LOG(format, ...) do { \
4079        printf(format, __VA_ARGS__); \
4080        printf("\n"); \
4081    } while(false)
4082    */
4083 #endif
4084 
4085 // Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
4086 #if VMA_STATS_STRING_ENABLED
VmaUint32ToStr(char * outStr,size_t strLen,uint32_t num)4087     static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num)
4088     {
4089         snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
4090     }
VmaUint64ToStr(char * outStr,size_t strLen,uint64_t num)4091     static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
4092     {
4093         snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
4094     }
VmaPtrToStr(char * outStr,size_t strLen,const void * ptr)4095     static inline void VmaPtrToStr(char* outStr, size_t strLen, const void* ptr)
4096     {
4097         snprintf(outStr, strLen, "%p", ptr);
4098     }
4099 #endif
4100 
4101 #ifndef VMA_MUTEX
4102     class VmaMutex
4103     {
4104     public:
Lock()4105         void Lock() { m_Mutex.lock(); }
Unlock()4106         void Unlock() { m_Mutex.unlock(); }
TryLock()4107         bool TryLock() { return m_Mutex.try_lock(); }
4108     private:
4109         std::mutex m_Mutex;
4110     };
4111     #define VMA_MUTEX VmaMutex
4112 #endif
4113 
4114 // Read-write mutex, where "read" is shared access, "write" is exclusive access.
4115 #ifndef VMA_RW_MUTEX
4116     #if VMA_USE_STL_SHARED_MUTEX
4117         // Use std::shared_mutex from C++17.
4118         #include <shared_mutex>
4119         class VmaRWMutex
4120         {
4121         public:
LockRead()4122             void LockRead() { m_Mutex.lock_shared(); }
UnlockRead()4123             void UnlockRead() { m_Mutex.unlock_shared(); }
TryLockRead()4124             bool TryLockRead() { return m_Mutex.try_lock_shared(); }
LockWrite()4125             void LockWrite() { m_Mutex.lock(); }
UnlockWrite()4126             void UnlockWrite() { m_Mutex.unlock(); }
TryLockWrite()4127             bool TryLockWrite() { return m_Mutex.try_lock(); }
4128         private:
4129             std::shared_mutex m_Mutex;
4130         };
4131         #define VMA_RW_MUTEX VmaRWMutex
4132     #elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600
4133         // Use SRWLOCK from WinAPI.
4134         // Minimum supported client = Windows Vista, server = Windows Server 2008.
4135         class VmaRWMutex
4136         {
4137         public:
VmaRWMutex()4138             VmaRWMutex() { InitializeSRWLock(&m_Lock); }
LockRead()4139             void LockRead() { AcquireSRWLockShared(&m_Lock); }
UnlockRead()4140             void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
TryLockRead()4141             bool TryLockRead() { return TryAcquireSRWLockShared(&m_Lock) != FALSE; }
LockWrite()4142             void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
UnlockWrite()4143             void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
TryLockWrite()4144             bool TryLockWrite() { return TryAcquireSRWLockExclusive(&m_Lock) != FALSE; }
4145         private:
4146             SRWLOCK m_Lock;
4147         };
4148         #define VMA_RW_MUTEX VmaRWMutex
4149     #else
4150         // Less efficient fallback: Use normal mutex.
4151         class VmaRWMutex
4152         {
4153         public:
LockRead()4154             void LockRead() { m_Mutex.Lock(); }
UnlockRead()4155             void UnlockRead() { m_Mutex.Unlock(); }
TryLockRead()4156             bool TryLockRead() { return m_Mutex.TryLock(); }
LockWrite()4157             void LockWrite() { m_Mutex.Lock(); }
UnlockWrite()4158             void UnlockWrite() { m_Mutex.Unlock(); }
TryLockWrite()4159             bool TryLockWrite() { return m_Mutex.TryLock(); }
4160         private:
4161             VMA_MUTEX m_Mutex;
4162         };
4163         #define VMA_RW_MUTEX VmaRWMutex
4164     #endif // #if VMA_USE_STL_SHARED_MUTEX
4165 #endif // #ifndef VMA_RW_MUTEX
4166 
4167 /*
4168 If providing your own implementation, you need to implement a subset of std::atomic.
4169 */
4170 #ifndef VMA_ATOMIC_UINT32
4171     #include <atomic>
4172     #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
4173 #endif
4174 
4175 #ifndef VMA_ATOMIC_UINT64
4176     #include <atomic>
4177     #define VMA_ATOMIC_UINT64 std::atomic<uint64_t>
4178 #endif
4179 
4180 #ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
4181     /**
4182     Every allocation will have its own memory block.
4183     Define to 1 for debugging purposes only.
4184     */
4185     #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
4186 #endif
4187 
4188 #ifndef VMA_DEBUG_ALIGNMENT
4189     /**
4190     Minimum alignment of all allocations, in bytes.
4191     Set to more than 1 for debugging purposes only. Must be power of two.
4192     */
4193     #define VMA_DEBUG_ALIGNMENT (1)
4194 #endif
4195 
4196 #ifndef VMA_DEBUG_MARGIN
4197     /**
4198     Minimum margin before and after every allocation, in bytes.
4199     Set nonzero for debugging purposes only.
4200     */
4201     #define VMA_DEBUG_MARGIN (0)
4202 #endif
4203 
4204 #ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
4205     /**
4206     Define this macro to 1 to automatically fill new allocations and destroyed
4207     allocations with some bit pattern.
4208     */
4209     #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)
4210 #endif
4211 
4212 #ifndef VMA_DEBUG_DETECT_CORRUPTION
4213     /**
4214     Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to
4215     enable writing magic value to the margin before and after every allocation and
4216     validating it, so that memory corruptions (out-of-bounds writes) are detected.
4217     */
4218     #define VMA_DEBUG_DETECT_CORRUPTION (0)
4219 #endif
4220 
4221 #ifndef VMA_DEBUG_GLOBAL_MUTEX
4222     /**
4223     Set this to 1 for debugging purposes only, to enable single mutex protecting all
4224     entry calls to the library. Can be useful for debugging multithreading issues.
4225     */
4226     #define VMA_DEBUG_GLOBAL_MUTEX (0)
4227 #endif
4228 
4229 #ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
4230     /**
4231     Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.
4232     Set to more than 1 for debugging purposes only. Must be power of two.
4233     */
4234     #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
4235 #endif
4236 
4237 #ifndef VMA_SMALL_HEAP_MAX_SIZE
4238    /// Maximum size of a memory heap in Vulkan to consider it "small".
4239    #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
4240 #endif
4241 
4242 #ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
4243    /// Default size of a block allocated as single VkDeviceMemory from a "large" heap.
4244    #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
4245 #endif
4246 
4247 #ifndef VMA_CLASS_NO_COPY
4248     #define VMA_CLASS_NO_COPY(className) \
4249         private: \
4250             className(const className&) = delete; \
4251             className& operator=(const className&) = delete;
4252 #endif
4253 
4254 static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX;
4255 
4256 // Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.
4257 static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
4258 
4259 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED   = 0xDC;
4260 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
4261 
4262 /*******************************************************************************
4263 END OF CONFIGURATION
4264 */
4265 
4266 // # Copy of some Vulkan definitions so we don't need to check their existence just to handle few constants.
4267 
4268 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY = 0x00000040;
4269 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY = 0x00000080;
4270 static const uint32_t VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY = 0x00020000;
4271 
4272 static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
4273 
4274 static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
4275     VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
4276 
4277 // Returns number of bits set to 1 in (v).
VmaCountBitsSet(uint32_t v)4278 static inline uint32_t VmaCountBitsSet(uint32_t v)
4279 {
4280     uint32_t c = v - ((v >> 1) & 0x55555555);
4281     c = ((c >>  2) & 0x33333333) + (c & 0x33333333);
4282     c = ((c >>  4) + c) & 0x0F0F0F0F;
4283     c = ((c >>  8) + c) & 0x00FF00FF;
4284     c = ((c >> 16) + c) & 0x0000FFFF;
4285     return c;
4286 }
4287 
4288 // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
4289 // Use types like uint32_t, uint64_t as T.
4290 template <typename T>
VmaAlignUp(T val,T align)4291 static inline T VmaAlignUp(T val, T align)
4292 {
4293     return (val + align - 1) / align * align;
4294 }
4295 // Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8.
4296 // Use types like uint32_t, uint64_t as T.
4297 template <typename T>
VmaAlignDown(T val,T align)4298 static inline T VmaAlignDown(T val, T align)
4299 {
4300     return val / align * align;
4301 }
4302 
4303 // Division with mathematical rounding to nearest number.
4304 template <typename T>
VmaRoundDiv(T x,T y)4305 static inline T VmaRoundDiv(T x, T y)
4306 {
4307     return (x + (y / (T)2)) / y;
4308 }
4309 
4310 /*
4311 Returns true if given number is a power of two.
4312 T must be unsigned integer number or signed integer but always nonnegative.
4313 For 0 returns true.
4314 */
4315 template <typename T>
VmaIsPow2(T x)4316 inline bool VmaIsPow2(T x)
4317 {
4318     return (x & (x-1)) == 0;
4319 }
4320 
4321 // Returns smallest power of 2 greater or equal to v.
VmaNextPow2(uint32_t v)4322 static inline uint32_t VmaNextPow2(uint32_t v)
4323 {
4324     v--;
4325     v |= v >> 1;
4326     v |= v >> 2;
4327     v |= v >> 4;
4328     v |= v >> 8;
4329     v |= v >> 16;
4330     v++;
4331     return v;
4332 }
VmaNextPow2(uint64_t v)4333 static inline uint64_t VmaNextPow2(uint64_t v)
4334 {
4335     v--;
4336     v |= v >> 1;
4337     v |= v >> 2;
4338     v |= v >> 4;
4339     v |= v >> 8;
4340     v |= v >> 16;
4341     v |= v >> 32;
4342     v++;
4343     return v;
4344 }
4345 
4346 // Returns largest power of 2 less or equal to v.
VmaPrevPow2(uint32_t v)4347 static inline uint32_t VmaPrevPow2(uint32_t v)
4348 {
4349     v |= v >> 1;
4350     v |= v >> 2;
4351     v |= v >> 4;
4352     v |= v >> 8;
4353     v |= v >> 16;
4354     v = v ^ (v >> 1);
4355     return v;
4356 }
VmaPrevPow2(uint64_t v)4357 static inline uint64_t VmaPrevPow2(uint64_t v)
4358 {
4359     v |= v >> 1;
4360     v |= v >> 2;
4361     v |= v >> 4;
4362     v |= v >> 8;
4363     v |= v >> 16;
4364     v |= v >> 32;
4365     v = v ^ (v >> 1);
4366     return v;
4367 }
4368 
VmaStrIsEmpty(const char * pStr)4369 static inline bool VmaStrIsEmpty(const char* pStr)
4370 {
4371     return pStr == VMA_NULL || *pStr == '\0';
4372 }
4373 
4374 #if VMA_STATS_STRING_ENABLED
4375 
VmaAlgorithmToStr(uint32_t algorithm)4376 static const char* VmaAlgorithmToStr(uint32_t algorithm)
4377 {
4378     switch(algorithm)
4379     {
4380     case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
4381         return "Linear";
4382     case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
4383         return "Buddy";
4384     case 0:
4385         return "Default";
4386     default:
4387         VMA_ASSERT(0);
4388         return "";
4389     }
4390 }
4391 
4392 #endif // #if VMA_STATS_STRING_ENABLED
4393 
4394 #ifndef VMA_SORT
4395 
4396 template<typename Iterator, typename Compare>
VmaQuickSortPartition(Iterator beg,Iterator end,Compare cmp)4397 Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
4398 {
4399     Iterator centerValue = end; --centerValue;
4400     Iterator insertIndex = beg;
4401     for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
4402     {
4403         if(cmp(*memTypeIndex, *centerValue))
4404         {
4405             if(insertIndex != memTypeIndex)
4406             {
4407                 VMA_SWAP(*memTypeIndex, *insertIndex);
4408             }
4409             ++insertIndex;
4410         }
4411     }
4412     if(insertIndex != centerValue)
4413     {
4414         VMA_SWAP(*insertIndex, *centerValue);
4415     }
4416     return insertIndex;
4417 }
4418 
4419 template<typename Iterator, typename Compare>
VmaQuickSort(Iterator beg,Iterator end,Compare cmp)4420 void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
4421 {
4422     if(beg < end)
4423     {
4424         Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
4425         VmaQuickSort<Iterator, Compare>(beg, it, cmp);
4426         VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
4427     }
4428 }
4429 
4430 #define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
4431 
4432 #endif // #ifndef VMA_SORT
4433 
4434 /*
4435 Returns true if two memory blocks occupy overlapping pages.
4436 ResourceA must be in less memory offset than ResourceB.
4437 
4438 Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
4439 chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
4440 */
VmaBlocksOnSamePage(VkDeviceSize resourceAOffset,VkDeviceSize resourceASize,VkDeviceSize resourceBOffset,VkDeviceSize pageSize)4441 static inline bool VmaBlocksOnSamePage(
4442     VkDeviceSize resourceAOffset,
4443     VkDeviceSize resourceASize,
4444     VkDeviceSize resourceBOffset,
4445     VkDeviceSize pageSize)
4446 {
4447     VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
4448     VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
4449     VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
4450     VkDeviceSize resourceBStart = resourceBOffset;
4451     VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
4452     return resourceAEndPage == resourceBStartPage;
4453 }
4454 
4455 enum VmaSuballocationType
4456 {
4457     VMA_SUBALLOCATION_TYPE_FREE = 0,
4458     VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
4459     VMA_SUBALLOCATION_TYPE_BUFFER = 2,
4460     VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
4461     VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
4462     VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
4463     VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
4464 };
4465 
4466 /*
4467 Returns true if given suballocation types could conflict and must respect
4468 VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
4469 or linear image and another one is optimal image. If type is unknown, behave
4470 conservatively.
4471 */
VmaIsBufferImageGranularityConflict(VmaSuballocationType suballocType1,VmaSuballocationType suballocType2)4472 static inline bool VmaIsBufferImageGranularityConflict(
4473     VmaSuballocationType suballocType1,
4474     VmaSuballocationType suballocType2)
4475 {
4476     if(suballocType1 > suballocType2)
4477     {
4478         VMA_SWAP(suballocType1, suballocType2);
4479     }
4480 
4481     switch(suballocType1)
4482     {
4483     case VMA_SUBALLOCATION_TYPE_FREE:
4484         return false;
4485     case VMA_SUBALLOCATION_TYPE_UNKNOWN:
4486         return true;
4487     case VMA_SUBALLOCATION_TYPE_BUFFER:
4488         return
4489             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
4490             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4491     case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
4492         return
4493             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
4494             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
4495             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4496     case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
4497         return
4498             suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4499     case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
4500         return false;
4501     default:
4502         VMA_ASSERT(0);
4503         return true;
4504     }
4505 }
4506 
VmaWriteMagicValue(void * pData,VkDeviceSize offset)4507 static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
4508 {
4509 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
4510     uint32_t* pDst = (uint32_t*)((char*)pData + offset);
4511     const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
4512     for(size_t i = 0; i < numberCount; ++i, ++pDst)
4513     {
4514         *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
4515     }
4516 #else
4517     // no-op
4518 #endif
4519 }
4520 
VmaValidateMagicValue(const void * pData,VkDeviceSize offset)4521 static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
4522 {
4523 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
4524     const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
4525     const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
4526     for(size_t i = 0; i < numberCount; ++i, ++pSrc)
4527     {
4528         if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
4529         {
4530             return false;
4531         }
4532     }
4533 #endif
4534     return true;
4535 }
4536 
4537 /*
4538 Fills structure with parameters of an example buffer to be used for transfers
4539 during GPU memory defragmentation.
4540 */
VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo & outBufCreateInfo)4541 static void VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo& outBufCreateInfo)
4542 {
4543     memset(&outBufCreateInfo, 0, sizeof(outBufCreateInfo));
4544     outBufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
4545     outBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
4546     outBufCreateInfo.size = (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE; // Example size.
4547 }
4548 
4549 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
4550 struct VmaMutexLock
4551 {
VMA_CLASS_NO_COPYVmaMutexLock4552     VMA_CLASS_NO_COPY(VmaMutexLock)
4553 public:
4554     VmaMutexLock(VMA_MUTEX& mutex, bool useMutex = true) :
4555         m_pMutex(useMutex ? &mutex : VMA_NULL)
4556     { if(m_pMutex) { m_pMutex->Lock(); } }
~VmaMutexLockVmaMutexLock4557     ~VmaMutexLock()
4558     { if(m_pMutex) { m_pMutex->Unlock(); } }
4559 private:
4560     VMA_MUTEX* m_pMutex;
4561 };
4562 
4563 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
4564 struct VmaMutexLockRead
4565 {
VMA_CLASS_NO_COPYVmaMutexLockRead4566     VMA_CLASS_NO_COPY(VmaMutexLockRead)
4567 public:
4568     VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
4569         m_pMutex(useMutex ? &mutex : VMA_NULL)
4570     { if(m_pMutex) { m_pMutex->LockRead(); } }
~VmaMutexLockReadVmaMutexLockRead4571     ~VmaMutexLockRead() { if(m_pMutex) { m_pMutex->UnlockRead(); } }
4572 private:
4573     VMA_RW_MUTEX* m_pMutex;
4574 };
4575 
4576 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
4577 struct VmaMutexLockWrite
4578 {
VMA_CLASS_NO_COPYVmaMutexLockWrite4579     VMA_CLASS_NO_COPY(VmaMutexLockWrite)
4580 public:
4581     VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) :
4582         m_pMutex(useMutex ? &mutex : VMA_NULL)
4583     { if(m_pMutex) { m_pMutex->LockWrite(); } }
~VmaMutexLockWriteVmaMutexLockWrite4584     ~VmaMutexLockWrite() { if(m_pMutex) { m_pMutex->UnlockWrite(); } }
4585 private:
4586     VMA_RW_MUTEX* m_pMutex;
4587 };
4588 
4589 #if VMA_DEBUG_GLOBAL_MUTEX
4590     static VMA_MUTEX gDebugGlobalMutex;
4591     #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
4592 #else
4593     #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
4594 #endif
4595 
4596 // Minimum size of a free suballocation to register it in the free suballocation collection.
4597 static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
4598 
4599 /*
4600 Performs binary search and returns iterator to first element that is greater or
4601 equal to (key), according to comparison (cmp).
4602 
4603 Cmp should return true if first argument is less than second argument.
4604 
4605 Returned value is the found element, if present in the collection or place where
4606 new element with value (key) should be inserted.
4607 */
4608 template <typename CmpLess, typename IterT, typename KeyT>
VmaBinaryFindFirstNotLess(IterT beg,IterT end,const KeyT & key,const CmpLess & cmp)4609 static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, const CmpLess& cmp)
4610 {
4611     size_t down = 0, up = (end - beg);
4612     while(down < up)
4613     {
4614         const size_t mid = (down + up) / 2;
4615         if(cmp(*(beg+mid), key))
4616         {
4617             down = mid + 1;
4618         }
4619         else
4620         {
4621             up = mid;
4622         }
4623     }
4624     return beg + down;
4625 }
4626 
4627 template<typename CmpLess, typename IterT, typename KeyT>
VmaBinaryFindSorted(const IterT & beg,const IterT & end,const KeyT & value,const CmpLess & cmp)4628 IterT VmaBinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp)
4629 {
4630     IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
4631         beg, end, value, cmp);
4632     if(it == end ||
4633         (!cmp(*it, value) && !cmp(value, *it)))
4634     {
4635         return it;
4636     }
4637     return end;
4638 }
4639 
4640 /*
4641 Returns true if all pointers in the array are not-null and unique.
4642 Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
4643 T must be pointer type, e.g. VmaAllocation, VmaPool.
4644 */
4645 template<typename T>
VmaValidatePointerArray(uint32_t count,const T * arr)4646 static bool VmaValidatePointerArray(uint32_t count, const T* arr)
4647 {
4648     for(uint32_t i = 0; i < count; ++i)
4649     {
4650         const T iPtr = arr[i];
4651         if(iPtr == VMA_NULL)
4652         {
4653             return false;
4654         }
4655         for(uint32_t j = i + 1; j < count; ++j)
4656         {
4657             if(iPtr == arr[j])
4658             {
4659                 return false;
4660             }
4661         }
4662     }
4663     return true;
4664 }
4665 
4666 template<typename MainT, typename NewT>
VmaPnextChainPushFront(MainT * mainStruct,NewT * newStruct)4667 static inline void VmaPnextChainPushFront(MainT* mainStruct, NewT* newStruct)
4668 {
4669     newStruct->pNext = mainStruct->pNext;
4670     mainStruct->pNext = newStruct;
4671 }
4672 
4673 ////////////////////////////////////////////////////////////////////////////////
4674 // Memory allocation
4675 
VmaMalloc(const VkAllocationCallbacks * pAllocationCallbacks,size_t size,size_t alignment)4676 static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
4677 {
4678     if((pAllocationCallbacks != VMA_NULL) &&
4679         (pAllocationCallbacks->pfnAllocation != VMA_NULL))
4680     {
4681         return (*pAllocationCallbacks->pfnAllocation)(
4682             pAllocationCallbacks->pUserData,
4683             size,
4684             alignment,
4685             VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
4686     }
4687     else
4688     {
4689         return VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
4690     }
4691 }
4692 
VmaFree(const VkAllocationCallbacks * pAllocationCallbacks,void * ptr)4693 static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
4694 {
4695     if((pAllocationCallbacks != VMA_NULL) &&
4696         (pAllocationCallbacks->pfnFree != VMA_NULL))
4697     {
4698         (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
4699     }
4700     else
4701     {
4702         VMA_SYSTEM_FREE(ptr);
4703     }
4704 }
4705 
4706 template<typename T>
VmaAllocate(const VkAllocationCallbacks * pAllocationCallbacks)4707 static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
4708 {
4709     return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
4710 }
4711 
4712 template<typename T>
VmaAllocateArray(const VkAllocationCallbacks * pAllocationCallbacks,size_t count)4713 static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
4714 {
4715     return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
4716 }
4717 
4718 #define vma_new(allocator, type)   new(VmaAllocate<type>(allocator))(type)
4719 
4720 #define vma_new_array(allocator, type, count)   new(VmaAllocateArray<type>((allocator), (count)))(type)
4721 
4722 template<typename T>
vma_delete(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr)4723 static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
4724 {
4725     ptr->~T();
4726     VmaFree(pAllocationCallbacks, ptr);
4727 }
4728 
4729 template<typename T>
vma_delete_array(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr,size_t count)4730 static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
4731 {
4732     if(ptr != VMA_NULL)
4733     {
4734         for(size_t i = count; i--; )
4735         {
4736             ptr[i].~T();
4737         }
4738         VmaFree(pAllocationCallbacks, ptr);
4739     }
4740 }
4741 
VmaCreateStringCopy(const VkAllocationCallbacks * allocs,const char * srcStr)4742 static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr)
4743 {
4744     if(srcStr != VMA_NULL)
4745     {
4746         const size_t len = strlen(srcStr);
4747         char* const result = vma_new_array(allocs, char, len + 1);
4748         memcpy(result, srcStr, len + 1);
4749         return result;
4750     }
4751     else
4752     {
4753         return VMA_NULL;
4754     }
4755 }
4756 
VmaFreeString(const VkAllocationCallbacks * allocs,char * str)4757 static void VmaFreeString(const VkAllocationCallbacks* allocs, char* str)
4758 {
4759     if(str != VMA_NULL)
4760     {
4761         const size_t len = strlen(str);
4762         vma_delete_array(allocs, str, len + 1);
4763     }
4764 }
4765 
4766 // STL-compatible allocator.
4767 template<typename T>
4768 class VmaStlAllocator
4769 {
4770 public:
4771     const VkAllocationCallbacks* const m_pCallbacks;
4772     typedef T value_type;
4773 
VmaStlAllocator(const VkAllocationCallbacks * pCallbacks)4774     VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
VmaStlAllocator(const VmaStlAllocator<U> & src)4775     template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
4776 
allocate(size_t n)4777     T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
deallocate(T * p,size_t n)4778     void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
4779 
4780     template<typename U>
4781     bool operator==(const VmaStlAllocator<U>& rhs) const
4782     {
4783         return m_pCallbacks == rhs.m_pCallbacks;
4784     }
4785     template<typename U>
4786     bool operator!=(const VmaStlAllocator<U>& rhs) const
4787     {
4788         return m_pCallbacks != rhs.m_pCallbacks;
4789     }
4790 
4791     VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
4792 };
4793 
4794 #if VMA_USE_STL_VECTOR
4795 
4796 #define VmaVector std::vector
4797 
4798 template<typename T, typename allocatorT>
VmaVectorInsert(std::vector<T,allocatorT> & vec,size_t index,const T & item)4799 static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
4800 {
4801     vec.insert(vec.begin() + index, item);
4802 }
4803 
4804 template<typename T, typename allocatorT>
VmaVectorRemove(std::vector<T,allocatorT> & vec,size_t index)4805 static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
4806 {
4807     vec.erase(vec.begin() + index);
4808 }
4809 
4810 #else // #if VMA_USE_STL_VECTOR
4811 
4812 /* Class with interface compatible with subset of std::vector.
4813 T must be POD because constructors and destructors are not called and memcpy is
4814 used for these objects. */
4815 template<typename T, typename AllocatorT>
4816 class VmaVector
4817 {
4818 public:
4819     typedef T value_type;
4820 
VmaVector(const AllocatorT & allocator)4821     VmaVector(const AllocatorT& allocator) :
4822         m_Allocator(allocator),
4823         m_pArray(VMA_NULL),
4824         m_Count(0),
4825         m_Capacity(0)
4826     {
4827     }
4828 
VmaVector(size_t count,const AllocatorT & allocator)4829     VmaVector(size_t count, const AllocatorT& allocator) :
4830         m_Allocator(allocator),
4831         m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
4832         m_Count(count),
4833         m_Capacity(count)
4834     {
4835     }
4836 
4837     // This version of the constructor is here for compatibility with pre-C++14 std::vector.
4838     // value is unused.
VmaVector(size_t count,const T & value,const AllocatorT & allocator)4839     VmaVector(size_t count, const T& value, const AllocatorT& allocator)
4840         : VmaVector(count, allocator) {}
4841 
VmaVector(const VmaVector<T,AllocatorT> & src)4842     VmaVector(const VmaVector<T, AllocatorT>& src) :
4843         m_Allocator(src.m_Allocator),
4844         m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
4845         m_Count(src.m_Count),
4846         m_Capacity(src.m_Count)
4847     {
4848         if(m_Count != 0)
4849         {
4850             memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
4851         }
4852     }
4853 
~VmaVector()4854     ~VmaVector()
4855     {
4856         VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4857     }
4858 
4859     VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
4860     {
4861         if(&rhs != this)
4862         {
4863             resize(rhs.m_Count);
4864             if(m_Count != 0)
4865             {
4866                 memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
4867             }
4868         }
4869         return *this;
4870     }
4871 
empty()4872     bool empty() const { return m_Count == 0; }
size()4873     size_t size() const { return m_Count; }
data()4874     T* data() { return m_pArray; }
data()4875     const T* data() const { return m_pArray; }
4876 
4877     T& operator[](size_t index)
4878     {
4879         VMA_HEAVY_ASSERT(index < m_Count);
4880         return m_pArray[index];
4881     }
4882     const T& operator[](size_t index) const
4883     {
4884         VMA_HEAVY_ASSERT(index < m_Count);
4885         return m_pArray[index];
4886     }
4887 
front()4888     T& front()
4889     {
4890         VMA_HEAVY_ASSERT(m_Count > 0);
4891         return m_pArray[0];
4892     }
front()4893     const T& front() const
4894     {
4895         VMA_HEAVY_ASSERT(m_Count > 0);
4896         return m_pArray[0];
4897     }
back()4898     T& back()
4899     {
4900         VMA_HEAVY_ASSERT(m_Count > 0);
4901         return m_pArray[m_Count - 1];
4902     }
back()4903     const T& back() const
4904     {
4905         VMA_HEAVY_ASSERT(m_Count > 0);
4906         return m_pArray[m_Count - 1];
4907     }
4908 
4909     void reserve(size_t newCapacity, bool freeMemory = false)
4910     {
4911         newCapacity = VMA_MAX(newCapacity, m_Count);
4912 
4913         if((newCapacity < m_Capacity) && !freeMemory)
4914         {
4915             newCapacity = m_Capacity;
4916         }
4917 
4918         if(newCapacity != m_Capacity)
4919         {
4920             T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
4921             if(m_Count != 0)
4922             {
4923                 memcpy(newArray, m_pArray, m_Count * sizeof(T));
4924             }
4925             VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4926             m_Capacity = newCapacity;
4927             m_pArray = newArray;
4928         }
4929     }
4930 
4931     void resize(size_t newCount, bool freeMemory = false)
4932     {
4933         size_t newCapacity = m_Capacity;
4934         if(newCount > m_Capacity)
4935         {
4936             newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
4937         }
4938         else if(freeMemory)
4939         {
4940             newCapacity = newCount;
4941         }
4942 
4943         if(newCapacity != m_Capacity)
4944         {
4945             T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
4946             const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
4947             if(elementsToCopy != 0)
4948             {
4949                 memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
4950             }
4951             VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4952             m_Capacity = newCapacity;
4953             m_pArray = newArray;
4954         }
4955 
4956         m_Count = newCount;
4957     }
4958 
4959     void clear(bool freeMemory = false)
4960     {
4961         resize(0, freeMemory);
4962     }
4963 
insert(size_t index,const T & src)4964     void insert(size_t index, const T& src)
4965     {
4966         VMA_HEAVY_ASSERT(index <= m_Count);
4967         const size_t oldCount = size();
4968         resize(oldCount + 1);
4969         if(index < oldCount)
4970         {
4971             memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
4972         }
4973         m_pArray[index] = src;
4974     }
4975 
remove(size_t index)4976     void remove(size_t index)
4977     {
4978         VMA_HEAVY_ASSERT(index < m_Count);
4979         const size_t oldCount = size();
4980         if(index < oldCount - 1)
4981         {
4982             memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
4983         }
4984         resize(oldCount - 1);
4985     }
4986 
push_back(const T & src)4987     void push_back(const T& src)
4988     {
4989         const size_t newIndex = size();
4990         resize(newIndex + 1);
4991         m_pArray[newIndex] = src;
4992     }
4993 
pop_back()4994     void pop_back()
4995     {
4996         VMA_HEAVY_ASSERT(m_Count > 0);
4997         resize(size() - 1);
4998     }
4999 
push_front(const T & src)5000     void push_front(const T& src)
5001     {
5002         insert(0, src);
5003     }
5004 
pop_front()5005     void pop_front()
5006     {
5007         VMA_HEAVY_ASSERT(m_Count > 0);
5008         remove(0);
5009     }
5010 
5011     typedef T* iterator;
5012 
begin()5013     iterator begin() { return m_pArray; }
end()5014     iterator end() { return m_pArray + m_Count; }
5015 
5016 private:
5017     AllocatorT m_Allocator;
5018     T* m_pArray;
5019     size_t m_Count;
5020     size_t m_Capacity;
5021 };
5022 
5023 template<typename T, typename allocatorT>
VmaVectorInsert(VmaVector<T,allocatorT> & vec,size_t index,const T & item)5024 static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
5025 {
5026     vec.insert(index, item);
5027 }
5028 
5029 template<typename T, typename allocatorT>
VmaVectorRemove(VmaVector<T,allocatorT> & vec,size_t index)5030 static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
5031 {
5032     vec.remove(index);
5033 }
5034 
5035 #endif // #if VMA_USE_STL_VECTOR
5036 
5037 template<typename CmpLess, typename VectorT>
VmaVectorInsertSorted(VectorT & vector,const typename VectorT::value_type & value)5038 size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
5039 {
5040     const size_t indexToInsert = VmaBinaryFindFirstNotLess(
5041         vector.data(),
5042         vector.data() + vector.size(),
5043         value,
5044         CmpLess()) - vector.data();
5045     VmaVectorInsert(vector, indexToInsert, value);
5046     return indexToInsert;
5047 }
5048 
5049 template<typename CmpLess, typename VectorT>
VmaVectorRemoveSorted(VectorT & vector,const typename VectorT::value_type & value)5050 bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
5051 {
5052     CmpLess comparator;
5053     typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
5054         vector.begin(),
5055         vector.end(),
5056         value,
5057         comparator);
5058     if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
5059     {
5060         size_t indexToRemove = it - vector.begin();
5061         VmaVectorRemove(vector, indexToRemove);
5062         return true;
5063     }
5064     return false;
5065 }
5066 
5067 ////////////////////////////////////////////////////////////////////////////////
5068 // class VmaSmallVector
5069 
5070 /*
5071 This is a vector (a variable-sized array), optimized for the case when the array is small.
5072 
5073 It contains some number of elements in-place, which allows it to avoid heap allocation
5074 when the actual number of elements is below that threshold. This allows normal "small"
5075 cases to be fast without losing generality for large inputs.
5076 */
5077 
5078 template<typename T, typename AllocatorT, size_t N>
5079 class VmaSmallVector
5080 {
5081 public:
5082     typedef T value_type;
5083 
VmaSmallVector(const AllocatorT & allocator)5084     VmaSmallVector(const AllocatorT& allocator) :
5085         m_Count(0),
5086         m_DynamicArray(allocator)
5087     {
5088     }
VmaSmallVector(size_t count,const AllocatorT & allocator)5089     VmaSmallVector(size_t count, const AllocatorT& allocator) :
5090         m_Count(count),
5091         m_DynamicArray(count > N ? count : 0, allocator)
5092     {
5093     }
5094     template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
5095     VmaSmallVector(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>& src) = delete;
5096     template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
5097     VmaSmallVector<T, AllocatorT, N>& operator=(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>& rhs) = delete;
5098 
empty()5099     bool empty() const { return m_Count == 0; }
size()5100     size_t size() const { return m_Count; }
data()5101     T* data() { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
data()5102     const T* data() const { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
5103 
5104     T& operator[](size_t index)
5105     {
5106         VMA_HEAVY_ASSERT(index < m_Count);
5107         return data()[index];
5108     }
5109     const T& operator[](size_t index) const
5110     {
5111         VMA_HEAVY_ASSERT(index < m_Count);
5112         return data()[index];
5113     }
5114 
front()5115     T& front()
5116     {
5117         VMA_HEAVY_ASSERT(m_Count > 0);
5118         return data()[0];
5119     }
front()5120     const T& front() const
5121     {
5122         VMA_HEAVY_ASSERT(m_Count > 0);
5123         return data()[0];
5124     }
back()5125     T& back()
5126     {
5127         VMA_HEAVY_ASSERT(m_Count > 0);
5128         return data()[m_Count - 1];
5129     }
back()5130     const T& back() const
5131     {
5132         VMA_HEAVY_ASSERT(m_Count > 0);
5133         return data()[m_Count - 1];
5134     }
5135 
5136     void resize(size_t newCount, bool freeMemory = false)
5137     {
5138         if(newCount > N && m_Count > N)
5139         {
5140             // Any direction, staying in m_DynamicArray
5141             m_DynamicArray.resize(newCount, freeMemory);
5142         }
5143         else if(newCount > N && m_Count <= N)
5144         {
5145             // Growing, moving from m_StaticArray to m_DynamicArray
5146             m_DynamicArray.resize(newCount, freeMemory);
5147             if(m_Count > 0)
5148             {
5149                 memcpy(m_DynamicArray.data(), m_StaticArray, m_Count * sizeof(T));
5150             }
5151         }
5152         else if(newCount <= N && m_Count > N)
5153         {
5154             // Shrinking, moving from m_DynamicArray to m_StaticArray
5155             if(newCount > 0)
5156             {
5157                 memcpy(m_StaticArray, m_DynamicArray.data(), newCount * sizeof(T));
5158             }
5159             m_DynamicArray.resize(0, freeMemory);
5160         }
5161         else
5162         {
5163             // Any direction, staying in m_StaticArray - nothing to do here
5164         }
5165         m_Count = newCount;
5166     }
5167 
5168     void clear(bool freeMemory = false)
5169     {
5170         m_DynamicArray.clear(freeMemory);
5171         m_Count = 0;
5172     }
5173 
insert(size_t index,const T & src)5174     void insert(size_t index, const T& src)
5175     {
5176         VMA_HEAVY_ASSERT(index <= m_Count);
5177         const size_t oldCount = size();
5178         resize(oldCount + 1);
5179         T* const dataPtr = data();
5180         if(index < oldCount)
5181         {
5182             //  I know, this could be more optimal for case where memmove can be memcpy directly from m_StaticArray to m_DynamicArray.
5183             memmove(dataPtr + (index + 1), dataPtr + index, (oldCount - index) * sizeof(T));
5184         }
5185         dataPtr[index] = src;
5186     }
5187 
remove(size_t index)5188     void remove(size_t index)
5189     {
5190         VMA_HEAVY_ASSERT(index < m_Count);
5191         const size_t oldCount = size();
5192         if(index < oldCount - 1)
5193         {
5194             //  I know, this could be more optimal for case where memmove can be memcpy directly from m_DynamicArray to m_StaticArray.
5195             T* const dataPtr = data();
5196             memmove(dataPtr + index, dataPtr + (index + 1), (oldCount - index - 1) * sizeof(T));
5197         }
5198         resize(oldCount - 1);
5199     }
5200 
push_back(const T & src)5201     void push_back(const T& src)
5202     {
5203         const size_t newIndex = size();
5204         resize(newIndex + 1);
5205         data()[newIndex] = src;
5206     }
5207 
pop_back()5208     void pop_back()
5209     {
5210         VMA_HEAVY_ASSERT(m_Count > 0);
5211         resize(size() - 1);
5212     }
5213 
push_front(const T & src)5214     void push_front(const T& src)
5215     {
5216         insert(0, src);
5217     }
5218 
pop_front()5219     void pop_front()
5220     {
5221         VMA_HEAVY_ASSERT(m_Count > 0);
5222         remove(0);
5223     }
5224 
5225     typedef T* iterator;
5226 
begin()5227     iterator begin() { return data(); }
end()5228     iterator end() { return data() + m_Count; }
5229 
5230 private:
5231     size_t m_Count;
5232     T m_StaticArray[N]; // Used when m_Size <= N
5233     VmaVector<T, AllocatorT> m_DynamicArray; // Used when m_Size > N
5234 };
5235 
5236 ////////////////////////////////////////////////////////////////////////////////
5237 // class VmaPoolAllocator
5238 
5239 /*
5240 Allocator for objects of type T using a list of arrays (pools) to speed up
5241 allocation. Number of elements that can be allocated is not bounded because
5242 allocator can create multiple blocks.
5243 */
5244 template<typename T>
5245 class VmaPoolAllocator
5246 {
5247     VMA_CLASS_NO_COPY(VmaPoolAllocator)
5248 public:
5249     VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity);
5250     ~VmaPoolAllocator();
5251     template<typename... Types> T* Alloc(Types... args);
5252     void Free(T* ptr);
5253 
5254 private:
5255     union Item
5256     {
5257         uint32_t NextFreeIndex;
5258         alignas(T) char Value[sizeof(T)];
5259     };
5260 
5261     struct ItemBlock
5262     {
5263         Item* pItems;
5264         uint32_t Capacity;
5265         uint32_t FirstFreeIndex;
5266     };
5267 
5268     const VkAllocationCallbacks* m_pAllocationCallbacks;
5269     const uint32_t m_FirstBlockCapacity;
5270     VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
5271 
5272     ItemBlock& CreateNewBlock();
5273 };
5274 
5275 template<typename T>
VmaPoolAllocator(const VkAllocationCallbacks * pAllocationCallbacks,uint32_t firstBlockCapacity)5276 VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity) :
5277     m_pAllocationCallbacks(pAllocationCallbacks),
5278     m_FirstBlockCapacity(firstBlockCapacity),
5279     m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
5280 {
5281     VMA_ASSERT(m_FirstBlockCapacity > 1);
5282 }
5283 
5284 template<typename T>
~VmaPoolAllocator()5285 VmaPoolAllocator<T>::~VmaPoolAllocator()
5286 {
5287     for(size_t i = m_ItemBlocks.size(); i--; )
5288         vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity);
5289     m_ItemBlocks.clear();
5290 }
5291 
5292 template<typename T>
Alloc(Types...args)5293 template<typename... Types> T* VmaPoolAllocator<T>::Alloc(Types... args)
5294 {
5295     for(size_t i = m_ItemBlocks.size(); i--; )
5296     {
5297         ItemBlock& block = m_ItemBlocks[i];
5298         // This block has some free items: Use first one.
5299         if(block.FirstFreeIndex != UINT32_MAX)
5300         {
5301             Item* const pItem = &block.pItems[block.FirstFreeIndex];
5302             block.FirstFreeIndex = pItem->NextFreeIndex;
5303             T* result = (T*)&pItem->Value;
5304             new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
5305             return result;
5306         }
5307     }
5308 
5309     // No block has free item: Create new one and use it.
5310     ItemBlock& newBlock = CreateNewBlock();
5311     Item* const pItem = &newBlock.pItems[0];
5312     newBlock.FirstFreeIndex = pItem->NextFreeIndex;
5313     T* result = (T*)&pItem->Value;
5314     new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
5315     return result;
5316 }
5317 
5318 template<typename T>
Free(T * ptr)5319 void VmaPoolAllocator<T>::Free(T* ptr)
5320 {
5321     // Search all memory blocks to find ptr.
5322     for(size_t i = m_ItemBlocks.size(); i--; )
5323     {
5324         ItemBlock& block = m_ItemBlocks[i];
5325 
5326         // Casting to union.
5327         Item* pItemPtr;
5328         memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
5329 
5330         // Check if pItemPtr is in address range of this block.
5331         if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity))
5332         {
5333             ptr->~T(); // Explicit destructor call.
5334             const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
5335             pItemPtr->NextFreeIndex = block.FirstFreeIndex;
5336             block.FirstFreeIndex = index;
5337             return;
5338         }
5339     }
5340     VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
5341 }
5342 
5343 template<typename T>
CreateNewBlock()5344 typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
5345 {
5346     const uint32_t newBlockCapacity = m_ItemBlocks.empty() ?
5347         m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2;
5348 
5349     const ItemBlock newBlock = {
5350         vma_new_array(m_pAllocationCallbacks, Item, newBlockCapacity),
5351         newBlockCapacity,
5352         0 };
5353 
5354     m_ItemBlocks.push_back(newBlock);
5355 
5356     // Setup singly-linked list of all free items in this block.
5357     for(uint32_t i = 0; i < newBlockCapacity - 1; ++i)
5358         newBlock.pItems[i].NextFreeIndex = i + 1;
5359     newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX;
5360     return m_ItemBlocks.back();
5361 }
5362 
5363 ////////////////////////////////////////////////////////////////////////////////
5364 // class VmaRawList, VmaList
5365 
5366 #if VMA_USE_STL_LIST
5367 
5368 #define VmaList std::list
5369 
5370 #else // #if VMA_USE_STL_LIST
5371 
5372 template<typename T>
5373 struct VmaListItem
5374 {
5375     VmaListItem* pPrev;
5376     VmaListItem* pNext;
5377     T Value;
5378 };
5379 
5380 // Doubly linked list.
5381 template<typename T>
5382 class VmaRawList
5383 {
5384     VMA_CLASS_NO_COPY(VmaRawList)
5385 public:
5386     typedef VmaListItem<T> ItemType;
5387 
5388     VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
5389     ~VmaRawList();
5390     void Clear();
5391 
GetCount()5392     size_t GetCount() const { return m_Count; }
IsEmpty()5393     bool IsEmpty() const { return m_Count == 0; }
5394 
Front()5395     ItemType* Front() { return m_pFront; }
Front()5396     const ItemType* Front() const { return m_pFront; }
Back()5397     ItemType* Back() { return m_pBack; }
Back()5398     const ItemType* Back() const { return m_pBack; }
5399 
5400     ItemType* PushBack();
5401     ItemType* PushFront();
5402     ItemType* PushBack(const T& value);
5403     ItemType* PushFront(const T& value);
5404     void PopBack();
5405     void PopFront();
5406 
5407     // Item can be null - it means PushBack.
5408     ItemType* InsertBefore(ItemType* pItem);
5409     // Item can be null - it means PushFront.
5410     ItemType* InsertAfter(ItemType* pItem);
5411 
5412     ItemType* InsertBefore(ItemType* pItem, const T& value);
5413     ItemType* InsertAfter(ItemType* pItem, const T& value);
5414 
5415     void Remove(ItemType* pItem);
5416 
5417 private:
5418     const VkAllocationCallbacks* const m_pAllocationCallbacks;
5419     VmaPoolAllocator<ItemType> m_ItemAllocator;
5420     ItemType* m_pFront;
5421     ItemType* m_pBack;
5422     size_t m_Count;
5423 };
5424 
5425 template<typename T>
VmaRawList(const VkAllocationCallbacks * pAllocationCallbacks)5426 VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
5427     m_pAllocationCallbacks(pAllocationCallbacks),
5428     m_ItemAllocator(pAllocationCallbacks, 128),
5429     m_pFront(VMA_NULL),
5430     m_pBack(VMA_NULL),
5431     m_Count(0)
5432 {
5433 }
5434 
5435 template<typename T>
~VmaRawList()5436 VmaRawList<T>::~VmaRawList()
5437 {
5438     // Intentionally not calling Clear, because that would be unnecessary
5439     // computations to return all items to m_ItemAllocator as free.
5440 }
5441 
5442 template<typename T>
Clear()5443 void VmaRawList<T>::Clear()
5444 {
5445     if(IsEmpty() == false)
5446     {
5447         ItemType* pItem = m_pBack;
5448         while(pItem != VMA_NULL)
5449         {
5450             ItemType* const pPrevItem = pItem->pPrev;
5451             m_ItemAllocator.Free(pItem);
5452             pItem = pPrevItem;
5453         }
5454         m_pFront = VMA_NULL;
5455         m_pBack = VMA_NULL;
5456         m_Count = 0;
5457     }
5458 }
5459 
5460 template<typename T>
PushBack()5461 VmaListItem<T>* VmaRawList<T>::PushBack()
5462 {
5463     ItemType* const pNewItem = m_ItemAllocator.Alloc();
5464     pNewItem->pNext = VMA_NULL;
5465     if(IsEmpty())
5466     {
5467         pNewItem->pPrev = VMA_NULL;
5468         m_pFront = pNewItem;
5469         m_pBack = pNewItem;
5470         m_Count = 1;
5471     }
5472     else
5473     {
5474         pNewItem->pPrev = m_pBack;
5475         m_pBack->pNext = pNewItem;
5476         m_pBack = pNewItem;
5477         ++m_Count;
5478     }
5479     return pNewItem;
5480 }
5481 
5482 template<typename T>
PushFront()5483 VmaListItem<T>* VmaRawList<T>::PushFront()
5484 {
5485     ItemType* const pNewItem = m_ItemAllocator.Alloc();
5486     pNewItem->pPrev = VMA_NULL;
5487     if(IsEmpty())
5488     {
5489         pNewItem->pNext = VMA_NULL;
5490         m_pFront = pNewItem;
5491         m_pBack = pNewItem;
5492         m_Count = 1;
5493     }
5494     else
5495     {
5496         pNewItem->pNext = m_pFront;
5497         m_pFront->pPrev = pNewItem;
5498         m_pFront = pNewItem;
5499         ++m_Count;
5500     }
5501     return pNewItem;
5502 }
5503 
5504 template<typename T>
PushBack(const T & value)5505 VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
5506 {
5507     ItemType* const pNewItem = PushBack();
5508     pNewItem->Value = value;
5509     return pNewItem;
5510 }
5511 
5512 template<typename T>
PushFront(const T & value)5513 VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
5514 {
5515     ItemType* const pNewItem = PushFront();
5516     pNewItem->Value = value;
5517     return pNewItem;
5518 }
5519 
5520 template<typename T>
PopBack()5521 void VmaRawList<T>::PopBack()
5522 {
5523     VMA_HEAVY_ASSERT(m_Count > 0);
5524     ItemType* const pBackItem = m_pBack;
5525     ItemType* const pPrevItem = pBackItem->pPrev;
5526     if(pPrevItem != VMA_NULL)
5527     {
5528         pPrevItem->pNext = VMA_NULL;
5529     }
5530     m_pBack = pPrevItem;
5531     m_ItemAllocator.Free(pBackItem);
5532     --m_Count;
5533 }
5534 
5535 template<typename T>
PopFront()5536 void VmaRawList<T>::PopFront()
5537 {
5538     VMA_HEAVY_ASSERT(m_Count > 0);
5539     ItemType* const pFrontItem = m_pFront;
5540     ItemType* const pNextItem = pFrontItem->pNext;
5541     if(pNextItem != VMA_NULL)
5542     {
5543         pNextItem->pPrev = VMA_NULL;
5544     }
5545     m_pFront = pNextItem;
5546     m_ItemAllocator.Free(pFrontItem);
5547     --m_Count;
5548 }
5549 
5550 template<typename T>
Remove(ItemType * pItem)5551 void VmaRawList<T>::Remove(ItemType* pItem)
5552 {
5553     VMA_HEAVY_ASSERT(pItem != VMA_NULL);
5554     VMA_HEAVY_ASSERT(m_Count > 0);
5555 
5556     if(pItem->pPrev != VMA_NULL)
5557     {
5558         pItem->pPrev->pNext = pItem->pNext;
5559     }
5560     else
5561     {
5562         VMA_HEAVY_ASSERT(m_pFront == pItem);
5563         m_pFront = pItem->pNext;
5564     }
5565 
5566     if(pItem->pNext != VMA_NULL)
5567     {
5568         pItem->pNext->pPrev = pItem->pPrev;
5569     }
5570     else
5571     {
5572         VMA_HEAVY_ASSERT(m_pBack == pItem);
5573         m_pBack = pItem->pPrev;
5574     }
5575 
5576     m_ItemAllocator.Free(pItem);
5577     --m_Count;
5578 }
5579 
5580 template<typename T>
InsertBefore(ItemType * pItem)5581 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
5582 {
5583     if(pItem != VMA_NULL)
5584     {
5585         ItemType* const prevItem = pItem->pPrev;
5586         ItemType* const newItem = m_ItemAllocator.Alloc();
5587         newItem->pPrev = prevItem;
5588         newItem->pNext = pItem;
5589         pItem->pPrev = newItem;
5590         if(prevItem != VMA_NULL)
5591         {
5592             prevItem->pNext = newItem;
5593         }
5594         else
5595         {
5596             VMA_HEAVY_ASSERT(m_pFront == pItem);
5597             m_pFront = newItem;
5598         }
5599         ++m_Count;
5600         return newItem;
5601     }
5602     else
5603         return PushBack();
5604 }
5605 
5606 template<typename T>
InsertAfter(ItemType * pItem)5607 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
5608 {
5609     if(pItem != VMA_NULL)
5610     {
5611         ItemType* const nextItem = pItem->pNext;
5612         ItemType* const newItem = m_ItemAllocator.Alloc();
5613         newItem->pNext = nextItem;
5614         newItem->pPrev = pItem;
5615         pItem->pNext = newItem;
5616         if(nextItem != VMA_NULL)
5617         {
5618             nextItem->pPrev = newItem;
5619         }
5620         else
5621         {
5622             VMA_HEAVY_ASSERT(m_pBack == pItem);
5623             m_pBack = newItem;
5624         }
5625         ++m_Count;
5626         return newItem;
5627     }
5628     else
5629         return PushFront();
5630 }
5631 
5632 template<typename T>
InsertBefore(ItemType * pItem,const T & value)5633 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
5634 {
5635     ItemType* const newItem = InsertBefore(pItem);
5636     newItem->Value = value;
5637     return newItem;
5638 }
5639 
5640 template<typename T>
InsertAfter(ItemType * pItem,const T & value)5641 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
5642 {
5643     ItemType* const newItem = InsertAfter(pItem);
5644     newItem->Value = value;
5645     return newItem;
5646 }
5647 
5648 template<typename T, typename AllocatorT>
5649 class VmaList
5650 {
VMA_CLASS_NO_COPY(VmaList)5651     VMA_CLASS_NO_COPY(VmaList)
5652 public:
5653     class iterator
5654     {
5655     public:
5656         iterator() :
5657             m_pList(VMA_NULL),
5658             m_pItem(VMA_NULL)
5659         {
5660         }
5661 
5662         T& operator*() const
5663         {
5664             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5665             return m_pItem->Value;
5666         }
5667         T* operator->() const
5668         {
5669             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5670             return &m_pItem->Value;
5671         }
5672 
5673         iterator& operator++()
5674         {
5675             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5676             m_pItem = m_pItem->pNext;
5677             return *this;
5678         }
5679         iterator& operator--()
5680         {
5681             if(m_pItem != VMA_NULL)
5682             {
5683                 m_pItem = m_pItem->pPrev;
5684             }
5685             else
5686             {
5687                 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
5688                 m_pItem = m_pList->Back();
5689             }
5690             return *this;
5691         }
5692 
5693         iterator operator++(int)
5694         {
5695             iterator result = *this;
5696             ++*this;
5697             return result;
5698         }
5699         iterator operator--(int)
5700         {
5701             iterator result = *this;
5702             --*this;
5703             return result;
5704         }
5705 
5706         bool operator==(const iterator& rhs) const
5707         {
5708             VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5709             return m_pItem == rhs.m_pItem;
5710         }
5711         bool operator!=(const iterator& rhs) const
5712         {
5713             VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5714             return m_pItem != rhs.m_pItem;
5715         }
5716 
5717     private:
5718         VmaRawList<T>* m_pList;
5719         VmaListItem<T>* m_pItem;
5720 
5721         iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
5722             m_pList(pList),
5723             m_pItem(pItem)
5724         {
5725         }
5726 
5727         friend class VmaList<T, AllocatorT>;
5728     };
5729 
5730     class const_iterator
5731     {
5732     public:
const_iterator()5733         const_iterator() :
5734             m_pList(VMA_NULL),
5735             m_pItem(VMA_NULL)
5736         {
5737         }
5738 
const_iterator(const iterator & src)5739         const_iterator(const iterator& src) :
5740             m_pList(src.m_pList),
5741             m_pItem(src.m_pItem)
5742         {
5743         }
5744 
5745         const T& operator*() const
5746         {
5747             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5748             return m_pItem->Value;
5749         }
5750         const T* operator->() const
5751         {
5752             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5753             return &m_pItem->Value;
5754         }
5755 
5756         const_iterator& operator++()
5757         {
5758             VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5759             m_pItem = m_pItem->pNext;
5760             return *this;
5761         }
5762         const_iterator& operator--()
5763         {
5764             if(m_pItem != VMA_NULL)
5765             {
5766                 m_pItem = m_pItem->pPrev;
5767             }
5768             else
5769             {
5770                 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
5771                 m_pItem = m_pList->Back();
5772             }
5773             return *this;
5774         }
5775 
5776         const_iterator operator++(int)
5777         {
5778             const_iterator result = *this;
5779             ++*this;
5780             return result;
5781         }
5782         const_iterator operator--(int)
5783         {
5784             const_iterator result = *this;
5785             --*this;
5786             return result;
5787         }
5788 
5789         bool operator==(const const_iterator& rhs) const
5790         {
5791             VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5792             return m_pItem == rhs.m_pItem;
5793         }
5794         bool operator!=(const const_iterator& rhs) const
5795         {
5796             VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5797             return m_pItem != rhs.m_pItem;
5798         }
5799 
5800     private:
const_iterator(const VmaRawList<T> * pList,const VmaListItem<T> * pItem)5801         const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
5802             m_pList(pList),
5803             m_pItem(pItem)
5804         {
5805         }
5806 
5807         const VmaRawList<T>* m_pList;
5808         const VmaListItem<T>* m_pItem;
5809 
5810         friend class VmaList<T, AllocatorT>;
5811     };
5812 
VmaList(const AllocatorT & allocator)5813     VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
5814 
empty()5815     bool empty() const { return m_RawList.IsEmpty(); }
size()5816     size_t size() const { return m_RawList.GetCount(); }
5817 
begin()5818     iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
end()5819     iterator end() { return iterator(&m_RawList, VMA_NULL); }
5820 
cbegin()5821     const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
cend()5822     const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
5823 
clear()5824     void clear() { m_RawList.Clear(); }
push_back(const T & value)5825     void push_back(const T& value) { m_RawList.PushBack(value); }
erase(iterator it)5826     void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
insert(iterator it,const T & value)5827     iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
5828 
5829 private:
5830     VmaRawList<T> m_RawList;
5831 };
5832 
5833 #endif // #if VMA_USE_STL_LIST
5834 
5835 ////////////////////////////////////////////////////////////////////////////////
5836 // class VmaMap
5837 
5838 // Unused in this version.
5839 #if 0
5840 
5841 #if VMA_USE_STL_UNORDERED_MAP
5842 
5843 #define VmaPair std::pair
5844 
5845 #define VMA_MAP_TYPE(KeyT, ValueT) \
5846     std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
5847 
5848 #else // #if VMA_USE_STL_UNORDERED_MAP
5849 
5850 template<typename T1, typename T2>
5851 struct VmaPair
5852 {
5853     T1 first;
5854     T2 second;
5855 
5856     VmaPair() : first(), second() { }
5857     VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
5858 };
5859 
5860 /* Class compatible with subset of interface of std::unordered_map.
5861 KeyT, ValueT must be POD because they will be stored in VmaVector.
5862 */
5863 template<typename KeyT, typename ValueT>
5864 class VmaMap
5865 {
5866 public:
5867     typedef VmaPair<KeyT, ValueT> PairType;
5868     typedef PairType* iterator;
5869 
5870     VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
5871 
5872     iterator begin() { return m_Vector.begin(); }
5873     iterator end() { return m_Vector.end(); }
5874 
5875     void insert(const PairType& pair);
5876     iterator find(const KeyT& key);
5877     void erase(iterator it);
5878 
5879 private:
5880     VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
5881 };
5882 
5883 #define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
5884 
5885 template<typename FirstT, typename SecondT>
5886 struct VmaPairFirstLess
5887 {
5888     bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
5889     {
5890         return lhs.first < rhs.first;
5891     }
5892     bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
5893     {
5894         return lhs.first < rhsFirst;
5895     }
5896 };
5897 
5898 template<typename KeyT, typename ValueT>
5899 void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
5900 {
5901     const size_t indexToInsert = VmaBinaryFindFirstNotLess(
5902         m_Vector.data(),
5903         m_Vector.data() + m_Vector.size(),
5904         pair,
5905         VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
5906     VmaVectorInsert(m_Vector, indexToInsert, pair);
5907 }
5908 
5909 template<typename KeyT, typename ValueT>
5910 VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
5911 {
5912     PairType* it = VmaBinaryFindFirstNotLess(
5913         m_Vector.data(),
5914         m_Vector.data() + m_Vector.size(),
5915         key,
5916         VmaPairFirstLess<KeyT, ValueT>());
5917     if((it != m_Vector.end()) && (it->first == key))
5918     {
5919         return it;
5920     }
5921     else
5922     {
5923         return m_Vector.end();
5924     }
5925 }
5926 
5927 template<typename KeyT, typename ValueT>
5928 void VmaMap<KeyT, ValueT>::erase(iterator it)
5929 {
5930     VmaVectorRemove(m_Vector, it - m_Vector.begin());
5931 }
5932 
5933 #endif // #if VMA_USE_STL_UNORDERED_MAP
5934 
5935 #endif // #if 0
5936 
5937 ////////////////////////////////////////////////////////////////////////////////
5938 
5939 class VmaDeviceMemoryBlock;
5940 
5941 enum VMA_CACHE_OPERATION { VMA_CACHE_FLUSH, VMA_CACHE_INVALIDATE };
5942 
5943 struct VmaAllocation_T
5944 {
5945 private:
5946     static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
5947 
5948     enum FLAGS
5949     {
5950         FLAG_USER_DATA_STRING = 0x01,
5951     };
5952 
5953 public:
5954     enum ALLOCATION_TYPE
5955     {
5956         ALLOCATION_TYPE_NONE,
5957         ALLOCATION_TYPE_BLOCK,
5958         ALLOCATION_TYPE_DEDICATED,
5959     };
5960 
5961     /*
5962     This struct is allocated using VmaPoolAllocator.
5963     */
5964 
VmaAllocation_TVmaAllocation_T5965     VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
5966         m_Alignment{1},
5967         m_Size{0},
5968         m_pUserData{VMA_NULL},
5969         m_LastUseFrameIndex{currentFrameIndex},
5970         m_MemoryTypeIndex{0},
5971         m_Type{(uint8_t)ALLOCATION_TYPE_NONE},
5972         m_SuballocationType{(uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN},
5973         m_MapCount{0},
5974         m_Flags{userDataString ? (uint8_t)FLAG_USER_DATA_STRING : (uint8_t)0},
5975         m_NewBlockFlag{false}
5976     {
5977 #if VMA_STATS_STRING_ENABLED
5978         m_CreationFrameIndex = currentFrameIndex;
5979         m_BufferImageUsage = 0;
5980 #endif
5981     }
5982 
~VmaAllocation_TVmaAllocation_T5983     ~VmaAllocation_T()
5984     {
5985         VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
5986 
5987         // Check if owned string was freed.
5988         VMA_ASSERT(m_pUserData == VMA_NULL);
5989     }
5990 
5991     void InitBlockAllocation(
5992         VmaDeviceMemoryBlock* block,
5993         VkDeviceSize offset,
5994         VkDeviceSize alignment,
5995         VkDeviceSize size,
5996         uint32_t memoryTypeIndex,
5997         VmaSuballocationType suballocationType,
5998         bool mapped,
5999         bool canBecomeLost,
6000         bool newBlockFlag = false)
6001     {
6002         VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
6003         VMA_ASSERT(block != VMA_NULL);
6004         m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
6005         m_Alignment = alignment;
6006         m_Size = size;
6007         m_MemoryTypeIndex = memoryTypeIndex;
6008         m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
6009         m_SuballocationType = (uint8_t)suballocationType;
6010         m_BlockAllocation.m_Block = block;
6011         m_BlockAllocation.m_Offset = offset;
6012         m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
6013         m_NewBlockFlag = newBlockFlag;
6014     }
6015 
InitLostVmaAllocation_T6016     void InitLost()
6017     {
6018         VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
6019         VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
6020         m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
6021         m_MemoryTypeIndex = 0;
6022         m_BlockAllocation.m_Block = VMA_NULL;
6023         m_BlockAllocation.m_Offset = 0;
6024         m_BlockAllocation.m_CanBecomeLost = true;
6025     }
6026 
6027     void ChangeBlockAllocation(
6028         VmaAllocator hAllocator,
6029         VmaDeviceMemoryBlock* block,
6030         VkDeviceSize offset);
6031 
6032     void ChangeOffset(VkDeviceSize newOffset);
6033 
6034     // pMappedData not null means allocation is created with MAPPED flag.
InitDedicatedAllocationVmaAllocation_T6035     void InitDedicatedAllocation(
6036         uint32_t memoryTypeIndex,
6037         VkDeviceMemory hMemory,
6038         VmaSuballocationType suballocationType,
6039         void* pMappedData,
6040         VkDeviceSize size)
6041     {
6042         VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
6043         VMA_ASSERT(hMemory != VK_NULL_HANDLE);
6044         m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
6045         m_Alignment = 0;
6046         m_Size = size;
6047         m_MemoryTypeIndex = memoryTypeIndex;
6048         m_SuballocationType = (uint8_t)suballocationType;
6049         m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
6050         m_DedicatedAllocation.m_hMemory = hMemory;
6051         m_DedicatedAllocation.m_pMappedData = pMappedData;
6052     }
6053 
GetTypeVmaAllocation_T6054     ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
GetAlignmentVmaAllocation_T6055     VkDeviceSize GetAlignment() const { return m_Alignment; }
GetSizeVmaAllocation_T6056     VkDeviceSize GetSize() const { return m_Size; }
IsUserDataStringVmaAllocation_T6057     bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
GetUserDataVmaAllocation_T6058     void* GetUserData() const { return m_pUserData; }
6059     void SetUserData(VmaAllocator hAllocator, void* pUserData);
GetSuballocationTypeVmaAllocation_T6060     VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
IsNewBlockFlagVmaAllocation_T6061     bool IsNewBlockFlag() const { return m_NewBlockFlag; }
ClearNewBlockFlagVmaAllocation_T6062     void ClearNewBlockFlag() { m_NewBlockFlag = false; }
6063 
GetBlockVmaAllocation_T6064     VmaDeviceMemoryBlock* GetBlock() const
6065     {
6066         VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
6067         return m_BlockAllocation.m_Block;
6068     }
6069     VkDeviceSize GetOffset() const;
6070     VkDeviceMemory GetMemory() const;
GetMemoryTypeIndexVmaAllocation_T6071     uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
IsPersistentMapVmaAllocation_T6072     bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
6073     void* GetMappedData() const;
6074     bool CanBecomeLost() const;
6075 
GetLastUseFrameIndexVmaAllocation_T6076     uint32_t GetLastUseFrameIndex() const
6077     {
6078         return m_LastUseFrameIndex.load();
6079     }
CompareExchangeLastUseFrameIndexVmaAllocation_T6080     bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
6081     {
6082         return m_LastUseFrameIndex.compare_exchange_weak(expected, desired);
6083     }
6084     /*
6085     - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
6086       makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
6087     - Else, returns false.
6088 
6089     If hAllocation is already lost, assert - you should not call it then.
6090     If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
6091     */
6092     bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6093 
DedicatedAllocCalcStatsInfoVmaAllocation_T6094     void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
6095     {
6096         VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
6097         outInfo.blockCount = 1;
6098         outInfo.allocationCount = 1;
6099         outInfo.unusedRangeCount = 0;
6100         outInfo.usedBytes = m_Size;
6101         outInfo.unusedBytes = 0;
6102         outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
6103         outInfo.unusedRangeSizeMin = UINT64_MAX;
6104         outInfo.unusedRangeSizeMax = 0;
6105     }
6106 
6107     void BlockAllocMap();
6108     void BlockAllocUnmap();
6109     VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
6110     void DedicatedAllocUnmap(VmaAllocator hAllocator);
6111 
6112 #if VMA_STATS_STRING_ENABLED
GetCreationFrameIndexVmaAllocation_T6113     uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; }
GetBufferImageUsageVmaAllocation_T6114     uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
6115 
InitBufferImageUsageVmaAllocation_T6116     void InitBufferImageUsage(uint32_t bufferImageUsage)
6117     {
6118         VMA_ASSERT(m_BufferImageUsage == 0);
6119         m_BufferImageUsage = bufferImageUsage;
6120     }
6121 
6122     void PrintParameters(class VmaJsonWriter& json) const;
6123 #endif
6124 
6125 private:
6126     VkDeviceSize m_Alignment;
6127     VkDeviceSize m_Size;
6128     void* m_pUserData;
6129     VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
6130     uint32_t m_MemoryTypeIndex;
6131     uint8_t m_Type; // ALLOCATION_TYPE
6132     uint8_t m_SuballocationType; // VmaSuballocationType
6133     // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
6134     // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
6135     uint8_t m_MapCount;
6136     uint8_t m_Flags; // enum FLAGS
6137     bool m_NewBlockFlag;
6138 
6139     // Allocation out of VmaDeviceMemoryBlock.
6140     struct BlockAllocation
6141     {
6142         VmaDeviceMemoryBlock* m_Block;
6143         VkDeviceSize m_Offset;
6144         bool m_CanBecomeLost;
6145     };
6146 
6147     // Allocation for an object that has its own private VkDeviceMemory.
6148     struct DedicatedAllocation
6149     {
6150         VkDeviceMemory m_hMemory;
6151         void* m_pMappedData; // Not null means memory is mapped.
6152     };
6153 
6154     union
6155     {
6156         // Allocation out of VmaDeviceMemoryBlock.
6157         BlockAllocation m_BlockAllocation;
6158         // Allocation for an object that has its own private VkDeviceMemory.
6159         DedicatedAllocation m_DedicatedAllocation;
6160     };
6161 
6162 #if VMA_STATS_STRING_ENABLED
6163     uint32_t m_CreationFrameIndex;
6164     uint32_t m_BufferImageUsage; // 0 if unknown.
6165 #endif
6166 
6167     void FreeUserDataString(VmaAllocator hAllocator);
6168 };
6169 
6170 /*
6171 Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
6172 allocated memory block or free.
6173 */
6174 struct VmaSuballocation
6175 {
6176     VkDeviceSize offset;
6177     VkDeviceSize size;
6178     VmaAllocation hAllocation;
6179     VmaSuballocationType type;
6180 };
6181 
6182 // Comparator for offsets.
6183 struct VmaSuballocationOffsetLess
6184 {
operatorVmaSuballocationOffsetLess6185     bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6186     {
6187         return lhs.offset < rhs.offset;
6188     }
6189 };
6190 struct VmaSuballocationOffsetGreater
6191 {
operatorVmaSuballocationOffsetGreater6192     bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6193     {
6194         return lhs.offset > rhs.offset;
6195     }
6196 };
6197 
6198 typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
6199 
6200 // Cost of one additional allocation lost, as equivalent in bytes.
6201 static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
6202 
6203 enum class VmaAllocationRequestType
6204 {
6205     Normal,
6206     // Used by "Linear" algorithm.
6207     UpperAddress,
6208     EndOf1st,
6209     EndOf2nd,
6210 };
6211 
6212 /*
6213 Parameters of planned allocation inside a VmaDeviceMemoryBlock.
6214 
6215 If canMakeOtherLost was false:
6216 - item points to a FREE suballocation.
6217 - itemsToMakeLostCount is 0.
6218 
6219 If canMakeOtherLost was true:
6220 - item points to first of sequence of suballocations, which are either FREE,
6221   or point to VmaAllocations that can become lost.
6222 - itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
6223   the requested allocation to succeed.
6224 */
6225 struct VmaAllocationRequest
6226 {
6227     VkDeviceSize offset;
6228     VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
6229     VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
6230     VmaSuballocationList::iterator item;
6231     size_t itemsToMakeLostCount;
6232     void* customData;
6233     VmaAllocationRequestType type;
6234 
CalcCostVmaAllocationRequest6235     VkDeviceSize CalcCost() const
6236     {
6237         return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
6238     }
6239 };
6240 
6241 /*
6242 Data structure used for bookkeeping of allocations and unused ranges of memory
6243 in a single VkDeviceMemory block.
6244 */
6245 class VmaBlockMetadata
6246 {
6247 public:
6248     VmaBlockMetadata(VmaAllocator hAllocator);
~VmaBlockMetadata()6249     virtual ~VmaBlockMetadata() { }
Init(VkDeviceSize size)6250     virtual void Init(VkDeviceSize size) { m_Size = size; }
6251 
6252     // Validates all data structures inside this object. If not valid, returns false.
6253     virtual bool Validate() const = 0;
GetSize()6254     VkDeviceSize GetSize() const { return m_Size; }
6255     virtual size_t GetAllocationCount() const = 0;
6256     virtual VkDeviceSize GetSumFreeSize() const = 0;
6257     virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0;
6258     // Returns true if this block is empty - contains only single free suballocation.
6259     virtual bool IsEmpty() const = 0;
6260 
6261     virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
6262     // Shouldn't modify blockCount.
6263     virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
6264 
6265 #if VMA_STATS_STRING_ENABLED
6266     virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
6267 #endif
6268 
6269     // Tries to find a place for suballocation with given parameters inside this block.
6270     // If succeeded, fills pAllocationRequest and returns true.
6271     // If failed, returns false.
6272     virtual bool CreateAllocationRequest(
6273         uint32_t currentFrameIndex,
6274         uint32_t frameInUseCount,
6275         VkDeviceSize bufferImageGranularity,
6276         VkDeviceSize allocSize,
6277         VkDeviceSize allocAlignment,
6278         bool upperAddress,
6279         VmaSuballocationType allocType,
6280         bool canMakeOtherLost,
6281         // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
6282         uint32_t strategy,
6283         VmaAllocationRequest* pAllocationRequest) = 0;
6284 
6285     virtual bool MakeRequestedAllocationsLost(
6286         uint32_t currentFrameIndex,
6287         uint32_t frameInUseCount,
6288         VmaAllocationRequest* pAllocationRequest) = 0;
6289 
6290     virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0;
6291 
6292     virtual VkResult CheckCorruption(const void* pBlockData) = 0;
6293 
6294     // Makes actual allocation based on request. Request must already be checked and valid.
6295     virtual void Alloc(
6296         const VmaAllocationRequest& request,
6297         VmaSuballocationType type,
6298         VkDeviceSize allocSize,
6299         VmaAllocation hAllocation) = 0;
6300 
6301     // Frees suballocation assigned to given memory region.
6302     virtual void Free(const VmaAllocation allocation) = 0;
6303     virtual void FreeAtOffset(VkDeviceSize offset) = 0;
6304 
6305 protected:
GetAllocationCallbacks()6306     const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
6307 
6308 #if VMA_STATS_STRING_ENABLED
6309     void PrintDetailedMap_Begin(class VmaJsonWriter& json,
6310         VkDeviceSize unusedBytes,
6311         size_t allocationCount,
6312         size_t unusedRangeCount) const;
6313     void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
6314         VkDeviceSize offset,
6315         VmaAllocation hAllocation) const;
6316     void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
6317         VkDeviceSize offset,
6318         VkDeviceSize size) const;
6319     void PrintDetailedMap_End(class VmaJsonWriter& json) const;
6320 #endif
6321 
6322 private:
6323     VkDeviceSize m_Size;
6324     const VkAllocationCallbacks* m_pAllocationCallbacks;
6325 };
6326 
6327 #define VMA_VALIDATE(cond) do { if(!(cond)) { \
6328         VMA_ASSERT(0 && "Validation failed: " #cond); \
6329         return false; \
6330     } } while(false)
6331 
6332 class VmaBlockMetadata_Generic : public VmaBlockMetadata
6333 {
6334     VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
6335 public:
6336     VmaBlockMetadata_Generic(VmaAllocator hAllocator);
6337     virtual ~VmaBlockMetadata_Generic();
6338     virtual void Init(VkDeviceSize size);
6339 
6340     virtual bool Validate() const;
GetAllocationCount()6341     virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
GetSumFreeSize()6342     virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
6343     virtual VkDeviceSize GetUnusedRangeSizeMax() const;
6344     virtual bool IsEmpty() const;
6345 
6346     virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6347     virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6348 
6349 #if VMA_STATS_STRING_ENABLED
6350     virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6351 #endif
6352 
6353     virtual bool CreateAllocationRequest(
6354         uint32_t currentFrameIndex,
6355         uint32_t frameInUseCount,
6356         VkDeviceSize bufferImageGranularity,
6357         VkDeviceSize allocSize,
6358         VkDeviceSize allocAlignment,
6359         bool upperAddress,
6360         VmaSuballocationType allocType,
6361         bool canMakeOtherLost,
6362         uint32_t strategy,
6363         VmaAllocationRequest* pAllocationRequest);
6364 
6365     virtual bool MakeRequestedAllocationsLost(
6366         uint32_t currentFrameIndex,
6367         uint32_t frameInUseCount,
6368         VmaAllocationRequest* pAllocationRequest);
6369 
6370     virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6371 
6372     virtual VkResult CheckCorruption(const void* pBlockData);
6373 
6374     virtual void Alloc(
6375         const VmaAllocationRequest& request,
6376         VmaSuballocationType type,
6377         VkDeviceSize allocSize,
6378         VmaAllocation hAllocation);
6379 
6380     virtual void Free(const VmaAllocation allocation);
6381     virtual void FreeAtOffset(VkDeviceSize offset);
6382 
6383     ////////////////////////////////////////////////////////////////////////////////
6384     // For defragmentation
6385 
6386     bool IsBufferImageGranularityConflictPossible(
6387         VkDeviceSize bufferImageGranularity,
6388         VmaSuballocationType& inOutPrevSuballocType) const;
6389 
6390 private:
6391     friend class VmaDefragmentationAlgorithm_Generic;
6392     friend class VmaDefragmentationAlgorithm_Fast;
6393 
6394     uint32_t m_FreeCount;
6395     VkDeviceSize m_SumFreeSize;
6396     VmaSuballocationList m_Suballocations;
6397     // Suballocations that are free and have size greater than certain threshold.
6398     // Sorted by size, ascending.
6399     VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
6400 
6401     bool ValidateFreeSuballocationList() const;
6402 
6403     // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
6404     // If yes, fills pOffset and returns true. If no, returns false.
6405     bool CheckAllocation(
6406         uint32_t currentFrameIndex,
6407         uint32_t frameInUseCount,
6408         VkDeviceSize bufferImageGranularity,
6409         VkDeviceSize allocSize,
6410         VkDeviceSize allocAlignment,
6411         VmaSuballocationType allocType,
6412         VmaSuballocationList::const_iterator suballocItem,
6413         bool canMakeOtherLost,
6414         VkDeviceSize* pOffset,
6415         size_t* itemsToMakeLostCount,
6416         VkDeviceSize* pSumFreeSize,
6417         VkDeviceSize* pSumItemSize) const;
6418     // Given free suballocation, it merges it with following one, which must also be free.
6419     void MergeFreeWithNext(VmaSuballocationList::iterator item);
6420     // Releases given suballocation, making it free.
6421     // Merges it with adjacent free suballocations if applicable.
6422     // Returns iterator to new free suballocation at this place.
6423     VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
6424     // Given free suballocation, it inserts it into sorted list of
6425     // m_FreeSuballocationsBySize if it's suitable.
6426     void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
6427     // Given free suballocation, it removes it from sorted list of
6428     // m_FreeSuballocationsBySize if it's suitable.
6429     void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
6430 };
6431 
6432 /*
6433 Allocations and their references in internal data structure look like this:
6434 
6435 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
6436 
6437         0 +-------+
6438           |       |
6439           |       |
6440           |       |
6441           +-------+
6442           | Alloc |  1st[m_1stNullItemsBeginCount]
6443           +-------+
6444           | Alloc |  1st[m_1stNullItemsBeginCount + 1]
6445           +-------+
6446           |  ...  |
6447           +-------+
6448           | Alloc |  1st[1st.size() - 1]
6449           +-------+
6450           |       |
6451           |       |
6452           |       |
6453 GetSize() +-------+
6454 
6455 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
6456 
6457         0 +-------+
6458           | Alloc |  2nd[0]
6459           +-------+
6460           | Alloc |  2nd[1]
6461           +-------+
6462           |  ...  |
6463           +-------+
6464           | Alloc |  2nd[2nd.size() - 1]
6465           +-------+
6466           |       |
6467           |       |
6468           |       |
6469           +-------+
6470           | Alloc |  1st[m_1stNullItemsBeginCount]
6471           +-------+
6472           | Alloc |  1st[m_1stNullItemsBeginCount + 1]
6473           +-------+
6474           |  ...  |
6475           +-------+
6476           | Alloc |  1st[1st.size() - 1]
6477           +-------+
6478           |       |
6479 GetSize() +-------+
6480 
6481 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
6482 
6483         0 +-------+
6484           |       |
6485           |       |
6486           |       |
6487           +-------+
6488           | Alloc |  1st[m_1stNullItemsBeginCount]
6489           +-------+
6490           | Alloc |  1st[m_1stNullItemsBeginCount + 1]
6491           +-------+
6492           |  ...  |
6493           +-------+
6494           | Alloc |  1st[1st.size() - 1]
6495           +-------+
6496           |       |
6497           |       |
6498           |       |
6499           +-------+
6500           | Alloc |  2nd[2nd.size() - 1]
6501           +-------+
6502           |  ...  |
6503           +-------+
6504           | Alloc |  2nd[1]
6505           +-------+
6506           | Alloc |  2nd[0]
6507 GetSize() +-------+
6508 
6509 */
6510 class VmaBlockMetadata_Linear : public VmaBlockMetadata
6511 {
6512     VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
6513 public:
6514     VmaBlockMetadata_Linear(VmaAllocator hAllocator);
6515     virtual ~VmaBlockMetadata_Linear();
6516     virtual void Init(VkDeviceSize size);
6517 
6518     virtual bool Validate() const;
6519     virtual size_t GetAllocationCount() const;
GetSumFreeSize()6520     virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
6521     virtual VkDeviceSize GetUnusedRangeSizeMax() const;
IsEmpty()6522     virtual bool IsEmpty() const { return GetAllocationCount() == 0; }
6523 
6524     virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6525     virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6526 
6527 #if VMA_STATS_STRING_ENABLED
6528     virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6529 #endif
6530 
6531     virtual bool CreateAllocationRequest(
6532         uint32_t currentFrameIndex,
6533         uint32_t frameInUseCount,
6534         VkDeviceSize bufferImageGranularity,
6535         VkDeviceSize allocSize,
6536         VkDeviceSize allocAlignment,
6537         bool upperAddress,
6538         VmaSuballocationType allocType,
6539         bool canMakeOtherLost,
6540         uint32_t strategy,
6541         VmaAllocationRequest* pAllocationRequest);
6542 
6543     virtual bool MakeRequestedAllocationsLost(
6544         uint32_t currentFrameIndex,
6545         uint32_t frameInUseCount,
6546         VmaAllocationRequest* pAllocationRequest);
6547 
6548     virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6549 
6550     virtual VkResult CheckCorruption(const void* pBlockData);
6551 
6552     virtual void Alloc(
6553         const VmaAllocationRequest& request,
6554         VmaSuballocationType type,
6555         VkDeviceSize allocSize,
6556         VmaAllocation hAllocation);
6557 
6558     virtual void Free(const VmaAllocation allocation);
6559     virtual void FreeAtOffset(VkDeviceSize offset);
6560 
6561 private:
6562     /*
6563     There are two suballocation vectors, used in ping-pong way.
6564     The one with index m_1stVectorIndex is called 1st.
6565     The one with index (m_1stVectorIndex ^ 1) is called 2nd.
6566     2nd can be non-empty only when 1st is not empty.
6567     When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
6568     */
6569     typedef VmaVector< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > SuballocationVectorType;
6570 
6571     enum SECOND_VECTOR_MODE
6572     {
6573         SECOND_VECTOR_EMPTY,
6574         /*
6575         Suballocations in 2nd vector are created later than the ones in 1st, but they
6576         all have smaller offset.
6577         */
6578         SECOND_VECTOR_RING_BUFFER,
6579         /*
6580         Suballocations in 2nd vector are upper side of double stack.
6581         They all have offsets higher than those in 1st vector.
6582         Top of this stack means smaller offsets, but higher indices in this vector.
6583         */
6584         SECOND_VECTOR_DOUBLE_STACK,
6585     };
6586 
6587     VkDeviceSize m_SumFreeSize;
6588     SuballocationVectorType m_Suballocations0, m_Suballocations1;
6589     uint32_t m_1stVectorIndex;
6590     SECOND_VECTOR_MODE m_2ndVectorMode;
6591 
AccessSuballocations1st()6592     SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
AccessSuballocations2nd()6593     SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
AccessSuballocations1st()6594     const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
AccessSuballocations2nd()6595     const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
6596 
6597     // Number of items in 1st vector with hAllocation = null at the beginning.
6598     size_t m_1stNullItemsBeginCount;
6599     // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
6600     size_t m_1stNullItemsMiddleCount;
6601     // Number of items in 2nd vector with hAllocation = null.
6602     size_t m_2ndNullItemsCount;
6603 
6604     bool ShouldCompact1st() const;
6605     void CleanupAfterFree();
6606 
6607     bool CreateAllocationRequest_LowerAddress(
6608         uint32_t currentFrameIndex,
6609         uint32_t frameInUseCount,
6610         VkDeviceSize bufferImageGranularity,
6611         VkDeviceSize allocSize,
6612         VkDeviceSize allocAlignment,
6613         VmaSuballocationType allocType,
6614         bool canMakeOtherLost,
6615         uint32_t strategy,
6616         VmaAllocationRequest* pAllocationRequest);
6617     bool CreateAllocationRequest_UpperAddress(
6618         uint32_t currentFrameIndex,
6619         uint32_t frameInUseCount,
6620         VkDeviceSize bufferImageGranularity,
6621         VkDeviceSize allocSize,
6622         VkDeviceSize allocAlignment,
6623         VmaSuballocationType allocType,
6624         bool canMakeOtherLost,
6625         uint32_t strategy,
6626         VmaAllocationRequest* pAllocationRequest);
6627 };
6628 
6629 /*
6630 - GetSize() is the original size of allocated memory block.
6631 - m_UsableSize is this size aligned down to a power of two.
6632   All allocations and calculations happen relative to m_UsableSize.
6633 - GetUnusableSize() is the difference between them.
6634   It is repoted as separate, unused range, not available for allocations.
6635 
6636 Node at level 0 has size = m_UsableSize.
6637 Each next level contains nodes with size 2 times smaller than current level.
6638 m_LevelCount is the maximum number of levels to use in the current object.
6639 */
6640 class VmaBlockMetadata_Buddy : public VmaBlockMetadata
6641 {
6642     VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
6643 public:
6644     VmaBlockMetadata_Buddy(VmaAllocator hAllocator);
6645     virtual ~VmaBlockMetadata_Buddy();
6646     virtual void Init(VkDeviceSize size);
6647 
6648     virtual bool Validate() const;
GetAllocationCount()6649     virtual size_t GetAllocationCount() const { return m_AllocationCount; }
GetSumFreeSize()6650     virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); }
6651     virtual VkDeviceSize GetUnusedRangeSizeMax() const;
IsEmpty()6652     virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; }
6653 
6654     virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6655     virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6656 
6657 #if VMA_STATS_STRING_ENABLED
6658     virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6659 #endif
6660 
6661     virtual bool CreateAllocationRequest(
6662         uint32_t currentFrameIndex,
6663         uint32_t frameInUseCount,
6664         VkDeviceSize bufferImageGranularity,
6665         VkDeviceSize allocSize,
6666         VkDeviceSize allocAlignment,
6667         bool upperAddress,
6668         VmaSuballocationType allocType,
6669         bool canMakeOtherLost,
6670         uint32_t strategy,
6671         VmaAllocationRequest* pAllocationRequest);
6672 
6673     virtual bool MakeRequestedAllocationsLost(
6674         uint32_t currentFrameIndex,
6675         uint32_t frameInUseCount,
6676         VmaAllocationRequest* pAllocationRequest);
6677 
6678     virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6679 
CheckCorruption(const void * pBlockData)6680     virtual VkResult CheckCorruption(const void* pBlockData) { return VK_ERROR_FEATURE_NOT_PRESENT; }
6681 
6682     virtual void Alloc(
6683         const VmaAllocationRequest& request,
6684         VmaSuballocationType type,
6685         VkDeviceSize allocSize,
6686         VmaAllocation hAllocation);
6687 
Free(const VmaAllocation allocation)6688     virtual void Free(const VmaAllocation allocation) { FreeAtOffset(allocation, allocation->GetOffset()); }
FreeAtOffset(VkDeviceSize offset)6689     virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VMA_NULL, offset); }
6690 
6691 private:
6692     static const VkDeviceSize MIN_NODE_SIZE = 32;
6693     static const size_t MAX_LEVELS = 30;
6694 
6695     struct ValidationContext
6696     {
6697         size_t calculatedAllocationCount;
6698         size_t calculatedFreeCount;
6699         VkDeviceSize calculatedSumFreeSize;
6700 
ValidationContextValidationContext6701         ValidationContext() :
6702             calculatedAllocationCount(0),
6703             calculatedFreeCount(0),
6704             calculatedSumFreeSize(0) { }
6705     };
6706 
6707     struct Node
6708     {
6709         VkDeviceSize offset;
6710         enum TYPE
6711         {
6712             TYPE_FREE,
6713             TYPE_ALLOCATION,
6714             TYPE_SPLIT,
6715             TYPE_COUNT
6716         } type;
6717         Node* parent;
6718         Node* buddy;
6719 
6720         union
6721         {
6722             struct
6723             {
6724                 Node* prev;
6725                 Node* next;
6726             } free;
6727             struct
6728             {
6729                 VmaAllocation alloc;
6730             } allocation;
6731             struct
6732             {
6733                 Node* leftChild;
6734             } split;
6735         };
6736     };
6737 
6738     // Size of the memory block aligned down to a power of two.
6739     VkDeviceSize m_UsableSize;
6740     uint32_t m_LevelCount;
6741 
6742     Node* m_Root;
6743     struct {
6744         Node* front;
6745         Node* back;
6746     } m_FreeList[MAX_LEVELS];
6747     // Number of nodes in the tree with type == TYPE_ALLOCATION.
6748     size_t m_AllocationCount;
6749     // Number of nodes in the tree with type == TYPE_FREE.
6750     size_t m_FreeCount;
6751     // This includes space wasted due to internal fragmentation. Doesn't include unusable size.
6752     VkDeviceSize m_SumFreeSize;
6753 
GetUnusableSize()6754     VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
6755     void DeleteNode(Node* node);
6756     bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
6757     uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
LevelToNodeSize(uint32_t level)6758     inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
6759     // Alloc passed just for validation. Can be null.
6760     void FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset);
6761     void CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const;
6762     // Adds node to the front of FreeList at given level.
6763     // node->type must be FREE.
6764     // node->free.prev, next can be undefined.
6765     void AddToFreeListFront(uint32_t level, Node* node);
6766     // Removes node from FreeList at given level.
6767     // node->type must be FREE.
6768     // node->free.prev, next stay untouched.
6769     void RemoveFromFreeList(uint32_t level, Node* node);
6770 
6771 #if VMA_STATS_STRING_ENABLED
6772     void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
6773 #endif
6774 };
6775 
6776 /*
6777 Represents a single block of device memory (`VkDeviceMemory`) with all the
6778 data about its regions (aka suballocations, #VmaAllocation), assigned and free.
6779 
6780 Thread-safety: This class must be externally synchronized.
6781 */
6782 class VmaDeviceMemoryBlock
6783 {
6784     VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
6785 public:
6786     VmaBlockMetadata* m_pMetadata;
6787 
6788     VmaDeviceMemoryBlock(VmaAllocator hAllocator);
6789 
~VmaDeviceMemoryBlock()6790     ~VmaDeviceMemoryBlock()
6791     {
6792         VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
6793         VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
6794     }
6795 
6796     // Always call after construction.
6797     void Init(
6798         VmaAllocator hAllocator,
6799         VmaPool hParentPool,
6800         uint32_t newMemoryTypeIndex,
6801         VkDeviceMemory newMemory,
6802         VkDeviceSize newSize,
6803         uint32_t id,
6804         uint32_t algorithm);
6805     // Always call before destruction.
6806     void Destroy(VmaAllocator allocator);
6807 
GetParentPool()6808     VmaPool GetParentPool() const { return m_hParentPool; }
GetDeviceMemory()6809     VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
GetMemoryTypeIndex()6810     uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetId()6811     uint32_t GetId() const { return m_Id; }
GetMappedData()6812     void* GetMappedData() const { return m_pMappedData; }
6813 
6814     // Validates all data structures inside this object. If not valid, returns false.
6815     bool Validate() const;
6816 
6817     VkResult CheckCorruption(VmaAllocator hAllocator);
6818 
6819     // ppData can be null.
6820     VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
6821     void Unmap(VmaAllocator hAllocator, uint32_t count);
6822 
6823     VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
6824     VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
6825 
6826     VkResult BindBufferMemory(
6827         const VmaAllocator hAllocator,
6828         const VmaAllocation hAllocation,
6829         VkDeviceSize allocationLocalOffset,
6830         VkBuffer hBuffer,
6831         const void* pNext);
6832     VkResult BindImageMemory(
6833         const VmaAllocator hAllocator,
6834         const VmaAllocation hAllocation,
6835         VkDeviceSize allocationLocalOffset,
6836         VkImage hImage,
6837         const void* pNext);
6838 
6839 private:
6840     VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
6841     uint32_t m_MemoryTypeIndex;
6842     uint32_t m_Id;
6843     VkDeviceMemory m_hMemory;
6844 
6845     /*
6846     Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
6847     Also protects m_MapCount, m_pMappedData.
6848     Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
6849     */
6850     VMA_MUTEX m_Mutex;
6851     uint32_t m_MapCount;
6852     void* m_pMappedData;
6853 };
6854 
6855 struct VmaPointerLess
6856 {
operatorVmaPointerLess6857     bool operator()(const void* lhs, const void* rhs) const
6858     {
6859         return lhs < rhs;
6860     }
6861 };
6862 
6863 struct VmaDefragmentationMove
6864 {
6865     size_t srcBlockIndex;
6866     size_t dstBlockIndex;
6867     VkDeviceSize srcOffset;
6868     VkDeviceSize dstOffset;
6869     VkDeviceSize size;
6870     VmaAllocation hAllocation;
6871     VmaDeviceMemoryBlock* pSrcBlock;
6872     VmaDeviceMemoryBlock* pDstBlock;
6873 };
6874 
6875 class VmaDefragmentationAlgorithm;
6876 
6877 /*
6878 Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
6879 Vulkan memory type.
6880 
6881 Synchronized internally with a mutex.
6882 */
6883 struct VmaBlockVector
6884 {
6885     VMA_CLASS_NO_COPY(VmaBlockVector)
6886 public:
6887     VmaBlockVector(
6888         VmaAllocator hAllocator,
6889         VmaPool hParentPool,
6890         uint32_t memoryTypeIndex,
6891         VkDeviceSize preferredBlockSize,
6892         size_t minBlockCount,
6893         size_t maxBlockCount,
6894         VkDeviceSize bufferImageGranularity,
6895         uint32_t frameInUseCount,
6896         bool explicitBlockSize,
6897         uint32_t algorithm);
6898     ~VmaBlockVector();
6899 
6900     VkResult CreateMinBlocks();
6901 
GetAllocatorVmaBlockVector6902     VmaAllocator GetAllocator() const { return m_hAllocator; }
GetParentPoolVmaBlockVector6903     VmaPool GetParentPool() const { return m_hParentPool; }
IsCustomPoolVmaBlockVector6904     bool IsCustomPool() const { return m_hParentPool != VMA_NULL; }
GetMemoryTypeIndexVmaBlockVector6905     uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetPreferredBlockSizeVmaBlockVector6906     VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
GetBufferImageGranularityVmaBlockVector6907     VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
GetFrameInUseCountVmaBlockVector6908     uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
GetAlgorithmVmaBlockVector6909     uint32_t GetAlgorithm() const { return m_Algorithm; }
6910 
6911     void GetPoolStats(VmaPoolStats* pStats);
6912 
6913     bool IsEmpty();
6914     bool IsCorruptionDetectionEnabled() const;
6915 
6916     VkResult Allocate(
6917         uint32_t currentFrameIndex,
6918         VkDeviceSize size,
6919         VkDeviceSize alignment,
6920         const VmaAllocationCreateInfo& createInfo,
6921         VmaSuballocationType suballocType,
6922         size_t allocationCount,
6923         VmaAllocation* pAllocations);
6924 
6925     // OH ISSUE: VMA preAlloc
6926     VkResult AllocateReserved(
6927         uint32_t currentFrameIndex,
6928         VkDeviceSize size,
6929         VkDeviceSize alignment,
6930         const VmaAllocationCreateInfo& createInfo,
6931         VmaSuballocationType suballocType,
6932         VmaAllocation* pAllocation);
6933 
6934     friend void SwapLastBlock(VmaBlockVector* blockVector1, VmaBlockVector* blockVector2);
6935 
6936     void Free(const VmaAllocation hAllocation);
6937 
6938     void FreeReserved(const VmaAllocation hAllocation);
6939 
6940     // Adds statistics of this BlockVector to pStats.
6941     void AddStats(VmaStats* pStats);
6942 
6943     void FreeEmptyBlock();
6944 
6945 #if VMA_STATS_STRING_ENABLED
6946     void PrintDetailedMap(class VmaJsonWriter& json);
6947 #endif
6948 
6949     void MakePoolAllocationsLost(
6950         uint32_t currentFrameIndex,
6951         size_t* pLostAllocationCount);
6952     VkResult CheckCorruption();
6953 
6954     // Saves results in pCtx->res.
6955     void Defragment(
6956         class VmaBlockVectorDefragmentationContext* pCtx,
6957         VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags,
6958         VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
6959         VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
6960         VkCommandBuffer commandBuffer);
6961     void DefragmentationEnd(
6962         class VmaBlockVectorDefragmentationContext* pCtx,
6963         uint32_t flags,
6964         VmaDefragmentationStats* pStats);
6965 
6966     uint32_t ProcessDefragmentations(
6967         class VmaBlockVectorDefragmentationContext *pCtx,
6968         VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves);
6969 
6970     void CommitDefragmentations(
6971         class VmaBlockVectorDefragmentationContext *pCtx,
6972         VmaDefragmentationStats* pStats);
6973 
6974     ////////////////////////////////////////////////////////////////////////////////
6975     // To be used only while the m_Mutex is locked. Used during defragmentation.
6976 
GetBlockCountVmaBlockVector6977     size_t GetBlockCount() const { return m_Blocks.size(); }
GetBlockVmaBlockVector6978     VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
6979     size_t CalcAllocationCount() const;
6980     bool IsBufferImageGranularityConflictPossible() const;
IsNewBlockFlagVmaBlockVector6981     bool IsNewBlockFlag() const { return m_NewBlockFlag; }
ClearNewBlockFlagVmaBlockVector6982     void ClearNewBlockFlag() { m_NewBlockFlag = false; }
6983 
6984 private:
6985     friend class VmaDefragmentationAlgorithm_Generic;
6986 
6987     const VmaAllocator m_hAllocator;
6988     const VmaPool m_hParentPool;
6989     const uint32_t m_MemoryTypeIndex;
6990     const VkDeviceSize m_PreferredBlockSize;
6991     const size_t m_MinBlockCount;
6992     const size_t m_MaxBlockCount;
6993     const VkDeviceSize m_BufferImageGranularity;
6994     const uint32_t m_FrameInUseCount;
6995     const bool m_ExplicitBlockSize;
6996     const uint32_t m_Algorithm;
6997     VMA_RW_MUTEX m_Mutex;
6998 
6999     /* There can be at most one allocation that is completely empty (except when minBlockCount > 0) -
7000     a hysteresis to avoid pessimistic case of alternating creation and destruction of a VkDeviceMemory. */
7001     bool m_HasEmptyBlock;
7002     // Incrementally sorted by sumFreeSize, ascending.
7003     VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
7004     uint32_t m_NextBlockId;
7005     bool m_NewBlockFlag = false;
7006 
7007     VkDeviceSize CalcMaxBlockSize() const;
7008 
7009     // Finds and removes given block from vector.
7010     void Remove(VmaDeviceMemoryBlock* pBlock);
7011 
7012     // Performs single step in sorting m_Blocks. They may not be fully sorted
7013     // after this call.
7014     void IncrementallySortBlocks();
7015 
7016     VkResult AllocatePage(
7017         uint32_t currentFrameIndex,
7018         VkDeviceSize size,
7019         VkDeviceSize alignment,
7020         const VmaAllocationCreateInfo& createInfo,
7021         VmaSuballocationType suballocType,
7022         VmaAllocation* pAllocation);
7023 
7024     // To be used only without CAN_MAKE_OTHER_LOST flag.
7025     VkResult AllocateFromBlock(
7026         VmaDeviceMemoryBlock* pBlock,
7027         uint32_t currentFrameIndex,
7028         VkDeviceSize size,
7029         VkDeviceSize alignment,
7030         VmaAllocationCreateFlags allocFlags,
7031         void* pUserData,
7032         VmaSuballocationType suballocType,
7033         uint32_t strategy,
7034         VmaAllocation* pAllocation);
7035 
7036     VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
7037 
7038     // Saves result to pCtx->res.
7039     void ApplyDefragmentationMovesCpu(
7040         class VmaBlockVectorDefragmentationContext* pDefragCtx,
7041         const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
7042     // Saves result to pCtx->res.
7043     void ApplyDefragmentationMovesGpu(
7044         class VmaBlockVectorDefragmentationContext* pDefragCtx,
7045         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7046         VkCommandBuffer commandBuffer);
7047 
7048     /*
7049     Used during defragmentation. pDefragmentationStats is optional. It's in/out
7050     - updated with new data.
7051     */
7052     void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
7053 
7054     void UpdateHasEmptyBlock();
7055 };
7056 
7057 struct VmaPool_T
7058 {
7059     VMA_CLASS_NO_COPY(VmaPool_T)
7060 public:
7061     VmaBlockVector m_BlockVector;
7062 
7063     VmaPool_T(
7064         VmaAllocator hAllocator,
7065         const VmaPoolCreateInfo& createInfo,
7066         VkDeviceSize preferredBlockSize);
7067     ~VmaPool_T();
7068 
GetIdVmaPool_T7069     uint32_t GetId() const { return m_Id; }
SetIdVmaPool_T7070     void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
7071 
GetNameVmaPool_T7072     const char* GetName() const { return m_Name; }
7073     void SetName(const char* pName);
7074 
7075 #if VMA_STATS_STRING_ENABLED
7076     //void PrintDetailedMap(class VmaStringBuilder& sb);
7077 #endif
7078 
7079 private:
7080     uint32_t m_Id;
7081     char* m_Name;
7082 };
7083 
7084 /*
7085 Performs defragmentation:
7086 
7087 - Updates `pBlockVector->m_pMetadata`.
7088 - Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
7089 - Does not move actual data, only returns requested moves as `moves`.
7090 */
7091 class VmaDefragmentationAlgorithm
7092 {
VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)7093     VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
7094 public:
7095     VmaDefragmentationAlgorithm(
7096         VmaAllocator hAllocator,
7097         VmaBlockVector* pBlockVector,
7098         uint32_t currentFrameIndex) :
7099         m_hAllocator(hAllocator),
7100         m_pBlockVector(pBlockVector),
7101         m_CurrentFrameIndex(currentFrameIndex)
7102     {
7103     }
~VmaDefragmentationAlgorithm()7104     virtual ~VmaDefragmentationAlgorithm()
7105     {
7106     }
7107 
7108     virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
7109     virtual void AddAll() = 0;
7110 
7111     virtual VkResult Defragment(
7112         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7113         VkDeviceSize maxBytesToMove,
7114         uint32_t maxAllocationsToMove,
7115         VmaDefragmentationFlags flags) = 0;
7116 
7117     virtual VkDeviceSize GetBytesMoved() const = 0;
7118     virtual uint32_t GetAllocationsMoved() const = 0;
7119 
7120 protected:
7121     VmaAllocator const m_hAllocator;
7122     VmaBlockVector* const m_pBlockVector;
7123     const uint32_t m_CurrentFrameIndex;
7124 
7125     struct AllocationInfo
7126     {
7127         VmaAllocation m_hAllocation;
7128         VkBool32* m_pChanged;
7129 
AllocationInfoAllocationInfo7130         AllocationInfo() :
7131             m_hAllocation(VK_NULL_HANDLE),
7132             m_pChanged(VMA_NULL)
7133         {
7134         }
AllocationInfoAllocationInfo7135         AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
7136             m_hAllocation(hAlloc),
7137             m_pChanged(pChanged)
7138         {
7139         }
7140     };
7141 };
7142 
7143 class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
7144 {
7145     VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
7146 public:
7147     VmaDefragmentationAlgorithm_Generic(
7148         VmaAllocator hAllocator,
7149         VmaBlockVector* pBlockVector,
7150         uint32_t currentFrameIndex,
7151         bool overlappingMoveSupported);
7152     virtual ~VmaDefragmentationAlgorithm_Generic();
7153 
7154     virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
AddAll()7155     virtual void AddAll() { m_AllAllocations = true; }
7156 
7157     virtual VkResult Defragment(
7158         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7159         VkDeviceSize maxBytesToMove,
7160         uint32_t maxAllocationsToMove,
7161         VmaDefragmentationFlags flags);
7162 
GetBytesMoved()7163     virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
GetAllocationsMoved()7164     virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
7165 
7166 private:
7167     uint32_t m_AllocationCount;
7168     bool m_AllAllocations;
7169 
7170     VkDeviceSize m_BytesMoved;
7171     uint32_t m_AllocationsMoved;
7172 
7173     struct AllocationInfoSizeGreater
7174     {
operatorAllocationInfoSizeGreater7175         bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
7176         {
7177             return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
7178         }
7179     };
7180 
7181     struct AllocationInfoOffsetGreater
7182     {
operatorAllocationInfoOffsetGreater7183         bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
7184         {
7185             return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
7186         }
7187     };
7188 
7189     struct BlockInfo
7190     {
7191         size_t m_OriginalBlockIndex;
7192         VmaDeviceMemoryBlock* m_pBlock;
7193         bool m_HasNonMovableAllocations;
7194         VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
7195 
BlockInfoBlockInfo7196         BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
7197             m_OriginalBlockIndex(SIZE_MAX),
7198             m_pBlock(VMA_NULL),
7199             m_HasNonMovableAllocations(true),
7200             m_Allocations(pAllocationCallbacks)
7201         {
7202         }
7203 
CalcHasNonMovableAllocationsBlockInfo7204         void CalcHasNonMovableAllocations()
7205         {
7206             const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
7207             const size_t defragmentAllocCount = m_Allocations.size();
7208             m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
7209         }
7210 
SortAllocationsBySizeDescendingBlockInfo7211         void SortAllocationsBySizeDescending()
7212         {
7213             VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
7214         }
7215 
SortAllocationsByOffsetDescendingBlockInfo7216         void SortAllocationsByOffsetDescending()
7217         {
7218             VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
7219         }
7220     };
7221 
7222     struct BlockPointerLess
7223     {
operatorBlockPointerLess7224         bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
7225         {
7226             return pLhsBlockInfo->m_pBlock < pRhsBlock;
7227         }
operatorBlockPointerLess7228         bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
7229         {
7230             return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
7231         }
7232     };
7233 
7234     // 1. Blocks with some non-movable allocations go first.
7235     // 2. Blocks with smaller sumFreeSize go first.
7236     struct BlockInfoCompareMoveDestination
7237     {
operatorBlockInfoCompareMoveDestination7238         bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
7239         {
7240             if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
7241             {
7242                 return true;
7243             }
7244             if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
7245             {
7246                 return false;
7247             }
7248             if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
7249             {
7250                 return true;
7251             }
7252             return false;
7253         }
7254     };
7255 
7256     typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
7257     BlockInfoVector m_Blocks;
7258 
7259     VkResult DefragmentRound(
7260         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7261         VkDeviceSize maxBytesToMove,
7262         uint32_t maxAllocationsToMove,
7263         bool freeOldAllocations);
7264 
7265     size_t CalcBlocksWithNonMovableCount() const;
7266 
7267     static bool MoveMakesSense(
7268         size_t dstBlockIndex, VkDeviceSize dstOffset,
7269         size_t srcBlockIndex, VkDeviceSize srcOffset);
7270 };
7271 
7272 class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
7273 {
7274     VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
7275 public:
7276     VmaDefragmentationAlgorithm_Fast(
7277         VmaAllocator hAllocator,
7278         VmaBlockVector* pBlockVector,
7279         uint32_t currentFrameIndex,
7280         bool overlappingMoveSupported);
7281     virtual ~VmaDefragmentationAlgorithm_Fast();
7282 
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)7283     virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { ++m_AllocationCount; }
AddAll()7284     virtual void AddAll() { m_AllAllocations = true; }
7285 
7286     virtual VkResult Defragment(
7287         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7288         VkDeviceSize maxBytesToMove,
7289         uint32_t maxAllocationsToMove,
7290         VmaDefragmentationFlags flags);
7291 
GetBytesMoved()7292     virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
GetAllocationsMoved()7293     virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
7294 
7295 private:
7296     struct BlockInfo
7297     {
7298         size_t origBlockIndex;
7299     };
7300 
7301     class FreeSpaceDatabase
7302     {
7303     public:
FreeSpaceDatabase()7304         FreeSpaceDatabase()
7305         {
7306             FreeSpace s = {};
7307             s.blockInfoIndex = SIZE_MAX;
7308             for(size_t i = 0; i < MAX_COUNT; ++i)
7309             {
7310                 m_FreeSpaces[i] = s;
7311             }
7312         }
7313 
Register(size_t blockInfoIndex,VkDeviceSize offset,VkDeviceSize size)7314         void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
7315         {
7316             if(size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7317             {
7318                 return;
7319             }
7320 
7321             // Find first invalid or the smallest structure.
7322             size_t bestIndex = SIZE_MAX;
7323             for(size_t i = 0; i < MAX_COUNT; ++i)
7324             {
7325                 // Empty structure.
7326                 if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
7327                 {
7328                     bestIndex = i;
7329                     break;
7330                 }
7331                 if(m_FreeSpaces[i].size < size &&
7332                     (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
7333                 {
7334                     bestIndex = i;
7335                 }
7336             }
7337 
7338             if(bestIndex != SIZE_MAX)
7339             {
7340                 m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
7341                 m_FreeSpaces[bestIndex].offset = offset;
7342                 m_FreeSpaces[bestIndex].size = size;
7343             }
7344         }
7345 
Fetch(VkDeviceSize alignment,VkDeviceSize size,size_t & outBlockInfoIndex,VkDeviceSize & outDstOffset)7346         bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
7347             size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
7348         {
7349             size_t bestIndex = SIZE_MAX;
7350             VkDeviceSize bestFreeSpaceAfter = 0;
7351             for(size_t i = 0; i < MAX_COUNT; ++i)
7352             {
7353                 // Structure is valid.
7354                 if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
7355                 {
7356                     const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment);
7357                     // Allocation fits into this structure.
7358                     if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
7359                     {
7360                         const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
7361                             (dstOffset + size);
7362                         if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
7363                         {
7364                             bestIndex = i;
7365                             bestFreeSpaceAfter = freeSpaceAfter;
7366                         }
7367                     }
7368                 }
7369             }
7370 
7371             if(bestIndex != SIZE_MAX)
7372             {
7373                 outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
7374                 outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment);
7375 
7376                 if(bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7377                 {
7378                     // Leave this structure for remaining empty space.
7379                     const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
7380                     m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
7381                     m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
7382                 }
7383                 else
7384                 {
7385                     // This structure becomes invalid.
7386                     m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX;
7387                 }
7388 
7389                 return true;
7390             }
7391 
7392             return false;
7393         }
7394 
7395     private:
7396         static const size_t MAX_COUNT = 4;
7397 
7398         struct FreeSpace
7399         {
7400             size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
7401             VkDeviceSize offset;
7402             VkDeviceSize size;
7403         } m_FreeSpaces[MAX_COUNT];
7404     };
7405 
7406     const bool m_OverlappingMoveSupported;
7407 
7408     uint32_t m_AllocationCount;
7409     bool m_AllAllocations;
7410 
7411     VkDeviceSize m_BytesMoved;
7412     uint32_t m_AllocationsMoved;
7413 
7414     VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
7415 
7416     void PreprocessMetadata();
7417     void PostprocessMetadata();
7418     void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
7419 };
7420 
7421 struct VmaBlockDefragmentationContext
7422 {
7423     enum BLOCK_FLAG
7424     {
7425         BLOCK_FLAG_USED = 0x00000001,
7426     };
7427     uint32_t flags;
7428     VkBuffer hBuffer;
7429 };
7430 
7431 class VmaBlockVectorDefragmentationContext
7432 {
7433     VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
7434 public:
7435     VkResult res;
7436     bool mutexLocked;
7437     VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
7438     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > defragmentationMoves;
7439     uint32_t defragmentationMovesProcessed;
7440     uint32_t defragmentationMovesCommitted;
7441     bool hasDefragmentationPlan;
7442 
7443     VmaBlockVectorDefragmentationContext(
7444         VmaAllocator hAllocator,
7445         VmaPool hCustomPool, // Optional.
7446         VmaBlockVector* pBlockVector,
7447         uint32_t currFrameIndex);
7448     ~VmaBlockVectorDefragmentationContext();
7449 
GetCustomPool()7450     VmaPool GetCustomPool() const { return m_hCustomPool; }
GetBlockVector()7451     VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
GetAlgorithm()7452     VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
7453 
7454     void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
AddAll()7455     void AddAll() { m_AllAllocations = true; }
7456 
7457     void Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags);
7458 
7459 private:
7460     const VmaAllocator m_hAllocator;
7461     // Null if not from custom pool.
7462     const VmaPool m_hCustomPool;
7463     // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
7464     VmaBlockVector* const m_pBlockVector;
7465     const uint32_t m_CurrFrameIndex;
7466     // Owner of this object.
7467     VmaDefragmentationAlgorithm* m_pAlgorithm;
7468 
7469     struct AllocInfo
7470     {
7471         VmaAllocation hAlloc;
7472         VkBool32* pChanged;
7473     };
7474     // Used between constructor and Begin.
7475     VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
7476     bool m_AllAllocations;
7477 };
7478 
7479 struct VmaDefragmentationContext_T
7480 {
7481 private:
7482     VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
7483 public:
7484     VmaDefragmentationContext_T(
7485         VmaAllocator hAllocator,
7486         uint32_t currFrameIndex,
7487         uint32_t flags,
7488         VmaDefragmentationStats* pStats);
7489     ~VmaDefragmentationContext_T();
7490 
7491     void AddPools(uint32_t poolCount, const VmaPool* pPools);
7492     void AddAllocations(
7493         uint32_t allocationCount,
7494         const VmaAllocation* pAllocations,
7495         VkBool32* pAllocationsChanged);
7496 
7497     /*
7498     Returns:
7499     - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
7500     - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
7501     - Negative value if error occured and object can be destroyed immediately.
7502     */
7503     VkResult Defragment(
7504         VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
7505         VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
7506         VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags);
7507 
7508     VkResult DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo);
7509     VkResult DefragmentPassEnd();
7510 
7511 private:
7512     const VmaAllocator m_hAllocator;
7513     const uint32_t m_CurrFrameIndex;
7514     const uint32_t m_Flags;
7515     VmaDefragmentationStats* const m_pStats;
7516 
7517     VkDeviceSize m_MaxCpuBytesToMove;
7518     uint32_t m_MaxCpuAllocationsToMove;
7519     VkDeviceSize m_MaxGpuBytesToMove;
7520     uint32_t m_MaxGpuAllocationsToMove;
7521 
7522     // Owner of these objects.
7523     VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
7524     // Owner of these objects.
7525     VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
7526 };
7527 
7528 #if VMA_RECORDING_ENABLED
7529 
7530 class VmaRecorder
7531 {
7532 public:
7533     VmaRecorder();
7534     VkResult Init(const VmaRecordSettings& settings, bool useMutex);
7535     void WriteConfiguration(
7536         const VkPhysicalDeviceProperties& devProps,
7537         const VkPhysicalDeviceMemoryProperties& memProps,
7538         uint32_t vulkanApiVersion,
7539         bool dedicatedAllocationExtensionEnabled,
7540         bool bindMemory2ExtensionEnabled,
7541         bool memoryBudgetExtensionEnabled,
7542         bool deviceCoherentMemoryExtensionEnabled);
7543     ~VmaRecorder();
7544 
7545     void RecordCreateAllocator(uint32_t frameIndex);
7546     void RecordDestroyAllocator(uint32_t frameIndex);
7547     void RecordCreatePool(uint32_t frameIndex,
7548         const VmaPoolCreateInfo& createInfo,
7549         VmaPool pool);
7550     void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
7551     void RecordAllocateMemory(uint32_t frameIndex,
7552         const VkMemoryRequirements& vkMemReq,
7553         const VmaAllocationCreateInfo& createInfo,
7554         VmaAllocation allocation);
7555     void RecordAllocateMemoryPages(uint32_t frameIndex,
7556         const VkMemoryRequirements& vkMemReq,
7557         const VmaAllocationCreateInfo& createInfo,
7558         uint64_t allocationCount,
7559         const VmaAllocation* pAllocations);
7560     void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
7561         const VkMemoryRequirements& vkMemReq,
7562         bool requiresDedicatedAllocation,
7563         bool prefersDedicatedAllocation,
7564         const VmaAllocationCreateInfo& createInfo,
7565         VmaAllocation allocation);
7566     void RecordAllocateMemoryForImage(uint32_t frameIndex,
7567         const VkMemoryRequirements& vkMemReq,
7568         bool requiresDedicatedAllocation,
7569         bool prefersDedicatedAllocation,
7570         const VmaAllocationCreateInfo& createInfo,
7571         VmaAllocation allocation);
7572     void RecordFreeMemory(uint32_t frameIndex,
7573         VmaAllocation allocation);
7574     void RecordFreeMemoryPages(uint32_t frameIndex,
7575         uint64_t allocationCount,
7576         const VmaAllocation* pAllocations);
7577     void RecordSetAllocationUserData(uint32_t frameIndex,
7578         VmaAllocation allocation,
7579         const void* pUserData);
7580     void RecordCreateLostAllocation(uint32_t frameIndex,
7581         VmaAllocation allocation);
7582     void RecordMapMemory(uint32_t frameIndex,
7583         VmaAllocation allocation);
7584     void RecordUnmapMemory(uint32_t frameIndex,
7585         VmaAllocation allocation);
7586     void RecordFlushAllocation(uint32_t frameIndex,
7587         VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
7588     void RecordInvalidateAllocation(uint32_t frameIndex,
7589         VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
7590     void RecordCreateBuffer(uint32_t frameIndex,
7591         const VkBufferCreateInfo& bufCreateInfo,
7592         const VmaAllocationCreateInfo& allocCreateInfo,
7593         VmaAllocation allocation);
7594     void RecordCreateImage(uint32_t frameIndex,
7595         const VkImageCreateInfo& imageCreateInfo,
7596         const VmaAllocationCreateInfo& allocCreateInfo,
7597         VmaAllocation allocation);
7598     void RecordDestroyBuffer(uint32_t frameIndex,
7599         VmaAllocation allocation);
7600     void RecordDestroyImage(uint32_t frameIndex,
7601         VmaAllocation allocation);
7602     void RecordTouchAllocation(uint32_t frameIndex,
7603         VmaAllocation allocation);
7604     void RecordGetAllocationInfo(uint32_t frameIndex,
7605         VmaAllocation allocation);
7606     void RecordMakePoolAllocationsLost(uint32_t frameIndex,
7607         VmaPool pool);
7608     void RecordDefragmentationBegin(uint32_t frameIndex,
7609         const VmaDefragmentationInfo2& info,
7610         VmaDefragmentationContext ctx);
7611     void RecordDefragmentationEnd(uint32_t frameIndex,
7612         VmaDefragmentationContext ctx);
7613     void RecordSetPoolName(uint32_t frameIndex,
7614         VmaPool pool,
7615         const char* name);
7616 
7617 private:
7618     struct CallParams
7619     {
7620         uint32_t threadId;
7621         double time;
7622     };
7623 
7624     class UserDataString
7625     {
7626     public:
7627         UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
GetString()7628         const char* GetString() const { return m_Str; }
7629 
7630     private:
7631         char m_PtrStr[17];
7632         const char* m_Str;
7633     };
7634 
7635     bool m_UseMutex;
7636     VmaRecordFlags m_Flags;
7637     FILE* m_File;
7638     VMA_MUTEX m_FileMutex;
7639     std::chrono::time_point<std::chrono::high_resolution_clock> m_RecordingStartTime;
7640 
7641     void GetBasicParams(CallParams& outParams);
7642 
7643     // T must be a pointer type, e.g. VmaAllocation, VmaPool.
7644     template<typename T>
PrintPointerList(uint64_t count,const T * pItems)7645     void PrintPointerList(uint64_t count, const T* pItems)
7646     {
7647         if(count)
7648         {
7649             fprintf(m_File, "%p", pItems[0]);
7650             for(uint64_t i = 1; i < count; ++i)
7651             {
7652                 fprintf(m_File, " %p", pItems[i]);
7653             }
7654         }
7655     }
7656 
7657     void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
7658     void Flush();
7659 };
7660 
7661 #endif // #if VMA_RECORDING_ENABLED
7662 
7663 /*
7664 Thread-safe wrapper over VmaPoolAllocator free list, for allocation of VmaAllocation_T objects.
7665 */
7666 class VmaAllocationObjectAllocator
7667 {
7668     VMA_CLASS_NO_COPY(VmaAllocationObjectAllocator)
7669 public:
7670     VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks);
7671 
7672     template<typename... Types> VmaAllocation Allocate(Types... args);
7673     void Free(VmaAllocation hAlloc);
7674 
7675 private:
7676     VMA_MUTEX m_Mutex;
7677     VmaPoolAllocator<VmaAllocation_T> m_Allocator;
7678 };
7679 
7680 struct VmaCurrentBudgetData
7681 {
7682     VMA_ATOMIC_UINT64 m_BlockBytes[VK_MAX_MEMORY_HEAPS];
7683     VMA_ATOMIC_UINT64 m_AllocationBytes[VK_MAX_MEMORY_HEAPS];
7684 
7685 #if VMA_MEMORY_BUDGET
7686     VMA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch;
7687     VMA_RW_MUTEX m_BudgetMutex;
7688     uint64_t m_VulkanUsage[VK_MAX_MEMORY_HEAPS];
7689     uint64_t m_VulkanBudget[VK_MAX_MEMORY_HEAPS];
7690     uint64_t m_BlockBytesAtBudgetFetch[VK_MAX_MEMORY_HEAPS];
7691 #endif // #if VMA_MEMORY_BUDGET
7692 
VmaCurrentBudgetDataVmaCurrentBudgetData7693     VmaCurrentBudgetData()
7694     {
7695         for(uint32_t heapIndex = 0; heapIndex < VK_MAX_MEMORY_HEAPS; ++heapIndex)
7696         {
7697             m_BlockBytes[heapIndex] = 0;
7698             m_AllocationBytes[heapIndex] = 0;
7699 #if VMA_MEMORY_BUDGET
7700             m_VulkanUsage[heapIndex] = 0;
7701             m_VulkanBudget[heapIndex] = 0;
7702             m_BlockBytesAtBudgetFetch[heapIndex] = 0;
7703 #endif
7704         }
7705 
7706 #if VMA_MEMORY_BUDGET
7707         m_OperationsSinceBudgetFetch = 0;
7708 #endif
7709     }
7710 
AddAllocationVmaCurrentBudgetData7711     void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
7712     {
7713         m_AllocationBytes[heapIndex] += allocationSize;
7714 #if VMA_MEMORY_BUDGET
7715         ++m_OperationsSinceBudgetFetch;
7716 #endif
7717     }
7718 
RemoveAllocationVmaCurrentBudgetData7719     void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
7720     {
7721         VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize); // DELME
7722         m_AllocationBytes[heapIndex] -= allocationSize;
7723 #if VMA_MEMORY_BUDGET
7724         ++m_OperationsSinceBudgetFetch;
7725 #endif
7726     }
7727 };
7728 
7729 // Main allocator object.
7730 struct VmaAllocator_T
7731 {
7732     VMA_CLASS_NO_COPY(VmaAllocator_T)
7733 public:
7734     bool m_UseMutex;
7735     uint32_t m_VulkanApiVersion;
7736     bool m_UseKhrDedicatedAllocation; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
7737     bool m_UseKhrBindMemory2; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
7738     bool m_UseExtMemoryBudget;
7739     bool m_UseAmdDeviceCoherentMemory;
7740     bool m_UseKhrBufferDeviceAddress;
7741     VkDevice m_hDevice;
7742     VkInstance m_hInstance;
7743     bool m_AllocationCallbacksSpecified;
7744     VkAllocationCallbacks m_AllocationCallbacks;
7745     VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
7746     VmaAllocationObjectAllocator m_AllocationObjectAllocator;
7747 
7748     // Each bit (1 << i) is set if HeapSizeLimit is enabled for that heap, so cannot allocate more than the heap size.
7749     uint32_t m_HeapSizeLimitMask;
7750 
7751     VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
7752     VkPhysicalDeviceMemoryProperties m_MemProps;
7753 
7754     // Default pools.
7755     VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
7756     // Reserved pools.
7757     VmaBlockVector* m_pReservedBlockVectors[VK_MAX_MEMORY_TYPES];
7758 
7759     // Each vector is sorted by memory (handle value).
7760     typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
7761     AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES];
7762     VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
7763 
7764     VmaCurrentBudgetData m_Budget;
7765 
7766     VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
7767     VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
7768     ~VmaAllocator_T();
7769 
GetAllocationCallbacksVmaAllocator_T7770     const VkAllocationCallbacks* GetAllocationCallbacks() const
7771     {
7772         return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
7773     }
GetVulkanFunctionsVmaAllocator_T7774     const VmaVulkanFunctions& GetVulkanFunctions() const
7775     {
7776         return m_VulkanFunctions;
7777     }
7778 
GetPhysicalDeviceVmaAllocator_T7779     VkPhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; }
7780 
GetBufferImageGranularityVmaAllocator_T7781     VkDeviceSize GetBufferImageGranularity() const
7782     {
7783         return VMA_MAX(
7784             static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
7785             m_PhysicalDeviceProperties.limits.bufferImageGranularity);
7786     }
7787 
GetMemoryHeapCountVmaAllocator_T7788     uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
GetMemoryTypeCountVmaAllocator_T7789     uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
7790 
MemoryTypeIndexToHeapIndexVmaAllocator_T7791     uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
7792     {
7793         VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
7794         return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
7795     }
7796     // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
IsMemoryTypeNonCoherentVmaAllocator_T7797     bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
7798     {
7799         return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
7800             VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
7801     }
7802     // Minimum alignment for all allocations in specific memory type.
GetMemoryTypeMinAlignmentVmaAllocator_T7803     VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
7804     {
7805         return IsMemoryTypeNonCoherent(memTypeIndex) ?
7806             VMA_MAX((VkDeviceSize)VMA_DEBUG_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
7807             (VkDeviceSize)VMA_DEBUG_ALIGNMENT;
7808     }
7809 
IsIntegratedGpuVmaAllocator_T7810     bool IsIntegratedGpu() const
7811     {
7812         return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
7813     }
7814 
GetGlobalMemoryTypeBitsVmaAllocator_T7815     uint32_t GetGlobalMemoryTypeBits() const { return m_GlobalMemoryTypeBits; }
7816 
7817 #if VMA_RECORDING_ENABLED
GetRecorderVmaAllocator_T7818     VmaRecorder* GetRecorder() const { return m_pRecorder; }
7819 #endif
7820 
7821     void GetBufferMemoryRequirements(
7822         VkBuffer hBuffer,
7823         VkMemoryRequirements& memReq,
7824         bool& requiresDedicatedAllocation,
7825         bool& prefersDedicatedAllocation) const;
7826     void GetImageMemoryRequirements(
7827         VkImage hImage,
7828         VkMemoryRequirements& memReq,
7829         bool& requiresDedicatedAllocation,
7830         bool& prefersDedicatedAllocation) const;
7831 
7832     // Main allocation function.
7833     VkResult AllocateMemory(
7834         const VkMemoryRequirements& vkMemReq,
7835         bool requiresDedicatedAllocation,
7836         bool prefersDedicatedAllocation,
7837         VkBuffer dedicatedBuffer,
7838         VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown.
7839         VkImage dedicatedImage,
7840         const VmaAllocationCreateInfo& createInfo,
7841         VmaSuballocationType suballocType,
7842         size_t allocationCount,
7843         VmaAllocation* pAllocations);
7844 
7845     // Main deallocation function.
7846     void FreeMemory(
7847         size_t allocationCount,
7848         const VmaAllocation* pAllocations);
7849 
7850     // OH ISSUE: VMA preAlloc
7851     // pre-allocation
7852     VkResult AllocateReservedMemory(
7853         const VkMemoryRequirements& vkMemReq,
7854         bool requiresDedicatedAllocation,
7855         bool prefersDedicatedAllocation,
7856         VkBuffer dedicatedBuffer,
7857         VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown.
7858         VkImage dedicatedImage,
7859         const VmaAllocationCreateInfo& createInfo,
7860         VmaSuballocationType suballocType,
7861         size_t allocationCount,
7862         VmaAllocation* pAllocations);
7863 
7864     // pre-deallocation function.
7865     void FreeReservedMemory(
7866         size_t allocationCount,
7867         const VmaAllocation* pAllocations);
7868 
7869     VkResult SwapReservedBlock(
7870         VkImage image,
7871         const VmaAllocationCreateInfo* pCreateInfo,
7872         VmaAllocation* pAllocation,
7873         VmaAllocationInfo* pAllocationInfo);
7874 
7875     uint32_t GetPreAllocBlockSize();
7876 
7877     VkResult ResizeAllocation(
7878         const VmaAllocation alloc,
7879         VkDeviceSize newSize);
7880 
7881     void CalculateStats(VmaStats* pStats);
7882 
7883     void FreeEmptyBlock();
7884 
7885     void GetBudget(
7886         VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount);
7887 
7888 #if VMA_STATS_STRING_ENABLED
7889     void PrintDetailedMap(class VmaJsonWriter& json);
7890 #endif
7891 
7892     VkResult DefragmentationBegin(
7893         const VmaDefragmentationInfo2& info,
7894         VmaDefragmentationStats* pStats,
7895         VmaDefragmentationContext* pContext);
7896     VkResult DefragmentationEnd(
7897         VmaDefragmentationContext context);
7898 
7899     VkResult DefragmentationPassBegin(
7900         VmaDefragmentationPassInfo* pInfo,
7901         VmaDefragmentationContext context);
7902     VkResult DefragmentationPassEnd(
7903         VmaDefragmentationContext context);
7904 
7905     void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
7906     bool TouchAllocation(VmaAllocation hAllocation);
7907 
7908     VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
7909     void DestroyPool(VmaPool pool);
7910     void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
7911 
7912     void SetCurrentFrameIndex(uint32_t frameIndex);
GetCurrentFrameIndexVmaAllocator_T7913     uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
7914 
7915     void MakePoolAllocationsLost(
7916         VmaPool hPool,
7917         size_t* pLostAllocationCount);
7918     VkResult CheckPoolCorruption(VmaPool hPool);
7919     VkResult CheckCorruption(uint32_t memoryTypeBits);
7920 
7921     void CreateLostAllocation(VmaAllocation* pAllocation);
7922 
7923     // Call to Vulkan function vkAllocateMemory with accompanying bookkeeping.
7924     VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
7925     // Call to Vulkan function vkFreeMemory with accompanying bookkeeping.
7926     void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
7927     // Call to Vulkan function vkBindBufferMemory or vkBindBufferMemory2KHR.
7928     VkResult BindVulkanBuffer(
7929         VkDeviceMemory memory,
7930         VkDeviceSize memoryOffset,
7931         VkBuffer buffer,
7932         const void* pNext);
7933     // Call to Vulkan function vkBindImageMemory or vkBindImageMemory2KHR.
7934     VkResult BindVulkanImage(
7935         VkDeviceMemory memory,
7936         VkDeviceSize memoryOffset,
7937         VkImage image,
7938         const void* pNext);
7939 
7940     VkResult Map(VmaAllocation hAllocation, void** ppData);
7941     void Unmap(VmaAllocation hAllocation);
7942 
7943     VkResult BindBufferMemory(
7944         VmaAllocation hAllocation,
7945         VkDeviceSize allocationLocalOffset,
7946         VkBuffer hBuffer,
7947         const void* pNext);
7948     VkResult BindImageMemory(
7949         VmaAllocation hAllocation,
7950         VkDeviceSize allocationLocalOffset,
7951         VkImage hImage,
7952         const void* pNext);
7953 
7954     VkResult FlushOrInvalidateAllocation(
7955         VmaAllocation hAllocation,
7956         VkDeviceSize offset, VkDeviceSize size,
7957         VMA_CACHE_OPERATION op);
7958     VkResult FlushOrInvalidateAllocations(
7959         uint32_t allocationCount,
7960         const VmaAllocation* allocations,
7961         const VkDeviceSize* offsets, const VkDeviceSize* sizes,
7962         VMA_CACHE_OPERATION op);
7963 
7964     void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
7965 
7966     /*
7967     Returns bit mask of memory types that can support defragmentation on GPU as
7968     they support creation of required buffer for copy operations.
7969     */
7970     uint32_t GetGpuDefragmentationMemoryTypeBits();
7971 
7972 private:
7973     VkDeviceSize m_PreferredLargeHeapBlockSize;
7974 
7975     VkPhysicalDevice m_PhysicalDevice;
7976     VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
7977     VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized.
7978 
7979     VMA_RW_MUTEX m_PoolsMutex;
7980     // Protected by m_PoolsMutex. Sorted by pointer value.
7981     VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools;
7982     uint32_t m_NextPoolId;
7983 
7984     VmaVulkanFunctions m_VulkanFunctions;
7985 
7986     // Global bit mask AND-ed with any memoryTypeBits to disallow certain memory types.
7987     uint32_t m_GlobalMemoryTypeBits;
7988 
7989 #if VMA_RECORDING_ENABLED
7990     VmaRecorder* m_pRecorder;
7991 #endif
7992 
7993     void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
7994 
7995 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
7996     void ImportVulkanFunctions_Static();
7997 #endif
7998 
7999     void ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions);
8000 
8001 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
8002     void ImportVulkanFunctions_Dynamic();
8003 #endif
8004 
8005     void ValidateVulkanFunctions();
8006 
8007     VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
8008 
8009     VkResult AllocateMemoryOfType(
8010         VkDeviceSize size,
8011         VkDeviceSize alignment,
8012         bool dedicatedAllocation,
8013         VkBuffer dedicatedBuffer,
8014         VkBufferUsageFlags dedicatedBufferUsage,
8015         VkImage dedicatedImage,
8016         const VmaAllocationCreateInfo& createInfo,
8017         uint32_t memTypeIndex,
8018         VmaSuballocationType suballocType,
8019         size_t allocationCount,
8020         VmaAllocation* pAllocations);
8021 
8022     // Helper function only to be used inside AllocateDedicatedMemory.
8023     VkResult AllocateDedicatedMemoryPage(
8024         VkDeviceSize size,
8025         VmaSuballocationType suballocType,
8026         uint32_t memTypeIndex,
8027         const VkMemoryAllocateInfo& allocInfo,
8028         bool map,
8029         bool isUserDataString,
8030         void* pUserData,
8031         VmaAllocation* pAllocation);
8032 
8033     // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
8034     VkResult AllocateDedicatedMemory(
8035         VkDeviceSize size,
8036         VmaSuballocationType suballocType,
8037         uint32_t memTypeIndex,
8038         bool withinBudget,
8039         bool map,
8040         bool isUserDataString,
8041         void* pUserData,
8042         VkBuffer dedicatedBuffer,
8043         VkBufferUsageFlags dedicatedBufferUsage,
8044         VkImage dedicatedImage,
8045         size_t allocationCount,
8046         VmaAllocation* pAllocations);
8047 
8048     void FreeDedicatedMemory(const VmaAllocation allocation);
8049 
8050     /*
8051     Calculates and returns bit mask of memory types that can support defragmentation
8052     on GPU as they support creation of required buffer for copy operations.
8053     */
8054     uint32_t CalculateGpuDefragmentationMemoryTypeBits() const;
8055 
8056     uint32_t CalculateGlobalMemoryTypeBits() const;
8057 
8058     bool GetFlushOrInvalidateRange(
8059         VmaAllocation allocation,
8060         VkDeviceSize offset, VkDeviceSize size,
8061         VkMappedMemoryRange& outRange) const;
8062 
8063 #if VMA_MEMORY_BUDGET
8064     void UpdateVulkanBudget();
8065 #endif // #if VMA_MEMORY_BUDGET
8066 };
8067 
8068 ////////////////////////////////////////////////////////////////////////////////
8069 // Memory allocation #2 after VmaAllocator_T definition
8070 
VmaMalloc(VmaAllocator hAllocator,size_t size,size_t alignment)8071 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
8072 {
8073     return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
8074 }
8075 
VmaFree(VmaAllocator hAllocator,void * ptr)8076 static void VmaFree(VmaAllocator hAllocator, void* ptr)
8077 {
8078     VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
8079 }
8080 
8081 template<typename T>
VmaAllocate(VmaAllocator hAllocator)8082 static T* VmaAllocate(VmaAllocator hAllocator)
8083 {
8084     return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
8085 }
8086 
8087 template<typename T>
VmaAllocateArray(VmaAllocator hAllocator,size_t count)8088 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
8089 {
8090     return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
8091 }
8092 
8093 template<typename T>
vma_delete(VmaAllocator hAllocator,T * ptr)8094 static void vma_delete(VmaAllocator hAllocator, T* ptr)
8095 {
8096     if(ptr != VMA_NULL)
8097     {
8098         ptr->~T();
8099         VmaFree(hAllocator, ptr);
8100     }
8101 }
8102 
8103 template<typename T>
vma_delete_array(VmaAllocator hAllocator,T * ptr,size_t count)8104 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
8105 {
8106     if(ptr != VMA_NULL)
8107     {
8108         for(size_t i = count; i--; )
8109             ptr[i].~T();
8110         VmaFree(hAllocator, ptr);
8111     }
8112 }
8113 
8114 ////////////////////////////////////////////////////////////////////////////////
8115 // VmaStringBuilder
8116 
8117 #if VMA_STATS_STRING_ENABLED
8118 
8119 class VmaStringBuilder
8120 {
8121 public:
VmaStringBuilder(VmaAllocator alloc)8122     VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
GetLength()8123     size_t GetLength() const { return m_Data.size(); }
GetData()8124     const char* GetData() const { return m_Data.data(); }
8125 
Add(char ch)8126     void Add(char ch) { m_Data.push_back(ch); }
8127     void Add(const char* pStr);
AddNewLine()8128     void AddNewLine() { Add('\n'); }
8129     void AddNumber(uint32_t num);
8130     void AddNumber(uint64_t num);
8131     void AddPointer(const void* ptr);
8132 
8133 private:
8134     VmaVector< char, VmaStlAllocator<char> > m_Data;
8135 };
8136 
Add(const char * pStr)8137 void VmaStringBuilder::Add(const char* pStr)
8138 {
8139     const size_t strLen = strlen(pStr);
8140     if(strLen > 0)
8141     {
8142         const size_t oldCount = m_Data.size();
8143         m_Data.resize(oldCount + strLen);
8144         memcpy(m_Data.data() + oldCount, pStr, strLen);
8145     }
8146 }
8147 
AddNumber(uint32_t num)8148 void VmaStringBuilder::AddNumber(uint32_t num)
8149 {
8150     char buf[11];
8151     buf[10] = '\0';
8152     char *p = &buf[10];
8153     do
8154     {
8155         *--p = '0' + (num % 10);
8156         num /= 10;
8157     }
8158     while(num);
8159     Add(p);
8160 }
8161 
AddNumber(uint64_t num)8162 void VmaStringBuilder::AddNumber(uint64_t num)
8163 {
8164     char buf[21];
8165     buf[20] = '\0';
8166     char *p = &buf[20];
8167     do
8168     {
8169         *--p = '0' + (num % 10);
8170         num /= 10;
8171     }
8172     while(num);
8173     Add(p);
8174 }
8175 
AddPointer(const void * ptr)8176 void VmaStringBuilder::AddPointer(const void* ptr)
8177 {
8178     char buf[21];
8179     VmaPtrToStr(buf, sizeof(buf), ptr);
8180     Add(buf);
8181 }
8182 
8183 #endif // #if VMA_STATS_STRING_ENABLED
8184 
8185 ////////////////////////////////////////////////////////////////////////////////
8186 // VmaJsonWriter
8187 
8188 #if VMA_STATS_STRING_ENABLED
8189 
8190 class VmaJsonWriter
8191 {
8192     VMA_CLASS_NO_COPY(VmaJsonWriter)
8193 public:
8194     VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
8195     ~VmaJsonWriter();
8196 
8197     void BeginObject(bool singleLine = false);
8198     void EndObject();
8199 
8200     void BeginArray(bool singleLine = false);
8201     void EndArray();
8202 
8203     void WriteString(const char* pStr);
8204     void BeginString(const char* pStr = VMA_NULL);
8205     void ContinueString(const char* pStr);
8206     void ContinueString(uint32_t n);
8207     void ContinueString(uint64_t n);
8208     void ContinueString_Pointer(const void* ptr);
8209     void EndString(const char* pStr = VMA_NULL);
8210 
8211     void WriteNumber(uint32_t n);
8212     void WriteNumber(uint64_t n);
8213     void WriteBool(bool b);
8214     void WriteNull();
8215 
8216 private:
8217     static const char* const INDENT;
8218 
8219     enum COLLECTION_TYPE
8220     {
8221         COLLECTION_TYPE_OBJECT,
8222         COLLECTION_TYPE_ARRAY,
8223     };
8224     struct StackItem
8225     {
8226         COLLECTION_TYPE type;
8227         uint32_t valueCount;
8228         bool singleLineMode;
8229     };
8230 
8231     VmaStringBuilder& m_SB;
8232     VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
8233     bool m_InsideString;
8234 
8235     void BeginValue(bool isString);
8236     void WriteIndent(bool oneLess = false);
8237 };
8238 
8239 const char* const VmaJsonWriter::INDENT = "  ";
8240 
VmaJsonWriter(const VkAllocationCallbacks * pAllocationCallbacks,VmaStringBuilder & sb)8241 VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
8242     m_SB(sb),
8243     m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
8244     m_InsideString(false)
8245 {
8246 }
8247 
~VmaJsonWriter()8248 VmaJsonWriter::~VmaJsonWriter()
8249 {
8250     VMA_ASSERT(!m_InsideString);
8251     VMA_ASSERT(m_Stack.empty());
8252 }
8253 
BeginObject(bool singleLine)8254 void VmaJsonWriter::BeginObject(bool singleLine)
8255 {
8256     VMA_ASSERT(!m_InsideString);
8257 
8258     BeginValue(false);
8259     m_SB.Add('{');
8260 
8261     StackItem item;
8262     item.type = COLLECTION_TYPE_OBJECT;
8263     item.valueCount = 0;
8264     item.singleLineMode = singleLine;
8265     m_Stack.push_back(item);
8266 }
8267 
EndObject()8268 void VmaJsonWriter::EndObject()
8269 {
8270     VMA_ASSERT(!m_InsideString);
8271 
8272     WriteIndent(true);
8273     m_SB.Add('}');
8274 
8275     VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
8276     m_Stack.pop_back();
8277 }
8278 
BeginArray(bool singleLine)8279 void VmaJsonWriter::BeginArray(bool singleLine)
8280 {
8281     VMA_ASSERT(!m_InsideString);
8282 
8283     BeginValue(false);
8284     m_SB.Add('[');
8285 
8286     StackItem item;
8287     item.type = COLLECTION_TYPE_ARRAY;
8288     item.valueCount = 0;
8289     item.singleLineMode = singleLine;
8290     m_Stack.push_back(item);
8291 }
8292 
EndArray()8293 void VmaJsonWriter::EndArray()
8294 {
8295     VMA_ASSERT(!m_InsideString);
8296 
8297     WriteIndent(true);
8298     m_SB.Add(']');
8299 
8300     VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
8301     m_Stack.pop_back();
8302 }
8303 
WriteString(const char * pStr)8304 void VmaJsonWriter::WriteString(const char* pStr)
8305 {
8306     BeginString(pStr);
8307     EndString();
8308 }
8309 
BeginString(const char * pStr)8310 void VmaJsonWriter::BeginString(const char* pStr)
8311 {
8312     VMA_ASSERT(!m_InsideString);
8313 
8314     BeginValue(true);
8315     m_SB.Add('"');
8316     m_InsideString = true;
8317     if(pStr != VMA_NULL && pStr[0] != '\0')
8318     {
8319         ContinueString(pStr);
8320     }
8321 }
8322 
ContinueString(const char * pStr)8323 void VmaJsonWriter::ContinueString(const char* pStr)
8324 {
8325     VMA_ASSERT(m_InsideString);
8326 
8327     const size_t strLen = strlen(pStr);
8328     for(size_t i = 0; i < strLen; ++i)
8329     {
8330         char ch = pStr[i];
8331         if(ch == '\\')
8332         {
8333             m_SB.Add("\\\\");
8334         }
8335         else if(ch == '"')
8336         {
8337             m_SB.Add("\\\"");
8338         }
8339         else if(ch >= 32)
8340         {
8341             m_SB.Add(ch);
8342         }
8343         else switch(ch)
8344         {
8345         case '\b':
8346             m_SB.Add("\\b");
8347             break;
8348         case '\f':
8349             m_SB.Add("\\f");
8350             break;
8351         case '\n':
8352             m_SB.Add("\\n");
8353             break;
8354         case '\r':
8355             m_SB.Add("\\r");
8356             break;
8357         case '\t':
8358             m_SB.Add("\\t");
8359             break;
8360         default:
8361             VMA_ASSERT(0 && "Character not currently supported.");
8362             break;
8363         }
8364     }
8365 }
8366 
ContinueString(uint32_t n)8367 void VmaJsonWriter::ContinueString(uint32_t n)
8368 {
8369     VMA_ASSERT(m_InsideString);
8370     m_SB.AddNumber(n);
8371 }
8372 
ContinueString(uint64_t n)8373 void VmaJsonWriter::ContinueString(uint64_t n)
8374 {
8375     VMA_ASSERT(m_InsideString);
8376     m_SB.AddNumber(n);
8377 }
8378 
ContinueString_Pointer(const void * ptr)8379 void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
8380 {
8381     VMA_ASSERT(m_InsideString);
8382     m_SB.AddPointer(ptr);
8383 }
8384 
EndString(const char * pStr)8385 void VmaJsonWriter::EndString(const char* pStr)
8386 {
8387     VMA_ASSERT(m_InsideString);
8388     if(pStr != VMA_NULL && pStr[0] != '\0')
8389     {
8390         ContinueString(pStr);
8391     }
8392     m_SB.Add('"');
8393     m_InsideString = false;
8394 }
8395 
WriteNumber(uint32_t n)8396 void VmaJsonWriter::WriteNumber(uint32_t n)
8397 {
8398     VMA_ASSERT(!m_InsideString);
8399     BeginValue(false);
8400     m_SB.AddNumber(n);
8401 }
8402 
WriteNumber(uint64_t n)8403 void VmaJsonWriter::WriteNumber(uint64_t n)
8404 {
8405     VMA_ASSERT(!m_InsideString);
8406     BeginValue(false);
8407     m_SB.AddNumber(n);
8408 }
8409 
WriteBool(bool b)8410 void VmaJsonWriter::WriteBool(bool b)
8411 {
8412     VMA_ASSERT(!m_InsideString);
8413     BeginValue(false);
8414     m_SB.Add(b ? "true" : "false");
8415 }
8416 
WriteNull()8417 void VmaJsonWriter::WriteNull()
8418 {
8419     VMA_ASSERT(!m_InsideString);
8420     BeginValue(false);
8421     m_SB.Add("null");
8422 }
8423 
BeginValue(bool isString)8424 void VmaJsonWriter::BeginValue(bool isString)
8425 {
8426     if(!m_Stack.empty())
8427     {
8428         StackItem& currItem = m_Stack.back();
8429         if(currItem.type == COLLECTION_TYPE_OBJECT &&
8430             currItem.valueCount % 2 == 0)
8431         {
8432             VMA_ASSERT(isString);
8433         }
8434 
8435         if(currItem.type == COLLECTION_TYPE_OBJECT &&
8436             currItem.valueCount % 2 != 0)
8437         {
8438             m_SB.Add(": ");
8439         }
8440         else if(currItem.valueCount > 0)
8441         {
8442             m_SB.Add(", ");
8443             WriteIndent();
8444         }
8445         else
8446         {
8447             WriteIndent();
8448         }
8449         ++currItem.valueCount;
8450     }
8451 }
8452 
WriteIndent(bool oneLess)8453 void VmaJsonWriter::WriteIndent(bool oneLess)
8454 {
8455     if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
8456     {
8457         m_SB.AddNewLine();
8458 
8459         size_t count = m_Stack.size();
8460         if(count > 0 && oneLess)
8461         {
8462             --count;
8463         }
8464         for(size_t i = 0; i < count; ++i)
8465         {
8466             m_SB.Add(INDENT);
8467         }
8468     }
8469 }
8470 
8471 #endif // #if VMA_STATS_STRING_ENABLED
8472 
8473 ////////////////////////////////////////////////////////////////////////////////
8474 
SetUserData(VmaAllocator hAllocator,void * pUserData)8475 void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
8476 {
8477     if(IsUserDataString())
8478     {
8479         VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
8480 
8481         FreeUserDataString(hAllocator);
8482 
8483         if(pUserData != VMA_NULL)
8484         {
8485             m_pUserData = VmaCreateStringCopy(hAllocator->GetAllocationCallbacks(), (const char*)pUserData);
8486         }
8487     }
8488     else
8489     {
8490         m_pUserData = pUserData;
8491     }
8492 }
8493 
ChangeBlockAllocation(VmaAllocator hAllocator,VmaDeviceMemoryBlock * block,VkDeviceSize offset)8494 void VmaAllocation_T::ChangeBlockAllocation(
8495     VmaAllocator hAllocator,
8496     VmaDeviceMemoryBlock* block,
8497     VkDeviceSize offset)
8498 {
8499     VMA_ASSERT(block != VMA_NULL);
8500     VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
8501 
8502     // Move mapping reference counter from old block to new block.
8503     if(block != m_BlockAllocation.m_Block)
8504     {
8505         uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
8506         if(IsPersistentMap())
8507             ++mapRefCount;
8508         m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
8509         block->Map(hAllocator, mapRefCount, VMA_NULL);
8510     }
8511 
8512     m_BlockAllocation.m_Block = block;
8513     m_BlockAllocation.m_Offset = offset;
8514 }
8515 
ChangeOffset(VkDeviceSize newOffset)8516 void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
8517 {
8518     VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
8519     m_BlockAllocation.m_Offset = newOffset;
8520 }
8521 
GetOffset()8522 VkDeviceSize VmaAllocation_T::GetOffset() const
8523 {
8524     switch(m_Type)
8525     {
8526     case ALLOCATION_TYPE_BLOCK:
8527         return m_BlockAllocation.m_Offset;
8528     case ALLOCATION_TYPE_DEDICATED:
8529         return 0;
8530     default:
8531         VMA_ASSERT(0);
8532         return 0;
8533     }
8534 }
8535 
GetMemory()8536 VkDeviceMemory VmaAllocation_T::GetMemory() const
8537 {
8538     switch(m_Type)
8539     {
8540     case ALLOCATION_TYPE_BLOCK:
8541         return m_BlockAllocation.m_Block->GetDeviceMemory();
8542     case ALLOCATION_TYPE_DEDICATED:
8543         return m_DedicatedAllocation.m_hMemory;
8544     default:
8545         VMA_ASSERT(0);
8546         return VK_NULL_HANDLE;
8547     }
8548 }
8549 
GetMappedData()8550 void* VmaAllocation_T::GetMappedData() const
8551 {
8552     switch(m_Type)
8553     {
8554     case ALLOCATION_TYPE_BLOCK:
8555         if(m_MapCount != 0)
8556         {
8557             void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
8558             VMA_ASSERT(pBlockData != VMA_NULL);
8559             return (char*)pBlockData + m_BlockAllocation.m_Offset;
8560         }
8561         else
8562         {
8563             return VMA_NULL;
8564         }
8565         break;
8566     case ALLOCATION_TYPE_DEDICATED:
8567         VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
8568         return m_DedicatedAllocation.m_pMappedData;
8569     default:
8570         VMA_ASSERT(0);
8571         return VMA_NULL;
8572     }
8573 }
8574 
CanBecomeLost()8575 bool VmaAllocation_T::CanBecomeLost() const
8576 {
8577     switch(m_Type)
8578     {
8579     case ALLOCATION_TYPE_BLOCK:
8580         return m_BlockAllocation.m_CanBecomeLost;
8581     case ALLOCATION_TYPE_DEDICATED:
8582         return false;
8583     default:
8584         VMA_ASSERT(0);
8585         return false;
8586     }
8587 }
8588 
MakeLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)8589 bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
8590 {
8591     VMA_ASSERT(CanBecomeLost());
8592 
8593     /*
8594     Warning: This is a carefully designed algorithm.
8595     Do not modify unless you really know what you're doing :)
8596     */
8597     uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
8598     for(;;)
8599     {
8600         if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
8601         {
8602             VMA_ASSERT(0);
8603             return false;
8604         }
8605         else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
8606         {
8607             return false;
8608         }
8609         else // Last use time earlier than current time.
8610         {
8611             if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
8612             {
8613                 // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
8614                 // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
8615                 return true;
8616             }
8617         }
8618     }
8619 }
8620 
8621 #if VMA_STATS_STRING_ENABLED
8622 
8623 // Correspond to values of enum VmaSuballocationType.
8624 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
8625     "FREE",
8626     "UNKNOWN",
8627     "BUFFER",
8628     "IMAGE_UNKNOWN",
8629     "IMAGE_LINEAR",
8630     "IMAGE_OPTIMAL",
8631 };
8632 
PrintParameters(class VmaJsonWriter & json)8633 void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
8634 {
8635     json.WriteString("Type");
8636     json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
8637 
8638     json.WriteString("Size");
8639     json.WriteNumber(m_Size);
8640 
8641     if(m_pUserData != VMA_NULL)
8642     {
8643         json.WriteString("UserData");
8644         if(IsUserDataString())
8645         {
8646             json.WriteString((const char*)m_pUserData);
8647         }
8648         else
8649         {
8650             json.BeginString();
8651             json.ContinueString_Pointer(m_pUserData);
8652             json.EndString();
8653         }
8654     }
8655 
8656     json.WriteString("CreationFrameIndex");
8657     json.WriteNumber(m_CreationFrameIndex);
8658 
8659     json.WriteString("LastUseFrameIndex");
8660     json.WriteNumber(GetLastUseFrameIndex());
8661 
8662     if(m_BufferImageUsage != 0)
8663     {
8664         json.WriteString("Usage");
8665         json.WriteNumber(m_BufferImageUsage);
8666     }
8667 }
8668 
8669 #endif
8670 
FreeUserDataString(VmaAllocator hAllocator)8671 void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
8672 {
8673     VMA_ASSERT(IsUserDataString());
8674     VmaFreeString(hAllocator->GetAllocationCallbacks(), (char*)m_pUserData);
8675     m_pUserData = VMA_NULL;
8676 }
8677 
BlockAllocMap()8678 void VmaAllocation_T::BlockAllocMap()
8679 {
8680     VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
8681 
8682     if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
8683     {
8684         ++m_MapCount;
8685     }
8686     else
8687     {
8688         VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
8689     }
8690 }
8691 
BlockAllocUnmap()8692 void VmaAllocation_T::BlockAllocUnmap()
8693 {
8694     VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
8695 
8696     if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
8697     {
8698         --m_MapCount;
8699     }
8700     else
8701     {
8702         VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
8703     }
8704 }
8705 
DedicatedAllocMap(VmaAllocator hAllocator,void ** ppData)8706 VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
8707 {
8708     VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
8709 
8710     if(m_MapCount != 0)
8711     {
8712         if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
8713         {
8714             VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
8715             *ppData = m_DedicatedAllocation.m_pMappedData;
8716             ++m_MapCount;
8717             return VK_SUCCESS;
8718         }
8719         else
8720         {
8721             VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
8722             return VK_ERROR_MEMORY_MAP_FAILED;
8723         }
8724     }
8725     else
8726     {
8727         VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
8728             hAllocator->m_hDevice,
8729             m_DedicatedAllocation.m_hMemory,
8730             0, // offset
8731             VK_WHOLE_SIZE,
8732             0, // flags
8733             ppData);
8734         if(result == VK_SUCCESS)
8735         {
8736             m_DedicatedAllocation.m_pMappedData = *ppData;
8737             m_MapCount = 1;
8738         }
8739         return result;
8740     }
8741 }
8742 
DedicatedAllocUnmap(VmaAllocator hAllocator)8743 void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
8744 {
8745     VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
8746 
8747     if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
8748     {
8749         --m_MapCount;
8750         if(m_MapCount == 0)
8751         {
8752             m_DedicatedAllocation.m_pMappedData = VMA_NULL;
8753             (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
8754                 hAllocator->m_hDevice,
8755                 m_DedicatedAllocation.m_hMemory);
8756         }
8757     }
8758     else
8759     {
8760         VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
8761     }
8762 }
8763 
8764 #if VMA_STATS_STRING_ENABLED
8765 
VmaPrintStatInfo(VmaJsonWriter & json,const VmaStatInfo & stat)8766 static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
8767 {
8768     json.BeginObject();
8769 
8770     json.WriteString("Blocks");
8771     json.WriteNumber(stat.blockCount);
8772 
8773     json.WriteString("Allocations");
8774     json.WriteNumber(stat.allocationCount);
8775 
8776     json.WriteString("UnusedRanges");
8777     json.WriteNumber(stat.unusedRangeCount);
8778 
8779     json.WriteString("UsedBytes");
8780     json.WriteNumber(stat.usedBytes);
8781 
8782     json.WriteString("UnusedBytes");
8783     json.WriteNumber(stat.unusedBytes);
8784 
8785     if(stat.allocationCount > 1)
8786     {
8787         json.WriteString("AllocationSize");
8788         json.BeginObject(true);
8789         json.WriteString("Min");
8790         json.WriteNumber(stat.allocationSizeMin);
8791         json.WriteString("Avg");
8792         json.WriteNumber(stat.allocationSizeAvg);
8793         json.WriteString("Max");
8794         json.WriteNumber(stat.allocationSizeMax);
8795         json.EndObject();
8796     }
8797 
8798     if(stat.unusedRangeCount > 1)
8799     {
8800         json.WriteString("UnusedRangeSize");
8801         json.BeginObject(true);
8802         json.WriteString("Min");
8803         json.WriteNumber(stat.unusedRangeSizeMin);
8804         json.WriteString("Avg");
8805         json.WriteNumber(stat.unusedRangeSizeAvg);
8806         json.WriteString("Max");
8807         json.WriteNumber(stat.unusedRangeSizeMax);
8808         json.EndObject();
8809     }
8810 
8811     json.EndObject();
8812 }
8813 
8814 #endif // #if VMA_STATS_STRING_ENABLED
8815 
8816 struct VmaSuballocationItemSizeLess
8817 {
operatorVmaSuballocationItemSizeLess8818     bool operator()(
8819         const VmaSuballocationList::iterator lhs,
8820         const VmaSuballocationList::iterator rhs) const
8821     {
8822         return lhs->size < rhs->size;
8823     }
operatorVmaSuballocationItemSizeLess8824     bool operator()(
8825         const VmaSuballocationList::iterator lhs,
8826         VkDeviceSize rhsSize) const
8827     {
8828         return lhs->size < rhsSize;
8829     }
8830 };
8831 
8832 
8833 ////////////////////////////////////////////////////////////////////////////////
8834 // class VmaBlockMetadata
8835 
VmaBlockMetadata(VmaAllocator hAllocator)8836 VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
8837     m_Size(0),
8838     m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks())
8839 {
8840 }
8841 
8842 #if VMA_STATS_STRING_ENABLED
8843 
PrintDetailedMap_Begin(class VmaJsonWriter & json,VkDeviceSize unusedBytes,size_t allocationCount,size_t unusedRangeCount)8844 void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
8845     VkDeviceSize unusedBytes,
8846     size_t allocationCount,
8847     size_t unusedRangeCount) const
8848 {
8849     json.BeginObject();
8850 
8851     json.WriteString("TotalBytes");
8852     json.WriteNumber(GetSize());
8853 
8854     json.WriteString("UnusedBytes");
8855     json.WriteNumber(unusedBytes);
8856 
8857     json.WriteString("Allocations");
8858     json.WriteNumber((uint64_t)allocationCount);
8859 
8860     json.WriteString("UnusedRanges");
8861     json.WriteNumber((uint64_t)unusedRangeCount);
8862 
8863     json.WriteString("Suballocations");
8864     json.BeginArray();
8865 }
8866 
PrintDetailedMap_Allocation(class VmaJsonWriter & json,VkDeviceSize offset,VmaAllocation hAllocation)8867 void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
8868     VkDeviceSize offset,
8869     VmaAllocation hAllocation) const
8870 {
8871     json.BeginObject(true);
8872 
8873     json.WriteString("Offset");
8874     json.WriteNumber(offset);
8875 
8876     hAllocation->PrintParameters(json);
8877 
8878     json.EndObject();
8879 }
8880 
PrintDetailedMap_UnusedRange(class VmaJsonWriter & json,VkDeviceSize offset,VkDeviceSize size)8881 void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
8882     VkDeviceSize offset,
8883     VkDeviceSize size) const
8884 {
8885     json.BeginObject(true);
8886 
8887     json.WriteString("Offset");
8888     json.WriteNumber(offset);
8889 
8890     json.WriteString("Type");
8891     json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
8892 
8893     json.WriteString("Size");
8894     json.WriteNumber(size);
8895 
8896     json.EndObject();
8897 }
8898 
PrintDetailedMap_End(class VmaJsonWriter & json)8899 void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
8900 {
8901     json.EndArray();
8902     json.EndObject();
8903 }
8904 
8905 #endif // #if VMA_STATS_STRING_ENABLED
8906 
8907 ////////////////////////////////////////////////////////////////////////////////
8908 // class VmaBlockMetadata_Generic
8909 
VmaBlockMetadata_Generic(VmaAllocator hAllocator)8910 VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) :
8911     VmaBlockMetadata(hAllocator),
8912     m_FreeCount(0),
8913     m_SumFreeSize(0),
8914     m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8915     m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
8916 {
8917 }
8918 
~VmaBlockMetadata_Generic()8919 VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
8920 {
8921 }
8922 
Init(VkDeviceSize size)8923 void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
8924 {
8925     VmaBlockMetadata::Init(size);
8926 
8927     m_FreeCount = 1;
8928     m_SumFreeSize = size;
8929 
8930     VmaSuballocation suballoc = {};
8931     suballoc.offset = 0;
8932     suballoc.size = size;
8933     suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8934     suballoc.hAllocation = VK_NULL_HANDLE;
8935 
8936     VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
8937     m_Suballocations.push_back(suballoc);
8938     VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
8939     --suballocItem;
8940     m_FreeSuballocationsBySize.push_back(suballocItem);
8941 }
8942 
Validate()8943 bool VmaBlockMetadata_Generic::Validate() const
8944 {
8945     VMA_VALIDATE(!m_Suballocations.empty());
8946 
8947     // Expected offset of new suballocation as calculated from previous ones.
8948     VkDeviceSize calculatedOffset = 0;
8949     // Expected number of free suballocations as calculated from traversing their list.
8950     uint32_t calculatedFreeCount = 0;
8951     // Expected sum size of free suballocations as calculated from traversing their list.
8952     VkDeviceSize calculatedSumFreeSize = 0;
8953     // Expected number of free suballocations that should be registered in
8954     // m_FreeSuballocationsBySize calculated from traversing their list.
8955     size_t freeSuballocationsToRegister = 0;
8956     // True if previous visited suballocation was free.
8957     bool prevFree = false;
8958 
8959     for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
8960         suballocItem != m_Suballocations.cend();
8961         ++suballocItem)
8962     {
8963         const VmaSuballocation& subAlloc = *suballocItem;
8964 
8965         // Actual offset of this suballocation doesn't match expected one.
8966         VMA_VALIDATE(subAlloc.offset == calculatedOffset);
8967 
8968         const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
8969         // Two adjacent free suballocations are invalid. They should be merged.
8970         VMA_VALIDATE(!prevFree || !currFree);
8971 
8972         VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE));
8973 
8974         if(currFree)
8975         {
8976             calculatedSumFreeSize += subAlloc.size;
8977             ++calculatedFreeCount;
8978             if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8979             {
8980                 ++freeSuballocationsToRegister;
8981             }
8982 
8983             // Margin required between allocations - every free space must be at least that large.
8984             VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);
8985         }
8986         else
8987         {
8988             VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset);
8989             VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size);
8990 
8991             // Margin required between allocations - previous allocation must be free.
8992             VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);
8993         }
8994 
8995         calculatedOffset += subAlloc.size;
8996         prevFree = currFree;
8997     }
8998 
8999     // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
9000     // match expected one.
9001     VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
9002 
9003     VkDeviceSize lastSize = 0;
9004     for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
9005     {
9006         VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
9007 
9008         // Only free suballocations can be registered in m_FreeSuballocationsBySize.
9009         VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
9010         // They must be sorted by size ascending.
9011         VMA_VALIDATE(suballocItem->size >= lastSize);
9012 
9013         lastSize = suballocItem->size;
9014     }
9015 
9016     // Check if totals match calculacted values.
9017     VMA_VALIDATE(ValidateFreeSuballocationList());
9018     VMA_VALIDATE(calculatedOffset == GetSize());
9019     VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
9020     VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
9021 
9022     return true;
9023 }
9024 
GetUnusedRangeSizeMax()9025 VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
9026 {
9027     if(!m_FreeSuballocationsBySize.empty())
9028     {
9029         return m_FreeSuballocationsBySize.back()->size;
9030     }
9031     else
9032     {
9033         return 0;
9034     }
9035 }
9036 
IsEmpty()9037 bool VmaBlockMetadata_Generic::IsEmpty() const
9038 {
9039     return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
9040 }
9041 
CalcAllocationStatInfo(VmaStatInfo & outInfo)9042 void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
9043 {
9044     outInfo.blockCount = 1;
9045 
9046     const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
9047     outInfo.allocationCount = rangeCount - m_FreeCount;
9048     outInfo.unusedRangeCount = m_FreeCount;
9049 
9050     outInfo.unusedBytes = m_SumFreeSize;
9051     outInfo.usedBytes = GetSize() - outInfo.unusedBytes;
9052 
9053     outInfo.allocationSizeMin = UINT64_MAX;
9054     outInfo.allocationSizeMax = 0;
9055     outInfo.unusedRangeSizeMin = UINT64_MAX;
9056     outInfo.unusedRangeSizeMax = 0;
9057 
9058     for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
9059         suballocItem != m_Suballocations.cend();
9060         ++suballocItem)
9061     {
9062         const VmaSuballocation& suballoc = *suballocItem;
9063         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
9064         {
9065             outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9066             outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
9067         }
9068         else
9069         {
9070             outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
9071             outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
9072         }
9073     }
9074 }
9075 
AddPoolStats(VmaPoolStats & inoutStats)9076 void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
9077 {
9078     const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
9079 
9080     inoutStats.size += GetSize();
9081     inoutStats.unusedSize += m_SumFreeSize;
9082     inoutStats.allocationCount += rangeCount - m_FreeCount;
9083     inoutStats.unusedRangeCount += m_FreeCount;
9084     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
9085 }
9086 
9087 #if VMA_STATS_STRING_ENABLED
9088 
PrintDetailedMap(class VmaJsonWriter & json)9089 void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
9090 {
9091     PrintDetailedMap_Begin(json,
9092         m_SumFreeSize, // unusedBytes
9093         m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
9094         m_FreeCount); // unusedRangeCount
9095 
9096     size_t i = 0;
9097     for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
9098         suballocItem != m_Suballocations.cend();
9099         ++suballocItem, ++i)
9100     {
9101         if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9102         {
9103             PrintDetailedMap_UnusedRange(json, suballocItem->offset, suballocItem->size);
9104         }
9105         else
9106         {
9107             PrintDetailedMap_Allocation(json, suballocItem->offset, suballocItem->hAllocation);
9108         }
9109     }
9110 
9111     PrintDetailedMap_End(json);
9112 }
9113 
9114 #endif // #if VMA_STATS_STRING_ENABLED
9115 
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)9116 bool VmaBlockMetadata_Generic::CreateAllocationRequest(
9117     uint32_t currentFrameIndex,
9118     uint32_t frameInUseCount,
9119     VkDeviceSize bufferImageGranularity,
9120     VkDeviceSize allocSize,
9121     VkDeviceSize allocAlignment,
9122     bool upperAddress,
9123     VmaSuballocationType allocType,
9124     bool canMakeOtherLost,
9125     uint32_t strategy,
9126     VmaAllocationRequest* pAllocationRequest)
9127 {
9128     VMA_ASSERT(allocSize > 0);
9129     VMA_ASSERT(!upperAddress);
9130     VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9131     VMA_ASSERT(pAllocationRequest != VMA_NULL);
9132     VMA_HEAVY_ASSERT(Validate());
9133 
9134     pAllocationRequest->type = VmaAllocationRequestType::Normal;
9135 
9136     // There is not enough total free space in this block to fullfill the request: Early return.
9137     if(canMakeOtherLost == false &&
9138         m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN)
9139     {
9140         return false;
9141     }
9142 
9143     // New algorithm, efficiently searching freeSuballocationsBySize.
9144     const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
9145     if(freeSuballocCount > 0)
9146     {
9147         if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
9148         {
9149             // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN.
9150             VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
9151                 m_FreeSuballocationsBySize.data(),
9152                 m_FreeSuballocationsBySize.data() + freeSuballocCount,
9153                 allocSize + 2 * VMA_DEBUG_MARGIN,
9154                 VmaSuballocationItemSizeLess());
9155             size_t index = it - m_FreeSuballocationsBySize.data();
9156             for(; index < freeSuballocCount; ++index)
9157             {
9158                 if(CheckAllocation(
9159                     currentFrameIndex,
9160                     frameInUseCount,
9161                     bufferImageGranularity,
9162                     allocSize,
9163                     allocAlignment,
9164                     allocType,
9165                     m_FreeSuballocationsBySize[index],
9166                     false, // canMakeOtherLost
9167                     &pAllocationRequest->offset,
9168                     &pAllocationRequest->itemsToMakeLostCount,
9169                     &pAllocationRequest->sumFreeSize,
9170                     &pAllocationRequest->sumItemSize))
9171                 {
9172                     pAllocationRequest->item = m_FreeSuballocationsBySize[index];
9173                     return true;
9174                 }
9175             }
9176         }
9177         else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
9178         {
9179             for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9180                 it != m_Suballocations.end();
9181                 ++it)
9182             {
9183                 if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
9184                     currentFrameIndex,
9185                     frameInUseCount,
9186                     bufferImageGranularity,
9187                     allocSize,
9188                     allocAlignment,
9189                     allocType,
9190                     it,
9191                     false, // canMakeOtherLost
9192                     &pAllocationRequest->offset,
9193                     &pAllocationRequest->itemsToMakeLostCount,
9194                     &pAllocationRequest->sumFreeSize,
9195                     &pAllocationRequest->sumItemSize))
9196                 {
9197                     pAllocationRequest->item = it;
9198                     return true;
9199                 }
9200             }
9201         }
9202         else // WORST_FIT, FIRST_FIT
9203         {
9204             // Search staring from biggest suballocations.
9205             for(size_t index = freeSuballocCount; index--; )
9206             {
9207                 if(CheckAllocation(
9208                     currentFrameIndex,
9209                     frameInUseCount,
9210                     bufferImageGranularity,
9211                     allocSize,
9212                     allocAlignment,
9213                     allocType,
9214                     m_FreeSuballocationsBySize[index],
9215                     false, // canMakeOtherLost
9216                     &pAllocationRequest->offset,
9217                     &pAllocationRequest->itemsToMakeLostCount,
9218                     &pAllocationRequest->sumFreeSize,
9219                     &pAllocationRequest->sumItemSize))
9220                 {
9221                     pAllocationRequest->item = m_FreeSuballocationsBySize[index];
9222                     return true;
9223                 }
9224             }
9225         }
9226     }
9227 
9228     if(canMakeOtherLost)
9229     {
9230         // Brute-force algorithm. TODO: Come up with something better.
9231 
9232         bool found = false;
9233         VmaAllocationRequest tmpAllocRequest = {};
9234         tmpAllocRequest.type = VmaAllocationRequestType::Normal;
9235         for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
9236             suballocIt != m_Suballocations.end();
9237             ++suballocIt)
9238         {
9239             if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
9240                 suballocIt->hAllocation->CanBecomeLost())
9241             {
9242                 if(CheckAllocation(
9243                     currentFrameIndex,
9244                     frameInUseCount,
9245                     bufferImageGranularity,
9246                     allocSize,
9247                     allocAlignment,
9248                     allocType,
9249                     suballocIt,
9250                     canMakeOtherLost,
9251                     &tmpAllocRequest.offset,
9252                     &tmpAllocRequest.itemsToMakeLostCount,
9253                     &tmpAllocRequest.sumFreeSize,
9254                     &tmpAllocRequest.sumItemSize))
9255                 {
9256                     if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
9257                     {
9258                         *pAllocationRequest = tmpAllocRequest;
9259                         pAllocationRequest->item = suballocIt;
9260                         break;
9261                     }
9262                     if(!found || tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost())
9263                     {
9264                         *pAllocationRequest = tmpAllocRequest;
9265                         pAllocationRequest->item = suballocIt;
9266                         found = true;
9267                     }
9268                 }
9269             }
9270         }
9271 
9272         return found;
9273     }
9274 
9275     return false;
9276 }
9277 
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)9278 bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
9279     uint32_t currentFrameIndex,
9280     uint32_t frameInUseCount,
9281     VmaAllocationRequest* pAllocationRequest)
9282 {
9283     VMA_ASSERT(pAllocationRequest && pAllocationRequest->type == VmaAllocationRequestType::Normal);
9284 
9285     while(pAllocationRequest->itemsToMakeLostCount > 0)
9286     {
9287         if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
9288         {
9289             ++pAllocationRequest->item;
9290         }
9291         VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
9292         VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
9293         VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
9294         if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
9295         {
9296             pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
9297             --pAllocationRequest->itemsToMakeLostCount;
9298         }
9299         else
9300         {
9301             return false;
9302         }
9303     }
9304 
9305     VMA_HEAVY_ASSERT(Validate());
9306     VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
9307     VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
9308 
9309     return true;
9310 }
9311 
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)9312 uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
9313 {
9314     uint32_t lostAllocationCount = 0;
9315     for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9316         it != m_Suballocations.end();
9317         ++it)
9318     {
9319         if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
9320             it->hAllocation->CanBecomeLost() &&
9321             it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
9322         {
9323             it = FreeSuballocation(it);
9324             ++lostAllocationCount;
9325         }
9326     }
9327     return lostAllocationCount;
9328 }
9329 
CheckCorruption(const void * pBlockData)9330 VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
9331 {
9332     for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9333         it != m_Suballocations.end();
9334         ++it)
9335     {
9336         if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
9337         {
9338             if(!VmaValidateMagicValue(pBlockData, it->offset - VMA_DEBUG_MARGIN))
9339             {
9340                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
9341                 return VK_ERROR_VALIDATION_FAILED_EXT;
9342             }
9343             if(!VmaValidateMagicValue(pBlockData, it->offset + it->size))
9344             {
9345                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
9346                 return VK_ERROR_VALIDATION_FAILED_EXT;
9347             }
9348         }
9349     }
9350 
9351     return VK_SUCCESS;
9352 }
9353 
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,VmaAllocation hAllocation)9354 void VmaBlockMetadata_Generic::Alloc(
9355     const VmaAllocationRequest& request,
9356     VmaSuballocationType type,
9357     VkDeviceSize allocSize,
9358     VmaAllocation hAllocation)
9359 {
9360     VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
9361     VMA_ASSERT(request.item != m_Suballocations.end());
9362     VmaSuballocation& suballoc = *request.item;
9363     // Given suballocation is a free block.
9364     VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9365     // Given offset is inside this suballocation.
9366     VMA_ASSERT(request.offset >= suballoc.offset);
9367     const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
9368     VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
9369     const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
9370 
9371     // Unregister this free suballocation from m_FreeSuballocationsBySize and update
9372     // it to become used.
9373     UnregisterFreeSuballocation(request.item);
9374 
9375     suballoc.offset = request.offset;
9376     suballoc.size = allocSize;
9377     suballoc.type = type;
9378     suballoc.hAllocation = hAllocation;
9379 
9380     // If there are any free bytes remaining at the end, insert new free suballocation after current one.
9381     if(paddingEnd)
9382     {
9383         VmaSuballocation paddingSuballoc = {};
9384         paddingSuballoc.offset = request.offset + allocSize;
9385         paddingSuballoc.size = paddingEnd;
9386         paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9387         VmaSuballocationList::iterator next = request.item;
9388         ++next;
9389         const VmaSuballocationList::iterator paddingEndItem =
9390             m_Suballocations.insert(next, paddingSuballoc);
9391         RegisterFreeSuballocation(paddingEndItem);
9392     }
9393 
9394     // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
9395     if(paddingBegin)
9396     {
9397         VmaSuballocation paddingSuballoc = {};
9398         paddingSuballoc.offset = request.offset - paddingBegin;
9399         paddingSuballoc.size = paddingBegin;
9400         paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9401         const VmaSuballocationList::iterator paddingBeginItem =
9402             m_Suballocations.insert(request.item, paddingSuballoc);
9403         RegisterFreeSuballocation(paddingBeginItem);
9404     }
9405 
9406     // Update totals.
9407     m_FreeCount = m_FreeCount - 1;
9408     if(paddingBegin > 0)
9409     {
9410         ++m_FreeCount;
9411     }
9412     if(paddingEnd > 0)
9413     {
9414         ++m_FreeCount;
9415     }
9416     m_SumFreeSize -= allocSize;
9417 }
9418 
Free(const VmaAllocation allocation)9419 void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation)
9420 {
9421     for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
9422         suballocItem != m_Suballocations.end();
9423         ++suballocItem)
9424     {
9425         VmaSuballocation& suballoc = *suballocItem;
9426         if(suballoc.hAllocation == allocation)
9427         {
9428             FreeSuballocation(suballocItem);
9429             VMA_HEAVY_ASSERT(Validate());
9430             return;
9431         }
9432     }
9433     VMA_ASSERT(0 && "Not found!");
9434 }
9435 
FreeAtOffset(VkDeviceSize offset)9436 void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
9437 {
9438     for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
9439         suballocItem != m_Suballocations.end();
9440         ++suballocItem)
9441     {
9442         VmaSuballocation& suballoc = *suballocItem;
9443         if(suballoc.offset == offset)
9444         {
9445             FreeSuballocation(suballocItem);
9446             return;
9447         }
9448     }
9449     VMA_ASSERT(0 && "Not found!");
9450 }
9451 
ValidateFreeSuballocationList()9452 bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
9453 {
9454     VkDeviceSize lastSize = 0;
9455     for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
9456     {
9457         const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
9458 
9459         VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
9460         VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
9461         VMA_VALIDATE(it->size >= lastSize);
9462         lastSize = it->size;
9463     }
9464     return true;
9465 }
9466 
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)9467 bool VmaBlockMetadata_Generic::CheckAllocation(
9468     uint32_t currentFrameIndex,
9469     uint32_t frameInUseCount,
9470     VkDeviceSize bufferImageGranularity,
9471     VkDeviceSize allocSize,
9472     VkDeviceSize allocAlignment,
9473     VmaSuballocationType allocType,
9474     VmaSuballocationList::const_iterator suballocItem,
9475     bool canMakeOtherLost,
9476     VkDeviceSize* pOffset,
9477     size_t* itemsToMakeLostCount,
9478     VkDeviceSize* pSumFreeSize,
9479     VkDeviceSize* pSumItemSize) const
9480 {
9481     VMA_ASSERT(allocSize > 0);
9482     VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9483     VMA_ASSERT(suballocItem != m_Suballocations.cend());
9484     VMA_ASSERT(pOffset != VMA_NULL);
9485 
9486     *itemsToMakeLostCount = 0;
9487     *pSumFreeSize = 0;
9488     *pSumItemSize = 0;
9489 
9490     if(canMakeOtherLost)
9491     {
9492         if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9493         {
9494             *pSumFreeSize = suballocItem->size;
9495         }
9496         else
9497         {
9498             if(suballocItem->hAllocation->CanBecomeLost() &&
9499                 suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9500             {
9501                 ++*itemsToMakeLostCount;
9502                 *pSumItemSize = suballocItem->size;
9503             }
9504             else
9505             {
9506                 return false;
9507             }
9508         }
9509 
9510         // Remaining size is too small for this request: Early return.
9511         if(GetSize() - suballocItem->offset < allocSize)
9512         {
9513             return false;
9514         }
9515 
9516         // Start from offset equal to beginning of this suballocation.
9517         *pOffset = suballocItem->offset;
9518 
9519         // Apply VMA_DEBUG_MARGIN at the beginning.
9520         if(VMA_DEBUG_MARGIN > 0)
9521         {
9522             *pOffset += VMA_DEBUG_MARGIN;
9523         }
9524 
9525         // Apply alignment.
9526         *pOffset = VmaAlignUp(*pOffset, allocAlignment);
9527 
9528         // Check previous suballocations for BufferImageGranularity conflicts.
9529         // Make bigger alignment if necessary.
9530         if(bufferImageGranularity > 1)
9531         {
9532             bool bufferImageGranularityConflict = false;
9533             VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
9534             while(prevSuballocItem != m_Suballocations.cbegin())
9535             {
9536                 --prevSuballocItem;
9537                 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
9538                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
9539                 {
9540                     if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9541                     {
9542                         bufferImageGranularityConflict = true;
9543                         break;
9544                     }
9545                 }
9546                 else
9547                     // Already on previous page.
9548                     break;
9549             }
9550             if(bufferImageGranularityConflict)
9551             {
9552                 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
9553             }
9554         }
9555 
9556         // Now that we have final *pOffset, check if we are past suballocItem.
9557         // If yes, return false - this function should be called for another suballocItem as starting point.
9558         if(*pOffset >= suballocItem->offset + suballocItem->size)
9559         {
9560             return false;
9561         }
9562 
9563         // Calculate padding at the beginning based on current offset.
9564         const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
9565 
9566         // Calculate required margin at the end.
9567         const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
9568 
9569         const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
9570         // Another early return check.
9571         if(suballocItem->offset + totalSize > GetSize())
9572         {
9573             return false;
9574         }
9575 
9576         // Advance lastSuballocItem until desired size is reached.
9577         // Update itemsToMakeLostCount.
9578         VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
9579         if(totalSize > suballocItem->size)
9580         {
9581             VkDeviceSize remainingSize = totalSize - suballocItem->size;
9582             while(remainingSize > 0)
9583             {
9584                 ++lastSuballocItem;
9585                 if(lastSuballocItem == m_Suballocations.cend())
9586                 {
9587                     return false;
9588                 }
9589                 if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9590                 {
9591                     *pSumFreeSize += lastSuballocItem->size;
9592                 }
9593                 else
9594                 {
9595                     VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
9596                     if(lastSuballocItem->hAllocation->CanBecomeLost() &&
9597                         lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9598                     {
9599                         ++*itemsToMakeLostCount;
9600                         *pSumItemSize += lastSuballocItem->size;
9601                     }
9602                     else
9603                     {
9604                         return false;
9605                     }
9606                 }
9607                 remainingSize = (lastSuballocItem->size < remainingSize) ?
9608                     remainingSize - lastSuballocItem->size : 0;
9609             }
9610         }
9611 
9612         // Check next suballocations for BufferImageGranularity conflicts.
9613         // If conflict exists, we must mark more allocations lost or fail.
9614         if(bufferImageGranularity > 1)
9615         {
9616             VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
9617             ++nextSuballocItem;
9618             while(nextSuballocItem != m_Suballocations.cend())
9619             {
9620                 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
9621                 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9622                 {
9623                     if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9624                     {
9625                         VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
9626                         if(nextSuballoc.hAllocation->CanBecomeLost() &&
9627                             nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9628                         {
9629                             ++*itemsToMakeLostCount;
9630                         }
9631                         else
9632                         {
9633                             return false;
9634                         }
9635                     }
9636                 }
9637                 else
9638                 {
9639                     // Already on next page.
9640                     break;
9641                 }
9642                 ++nextSuballocItem;
9643             }
9644         }
9645     }
9646     else
9647     {
9648         const VmaSuballocation& suballoc = *suballocItem;
9649         VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9650 
9651         *pSumFreeSize = suballoc.size;
9652 
9653         // Size of this suballocation is too small for this request: Early return.
9654         if(suballoc.size < allocSize)
9655         {
9656             return false;
9657         }
9658 
9659         // Start from offset equal to beginning of this suballocation.
9660         *pOffset = suballoc.offset;
9661 
9662         // Apply VMA_DEBUG_MARGIN at the beginning.
9663         if(VMA_DEBUG_MARGIN > 0)
9664         {
9665             *pOffset += VMA_DEBUG_MARGIN;
9666         }
9667 
9668         // Apply alignment.
9669         *pOffset = VmaAlignUp(*pOffset, allocAlignment);
9670 
9671         // Check previous suballocations for BufferImageGranularity conflicts.
9672         // Make bigger alignment if necessary.
9673         if(bufferImageGranularity > 1)
9674         {
9675             bool bufferImageGranularityConflict = false;
9676             VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
9677             while(prevSuballocItem != m_Suballocations.cbegin())
9678             {
9679                 --prevSuballocItem;
9680                 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
9681                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
9682                 {
9683                     if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9684                     {
9685                         bufferImageGranularityConflict = true;
9686                         break;
9687                     }
9688                 }
9689                 else
9690                     // Already on previous page.
9691                     break;
9692             }
9693             if(bufferImageGranularityConflict)
9694             {
9695                 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
9696             }
9697         }
9698 
9699         // Calculate padding at the beginning based on current offset.
9700         const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
9701 
9702         // Calculate required margin at the end.
9703         const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
9704 
9705         // Fail if requested size plus margin before and after is bigger than size of this suballocation.
9706         if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
9707         {
9708             return false;
9709         }
9710 
9711         // Check next suballocations for BufferImageGranularity conflicts.
9712         // If conflict exists, allocation cannot be made here.
9713         if(bufferImageGranularity > 1)
9714         {
9715             VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
9716             ++nextSuballocItem;
9717             while(nextSuballocItem != m_Suballocations.cend())
9718             {
9719                 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
9720                 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9721                 {
9722                     if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9723                     {
9724                         return false;
9725                     }
9726                 }
9727                 else
9728                 {
9729                     // Already on next page.
9730                     break;
9731                 }
9732                 ++nextSuballocItem;
9733             }
9734         }
9735     }
9736 
9737     // All tests passed: Success. pOffset is already filled.
9738     return true;
9739 }
9740 
MergeFreeWithNext(VmaSuballocationList::iterator item)9741 void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
9742 {
9743     VMA_ASSERT(item != m_Suballocations.end());
9744     VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9745 
9746     VmaSuballocationList::iterator nextItem = item;
9747     ++nextItem;
9748     VMA_ASSERT(nextItem != m_Suballocations.end());
9749     VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
9750 
9751     item->size += nextItem->size;
9752     --m_FreeCount;
9753     m_Suballocations.erase(nextItem);
9754 }
9755 
FreeSuballocation(VmaSuballocationList::iterator suballocItem)9756 VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
9757 {
9758     // Change this suballocation to be marked as free.
9759     VmaSuballocation& suballoc = *suballocItem;
9760     suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9761     suballoc.hAllocation = VK_NULL_HANDLE;
9762 
9763     // Update totals.
9764     ++m_FreeCount;
9765     m_SumFreeSize += suballoc.size;
9766 
9767     // Merge with previous and/or next suballocation if it's also free.
9768     bool mergeWithNext = false;
9769     bool mergeWithPrev = false;
9770 
9771     VmaSuballocationList::iterator nextItem = suballocItem;
9772     ++nextItem;
9773     if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
9774     {
9775         mergeWithNext = true;
9776     }
9777 
9778     VmaSuballocationList::iterator prevItem = suballocItem;
9779     if(suballocItem != m_Suballocations.begin())
9780     {
9781         --prevItem;
9782         if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9783         {
9784             mergeWithPrev = true;
9785         }
9786     }
9787 
9788     if(mergeWithNext)
9789     {
9790         UnregisterFreeSuballocation(nextItem);
9791         MergeFreeWithNext(suballocItem);
9792     }
9793 
9794     if(mergeWithPrev)
9795     {
9796         UnregisterFreeSuballocation(prevItem);
9797         MergeFreeWithNext(prevItem);
9798         RegisterFreeSuballocation(prevItem);
9799         return prevItem;
9800     }
9801     else
9802     {
9803         RegisterFreeSuballocation(suballocItem);
9804         return suballocItem;
9805     }
9806 }
9807 
RegisterFreeSuballocation(VmaSuballocationList::iterator item)9808 void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
9809 {
9810     VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9811     VMA_ASSERT(item->size > 0);
9812 
9813     // You may want to enable this validation at the beginning or at the end of
9814     // this function, depending on what do you want to check.
9815     VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9816 
9817     if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
9818     {
9819         if(m_FreeSuballocationsBySize.empty())
9820         {
9821             m_FreeSuballocationsBySize.push_back(item);
9822         }
9823         else
9824         {
9825             VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
9826         }
9827     }
9828 
9829     //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9830 }
9831 
9832 
UnregisterFreeSuballocation(VmaSuballocationList::iterator item)9833 void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
9834 {
9835     VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9836     VMA_ASSERT(item->size > 0);
9837 
9838     // You may want to enable this validation at the beginning or at the end of
9839     // this function, depending on what do you want to check.
9840     VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9841 
9842     if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
9843     {
9844         VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
9845             m_FreeSuballocationsBySize.data(),
9846             m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
9847             item,
9848             VmaSuballocationItemSizeLess());
9849         for(size_t index = it - m_FreeSuballocationsBySize.data();
9850             index < m_FreeSuballocationsBySize.size();
9851             ++index)
9852         {
9853             if(m_FreeSuballocationsBySize[index] == item)
9854             {
9855                 VmaVectorRemove(m_FreeSuballocationsBySize, index);
9856                 return;
9857             }
9858             VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
9859         }
9860         VMA_ASSERT(0 && "Not found.");
9861     }
9862 
9863     //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9864 }
9865 
IsBufferImageGranularityConflictPossible(VkDeviceSize bufferImageGranularity,VmaSuballocationType & inOutPrevSuballocType)9866 bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
9867     VkDeviceSize bufferImageGranularity,
9868     VmaSuballocationType& inOutPrevSuballocType) const
9869 {
9870     if(bufferImageGranularity == 1 || IsEmpty())
9871     {
9872         return false;
9873     }
9874 
9875     VkDeviceSize minAlignment = VK_WHOLE_SIZE;
9876     bool typeConflictFound = false;
9877     for(VmaSuballocationList::const_iterator it = m_Suballocations.cbegin();
9878         it != m_Suballocations.cend();
9879         ++it)
9880     {
9881         const VmaSuballocationType suballocType = it->type;
9882         if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)
9883         {
9884             minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment());
9885             if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))
9886             {
9887                 typeConflictFound = true;
9888             }
9889             inOutPrevSuballocType = suballocType;
9890         }
9891     }
9892 
9893     return typeConflictFound || minAlignment >= bufferImageGranularity;
9894 }
9895 
9896 ////////////////////////////////////////////////////////////////////////////////
9897 // class VmaBlockMetadata_Linear
9898 
VmaBlockMetadata_Linear(VmaAllocator hAllocator)9899 VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) :
9900     VmaBlockMetadata(hAllocator),
9901     m_SumFreeSize(0),
9902     m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
9903     m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
9904     m_1stVectorIndex(0),
9905     m_2ndVectorMode(SECOND_VECTOR_EMPTY),
9906     m_1stNullItemsBeginCount(0),
9907     m_1stNullItemsMiddleCount(0),
9908     m_2ndNullItemsCount(0)
9909 {
9910 }
9911 
~VmaBlockMetadata_Linear()9912 VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
9913 {
9914 }
9915 
Init(VkDeviceSize size)9916 void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
9917 {
9918     VmaBlockMetadata::Init(size);
9919     m_SumFreeSize = size;
9920 }
9921 
Validate()9922 bool VmaBlockMetadata_Linear::Validate() const
9923 {
9924     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9925     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9926 
9927     VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
9928     VMA_VALIDATE(!suballocations1st.empty() ||
9929         suballocations2nd.empty() ||
9930         m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
9931 
9932     if(!suballocations1st.empty())
9933     {
9934         // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
9935         VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE);
9936         // Null item at the end should be just pop_back().
9937         VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE);
9938     }
9939     if(!suballocations2nd.empty())
9940     {
9941         // Null item at the end should be just pop_back().
9942         VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE);
9943     }
9944 
9945     VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
9946     VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
9947 
9948     VkDeviceSize sumUsedSize = 0;
9949     const size_t suballoc1stCount = suballocations1st.size();
9950     VkDeviceSize offset = VMA_DEBUG_MARGIN;
9951 
9952     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9953     {
9954         const size_t suballoc2ndCount = suballocations2nd.size();
9955         size_t nullItem2ndCount = 0;
9956         for(size_t i = 0; i < suballoc2ndCount; ++i)
9957         {
9958             const VmaSuballocation& suballoc = suballocations2nd[i];
9959             const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9960 
9961             VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
9962             VMA_VALIDATE(suballoc.offset >= offset);
9963 
9964             if(!currFree)
9965             {
9966                 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
9967                 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
9968                 sumUsedSize += suballoc.size;
9969             }
9970             else
9971             {
9972                 ++nullItem2ndCount;
9973             }
9974 
9975             offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
9976         }
9977 
9978         VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
9979     }
9980 
9981     for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
9982     {
9983         const VmaSuballocation& suballoc = suballocations1st[i];
9984         VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
9985             suballoc.hAllocation == VK_NULL_HANDLE);
9986     }
9987 
9988     size_t nullItem1stCount = m_1stNullItemsBeginCount;
9989 
9990     for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
9991     {
9992         const VmaSuballocation& suballoc = suballocations1st[i];
9993         const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9994 
9995         VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
9996         VMA_VALIDATE(suballoc.offset >= offset);
9997         VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
9998 
9999         if(!currFree)
10000         {
10001             VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
10002             VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
10003             sumUsedSize += suballoc.size;
10004         }
10005         else
10006         {
10007             ++nullItem1stCount;
10008         }
10009 
10010         offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
10011     }
10012     VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
10013 
10014     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10015     {
10016         const size_t suballoc2ndCount = suballocations2nd.size();
10017         size_t nullItem2ndCount = 0;
10018         for(size_t i = suballoc2ndCount; i--; )
10019         {
10020             const VmaSuballocation& suballoc = suballocations2nd[i];
10021             const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
10022 
10023             VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
10024             VMA_VALIDATE(suballoc.offset >= offset);
10025 
10026             if(!currFree)
10027             {
10028                 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
10029                 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
10030                 sumUsedSize += suballoc.size;
10031             }
10032             else
10033             {
10034                 ++nullItem2ndCount;
10035             }
10036 
10037             offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
10038         }
10039 
10040         VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
10041     }
10042 
10043     VMA_VALIDATE(offset <= GetSize());
10044     VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
10045 
10046     return true;
10047 }
10048 
GetAllocationCount()10049 size_t VmaBlockMetadata_Linear::GetAllocationCount() const
10050 {
10051     return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) +
10052         AccessSuballocations2nd().size() - m_2ndNullItemsCount;
10053 }
10054 
GetUnusedRangeSizeMax()10055 VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
10056 {
10057     const VkDeviceSize size = GetSize();
10058 
10059     /*
10060     We don't consider gaps inside allocation vectors with freed allocations because
10061     they are not suitable for reuse in linear allocator. We consider only space that
10062     is available for new allocations.
10063     */
10064     if(IsEmpty())
10065     {
10066         return size;
10067     }
10068 
10069     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10070 
10071     switch(m_2ndVectorMode)
10072     {
10073     case SECOND_VECTOR_EMPTY:
10074         /*
10075         Available space is after end of 1st, as well as before beginning of 1st (which
10076         whould make it a ring buffer).
10077         */
10078         {
10079             const size_t suballocations1stCount = suballocations1st.size();
10080             VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
10081             const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
10082             const VmaSuballocation& lastSuballoc  = suballocations1st[suballocations1stCount - 1];
10083             return VMA_MAX(
10084                 firstSuballoc.offset,
10085                 size - (lastSuballoc.offset + lastSuballoc.size));
10086         }
10087         break;
10088 
10089     case SECOND_VECTOR_RING_BUFFER:
10090         /*
10091         Available space is only between end of 2nd and beginning of 1st.
10092         */
10093         {
10094             const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10095             const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
10096             const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
10097             return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
10098         }
10099         break;
10100 
10101     case SECOND_VECTOR_DOUBLE_STACK:
10102         /*
10103         Available space is only between end of 1st and top of 2nd.
10104         */
10105         {
10106             const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10107             const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
10108             const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
10109             return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
10110         }
10111         break;
10112 
10113     default:
10114         VMA_ASSERT(0);
10115         return 0;
10116     }
10117 }
10118 
CalcAllocationStatInfo(VmaStatInfo & outInfo)10119 void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
10120 {
10121     const VkDeviceSize size = GetSize();
10122     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10123     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10124     const size_t suballoc1stCount = suballocations1st.size();
10125     const size_t suballoc2ndCount = suballocations2nd.size();
10126 
10127     outInfo.blockCount = 1;
10128     outInfo.allocationCount = (uint32_t)GetAllocationCount();
10129     outInfo.unusedRangeCount = 0;
10130     outInfo.usedBytes = 0;
10131     outInfo.allocationSizeMin = UINT64_MAX;
10132     outInfo.allocationSizeMax = 0;
10133     outInfo.unusedRangeSizeMin = UINT64_MAX;
10134     outInfo.unusedRangeSizeMax = 0;
10135 
10136     VkDeviceSize lastOffset = 0;
10137 
10138     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10139     {
10140         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10141         size_t nextAlloc2ndIndex = 0;
10142         while(lastOffset < freeSpace2ndTo1stEnd)
10143         {
10144             // Find next non-null allocation or move nextAllocIndex to the end.
10145             while(nextAlloc2ndIndex < suballoc2ndCount &&
10146                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10147             {
10148                 ++nextAlloc2ndIndex;
10149             }
10150 
10151             // Found non-null allocation.
10152             if(nextAlloc2ndIndex < suballoc2ndCount)
10153             {
10154                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10155 
10156                 // 1. Process free space before this allocation.
10157                 if(lastOffset < suballoc.offset)
10158                 {
10159                     // There is free space from lastOffset to suballoc.offset.
10160                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10161                     ++outInfo.unusedRangeCount;
10162                     outInfo.unusedBytes += unusedRangeSize;
10163                     outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10164                     outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10165                 }
10166 
10167                 // 2. Process this allocation.
10168                 // There is allocation with suballoc.offset, suballoc.size.
10169                 outInfo.usedBytes += suballoc.size;
10170                 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10171                 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10172 
10173                 // 3. Prepare for next iteration.
10174                 lastOffset = suballoc.offset + suballoc.size;
10175                 ++nextAlloc2ndIndex;
10176             }
10177             // We are at the end.
10178             else
10179             {
10180                 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10181                 if(lastOffset < freeSpace2ndTo1stEnd)
10182                 {
10183                     const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10184                     ++outInfo.unusedRangeCount;
10185                     outInfo.unusedBytes += unusedRangeSize;
10186                     outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10187                     outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10188                }
10189 
10190                 // End of loop.
10191                 lastOffset = freeSpace2ndTo1stEnd;
10192             }
10193         }
10194     }
10195 
10196     size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10197     const VkDeviceSize freeSpace1stTo2ndEnd =
10198         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10199     while(lastOffset < freeSpace1stTo2ndEnd)
10200     {
10201         // Find next non-null allocation or move nextAllocIndex to the end.
10202         while(nextAlloc1stIndex < suballoc1stCount &&
10203             suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10204         {
10205             ++nextAlloc1stIndex;
10206         }
10207 
10208         // Found non-null allocation.
10209         if(nextAlloc1stIndex < suballoc1stCount)
10210         {
10211             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10212 
10213             // 1. Process free space before this allocation.
10214             if(lastOffset < suballoc.offset)
10215             {
10216                 // There is free space from lastOffset to suballoc.offset.
10217                 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10218                 ++outInfo.unusedRangeCount;
10219                 outInfo.unusedBytes += unusedRangeSize;
10220                 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10221                 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10222             }
10223 
10224             // 2. Process this allocation.
10225             // There is allocation with suballoc.offset, suballoc.size.
10226             outInfo.usedBytes += suballoc.size;
10227             outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10228             outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10229 
10230             // 3. Prepare for next iteration.
10231             lastOffset = suballoc.offset + suballoc.size;
10232             ++nextAlloc1stIndex;
10233         }
10234         // We are at the end.
10235         else
10236         {
10237             // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10238             if(lastOffset < freeSpace1stTo2ndEnd)
10239             {
10240                 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10241                 ++outInfo.unusedRangeCount;
10242                 outInfo.unusedBytes += unusedRangeSize;
10243                 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10244                 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10245            }
10246 
10247             // End of loop.
10248             lastOffset = freeSpace1stTo2ndEnd;
10249         }
10250     }
10251 
10252     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10253     {
10254         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10255         while(lastOffset < size)
10256         {
10257             // Find next non-null allocation or move nextAllocIndex to the end.
10258             while(nextAlloc2ndIndex != SIZE_MAX &&
10259                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10260             {
10261                 --nextAlloc2ndIndex;
10262             }
10263 
10264             // Found non-null allocation.
10265             if(nextAlloc2ndIndex != SIZE_MAX)
10266             {
10267                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10268 
10269                 // 1. Process free space before this allocation.
10270                 if(lastOffset < suballoc.offset)
10271                 {
10272                     // There is free space from lastOffset to suballoc.offset.
10273                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10274                     ++outInfo.unusedRangeCount;
10275                     outInfo.unusedBytes += unusedRangeSize;
10276                     outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10277                     outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10278                 }
10279 
10280                 // 2. Process this allocation.
10281                 // There is allocation with suballoc.offset, suballoc.size.
10282                 outInfo.usedBytes += suballoc.size;
10283                 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10284                 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10285 
10286                 // 3. Prepare for next iteration.
10287                 lastOffset = suballoc.offset + suballoc.size;
10288                 --nextAlloc2ndIndex;
10289             }
10290             // We are at the end.
10291             else
10292             {
10293                 // There is free space from lastOffset to size.
10294                 if(lastOffset < size)
10295                 {
10296                     const VkDeviceSize unusedRangeSize = size - lastOffset;
10297                     ++outInfo.unusedRangeCount;
10298                     outInfo.unusedBytes += unusedRangeSize;
10299                     outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10300                     outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10301                }
10302 
10303                 // End of loop.
10304                 lastOffset = size;
10305             }
10306         }
10307     }
10308 
10309     outInfo.unusedBytes = size - outInfo.usedBytes;
10310 }
10311 
AddPoolStats(VmaPoolStats & inoutStats)10312 void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
10313 {
10314     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10315     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10316     const VkDeviceSize size = GetSize();
10317     const size_t suballoc1stCount = suballocations1st.size();
10318     const size_t suballoc2ndCount = suballocations2nd.size();
10319 
10320     inoutStats.size += size;
10321 
10322     VkDeviceSize lastOffset = 0;
10323 
10324     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10325     {
10326         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10327         size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
10328         while(lastOffset < freeSpace2ndTo1stEnd)
10329         {
10330             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10331             while(nextAlloc2ndIndex < suballoc2ndCount &&
10332                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10333             {
10334                 ++nextAlloc2ndIndex;
10335             }
10336 
10337             // Found non-null allocation.
10338             if(nextAlloc2ndIndex < suballoc2ndCount)
10339             {
10340                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10341 
10342                 // 1. Process free space before this allocation.
10343                 if(lastOffset < suballoc.offset)
10344                 {
10345                     // There is free space from lastOffset to suballoc.offset.
10346                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10347                     inoutStats.unusedSize += unusedRangeSize;
10348                     ++inoutStats.unusedRangeCount;
10349                     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10350                 }
10351 
10352                 // 2. Process this allocation.
10353                 // There is allocation with suballoc.offset, suballoc.size.
10354                 ++inoutStats.allocationCount;
10355 
10356                 // 3. Prepare for next iteration.
10357                 lastOffset = suballoc.offset + suballoc.size;
10358                 ++nextAlloc2ndIndex;
10359             }
10360             // We are at the end.
10361             else
10362             {
10363                 if(lastOffset < freeSpace2ndTo1stEnd)
10364                 {
10365                     // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10366                     const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10367                     inoutStats.unusedSize += unusedRangeSize;
10368                     ++inoutStats.unusedRangeCount;
10369                     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10370                 }
10371 
10372                 // End of loop.
10373                 lastOffset = freeSpace2ndTo1stEnd;
10374             }
10375         }
10376     }
10377 
10378     size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10379     const VkDeviceSize freeSpace1stTo2ndEnd =
10380         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10381     while(lastOffset < freeSpace1stTo2ndEnd)
10382     {
10383         // Find next non-null allocation or move nextAllocIndex to the end.
10384         while(nextAlloc1stIndex < suballoc1stCount &&
10385             suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10386         {
10387             ++nextAlloc1stIndex;
10388         }
10389 
10390         // Found non-null allocation.
10391         if(nextAlloc1stIndex < suballoc1stCount)
10392         {
10393             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10394 
10395             // 1. Process free space before this allocation.
10396             if(lastOffset < suballoc.offset)
10397             {
10398                 // There is free space from lastOffset to suballoc.offset.
10399                 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10400                 inoutStats.unusedSize += unusedRangeSize;
10401                 ++inoutStats.unusedRangeCount;
10402                 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10403             }
10404 
10405             // 2. Process this allocation.
10406             // There is allocation with suballoc.offset, suballoc.size.
10407             ++inoutStats.allocationCount;
10408 
10409             // 3. Prepare for next iteration.
10410             lastOffset = suballoc.offset + suballoc.size;
10411             ++nextAlloc1stIndex;
10412         }
10413         // We are at the end.
10414         else
10415         {
10416             if(lastOffset < freeSpace1stTo2ndEnd)
10417             {
10418                 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10419                 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10420                 inoutStats.unusedSize += unusedRangeSize;
10421                 ++inoutStats.unusedRangeCount;
10422                 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10423             }
10424 
10425             // End of loop.
10426             lastOffset = freeSpace1stTo2ndEnd;
10427         }
10428     }
10429 
10430     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10431     {
10432         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10433         while(lastOffset < size)
10434         {
10435             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10436             while(nextAlloc2ndIndex != SIZE_MAX &&
10437                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10438             {
10439                 --nextAlloc2ndIndex;
10440             }
10441 
10442             // Found non-null allocation.
10443             if(nextAlloc2ndIndex != SIZE_MAX)
10444             {
10445                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10446 
10447                 // 1. Process free space before this allocation.
10448                 if(lastOffset < suballoc.offset)
10449                 {
10450                     // There is free space from lastOffset to suballoc.offset.
10451                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10452                     inoutStats.unusedSize += unusedRangeSize;
10453                     ++inoutStats.unusedRangeCount;
10454                     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10455                 }
10456 
10457                 // 2. Process this allocation.
10458                 // There is allocation with suballoc.offset, suballoc.size.
10459                 ++inoutStats.allocationCount;
10460 
10461                 // 3. Prepare for next iteration.
10462                 lastOffset = suballoc.offset + suballoc.size;
10463                 --nextAlloc2ndIndex;
10464             }
10465             // We are at the end.
10466             else
10467             {
10468                 if(lastOffset < size)
10469                 {
10470                     // There is free space from lastOffset to size.
10471                     const VkDeviceSize unusedRangeSize = size - lastOffset;
10472                     inoutStats.unusedSize += unusedRangeSize;
10473                     ++inoutStats.unusedRangeCount;
10474                     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10475                 }
10476 
10477                 // End of loop.
10478                 lastOffset = size;
10479             }
10480         }
10481     }
10482 }
10483 
10484 #if VMA_STATS_STRING_ENABLED
PrintDetailedMap(class VmaJsonWriter & json)10485 void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
10486 {
10487     const VkDeviceSize size = GetSize();
10488     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10489     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10490     const size_t suballoc1stCount = suballocations1st.size();
10491     const size_t suballoc2ndCount = suballocations2nd.size();
10492 
10493     // FIRST PASS
10494 
10495     size_t unusedRangeCount = 0;
10496     VkDeviceSize usedBytes = 0;
10497 
10498     VkDeviceSize lastOffset = 0;
10499 
10500     size_t alloc2ndCount = 0;
10501     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10502     {
10503         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10504         size_t nextAlloc2ndIndex = 0;
10505         while(lastOffset < freeSpace2ndTo1stEnd)
10506         {
10507             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10508             while(nextAlloc2ndIndex < suballoc2ndCount &&
10509                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10510             {
10511                 ++nextAlloc2ndIndex;
10512             }
10513 
10514             // Found non-null allocation.
10515             if(nextAlloc2ndIndex < suballoc2ndCount)
10516             {
10517                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10518 
10519                 // 1. Process free space before this allocation.
10520                 if(lastOffset < suballoc.offset)
10521                 {
10522                     // There is free space from lastOffset to suballoc.offset.
10523                     ++unusedRangeCount;
10524                 }
10525 
10526                 // 2. Process this allocation.
10527                 // There is allocation with suballoc.offset, suballoc.size.
10528                 ++alloc2ndCount;
10529                 usedBytes += suballoc.size;
10530 
10531                 // 3. Prepare for next iteration.
10532                 lastOffset = suballoc.offset + suballoc.size;
10533                 ++nextAlloc2ndIndex;
10534             }
10535             // We are at the end.
10536             else
10537             {
10538                 if(lastOffset < freeSpace2ndTo1stEnd)
10539                 {
10540                     // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10541                     ++unusedRangeCount;
10542                 }
10543 
10544                 // End of loop.
10545                 lastOffset = freeSpace2ndTo1stEnd;
10546             }
10547         }
10548     }
10549 
10550     size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10551     size_t alloc1stCount = 0;
10552     const VkDeviceSize freeSpace1stTo2ndEnd =
10553         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10554     while(lastOffset < freeSpace1stTo2ndEnd)
10555     {
10556         // Find next non-null allocation or move nextAllocIndex to the end.
10557         while(nextAlloc1stIndex < suballoc1stCount &&
10558             suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10559         {
10560             ++nextAlloc1stIndex;
10561         }
10562 
10563         // Found non-null allocation.
10564         if(nextAlloc1stIndex < suballoc1stCount)
10565         {
10566             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10567 
10568             // 1. Process free space before this allocation.
10569             if(lastOffset < suballoc.offset)
10570             {
10571                 // There is free space from lastOffset to suballoc.offset.
10572                 ++unusedRangeCount;
10573             }
10574 
10575             // 2. Process this allocation.
10576             // There is allocation with suballoc.offset, suballoc.size.
10577             ++alloc1stCount;
10578             usedBytes += suballoc.size;
10579 
10580             // 3. Prepare for next iteration.
10581             lastOffset = suballoc.offset + suballoc.size;
10582             ++nextAlloc1stIndex;
10583         }
10584         // We are at the end.
10585         else
10586         {
10587             if(lastOffset < size)
10588             {
10589                 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10590                 ++unusedRangeCount;
10591             }
10592 
10593             // End of loop.
10594             lastOffset = freeSpace1stTo2ndEnd;
10595         }
10596     }
10597 
10598     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10599     {
10600         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10601         while(lastOffset < size)
10602         {
10603             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10604             while(nextAlloc2ndIndex != SIZE_MAX &&
10605                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10606             {
10607                 --nextAlloc2ndIndex;
10608             }
10609 
10610             // Found non-null allocation.
10611             if(nextAlloc2ndIndex != SIZE_MAX)
10612             {
10613                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10614 
10615                 // 1. Process free space before this allocation.
10616                 if(lastOffset < suballoc.offset)
10617                 {
10618                     // There is free space from lastOffset to suballoc.offset.
10619                     ++unusedRangeCount;
10620                 }
10621 
10622                 // 2. Process this allocation.
10623                 // There is allocation with suballoc.offset, suballoc.size.
10624                 ++alloc2ndCount;
10625                 usedBytes += suballoc.size;
10626 
10627                 // 3. Prepare for next iteration.
10628                 lastOffset = suballoc.offset + suballoc.size;
10629                 --nextAlloc2ndIndex;
10630             }
10631             // We are at the end.
10632             else
10633             {
10634                 if(lastOffset < size)
10635                 {
10636                     // There is free space from lastOffset to size.
10637                     ++unusedRangeCount;
10638                 }
10639 
10640                 // End of loop.
10641                 lastOffset = size;
10642             }
10643         }
10644     }
10645 
10646     const VkDeviceSize unusedBytes = size - usedBytes;
10647     PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
10648 
10649     // SECOND PASS
10650     lastOffset = 0;
10651 
10652     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10653     {
10654         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10655         size_t nextAlloc2ndIndex = 0;
10656         while(lastOffset < freeSpace2ndTo1stEnd)
10657         {
10658             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10659             while(nextAlloc2ndIndex < suballoc2ndCount &&
10660                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10661             {
10662                 ++nextAlloc2ndIndex;
10663             }
10664 
10665             // Found non-null allocation.
10666             if(nextAlloc2ndIndex < suballoc2ndCount)
10667             {
10668                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10669 
10670                 // 1. Process free space before this allocation.
10671                 if(lastOffset < suballoc.offset)
10672                 {
10673                     // There is free space from lastOffset to suballoc.offset.
10674                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10675                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10676                 }
10677 
10678                 // 2. Process this allocation.
10679                 // There is allocation with suballoc.offset, suballoc.size.
10680                 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10681 
10682                 // 3. Prepare for next iteration.
10683                 lastOffset = suballoc.offset + suballoc.size;
10684                 ++nextAlloc2ndIndex;
10685             }
10686             // We are at the end.
10687             else
10688             {
10689                 if(lastOffset < freeSpace2ndTo1stEnd)
10690                 {
10691                     // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10692                     const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10693                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10694                 }
10695 
10696                 // End of loop.
10697                 lastOffset = freeSpace2ndTo1stEnd;
10698             }
10699         }
10700     }
10701 
10702     nextAlloc1stIndex = m_1stNullItemsBeginCount;
10703     while(lastOffset < freeSpace1stTo2ndEnd)
10704     {
10705         // Find next non-null allocation or move nextAllocIndex to the end.
10706         while(nextAlloc1stIndex < suballoc1stCount &&
10707             suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10708         {
10709             ++nextAlloc1stIndex;
10710         }
10711 
10712         // Found non-null allocation.
10713         if(nextAlloc1stIndex < suballoc1stCount)
10714         {
10715             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10716 
10717             // 1. Process free space before this allocation.
10718             if(lastOffset < suballoc.offset)
10719             {
10720                 // There is free space from lastOffset to suballoc.offset.
10721                 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10722                 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10723             }
10724 
10725             // 2. Process this allocation.
10726             // There is allocation with suballoc.offset, suballoc.size.
10727             PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10728 
10729             // 3. Prepare for next iteration.
10730             lastOffset = suballoc.offset + suballoc.size;
10731             ++nextAlloc1stIndex;
10732         }
10733         // We are at the end.
10734         else
10735         {
10736             if(lastOffset < freeSpace1stTo2ndEnd)
10737             {
10738                 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10739                 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10740                 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10741             }
10742 
10743             // End of loop.
10744             lastOffset = freeSpace1stTo2ndEnd;
10745         }
10746     }
10747 
10748     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10749     {
10750         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10751         while(lastOffset < size)
10752         {
10753             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10754             while(nextAlloc2ndIndex != SIZE_MAX &&
10755                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10756             {
10757                 --nextAlloc2ndIndex;
10758             }
10759 
10760             // Found non-null allocation.
10761             if(nextAlloc2ndIndex != SIZE_MAX)
10762             {
10763                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10764 
10765                 // 1. Process free space before this allocation.
10766                 if(lastOffset < suballoc.offset)
10767                 {
10768                     // There is free space from lastOffset to suballoc.offset.
10769                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10770                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10771                 }
10772 
10773                 // 2. Process this allocation.
10774                 // There is allocation with suballoc.offset, suballoc.size.
10775                 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10776 
10777                 // 3. Prepare for next iteration.
10778                 lastOffset = suballoc.offset + suballoc.size;
10779                 --nextAlloc2ndIndex;
10780             }
10781             // We are at the end.
10782             else
10783             {
10784                 if(lastOffset < size)
10785                 {
10786                     // There is free space from lastOffset to size.
10787                     const VkDeviceSize unusedRangeSize = size - lastOffset;
10788                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10789                 }
10790 
10791                 // End of loop.
10792                 lastOffset = size;
10793             }
10794         }
10795     }
10796 
10797     PrintDetailedMap_End(json);
10798 }
10799 #endif // #if VMA_STATS_STRING_ENABLED
10800 
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)10801 bool VmaBlockMetadata_Linear::CreateAllocationRequest(
10802     uint32_t currentFrameIndex,
10803     uint32_t frameInUseCount,
10804     VkDeviceSize bufferImageGranularity,
10805     VkDeviceSize allocSize,
10806     VkDeviceSize allocAlignment,
10807     bool upperAddress,
10808     VmaSuballocationType allocType,
10809     bool canMakeOtherLost,
10810     uint32_t strategy,
10811     VmaAllocationRequest* pAllocationRequest)
10812 {
10813     VMA_ASSERT(allocSize > 0);
10814     VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
10815     VMA_ASSERT(pAllocationRequest != VMA_NULL);
10816     VMA_HEAVY_ASSERT(Validate());
10817     return upperAddress ?
10818         CreateAllocationRequest_UpperAddress(
10819             currentFrameIndex, frameInUseCount, bufferImageGranularity,
10820             allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest) :
10821         CreateAllocationRequest_LowerAddress(
10822             currentFrameIndex, frameInUseCount, bufferImageGranularity,
10823             allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest);
10824 }
10825 
CreateAllocationRequest_UpperAddress(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)10826 bool VmaBlockMetadata_Linear::CreateAllocationRequest_UpperAddress(
10827     uint32_t currentFrameIndex,
10828     uint32_t frameInUseCount,
10829     VkDeviceSize bufferImageGranularity,
10830     VkDeviceSize allocSize,
10831     VkDeviceSize allocAlignment,
10832     VmaSuballocationType allocType,
10833     bool canMakeOtherLost,
10834     uint32_t strategy,
10835     VmaAllocationRequest* pAllocationRequest)
10836 {
10837     const VkDeviceSize size = GetSize();
10838     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10839     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10840 
10841     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10842     {
10843         VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
10844         return false;
10845     }
10846 
10847     // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
10848     if(allocSize > size)
10849     {
10850         return false;
10851     }
10852     VkDeviceSize resultBaseOffset = size - allocSize;
10853     if(!suballocations2nd.empty())
10854     {
10855         const VmaSuballocation& lastSuballoc = suballocations2nd.back();
10856         resultBaseOffset = lastSuballoc.offset - allocSize;
10857         if(allocSize > lastSuballoc.offset)
10858         {
10859             return false;
10860         }
10861     }
10862 
10863     // Start from offset equal to end of free space.
10864     VkDeviceSize resultOffset = resultBaseOffset;
10865 
10866     // Apply VMA_DEBUG_MARGIN at the end.
10867     if(VMA_DEBUG_MARGIN > 0)
10868     {
10869         if(resultOffset < VMA_DEBUG_MARGIN)
10870         {
10871             return false;
10872         }
10873         resultOffset -= VMA_DEBUG_MARGIN;
10874     }
10875 
10876     // Apply alignment.
10877     resultOffset = VmaAlignDown(resultOffset, allocAlignment);
10878 
10879     // Check next suballocations from 2nd for BufferImageGranularity conflicts.
10880     // Make bigger alignment if necessary.
10881     if(bufferImageGranularity > 1 && !suballocations2nd.empty())
10882     {
10883         bool bufferImageGranularityConflict = false;
10884         for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
10885         {
10886             const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
10887             if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
10888             {
10889                 if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
10890                 {
10891                     bufferImageGranularityConflict = true;
10892                     break;
10893                 }
10894             }
10895             else
10896                 // Already on previous page.
10897                 break;
10898         }
10899         if(bufferImageGranularityConflict)
10900         {
10901             resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
10902         }
10903     }
10904 
10905     // There is enough free space.
10906     const VkDeviceSize endOf1st = !suballocations1st.empty() ?
10907         suballocations1st.back().offset + suballocations1st.back().size :
10908         0;
10909     if(endOf1st + VMA_DEBUG_MARGIN <= resultOffset)
10910     {
10911         // Check previous suballocations for BufferImageGranularity conflicts.
10912         // If conflict exists, allocation cannot be made here.
10913         if(bufferImageGranularity > 1)
10914         {
10915             for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
10916             {
10917                 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
10918                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
10919                 {
10920                     if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
10921                     {
10922                         return false;
10923                     }
10924                 }
10925                 else
10926                 {
10927                     // Already on next page.
10928                     break;
10929                 }
10930             }
10931         }
10932 
10933         // All tests passed: Success.
10934         pAllocationRequest->offset = resultOffset;
10935         pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
10936         pAllocationRequest->sumItemSize = 0;
10937         // pAllocationRequest->item unused.
10938         pAllocationRequest->itemsToMakeLostCount = 0;
10939         pAllocationRequest->type = VmaAllocationRequestType::UpperAddress;
10940         return true;
10941     }
10942 
10943     return false;
10944 }
10945 
CreateAllocationRequest_LowerAddress(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)10946 bool VmaBlockMetadata_Linear::CreateAllocationRequest_LowerAddress(
10947     uint32_t currentFrameIndex,
10948     uint32_t frameInUseCount,
10949     VkDeviceSize bufferImageGranularity,
10950     VkDeviceSize allocSize,
10951     VkDeviceSize allocAlignment,
10952     VmaSuballocationType allocType,
10953     bool canMakeOtherLost,
10954     uint32_t strategy,
10955     VmaAllocationRequest* pAllocationRequest)
10956 {
10957     const VkDeviceSize size = GetSize();
10958     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10959     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10960 
10961     if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10962     {
10963         // Try to allocate at the end of 1st vector.
10964 
10965         VkDeviceSize resultBaseOffset = 0;
10966         if(!suballocations1st.empty())
10967         {
10968             const VmaSuballocation& lastSuballoc = suballocations1st.back();
10969             resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
10970         }
10971 
10972         // Start from offset equal to beginning of free space.
10973         VkDeviceSize resultOffset = resultBaseOffset;
10974 
10975         // Apply VMA_DEBUG_MARGIN at the beginning.
10976         if(VMA_DEBUG_MARGIN > 0)
10977         {
10978             resultOffset += VMA_DEBUG_MARGIN;
10979         }
10980 
10981         // Apply alignment.
10982         resultOffset = VmaAlignUp(resultOffset, allocAlignment);
10983 
10984         // Check previous suballocations for BufferImageGranularity conflicts.
10985         // Make bigger alignment if necessary.
10986         if(bufferImageGranularity > 1 && !suballocations1st.empty())
10987         {
10988             bool bufferImageGranularityConflict = false;
10989             for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
10990             {
10991                 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
10992                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
10993                 {
10994                     if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
10995                     {
10996                         bufferImageGranularityConflict = true;
10997                         break;
10998                     }
10999                 }
11000                 else
11001                     // Already on previous page.
11002                     break;
11003             }
11004             if(bufferImageGranularityConflict)
11005             {
11006                 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
11007             }
11008         }
11009 
11010         const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
11011             suballocations2nd.back().offset : size;
11012 
11013         // There is enough free space at the end after alignment.
11014         if(resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd)
11015         {
11016             // Check next suballocations for BufferImageGranularity conflicts.
11017             // If conflict exists, allocation cannot be made here.
11018             if(bufferImageGranularity > 1 && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11019             {
11020                 for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
11021                 {
11022                     const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
11023                     if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
11024                     {
11025                         if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
11026                         {
11027                             return false;
11028                         }
11029                     }
11030                     else
11031                     {
11032                         // Already on previous page.
11033                         break;
11034                     }
11035                 }
11036             }
11037 
11038             // All tests passed: Success.
11039             pAllocationRequest->offset = resultOffset;
11040             pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
11041             pAllocationRequest->sumItemSize = 0;
11042             // pAllocationRequest->item, customData unused.
11043             pAllocationRequest->type = VmaAllocationRequestType::EndOf1st;
11044             pAllocationRequest->itemsToMakeLostCount = 0;
11045             return true;
11046         }
11047     }
11048 
11049     // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
11050     // beginning of 1st vector as the end of free space.
11051     if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11052     {
11053         VMA_ASSERT(!suballocations1st.empty());
11054 
11055         VkDeviceSize resultBaseOffset = 0;
11056         if(!suballocations2nd.empty())
11057         {
11058             const VmaSuballocation& lastSuballoc = suballocations2nd.back();
11059             resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
11060         }
11061 
11062         // Start from offset equal to beginning of free space.
11063         VkDeviceSize resultOffset = resultBaseOffset;
11064 
11065         // Apply VMA_DEBUG_MARGIN at the beginning.
11066         if(VMA_DEBUG_MARGIN > 0)
11067         {
11068             resultOffset += VMA_DEBUG_MARGIN;
11069         }
11070 
11071         // Apply alignment.
11072         resultOffset = VmaAlignUp(resultOffset, allocAlignment);
11073 
11074         // Check previous suballocations for BufferImageGranularity conflicts.
11075         // Make bigger alignment if necessary.
11076         if(bufferImageGranularity > 1 && !suballocations2nd.empty())
11077         {
11078             bool bufferImageGranularityConflict = false;
11079             for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
11080             {
11081                 const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
11082                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
11083                 {
11084                     if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
11085                     {
11086                         bufferImageGranularityConflict = true;
11087                         break;
11088                     }
11089                 }
11090                 else
11091                     // Already on previous page.
11092                     break;
11093             }
11094             if(bufferImageGranularityConflict)
11095             {
11096                 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
11097             }
11098         }
11099 
11100         pAllocationRequest->itemsToMakeLostCount = 0;
11101         pAllocationRequest->sumItemSize = 0;
11102         size_t index1st = m_1stNullItemsBeginCount;
11103 
11104         if(canMakeOtherLost)
11105         {
11106             while(index1st < suballocations1st.size() &&
11107                 resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset)
11108             {
11109                 // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
11110                 const VmaSuballocation& suballoc = suballocations1st[index1st];
11111                 if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
11112                 {
11113                     // No problem.
11114                 }
11115                 else
11116                 {
11117                     VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
11118                     if(suballoc.hAllocation->CanBecomeLost() &&
11119                         suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
11120                     {
11121                         ++pAllocationRequest->itemsToMakeLostCount;
11122                         pAllocationRequest->sumItemSize += suballoc.size;
11123                     }
11124                     else
11125                     {
11126                         return false;
11127                     }
11128                 }
11129                 ++index1st;
11130             }
11131 
11132             // Check next suballocations for BufferImageGranularity conflicts.
11133             // If conflict exists, we must mark more allocations lost or fail.
11134             if(bufferImageGranularity > 1)
11135             {
11136                 while(index1st < suballocations1st.size())
11137                 {
11138                     const VmaSuballocation& suballoc = suballocations1st[index1st];
11139                     if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))
11140                     {
11141                         if(suballoc.hAllocation != VK_NULL_HANDLE)
11142                         {
11143                             // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
11144                             if(suballoc.hAllocation->CanBecomeLost() &&
11145                                 suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
11146                             {
11147                                 ++pAllocationRequest->itemsToMakeLostCount;
11148                                 pAllocationRequest->sumItemSize += suballoc.size;
11149                             }
11150                             else
11151                             {
11152                                 return false;
11153                             }
11154                         }
11155                     }
11156                     else
11157                     {
11158                         // Already on next page.
11159                         break;
11160                     }
11161                     ++index1st;
11162                 }
11163             }
11164 
11165             // Special case: There is not enough room at the end for this allocation, even after making all from the 1st lost.
11166             if(index1st == suballocations1st.size() &&
11167                 resultOffset + allocSize + VMA_DEBUG_MARGIN > size)
11168             {
11169                 // TODO: This is a known bug that it's not yet implemented and the allocation is failing.
11170                 VMA_DEBUG_LOG("Unsupported special case in custom pool with linear allocation algorithm used as ring buffer with allocations that can be lost.");
11171             }
11172         }
11173 
11174         // There is enough free space at the end after alignment.
11175         if((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= size) ||
11176             (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset))
11177         {
11178             // Check next suballocations for BufferImageGranularity conflicts.
11179             // If conflict exists, allocation cannot be made here.
11180             if(bufferImageGranularity > 1)
11181             {
11182                 for(size_t nextSuballocIndex = index1st;
11183                     nextSuballocIndex < suballocations1st.size();
11184                     nextSuballocIndex++)
11185                 {
11186                     const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
11187                     if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
11188                     {
11189                         if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
11190                         {
11191                             return false;
11192                         }
11193                     }
11194                     else
11195                     {
11196                         // Already on next page.
11197                         break;
11198                     }
11199                 }
11200             }
11201 
11202             // All tests passed: Success.
11203             pAllocationRequest->offset = resultOffset;
11204             pAllocationRequest->sumFreeSize =
11205                 (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size)
11206                 - resultBaseOffset
11207                 - pAllocationRequest->sumItemSize;
11208             pAllocationRequest->type = VmaAllocationRequestType::EndOf2nd;
11209             // pAllocationRequest->item, customData unused.
11210             return true;
11211         }
11212     }
11213 
11214     return false;
11215 }
11216 
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)11217 bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
11218     uint32_t currentFrameIndex,
11219     uint32_t frameInUseCount,
11220     VmaAllocationRequest* pAllocationRequest)
11221 {
11222     if(pAllocationRequest->itemsToMakeLostCount == 0)
11223     {
11224         return true;
11225     }
11226 
11227     VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
11228 
11229     // We always start from 1st.
11230     SuballocationVectorType* suballocations = &AccessSuballocations1st();
11231     size_t index = m_1stNullItemsBeginCount;
11232     size_t madeLostCount = 0;
11233     while(madeLostCount < pAllocationRequest->itemsToMakeLostCount)
11234     {
11235         if(index == suballocations->size())
11236         {
11237             index = 0;
11238             // If we get to the end of 1st, we wrap around to beginning of 2nd of 1st.
11239             if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11240             {
11241                 suballocations = &AccessSuballocations2nd();
11242             }
11243             // else: m_2ndVectorMode == SECOND_VECTOR_EMPTY:
11244             // suballocations continues pointing at AccessSuballocations1st().
11245             VMA_ASSERT(!suballocations->empty());
11246         }
11247         VmaSuballocation& suballoc = (*suballocations)[index];
11248         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11249         {
11250             VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
11251             VMA_ASSERT(suballoc.hAllocation->CanBecomeLost());
11252             if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11253             {
11254                 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11255                 suballoc.hAllocation = VK_NULL_HANDLE;
11256                 m_SumFreeSize += suballoc.size;
11257                 if(suballocations == &AccessSuballocations1st())
11258                 {
11259                     ++m_1stNullItemsMiddleCount;
11260                 }
11261                 else
11262                 {
11263                     ++m_2ndNullItemsCount;
11264                 }
11265                 ++madeLostCount;
11266             }
11267             else
11268             {
11269                 return false;
11270             }
11271         }
11272         ++index;
11273     }
11274 
11275     CleanupAfterFree();
11276     //VMA_HEAVY_ASSERT(Validate()); // Already called by ClanupAfterFree().
11277 
11278     return true;
11279 }
11280 
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)11281 uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
11282 {
11283     uint32_t lostAllocationCount = 0;
11284 
11285     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11286     for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
11287     {
11288         VmaSuballocation& suballoc = suballocations1st[i];
11289         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
11290             suballoc.hAllocation->CanBecomeLost() &&
11291             suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11292         {
11293             suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11294             suballoc.hAllocation = VK_NULL_HANDLE;
11295             ++m_1stNullItemsMiddleCount;
11296             m_SumFreeSize += suballoc.size;
11297             ++lostAllocationCount;
11298         }
11299     }
11300 
11301     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11302     for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
11303     {
11304         VmaSuballocation& suballoc = suballocations2nd[i];
11305         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
11306             suballoc.hAllocation->CanBecomeLost() &&
11307             suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11308         {
11309             suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11310             suballoc.hAllocation = VK_NULL_HANDLE;
11311             ++m_2ndNullItemsCount;
11312             m_SumFreeSize += suballoc.size;
11313             ++lostAllocationCount;
11314         }
11315     }
11316 
11317     if(lostAllocationCount)
11318     {
11319         CleanupAfterFree();
11320     }
11321 
11322     return lostAllocationCount;
11323 }
11324 
CheckCorruption(const void * pBlockData)11325 VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
11326 {
11327     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11328     for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
11329     {
11330         const VmaSuballocation& suballoc = suballocations1st[i];
11331         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11332         {
11333             if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
11334             {
11335                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
11336                 return VK_ERROR_VALIDATION_FAILED_EXT;
11337             }
11338             if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
11339             {
11340                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
11341                 return VK_ERROR_VALIDATION_FAILED_EXT;
11342             }
11343         }
11344     }
11345 
11346     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11347     for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
11348     {
11349         const VmaSuballocation& suballoc = suballocations2nd[i];
11350         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11351         {
11352             if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
11353             {
11354                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
11355                 return VK_ERROR_VALIDATION_FAILED_EXT;
11356             }
11357             if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
11358             {
11359                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
11360                 return VK_ERROR_VALIDATION_FAILED_EXT;
11361             }
11362         }
11363     }
11364 
11365     return VK_SUCCESS;
11366 }
11367 
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,VmaAllocation hAllocation)11368 void VmaBlockMetadata_Linear::Alloc(
11369     const VmaAllocationRequest& request,
11370     VmaSuballocationType type,
11371     VkDeviceSize allocSize,
11372     VmaAllocation hAllocation)
11373 {
11374     const VmaSuballocation newSuballoc = { request.offset, allocSize, hAllocation, type };
11375 
11376     switch(request.type)
11377     {
11378     case VmaAllocationRequestType::UpperAddress:
11379         {
11380             VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
11381                 "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
11382             SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11383             suballocations2nd.push_back(newSuballoc);
11384             m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
11385         }
11386         break;
11387     case VmaAllocationRequestType::EndOf1st:
11388         {
11389             SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11390 
11391             VMA_ASSERT(suballocations1st.empty() ||
11392                 request.offset >= suballocations1st.back().offset + suballocations1st.back().size);
11393             // Check if it fits before the end of the block.
11394             VMA_ASSERT(request.offset + allocSize <= GetSize());
11395 
11396             suballocations1st.push_back(newSuballoc);
11397         }
11398         break;
11399     case VmaAllocationRequestType::EndOf2nd:
11400         {
11401             SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11402             // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
11403             VMA_ASSERT(!suballocations1st.empty() &&
11404                 request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset);
11405             SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11406 
11407             switch(m_2ndVectorMode)
11408             {
11409             case SECOND_VECTOR_EMPTY:
11410                 // First allocation from second part ring buffer.
11411                 VMA_ASSERT(suballocations2nd.empty());
11412                 m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
11413                 break;
11414             case SECOND_VECTOR_RING_BUFFER:
11415                 // 2-part ring buffer is already started.
11416                 VMA_ASSERT(!suballocations2nd.empty());
11417                 break;
11418             case SECOND_VECTOR_DOUBLE_STACK:
11419                 VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
11420                 break;
11421             default:
11422                 VMA_ASSERT(0);
11423             }
11424 
11425             suballocations2nd.push_back(newSuballoc);
11426         }
11427         break;
11428     default:
11429         VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
11430     }
11431 
11432     m_SumFreeSize -= newSuballoc.size;
11433 }
11434 
Free(const VmaAllocation allocation)11435 void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation)
11436 {
11437     FreeAtOffset(allocation->GetOffset());
11438 }
11439 
FreeAtOffset(VkDeviceSize offset)11440 void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
11441 {
11442     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11443     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11444 
11445     if(!suballocations1st.empty())
11446     {
11447         // First allocation: Mark it as next empty at the beginning.
11448         VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
11449         if(firstSuballoc.offset == offset)
11450         {
11451             firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11452             firstSuballoc.hAllocation = VK_NULL_HANDLE;
11453             m_SumFreeSize += firstSuballoc.size;
11454             ++m_1stNullItemsBeginCount;
11455             CleanupAfterFree();
11456             return;
11457         }
11458     }
11459 
11460     // Last allocation in 2-part ring buffer or top of upper stack (same logic).
11461     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
11462         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11463     {
11464         VmaSuballocation& lastSuballoc = suballocations2nd.back();
11465         if(lastSuballoc.offset == offset)
11466         {
11467             m_SumFreeSize += lastSuballoc.size;
11468             suballocations2nd.pop_back();
11469             CleanupAfterFree();
11470             return;
11471         }
11472     }
11473     // Last allocation in 1st vector.
11474     else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY)
11475     {
11476         VmaSuballocation& lastSuballoc = suballocations1st.back();
11477         if(lastSuballoc.offset == offset)
11478         {
11479             m_SumFreeSize += lastSuballoc.size;
11480             suballocations1st.pop_back();
11481             CleanupAfterFree();
11482             return;
11483         }
11484     }
11485 
11486     // Item from the middle of 1st vector.
11487     {
11488         VmaSuballocation refSuballoc;
11489         refSuballoc.offset = offset;
11490         // Rest of members stays uninitialized intentionally for better performance.
11491         SuballocationVectorType::iterator it = VmaBinaryFindSorted(
11492             suballocations1st.begin() + m_1stNullItemsBeginCount,
11493             suballocations1st.end(),
11494             refSuballoc,
11495             VmaSuballocationOffsetLess());
11496         if(it != suballocations1st.end())
11497         {
11498             it->type = VMA_SUBALLOCATION_TYPE_FREE;
11499             it->hAllocation = VK_NULL_HANDLE;
11500             ++m_1stNullItemsMiddleCount;
11501             m_SumFreeSize += it->size;
11502             CleanupAfterFree();
11503             return;
11504         }
11505     }
11506 
11507     if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
11508     {
11509         // Item from the middle of 2nd vector.
11510         VmaSuballocation refSuballoc;
11511         refSuballoc.offset = offset;
11512         // Rest of members stays uninitialized intentionally for better performance.
11513         SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
11514             VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :
11515             VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());
11516         if(it != suballocations2nd.end())
11517         {
11518             it->type = VMA_SUBALLOCATION_TYPE_FREE;
11519             it->hAllocation = VK_NULL_HANDLE;
11520             ++m_2ndNullItemsCount;
11521             m_SumFreeSize += it->size;
11522             CleanupAfterFree();
11523             return;
11524         }
11525     }
11526 
11527     VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
11528 }
11529 
ShouldCompact1st()11530 bool VmaBlockMetadata_Linear::ShouldCompact1st() const
11531 {
11532     const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
11533     const size_t suballocCount = AccessSuballocations1st().size();
11534     return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
11535 }
11536 
CleanupAfterFree()11537 void VmaBlockMetadata_Linear::CleanupAfterFree()
11538 {
11539     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11540     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11541 
11542     if(IsEmpty())
11543     {
11544         suballocations1st.clear();
11545         suballocations2nd.clear();
11546         m_1stNullItemsBeginCount = 0;
11547         m_1stNullItemsMiddleCount = 0;
11548         m_2ndNullItemsCount = 0;
11549         m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11550     }
11551     else
11552     {
11553         const size_t suballoc1stCount = suballocations1st.size();
11554         const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
11555         VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
11556 
11557         // Find more null items at the beginning of 1st vector.
11558         while(m_1stNullItemsBeginCount < suballoc1stCount &&
11559             suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
11560         {
11561             ++m_1stNullItemsBeginCount;
11562             --m_1stNullItemsMiddleCount;
11563         }
11564 
11565         // Find more null items at the end of 1st vector.
11566         while(m_1stNullItemsMiddleCount > 0 &&
11567             suballocations1st.back().hAllocation == VK_NULL_HANDLE)
11568         {
11569             --m_1stNullItemsMiddleCount;
11570             suballocations1st.pop_back();
11571         }
11572 
11573         // Find more null items at the end of 2nd vector.
11574         while(m_2ndNullItemsCount > 0 &&
11575             suballocations2nd.back().hAllocation == VK_NULL_HANDLE)
11576         {
11577             --m_2ndNullItemsCount;
11578             suballocations2nd.pop_back();
11579         }
11580 
11581         // Find more null items at the beginning of 2nd vector.
11582         while(m_2ndNullItemsCount > 0 &&
11583             suballocations2nd[0].hAllocation == VK_NULL_HANDLE)
11584         {
11585             --m_2ndNullItemsCount;
11586             VmaVectorRemove(suballocations2nd, 0);
11587         }
11588 
11589         if(ShouldCompact1st())
11590         {
11591             const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
11592             size_t srcIndex = m_1stNullItemsBeginCount;
11593             for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
11594             {
11595                 while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE)
11596                 {
11597                     ++srcIndex;
11598                 }
11599                 if(dstIndex != srcIndex)
11600                 {
11601                     suballocations1st[dstIndex] = suballocations1st[srcIndex];
11602                 }
11603                 ++srcIndex;
11604             }
11605             suballocations1st.resize(nonNullItemCount);
11606             m_1stNullItemsBeginCount = 0;
11607             m_1stNullItemsMiddleCount = 0;
11608         }
11609 
11610         // 2nd vector became empty.
11611         if(suballocations2nd.empty())
11612         {
11613             m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11614         }
11615 
11616         // 1st vector became empty.
11617         if(suballocations1st.size() - m_1stNullItemsBeginCount == 0)
11618         {
11619             suballocations1st.clear();
11620             m_1stNullItemsBeginCount = 0;
11621 
11622             if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11623             {
11624                 // Swap 1st with 2nd. Now 2nd is empty.
11625                 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11626                 m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
11627                 while(m_1stNullItemsBeginCount < suballocations2nd.size() &&
11628                     suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
11629                 {
11630                     ++m_1stNullItemsBeginCount;
11631                     --m_1stNullItemsMiddleCount;
11632                 }
11633                 m_2ndNullItemsCount = 0;
11634                 m_1stVectorIndex ^= 1;
11635             }
11636         }
11637     }
11638 
11639     VMA_HEAVY_ASSERT(Validate());
11640 }
11641 
11642 
11643 ////////////////////////////////////////////////////////////////////////////////
11644 // class VmaBlockMetadata_Buddy
11645 
VmaBlockMetadata_Buddy(VmaAllocator hAllocator)11646 VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :
11647     VmaBlockMetadata(hAllocator),
11648     m_Root(VMA_NULL),
11649     m_AllocationCount(0),
11650     m_FreeCount(1),
11651     m_SumFreeSize(0)
11652 {
11653     memset(m_FreeList, 0, sizeof(m_FreeList));
11654 }
11655 
~VmaBlockMetadata_Buddy()11656 VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
11657 {
11658     DeleteNode(m_Root);
11659 }
11660 
Init(VkDeviceSize size)11661 void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
11662 {
11663     VmaBlockMetadata::Init(size);
11664 
11665     m_UsableSize = VmaPrevPow2(size);
11666     m_SumFreeSize = m_UsableSize;
11667 
11668     // Calculate m_LevelCount.
11669     m_LevelCount = 1;
11670     while(m_LevelCount < MAX_LEVELS &&
11671         LevelToNodeSize(m_LevelCount) >= MIN_NODE_SIZE)
11672     {
11673         ++m_LevelCount;
11674     }
11675 
11676     Node* rootNode = vma_new(GetAllocationCallbacks(), Node)();
11677     rootNode->offset = 0;
11678     rootNode->type = Node::TYPE_FREE;
11679     rootNode->parent = VMA_NULL;
11680     rootNode->buddy = VMA_NULL;
11681 
11682     m_Root = rootNode;
11683     AddToFreeListFront(0, rootNode);
11684 }
11685 
Validate()11686 bool VmaBlockMetadata_Buddy::Validate() const
11687 {
11688     // Validate tree.
11689     ValidationContext ctx;
11690     if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
11691     {
11692         VMA_VALIDATE(false && "ValidateNode failed.");
11693     }
11694     VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
11695     VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
11696 
11697     // Validate free node lists.
11698     for(uint32_t level = 0; level < m_LevelCount; ++level)
11699     {
11700         VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
11701             m_FreeList[level].front->free.prev == VMA_NULL);
11702 
11703         for(Node* node = m_FreeList[level].front;
11704             node != VMA_NULL;
11705             node = node->free.next)
11706         {
11707             VMA_VALIDATE(node->type == Node::TYPE_FREE);
11708 
11709             if(node->free.next == VMA_NULL)
11710             {
11711                 VMA_VALIDATE(m_FreeList[level].back == node);
11712             }
11713             else
11714             {
11715                 VMA_VALIDATE(node->free.next->free.prev == node);
11716             }
11717         }
11718     }
11719 
11720     // Validate that free lists ar higher levels are empty.
11721     for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
11722     {
11723         VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
11724     }
11725 
11726     return true;
11727 }
11728 
GetUnusedRangeSizeMax()11729 VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
11730 {
11731     for(uint32_t level = 0; level < m_LevelCount; ++level)
11732     {
11733         if(m_FreeList[level].front != VMA_NULL)
11734         {
11735             return LevelToNodeSize(level);
11736         }
11737     }
11738     return 0;
11739 }
11740 
CalcAllocationStatInfo(VmaStatInfo & outInfo)11741 void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
11742 {
11743     const VkDeviceSize unusableSize = GetUnusableSize();
11744 
11745     outInfo.blockCount = 1;
11746 
11747     outInfo.allocationCount = outInfo.unusedRangeCount = 0;
11748     outInfo.usedBytes = outInfo.unusedBytes = 0;
11749 
11750     outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
11751     outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
11752     outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused.
11753 
11754     CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0));
11755 
11756     if(unusableSize > 0)
11757     {
11758         ++outInfo.unusedRangeCount;
11759         outInfo.unusedBytes += unusableSize;
11760         outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize);
11761         outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize);
11762     }
11763 }
11764 
AddPoolStats(VmaPoolStats & inoutStats)11765 void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
11766 {
11767     const VkDeviceSize unusableSize = GetUnusableSize();
11768 
11769     inoutStats.size += GetSize();
11770     inoutStats.unusedSize += m_SumFreeSize + unusableSize;
11771     inoutStats.allocationCount += m_AllocationCount;
11772     inoutStats.unusedRangeCount += m_FreeCount;
11773     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
11774 
11775     if(unusableSize > 0)
11776     {
11777         ++inoutStats.unusedRangeCount;
11778         // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
11779     }
11780 }
11781 
11782 #if VMA_STATS_STRING_ENABLED
11783 
PrintDetailedMap(class VmaJsonWriter & json)11784 void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
11785 {
11786     // TODO optimize
11787     VmaStatInfo stat;
11788     CalcAllocationStatInfo(stat);
11789 
11790     PrintDetailedMap_Begin(
11791         json,
11792         stat.unusedBytes,
11793         stat.allocationCount,
11794         stat.unusedRangeCount);
11795 
11796     PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
11797 
11798     const VkDeviceSize unusableSize = GetUnusableSize();
11799     if(unusableSize > 0)
11800     {
11801         PrintDetailedMap_UnusedRange(json,
11802             m_UsableSize, // offset
11803             unusableSize); // size
11804     }
11805 
11806     PrintDetailedMap_End(json);
11807 }
11808 
11809 #endif // #if VMA_STATS_STRING_ENABLED
11810 
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)11811 bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
11812     uint32_t currentFrameIndex,
11813     uint32_t frameInUseCount,
11814     VkDeviceSize bufferImageGranularity,
11815     VkDeviceSize allocSize,
11816     VkDeviceSize allocAlignment,
11817     bool upperAddress,
11818     VmaSuballocationType allocType,
11819     bool canMakeOtherLost,
11820     uint32_t strategy,
11821     VmaAllocationRequest* pAllocationRequest)
11822 {
11823     VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
11824 
11825     // Simple way to respect bufferImageGranularity. May be optimized some day.
11826     // Whenever it might be an OPTIMAL image...
11827     if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
11828         allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
11829         allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
11830     {
11831         allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity);
11832         allocSize = VMA_MAX(allocSize, bufferImageGranularity);
11833     }
11834 
11835     if(allocSize > m_UsableSize)
11836     {
11837         return false;
11838     }
11839 
11840     const uint32_t targetLevel = AllocSizeToLevel(allocSize);
11841     for(uint32_t level = targetLevel + 1; level--; )
11842     {
11843         for(Node* freeNode = m_FreeList[level].front;
11844             freeNode != VMA_NULL;
11845             freeNode = freeNode->free.next)
11846         {
11847             if(freeNode->offset % allocAlignment == 0)
11848             {
11849                 pAllocationRequest->type = VmaAllocationRequestType::Normal;
11850                 pAllocationRequest->offset = freeNode->offset;
11851                 pAllocationRequest->sumFreeSize = LevelToNodeSize(level);
11852                 pAllocationRequest->sumItemSize = 0;
11853                 pAllocationRequest->itemsToMakeLostCount = 0;
11854                 pAllocationRequest->customData = (void*)(uintptr_t)level;
11855                 return true;
11856             }
11857         }
11858     }
11859 
11860     return false;
11861 }
11862 
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)11863 bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
11864     uint32_t currentFrameIndex,
11865     uint32_t frameInUseCount,
11866     VmaAllocationRequest* pAllocationRequest)
11867 {
11868     /*
11869     Lost allocations are not supported in buddy allocator at the moment.
11870     Support might be added in the future.
11871     */
11872     return pAllocationRequest->itemsToMakeLostCount == 0;
11873 }
11874 
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)11875 uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
11876 {
11877     /*
11878     Lost allocations are not supported in buddy allocator at the moment.
11879     Support might be added in the future.
11880     */
11881     return 0;
11882 }
11883 
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,VmaAllocation hAllocation)11884 void VmaBlockMetadata_Buddy::Alloc(
11885     const VmaAllocationRequest& request,
11886     VmaSuballocationType type,
11887     VkDeviceSize allocSize,
11888     VmaAllocation hAllocation)
11889 {
11890     VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
11891 
11892     const uint32_t targetLevel = AllocSizeToLevel(allocSize);
11893     uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
11894 
11895     Node* currNode = m_FreeList[currLevel].front;
11896     VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
11897     while(currNode->offset != request.offset)
11898     {
11899         currNode = currNode->free.next;
11900         VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
11901     }
11902 
11903     // Go down, splitting free nodes.
11904     while(currLevel < targetLevel)
11905     {
11906         // currNode is already first free node at currLevel.
11907         // Remove it from list of free nodes at this currLevel.
11908         RemoveFromFreeList(currLevel, currNode);
11909 
11910         const uint32_t childrenLevel = currLevel + 1;
11911 
11912         // Create two free sub-nodes.
11913         Node* leftChild = vma_new(GetAllocationCallbacks(), Node)();
11914         Node* rightChild = vma_new(GetAllocationCallbacks(), Node)();
11915 
11916         leftChild->offset = currNode->offset;
11917         leftChild->type = Node::TYPE_FREE;
11918         leftChild->parent = currNode;
11919         leftChild->buddy = rightChild;
11920 
11921         rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
11922         rightChild->type = Node::TYPE_FREE;
11923         rightChild->parent = currNode;
11924         rightChild->buddy = leftChild;
11925 
11926         // Convert current currNode to split type.
11927         currNode->type = Node::TYPE_SPLIT;
11928         currNode->split.leftChild = leftChild;
11929 
11930         // Add child nodes to free list. Order is important!
11931         AddToFreeListFront(childrenLevel, rightChild);
11932         AddToFreeListFront(childrenLevel, leftChild);
11933 
11934         ++m_FreeCount;
11935         //m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2.
11936         ++currLevel;
11937         currNode = m_FreeList[currLevel].front;
11938 
11939         /*
11940         We can be sure that currNode, as left child of node previously split,
11941         also fullfills the alignment requirement.
11942         */
11943     }
11944 
11945     // Remove from free list.
11946     VMA_ASSERT(currLevel == targetLevel &&
11947         currNode != VMA_NULL &&
11948         currNode->type == Node::TYPE_FREE);
11949     RemoveFromFreeList(currLevel, currNode);
11950 
11951     // Convert to allocation node.
11952     currNode->type = Node::TYPE_ALLOCATION;
11953     currNode->allocation.alloc = hAllocation;
11954 
11955     ++m_AllocationCount;
11956     --m_FreeCount;
11957     m_SumFreeSize -= allocSize;
11958 }
11959 
DeleteNode(Node * node)11960 void VmaBlockMetadata_Buddy::DeleteNode(Node* node)
11961 {
11962     if(node->type == Node::TYPE_SPLIT)
11963     {
11964         DeleteNode(node->split.leftChild->buddy);
11965         DeleteNode(node->split.leftChild);
11966     }
11967 
11968     vma_delete(GetAllocationCallbacks(), node);
11969 }
11970 
ValidateNode(ValidationContext & ctx,const Node * parent,const Node * curr,uint32_t level,VkDeviceSize levelNodeSize)11971 bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
11972 {
11973     VMA_VALIDATE(level < m_LevelCount);
11974     VMA_VALIDATE(curr->parent == parent);
11975     VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
11976     VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
11977     switch(curr->type)
11978     {
11979     case Node::TYPE_FREE:
11980         // curr->free.prev, next are validated separately.
11981         ctx.calculatedSumFreeSize += levelNodeSize;
11982         ++ctx.calculatedFreeCount;
11983         break;
11984     case Node::TYPE_ALLOCATION:
11985         ++ctx.calculatedAllocationCount;
11986         ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize();
11987         VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);
11988         break;
11989     case Node::TYPE_SPLIT:
11990         {
11991             const uint32_t childrenLevel = level + 1;
11992             const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2;
11993             const Node* const leftChild = curr->split.leftChild;
11994             VMA_VALIDATE(leftChild != VMA_NULL);
11995             VMA_VALIDATE(leftChild->offset == curr->offset);
11996             if(!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
11997             {
11998                 VMA_VALIDATE(false && "ValidateNode for left child failed.");
11999             }
12000             const Node* const rightChild = leftChild->buddy;
12001             VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
12002             if(!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
12003             {
12004                 VMA_VALIDATE(false && "ValidateNode for right child failed.");
12005             }
12006         }
12007         break;
12008     default:
12009         return false;
12010     }
12011 
12012     return true;
12013 }
12014 
AllocSizeToLevel(VkDeviceSize allocSize)12015 uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
12016 {
12017     // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
12018     uint32_t level = 0;
12019     VkDeviceSize currLevelNodeSize = m_UsableSize;
12020     VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
12021     while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
12022     {
12023         ++level;
12024         currLevelNodeSize = nextLevelNodeSize;
12025         nextLevelNodeSize = currLevelNodeSize >> 1;
12026     }
12027     return level;
12028 }
12029 
FreeAtOffset(VmaAllocation alloc,VkDeviceSize offset)12030 void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset)
12031 {
12032     // Find node and level.
12033     Node* node = m_Root;
12034     VkDeviceSize nodeOffset = 0;
12035     uint32_t level = 0;
12036     VkDeviceSize levelNodeSize = LevelToNodeSize(0);
12037     while(node->type == Node::TYPE_SPLIT)
12038     {
12039         const VkDeviceSize nextLevelSize = levelNodeSize >> 1;
12040         if(offset < nodeOffset + nextLevelSize)
12041         {
12042             node = node->split.leftChild;
12043         }
12044         else
12045         {
12046             node = node->split.leftChild->buddy;
12047             nodeOffset += nextLevelSize;
12048         }
12049         ++level;
12050         levelNodeSize = nextLevelSize;
12051     }
12052 
12053     VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
12054     VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc);
12055 
12056     ++m_FreeCount;
12057     --m_AllocationCount;
12058     m_SumFreeSize += alloc->GetSize();
12059 
12060     node->type = Node::TYPE_FREE;
12061 
12062     // Join free nodes if possible.
12063     while(level > 0 && node->buddy->type == Node::TYPE_FREE)
12064     {
12065         RemoveFromFreeList(level, node->buddy);
12066         Node* const parent = node->parent;
12067 
12068         vma_delete(GetAllocationCallbacks(), node->buddy);
12069         vma_delete(GetAllocationCallbacks(), node);
12070         parent->type = Node::TYPE_FREE;
12071 
12072         node = parent;
12073         --level;
12074         //m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2.
12075         --m_FreeCount;
12076     }
12077 
12078     AddToFreeListFront(level, node);
12079 }
12080 
CalcAllocationStatInfoNode(VmaStatInfo & outInfo,const Node * node,VkDeviceSize levelNodeSize)12081 void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const
12082 {
12083     switch(node->type)
12084     {
12085     case Node::TYPE_FREE:
12086         ++outInfo.unusedRangeCount;
12087         outInfo.unusedBytes += levelNodeSize;
12088         outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, levelNodeSize);
12089         outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize);
12090         break;
12091     case Node::TYPE_ALLOCATION:
12092         {
12093             const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
12094             ++outInfo.allocationCount;
12095             outInfo.usedBytes += allocSize;
12096             outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize);
12097             outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize);
12098 
12099             const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize;
12100             if(unusedRangeSize > 0)
12101             {
12102                 ++outInfo.unusedRangeCount;
12103                 outInfo.unusedBytes += unusedRangeSize;
12104                 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize);
12105                 outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize);
12106             }
12107         }
12108         break;
12109     case Node::TYPE_SPLIT:
12110         {
12111             const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
12112             const Node* const leftChild = node->split.leftChild;
12113             CalcAllocationStatInfoNode(outInfo, leftChild, childrenNodeSize);
12114             const Node* const rightChild = leftChild->buddy;
12115             CalcAllocationStatInfoNode(outInfo, rightChild, childrenNodeSize);
12116         }
12117         break;
12118     default:
12119         VMA_ASSERT(0);
12120     }
12121 }
12122 
AddToFreeListFront(uint32_t level,Node * node)12123 void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
12124 {
12125     VMA_ASSERT(node->type == Node::TYPE_FREE);
12126 
12127     // List is empty.
12128     Node* const frontNode = m_FreeList[level].front;
12129     if(frontNode == VMA_NULL)
12130     {
12131         VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
12132         node->free.prev = node->free.next = VMA_NULL;
12133         m_FreeList[level].front = m_FreeList[level].back = node;
12134     }
12135     else
12136     {
12137         VMA_ASSERT(frontNode->free.prev == VMA_NULL);
12138         node->free.prev = VMA_NULL;
12139         node->free.next = frontNode;
12140         frontNode->free.prev = node;
12141         m_FreeList[level].front = node;
12142     }
12143 }
12144 
RemoveFromFreeList(uint32_t level,Node * node)12145 void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
12146 {
12147     VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
12148 
12149     // It is at the front.
12150     if(node->free.prev == VMA_NULL)
12151     {
12152         VMA_ASSERT(m_FreeList[level].front == node);
12153         m_FreeList[level].front = node->free.next;
12154     }
12155     else
12156     {
12157         Node* const prevFreeNode = node->free.prev;
12158         VMA_ASSERT(prevFreeNode->free.next == node);
12159         prevFreeNode->free.next = node->free.next;
12160     }
12161 
12162     // It is at the back.
12163     if(node->free.next == VMA_NULL)
12164     {
12165         VMA_ASSERT(m_FreeList[level].back == node);
12166         m_FreeList[level].back = node->free.prev;
12167     }
12168     else
12169     {
12170         Node* const nextFreeNode = node->free.next;
12171         VMA_ASSERT(nextFreeNode->free.prev == node);
12172         nextFreeNode->free.prev = node->free.prev;
12173     }
12174 }
12175 
12176 #if VMA_STATS_STRING_ENABLED
PrintDetailedMapNode(class VmaJsonWriter & json,const Node * node,VkDeviceSize levelNodeSize)12177 void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
12178 {
12179     switch(node->type)
12180     {
12181     case Node::TYPE_FREE:
12182         PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
12183         break;
12184     case Node::TYPE_ALLOCATION:
12185         {
12186             PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);
12187             const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
12188             if(allocSize < levelNodeSize)
12189             {
12190                 PrintDetailedMap_UnusedRange(json, node->offset + allocSize, levelNodeSize - allocSize);
12191             }
12192         }
12193         break;
12194     case Node::TYPE_SPLIT:
12195         {
12196             const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
12197             const Node* const leftChild = node->split.leftChild;
12198             PrintDetailedMapNode(json, leftChild, childrenNodeSize);
12199             const Node* const rightChild = leftChild->buddy;
12200             PrintDetailedMapNode(json, rightChild, childrenNodeSize);
12201         }
12202         break;
12203     default:
12204         VMA_ASSERT(0);
12205     }
12206 }
12207 #endif // #if VMA_STATS_STRING_ENABLED
12208 
12209 
12210 ////////////////////////////////////////////////////////////////////////////////
12211 // class VmaDeviceMemoryBlock
12212 
VmaDeviceMemoryBlock(VmaAllocator hAllocator)12213 VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) :
12214     m_pMetadata(VMA_NULL),
12215     m_MemoryTypeIndex(UINT32_MAX),
12216     m_Id(0),
12217     m_hMemory(VK_NULL_HANDLE),
12218     m_MapCount(0),
12219     m_pMappedData(VMA_NULL)
12220 {
12221 }
12222 
Init(VmaAllocator hAllocator,VmaPool hParentPool,uint32_t newMemoryTypeIndex,VkDeviceMemory newMemory,VkDeviceSize newSize,uint32_t id,uint32_t algorithm)12223 void VmaDeviceMemoryBlock::Init(
12224     VmaAllocator hAllocator,
12225     VmaPool hParentPool,
12226     uint32_t newMemoryTypeIndex,
12227     VkDeviceMemory newMemory,
12228     VkDeviceSize newSize,
12229     uint32_t id,
12230     uint32_t algorithm)
12231 {
12232     VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
12233 
12234     m_hParentPool = hParentPool;
12235     m_MemoryTypeIndex = newMemoryTypeIndex;
12236     m_Id = id;
12237     m_hMemory = newMemory;
12238 
12239     switch(algorithm)
12240     {
12241     case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
12242         m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator);
12243         break;
12244     case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
12245         m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator);
12246         break;
12247     default:
12248         VMA_ASSERT(0);
12249         // Fall-through.
12250     case 0:
12251         m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator);
12252     }
12253     m_pMetadata->Init(newSize);
12254 }
12255 
Destroy(VmaAllocator allocator)12256 void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
12257 {
12258     // This is the most important assert in the entire library.
12259     // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
12260     VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
12261 
12262     VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
12263     allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
12264     m_hMemory = VK_NULL_HANDLE;
12265 
12266     vma_delete(allocator, m_pMetadata);
12267     m_pMetadata = VMA_NULL;
12268 }
12269 
Validate()12270 bool VmaDeviceMemoryBlock::Validate() const
12271 {
12272     VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
12273         (m_pMetadata->GetSize() != 0));
12274 
12275     return m_pMetadata->Validate();
12276 }
12277 
CheckCorruption(VmaAllocator hAllocator)12278 VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
12279 {
12280     void* pData = nullptr;
12281     VkResult res = Map(hAllocator, 1, &pData);
12282     if(res != VK_SUCCESS)
12283     {
12284         return res;
12285     }
12286 
12287     res = m_pMetadata->CheckCorruption(pData);
12288 
12289     Unmap(hAllocator, 1);
12290 
12291     return res;
12292 }
12293 
Map(VmaAllocator hAllocator,uint32_t count,void ** ppData)12294 VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
12295 {
12296     if(count == 0)
12297     {
12298         return VK_SUCCESS;
12299     }
12300 
12301     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12302     if(m_MapCount != 0)
12303     {
12304         m_MapCount += count;
12305         VMA_ASSERT(m_pMappedData != VMA_NULL);
12306         if(ppData != VMA_NULL)
12307         {
12308             *ppData = m_pMappedData;
12309         }
12310         return VK_SUCCESS;
12311     }
12312     else
12313     {
12314         VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
12315             hAllocator->m_hDevice,
12316             m_hMemory,
12317             0, // offset
12318             VK_WHOLE_SIZE,
12319             0, // flags
12320             &m_pMappedData);
12321         if(result == VK_SUCCESS)
12322         {
12323             if(ppData != VMA_NULL)
12324             {
12325                 *ppData = m_pMappedData;
12326             }
12327             m_MapCount = count;
12328         }
12329         return result;
12330     }
12331 }
12332 
Unmap(VmaAllocator hAllocator,uint32_t count)12333 void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
12334 {
12335     if(count == 0)
12336     {
12337         return;
12338     }
12339 
12340     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12341     if(m_MapCount >= count)
12342     {
12343         m_MapCount -= count;
12344         if(m_MapCount == 0)
12345         {
12346             m_pMappedData = VMA_NULL;
12347             (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
12348         }
12349     }
12350     else
12351     {
12352         VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
12353     }
12354 }
12355 
WriteMagicValueAroundAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)12356 VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
12357 {
12358     VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
12359     VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
12360 
12361     void* pData;
12362     VkResult res = Map(hAllocator, 1, &pData);
12363     if(res != VK_SUCCESS)
12364     {
12365         return res;
12366     }
12367 
12368     VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN);
12369     VmaWriteMagicValue(pData, allocOffset + allocSize);
12370 
12371     Unmap(hAllocator, 1);
12372 
12373     return VK_SUCCESS;
12374 }
12375 
ValidateMagicValueAroundAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)12376 VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
12377 {
12378     VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
12379     VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
12380 
12381     void* pData;
12382     VkResult res = Map(hAllocator, 1, &pData);
12383     if(res != VK_SUCCESS)
12384     {
12385         return res;
12386     }
12387 
12388     if(!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN))
12389     {
12390         VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
12391     }
12392     else if(!VmaValidateMagicValue(pData, allocOffset + allocSize))
12393     {
12394         VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
12395     }
12396 
12397     Unmap(hAllocator, 1);
12398 
12399     return VK_SUCCESS;
12400 }
12401 
BindBufferMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkBuffer hBuffer,const void * pNext)12402 VkResult VmaDeviceMemoryBlock::BindBufferMemory(
12403     const VmaAllocator hAllocator,
12404     const VmaAllocation hAllocation,
12405     VkDeviceSize allocationLocalOffset,
12406     VkBuffer hBuffer,
12407     const void* pNext)
12408 {
12409     VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
12410         hAllocation->GetBlock() == this);
12411     VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
12412         "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
12413     const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
12414     // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
12415     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12416     return hAllocator->BindVulkanBuffer(m_hMemory, memoryOffset, hBuffer, pNext);
12417 }
12418 
BindImageMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkImage hImage,const void * pNext)12419 VkResult VmaDeviceMemoryBlock::BindImageMemory(
12420     const VmaAllocator hAllocator,
12421     const VmaAllocation hAllocation,
12422     VkDeviceSize allocationLocalOffset,
12423     VkImage hImage,
12424     const void* pNext)
12425 {
12426     VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
12427         hAllocation->GetBlock() == this);
12428     VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
12429         "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
12430     const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
12431     // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
12432     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12433     return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext);
12434 }
12435 
InitStatInfo(VmaStatInfo & outInfo)12436 static void InitStatInfo(VmaStatInfo& outInfo)
12437 {
12438     memset(&outInfo, 0, sizeof(outInfo));
12439     outInfo.allocationSizeMin = UINT64_MAX;
12440     outInfo.unusedRangeSizeMin = UINT64_MAX;
12441 }
12442 
12443 // Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
VmaAddStatInfo(VmaStatInfo & inoutInfo,const VmaStatInfo & srcInfo)12444 static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
12445 {
12446     inoutInfo.blockCount += srcInfo.blockCount;
12447     inoutInfo.allocationCount += srcInfo.allocationCount;
12448     inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
12449     inoutInfo.usedBytes += srcInfo.usedBytes;
12450     inoutInfo.unusedBytes += srcInfo.unusedBytes;
12451     inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
12452     inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
12453     inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
12454     inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
12455 }
12456 
VmaPostprocessCalcStatInfo(VmaStatInfo & inoutInfo)12457 static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
12458 {
12459     inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
12460         VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
12461     inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
12462         VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
12463 }
12464 
VmaPool_T(VmaAllocator hAllocator,const VmaPoolCreateInfo & createInfo,VkDeviceSize preferredBlockSize)12465 VmaPool_T::VmaPool_T(
12466     VmaAllocator hAllocator,
12467     const VmaPoolCreateInfo& createInfo,
12468     VkDeviceSize preferredBlockSize) :
12469     m_BlockVector(
12470         hAllocator,
12471         this, // hParentPool
12472         createInfo.memoryTypeIndex,
12473         createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
12474         createInfo.minBlockCount,
12475         createInfo.maxBlockCount,
12476         (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
12477         createInfo.frameInUseCount,
12478         createInfo.blockSize != 0, // explicitBlockSize
12479         createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK), // algorithm
12480     m_Id(0),
12481     m_Name(VMA_NULL)
12482 {
12483 }
12484 
~VmaPool_T()12485 VmaPool_T::~VmaPool_T()
12486 {
12487 }
12488 
SetName(const char * pName)12489 void VmaPool_T::SetName(const char* pName)
12490 {
12491     const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks();
12492     VmaFreeString(allocs, m_Name);
12493 
12494     if(pName != VMA_NULL)
12495     {
12496         m_Name = VmaCreateStringCopy(allocs, pName);
12497     }
12498     else
12499     {
12500         m_Name = VMA_NULL;
12501     }
12502 }
12503 
12504 #if VMA_STATS_STRING_ENABLED
12505 
12506 #endif // #if VMA_STATS_STRING_ENABLED
12507 
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)12508 VmaBlockVector::VmaBlockVector(
12509     VmaAllocator hAllocator,
12510     VmaPool hParentPool,
12511     uint32_t memoryTypeIndex,
12512     VkDeviceSize preferredBlockSize,
12513     size_t minBlockCount,
12514     size_t maxBlockCount,
12515     VkDeviceSize bufferImageGranularity,
12516     uint32_t frameInUseCount,
12517     bool explicitBlockSize,
12518     uint32_t algorithm) :
12519     m_hAllocator(hAllocator),
12520     m_hParentPool(hParentPool),
12521     m_MemoryTypeIndex(memoryTypeIndex),
12522     m_PreferredBlockSize(preferredBlockSize),
12523     m_MinBlockCount(minBlockCount),
12524     m_MaxBlockCount(maxBlockCount),
12525     m_BufferImageGranularity(bufferImageGranularity),
12526     m_FrameInUseCount(frameInUseCount),
12527     m_ExplicitBlockSize(explicitBlockSize),
12528     m_Algorithm(algorithm),
12529     m_HasEmptyBlock(false),
12530     m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
12531     m_NextBlockId(0)
12532 {
12533 }
12534 
~VmaBlockVector()12535 VmaBlockVector::~VmaBlockVector()
12536 {
12537     for(size_t i = m_Blocks.size(); i--; )
12538     {
12539         m_Blocks[i]->Destroy(m_hAllocator);
12540         vma_delete(m_hAllocator, m_Blocks[i]);
12541     }
12542 }
12543 
CreateMinBlocks()12544 VkResult VmaBlockVector::CreateMinBlocks()
12545 {
12546     for(size_t i = 0; i < m_MinBlockCount; ++i)
12547     {
12548         VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
12549         if(res != VK_SUCCESS)
12550         {
12551             return res;
12552         }
12553     }
12554     return VK_SUCCESS;
12555 }
12556 
GetPoolStats(VmaPoolStats * pStats)12557 void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
12558 {
12559     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12560 
12561     const size_t blockCount = m_Blocks.size();
12562 
12563     pStats->size = 0;
12564     pStats->unusedSize = 0;
12565     pStats->allocationCount = 0;
12566     pStats->unusedRangeCount = 0;
12567     pStats->unusedRangeSizeMax = 0;
12568     pStats->blockCount = blockCount;
12569 
12570     for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12571     {
12572         const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12573         VMA_ASSERT(pBlock);
12574         VMA_HEAVY_ASSERT(pBlock->Validate());
12575         pBlock->m_pMetadata->AddPoolStats(*pStats);
12576     }
12577 }
12578 
IsEmpty()12579 bool VmaBlockVector::IsEmpty()
12580 {
12581     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12582     return m_Blocks.empty();
12583 }
12584 
IsCorruptionDetectionEnabled()12585 bool VmaBlockVector::IsCorruptionDetectionEnabled() const
12586 {
12587     const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
12588     return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
12589         (VMA_DEBUG_MARGIN > 0) &&
12590         (m_Algorithm == 0 || m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) &&
12591         (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
12592 }
12593 
12594 static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
12595 
Allocate(uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)12596 VkResult VmaBlockVector::Allocate(
12597     uint32_t currentFrameIndex,
12598     VkDeviceSize size,
12599     VkDeviceSize alignment,
12600     const VmaAllocationCreateInfo& createInfo,
12601     VmaSuballocationType suballocType,
12602     size_t allocationCount,
12603     VmaAllocation* pAllocations)
12604 {
12605     size_t allocIndex;
12606     VkResult res = VK_SUCCESS;
12607 
12608     if(IsCorruptionDetectionEnabled())
12609     {
12610         size = VmaAlignUp<VkDeviceSize>(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12611         alignment = VmaAlignUp<VkDeviceSize>(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12612     }
12613 
12614     {
12615         VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12616         for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
12617         {
12618             res = AllocatePage(
12619                 currentFrameIndex,
12620                 size,
12621                 alignment,
12622                 createInfo,
12623                 suballocType,
12624                 pAllocations + allocIndex);
12625             if(res != VK_SUCCESS)
12626             {
12627                 break;
12628             }
12629         }
12630     }
12631 
12632     if(res != VK_SUCCESS)
12633     {
12634         // Free all already created allocations.
12635         while(allocIndex--)
12636         {
12637             Free(pAllocations[allocIndex]);
12638         }
12639         memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
12640     }
12641 
12642     return res;
12643 }
12644 
12645 // OH ISSUE: VMA preAlloc
AllocateReserved(uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)12646 VkResult VmaBlockVector::AllocateReserved(
12647     uint32_t currentFrameIndex,
12648     VkDeviceSize size,
12649     VkDeviceSize alignment,
12650     const VmaAllocationCreateInfo& createInfo,
12651     VmaSuballocationType suballocType,
12652     VmaAllocation* pAllocation)
12653 {
12654     size_t allocIndex;
12655     VkResult res = VK_SUCCESS;
12656 
12657     if(IsCorruptionDetectionEnabled())
12658     {
12659         size = VmaAlignUp<VkDeviceSize>(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12660         alignment = VmaAlignUp<VkDeviceSize>(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12661     }
12662     uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
12663 
12664     // Validate strategy.
12665     switch(strategy)
12666     {
12667     case 0:
12668         strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
12669         break;
12670     case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
12671     case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
12672     case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
12673         break;
12674     default:
12675         return VK_ERROR_FEATURE_NOT_PRESENT;
12676     }
12677 
12678     {
12679         VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12680         size_t newBlockIndex = 0;
12681         res = CreateBlock(m_PreferredBlockSize, &newBlockIndex);
12682         if(res != VK_SUCCESS)
12683         {
12684             return res;
12685         }
12686         VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
12687         VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
12688 
12689         res = AllocateFromBlock(
12690             pBlock,
12691             currentFrameIndex,
12692             size,
12693             alignment,
12694             createInfo.flags,
12695             createInfo.pUserData,
12696             suballocType,
12697             strategy,
12698             pAllocation);
12699     }
12700     return res;
12701 }
12702 
SwapLastBlock(VmaBlockVector * blockVector1,VmaBlockVector * blockVector2)12703 void SwapLastBlock(VmaBlockVector* blockVector1, VmaBlockVector* blockVector2)
12704 {
12705     VmaDeviceMemoryBlock* lastBlock1 = blockVector1->m_Blocks.back();
12706     blockVector1->m_Blocks.pop_back();
12707     VmaDeviceMemoryBlock* lastBlock2 = blockVector2->m_Blocks.back();
12708     blockVector2->m_Blocks.pop_back();
12709 
12710     blockVector1->m_Blocks.push_back(lastBlock2);
12711     blockVector2->m_Blocks.push_back(lastBlock1);
12712 }
12713 
AllocatePage(uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)12714 VkResult VmaBlockVector::AllocatePage(
12715     uint32_t currentFrameIndex,
12716     VkDeviceSize size,
12717     VkDeviceSize alignment,
12718     const VmaAllocationCreateInfo& createInfo,
12719     VmaSuballocationType suballocType,
12720     VmaAllocation* pAllocation)
12721 {
12722     const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
12723     bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
12724     const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
12725     const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
12726 
12727     VkDeviceSize freeMemory;
12728     {
12729         const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
12730         VmaBudget heapBudget = {};
12731         m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
12732         freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0;
12733     }
12734 
12735     const bool canFallbackToDedicated = !IsCustomPool();
12736     const bool canCreateNewBlock =
12737         ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
12738         (m_Blocks.size() < m_MaxBlockCount) &&
12739         (freeMemory >= size || !canFallbackToDedicated);
12740     uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
12741 
12742     // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
12743     // Which in turn is available only when maxBlockCount = 1.
12744     if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
12745     {
12746         canMakeOtherLost = false;
12747     }
12748 
12749     // Upper address can only be used with linear allocator and within single memory block.
12750     if(isUpperAddress &&
12751         (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
12752     {
12753         return VK_ERROR_FEATURE_NOT_PRESENT;
12754     }
12755 
12756     // Validate strategy.
12757     switch(strategy)
12758     {
12759     case 0:
12760         strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
12761         break;
12762     case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
12763     case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
12764     case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
12765         break;
12766     default:
12767         return VK_ERROR_FEATURE_NOT_PRESENT;
12768     }
12769 
12770     // Early reject: requested allocation size is larger that maximum block size for this block vector.
12771     if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
12772     {
12773         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12774     }
12775 
12776     /*
12777     Under certain condition, this whole section can be skipped for optimization, so
12778     we move on directly to trying to allocate with canMakeOtherLost. That's the case
12779     e.g. for custom pools with linear algorithm.
12780     */
12781     if(!canMakeOtherLost || canCreateNewBlock)
12782     {
12783         // 1. Search existing allocations. Try to allocate without making other allocations lost.
12784         VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
12785         allocFlagsCopy &= ~VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
12786 
12787         if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
12788         {
12789             // Use only last block.
12790             if(!m_Blocks.empty())
12791             {
12792                 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
12793                 VMA_ASSERT(pCurrBlock);
12794                 VkResult res = AllocateFromBlock(
12795                     pCurrBlock,
12796                     currentFrameIndex,
12797                     size,
12798                     alignment,
12799                     allocFlagsCopy,
12800                     createInfo.pUserData,
12801                     suballocType,
12802                     strategy,
12803                     pAllocation);
12804                 if(res == VK_SUCCESS)
12805                 {
12806                     VMA_DEBUG_LOG("    Returned from last block #%u", pCurrBlock->GetId());
12807                     return VK_SUCCESS;
12808                 }
12809             }
12810         }
12811         else
12812         {
12813             if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
12814             {
12815                 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12816                 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
12817                 {
12818                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12819                     VMA_ASSERT(pCurrBlock);
12820                     VkResult res = AllocateFromBlock(
12821                         pCurrBlock,
12822                         currentFrameIndex,
12823                         size,
12824                         alignment,
12825                         allocFlagsCopy,
12826                         createInfo.pUserData,
12827                         suballocType,
12828                         strategy,
12829                         pAllocation);
12830                     if(res == VK_SUCCESS)
12831                     {
12832                         VMA_DEBUG_LOG("    Returned from existing block #%u", pCurrBlock->GetId());
12833                         return VK_SUCCESS;
12834                     }
12835                 }
12836             }
12837             else // WORST_FIT, FIRST_FIT
12838             {
12839                 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
12840                 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12841                 {
12842                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12843                     VMA_ASSERT(pCurrBlock);
12844                     VkResult res = AllocateFromBlock(
12845                         pCurrBlock,
12846                         currentFrameIndex,
12847                         size,
12848                         alignment,
12849                         allocFlagsCopy,
12850                         createInfo.pUserData,
12851                         suballocType,
12852                         strategy,
12853                         pAllocation);
12854                     if(res == VK_SUCCESS)
12855                     {
12856                         VMA_DEBUG_LOG("    Returned from existing block #%u", pCurrBlock->GetId());
12857                         return VK_SUCCESS;
12858                     }
12859                 }
12860             }
12861         }
12862 
12863         // 2. Try to create new block.
12864         if(canCreateNewBlock)
12865         {
12866             // Calculate optimal size for new block.
12867             VkDeviceSize newBlockSize = m_PreferredBlockSize;
12868             uint32_t newBlockSizeShift = 0;
12869             const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
12870 
12871             if(!m_ExplicitBlockSize)
12872             {
12873                 // Allocate 1/8, 1/4, 1/2 as first blocks.
12874                 const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
12875                 for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
12876                 {
12877                     const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12878                     if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
12879                     {
12880                         newBlockSize = smallerNewBlockSize;
12881                         ++newBlockSizeShift;
12882                     }
12883                     else
12884                     {
12885                         break;
12886                     }
12887                 }
12888             }
12889 
12890             size_t newBlockIndex = 0;
12891             VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12892                 CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12893             // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
12894             if(!m_ExplicitBlockSize)
12895             {
12896                 while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
12897                 {
12898                     const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12899                     if(smallerNewBlockSize >= size)
12900                     {
12901                         newBlockSize = smallerNewBlockSize;
12902                         ++newBlockSizeShift;
12903                         res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12904                             CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12905                     }
12906                     else
12907                     {
12908                         break;
12909                     }
12910                 }
12911             }
12912 
12913             if(res == VK_SUCCESS)
12914             {
12915                 VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
12916                 VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
12917 
12918                 res = AllocateFromBlock(
12919                     pBlock,
12920                     currentFrameIndex,
12921                     size,
12922                     alignment,
12923                     allocFlagsCopy,
12924                     createInfo.pUserData,
12925                     suballocType,
12926                     strategy,
12927                     pAllocation);
12928                 if(res == VK_SUCCESS)
12929                 {
12930                     VMA_DEBUG_LOG("    Created new block #%u Size=%llu", pBlock->GetId(), newBlockSize);
12931                     return VK_SUCCESS;
12932                 }
12933                 else
12934                 {
12935                     // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
12936                     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12937                 }
12938             }
12939         }
12940     }
12941 
12942     // 3. Try to allocate from existing blocks with making other allocations lost.
12943     if(canMakeOtherLost)
12944     {
12945         uint32_t tryIndex = 0;
12946         for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
12947         {
12948             VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
12949             VmaAllocationRequest bestRequest = {};
12950             VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
12951 
12952             // 1. Search existing allocations.
12953             if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
12954             {
12955                 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12956                 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
12957                 {
12958                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12959                     VMA_ASSERT(pCurrBlock);
12960                     VmaAllocationRequest currRequest = {};
12961                     if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
12962                         currentFrameIndex,
12963                         m_FrameInUseCount,
12964                         m_BufferImageGranularity,
12965                         size,
12966                         alignment,
12967                         (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
12968                         suballocType,
12969                         canMakeOtherLost,
12970                         strategy,
12971                         &currRequest))
12972                     {
12973                         const VkDeviceSize currRequestCost = currRequest.CalcCost();
12974                         if(pBestRequestBlock == VMA_NULL ||
12975                             currRequestCost < bestRequestCost)
12976                         {
12977                             pBestRequestBlock = pCurrBlock;
12978                             bestRequest = currRequest;
12979                             bestRequestCost = currRequestCost;
12980 
12981                             if(bestRequestCost == 0)
12982                             {
12983                                 break;
12984                             }
12985                         }
12986                     }
12987                 }
12988             }
12989             else // WORST_FIT, FIRST_FIT
12990             {
12991                 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
12992                 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12993                 {
12994                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12995                     VMA_ASSERT(pCurrBlock);
12996                     VmaAllocationRequest currRequest = {};
12997                     if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
12998                         currentFrameIndex,
12999                         m_FrameInUseCount,
13000                         m_BufferImageGranularity,
13001                         size,
13002                         alignment,
13003                         (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
13004                         suballocType,
13005                         canMakeOtherLost,
13006                         strategy,
13007                         &currRequest))
13008                     {
13009                         const VkDeviceSize currRequestCost = currRequest.CalcCost();
13010                         if(pBestRequestBlock == VMA_NULL ||
13011                             currRequestCost < bestRequestCost ||
13012                             strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
13013                         {
13014                             pBestRequestBlock = pCurrBlock;
13015                             bestRequest = currRequest;
13016                             bestRequestCost = currRequestCost;
13017 
13018                             if(bestRequestCost == 0 ||
13019                                 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
13020                             {
13021                                 break;
13022                             }
13023                         }
13024                     }
13025                 }
13026             }
13027 
13028             if(pBestRequestBlock != VMA_NULL)
13029             {
13030                 if(mapped)
13031                 {
13032                     VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
13033                     if(res != VK_SUCCESS)
13034                     {
13035                         return res;
13036                     }
13037                 }
13038 
13039                 if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
13040                     currentFrameIndex,
13041                     m_FrameInUseCount,
13042                     &bestRequest))
13043                 {
13044                     // Allocate from this pBlock.
13045                     *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
13046                     pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, *pAllocation);
13047                     UpdateHasEmptyBlock();
13048                     (*pAllocation)->InitBlockAllocation(
13049                         pBestRequestBlock,
13050                         bestRequest.offset,
13051                         alignment,
13052                         size,
13053                         m_MemoryTypeIndex,
13054                         suballocType,
13055                         mapped,
13056                         (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0,
13057                         IsNewBlockFlag());
13058                     ClearNewBlockFlag();
13059                     VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
13060                     VMA_DEBUG_LOG("    Returned from existing block");
13061                     (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
13062                     m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
13063                     if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13064                     {
13065                         m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13066                     }
13067                     if(IsCorruptionDetectionEnabled())
13068                     {
13069                         VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
13070                         VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
13071                     }
13072                     return VK_SUCCESS;
13073                 }
13074                 // else: Some allocations must have been touched while we are here. Next try.
13075             }
13076             else
13077             {
13078                 // Could not find place in any of the blocks - break outer loop.
13079                 break;
13080             }
13081         }
13082         /* Maximum number of tries exceeded - a very unlike event when many other
13083         threads are simultaneously touching allocations making it impossible to make
13084         lost at the same time as we try to allocate. */
13085         if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
13086         {
13087             return VK_ERROR_TOO_MANY_OBJECTS;
13088         }
13089     }
13090 
13091     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13092 }
13093 
Free(const VmaAllocation hAllocation)13094 void VmaBlockVector::Free(
13095     const VmaAllocation hAllocation)
13096 {
13097     VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
13098 
13099     bool budgetExceeded = false;
13100     {
13101         const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
13102         VmaBudget heapBudget = {};
13103         m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
13104         budgetExceeded = heapBudget.usage >= heapBudget.budget;
13105     }
13106 
13107     // Scope for lock.
13108     {
13109         VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13110 
13111         VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
13112 
13113         if(IsCorruptionDetectionEnabled())
13114         {
13115             VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
13116             VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
13117         }
13118 
13119         if(hAllocation->IsPersistentMap())
13120         {
13121             pBlock->Unmap(m_hAllocator, 1);
13122         }
13123 
13124         pBlock->m_pMetadata->Free(hAllocation);
13125         VMA_HEAVY_ASSERT(pBlock->Validate());
13126 
13127         VMA_DEBUG_LOG("  Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
13128 
13129         const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount;
13130         // pBlock became empty after this deallocation.
13131         if(pBlock->m_pMetadata->IsEmpty())
13132         {
13133             // Already has empty block. We don't want to have two, so delete this one.
13134             if((m_HasEmptyBlock || budgetExceeded) && canDeleteBlock)
13135             {
13136                 pBlockToDelete = pBlock;
13137                 Remove(pBlock);
13138             }
13139             // else: We now have an empty block - leave it.
13140         }
13141         // pBlock didn't become empty, but we have another empty block - find and free that one.
13142         // (This is optional, heuristics.)
13143         else if(m_HasEmptyBlock && canDeleteBlock)
13144         {
13145             VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
13146             if(pLastBlock->m_pMetadata->IsEmpty())
13147             {
13148                 pBlockToDelete = pLastBlock;
13149                 m_Blocks.pop_back();
13150             }
13151         }
13152 
13153         UpdateHasEmptyBlock();
13154         IncrementallySortBlocks();
13155     }
13156 
13157     // Destruction of a free block. Deferred until this point, outside of mutex
13158     // lock, for performance reason.
13159     if(pBlockToDelete != VMA_NULL)
13160     {
13161         VMA_DEBUG_LOG("    Deleted empty block");
13162         pBlockToDelete->Destroy(m_hAllocator);
13163         vma_delete(m_hAllocator, pBlockToDelete);
13164     }
13165 }
13166 
13167 // OH ISSUE: VMA preAlloc
FreeReserved(const VmaAllocation hAllocation)13168 void VmaBlockVector::FreeReserved(
13169     const VmaAllocation hAllocation)
13170 {
13171     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13172 
13173     VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
13174 
13175     if(IsCorruptionDetectionEnabled())
13176     {
13177         VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
13178         VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
13179     }
13180 
13181     if(hAllocation->IsPersistentMap())
13182     {
13183         pBlock->Unmap(m_hAllocator, 1);
13184     }
13185 
13186     pBlock->m_pMetadata->Free(hAllocation);
13187     VMA_HEAVY_ASSERT(pBlock->Validate());
13188 
13189     VMA_DEBUG_LOG("  Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
13190     IncrementallySortBlocks();
13191 }
13192 
CalcMaxBlockSize()13193 VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
13194 {
13195     VkDeviceSize result = 0;
13196     for(size_t i = m_Blocks.size(); i--; )
13197     {
13198         result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
13199         if(result >= m_PreferredBlockSize)
13200         {
13201             break;
13202         }
13203     }
13204     return result;
13205 }
13206 
Remove(VmaDeviceMemoryBlock * pBlock)13207 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
13208 {
13209     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13210     {
13211         if(m_Blocks[blockIndex] == pBlock)
13212         {
13213             VmaVectorRemove(m_Blocks, blockIndex);
13214             return;
13215         }
13216     }
13217     VMA_ASSERT(0);
13218 }
13219 
IncrementallySortBlocks()13220 void VmaBlockVector::IncrementallySortBlocks()
13221 {
13222     if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
13223     {
13224         // Bubble sort only until first swap.
13225         for(size_t i = 1; i < m_Blocks.size(); ++i)
13226         {
13227             if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
13228             {
13229                 VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
13230                 return;
13231             }
13232         }
13233     }
13234 }
13235 
AllocateFromBlock(VmaDeviceMemoryBlock * pBlock,uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,VmaAllocationCreateFlags allocFlags,void * pUserData,VmaSuballocationType suballocType,uint32_t strategy,VmaAllocation * pAllocation)13236 VkResult VmaBlockVector::AllocateFromBlock(
13237     VmaDeviceMemoryBlock* pBlock,
13238     uint32_t currentFrameIndex,
13239     VkDeviceSize size,
13240     VkDeviceSize alignment,
13241     VmaAllocationCreateFlags allocFlags,
13242     void* pUserData,
13243     VmaSuballocationType suballocType,
13244     uint32_t strategy,
13245     VmaAllocation* pAllocation)
13246 {
13247     VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
13248     const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
13249     const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
13250     const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
13251 
13252     VmaAllocationRequest currRequest = {};
13253     if(pBlock->m_pMetadata->CreateAllocationRequest(
13254         currentFrameIndex,
13255         m_FrameInUseCount,
13256         m_BufferImageGranularity,
13257         size,
13258         alignment,
13259         isUpperAddress,
13260         suballocType,
13261         false, // canMakeOtherLost
13262         strategy,
13263         &currRequest))
13264     {
13265         // Allocate from pCurrBlock.
13266         VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
13267 
13268         if(mapped)
13269         {
13270             VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
13271             if(res != VK_SUCCESS)
13272             {
13273                 return res;
13274             }
13275         }
13276 
13277         *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
13278         pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, *pAllocation);
13279         UpdateHasEmptyBlock();
13280         (*pAllocation)->InitBlockAllocation(
13281             pBlock,
13282             currRequest.offset,
13283             alignment,
13284             size,
13285             m_MemoryTypeIndex,
13286             suballocType,
13287             mapped,
13288             (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0,
13289             IsNewBlockFlag());
13290         ClearNewBlockFlag();
13291         VMA_HEAVY_ASSERT(pBlock->Validate());
13292         (*pAllocation)->SetUserData(m_hAllocator, pUserData);
13293         m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
13294         if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13295         {
13296             m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13297         }
13298         if(IsCorruptionDetectionEnabled())
13299         {
13300             VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
13301             VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
13302         }
13303         return VK_SUCCESS;
13304     }
13305     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13306 }
13307 
CreateBlock(VkDeviceSize blockSize,size_t * pNewBlockIndex)13308 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
13309 {
13310     VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
13311     allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
13312     allocInfo.allocationSize = blockSize;
13313 
13314 #if VMA_BUFFER_DEVICE_ADDRESS
13315     // Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature.
13316     VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
13317     if(m_hAllocator->m_UseKhrBufferDeviceAddress)
13318     {
13319         allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
13320         VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
13321     }
13322 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
13323 
13324     VkDeviceMemory mem = VK_NULL_HANDLE;
13325     VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
13326     if(res < 0)
13327     {
13328         return res;
13329     }
13330 
13331     // New VkDeviceMemory successfully created.
13332 
13333     // Create new Allocation for it.
13334     VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
13335     pBlock->Init(
13336         m_hAllocator,
13337         m_hParentPool,
13338         m_MemoryTypeIndex,
13339         mem,
13340         allocInfo.allocationSize,
13341         m_NextBlockId++,
13342         m_Algorithm);
13343     m_NewBlockFlag = true;
13344 
13345     m_Blocks.push_back(pBlock);
13346     if(pNewBlockIndex != VMA_NULL)
13347     {
13348         *pNewBlockIndex = m_Blocks.size() - 1;
13349     }
13350 
13351     return VK_SUCCESS;
13352 }
13353 
ApplyDefragmentationMovesCpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,const VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves)13354 void VmaBlockVector::ApplyDefragmentationMovesCpu(
13355     class VmaBlockVectorDefragmentationContext* pDefragCtx,
13356     const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
13357 {
13358     const size_t blockCount = m_Blocks.size();
13359     const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
13360 
13361     enum BLOCK_FLAG
13362     {
13363         BLOCK_FLAG_USED = 0x00000001,
13364         BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
13365     };
13366 
13367     struct BlockInfo
13368     {
13369         uint32_t flags;
13370         void* pMappedData;
13371     };
13372     VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
13373         blockInfo(blockCount, BlockInfo(), VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
13374     memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
13375 
13376     // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13377     const size_t moveCount = moves.size();
13378     for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13379     {
13380         const VmaDefragmentationMove& move = moves[moveIndex];
13381         blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
13382         blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
13383     }
13384 
13385     VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13386 
13387     // Go over all blocks. Get mapped pointer or map if necessary.
13388     for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13389     {
13390         BlockInfo& currBlockInfo = blockInfo[blockIndex];
13391         VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13392         if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
13393         {
13394             currBlockInfo.pMappedData = pBlock->GetMappedData();
13395             // It is not originally mapped - map it.
13396             if(currBlockInfo.pMappedData == VMA_NULL)
13397             {
13398                 pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
13399                 if(pDefragCtx->res == VK_SUCCESS)
13400                 {
13401                     currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
13402                 }
13403             }
13404         }
13405     }
13406 
13407     // Go over all moves. Do actual data transfer.
13408     if(pDefragCtx->res == VK_SUCCESS)
13409     {
13410         const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
13411         VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
13412 
13413         for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13414         {
13415             const VmaDefragmentationMove& move = moves[moveIndex];
13416 
13417             const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
13418             const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
13419 
13420             VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
13421 
13422             // Invalidate source.
13423             if(isNonCoherent)
13424             {
13425                 VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
13426                 memRange.memory = pSrcBlock->GetDeviceMemory();
13427                 memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
13428                 memRange.size = VMA_MIN(
13429                     VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
13430                     pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
13431                 (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13432             }
13433 
13434             // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
13435             memmove(
13436                 reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
13437                 reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
13438                 static_cast<size_t>(move.size));
13439 
13440             if(IsCorruptionDetectionEnabled())
13441             {
13442                 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
13443                 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
13444             }
13445 
13446             // Flush destination.
13447             if(isNonCoherent)
13448             {
13449                 VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
13450                 memRange.memory = pDstBlock->GetDeviceMemory();
13451                 memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
13452                 memRange.size = VMA_MIN(
13453                     VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
13454                     pDstBlock->m_pMetadata->GetSize() - memRange.offset);
13455                 (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13456             }
13457         }
13458     }
13459 
13460     // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
13461     // Regardless of pCtx->res == VK_SUCCESS.
13462     for(size_t blockIndex = blockCount; blockIndex--; )
13463     {
13464         const BlockInfo& currBlockInfo = blockInfo[blockIndex];
13465         if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
13466         {
13467             VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13468             pBlock->Unmap(m_hAllocator, 1);
13469         }
13470     }
13471 }
13472 
ApplyDefragmentationMovesGpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkCommandBuffer commandBuffer)13473 void VmaBlockVector::ApplyDefragmentationMovesGpu(
13474     class VmaBlockVectorDefragmentationContext* pDefragCtx,
13475     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13476     VkCommandBuffer commandBuffer)
13477 {
13478     const size_t blockCount = m_Blocks.size();
13479 
13480     pDefragCtx->blockContexts.resize(blockCount);
13481     memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext));
13482 
13483     // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13484     const size_t moveCount = moves.size();
13485     for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13486     {
13487         const VmaDefragmentationMove& move = moves[moveIndex];
13488 
13489         //if(move.type == VMA_ALLOCATION_TYPE_UNKNOWN)
13490         {
13491             // Old school move still require us to map the whole block
13492             pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13493             pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13494         }
13495     }
13496 
13497     VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13498 
13499     // Go over all blocks. Create and bind buffer for whole block if necessary.
13500     {
13501         VkBufferCreateInfo bufCreateInfo;
13502         VmaFillGpuDefragmentationBufferCreateInfo(bufCreateInfo);
13503 
13504         for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13505         {
13506             VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
13507             VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13508             if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
13509             {
13510                 bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
13511                 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
13512                     m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
13513                 if(pDefragCtx->res == VK_SUCCESS)
13514                 {
13515                     pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
13516                         m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
13517                 }
13518             }
13519         }
13520     }
13521 
13522     // Go over all moves. Post data transfer commands to command buffer.
13523     if(pDefragCtx->res == VK_SUCCESS)
13524     {
13525         for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13526         {
13527             const VmaDefragmentationMove& move = moves[moveIndex];
13528 
13529             const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
13530             const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
13531 
13532             VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
13533 
13534             VkBufferCopy region = {
13535                 move.srcOffset,
13536                 move.dstOffset,
13537                 move.size };
13538             (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
13539                 commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, &region);
13540         }
13541     }
13542 
13543     // Save buffers to defrag context for later destruction.
13544     if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
13545     {
13546         pDefragCtx->res = VK_NOT_READY;
13547     }
13548 }
13549 
FreeEmptyBlocks(VmaDefragmentationStats * pDefragmentationStats)13550 void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
13551 {
13552     for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
13553     {
13554         VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13555         if(pBlock->m_pMetadata->IsEmpty())
13556         {
13557             if(m_Blocks.size() > m_MinBlockCount)
13558             {
13559                 if(pDefragmentationStats != VMA_NULL)
13560                 {
13561                     ++pDefragmentationStats->deviceMemoryBlocksFreed;
13562                     pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
13563                 }
13564 
13565                 VmaVectorRemove(m_Blocks, blockIndex);
13566                 pBlock->Destroy(m_hAllocator);
13567                 vma_delete(m_hAllocator, pBlock);
13568             }
13569             else
13570             {
13571                 break;
13572             }
13573         }
13574     }
13575     UpdateHasEmptyBlock();
13576 }
13577 
UpdateHasEmptyBlock()13578 void VmaBlockVector::UpdateHasEmptyBlock()
13579 {
13580     m_HasEmptyBlock = false;
13581     for(size_t index = 0, count = m_Blocks.size(); index < count; ++index)
13582     {
13583         VmaDeviceMemoryBlock* const pBlock = m_Blocks[index];
13584         if(pBlock->m_pMetadata->IsEmpty())
13585         {
13586             m_HasEmptyBlock = true;
13587             break;
13588         }
13589     }
13590 }
13591 
13592 #if VMA_STATS_STRING_ENABLED
13593 
PrintDetailedMap(class VmaJsonWriter & json)13594 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
13595 {
13596     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13597 
13598     json.BeginObject();
13599 
13600     if(IsCustomPool())
13601     {
13602         const char* poolName = m_hParentPool->GetName();
13603         if(poolName != VMA_NULL && poolName[0] != '\0')
13604         {
13605             json.WriteString("Name");
13606             json.WriteString(poolName);
13607         }
13608 
13609         json.WriteString("MemoryTypeIndex");
13610         json.WriteNumber(m_MemoryTypeIndex);
13611 
13612         json.WriteString("BlockSize");
13613         json.WriteNumber(m_PreferredBlockSize);
13614 
13615         json.WriteString("BlockCount");
13616         json.BeginObject(true);
13617         if(m_MinBlockCount > 0)
13618         {
13619             json.WriteString("Min");
13620             json.WriteNumber((uint64_t)m_MinBlockCount);
13621         }
13622         if(m_MaxBlockCount < SIZE_MAX)
13623         {
13624             json.WriteString("Max");
13625             json.WriteNumber((uint64_t)m_MaxBlockCount);
13626         }
13627         json.WriteString("Cur");
13628         json.WriteNumber((uint64_t)m_Blocks.size());
13629         json.EndObject();
13630 
13631         if(m_FrameInUseCount > 0)
13632         {
13633             json.WriteString("FrameInUseCount");
13634             json.WriteNumber(m_FrameInUseCount);
13635         }
13636 
13637         if(m_Algorithm != 0)
13638         {
13639             json.WriteString("Algorithm");
13640             json.WriteString(VmaAlgorithmToStr(m_Algorithm));
13641         }
13642     }
13643     else
13644     {
13645         json.WriteString("PreferredBlockSize");
13646         json.WriteNumber(m_PreferredBlockSize);
13647     }
13648 
13649     json.WriteString("Blocks");
13650     json.BeginObject();
13651     for(size_t i = 0; i < m_Blocks.size(); ++i)
13652     {
13653         json.BeginString();
13654         json.ContinueString(m_Blocks[i]->GetId());
13655         json.EndString();
13656 
13657         m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
13658     }
13659     json.EndObject();
13660 
13661     json.EndObject();
13662 }
13663 
13664 #endif // #if VMA_STATS_STRING_ENABLED
13665 
Defragment(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats,VmaDefragmentationFlags flags,VkDeviceSize & maxCpuBytesToMove,uint32_t & maxCpuAllocationsToMove,VkDeviceSize & maxGpuBytesToMove,uint32_t & maxGpuAllocationsToMove,VkCommandBuffer commandBuffer)13666 void VmaBlockVector::Defragment(
13667     class VmaBlockVectorDefragmentationContext* pCtx,
13668     VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags,
13669     VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
13670     VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
13671     VkCommandBuffer commandBuffer)
13672 {
13673     pCtx->res = VK_SUCCESS;
13674 
13675     const VkMemoryPropertyFlags memPropFlags =
13676         m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
13677     const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
13678 
13679     const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
13680         isHostVisible;
13681     const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
13682         !IsCorruptionDetectionEnabled() &&
13683         ((1u << m_MemoryTypeIndex) & m_hAllocator->GetGpuDefragmentationMemoryTypeBits()) != 0;
13684 
13685     // There are options to defragment this memory type.
13686     if(canDefragmentOnCpu || canDefragmentOnGpu)
13687     {
13688         bool defragmentOnGpu;
13689         // There is only one option to defragment this memory type.
13690         if(canDefragmentOnGpu != canDefragmentOnCpu)
13691         {
13692             defragmentOnGpu = canDefragmentOnGpu;
13693         }
13694         // Both options are available: Heuristics to choose the best one.
13695         else
13696         {
13697             defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
13698                 m_hAllocator->IsIntegratedGpu();
13699         }
13700 
13701         bool overlappingMoveSupported = !defragmentOnGpu;
13702 
13703         if(m_hAllocator->m_UseMutex)
13704         {
13705             if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
13706             {
13707                 if(!m_Mutex.TryLockWrite())
13708                 {
13709                     pCtx->res = VK_ERROR_INITIALIZATION_FAILED;
13710                     return;
13711                 }
13712             }
13713             else
13714             {
13715                 m_Mutex.LockWrite();
13716                 pCtx->mutexLocked = true;
13717             }
13718         }
13719 
13720         pCtx->Begin(overlappingMoveSupported, flags);
13721 
13722         // Defragment.
13723 
13724         const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
13725         const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
13726         pCtx->res = pCtx->GetAlgorithm()->Defragment(pCtx->defragmentationMoves, maxBytesToMove, maxAllocationsToMove, flags);
13727 
13728         // Accumulate statistics.
13729         if(pStats != VMA_NULL)
13730         {
13731             const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
13732             const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
13733             pStats->bytesMoved += bytesMoved;
13734             pStats->allocationsMoved += allocationsMoved;
13735             VMA_ASSERT(bytesMoved <= maxBytesToMove);
13736             VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
13737             if(defragmentOnGpu)
13738             {
13739                 maxGpuBytesToMove -= bytesMoved;
13740                 maxGpuAllocationsToMove -= allocationsMoved;
13741             }
13742             else
13743             {
13744                 maxCpuBytesToMove -= bytesMoved;
13745                 maxCpuAllocationsToMove -= allocationsMoved;
13746             }
13747         }
13748 
13749         if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
13750         {
13751             if(m_hAllocator->m_UseMutex)
13752                 m_Mutex.UnlockWrite();
13753 
13754             if(pCtx->res >= VK_SUCCESS && !pCtx->defragmentationMoves.empty())
13755                 pCtx->res = VK_NOT_READY;
13756 
13757             return;
13758         }
13759 
13760         if(pCtx->res >= VK_SUCCESS)
13761         {
13762             if(defragmentOnGpu)
13763             {
13764                 ApplyDefragmentationMovesGpu(pCtx, pCtx->defragmentationMoves, commandBuffer);
13765             }
13766             else
13767             {
13768                 ApplyDefragmentationMovesCpu(pCtx, pCtx->defragmentationMoves);
13769             }
13770         }
13771     }
13772 }
13773 
DefragmentationEnd(class VmaBlockVectorDefragmentationContext * pCtx,uint32_t flags,VmaDefragmentationStats * pStats)13774 void VmaBlockVector::DefragmentationEnd(
13775     class VmaBlockVectorDefragmentationContext* pCtx,
13776     uint32_t flags,
13777     VmaDefragmentationStats* pStats)
13778 {
13779     if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL && m_hAllocator->m_UseMutex)
13780     {
13781         VMA_ASSERT(pCtx->mutexLocked == false);
13782 
13783         // Incremental defragmentation doesn't hold the lock, so when we enter here we don't actually have any
13784         // lock protecting us. Since we mutate state here, we have to take the lock out now
13785         m_Mutex.LockWrite();
13786         pCtx->mutexLocked = true;
13787     }
13788 
13789     // If the mutex isn't locked we didn't do any work and there is nothing to delete.
13790     if(pCtx->mutexLocked || !m_hAllocator->m_UseMutex)
13791     {
13792         // Destroy buffers.
13793         for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--;)
13794         {
13795             VmaBlockDefragmentationContext &blockCtx = pCtx->blockContexts[blockIndex];
13796             if(blockCtx.hBuffer)
13797             {
13798                 (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
13799             }
13800         }
13801 
13802         if(pCtx->res >= VK_SUCCESS)
13803         {
13804             FreeEmptyBlocks(pStats);
13805         }
13806     }
13807 
13808     if(pCtx->mutexLocked)
13809     {
13810         VMA_ASSERT(m_hAllocator->m_UseMutex);
13811         m_Mutex.UnlockWrite();
13812     }
13813 }
13814 
ProcessDefragmentations(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationPassMoveInfo * pMove,uint32_t maxMoves)13815 uint32_t VmaBlockVector::ProcessDefragmentations(
13816     class VmaBlockVectorDefragmentationContext *pCtx,
13817     VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves)
13818 {
13819     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13820 
13821     const uint32_t moveCount = std::min(uint32_t(pCtx->defragmentationMoves.size()) - pCtx->defragmentationMovesProcessed, maxMoves);
13822 
13823     for(uint32_t i = 0; i < moveCount; ++ i)
13824     {
13825         VmaDefragmentationMove& move = pCtx->defragmentationMoves[pCtx->defragmentationMovesProcessed + i];
13826 
13827         pMove->allocation = move.hAllocation;
13828         pMove->memory = move.pDstBlock->GetDeviceMemory();
13829         pMove->offset = move.dstOffset;
13830 
13831         ++ pMove;
13832     }
13833 
13834     pCtx->defragmentationMovesProcessed += moveCount;
13835 
13836     return moveCount;
13837 }
13838 
CommitDefragmentations(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats)13839 void VmaBlockVector::CommitDefragmentations(
13840     class VmaBlockVectorDefragmentationContext *pCtx,
13841     VmaDefragmentationStats* pStats)
13842 {
13843     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13844 
13845     for(uint32_t i = pCtx->defragmentationMovesCommitted; i < pCtx->defragmentationMovesProcessed; ++ i)
13846     {
13847         const VmaDefragmentationMove &move = pCtx->defragmentationMoves[i];
13848 
13849         move.pSrcBlock->m_pMetadata->FreeAtOffset(move.srcOffset);
13850         move.hAllocation->ChangeBlockAllocation(m_hAllocator, move.pDstBlock, move.dstOffset);
13851     }
13852 
13853     pCtx->defragmentationMovesCommitted = pCtx->defragmentationMovesProcessed;
13854     FreeEmptyBlocks(pStats);
13855 }
13856 
CalcAllocationCount()13857 size_t VmaBlockVector::CalcAllocationCount() const
13858 {
13859     size_t result = 0;
13860     for(size_t i = 0; i < m_Blocks.size(); ++i)
13861     {
13862         result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
13863     }
13864     return result;
13865 }
13866 
IsBufferImageGranularityConflictPossible()13867 bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
13868 {
13869     if(m_BufferImageGranularity == 1)
13870     {
13871         return false;
13872     }
13873     VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
13874     for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
13875     {
13876         VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
13877         VMA_ASSERT(m_Algorithm == 0);
13878         VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
13879         if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
13880         {
13881             return true;
13882         }
13883     }
13884     return false;
13885 }
13886 
MakePoolAllocationsLost(uint32_t currentFrameIndex,size_t * pLostAllocationCount)13887 void VmaBlockVector::MakePoolAllocationsLost(
13888     uint32_t currentFrameIndex,
13889     size_t* pLostAllocationCount)
13890 {
13891     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13892     size_t lostAllocationCount = 0;
13893     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13894     {
13895         VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13896         VMA_ASSERT(pBlock);
13897         lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
13898     }
13899     if(pLostAllocationCount != VMA_NULL)
13900     {
13901         *pLostAllocationCount = lostAllocationCount;
13902     }
13903 }
13904 
CheckCorruption()13905 VkResult VmaBlockVector::CheckCorruption()
13906 {
13907     if(!IsCorruptionDetectionEnabled())
13908     {
13909         return VK_ERROR_FEATURE_NOT_PRESENT;
13910     }
13911 
13912     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13913     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13914     {
13915         VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13916         VMA_ASSERT(pBlock);
13917         VkResult res = pBlock->CheckCorruption(m_hAllocator);
13918         if(res != VK_SUCCESS)
13919         {
13920             return res;
13921         }
13922     }
13923     return VK_SUCCESS;
13924 }
13925 
AddStats(VmaStats * pStats)13926 void VmaBlockVector::AddStats(VmaStats* pStats)
13927 {
13928     const uint32_t memTypeIndex = m_MemoryTypeIndex;
13929     const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
13930 
13931     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13932 
13933     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13934     {
13935         const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13936         VMA_ASSERT(pBlock);
13937         VMA_HEAVY_ASSERT(pBlock->Validate());
13938         VmaStatInfo allocationStatInfo;
13939         pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
13940         VmaAddStatInfo(pStats->total, allocationStatInfo);
13941         VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
13942         VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
13943     }
13944 }
13945 
FreeEmptyBlock()13946 void VmaBlockVector::FreeEmptyBlock()
13947 {
13948     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13949     FreeEmptyBlocks(VMA_NULL);
13950 }
13951 
13952 ////////////////////////////////////////////////////////////////////////////////
13953 // VmaDefragmentationAlgorithm_Generic members definition
13954 
VmaDefragmentationAlgorithm_Generic(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)13955 VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
13956     VmaAllocator hAllocator,
13957     VmaBlockVector* pBlockVector,
13958     uint32_t currentFrameIndex,
13959     bool overlappingMoveSupported) :
13960     VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
13961     m_AllocationCount(0),
13962     m_AllAllocations(false),
13963     m_BytesMoved(0),
13964     m_AllocationsMoved(0),
13965     m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
13966 {
13967     // Create block info for each block.
13968     const size_t blockCount = m_pBlockVector->m_Blocks.size();
13969     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13970     {
13971         BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
13972         pBlockInfo->m_OriginalBlockIndex = blockIndex;
13973         pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
13974         m_Blocks.push_back(pBlockInfo);
13975     }
13976 
13977     // Sort them by m_pBlock pointer value.
13978     VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
13979 }
13980 
~VmaDefragmentationAlgorithm_Generic()13981 VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
13982 {
13983     for(size_t i = m_Blocks.size(); i--; )
13984     {
13985         vma_delete(m_hAllocator, m_Blocks[i]);
13986     }
13987 }
13988 
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)13989 void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
13990 {
13991     // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
13992     if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
13993     {
13994         VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
13995         BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
13996         if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
13997         {
13998             AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
13999             (*it)->m_Allocations.push_back(allocInfo);
14000         }
14001         else
14002         {
14003             VMA_ASSERT(0);
14004         }
14005 
14006         ++m_AllocationCount;
14007     }
14008 }
14009 
DefragmentRound(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,bool freeOldAllocations)14010 VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
14011     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14012     VkDeviceSize maxBytesToMove,
14013     uint32_t maxAllocationsToMove,
14014     bool freeOldAllocations)
14015 {
14016     if(m_Blocks.empty())
14017     {
14018         return VK_SUCCESS;
14019     }
14020 
14021     // This is a choice based on research.
14022     // Option 1:
14023     uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
14024     // Option 2:
14025     //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
14026     // Option 3:
14027     //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
14028 
14029     size_t srcBlockMinIndex = 0;
14030     // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
14031     /*
14032     if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
14033     {
14034         const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
14035         if(blocksWithNonMovableCount > 0)
14036         {
14037             srcBlockMinIndex = blocksWithNonMovableCount - 1;
14038         }
14039     }
14040     */
14041 
14042     size_t srcBlockIndex = m_Blocks.size() - 1;
14043     size_t srcAllocIndex = SIZE_MAX;
14044     for(;;)
14045     {
14046         // 1. Find next allocation to move.
14047         // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
14048         // 1.2. Then start from last to first m_Allocations.
14049         while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
14050         {
14051             if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
14052             {
14053                 // Finished: no more allocations to process.
14054                 if(srcBlockIndex == srcBlockMinIndex)
14055                 {
14056                     return VK_SUCCESS;
14057                 }
14058                 else
14059                 {
14060                     --srcBlockIndex;
14061                     srcAllocIndex = SIZE_MAX;
14062                 }
14063             }
14064             else
14065             {
14066                 srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
14067             }
14068         }
14069 
14070         BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
14071         AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
14072 
14073         const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
14074         const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
14075         const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
14076         const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
14077 
14078         // 2. Try to find new place for this allocation in preceding or current block.
14079         for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
14080         {
14081             BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
14082             VmaAllocationRequest dstAllocRequest;
14083             if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
14084                 m_CurrentFrameIndex,
14085                 m_pBlockVector->GetFrameInUseCount(),
14086                 m_pBlockVector->GetBufferImageGranularity(),
14087                 size,
14088                 alignment,
14089                 false, // upperAddress
14090                 suballocType,
14091                 false, // canMakeOtherLost
14092                 strategy,
14093                 &dstAllocRequest) &&
14094             MoveMakesSense(
14095                 dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
14096             {
14097                 VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
14098 
14099                 // Reached limit on number of allocations or bytes to move.
14100                 if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
14101                     (m_BytesMoved + size > maxBytesToMove))
14102                 {
14103                     return VK_SUCCESS;
14104                 }
14105 
14106                 VmaDefragmentationMove move = {};
14107                 move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
14108                 move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
14109                 move.srcOffset = srcOffset;
14110                 move.dstOffset = dstAllocRequest.offset;
14111                 move.size = size;
14112                 move.hAllocation = allocInfo.m_hAllocation;
14113                 move.pSrcBlock = pSrcBlockInfo->m_pBlock;
14114                 move.pDstBlock = pDstBlockInfo->m_pBlock;
14115 
14116                 moves.push_back(move);
14117 
14118                 pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
14119                     dstAllocRequest,
14120                     suballocType,
14121                     size,
14122                     allocInfo.m_hAllocation);
14123 
14124                 if(freeOldAllocations)
14125                 {
14126                     pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
14127                     allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
14128                 }
14129 
14130                 if(allocInfo.m_pChanged != VMA_NULL)
14131                 {
14132                     *allocInfo.m_pChanged = VK_TRUE;
14133                 }
14134 
14135                 ++m_AllocationsMoved;
14136                 m_BytesMoved += size;
14137 
14138                 VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
14139 
14140                 break;
14141             }
14142         }
14143 
14144         // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
14145 
14146         if(srcAllocIndex > 0)
14147         {
14148             --srcAllocIndex;
14149         }
14150         else
14151         {
14152             if(srcBlockIndex > 0)
14153             {
14154                 --srcBlockIndex;
14155                 srcAllocIndex = SIZE_MAX;
14156             }
14157             else
14158             {
14159                 return VK_SUCCESS;
14160             }
14161         }
14162     }
14163 }
14164 
CalcBlocksWithNonMovableCount()14165 size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
14166 {
14167     size_t result = 0;
14168     for(size_t i = 0; i < m_Blocks.size(); ++i)
14169     {
14170         if(m_Blocks[i]->m_HasNonMovableAllocations)
14171         {
14172             ++result;
14173         }
14174     }
14175     return result;
14176 }
14177 
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,VmaDefragmentationFlags flags)14178 VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
14179     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14180     VkDeviceSize maxBytesToMove,
14181     uint32_t maxAllocationsToMove,
14182     VmaDefragmentationFlags flags)
14183 {
14184     if(!m_AllAllocations && m_AllocationCount == 0)
14185     {
14186         return VK_SUCCESS;
14187     }
14188 
14189     const size_t blockCount = m_Blocks.size();
14190     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14191     {
14192         BlockInfo* pBlockInfo = m_Blocks[blockIndex];
14193 
14194         if(m_AllAllocations)
14195         {
14196             VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
14197             for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
14198                 it != pMetadata->m_Suballocations.end();
14199                 ++it)
14200             {
14201                 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
14202                 {
14203                     AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
14204                     pBlockInfo->m_Allocations.push_back(allocInfo);
14205                 }
14206             }
14207         }
14208 
14209         pBlockInfo->CalcHasNonMovableAllocations();
14210 
14211         // This is a choice based on research.
14212         // Option 1:
14213         pBlockInfo->SortAllocationsByOffsetDescending();
14214         // Option 2:
14215         //pBlockInfo->SortAllocationsBySizeDescending();
14216     }
14217 
14218     // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
14219     VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
14220 
14221     // This is a choice based on research.
14222     const uint32_t roundCount = 2;
14223 
14224     // Execute defragmentation rounds (the main part).
14225     VkResult result = VK_SUCCESS;
14226     for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
14227     {
14228         result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove, !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL));
14229     }
14230 
14231     return result;
14232 }
14233 
MoveMakesSense(size_t dstBlockIndex,VkDeviceSize dstOffset,size_t srcBlockIndex,VkDeviceSize srcOffset)14234 bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
14235         size_t dstBlockIndex, VkDeviceSize dstOffset,
14236         size_t srcBlockIndex, VkDeviceSize srcOffset)
14237 {
14238     if(dstBlockIndex < srcBlockIndex)
14239     {
14240         return true;
14241     }
14242     if(dstBlockIndex > srcBlockIndex)
14243     {
14244         return false;
14245     }
14246     if(dstOffset < srcOffset)
14247     {
14248         return true;
14249     }
14250     return false;
14251 }
14252 
14253 ////////////////////////////////////////////////////////////////////////////////
14254 // VmaDefragmentationAlgorithm_Fast
14255 
VmaDefragmentationAlgorithm_Fast(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)14256 VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
14257     VmaAllocator hAllocator,
14258     VmaBlockVector* pBlockVector,
14259     uint32_t currentFrameIndex,
14260     bool overlappingMoveSupported) :
14261     VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
14262     m_OverlappingMoveSupported(overlappingMoveSupported),
14263     m_AllocationCount(0),
14264     m_AllAllocations(false),
14265     m_BytesMoved(0),
14266     m_AllocationsMoved(0),
14267     m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
14268 {
14269     VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
14270 
14271 }
14272 
~VmaDefragmentationAlgorithm_Fast()14273 VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
14274 {
14275 }
14276 
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,VmaDefragmentationFlags flags)14277 VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
14278     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14279     VkDeviceSize maxBytesToMove,
14280     uint32_t maxAllocationsToMove,
14281     VmaDefragmentationFlags flags)
14282 {
14283     VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
14284 
14285     const size_t blockCount = m_pBlockVector->GetBlockCount();
14286     if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
14287     {
14288         return VK_SUCCESS;
14289     }
14290 
14291     PreprocessMetadata();
14292 
14293     // Sort blocks in order from most destination.
14294 
14295     m_BlockInfos.resize(blockCount);
14296     for(size_t i = 0; i < blockCount; ++i)
14297     {
14298         m_BlockInfos[i].origBlockIndex = i;
14299     }
14300 
14301     VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
14302         return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
14303             m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
14304     });
14305 
14306     // THE MAIN ALGORITHM
14307 
14308     FreeSpaceDatabase freeSpaceDb;
14309 
14310     size_t dstBlockInfoIndex = 0;
14311     size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14312     VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14313     VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14314     VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
14315     VkDeviceSize dstOffset = 0;
14316 
14317     bool end = false;
14318     for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
14319     {
14320         const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
14321         VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
14322         VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
14323         for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
14324             !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
14325         {
14326             VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
14327             const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
14328             const VkDeviceSize srcAllocSize = srcSuballocIt->size;
14329             if(m_AllocationsMoved == maxAllocationsToMove ||
14330                 m_BytesMoved + srcAllocSize > maxBytesToMove)
14331             {
14332                 end = true;
14333                 break;
14334             }
14335             const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
14336 
14337             VmaDefragmentationMove move = {};
14338             // Try to place it in one of free spaces from the database.
14339             size_t freeSpaceInfoIndex;
14340             VkDeviceSize dstAllocOffset;
14341             if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
14342                 freeSpaceInfoIndex, dstAllocOffset))
14343             {
14344                 size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
14345                 VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
14346                 VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
14347 
14348                 // Same block
14349                 if(freeSpaceInfoIndex == srcBlockInfoIndex)
14350                 {
14351                     VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14352 
14353                     // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14354 
14355                     VmaSuballocation suballoc = *srcSuballocIt;
14356                     suballoc.offset = dstAllocOffset;
14357                     suballoc.hAllocation->ChangeOffset(dstAllocOffset);
14358                     m_BytesMoved += srcAllocSize;
14359                     ++m_AllocationsMoved;
14360 
14361                     VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14362                     ++nextSuballocIt;
14363                     pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14364                     srcSuballocIt = nextSuballocIt;
14365 
14366                     InsertSuballoc(pFreeSpaceMetadata, suballoc);
14367 
14368                     move.srcBlockIndex = srcOrigBlockIndex;
14369                     move.dstBlockIndex = freeSpaceOrigBlockIndex;
14370                     move.srcOffset = srcAllocOffset;
14371                     move.dstOffset = dstAllocOffset;
14372                     move.size = srcAllocSize;
14373 
14374                     moves.push_back(move);
14375                 }
14376                 // Different block
14377                 else
14378                 {
14379                     // MOVE OPTION 2: Move the allocation to a different block.
14380 
14381                     VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
14382 
14383                     VmaSuballocation suballoc = *srcSuballocIt;
14384                     suballoc.offset = dstAllocOffset;
14385                     suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
14386                     m_BytesMoved += srcAllocSize;
14387                     ++m_AllocationsMoved;
14388 
14389                     VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14390                     ++nextSuballocIt;
14391                     pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14392                     srcSuballocIt = nextSuballocIt;
14393 
14394                     InsertSuballoc(pFreeSpaceMetadata, suballoc);
14395 
14396                     move.srcBlockIndex = srcOrigBlockIndex;
14397                     move.dstBlockIndex = freeSpaceOrigBlockIndex;
14398                     move.srcOffset = srcAllocOffset;
14399                     move.dstOffset = dstAllocOffset;
14400                     move.size = srcAllocSize;
14401 
14402                     moves.push_back(move);
14403                 }
14404             }
14405             else
14406             {
14407                 dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
14408 
14409                 // If the allocation doesn't fit before the end of dstBlock, forward to next block.
14410                 while(dstBlockInfoIndex < srcBlockInfoIndex &&
14411                     dstAllocOffset + srcAllocSize > dstBlockSize)
14412                 {
14413                     // But before that, register remaining free space at the end of dst block.
14414                     freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
14415 
14416                     ++dstBlockInfoIndex;
14417                     dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14418                     pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14419                     pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14420                     dstBlockSize = pDstMetadata->GetSize();
14421                     dstOffset = 0;
14422                     dstAllocOffset = 0;
14423                 }
14424 
14425                 // Same block
14426                 if(dstBlockInfoIndex == srcBlockInfoIndex)
14427                 {
14428                     VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14429 
14430                     const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
14431 
14432                     bool skipOver = overlap;
14433                     if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
14434                     {
14435                         // If destination and source place overlap, skip if it would move it
14436                         // by only < 1/64 of its size.
14437                         skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
14438                     }
14439 
14440                     if(skipOver)
14441                     {
14442                         freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
14443 
14444                         dstOffset = srcAllocOffset + srcAllocSize;
14445                         ++srcSuballocIt;
14446                     }
14447                     // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14448                     else
14449                     {
14450                         srcSuballocIt->offset = dstAllocOffset;
14451                         srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);
14452                         dstOffset = dstAllocOffset + srcAllocSize;
14453                         m_BytesMoved += srcAllocSize;
14454                         ++m_AllocationsMoved;
14455                         ++srcSuballocIt;
14456 
14457                         move.srcBlockIndex = srcOrigBlockIndex;
14458                         move.dstBlockIndex = dstOrigBlockIndex;
14459                         move.srcOffset = srcAllocOffset;
14460                         move.dstOffset = dstAllocOffset;
14461                         move.size = srcAllocSize;
14462 
14463                         moves.push_back(move);
14464                     }
14465                 }
14466                 // Different block
14467                 else
14468                 {
14469                     // MOVE OPTION 2: Move the allocation to a different block.
14470 
14471                     VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
14472                     VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
14473 
14474                     VmaSuballocation suballoc = *srcSuballocIt;
14475                     suballoc.offset = dstAllocOffset;
14476                     suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
14477                     dstOffset = dstAllocOffset + srcAllocSize;
14478                     m_BytesMoved += srcAllocSize;
14479                     ++m_AllocationsMoved;
14480 
14481                     VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14482                     ++nextSuballocIt;
14483                     pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14484                     srcSuballocIt = nextSuballocIt;
14485 
14486                     pDstMetadata->m_Suballocations.push_back(suballoc);
14487 
14488                     move.srcBlockIndex = srcOrigBlockIndex;
14489                     move.dstBlockIndex = dstOrigBlockIndex;
14490                     move.srcOffset = srcAllocOffset;
14491                     move.dstOffset = dstAllocOffset;
14492                     move.size = srcAllocSize;
14493 
14494                     moves.push_back(move);
14495                 }
14496             }
14497         }
14498     }
14499 
14500     m_BlockInfos.clear();
14501 
14502     PostprocessMetadata();
14503 
14504     return VK_SUCCESS;
14505 }
14506 
PreprocessMetadata()14507 void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
14508 {
14509     const size_t blockCount = m_pBlockVector->GetBlockCount();
14510     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14511     {
14512         VmaBlockMetadata_Generic* const pMetadata =
14513             (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14514         pMetadata->m_FreeCount = 0;
14515         pMetadata->m_SumFreeSize = pMetadata->GetSize();
14516         pMetadata->m_FreeSuballocationsBySize.clear();
14517         for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14518             it != pMetadata->m_Suballocations.end(); )
14519         {
14520             if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
14521             {
14522                 VmaSuballocationList::iterator nextIt = it;
14523                 ++nextIt;
14524                 pMetadata->m_Suballocations.erase(it);
14525                 it = nextIt;
14526             }
14527             else
14528             {
14529                 ++it;
14530             }
14531         }
14532     }
14533 }
14534 
PostprocessMetadata()14535 void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
14536 {
14537     const size_t blockCount = m_pBlockVector->GetBlockCount();
14538     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14539     {
14540         VmaBlockMetadata_Generic* const pMetadata =
14541             (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14542         const VkDeviceSize blockSize = pMetadata->GetSize();
14543 
14544         // No allocations in this block - entire area is free.
14545         if(pMetadata->m_Suballocations.empty())
14546         {
14547             pMetadata->m_FreeCount = 1;
14548             //pMetadata->m_SumFreeSize is already set to blockSize.
14549             VmaSuballocation suballoc = {
14550                 0, // offset
14551                 blockSize, // size
14552                 VMA_NULL, // hAllocation
14553                 VMA_SUBALLOCATION_TYPE_FREE };
14554             pMetadata->m_Suballocations.push_back(suballoc);
14555             pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
14556         }
14557         // There are some allocations in this block.
14558         else
14559         {
14560             VkDeviceSize offset = 0;
14561             VmaSuballocationList::iterator it;
14562             for(it = pMetadata->m_Suballocations.begin();
14563                 it != pMetadata->m_Suballocations.end();
14564                 ++it)
14565             {
14566                 VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
14567                 VMA_ASSERT(it->offset >= offset);
14568 
14569                 // Need to insert preceding free space.
14570                 if(it->offset > offset)
14571                 {
14572                     ++pMetadata->m_FreeCount;
14573                     const VkDeviceSize freeSize = it->offset - offset;
14574                     VmaSuballocation suballoc = {
14575                         offset, // offset
14576                         freeSize, // size
14577                         VMA_NULL, // hAllocation
14578                         VMA_SUBALLOCATION_TYPE_FREE };
14579                     VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14580                     if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14581                     {
14582                         pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
14583                     }
14584                 }
14585 
14586                 pMetadata->m_SumFreeSize -= it->size;
14587                 offset = it->offset + it->size;
14588             }
14589 
14590             // Need to insert trailing free space.
14591             if(offset < blockSize)
14592             {
14593                 ++pMetadata->m_FreeCount;
14594                 const VkDeviceSize freeSize = blockSize - offset;
14595                 VmaSuballocation suballoc = {
14596                     offset, // offset
14597                     freeSize, // size
14598                     VMA_NULL, // hAllocation
14599                     VMA_SUBALLOCATION_TYPE_FREE };
14600                 VMA_ASSERT(it == pMetadata->m_Suballocations.end());
14601                 VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14602                 if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14603                 {
14604                     pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
14605                 }
14606             }
14607 
14608             VMA_SORT(
14609                 pMetadata->m_FreeSuballocationsBySize.begin(),
14610                 pMetadata->m_FreeSuballocationsBySize.end(),
14611                 VmaSuballocationItemSizeLess());
14612         }
14613 
14614         VMA_HEAVY_ASSERT(pMetadata->Validate());
14615     }
14616 }
14617 
InsertSuballoc(VmaBlockMetadata_Generic * pMetadata,const VmaSuballocation & suballoc)14618 void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
14619 {
14620     // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
14621     VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14622     while(it != pMetadata->m_Suballocations.end())
14623     {
14624         if(it->offset < suballoc.offset)
14625         {
14626             ++it;
14627         }
14628     }
14629     pMetadata->m_Suballocations.insert(it, suballoc);
14630 }
14631 
14632 ////////////////////////////////////////////////////////////////////////////////
14633 // VmaBlockVectorDefragmentationContext
14634 
VmaBlockVectorDefragmentationContext(VmaAllocator hAllocator,VmaPool hCustomPool,VmaBlockVector * pBlockVector,uint32_t currFrameIndex)14635 VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
14636     VmaAllocator hAllocator,
14637     VmaPool hCustomPool,
14638     VmaBlockVector* pBlockVector,
14639     uint32_t currFrameIndex) :
14640     res(VK_SUCCESS),
14641     mutexLocked(false),
14642     blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
14643     defragmentationMoves(VmaStlAllocator<VmaDefragmentationMove>(hAllocator->GetAllocationCallbacks())),
14644     defragmentationMovesProcessed(0),
14645     defragmentationMovesCommitted(0),
14646     hasDefragmentationPlan(0),
14647     m_hAllocator(hAllocator),
14648     m_hCustomPool(hCustomPool),
14649     m_pBlockVector(pBlockVector),
14650     m_CurrFrameIndex(currFrameIndex),
14651     m_pAlgorithm(VMA_NULL),
14652     m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
14653     m_AllAllocations(false)
14654 {
14655 }
14656 
~VmaBlockVectorDefragmentationContext()14657 VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
14658 {
14659     vma_delete(m_hAllocator, m_pAlgorithm);
14660 }
14661 
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)14662 void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
14663 {
14664     AllocInfo info = { hAlloc, pChanged };
14665     m_Allocations.push_back(info);
14666 }
14667 
Begin(bool overlappingMoveSupported,VmaDefragmentationFlags flags)14668 void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags)
14669 {
14670     const bool allAllocations = m_AllAllocations ||
14671         m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
14672 
14673     /********************************
14674     HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
14675     ********************************/
14676 
14677     /*
14678     Fast algorithm is supported only when certain criteria are met:
14679     - VMA_DEBUG_MARGIN is 0.
14680     - All allocations in this block vector are moveable.
14681     - There is no possibility of image/buffer granularity conflict.
14682     - The defragmentation is not incremental
14683     */
14684     if(VMA_DEBUG_MARGIN == 0 &&
14685         allAllocations &&
14686         !m_pBlockVector->IsBufferImageGranularityConflictPossible() &&
14687         !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL))
14688     {
14689         m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
14690             m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14691     }
14692     else
14693     {
14694         m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
14695             m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14696     }
14697 
14698     if(allAllocations)
14699     {
14700         m_pAlgorithm->AddAll();
14701     }
14702     else
14703     {
14704         for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
14705         {
14706             m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
14707         }
14708     }
14709 }
14710 
14711 ////////////////////////////////////////////////////////////////////////////////
14712 // VmaDefragmentationContext
14713 
VmaDefragmentationContext_T(VmaAllocator hAllocator,uint32_t currFrameIndex,uint32_t flags,VmaDefragmentationStats * pStats)14714 VmaDefragmentationContext_T::VmaDefragmentationContext_T(
14715     VmaAllocator hAllocator,
14716     uint32_t currFrameIndex,
14717     uint32_t flags,
14718     VmaDefragmentationStats* pStats) :
14719     m_hAllocator(hAllocator),
14720     m_CurrFrameIndex(currFrameIndex),
14721     m_Flags(flags),
14722     m_pStats(pStats),
14723     m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
14724 {
14725     memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
14726 }
14727 
~VmaDefragmentationContext_T()14728 VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
14729 {
14730     for(size_t i = m_CustomPoolContexts.size(); i--; )
14731     {
14732         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
14733         pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
14734         vma_delete(m_hAllocator, pBlockVectorCtx);
14735     }
14736     for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
14737     {
14738         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
14739         if(pBlockVectorCtx)
14740         {
14741             pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
14742             vma_delete(m_hAllocator, pBlockVectorCtx);
14743         }
14744     }
14745 }
14746 
AddPools(uint32_t poolCount,const VmaPool * pPools)14747 void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, const VmaPool* pPools)
14748 {
14749     for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
14750     {
14751         VmaPool pool = pPools[poolIndex];
14752         VMA_ASSERT(pool);
14753         // Pools with algorithm other than default are not defragmented.
14754         if(pool->m_BlockVector.GetAlgorithm() == 0)
14755         {
14756             VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
14757 
14758             for(size_t i = m_CustomPoolContexts.size(); i--; )
14759             {
14760                 if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
14761                 {
14762                     pBlockVectorDefragCtx = m_CustomPoolContexts[i];
14763                     break;
14764                 }
14765             }
14766 
14767             if(!pBlockVectorDefragCtx)
14768             {
14769                 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14770                     m_hAllocator,
14771                     pool,
14772                     &pool->m_BlockVector,
14773                     m_CurrFrameIndex);
14774                 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
14775             }
14776 
14777             pBlockVectorDefragCtx->AddAll();
14778         }
14779     }
14780 }
14781 
AddAllocations(uint32_t allocationCount,const VmaAllocation * pAllocations,VkBool32 * pAllocationsChanged)14782 void VmaDefragmentationContext_T::AddAllocations(
14783     uint32_t allocationCount,
14784     const VmaAllocation* pAllocations,
14785     VkBool32* pAllocationsChanged)
14786 {
14787     // Dispatch pAllocations among defragmentators. Create them when necessary.
14788     for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14789     {
14790         const VmaAllocation hAlloc = pAllocations[allocIndex];
14791         VMA_ASSERT(hAlloc);
14792         // DedicatedAlloc cannot be defragmented.
14793         if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
14794             // Lost allocation cannot be defragmented.
14795             (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
14796         {
14797             VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
14798 
14799             const VmaPool hAllocPool = hAlloc->GetBlock()->GetParentPool();
14800             // This allocation belongs to custom pool.
14801             if(hAllocPool != VK_NULL_HANDLE)
14802             {
14803                 // Pools with algorithm other than default are not defragmented.
14804                 if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
14805                 {
14806                     for(size_t i = m_CustomPoolContexts.size(); i--; )
14807                     {
14808                         if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
14809                         {
14810                             pBlockVectorDefragCtx = m_CustomPoolContexts[i];
14811                             break;
14812                         }
14813                     }
14814                     if(!pBlockVectorDefragCtx)
14815                     {
14816                         pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14817                             m_hAllocator,
14818                             hAllocPool,
14819                             &hAllocPool->m_BlockVector,
14820                             m_CurrFrameIndex);
14821                         m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
14822                     }
14823                 }
14824             }
14825             // This allocation belongs to default pool.
14826             else
14827             {
14828                 const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
14829                 pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
14830                 if(!pBlockVectorDefragCtx)
14831                 {
14832                     pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14833                         m_hAllocator,
14834                         VMA_NULL, // hCustomPool
14835                         m_hAllocator->m_pBlockVectors[memTypeIndex],
14836                         m_CurrFrameIndex);
14837                     m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
14838                 }
14839             }
14840 
14841             if(pBlockVectorDefragCtx)
14842             {
14843                 VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
14844                     &pAllocationsChanged[allocIndex] : VMA_NULL;
14845                 pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
14846             }
14847         }
14848     }
14849 }
14850 
Defragment(VkDeviceSize maxCpuBytesToMove,uint32_t maxCpuAllocationsToMove,VkDeviceSize maxGpuBytesToMove,uint32_t maxGpuAllocationsToMove,VkCommandBuffer commandBuffer,VmaDefragmentationStats * pStats,VmaDefragmentationFlags flags)14851 VkResult VmaDefragmentationContext_T::Defragment(
14852     VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
14853     VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
14854     VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags)
14855 {
14856     if(pStats)
14857     {
14858         memset(pStats, 0, sizeof(VmaDefragmentationStats));
14859     }
14860 
14861     if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
14862     {
14863         // For incremental defragmetnations, we just earmark how much we can move
14864         // The real meat is in the defragmentation steps
14865         m_MaxCpuBytesToMove = maxCpuBytesToMove;
14866         m_MaxCpuAllocationsToMove = maxCpuAllocationsToMove;
14867 
14868         m_MaxGpuBytesToMove = maxGpuBytesToMove;
14869         m_MaxGpuAllocationsToMove = maxGpuAllocationsToMove;
14870 
14871         if(m_MaxCpuBytesToMove == 0 && m_MaxCpuAllocationsToMove == 0 &&
14872             m_MaxGpuBytesToMove == 0 && m_MaxGpuAllocationsToMove == 0)
14873             return VK_SUCCESS;
14874 
14875         return VK_NOT_READY;
14876     }
14877 
14878     if(commandBuffer == VK_NULL_HANDLE)
14879     {
14880         maxGpuBytesToMove = 0;
14881         maxGpuAllocationsToMove = 0;
14882     }
14883 
14884     VkResult res = VK_SUCCESS;
14885 
14886     // Process default pools.
14887     for(uint32_t memTypeIndex = 0;
14888         memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
14889         ++memTypeIndex)
14890     {
14891         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14892         if(pBlockVectorCtx)
14893         {
14894             VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14895             pBlockVectorCtx->GetBlockVector()->Defragment(
14896                 pBlockVectorCtx,
14897                 pStats, flags,
14898                 maxCpuBytesToMove, maxCpuAllocationsToMove,
14899                 maxGpuBytesToMove, maxGpuAllocationsToMove,
14900                 commandBuffer);
14901             if(pBlockVectorCtx->res != VK_SUCCESS)
14902             {
14903                 res = pBlockVectorCtx->res;
14904             }
14905         }
14906     }
14907 
14908     // Process custom pools.
14909     for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14910         customCtxIndex < customCtxCount && res >= VK_SUCCESS;
14911         ++customCtxIndex)
14912     {
14913         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14914         VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14915         pBlockVectorCtx->GetBlockVector()->Defragment(
14916             pBlockVectorCtx,
14917             pStats, flags,
14918             maxCpuBytesToMove, maxCpuAllocationsToMove,
14919             maxGpuBytesToMove, maxGpuAllocationsToMove,
14920             commandBuffer);
14921         if(pBlockVectorCtx->res != VK_SUCCESS)
14922         {
14923             res = pBlockVectorCtx->res;
14924         }
14925     }
14926 
14927     return res;
14928 }
14929 
DefragmentPassBegin(VmaDefragmentationPassInfo * pInfo)14930 VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo)
14931 {
14932     VmaDefragmentationPassMoveInfo* pCurrentMove = pInfo->pMoves;
14933     uint32_t movesLeft = pInfo->moveCount;
14934 
14935     // Process default pools.
14936     for(uint32_t memTypeIndex = 0;
14937         memTypeIndex < m_hAllocator->GetMemoryTypeCount();
14938         ++memTypeIndex)
14939     {
14940         VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14941         if(pBlockVectorCtx)
14942         {
14943             VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14944 
14945             if(!pBlockVectorCtx->hasDefragmentationPlan)
14946             {
14947                 pBlockVectorCtx->GetBlockVector()->Defragment(
14948                     pBlockVectorCtx,
14949                     m_pStats, m_Flags,
14950                     m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
14951                     m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
14952                     VK_NULL_HANDLE);
14953 
14954                 if(pBlockVectorCtx->res < VK_SUCCESS)
14955                     continue;
14956 
14957                 pBlockVectorCtx->hasDefragmentationPlan = true;
14958             }
14959 
14960             const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
14961                 pBlockVectorCtx,
14962                 pCurrentMove, movesLeft);
14963 
14964             movesLeft -= processed;
14965             pCurrentMove += processed;
14966         }
14967     }
14968 
14969     // Process custom pools.
14970     for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14971         customCtxIndex < customCtxCount;
14972         ++customCtxIndex)
14973     {
14974         VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14975         VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14976 
14977         if(!pBlockVectorCtx->hasDefragmentationPlan)
14978         {
14979             pBlockVectorCtx->GetBlockVector()->Defragment(
14980                 pBlockVectorCtx,
14981                 m_pStats, m_Flags,
14982                 m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
14983                 m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
14984                 VK_NULL_HANDLE);
14985 
14986             if(pBlockVectorCtx->res < VK_SUCCESS)
14987                 continue;
14988 
14989             pBlockVectorCtx->hasDefragmentationPlan = true;
14990         }
14991 
14992         const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
14993             pBlockVectorCtx,
14994             pCurrentMove, movesLeft);
14995 
14996         movesLeft -= processed;
14997         pCurrentMove += processed;
14998     }
14999 
15000     pInfo->moveCount = pInfo->moveCount - movesLeft;
15001 
15002     return VK_SUCCESS;
15003 }
DefragmentPassEnd()15004 VkResult VmaDefragmentationContext_T::DefragmentPassEnd()
15005 {
15006     VkResult res = VK_SUCCESS;
15007 
15008     // Process default pools.
15009     for(uint32_t memTypeIndex = 0;
15010         memTypeIndex < m_hAllocator->GetMemoryTypeCount();
15011         ++memTypeIndex)
15012     {
15013         VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
15014         if(pBlockVectorCtx)
15015         {
15016             VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
15017 
15018             if(!pBlockVectorCtx->hasDefragmentationPlan)
15019             {
15020                 res = VK_NOT_READY;
15021                 continue;
15022             }
15023 
15024             pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
15025                 pBlockVectorCtx, m_pStats);
15026 
15027             if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
15028                 res = VK_NOT_READY;
15029         }
15030     }
15031 
15032     // Process custom pools.
15033     for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
15034         customCtxIndex < customCtxCount;
15035         ++customCtxIndex)
15036     {
15037         VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
15038         VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
15039 
15040         if(!pBlockVectorCtx->hasDefragmentationPlan)
15041         {
15042             res = VK_NOT_READY;
15043             continue;
15044         }
15045 
15046         pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
15047             pBlockVectorCtx, m_pStats);
15048 
15049         if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
15050             res = VK_NOT_READY;
15051     }
15052 
15053     return res;
15054 }
15055 
15056 ////////////////////////////////////////////////////////////////////////////////
15057 // VmaRecorder
15058 
15059 #if VMA_RECORDING_ENABLED
15060 
VmaRecorder()15061 VmaRecorder::VmaRecorder() :
15062     m_UseMutex(true),
15063     m_Flags(0),
15064     m_File(VMA_NULL),
15065     m_RecordingStartTime(std::chrono::high_resolution_clock::now())
15066 {
15067 }
15068 
Init(const VmaRecordSettings & settings,bool useMutex)15069 VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
15070 {
15071     m_UseMutex = useMutex;
15072     m_Flags = settings.flags;
15073 
15074 #if defined(_WIN32)
15075     // Open file for writing.
15076     errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
15077 
15078     if(err != 0)
15079     {
15080         return VK_ERROR_INITIALIZATION_FAILED;
15081     }
15082 #else
15083     // Open file for writing.
15084     m_File = fopen(settings.pFilePath, "wb");
15085 
15086     if(m_File == 0)
15087     {
15088         return VK_ERROR_INITIALIZATION_FAILED;
15089     }
15090 #endif
15091 
15092     // Write header.
15093     fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
15094     fprintf(m_File, "%s\n", "1,8");
15095 
15096     return VK_SUCCESS;
15097 }
15098 
~VmaRecorder()15099 VmaRecorder::~VmaRecorder()
15100 {
15101     if(m_File != VMA_NULL)
15102     {
15103         fclose(m_File);
15104     }
15105 }
15106 
RecordCreateAllocator(uint32_t frameIndex)15107 void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
15108 {
15109     CallParams callParams;
15110     GetBasicParams(callParams);
15111 
15112     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15113     fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
15114     Flush();
15115 }
15116 
RecordDestroyAllocator(uint32_t frameIndex)15117 void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
15118 {
15119     CallParams callParams;
15120     GetBasicParams(callParams);
15121 
15122     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15123     fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
15124     Flush();
15125 }
15126 
RecordCreatePool(uint32_t frameIndex,const VmaPoolCreateInfo & createInfo,VmaPool pool)15127 void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
15128 {
15129     CallParams callParams;
15130     GetBasicParams(callParams);
15131 
15132     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15133     fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
15134         createInfo.memoryTypeIndex,
15135         createInfo.flags,
15136         createInfo.blockSize,
15137         (uint64_t)createInfo.minBlockCount,
15138         (uint64_t)createInfo.maxBlockCount,
15139         createInfo.frameInUseCount,
15140         pool);
15141     Flush();
15142 }
15143 
RecordDestroyPool(uint32_t frameIndex,VmaPool pool)15144 void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
15145 {
15146     CallParams callParams;
15147     GetBasicParams(callParams);
15148 
15149     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15150     fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
15151         pool);
15152     Flush();
15153 }
15154 
RecordAllocateMemory(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)15155 void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
15156         const VkMemoryRequirements& vkMemReq,
15157         const VmaAllocationCreateInfo& createInfo,
15158         VmaAllocation allocation)
15159 {
15160     CallParams callParams;
15161     GetBasicParams(callParams);
15162 
15163     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15164     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15165     fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15166         vkMemReq.size,
15167         vkMemReq.alignment,
15168         vkMemReq.memoryTypeBits,
15169         createInfo.flags,
15170         createInfo.usage,
15171         createInfo.requiredFlags,
15172         createInfo.preferredFlags,
15173         createInfo.memoryTypeBits,
15174         createInfo.pool,
15175         allocation,
15176         userDataStr.GetString());
15177     Flush();
15178 }
15179 
RecordAllocateMemoryPages(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,uint64_t allocationCount,const VmaAllocation * pAllocations)15180 void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
15181     const VkMemoryRequirements& vkMemReq,
15182     const VmaAllocationCreateInfo& createInfo,
15183     uint64_t allocationCount,
15184     const VmaAllocation* pAllocations)
15185 {
15186     CallParams callParams;
15187     GetBasicParams(callParams);
15188 
15189     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15190     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15191     fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
15192         vkMemReq.size,
15193         vkMemReq.alignment,
15194         vkMemReq.memoryTypeBits,
15195         createInfo.flags,
15196         createInfo.usage,
15197         createInfo.requiredFlags,
15198         createInfo.preferredFlags,
15199         createInfo.memoryTypeBits,
15200         createInfo.pool);
15201     PrintPointerList(allocationCount, pAllocations);
15202     fprintf(m_File, ",%s\n", userDataStr.GetString());
15203     Flush();
15204 }
15205 
RecordAllocateMemoryForBuffer(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)15206 void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
15207     const VkMemoryRequirements& vkMemReq,
15208     bool requiresDedicatedAllocation,
15209     bool prefersDedicatedAllocation,
15210     const VmaAllocationCreateInfo& createInfo,
15211     VmaAllocation allocation)
15212 {
15213     CallParams callParams;
15214     GetBasicParams(callParams);
15215 
15216     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15217     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15218     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,
15219         vkMemReq.size,
15220         vkMemReq.alignment,
15221         vkMemReq.memoryTypeBits,
15222         requiresDedicatedAllocation ? 1 : 0,
15223         prefersDedicatedAllocation ? 1 : 0,
15224         createInfo.flags,
15225         createInfo.usage,
15226         createInfo.requiredFlags,
15227         createInfo.preferredFlags,
15228         createInfo.memoryTypeBits,
15229         createInfo.pool,
15230         allocation,
15231         userDataStr.GetString());
15232     Flush();
15233 }
15234 
RecordAllocateMemoryForImage(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)15235 void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
15236     const VkMemoryRequirements& vkMemReq,
15237     bool requiresDedicatedAllocation,
15238     bool prefersDedicatedAllocation,
15239     const VmaAllocationCreateInfo& createInfo,
15240     VmaAllocation allocation)
15241 {
15242     CallParams callParams;
15243     GetBasicParams(callParams);
15244 
15245     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15246     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15247     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,
15248         vkMemReq.size,
15249         vkMemReq.alignment,
15250         vkMemReq.memoryTypeBits,
15251         requiresDedicatedAllocation ? 1 : 0,
15252         prefersDedicatedAllocation ? 1 : 0,
15253         createInfo.flags,
15254         createInfo.usage,
15255         createInfo.requiredFlags,
15256         createInfo.preferredFlags,
15257         createInfo.memoryTypeBits,
15258         createInfo.pool,
15259         allocation,
15260         userDataStr.GetString());
15261     Flush();
15262 }
15263 
RecordFreeMemory(uint32_t frameIndex,VmaAllocation allocation)15264 void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
15265     VmaAllocation allocation)
15266 {
15267     CallParams callParams;
15268     GetBasicParams(callParams);
15269 
15270     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15271     fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15272         allocation);
15273     Flush();
15274 }
15275 
RecordFreeMemoryPages(uint32_t frameIndex,uint64_t allocationCount,const VmaAllocation * pAllocations)15276 void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
15277     uint64_t allocationCount,
15278     const VmaAllocation* pAllocations)
15279 {
15280     CallParams callParams;
15281     GetBasicParams(callParams);
15282 
15283     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15284     fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
15285     PrintPointerList(allocationCount, pAllocations);
15286     fprintf(m_File, "\n");
15287     Flush();
15288 }
15289 
RecordSetAllocationUserData(uint32_t frameIndex,VmaAllocation allocation,const void * pUserData)15290 void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
15291     VmaAllocation allocation,
15292     const void* pUserData)
15293 {
15294     CallParams callParams;
15295     GetBasicParams(callParams);
15296 
15297     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15298     UserDataString userDataStr(
15299         allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
15300         pUserData);
15301     fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15302         allocation,
15303         userDataStr.GetString());
15304     Flush();
15305 }
15306 
RecordCreateLostAllocation(uint32_t frameIndex,VmaAllocation allocation)15307 void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
15308     VmaAllocation allocation)
15309 {
15310     CallParams callParams;
15311     GetBasicParams(callParams);
15312 
15313     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15314     fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15315         allocation);
15316     Flush();
15317 }
15318 
RecordMapMemory(uint32_t frameIndex,VmaAllocation allocation)15319 void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
15320     VmaAllocation allocation)
15321 {
15322     CallParams callParams;
15323     GetBasicParams(callParams);
15324 
15325     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15326     fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15327         allocation);
15328     Flush();
15329 }
15330 
RecordUnmapMemory(uint32_t frameIndex,VmaAllocation allocation)15331 void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
15332     VmaAllocation allocation)
15333 {
15334     CallParams callParams;
15335     GetBasicParams(callParams);
15336 
15337     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15338     fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15339         allocation);
15340     Flush();
15341 }
15342 
RecordFlushAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)15343 void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
15344     VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15345 {
15346     CallParams callParams;
15347     GetBasicParams(callParams);
15348 
15349     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15350     fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15351         allocation,
15352         offset,
15353         size);
15354     Flush();
15355 }
15356 
RecordInvalidateAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)15357 void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
15358     VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15359 {
15360     CallParams callParams;
15361     GetBasicParams(callParams);
15362 
15363     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15364     fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15365         allocation,
15366         offset,
15367         size);
15368     Flush();
15369 }
15370 
RecordCreateBuffer(uint32_t frameIndex,const VkBufferCreateInfo & bufCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)15371 void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
15372     const VkBufferCreateInfo& bufCreateInfo,
15373     const VmaAllocationCreateInfo& allocCreateInfo,
15374     VmaAllocation allocation)
15375 {
15376     CallParams callParams;
15377     GetBasicParams(callParams);
15378 
15379     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15380     UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15381     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,
15382         bufCreateInfo.flags,
15383         bufCreateInfo.size,
15384         bufCreateInfo.usage,
15385         bufCreateInfo.sharingMode,
15386         allocCreateInfo.flags,
15387         allocCreateInfo.usage,
15388         allocCreateInfo.requiredFlags,
15389         allocCreateInfo.preferredFlags,
15390         allocCreateInfo.memoryTypeBits,
15391         allocCreateInfo.pool,
15392         allocation,
15393         userDataStr.GetString());
15394     Flush();
15395 }
15396 
RecordCreateImage(uint32_t frameIndex,const VkImageCreateInfo & imageCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)15397 void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
15398     const VkImageCreateInfo& imageCreateInfo,
15399     const VmaAllocationCreateInfo& allocCreateInfo,
15400     VmaAllocation allocation)
15401 {
15402     CallParams callParams;
15403     GetBasicParams(callParams);
15404 
15405     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15406     UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15407     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,
15408         imageCreateInfo.flags,
15409         imageCreateInfo.imageType,
15410         imageCreateInfo.format,
15411         imageCreateInfo.extent.width,
15412         imageCreateInfo.extent.height,
15413         imageCreateInfo.extent.depth,
15414         imageCreateInfo.mipLevels,
15415         imageCreateInfo.arrayLayers,
15416         imageCreateInfo.samples,
15417         imageCreateInfo.tiling,
15418         imageCreateInfo.usage,
15419         imageCreateInfo.sharingMode,
15420         imageCreateInfo.initialLayout,
15421         allocCreateInfo.flags,
15422         allocCreateInfo.usage,
15423         allocCreateInfo.requiredFlags,
15424         allocCreateInfo.preferredFlags,
15425         allocCreateInfo.memoryTypeBits,
15426         allocCreateInfo.pool,
15427         allocation,
15428         userDataStr.GetString());
15429     Flush();
15430 }
15431 
RecordDestroyBuffer(uint32_t frameIndex,VmaAllocation allocation)15432 void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
15433     VmaAllocation allocation)
15434 {
15435     CallParams callParams;
15436     GetBasicParams(callParams);
15437 
15438     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15439     fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
15440         allocation);
15441     Flush();
15442 }
15443 
RecordDestroyImage(uint32_t frameIndex,VmaAllocation allocation)15444 void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
15445     VmaAllocation allocation)
15446 {
15447     CallParams callParams;
15448     GetBasicParams(callParams);
15449 
15450     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15451     fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
15452         allocation);
15453     Flush();
15454 }
15455 
RecordTouchAllocation(uint32_t frameIndex,VmaAllocation allocation)15456 void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
15457     VmaAllocation allocation)
15458 {
15459     CallParams callParams;
15460     GetBasicParams(callParams);
15461 
15462     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15463     fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15464         allocation);
15465     Flush();
15466 }
15467 
RecordGetAllocationInfo(uint32_t frameIndex,VmaAllocation allocation)15468 void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
15469     VmaAllocation allocation)
15470 {
15471     CallParams callParams;
15472     GetBasicParams(callParams);
15473 
15474     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15475     fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
15476         allocation);
15477     Flush();
15478 }
15479 
RecordMakePoolAllocationsLost(uint32_t frameIndex,VmaPool pool)15480 void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
15481     VmaPool pool)
15482 {
15483     CallParams callParams;
15484     GetBasicParams(callParams);
15485 
15486     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15487     fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
15488         pool);
15489     Flush();
15490 }
15491 
RecordDefragmentationBegin(uint32_t frameIndex,const VmaDefragmentationInfo2 & info,VmaDefragmentationContext ctx)15492 void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
15493     const VmaDefragmentationInfo2& info,
15494     VmaDefragmentationContext ctx)
15495 {
15496     CallParams callParams;
15497     GetBasicParams(callParams);
15498 
15499     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15500     fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
15501         info.flags);
15502     PrintPointerList(info.allocationCount, info.pAllocations);
15503     fprintf(m_File, ",");
15504     PrintPointerList(info.poolCount, info.pPools);
15505     fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
15506         info.maxCpuBytesToMove,
15507         info.maxCpuAllocationsToMove,
15508         info.maxGpuBytesToMove,
15509         info.maxGpuAllocationsToMove,
15510         info.commandBuffer,
15511         ctx);
15512     Flush();
15513 }
15514 
RecordDefragmentationEnd(uint32_t frameIndex,VmaDefragmentationContext ctx)15515 void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
15516     VmaDefragmentationContext ctx)
15517 {
15518     CallParams callParams;
15519     GetBasicParams(callParams);
15520 
15521     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15522     fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
15523         ctx);
15524     Flush();
15525 }
15526 
RecordSetPoolName(uint32_t frameIndex,VmaPool pool,const char * name)15527 void VmaRecorder::RecordSetPoolName(uint32_t frameIndex,
15528     VmaPool pool,
15529     const char* name)
15530 {
15531     CallParams callParams;
15532     GetBasicParams(callParams);
15533 
15534     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15535     fprintf(m_File, "%u,%.3f,%u,vmaSetPoolName,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15536         pool, name != VMA_NULL ? name : "");
15537     Flush();
15538 }
15539 
UserDataString(VmaAllocationCreateFlags allocFlags,const void * pUserData)15540 VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
15541 {
15542     if(pUserData != VMA_NULL)
15543     {
15544         if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
15545         {
15546             m_Str = (const char*)pUserData;
15547         }
15548         else
15549         {
15550             // If VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is not specified, convert the string's memory address to a string and store it.
15551             snprintf(m_PtrStr, 17, "%p", pUserData);
15552             m_Str = m_PtrStr;
15553         }
15554     }
15555     else
15556     {
15557         m_Str = "";
15558     }
15559 }
15560 
WriteConfiguration(const VkPhysicalDeviceProperties & devProps,const VkPhysicalDeviceMemoryProperties & memProps,uint32_t vulkanApiVersion,bool dedicatedAllocationExtensionEnabled,bool bindMemory2ExtensionEnabled,bool memoryBudgetExtensionEnabled,bool deviceCoherentMemoryExtensionEnabled)15561 void VmaRecorder::WriteConfiguration(
15562     const VkPhysicalDeviceProperties& devProps,
15563     const VkPhysicalDeviceMemoryProperties& memProps,
15564     uint32_t vulkanApiVersion,
15565     bool dedicatedAllocationExtensionEnabled,
15566     bool bindMemory2ExtensionEnabled,
15567     bool memoryBudgetExtensionEnabled,
15568     bool deviceCoherentMemoryExtensionEnabled)
15569 {
15570     fprintf(m_File, "Config,Begin\n");
15571 
15572     fprintf(m_File, "VulkanApiVersion,%u,%u\n", VK_VERSION_MAJOR(vulkanApiVersion), VK_VERSION_MINOR(vulkanApiVersion));
15573 
15574     fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
15575     fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
15576     fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
15577     fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
15578     fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
15579     fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
15580 
15581     fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
15582     fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
15583     fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
15584 
15585     fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
15586     for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
15587     {
15588         fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
15589         fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
15590     }
15591     fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
15592     for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
15593     {
15594         fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
15595         fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
15596     }
15597 
15598     fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
15599     fprintf(m_File, "Extension,VK_KHR_bind_memory2,%u\n", bindMemory2ExtensionEnabled ? 1 : 0);
15600     fprintf(m_File, "Extension,VK_EXT_memory_budget,%u\n", memoryBudgetExtensionEnabled ? 1 : 0);
15601     fprintf(m_File, "Extension,VK_AMD_device_coherent_memory,%u\n", deviceCoherentMemoryExtensionEnabled ? 1 : 0);
15602 
15603     fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
15604     fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);
15605     fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
15606     fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
15607     fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
15608     fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
15609     fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
15610     fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
15611     fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15612 
15613     fprintf(m_File, "Config,End\n");
15614 }
15615 
GetBasicParams(CallParams & outParams)15616 void VmaRecorder::GetBasicParams(CallParams& outParams)
15617 {
15618     #if defined(_WIN32)
15619         outParams.threadId = GetCurrentThreadId();
15620     #else
15621         // Use C++11 features to get thread id and convert it to uint32_t.
15622         // There is room for optimization since sstream is quite slow.
15623         // Is there a better way to convert std::this_thread::get_id() to uint32_t?
15624         std::thread::id thread_id = std::this_thread::get_id();
15625         stringstream thread_id_to_string_converter;
15626         thread_id_to_string_converter << thread_id;
15627         string thread_id_as_string = thread_id_to_string_converter.str();
15628         outParams.threadId = static_cast<uint32_t>(std::stoi(thread_id_as_string.c_str()));
15629     #endif
15630 
15631     auto current_time = std::chrono::high_resolution_clock::now();
15632 
15633     outParams.time = std::chrono::duration<double, std::chrono::seconds::period>(current_time - m_RecordingStartTime).count();
15634 }
15635 
PrintPointerList(uint64_t count,const VmaAllocation * pItems)15636 void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
15637 {
15638     if(count)
15639     {
15640         fprintf(m_File, "%p", pItems[0]);
15641         for(uint64_t i = 1; i < count; ++i)
15642         {
15643             fprintf(m_File, " %p", pItems[i]);
15644         }
15645     }
15646 }
15647 
Flush()15648 void VmaRecorder::Flush()
15649 {
15650     if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
15651     {
15652         fflush(m_File);
15653     }
15654 }
15655 
15656 #endif // #if VMA_RECORDING_ENABLED
15657 
15658 ////////////////////////////////////////////////////////////////////////////////
15659 // VmaAllocationObjectAllocator
15660 
VmaAllocationObjectAllocator(const VkAllocationCallbacks * pAllocationCallbacks)15661 VmaAllocationObjectAllocator::VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks) :
15662     m_Allocator(pAllocationCallbacks, 1024)
15663 {
15664 }
15665 
Allocate(Types...args)15666 template<typename... Types> VmaAllocation VmaAllocationObjectAllocator::Allocate(Types... args)
15667 {
15668     VmaMutexLock mutexLock(m_Mutex);
15669     return m_Allocator.Alloc<Types...>(std::forward<Types>(args)...);
15670 }
15671 
Free(VmaAllocation hAlloc)15672 void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc)
15673 {
15674     VmaMutexLock mutexLock(m_Mutex);
15675     m_Allocator.Free(hAlloc);
15676 }
15677 
15678 ////////////////////////////////////////////////////////////////////////////////
15679 // VmaAllocator_T
15680 
VmaAllocator_T(const VmaAllocatorCreateInfo * pCreateInfo)15681 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
15682     m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
15683     m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0),
15684     m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
15685     m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0),
15686     m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0),
15687     m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0),
15688     m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0),
15689     m_hDevice(pCreateInfo->device),
15690     m_hInstance(pCreateInfo->instance),
15691     m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
15692     m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
15693         *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
15694     m_AllocationObjectAllocator(&m_AllocationCallbacks),
15695     m_HeapSizeLimitMask(0),
15696     m_PreferredLargeHeapBlockSize(0),
15697     m_PhysicalDevice(pCreateInfo->physicalDevice),
15698     m_CurrentFrameIndex(0),
15699     m_GpuDefragmentationMemoryTypeBits(UINT32_MAX),
15700     m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks())),
15701     m_NextPoolId(0),
15702     m_GlobalMemoryTypeBits(UINT32_MAX)
15703 #if VMA_RECORDING_ENABLED
15704     ,m_pRecorder(VMA_NULL)
15705 #endif
15706 {
15707     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15708     {
15709         m_UseKhrDedicatedAllocation = false;
15710         m_UseKhrBindMemory2 = false;
15711     }
15712 
15713     if(VMA_DEBUG_DETECT_CORRUPTION)
15714     {
15715         // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
15716         VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
15717     }
15718 
15719     VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance);
15720 
15721     if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
15722     {
15723 #if !(VMA_DEDICATED_ALLOCATION)
15724         if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
15725         {
15726             VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
15727         }
15728 #endif
15729 #if !(VMA_BIND_MEMORY2)
15730         if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0)
15731         {
15732             VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros.");
15733         }
15734 #endif
15735     }
15736 #if !(VMA_MEMORY_BUDGET)
15737     if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0)
15738     {
15739         VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros.");
15740     }
15741 #endif
15742 #if !(VMA_BUFFER_DEVICE_ADDRESS)
15743     if(m_UseKhrBufferDeviceAddress)
15744     {
15745         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.");
15746     }
15747 #endif
15748 #if VMA_VULKAN_VERSION < 1002000
15749     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0))
15750     {
15751         VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros.");
15752     }
15753 #endif
15754 #if VMA_VULKAN_VERSION < 1001000
15755     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15756     {
15757         VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros.");
15758     }
15759 #endif
15760 
15761     memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
15762     memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
15763     memset(&m_MemProps, 0, sizeof(m_MemProps));
15764 
15765     memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
15766     memset(&m_pReservedBlockVectors, 0, sizeof(m_pReservedBlockVectors));
15767     memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations));
15768     memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions));
15769 
15770     if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
15771     {
15772         m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData;
15773         m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
15774         m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
15775     }
15776 
15777     ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
15778 
15779     (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
15780     (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
15781 
15782     VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT));
15783     VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
15784     VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
15785     VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
15786 
15787     m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
15788         pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15789 
15790     m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits();
15791 
15792     if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
15793     {
15794         for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
15795         {
15796             const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
15797             if(limit != VK_WHOLE_SIZE)
15798             {
15799                 m_HeapSizeLimitMask |= 1u << heapIndex;
15800                 if(limit < m_MemProps.memoryHeaps[heapIndex].size)
15801                 {
15802                     m_MemProps.memoryHeaps[heapIndex].size = limit;
15803                 }
15804             }
15805         }
15806     }
15807 
15808     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15809     {
15810         const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
15811 
15812         m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
15813             this,
15814             VK_NULL_HANDLE, // hParentPool
15815             memTypeIndex,
15816             preferredBlockSize,
15817             0,
15818             pCreateInfo->maxBlockCount,
15819             GetBufferImageGranularity(),
15820             pCreateInfo->frameInUseCount,
15821             false, // explicitBlockSize
15822             false); // linearAlgorithm
15823         m_pReservedBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
15824             this,
15825             VK_NULL_HANDLE, // hParentPool
15826             memTypeIndex,
15827             preferredBlockSize,
15828             0,
15829             1, // max block count 1
15830             GetBufferImageGranularity(),
15831             pCreateInfo->frameInUseCount,
15832             false, // explicitBlockSize
15833             false); // linearAlgorithm
15834         // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
15835         // becase minBlockCount is 0.
15836         m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
15837 
15838     }
15839 }
15840 
Init(const VmaAllocatorCreateInfo * pCreateInfo)15841 VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
15842 {
15843     VkResult res = VK_SUCCESS;
15844 
15845     if(pCreateInfo->pRecordSettings != VMA_NULL &&
15846         !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
15847     {
15848 #if VMA_RECORDING_ENABLED
15849         m_pRecorder = vma_new(this, VmaRecorder)();
15850         res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
15851         if(res != VK_SUCCESS)
15852         {
15853             return res;
15854         }
15855         m_pRecorder->WriteConfiguration(
15856             m_PhysicalDeviceProperties,
15857             m_MemProps,
15858             m_VulkanApiVersion,
15859             m_UseKhrDedicatedAllocation,
15860             m_UseKhrBindMemory2,
15861             m_UseExtMemoryBudget,
15862             m_UseAmdDeviceCoherentMemory);
15863         m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
15864 #else
15865         VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
15866         return VK_ERROR_FEATURE_NOT_PRESENT;
15867 #endif
15868     }
15869 
15870 #if VMA_MEMORY_BUDGET
15871     if(m_UseExtMemoryBudget)
15872     {
15873         UpdateVulkanBudget();
15874     }
15875 #endif // #if VMA_MEMORY_BUDGET
15876 
15877     return res;
15878 }
15879 
~VmaAllocator_T()15880 VmaAllocator_T::~VmaAllocator_T()
15881 {
15882 #if VMA_RECORDING_ENABLED
15883     if(m_pRecorder != VMA_NULL)
15884     {
15885         m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
15886         vma_delete(this, m_pRecorder);
15887     }
15888 #endif
15889 
15890     VMA_ASSERT(m_Pools.empty());
15891 
15892     for(size_t i = GetMemoryTypeCount(); i--; )
15893     {
15894         if(m_pDedicatedAllocations[i] != VMA_NULL && !m_pDedicatedAllocations[i]->empty())
15895         {
15896             VMA_ASSERT(0 && "Unfreed dedicated allocations found.");
15897         }
15898 
15899         vma_delete(this, m_pDedicatedAllocations[i]);
15900         vma_delete(this, m_pBlockVectors[i]);
15901         vma_delete(this, m_pReservedBlockVectors[i]);
15902     }
15903 }
15904 
ImportVulkanFunctions(const VmaVulkanFunctions * pVulkanFunctions)15905 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
15906 {
15907 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15908     ImportVulkanFunctions_Static();
15909 #endif
15910 
15911     if(pVulkanFunctions != VMA_NULL)
15912     {
15913         ImportVulkanFunctions_Custom(pVulkanFunctions);
15914     }
15915 
15916 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
15917     ImportVulkanFunctions_Dynamic();
15918 #endif
15919 
15920     ValidateVulkanFunctions();
15921 }
15922 
15923 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15924 
ImportVulkanFunctions_Static()15925 void VmaAllocator_T::ImportVulkanFunctions_Static()
15926 {
15927     // Vulkan 1.0
15928     m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties;
15929     m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties;
15930     m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
15931     m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory;
15932     m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory;
15933     m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory;
15934     m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges;
15935     m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges;
15936     m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory;
15937     m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory;
15938     m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements;
15939     m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements;
15940     m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer;
15941     m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer;
15942     m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage;
15943     m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage;
15944     m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer;
15945 
15946     // Vulkan 1.1
15947 #if VMA_VULKAN_VERSION >= 1001000
15948     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15949     {
15950         m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2;
15951         m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2;
15952         m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2;
15953         m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2;
15954         m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2;
15955     }
15956 #endif
15957 }
15958 
15959 #endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15960 
ImportVulkanFunctions_Custom(const VmaVulkanFunctions * pVulkanFunctions)15961 void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions)
15962 {
15963     VMA_ASSERT(pVulkanFunctions != VMA_NULL);
15964 
15965 #define VMA_COPY_IF_NOT_NULL(funcName) \
15966     if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
15967 
15968     VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
15969     VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
15970     VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
15971     VMA_COPY_IF_NOT_NULL(vkFreeMemory);
15972     VMA_COPY_IF_NOT_NULL(vkMapMemory);
15973     VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
15974     VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
15975     VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
15976     VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
15977     VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
15978     VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
15979     VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
15980     VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
15981     VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
15982     VMA_COPY_IF_NOT_NULL(vkCreateImage);
15983     VMA_COPY_IF_NOT_NULL(vkDestroyImage);
15984     VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
15985 
15986 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15987     VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
15988     VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
15989 #endif
15990 
15991 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
15992     VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR);
15993     VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR);
15994 #endif
15995 
15996 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
15997     VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR);
15998 #endif
15999 
16000 #undef VMA_COPY_IF_NOT_NULL
16001 }
16002 
16003 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
16004 
ImportVulkanFunctions_Dynamic()16005 void VmaAllocator_T::ImportVulkanFunctions_Dynamic()
16006 {
16007 #define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \
16008     if(m_VulkanFunctions.memberName == VMA_NULL) \
16009         m_VulkanFunctions.memberName = \
16010             (functionPointerType)vkGetInstanceProcAddr(m_hInstance, functionNameString);
16011 #define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \
16012     if(m_VulkanFunctions.memberName == VMA_NULL) \
16013         m_VulkanFunctions.memberName = \
16014             (functionPointerType)vkGetDeviceProcAddr(m_hDevice, functionNameString);
16015 
16016     VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");
16017     VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties");
16018     VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory");
16019     VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory");
16020     VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory");
16021     VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory");
16022     VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges");
16023     VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges");
16024     VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory");
16025     VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory");
16026     VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements");
16027     VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements");
16028     VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer");
16029     VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer");
16030     VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage");
16031     VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage");
16032     VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer");
16033 
16034 #if VMA_DEDICATED_ALLOCATION
16035     if(m_UseKhrDedicatedAllocation)
16036     {
16037         VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR");
16038         VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR");
16039     }
16040 #endif
16041 
16042 #if VMA_BIND_MEMORY2
16043     if(m_UseKhrBindMemory2)
16044     {
16045         VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR");
16046         VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR");
16047     }
16048 #endif // #if VMA_BIND_MEMORY2
16049 
16050 #if VMA_MEMORY_BUDGET
16051     if(m_UseExtMemoryBudget && m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
16052     {
16053         VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");
16054     }
16055 #endif // #if VMA_MEMORY_BUDGET
16056 
16057 #undef VMA_FETCH_DEVICE_FUNC
16058 #undef VMA_FETCH_INSTANCE_FUNC
16059 }
16060 
16061 #endif // #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
16062 
ValidateVulkanFunctions()16063 void VmaAllocator_T::ValidateVulkanFunctions()
16064 {
16065     VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
16066     VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
16067     VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
16068     VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
16069     VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
16070     VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
16071     VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
16072     VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
16073     VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
16074     VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
16075     VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
16076     VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
16077     VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
16078     VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
16079     VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
16080     VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
16081     VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
16082 
16083 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16084     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation)
16085     {
16086         VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
16087         VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
16088     }
16089 #endif
16090 
16091 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
16092     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2)
16093     {
16094         VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL);
16095         VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL);
16096     }
16097 #endif
16098 
16099 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
16100     if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16101     {
16102         VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);
16103     }
16104 #endif
16105 }
16106 
CalcPreferredBlockSize(uint32_t memTypeIndex)16107 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
16108 {
16109     const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16110     const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
16111     const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
16112     return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32);
16113 }
16114 
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)16115 VkResult VmaAllocator_T::AllocateMemoryOfType(
16116     VkDeviceSize size,
16117     VkDeviceSize alignment,
16118     bool dedicatedAllocation,
16119     VkBuffer dedicatedBuffer,
16120     VkBufferUsageFlags dedicatedBufferUsage,
16121     VkImage dedicatedImage,
16122     const VmaAllocationCreateInfo& createInfo,
16123     uint32_t memTypeIndex,
16124     VmaSuballocationType suballocType,
16125     size_t allocationCount,
16126     VmaAllocation* pAllocations)
16127 {
16128     VMA_ASSERT(pAllocations != VMA_NULL);
16129     VMA_DEBUG_LOG("  AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size);
16130 
16131     VmaAllocationCreateInfo finalCreateInfo = createInfo;
16132 
16133     // If memory type is not HOST_VISIBLE, disable MAPPED.
16134     if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16135         (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
16136     {
16137         finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
16138     }
16139     // If memory is lazily allocated, it should be always dedicated.
16140     if(finalCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED)
16141     {
16142         finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
16143     }
16144 
16145     VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
16146     VMA_ASSERT(blockVector);
16147 
16148     const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
16149     bool preferDedicatedMemory =
16150         VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
16151         dedicatedAllocation ||
16152         // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
16153         size > preferredBlockSize / 2;
16154 
16155     if(preferDedicatedMemory &&
16156         (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
16157         finalCreateInfo.pool == VK_NULL_HANDLE)
16158     {
16159         finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
16160     }
16161 
16162     if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
16163     {
16164         if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16165         {
16166             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16167         }
16168         else
16169         {
16170             return AllocateDedicatedMemory(
16171                 size,
16172                 suballocType,
16173                 memTypeIndex,
16174                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
16175                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
16176                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
16177                 finalCreateInfo.pUserData,
16178                 dedicatedBuffer,
16179                 dedicatedBufferUsage,
16180                 dedicatedImage,
16181                 allocationCount,
16182                 pAllocations);
16183         }
16184     }
16185     else
16186     {
16187         VkResult res = blockVector->Allocate(
16188             m_CurrentFrameIndex.load(),
16189             size,
16190             alignment,
16191             finalCreateInfo,
16192             suballocType,
16193             allocationCount,
16194             pAllocations);
16195         if(res == VK_SUCCESS)
16196         {
16197             return res;
16198         }
16199 
16200         // 5. Try dedicated memory.
16201         if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16202         {
16203             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16204         }
16205         else
16206         {
16207             res = AllocateDedicatedMemory(
16208                 size,
16209                 suballocType,
16210                 memTypeIndex,
16211                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
16212                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
16213                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
16214                 finalCreateInfo.pUserData,
16215                 dedicatedBuffer,
16216                 dedicatedBufferUsage,
16217                 dedicatedImage,
16218                 allocationCount,
16219                 pAllocations);
16220             if(res == VK_SUCCESS)
16221             {
16222                 // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
16223                 VMA_DEBUG_LOG("    Allocated as DedicatedMemory");
16224                 return VK_SUCCESS;
16225             }
16226             else
16227             {
16228                 // Everything failed: Return error code.
16229                 VMA_DEBUG_LOG("    vkAllocateMemory FAILED");
16230                 return res;
16231             }
16232         }
16233     }
16234 }
16235 
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)16236 VkResult VmaAllocator_T::AllocateDedicatedMemory(
16237     VkDeviceSize size,
16238     VmaSuballocationType suballocType,
16239     uint32_t memTypeIndex,
16240     bool withinBudget,
16241     bool map,
16242     bool isUserDataString,
16243     void* pUserData,
16244     VkBuffer dedicatedBuffer,
16245     VkBufferUsageFlags dedicatedBufferUsage,
16246     VkImage dedicatedImage,
16247     size_t allocationCount,
16248     VmaAllocation* pAllocations)
16249 {
16250     VMA_ASSERT(allocationCount > 0 && pAllocations);
16251 
16252     if(withinBudget)
16253     {
16254         const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16255         VmaBudget heapBudget = {};
16256         GetBudget(&heapBudget, heapIndex, 1);
16257         if(heapBudget.usage + size * allocationCount > heapBudget.budget)
16258         {
16259             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16260         }
16261     }
16262 
16263     VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
16264     allocInfo.memoryTypeIndex = memTypeIndex;
16265     allocInfo.allocationSize = size;
16266 
16267 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16268     VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
16269     if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16270     {
16271         if(dedicatedBuffer != VK_NULL_HANDLE)
16272         {
16273             VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
16274             dedicatedAllocInfo.buffer = dedicatedBuffer;
16275             VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16276         }
16277         else if(dedicatedImage != VK_NULL_HANDLE)
16278         {
16279             dedicatedAllocInfo.image = dedicatedImage;
16280             VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16281         }
16282     }
16283 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16284 
16285 #if VMA_BUFFER_DEVICE_ADDRESS
16286     VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
16287     if(m_UseKhrBufferDeviceAddress)
16288     {
16289         bool canContainBufferWithDeviceAddress = true;
16290         if(dedicatedBuffer != VK_NULL_HANDLE)
16291         {
16292             canContainBufferWithDeviceAddress = dedicatedBufferUsage == UINT32_MAX || // Usage flags unknown
16293                 (dedicatedBufferUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) != 0;
16294         }
16295         else if(dedicatedImage != VK_NULL_HANDLE)
16296         {
16297             canContainBufferWithDeviceAddress = false;
16298         }
16299         if(canContainBufferWithDeviceAddress)
16300         {
16301             allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
16302             VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
16303         }
16304     }
16305 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
16306 
16307     size_t allocIndex;
16308     VkResult res = VK_SUCCESS;
16309     for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16310     {
16311         res = AllocateDedicatedMemoryPage(
16312             size,
16313             suballocType,
16314             memTypeIndex,
16315             allocInfo,
16316             map,
16317             isUserDataString,
16318             pUserData,
16319             pAllocations + allocIndex);
16320         if(res != VK_SUCCESS)
16321         {
16322             break;
16323         }
16324     }
16325 
16326     if(res == VK_SUCCESS)
16327     {
16328         // Register them in m_pDedicatedAllocations.
16329         {
16330             VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16331             AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
16332             VMA_ASSERT(pDedicatedAllocations);
16333             for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16334             {
16335                 VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, pAllocations[allocIndex]);
16336             }
16337         }
16338 
16339         VMA_DEBUG_LOG("    Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
16340     }
16341     else
16342     {
16343         // Free all already created allocations.
16344         while(allocIndex--)
16345         {
16346             VmaAllocation currAlloc = pAllocations[allocIndex];
16347             VkDeviceMemory hMemory = currAlloc->GetMemory();
16348 
16349             /*
16350             There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
16351             before vkFreeMemory.
16352 
16353             if(currAlloc->GetMappedData() != VMA_NULL)
16354             {
16355                 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
16356             }
16357             */
16358 
16359             FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
16360             m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize());
16361             currAlloc->SetUserData(this, VMA_NULL);
16362             m_AllocationObjectAllocator.Free(currAlloc);
16363         }
16364 
16365         memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16366     }
16367 
16368     return res;
16369 }
16370 
AllocateDedicatedMemoryPage(VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,const VkMemoryAllocateInfo & allocInfo,bool map,bool isUserDataString,void * pUserData,VmaAllocation * pAllocation)16371 VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
16372     VkDeviceSize size,
16373     VmaSuballocationType suballocType,
16374     uint32_t memTypeIndex,
16375     const VkMemoryAllocateInfo& allocInfo,
16376     bool map,
16377     bool isUserDataString,
16378     void* pUserData,
16379     VmaAllocation* pAllocation)
16380 {
16381     VkDeviceMemory hMemory = VK_NULL_HANDLE;
16382     VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
16383     if(res < 0)
16384     {
16385         VMA_DEBUG_LOG("    vkAllocateMemory FAILED");
16386         return res;
16387     }
16388 
16389     void* pMappedData = VMA_NULL;
16390     if(map)
16391     {
16392         res = (*m_VulkanFunctions.vkMapMemory)(
16393             m_hDevice,
16394             hMemory,
16395             0,
16396             VK_WHOLE_SIZE,
16397             0,
16398             &pMappedData);
16399         if(res < 0)
16400         {
16401             VMA_DEBUG_LOG("    vkMapMemory FAILED");
16402             FreeVulkanMemory(memTypeIndex, size, hMemory);
16403             return res;
16404         }
16405     }
16406 
16407     *pAllocation = m_AllocationObjectAllocator.Allocate(m_CurrentFrameIndex.load(), isUserDataString);
16408     (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
16409     (*pAllocation)->SetUserData(this, pUserData);
16410     m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size);
16411     if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16412     {
16413         FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
16414     }
16415 
16416     return VK_SUCCESS;
16417 }
16418 
GetBufferMemoryRequirements(VkBuffer hBuffer,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)16419 void VmaAllocator_T::GetBufferMemoryRequirements(
16420     VkBuffer hBuffer,
16421     VkMemoryRequirements& memReq,
16422     bool& requiresDedicatedAllocation,
16423     bool& prefersDedicatedAllocation) const
16424 {
16425 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16426     if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16427     {
16428         VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
16429         memReqInfo.buffer = hBuffer;
16430 
16431         VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16432 
16433         VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16434         VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16435 
16436         (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16437 
16438         memReq = memReq2.memoryRequirements;
16439         requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16440         prefersDedicatedAllocation  = (memDedicatedReq.prefersDedicatedAllocation  != VK_FALSE);
16441     }
16442     else
16443 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16444     {
16445         (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
16446         requiresDedicatedAllocation = false;
16447         prefersDedicatedAllocation  = false;
16448     }
16449 }
16450 
GetImageMemoryRequirements(VkImage hImage,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)16451 void VmaAllocator_T::GetImageMemoryRequirements(
16452     VkImage hImage,
16453     VkMemoryRequirements& memReq,
16454     bool& requiresDedicatedAllocation,
16455     bool& prefersDedicatedAllocation) const
16456 {
16457 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16458     if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16459     {
16460         VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
16461         memReqInfo.image = hImage;
16462 
16463         VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16464 
16465         VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16466         VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16467 
16468         (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16469 
16470         memReq = memReq2.memoryRequirements;
16471         requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16472         prefersDedicatedAllocation  = (memDedicatedReq.prefersDedicatedAllocation  != VK_FALSE);
16473     }
16474     else
16475 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16476     {
16477         (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
16478         requiresDedicatedAllocation = false;
16479         prefersDedicatedAllocation  = false;
16480     }
16481 }
16482 
AllocateMemory(const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,VkBuffer dedicatedBuffer,VkBufferUsageFlags dedicatedBufferUsage,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)16483 VkResult VmaAllocator_T::AllocateMemory(
16484     const VkMemoryRequirements& vkMemReq,
16485     bool requiresDedicatedAllocation,
16486     bool prefersDedicatedAllocation,
16487     VkBuffer dedicatedBuffer,
16488     VkBufferUsageFlags dedicatedBufferUsage,
16489     VkImage dedicatedImage,
16490     const VmaAllocationCreateInfo& createInfo,
16491     VmaSuballocationType suballocType,
16492     size_t allocationCount,
16493     VmaAllocation* pAllocations)
16494 {
16495     memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16496 
16497     VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
16498 
16499     if(vkMemReq.size == 0)
16500     {
16501         return VK_ERROR_VALIDATION_FAILED_EXT;
16502     }
16503     if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
16504         (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16505     {
16506         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
16507         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16508     }
16509     if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16510         (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
16511     {
16512         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
16513         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16514     }
16515     if(requiresDedicatedAllocation)
16516     {
16517         if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16518         {
16519             VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
16520             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16521         }
16522         if(createInfo.pool != VK_NULL_HANDLE)
16523         {
16524             VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
16525             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16526         }
16527     }
16528     if((createInfo.pool != VK_NULL_HANDLE) &&
16529         ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
16530     {
16531         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
16532         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16533     }
16534 
16535     if(createInfo.pool != VK_NULL_HANDLE)
16536     {
16537         const VkDeviceSize alignmentForPool = VMA_MAX(
16538             vkMemReq.alignment,
16539             GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex()));
16540 
16541         VmaAllocationCreateInfo createInfoForPool = createInfo;
16542         // If memory type is not HOST_VISIBLE, disable MAPPED.
16543         if((createInfoForPool.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16544             (m_MemProps.memoryTypes[createInfo.pool->m_BlockVector.GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
16545         {
16546             createInfoForPool.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
16547         }
16548 
16549         return createInfo.pool->m_BlockVector.Allocate(
16550             m_CurrentFrameIndex.load(),
16551             vkMemReq.size,
16552             alignmentForPool,
16553             createInfoForPool,
16554             suballocType,
16555             allocationCount,
16556             pAllocations);
16557     }
16558     else
16559     {
16560         // Bit mask of memory Vulkan types acceptable for this allocation.
16561         uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
16562         uint32_t memTypeIndex = UINT32_MAX;
16563         VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16564         if(res == VK_SUCCESS)
16565         {
16566             VkDeviceSize alignmentForMemType = VMA_MAX(
16567                 vkMemReq.alignment,
16568                 GetMemoryTypeMinAlignment(memTypeIndex));
16569 
16570             res = AllocateMemoryOfType(
16571                 vkMemReq.size,
16572                 alignmentForMemType,
16573                 requiresDedicatedAllocation || prefersDedicatedAllocation,
16574                 dedicatedBuffer,
16575                 dedicatedBufferUsage,
16576                 dedicatedImage,
16577                 createInfo,
16578                 memTypeIndex,
16579                 suballocType,
16580                 allocationCount,
16581                 pAllocations);
16582             // Succeeded on first try.
16583             if(res == VK_SUCCESS)
16584             {
16585                 return res;
16586             }
16587             // Allocation from this memory type failed. Try other compatible memory types.
16588             else
16589             {
16590                 for(;;)
16591                 {
16592                     // Remove old memTypeIndex from list of possibilities.
16593                     memoryTypeBits &= ~(1u << memTypeIndex);
16594                     // Find alternative memTypeIndex.
16595                     res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16596                     if(res == VK_SUCCESS)
16597                     {
16598                         alignmentForMemType = VMA_MAX(
16599                             vkMemReq.alignment,
16600                             GetMemoryTypeMinAlignment(memTypeIndex));
16601 
16602                         res = AllocateMemoryOfType(
16603                             vkMemReq.size,
16604                             alignmentForMemType,
16605                             requiresDedicatedAllocation || prefersDedicatedAllocation,
16606                             dedicatedBuffer,
16607                             dedicatedBufferUsage,
16608                             dedicatedImage,
16609                             createInfo,
16610                             memTypeIndex,
16611                             suballocType,
16612                             allocationCount,
16613                             pAllocations);
16614                         // Allocation from this alternative memory type succeeded.
16615                         if(res == VK_SUCCESS)
16616                         {
16617                             return res;
16618                         }
16619                         // else: Allocation from this memory type failed. Try next one - next loop iteration.
16620                     }
16621                     // No other matching memory type index could be found.
16622                     else
16623                     {
16624                         // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
16625                         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16626                     }
16627                 }
16628             }
16629         }
16630         // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
16631         else
16632             return res;
16633     }
16634 }
16635 
FreeMemory(size_t allocationCount,const VmaAllocation * pAllocations)16636 void VmaAllocator_T::FreeMemory(
16637     size_t allocationCount,
16638     const VmaAllocation* pAllocations)
16639 {
16640     VMA_ASSERT(pAllocations);
16641 
16642     for(size_t allocIndex = allocationCount; allocIndex--; )
16643     {
16644         VmaAllocation allocation = pAllocations[allocIndex];
16645 
16646         if(allocation != VK_NULL_HANDLE)
16647         {
16648             if(TouchAllocation(allocation))
16649             {
16650                 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16651                 {
16652                     FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
16653                 }
16654 
16655                 switch(allocation->GetType())
16656                 {
16657                 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16658                     {
16659                         VmaBlockVector* pBlockVector = VMA_NULL;
16660                         VmaPool hPool = allocation->GetBlock()->GetParentPool();
16661                         if(hPool != VK_NULL_HANDLE)
16662                         {
16663                             pBlockVector = &hPool->m_BlockVector;
16664                         }
16665                         else
16666                         {
16667                             const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16668                             pBlockVector = m_pBlockVectors[memTypeIndex];
16669                         }
16670                         pBlockVector->Free(allocation);
16671                     }
16672                     break;
16673                 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
16674                     FreeDedicatedMemory(allocation);
16675                     break;
16676                 default:
16677                     VMA_ASSERT(0);
16678                 }
16679             }
16680 
16681             // Do this regardless of whether the allocation is lost. Lost allocations still account to Budget.AllocationBytes.
16682             m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
16683             allocation->SetUserData(this, VMA_NULL);
16684             m_AllocationObjectAllocator.Free(allocation);
16685         }
16686     }
16687 }
16688 
16689 // OH ISSUE: VMA preAlloc
AllocateReservedMemory(const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,VkBuffer dedicatedBuffer,VkBufferUsageFlags dedicatedBufferUsage,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)16690 VkResult VmaAllocator_T::AllocateReservedMemory(
16691     const VkMemoryRequirements& vkMemReq,
16692     bool requiresDedicatedAllocation,
16693     bool prefersDedicatedAllocation,
16694     VkBuffer dedicatedBuffer,
16695     VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown.
16696     VkImage dedicatedImage,
16697     const VmaAllocationCreateInfo& createInfo,
16698     VmaSuballocationType suballocType,
16699     size_t allocationCount,
16700     VmaAllocation* pAllocations)
16701 {
16702     memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16703     if (allocationCount != 1) {
16704         return VK_NOT_READY;
16705     }
16706     uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
16707     uint32_t memTypeIndex = UINT32_MAX;
16708     VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16709     if(res != VK_SUCCESS) {
16710         return res;
16711     }
16712     VkDeviceSize alignmentForMemType = VMA_MAX(
16713         vkMemReq.alignment,
16714         GetMemoryTypeMinAlignment(memTypeIndex));
16715     VmaBlockVector* reservedBlockVector = m_pReservedBlockVectors[memTypeIndex];
16716     VMA_ASSERT(reservedBlockVector);
16717 
16718     res = reservedBlockVector->AllocateReserved(
16719         m_CurrentFrameIndex.load(),
16720         vkMemReq.size,
16721         alignmentForMemType,
16722         createInfo,
16723         suballocType,
16724         pAllocations);
16725     return res;
16726 }
16727 
FreeReservedMemory(size_t allocationCount,const VmaAllocation * pAllocations)16728 void VmaAllocator_T::FreeReservedMemory(
16729     size_t allocationCount,
16730     const VmaAllocation* pAllocations)
16731 {
16732     VMA_ASSERT(pAllocations);
16733 
16734     for(size_t allocIndex = allocationCount; allocIndex--; )
16735     {
16736         VmaAllocation allocation = pAllocations[allocIndex];
16737 
16738         if(allocation != VK_NULL_HANDLE)
16739         {
16740             if(TouchAllocation(allocation))
16741             {
16742                 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16743                 {
16744                     FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
16745                 }
16746 
16747                 switch(allocation->GetType())
16748                 {
16749                 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16750                     {
16751                         VmaBlockVector* pBlockVector = VMA_NULL;
16752                         VmaPool hPool = allocation->GetBlock()->GetParentPool();
16753                         if(hPool != VK_NULL_HANDLE)
16754                         {
16755                             pBlockVector = &hPool->m_BlockVector;
16756                         }
16757                         else
16758                         {
16759                             const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16760                             pBlockVector = m_pReservedBlockVectors[memTypeIndex];
16761                         }
16762                         VMA_ASSERT(pBlockVector);
16763                         pBlockVector->FreeReserved(allocation);
16764                     }
16765                     break;
16766                 default:
16767                     VMA_ASSERT(0);
16768                 }
16769             }
16770             m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
16771         }
16772     }
16773 }
16774 
SwapReservedBlock(VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)16775 VkResult VmaAllocator_T::SwapReservedBlock(
16776     VkImage image,
16777     const VmaAllocationCreateInfo* pCreateInfo,
16778     VmaAllocation* pAllocation,
16779     VmaAllocationInfo* pAllocationInfo)
16780 {
16781     VmaAllocation oldAllocation = *pAllocation;
16782     if (oldAllocation->GetType() != VmaAllocation_T::ALLOCATION_TYPE_BLOCK) {
16783         return VK_ERROR_UNKNOWN;
16784     }
16785     VmaPool hPool = oldAllocation->GetBlock()->GetParentPool();
16786     if(hPool != VK_NULL_HANDLE) {
16787         return VK_ERROR_UNKNOWN;
16788     }
16789     const uint32_t memTypeIndex = oldAllocation->GetMemoryTypeIndex();
16790     VmaBlockVector* reservedBlockVector = m_pReservedBlockVectors[memTypeIndex];
16791     if (reservedBlockVector->IsEmpty()) {
16792         return VK_NOT_READY;
16793     }
16794     VmaBlockVector* blockVector = m_pBlockVectors[memTypeIndex];
16795     SwapLastBlock(blockVector, reservedBlockVector);
16796     return VK_SUCCESS;
16797 }
16798 
GetPreAllocBlockSize()16799 uint32_t VmaAllocator_T::GetPreAllocBlockSize()
16800 {
16801     VmaBlockVector* reservedBlockVector = m_pReservedBlockVectors[0];
16802     VMA_ASSERT(reservedBlockVector);
16803     return reservedBlockVector->GetBlockCount();
16804 }
16805 
ResizeAllocation(const VmaAllocation alloc,VkDeviceSize newSize)16806 VkResult VmaAllocator_T::ResizeAllocation(
16807     const VmaAllocation alloc,
16808     VkDeviceSize newSize)
16809 {
16810     // This function is deprecated and so it does nothing. It's left for backward compatibility.
16811     if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST)
16812     {
16813         return VK_ERROR_VALIDATION_FAILED_EXT;
16814     }
16815     if(newSize == alloc->GetSize())
16816     {
16817         return VK_SUCCESS;
16818     }
16819     return VK_ERROR_OUT_OF_POOL_MEMORY;
16820 }
16821 
CalculateStats(VmaStats * pStats)16822 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
16823 {
16824     // Initialize.
16825     InitStatInfo(pStats->total);
16826     for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
16827         InitStatInfo(pStats->memoryType[i]);
16828     for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
16829         InitStatInfo(pStats->memoryHeap[i]);
16830 
16831     // Process default pools.
16832     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16833     {
16834         VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
16835         VMA_ASSERT(pBlockVector);
16836         pBlockVector->AddStats(pStats);
16837     }
16838 
16839     // Process custom pools.
16840     {
16841         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
16842         for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
16843         {
16844             m_Pools[poolIndex]->m_BlockVector.AddStats(pStats);
16845         }
16846     }
16847 
16848     // Process dedicated allocations.
16849     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16850     {
16851         const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16852         VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16853         AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
16854         VMA_ASSERT(pDedicatedAllocVector);
16855         for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
16856         {
16857             VmaStatInfo allocationStatInfo;
16858             (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo);
16859             VmaAddStatInfo(pStats->total, allocationStatInfo);
16860             VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
16861             VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
16862         }
16863     }
16864 
16865     // Postprocess.
16866     VmaPostprocessCalcStatInfo(pStats->total);
16867     for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
16868         VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
16869     for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
16870         VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
16871 }
16872 
FreeEmptyBlock()16873 void VmaAllocator_T::FreeEmptyBlock()
16874 {
16875     for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) {
16876         VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
16877         VMA_ASSERT(pBlockVector);
16878         pBlockVector->FreeEmptyBlock();
16879 
16880         VmaBlockVector* const pReservedBlockVector = m_pReservedBlockVectors[memTypeIndex];
16881         VMA_ASSERT(pReservedBlockVector);
16882         pReservedBlockVector->FreeEmptyBlock();
16883     }
16884 }
16885 
GetBudget(VmaBudget * outBudget,uint32_t firstHeap,uint32_t heapCount)16886 void VmaAllocator_T::GetBudget(VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount)
16887 {
16888 #if VMA_MEMORY_BUDGET
16889     if(m_UseExtMemoryBudget)
16890     {
16891         if(m_Budget.m_OperationsSinceBudgetFetch < 30)
16892         {
16893             VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex);
16894             for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
16895             {
16896                 const uint32_t heapIndex = firstHeap + i;
16897 
16898                 outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
16899                 outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
16900 
16901                 if(m_Budget.m_VulkanUsage[heapIndex] + outBudget->blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex])
16902                 {
16903                     outBudget->usage = m_Budget.m_VulkanUsage[heapIndex] +
16904                         outBudget->blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
16905                 }
16906                 else
16907                 {
16908                     outBudget->usage = 0;
16909                 }
16910 
16911                 // Have to take MIN with heap size because explicit HeapSizeLimit is included in it.
16912                 outBudget->budget = VMA_MIN(
16913                     m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size);
16914             }
16915         }
16916         else
16917         {
16918             UpdateVulkanBudget(); // Outside of mutex lock
16919             GetBudget(outBudget, firstHeap, heapCount); // Recursion
16920         }
16921     }
16922     else
16923 #endif
16924     {
16925         for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
16926         {
16927             const uint32_t heapIndex = firstHeap + i;
16928 
16929             outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
16930             outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
16931 
16932             outBudget->usage = outBudget->blockBytes;
16933             outBudget->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
16934         }
16935     }
16936 }
16937 
16938 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
16939 
DefragmentationBegin(const VmaDefragmentationInfo2 & info,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)16940 VkResult VmaAllocator_T::DefragmentationBegin(
16941     const VmaDefragmentationInfo2& info,
16942     VmaDefragmentationStats* pStats,
16943     VmaDefragmentationContext* pContext)
16944 {
16945     if(info.pAllocationsChanged != VMA_NULL)
16946     {
16947         memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
16948     }
16949 
16950     *pContext = vma_new(this, VmaDefragmentationContext_T)(
16951         this, m_CurrentFrameIndex.load(), info.flags, pStats);
16952 
16953     (*pContext)->AddPools(info.poolCount, info.pPools);
16954     (*pContext)->AddAllocations(
16955         info.allocationCount, info.pAllocations, info.pAllocationsChanged);
16956 
16957     VkResult res = (*pContext)->Defragment(
16958         info.maxCpuBytesToMove, info.maxCpuAllocationsToMove,
16959         info.maxGpuBytesToMove, info.maxGpuAllocationsToMove,
16960         info.commandBuffer, pStats, info.flags);
16961 
16962     if(res != VK_NOT_READY)
16963     {
16964         vma_delete(this, *pContext);
16965         *pContext = VMA_NULL;
16966     }
16967 
16968     return res;
16969 }
16970 
DefragmentationEnd(VmaDefragmentationContext context)16971 VkResult VmaAllocator_T::DefragmentationEnd(
16972     VmaDefragmentationContext context)
16973 {
16974     vma_delete(this, context);
16975     return VK_SUCCESS;
16976 }
16977 
DefragmentationPassBegin(VmaDefragmentationPassInfo * pInfo,VmaDefragmentationContext context)16978 VkResult VmaAllocator_T::DefragmentationPassBegin(
16979     VmaDefragmentationPassInfo* pInfo,
16980     VmaDefragmentationContext context)
16981 {
16982     return context->DefragmentPassBegin(pInfo);
16983 }
DefragmentationPassEnd(VmaDefragmentationContext context)16984 VkResult VmaAllocator_T::DefragmentationPassEnd(
16985     VmaDefragmentationContext context)
16986 {
16987     return context->DefragmentPassEnd();
16988 
16989 }
16990 
GetAllocationInfo(VmaAllocation hAllocation,VmaAllocationInfo * pAllocationInfo)16991 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
16992 {
16993     if(hAllocation->CanBecomeLost())
16994     {
16995         /*
16996         Warning: This is a carefully designed algorithm.
16997         Do not modify unless you really know what you're doing :)
16998         */
16999         const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17000         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17001         for(;;)
17002         {
17003             if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
17004             {
17005                 pAllocationInfo->memoryType = UINT32_MAX;
17006                 pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
17007                 pAllocationInfo->offset = 0;
17008                 pAllocationInfo->size = hAllocation->GetSize();
17009                 pAllocationInfo->pMappedData = VMA_NULL;
17010                 pAllocationInfo->pUserData = hAllocation->GetUserData();
17011                 return;
17012             }
17013             else if(localLastUseFrameIndex == localCurrFrameIndex)
17014             {
17015                 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
17016                 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
17017                 pAllocationInfo->offset = hAllocation->GetOffset();
17018                 pAllocationInfo->size = hAllocation->GetSize();
17019                 pAllocationInfo->pMappedData = VMA_NULL;
17020                 pAllocationInfo->pUserData = hAllocation->GetUserData();
17021                 return;
17022             }
17023             else // Last use time earlier than current time.
17024             {
17025                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17026                 {
17027                     localLastUseFrameIndex = localCurrFrameIndex;
17028                 }
17029             }
17030         }
17031     }
17032     else
17033     {
17034 #if VMA_STATS_STRING_ENABLED
17035         uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17036         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17037         for(;;)
17038         {
17039             VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
17040             if(localLastUseFrameIndex == localCurrFrameIndex)
17041             {
17042                 break;
17043             }
17044             else // Last use time earlier than current time.
17045             {
17046                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17047                 {
17048                     localLastUseFrameIndex = localCurrFrameIndex;
17049                 }
17050             }
17051         }
17052 #endif
17053 
17054         pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
17055         pAllocationInfo->deviceMemory = hAllocation->GetMemory();
17056         pAllocationInfo->offset = hAllocation->GetOffset();
17057         pAllocationInfo->size = hAllocation->GetSize();
17058         pAllocationInfo->pMappedData = hAllocation->GetMappedData();
17059         pAllocationInfo->pUserData = hAllocation->GetUserData();
17060     }
17061 }
17062 
TouchAllocation(VmaAllocation hAllocation)17063 bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
17064 {
17065     // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
17066     if(hAllocation->CanBecomeLost())
17067     {
17068         uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17069         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17070         for(;;)
17071         {
17072             if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
17073             {
17074                 return false;
17075             }
17076             else if(localLastUseFrameIndex == localCurrFrameIndex)
17077             {
17078                 return true;
17079             }
17080             else // Last use time earlier than current time.
17081             {
17082                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17083                 {
17084                     localLastUseFrameIndex = localCurrFrameIndex;
17085                 }
17086             }
17087         }
17088     }
17089     else
17090     {
17091 #if VMA_STATS_STRING_ENABLED
17092         uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17093         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17094         for(;;)
17095         {
17096             VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
17097             if(localLastUseFrameIndex == localCurrFrameIndex)
17098             {
17099                 break;
17100             }
17101             else // Last use time earlier than current time.
17102             {
17103                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17104                 {
17105                     localLastUseFrameIndex = localCurrFrameIndex;
17106                 }
17107             }
17108         }
17109 #endif
17110 
17111         return true;
17112     }
17113 }
17114 
CreatePool(const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)17115 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
17116 {
17117     VMA_DEBUG_LOG("  CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
17118 
17119     VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
17120 
17121     if(newCreateInfo.maxBlockCount == 0)
17122     {
17123         newCreateInfo.maxBlockCount = SIZE_MAX;
17124     }
17125     if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
17126     {
17127         return VK_ERROR_INITIALIZATION_FAILED;
17128     }
17129     // Memory type index out of range or forbidden.
17130     if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() ||
17131         ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0)
17132     {
17133         return VK_ERROR_FEATURE_NOT_PRESENT;
17134     }
17135 
17136     const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
17137 
17138     *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
17139 
17140     VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
17141     if(res != VK_SUCCESS)
17142     {
17143         vma_delete(this, *pPool);
17144         *pPool = VMA_NULL;
17145         return res;
17146     }
17147 
17148     // Add to m_Pools.
17149     {
17150         VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
17151         (*pPool)->SetId(m_NextPoolId++);
17152         VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool);
17153     }
17154 
17155     return VK_SUCCESS;
17156 }
17157 
DestroyPool(VmaPool pool)17158 void VmaAllocator_T::DestroyPool(VmaPool pool)
17159 {
17160     // Remove from m_Pools.
17161     {
17162         VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
17163         bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
17164         VMA_ASSERT(success && "Pool not found in Allocator.");
17165     }
17166 
17167     vma_delete(this, pool);
17168 }
17169 
GetPoolStats(VmaPool pool,VmaPoolStats * pPoolStats)17170 void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
17171 {
17172     pool->m_BlockVector.GetPoolStats(pPoolStats);
17173 }
17174 
SetCurrentFrameIndex(uint32_t frameIndex)17175 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
17176 {
17177     m_CurrentFrameIndex.store(frameIndex);
17178 
17179 #if VMA_MEMORY_BUDGET
17180     if(m_UseExtMemoryBudget)
17181     {
17182         UpdateVulkanBudget();
17183     }
17184 #endif // #if VMA_MEMORY_BUDGET
17185 }
17186 
MakePoolAllocationsLost(VmaPool hPool,size_t * pLostAllocationCount)17187 void VmaAllocator_T::MakePoolAllocationsLost(
17188     VmaPool hPool,
17189     size_t* pLostAllocationCount)
17190 {
17191     hPool->m_BlockVector.MakePoolAllocationsLost(
17192         m_CurrentFrameIndex.load(),
17193         pLostAllocationCount);
17194 }
17195 
CheckPoolCorruption(VmaPool hPool)17196 VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
17197 {
17198     return hPool->m_BlockVector.CheckCorruption();
17199 }
17200 
CheckCorruption(uint32_t memoryTypeBits)17201 VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
17202 {
17203     VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
17204 
17205     // Process default pools.
17206     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17207     {
17208         if(((1u << memTypeIndex) & memoryTypeBits) != 0)
17209         {
17210             VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
17211             VMA_ASSERT(pBlockVector);
17212             VkResult localRes = pBlockVector->CheckCorruption();
17213             switch(localRes)
17214             {
17215             case VK_ERROR_FEATURE_NOT_PRESENT:
17216                 break;
17217             case VK_SUCCESS:
17218                 finalRes = VK_SUCCESS;
17219                 break;
17220             default:
17221                 return localRes;
17222             }
17223         }
17224     }
17225 
17226     // Process custom pools.
17227     {
17228         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
17229         for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
17230         {
17231             if(((1u << m_Pools[poolIndex]->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
17232             {
17233                 VkResult localRes = m_Pools[poolIndex]->m_BlockVector.CheckCorruption();
17234                 switch(localRes)
17235                 {
17236                 case VK_ERROR_FEATURE_NOT_PRESENT:
17237                     break;
17238                 case VK_SUCCESS:
17239                     finalRes = VK_SUCCESS;
17240                     break;
17241                 default:
17242                     return localRes;
17243                 }
17244             }
17245         }
17246     }
17247 
17248     return finalRes;
17249 }
17250 
CreateLostAllocation(VmaAllocation * pAllocation)17251 void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
17252 {
17253     *pAllocation = m_AllocationObjectAllocator.Allocate(VMA_FRAME_INDEX_LOST, false);
17254     (*pAllocation)->InitLost();
17255 }
17256 
AllocateVulkanMemory(const VkMemoryAllocateInfo * pAllocateInfo,VkDeviceMemory * pMemory)17257 VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
17258 {
17259     const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
17260 
17261     // HeapSizeLimit is in effect for this heap.
17262     if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0)
17263     {
17264         const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
17265         VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex];
17266         for(;;)
17267         {
17268             const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize;
17269             if(blockBytesAfterAllocation > heapSize)
17270             {
17271                 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
17272             }
17273             if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation))
17274             {
17275                 break;
17276             }
17277         }
17278     }
17279     else
17280     {
17281         m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize;
17282     }
17283 
17284     // VULKAN CALL vkAllocateMemory.
17285     VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
17286 
17287     if(res == VK_SUCCESS)
17288     {
17289 #if VMA_MEMORY_BUDGET
17290         ++m_Budget.m_OperationsSinceBudgetFetch;
17291 #endif
17292 
17293         // Informative callback.
17294         if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
17295         {
17296             (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData);
17297         }
17298     }
17299     else
17300     {
17301         m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize;
17302     }
17303 
17304     return res;
17305 }
17306 
FreeVulkanMemory(uint32_t memoryType,VkDeviceSize size,VkDeviceMemory hMemory)17307 void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
17308 {
17309     // Informative callback.
17310     if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
17311     {
17312         (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData);
17313     }
17314 
17315     // VULKAN CALL vkFreeMemory.
17316     (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
17317 
17318     m_Budget.m_BlockBytes[MemoryTypeIndexToHeapIndex(memoryType)] -= size;
17319 }
17320 
BindVulkanBuffer(VkDeviceMemory memory,VkDeviceSize memoryOffset,VkBuffer buffer,const void * pNext)17321 VkResult VmaAllocator_T::BindVulkanBuffer(
17322     VkDeviceMemory memory,
17323     VkDeviceSize memoryOffset,
17324     VkBuffer buffer,
17325     const void* pNext)
17326 {
17327     if(pNext != VMA_NULL)
17328     {
17329 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17330         if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
17331             m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL)
17332         {
17333             VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR };
17334             bindBufferMemoryInfo.pNext = pNext;
17335             bindBufferMemoryInfo.buffer = buffer;
17336             bindBufferMemoryInfo.memory = memory;
17337             bindBufferMemoryInfo.memoryOffset = memoryOffset;
17338             return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17339         }
17340         else
17341 #endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17342         {
17343             return VK_ERROR_EXTENSION_NOT_PRESENT;
17344         }
17345     }
17346     else
17347     {
17348         return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset);
17349     }
17350 }
17351 
BindVulkanImage(VkDeviceMemory memory,VkDeviceSize memoryOffset,VkImage image,const void * pNext)17352 VkResult VmaAllocator_T::BindVulkanImage(
17353     VkDeviceMemory memory,
17354     VkDeviceSize memoryOffset,
17355     VkImage image,
17356     const void* pNext)
17357 {
17358     if(pNext != VMA_NULL)
17359     {
17360 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17361         if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
17362             m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL)
17363         {
17364             VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR };
17365             bindBufferMemoryInfo.pNext = pNext;
17366             bindBufferMemoryInfo.image = image;
17367             bindBufferMemoryInfo.memory = memory;
17368             bindBufferMemoryInfo.memoryOffset = memoryOffset;
17369             return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17370         }
17371         else
17372 #endif // #if VMA_BIND_MEMORY2
17373         {
17374             return VK_ERROR_EXTENSION_NOT_PRESENT;
17375         }
17376     }
17377     else
17378     {
17379         return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset);
17380     }
17381 }
17382 
Map(VmaAllocation hAllocation,void ** ppData)17383 VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
17384 {
17385     if(hAllocation->CanBecomeLost())
17386     {
17387         return VK_ERROR_MEMORY_MAP_FAILED;
17388     }
17389 
17390     switch(hAllocation->GetType())
17391     {
17392     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17393         {
17394             VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17395             char *pBytes = VMA_NULL;
17396             VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
17397             if(res == VK_SUCCESS)
17398             {
17399                 *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
17400                 hAllocation->BlockAllocMap();
17401             }
17402             return res;
17403         }
17404     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17405         return hAllocation->DedicatedAllocMap(this, ppData);
17406     default:
17407         VMA_ASSERT(0);
17408         return VK_ERROR_MEMORY_MAP_FAILED;
17409     }
17410 }
17411 
Unmap(VmaAllocation hAllocation)17412 void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
17413 {
17414     switch(hAllocation->GetType())
17415     {
17416     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17417         {
17418             VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17419             hAllocation->BlockAllocUnmap();
17420             pBlock->Unmap(this, 1);
17421         }
17422         break;
17423     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17424         hAllocation->DedicatedAllocUnmap(this);
17425         break;
17426     default:
17427         VMA_ASSERT(0);
17428     }
17429 }
17430 
BindBufferMemory(VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkBuffer hBuffer,const void * pNext)17431 VkResult VmaAllocator_T::BindBufferMemory(
17432     VmaAllocation hAllocation,
17433     VkDeviceSize allocationLocalOffset,
17434     VkBuffer hBuffer,
17435     const void* pNext)
17436 {
17437     VkResult res = VK_SUCCESS;
17438     switch(hAllocation->GetType())
17439     {
17440     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17441         res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext);
17442         break;
17443     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17444     {
17445         VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17446         VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
17447         res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext);
17448         break;
17449     }
17450     default:
17451         VMA_ASSERT(0);
17452     }
17453     return res;
17454 }
17455 
BindImageMemory(VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkImage hImage,const void * pNext)17456 VkResult VmaAllocator_T::BindImageMemory(
17457     VmaAllocation hAllocation,
17458     VkDeviceSize allocationLocalOffset,
17459     VkImage hImage,
17460     const void* pNext)
17461 {
17462     VkResult res = VK_SUCCESS;
17463     switch(hAllocation->GetType())
17464     {
17465     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17466         res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext);
17467         break;
17468     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17469     {
17470         VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
17471         VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
17472         res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext);
17473         break;
17474     }
17475     default:
17476         VMA_ASSERT(0);
17477     }
17478     return res;
17479 }
17480 
FlushOrInvalidateAllocation(VmaAllocation hAllocation,VkDeviceSize offset,VkDeviceSize size,VMA_CACHE_OPERATION op)17481 VkResult VmaAllocator_T::FlushOrInvalidateAllocation(
17482     VmaAllocation hAllocation,
17483     VkDeviceSize offset, VkDeviceSize size,
17484     VMA_CACHE_OPERATION op)
17485 {
17486     VkResult res = VK_SUCCESS;
17487 
17488     VkMappedMemoryRange memRange = {};
17489     if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange))
17490     {
17491         switch(op)
17492         {
17493         case VMA_CACHE_FLUSH:
17494             res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
17495             break;
17496         case VMA_CACHE_INVALIDATE:
17497             res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
17498             break;
17499         default:
17500             VMA_ASSERT(0);
17501         }
17502     }
17503     // else: Just ignore this call.
17504     return res;
17505 }
17506 
FlushOrInvalidateAllocations(uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes,VMA_CACHE_OPERATION op)17507 VkResult VmaAllocator_T::FlushOrInvalidateAllocations(
17508     uint32_t allocationCount,
17509     const VmaAllocation* allocations,
17510     const VkDeviceSize* offsets, const VkDeviceSize* sizes,
17511     VMA_CACHE_OPERATION op)
17512 {
17513     typedef VmaStlAllocator<VkMappedMemoryRange> RangeAllocator;
17514     typedef VmaSmallVector<VkMappedMemoryRange, RangeAllocator, 16> RangeVector;
17515     RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks()));
17516 
17517     for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
17518     {
17519         const VmaAllocation alloc = allocations[allocIndex];
17520         const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0;
17521         const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE;
17522         VkMappedMemoryRange newRange;
17523         if(GetFlushOrInvalidateRange(alloc, offset, size, newRange))
17524         {
17525             ranges.push_back(newRange);
17526         }
17527     }
17528 
17529     VkResult res = VK_SUCCESS;
17530     if(!ranges.empty())
17531     {
17532         switch(op)
17533         {
17534         case VMA_CACHE_FLUSH:
17535             res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17536             break;
17537         case VMA_CACHE_INVALIDATE:
17538             res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17539             break;
17540         default:
17541             VMA_ASSERT(0);
17542         }
17543     }
17544     // else: Just ignore this call.
17545     return res;
17546 }
17547 
FreeDedicatedMemory(const VmaAllocation allocation)17548 void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
17549 {
17550     VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
17551 
17552     const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17553     {
17554         VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17555         AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
17556         VMA_ASSERT(pDedicatedAllocations);
17557         bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
17558         VMA_ASSERT(success);
17559     }
17560 
17561     VkDeviceMemory hMemory = allocation->GetMemory();
17562 
17563     /*
17564     There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
17565     before vkFreeMemory.
17566 
17567     if(allocation->GetMappedData() != VMA_NULL)
17568     {
17569         (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
17570     }
17571     */
17572 
17573     FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
17574 
17575     VMA_DEBUG_LOG("    Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
17576 }
17577 
CalculateGpuDefragmentationMemoryTypeBits()17578 uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const
17579 {
17580     VkBufferCreateInfo dummyBufCreateInfo;
17581     VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo);
17582 
17583     uint32_t memoryTypeBits = 0;
17584 
17585     // Create buffer.
17586     VkBuffer buf = VK_NULL_HANDLE;
17587     VkResult res = (*GetVulkanFunctions().vkCreateBuffer)(
17588         m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf);
17589     if(res == VK_SUCCESS)
17590     {
17591         // Query for supported memory types.
17592         VkMemoryRequirements memReq;
17593         (*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq);
17594         memoryTypeBits = memReq.memoryTypeBits;
17595 
17596         // Destroy buffer.
17597         (*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks());
17598     }
17599 
17600     return memoryTypeBits;
17601 }
17602 
CalculateGlobalMemoryTypeBits()17603 uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const
17604 {
17605     // Make sure memory information is already fetched.
17606     VMA_ASSERT(GetMemoryTypeCount() > 0);
17607 
17608     uint32_t memoryTypeBits = UINT32_MAX;
17609 
17610     if(!m_UseAmdDeviceCoherentMemory)
17611     {
17612         // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD.
17613         for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17614         {
17615             if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
17616             {
17617                 memoryTypeBits &= ~(1u << memTypeIndex);
17618             }
17619         }
17620     }
17621 
17622     return memoryTypeBits;
17623 }
17624 
GetFlushOrInvalidateRange(VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size,VkMappedMemoryRange & outRange)17625 bool VmaAllocator_T::GetFlushOrInvalidateRange(
17626     VmaAllocation allocation,
17627     VkDeviceSize offset, VkDeviceSize size,
17628     VkMappedMemoryRange& outRange) const
17629 {
17630     const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17631     if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
17632     {
17633         const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
17634         const VkDeviceSize allocationSize = allocation->GetSize();
17635         VMA_ASSERT(offset <= allocationSize);
17636 
17637         outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
17638         outRange.pNext = VMA_NULL;
17639         outRange.memory = allocation->GetMemory();
17640 
17641         switch(allocation->GetType())
17642         {
17643         case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17644             outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17645             if(size == VK_WHOLE_SIZE)
17646             {
17647                 outRange.size = allocationSize - outRange.offset;
17648             }
17649             else
17650             {
17651                 VMA_ASSERT(offset + size <= allocationSize);
17652                 outRange.size = VMA_MIN(
17653                     VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize),
17654                     allocationSize - outRange.offset);
17655             }
17656             break;
17657         case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17658         {
17659             // 1. Still within this allocation.
17660             outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17661             if(size == VK_WHOLE_SIZE)
17662             {
17663                 size = allocationSize - offset;
17664             }
17665             else
17666             {
17667                 VMA_ASSERT(offset + size <= allocationSize);
17668             }
17669             outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize);
17670 
17671             // 2. Adjust to whole block.
17672             const VkDeviceSize allocationOffset = allocation->GetOffset();
17673             VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
17674             const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize();
17675             outRange.offset += allocationOffset;
17676             outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset);
17677 
17678             break;
17679         }
17680         default:
17681             VMA_ASSERT(0);
17682         }
17683         return true;
17684     }
17685     return false;
17686 }
17687 
17688 #if VMA_MEMORY_BUDGET
17689 
UpdateVulkanBudget()17690 void VmaAllocator_T::UpdateVulkanBudget()
17691 {
17692     VMA_ASSERT(m_UseExtMemoryBudget);
17693 
17694     VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR };
17695 
17696     VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };
17697     VmaPnextChainPushFront(&memProps, &budgetProps);
17698 
17699     GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps);
17700 
17701     {
17702         VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex);
17703 
17704         for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
17705         {
17706             m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex];
17707             m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex];
17708             m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load();
17709 
17710             // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size.
17711             if(m_Budget.m_VulkanBudget[heapIndex] == 0)
17712             {
17713                 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
17714             }
17715             else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size)
17716             {
17717                 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size;
17718             }
17719             if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0)
17720             {
17721                 m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
17722             }
17723         }
17724         m_Budget.m_OperationsSinceBudgetFetch = 0;
17725     }
17726 }
17727 
17728 #endif // #if VMA_MEMORY_BUDGET
17729 
FillAllocation(const VmaAllocation hAllocation,uint8_t pattern)17730 void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
17731 {
17732     if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
17733         !hAllocation->CanBecomeLost() &&
17734         (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
17735     {
17736         void* pData = VMA_NULL;
17737         VkResult res = Map(hAllocation, &pData);
17738         if(res == VK_SUCCESS)
17739         {
17740             memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
17741             FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
17742             Unmap(hAllocation);
17743         }
17744         else
17745         {
17746             VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
17747         }
17748     }
17749 }
17750 
GetGpuDefragmentationMemoryTypeBits()17751 uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits()
17752 {
17753     uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load();
17754     if(memoryTypeBits == UINT32_MAX)
17755     {
17756         memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits();
17757         m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits);
17758     }
17759     return memoryTypeBits;
17760 }
17761 
17762 #if VMA_STATS_STRING_ENABLED
17763 
PrintDetailedMap(VmaJsonWriter & json)17764 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
17765 {
17766     bool dedicatedAllocationsStarted = false;
17767     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17768     {
17769         VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17770         AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
17771         VMA_ASSERT(pDedicatedAllocVector);
17772         if(pDedicatedAllocVector->empty() == false)
17773         {
17774             if(dedicatedAllocationsStarted == false)
17775             {
17776                 dedicatedAllocationsStarted = true;
17777                 json.WriteString("DedicatedAllocations");
17778                 json.BeginObject();
17779             }
17780 
17781             json.BeginString("Type ");
17782             json.ContinueString(memTypeIndex);
17783             json.EndString();
17784 
17785             json.BeginArray();
17786 
17787             for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
17788             {
17789                 json.BeginObject(true);
17790                 const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
17791                 hAlloc->PrintParameters(json);
17792                 json.EndObject();
17793             }
17794 
17795             json.EndArray();
17796         }
17797     }
17798     if(dedicatedAllocationsStarted)
17799     {
17800         json.EndObject();
17801     }
17802 
17803     {
17804         bool allocationsStarted = false;
17805         for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17806         {
17807             if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
17808             {
17809                 if(allocationsStarted == false)
17810                 {
17811                     allocationsStarted = true;
17812                     json.WriteString("DefaultPools");
17813                     json.BeginObject();
17814                 }
17815 
17816                 json.BeginString("Type ");
17817                 json.ContinueString(memTypeIndex);
17818                 json.EndString();
17819 
17820                 m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
17821             }
17822         }
17823         if(allocationsStarted)
17824         {
17825             json.EndObject();
17826         }
17827     }
17828 
17829     // Custom pools
17830     {
17831         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
17832         const size_t poolCount = m_Pools.size();
17833         if(poolCount > 0)
17834         {
17835             json.WriteString("Pools");
17836             json.BeginObject();
17837             for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
17838             {
17839                 json.BeginString();
17840                 json.ContinueString(m_Pools[poolIndex]->GetId());
17841                 json.EndString();
17842 
17843                 m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
17844             }
17845             json.EndObject();
17846         }
17847     }
17848 }
17849 
17850 #endif // #if VMA_STATS_STRING_ENABLED
17851 
17852 ////////////////////////////////////////////////////////////////////////////////
17853 // Public interface
17854 
vmaCreateAllocator(const VmaAllocatorCreateInfo * pCreateInfo,VmaAllocator * pAllocator)17855 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
17856     const VmaAllocatorCreateInfo* pCreateInfo,
17857     VmaAllocator* pAllocator)
17858 {
17859     VMA_ASSERT(pCreateInfo && pAllocator);
17860     VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 ||
17861         (VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 2));
17862     VMA_DEBUG_LOG("vmaCreateAllocator");
17863     *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
17864     return (*pAllocator)->Init(pCreateInfo);
17865 }
17866 
vmaDestroyAllocator(VmaAllocator allocator)17867 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
17868     VmaAllocator allocator)
17869 {
17870     if(allocator != VK_NULL_HANDLE)
17871     {
17872         VMA_DEBUG_LOG("vmaDestroyAllocator");
17873         VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
17874         vma_delete(&allocationCallbacks, allocator);
17875     }
17876 }
17877 
vmaGetAllocatorInfo(VmaAllocator allocator,VmaAllocatorInfo * pAllocatorInfo)17878 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo)
17879 {
17880     VMA_ASSERT(allocator && pAllocatorInfo);
17881     pAllocatorInfo->instance = allocator->m_hInstance;
17882     pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice();
17883     pAllocatorInfo->device = allocator->m_hDevice;
17884 }
17885 
vmaGetPhysicalDeviceProperties(VmaAllocator allocator,const VkPhysicalDeviceProperties ** ppPhysicalDeviceProperties)17886 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
17887     VmaAllocator allocator,
17888     const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
17889 {
17890     VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
17891     *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
17892 }
17893 
vmaGetMemoryProperties(VmaAllocator allocator,const VkPhysicalDeviceMemoryProperties ** ppPhysicalDeviceMemoryProperties)17894 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
17895     VmaAllocator allocator,
17896     const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
17897 {
17898     VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
17899     *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
17900 }
17901 
vmaGetMemoryTypeProperties(VmaAllocator allocator,uint32_t memoryTypeIndex,VkMemoryPropertyFlags * pFlags)17902 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
17903     VmaAllocator allocator,
17904     uint32_t memoryTypeIndex,
17905     VkMemoryPropertyFlags* pFlags)
17906 {
17907     VMA_ASSERT(allocator && pFlags);
17908     VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
17909     *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
17910 }
17911 
vmaSetCurrentFrameIndex(VmaAllocator allocator,uint32_t frameIndex)17912 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
17913     VmaAllocator allocator,
17914     uint32_t frameIndex)
17915 {
17916     VMA_ASSERT(allocator);
17917     VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
17918 
17919     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17920 
17921     allocator->SetCurrentFrameIndex(frameIndex);
17922 }
17923 
vmaCalculateStats(VmaAllocator allocator,VmaStats * pStats)17924 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
17925     VmaAllocator allocator,
17926     VmaStats* pStats)
17927 {
17928     VMA_ASSERT(allocator && pStats);
17929     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17930     allocator->CalculateStats(pStats);
17931 }
17932 
vmaFreeEmptyBlock(VmaAllocator allocator)17933 VMA_CALL_PRE void VMA_CALL_POST vmaFreeEmptyBlock(
17934     VmaAllocator allocator)
17935 {
17936     VMA_ASSERT(allocator);
17937     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17938     allocator->FreeEmptyBlock();
17939 }
17940 
vmaGetBudget(VmaAllocator allocator,VmaBudget * pBudget)17941 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
17942     VmaAllocator allocator,
17943     VmaBudget* pBudget)
17944 {
17945     VMA_ASSERT(allocator && pBudget);
17946     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17947     allocator->GetBudget(pBudget, 0, allocator->GetMemoryHeapCount());
17948 }
17949 
17950 #if VMA_STATS_STRING_ENABLED
17951 
vmaBuildStatsString(VmaAllocator allocator,char ** ppStatsString,VkBool32 detailedMap)17952 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
17953     VmaAllocator allocator,
17954     char** ppStatsString,
17955     VkBool32 detailedMap)
17956 {
17957     VMA_ASSERT(allocator && ppStatsString);
17958     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17959 
17960     VmaStringBuilder sb(allocator);
17961     {
17962         VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
17963         json.BeginObject();
17964 
17965         VmaBudget budget[VK_MAX_MEMORY_HEAPS];
17966         allocator->GetBudget(budget, 0, allocator->GetMemoryHeapCount());
17967 
17968         VmaStats stats;
17969         allocator->CalculateStats(&stats);
17970 
17971         json.WriteString("Total");
17972         VmaPrintStatInfo(json, stats.total);
17973 
17974         for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
17975         {
17976             json.BeginString("Heap ");
17977             json.ContinueString(heapIndex);
17978             json.EndString();
17979             json.BeginObject();
17980 
17981             json.WriteString("Size");
17982             json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
17983 
17984             json.WriteString("Flags");
17985             json.BeginArray(true);
17986             if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
17987             {
17988                 json.WriteString("DEVICE_LOCAL");
17989             }
17990             json.EndArray();
17991 
17992             json.WriteString("Budget");
17993             json.BeginObject();
17994             {
17995                 json.WriteString("BlockBytes");
17996                 json.WriteNumber(budget[heapIndex].blockBytes);
17997                 json.WriteString("AllocationBytes");
17998                 json.WriteNumber(budget[heapIndex].allocationBytes);
17999                 json.WriteString("Usage");
18000                 json.WriteNumber(budget[heapIndex].usage);
18001                 json.WriteString("Budget");
18002                 json.WriteNumber(budget[heapIndex].budget);
18003             }
18004             json.EndObject();
18005 
18006             if(stats.memoryHeap[heapIndex].blockCount > 0)
18007             {
18008                 json.WriteString("Stats");
18009                 VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
18010             }
18011 
18012             for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
18013             {
18014                 if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
18015                 {
18016                     json.BeginString("Type ");
18017                     json.ContinueString(typeIndex);
18018                     json.EndString();
18019 
18020                     json.BeginObject();
18021 
18022                     json.WriteString("Flags");
18023                     json.BeginArray(true);
18024                     VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
18025                     if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
18026                     {
18027                         json.WriteString("DEVICE_LOCAL");
18028                     }
18029                     if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
18030                     {
18031                         json.WriteString("HOST_VISIBLE");
18032                     }
18033                     if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
18034                     {
18035                         json.WriteString("HOST_COHERENT");
18036                     }
18037                     if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
18038                     {
18039                         json.WriteString("HOST_CACHED");
18040                     }
18041                     if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
18042                     {
18043                         json.WriteString("LAZILY_ALLOCATED");
18044                     }
18045                     if((flags & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0)
18046                     {
18047                         json.WriteString(" PROTECTED");
18048                     }
18049                     if((flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
18050                     {
18051                         json.WriteString(" DEVICE_COHERENT");
18052                     }
18053                     if((flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY) != 0)
18054                     {
18055                         json.WriteString(" DEVICE_UNCACHED");
18056                     }
18057                     json.EndArray();
18058 
18059                     if(stats.memoryType[typeIndex].blockCount > 0)
18060                     {
18061                         json.WriteString("Stats");
18062                         VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
18063                     }
18064 
18065                     json.EndObject();
18066                 }
18067             }
18068 
18069             json.EndObject();
18070         }
18071         if(detailedMap == VK_TRUE)
18072         {
18073             allocator->PrintDetailedMap(json);
18074         }
18075 
18076         json.EndObject();
18077     }
18078 
18079     const size_t len = sb.GetLength();
18080     char* const pChars = vma_new_array(allocator, char, len + 1);
18081     if(len > 0)
18082     {
18083         memcpy(pChars, sb.GetData(), len);
18084     }
18085     pChars[len] = '\0';
18086     *ppStatsString = pChars;
18087 }
18088 
vmaFreeStatsString(VmaAllocator allocator,char * pStatsString)18089 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
18090     VmaAllocator allocator,
18091     char* pStatsString)
18092 {
18093     if(pStatsString != VMA_NULL)
18094     {
18095         VMA_ASSERT(allocator);
18096         size_t len = strlen(pStatsString);
18097         vma_delete_array(allocator, pStatsString, len + 1);
18098     }
18099 }
18100 
18101 #endif // #if VMA_STATS_STRING_ENABLED
18102 
18103 /*
18104 This function is not protected by any mutex because it just reads immutable data.
18105 */
vmaFindMemoryTypeIndex(VmaAllocator allocator,uint32_t memoryTypeBits,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)18106 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
18107     VmaAllocator allocator,
18108     uint32_t memoryTypeBits,
18109     const VmaAllocationCreateInfo* pAllocationCreateInfo,
18110     uint32_t* pMemoryTypeIndex)
18111 {
18112     VMA_ASSERT(allocator != VK_NULL_HANDLE);
18113     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18114     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18115 
18116     memoryTypeBits &= allocator->GetGlobalMemoryTypeBits();
18117 
18118     if(pAllocationCreateInfo->memoryTypeBits != 0)
18119     {
18120         memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
18121     }
18122 
18123     uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
18124     uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
18125     uint32_t notPreferredFlags = 0;
18126 
18127     // Convert usage to requiredFlags and preferredFlags.
18128     switch(pAllocationCreateInfo->usage)
18129     {
18130     case VMA_MEMORY_USAGE_UNKNOWN:
18131         break;
18132     case VMA_MEMORY_USAGE_GPU_ONLY:
18133         if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
18134         {
18135             preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18136         }
18137         break;
18138     case VMA_MEMORY_USAGE_CPU_ONLY:
18139         requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
18140         break;
18141     case VMA_MEMORY_USAGE_CPU_TO_GPU:
18142         requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
18143         if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
18144         {
18145             preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18146         }
18147         break;
18148     case VMA_MEMORY_USAGE_GPU_TO_CPU:
18149         requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
18150         preferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
18151         break;
18152     case VMA_MEMORY_USAGE_CPU_COPY:
18153         notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18154         break;
18155     case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED:
18156         requiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
18157         break;
18158     default:
18159         VMA_ASSERT(0);
18160         break;
18161     }
18162 
18163     // Avoid DEVICE_COHERENT unless explicitly requested.
18164     if(((pAllocationCreateInfo->requiredFlags | pAllocationCreateInfo->preferredFlags) &
18165         (VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0)
18166     {
18167         notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY;
18168     }
18169 
18170     *pMemoryTypeIndex = UINT32_MAX;
18171     uint32_t minCost = UINT32_MAX;
18172     for(uint32_t memTypeIndex = 0, memTypeBit = 1;
18173         memTypeIndex < allocator->GetMemoryTypeCount();
18174         ++memTypeIndex, memTypeBit <<= 1)
18175     {
18176         // This memory type is acceptable according to memoryTypeBits bitmask.
18177         if((memTypeBit & memoryTypeBits) != 0)
18178         {
18179             const VkMemoryPropertyFlags currFlags =
18180                 allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
18181             // This memory type contains requiredFlags.
18182             if((requiredFlags & ~currFlags) == 0)
18183             {
18184                 // Calculate cost as number of bits from preferredFlags not present in this memory type.
18185                 uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags) +
18186                     VmaCountBitsSet(currFlags & notPreferredFlags);
18187                 // Remember memory type with lowest cost.
18188                 if(currCost < minCost)
18189                 {
18190                     *pMemoryTypeIndex = memTypeIndex;
18191                     if(currCost == 0)
18192                     {
18193                         return VK_SUCCESS;
18194                     }
18195                     minCost = currCost;
18196                 }
18197             }
18198         }
18199     }
18200     return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
18201 }
18202 
vmaFindMemoryTypeIndexForBufferInfo(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)18203 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
18204     VmaAllocator allocator,
18205     const VkBufferCreateInfo* pBufferCreateInfo,
18206     const VmaAllocationCreateInfo* pAllocationCreateInfo,
18207     uint32_t* pMemoryTypeIndex)
18208 {
18209     VMA_ASSERT(allocator != VK_NULL_HANDLE);
18210     VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
18211     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18212     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18213 
18214     const VkDevice hDev = allocator->m_hDevice;
18215     VkBuffer hBuffer = VK_NULL_HANDLE;
18216     VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
18217         hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
18218     if(res == VK_SUCCESS)
18219     {
18220         VkMemoryRequirements memReq = {};
18221         allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
18222             hDev, hBuffer, &memReq);
18223 
18224         res = vmaFindMemoryTypeIndex(
18225             allocator,
18226             memReq.memoryTypeBits,
18227             pAllocationCreateInfo,
18228             pMemoryTypeIndex);
18229 
18230         allocator->GetVulkanFunctions().vkDestroyBuffer(
18231             hDev, hBuffer, allocator->GetAllocationCallbacks());
18232     }
18233     return res;
18234 }
18235 
vmaFindMemoryTypeIndexForImageInfo(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)18236 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
18237     VmaAllocator allocator,
18238     const VkImageCreateInfo* pImageCreateInfo,
18239     const VmaAllocationCreateInfo* pAllocationCreateInfo,
18240     uint32_t* pMemoryTypeIndex)
18241 {
18242     VMA_ASSERT(allocator != VK_NULL_HANDLE);
18243     VMA_ASSERT(pImageCreateInfo != VMA_NULL);
18244     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18245     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18246 
18247     const VkDevice hDev = allocator->m_hDevice;
18248     VkImage hImage = VK_NULL_HANDLE;
18249     VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
18250         hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
18251     if(res == VK_SUCCESS)
18252     {
18253         VkMemoryRequirements memReq = {};
18254         allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
18255             hDev, hImage, &memReq);
18256 
18257         res = vmaFindMemoryTypeIndex(
18258             allocator,
18259             memReq.memoryTypeBits,
18260             pAllocationCreateInfo,
18261             pMemoryTypeIndex);
18262 
18263         allocator->GetVulkanFunctions().vkDestroyImage(
18264             hDev, hImage, allocator->GetAllocationCallbacks());
18265     }
18266     return res;
18267 }
18268 
vmaCreatePool(VmaAllocator allocator,const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)18269 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
18270     VmaAllocator allocator,
18271     const VmaPoolCreateInfo* pCreateInfo,
18272     VmaPool* pPool)
18273 {
18274     VMA_ASSERT(allocator && pCreateInfo && pPool);
18275 
18276     VMA_DEBUG_LOG("vmaCreatePool");
18277 
18278     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18279 
18280     VkResult res = allocator->CreatePool(pCreateInfo, pPool);
18281 
18282 #if VMA_RECORDING_ENABLED
18283     if(allocator->GetRecorder() != VMA_NULL)
18284     {
18285         allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
18286     }
18287 #endif
18288 
18289     return res;
18290 }
18291 
vmaDestroyPool(VmaAllocator allocator,VmaPool pool)18292 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
18293     VmaAllocator allocator,
18294     VmaPool pool)
18295 {
18296     VMA_ASSERT(allocator);
18297 
18298     if(pool == VK_NULL_HANDLE)
18299     {
18300         return;
18301     }
18302 
18303     VMA_DEBUG_LOG("vmaDestroyPool");
18304 
18305     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18306 
18307 #if VMA_RECORDING_ENABLED
18308     if(allocator->GetRecorder() != VMA_NULL)
18309     {
18310         allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
18311     }
18312 #endif
18313 
18314     allocator->DestroyPool(pool);
18315 }
18316 
vmaGetPoolStats(VmaAllocator allocator,VmaPool pool,VmaPoolStats * pPoolStats)18317 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
18318     VmaAllocator allocator,
18319     VmaPool pool,
18320     VmaPoolStats* pPoolStats)
18321 {
18322     VMA_ASSERT(allocator && pool && pPoolStats);
18323 
18324     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18325 
18326     allocator->GetPoolStats(pool, pPoolStats);
18327 }
18328 
vmaMakePoolAllocationsLost(VmaAllocator allocator,VmaPool pool,size_t * pLostAllocationCount)18329 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
18330     VmaAllocator allocator,
18331     VmaPool pool,
18332     size_t* pLostAllocationCount)
18333 {
18334     VMA_ASSERT(allocator && pool);
18335 
18336     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18337 
18338 #if VMA_RECORDING_ENABLED
18339     if(allocator->GetRecorder() != VMA_NULL)
18340     {
18341         allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
18342     }
18343 #endif
18344 
18345     allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
18346 }
18347 
vmaCheckPoolCorruption(VmaAllocator allocator,VmaPool pool)18348 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
18349 {
18350     VMA_ASSERT(allocator && pool);
18351 
18352     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18353 
18354     VMA_DEBUG_LOG("vmaCheckPoolCorruption");
18355 
18356     return allocator->CheckPoolCorruption(pool);
18357 }
18358 
vmaGetPoolName(VmaAllocator allocator,VmaPool pool,const char ** ppName)18359 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
18360     VmaAllocator allocator,
18361     VmaPool pool,
18362     const char** ppName)
18363 {
18364     VMA_ASSERT(allocator && pool && ppName);
18365 
18366     VMA_DEBUG_LOG("vmaGetPoolName");
18367 
18368     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18369 
18370     *ppName = pool->GetName();
18371 }
18372 
vmaSetPoolName(VmaAllocator allocator,VmaPool pool,const char * pName)18373 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
18374     VmaAllocator allocator,
18375     VmaPool pool,
18376     const char* pName)
18377 {
18378     VMA_ASSERT(allocator && pool);
18379 
18380     VMA_DEBUG_LOG("vmaSetPoolName");
18381 
18382     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18383 
18384     pool->SetName(pName);
18385 
18386 #if VMA_RECORDING_ENABLED
18387     if(allocator->GetRecorder() != VMA_NULL)
18388     {
18389         allocator->GetRecorder()->RecordSetPoolName(allocator->GetCurrentFrameIndex(), pool, pName);
18390     }
18391 #endif
18392 }
18393 
vmaAllocateMemory(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18394 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
18395     VmaAllocator allocator,
18396     const VkMemoryRequirements* pVkMemoryRequirements,
18397     const VmaAllocationCreateInfo* pCreateInfo,
18398     VmaAllocation* pAllocation,
18399     VmaAllocationInfo* pAllocationInfo)
18400 {
18401     VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
18402 
18403     VMA_DEBUG_LOG("vmaAllocateMemory");
18404 
18405     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18406 
18407     VkResult result = allocator->AllocateMemory(
18408         *pVkMemoryRequirements,
18409         false, // requiresDedicatedAllocation
18410         false, // prefersDedicatedAllocation
18411         VK_NULL_HANDLE, // dedicatedBuffer
18412         UINT32_MAX, // dedicatedBufferUsage
18413         VK_NULL_HANDLE, // dedicatedImage
18414         *pCreateInfo,
18415         VMA_SUBALLOCATION_TYPE_UNKNOWN,
18416         1, // allocationCount
18417         pAllocation);
18418 
18419 #if VMA_RECORDING_ENABLED
18420     if(allocator->GetRecorder() != VMA_NULL)
18421     {
18422         allocator->GetRecorder()->RecordAllocateMemory(
18423             allocator->GetCurrentFrameIndex(),
18424             *pVkMemoryRequirements,
18425             *pCreateInfo,
18426             *pAllocation);
18427     }
18428 #endif
18429 
18430     if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18431     {
18432         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18433     }
18434 
18435     return result;
18436 }
18437 
vmaAllocateMemoryPages(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,size_t allocationCount,VmaAllocation * pAllocations,VmaAllocationInfo * pAllocationInfo)18438 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
18439     VmaAllocator allocator,
18440     const VkMemoryRequirements* pVkMemoryRequirements,
18441     const VmaAllocationCreateInfo* pCreateInfo,
18442     size_t allocationCount,
18443     VmaAllocation* pAllocations,
18444     VmaAllocationInfo* pAllocationInfo)
18445 {
18446     if(allocationCount == 0)
18447     {
18448         return VK_SUCCESS;
18449     }
18450 
18451     VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
18452 
18453     VMA_DEBUG_LOG("vmaAllocateMemoryPages");
18454 
18455     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18456 
18457     VkResult result = allocator->AllocateMemory(
18458         *pVkMemoryRequirements,
18459         false, // requiresDedicatedAllocation
18460         false, // prefersDedicatedAllocation
18461         VK_NULL_HANDLE, // dedicatedBuffer
18462         UINT32_MAX, // dedicatedBufferUsage
18463         VK_NULL_HANDLE, // dedicatedImage
18464         *pCreateInfo,
18465         VMA_SUBALLOCATION_TYPE_UNKNOWN,
18466         allocationCount,
18467         pAllocations);
18468 
18469 #if VMA_RECORDING_ENABLED
18470     if(allocator->GetRecorder() != VMA_NULL)
18471     {
18472         allocator->GetRecorder()->RecordAllocateMemoryPages(
18473             allocator->GetCurrentFrameIndex(),
18474             *pVkMemoryRequirements,
18475             *pCreateInfo,
18476             (uint64_t)allocationCount,
18477             pAllocations);
18478     }
18479 #endif
18480 
18481     if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18482     {
18483         for(size_t i = 0; i < allocationCount; ++i)
18484         {
18485             allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
18486         }
18487     }
18488 
18489     return result;
18490 }
18491 
vmaAllocateMemoryForBuffer(VmaAllocator allocator,VkBuffer buffer,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18492 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
18493     VmaAllocator allocator,
18494     VkBuffer buffer,
18495     const VmaAllocationCreateInfo* pCreateInfo,
18496     VmaAllocation* pAllocation,
18497     VmaAllocationInfo* pAllocationInfo)
18498 {
18499     VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18500 
18501     VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
18502 
18503     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18504 
18505     VkMemoryRequirements vkMemReq = {};
18506     bool requiresDedicatedAllocation = false;
18507     bool prefersDedicatedAllocation = false;
18508     allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
18509         requiresDedicatedAllocation,
18510         prefersDedicatedAllocation);
18511 
18512     VkResult result = allocator->AllocateMemory(
18513         vkMemReq,
18514         requiresDedicatedAllocation,
18515         prefersDedicatedAllocation,
18516         buffer, // dedicatedBuffer
18517         UINT32_MAX, // dedicatedBufferUsage
18518         VK_NULL_HANDLE, // dedicatedImage
18519         *pCreateInfo,
18520         VMA_SUBALLOCATION_TYPE_BUFFER,
18521         1, // allocationCount
18522         pAllocation);
18523 
18524 #if VMA_RECORDING_ENABLED
18525     if(allocator->GetRecorder() != VMA_NULL)
18526     {
18527         allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
18528             allocator->GetCurrentFrameIndex(),
18529             vkMemReq,
18530             requiresDedicatedAllocation,
18531             prefersDedicatedAllocation,
18532             *pCreateInfo,
18533             *pAllocation);
18534     }
18535 #endif
18536 
18537     if(pAllocationInfo && result == VK_SUCCESS)
18538     {
18539         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18540     }
18541 
18542     return result;
18543 }
18544 
vmaAllocateMemoryForImage(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18545 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
18546     VmaAllocator allocator,
18547     VkImage image,
18548     const VmaAllocationCreateInfo* pCreateInfo,
18549     VmaAllocation* pAllocation,
18550     VmaAllocationInfo* pAllocationInfo)
18551 {
18552     VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18553 
18554     VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
18555 
18556     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18557 
18558     VkMemoryRequirements vkMemReq = {};
18559     bool requiresDedicatedAllocation = false;
18560     bool prefersDedicatedAllocation  = false;
18561     allocator->GetImageMemoryRequirements(image, vkMemReq,
18562         requiresDedicatedAllocation, prefersDedicatedAllocation);
18563 
18564     VkResult result = allocator->AllocateMemory(
18565         vkMemReq,
18566         requiresDedicatedAllocation,
18567         prefersDedicatedAllocation,
18568         VK_NULL_HANDLE, // dedicatedBuffer
18569         UINT32_MAX, // dedicatedBufferUsage
18570         image, // dedicatedImage
18571         *pCreateInfo,
18572         VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
18573         1, // allocationCount
18574         pAllocation);
18575 
18576 #if VMA_RECORDING_ENABLED
18577     if(allocator->GetRecorder() != VMA_NULL)
18578     {
18579         allocator->GetRecorder()->RecordAllocateMemoryForImage(
18580             allocator->GetCurrentFrameIndex(),
18581             vkMemReq,
18582             requiresDedicatedAllocation,
18583             prefersDedicatedAllocation,
18584             *pCreateInfo,
18585             *pAllocation);
18586     }
18587 #endif
18588 
18589     if(pAllocationInfo && result == VK_SUCCESS)
18590     {
18591         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18592     }
18593 
18594     return result;
18595 }
18596 
vmaFreeMemory(VmaAllocator allocator,VmaAllocation allocation)18597 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
18598     VmaAllocator allocator,
18599     VmaAllocation allocation)
18600 {
18601     VMA_ASSERT(allocator);
18602 
18603     if(allocation == VK_NULL_HANDLE)
18604     {
18605         return;
18606     }
18607 
18608     VMA_DEBUG_LOG("vmaFreeMemory");
18609 
18610     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18611 
18612 #if VMA_RECORDING_ENABLED
18613     if(allocator->GetRecorder() != VMA_NULL)
18614     {
18615         allocator->GetRecorder()->RecordFreeMemory(
18616             allocator->GetCurrentFrameIndex(),
18617             allocation);
18618     }
18619 #endif
18620 
18621     allocator->FreeMemory(
18622         1, // allocationCount
18623         &allocation);
18624 }
18625 
vmaFreeMemoryPages(VmaAllocator allocator,size_t allocationCount,const VmaAllocation * pAllocations)18626 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
18627     VmaAllocator allocator,
18628     size_t allocationCount,
18629     const VmaAllocation* pAllocations)
18630 {
18631     if(allocationCount == 0)
18632     {
18633         return;
18634     }
18635 
18636     VMA_ASSERT(allocator);
18637 
18638     VMA_DEBUG_LOG("vmaFreeMemoryPages");
18639 
18640     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18641 
18642 #if VMA_RECORDING_ENABLED
18643     if(allocator->GetRecorder() != VMA_NULL)
18644     {
18645         allocator->GetRecorder()->RecordFreeMemoryPages(
18646             allocator->GetCurrentFrameIndex(),
18647             (uint64_t)allocationCount,
18648             pAllocations);
18649     }
18650 #endif
18651 
18652     allocator->FreeMemory(allocationCount, pAllocations);
18653 }
18654 
18655 // OH ISSUE: VMA preAlloc
vmaAllocateReservedMemoryForImage(VmaAllocator VMA_NOT_NULL allocator,VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,const VmaAllocationCreateInfo * VMA_NOT_NULL pCreateInfo,VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,VmaAllocationInfo * VMA_NULLABLE pAllocationInfo)18656 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateReservedMemoryForImage(
18657     VmaAllocator VMA_NOT_NULL allocator,
18658     VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
18659     const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
18660     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
18661     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo)
18662 {
18663     VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18664     VMA_DEBUG_LOG("vmaAllocateReservedMemoryForImage");
18665     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18666 
18667     VkMemoryRequirements vkMemReq = {};
18668     bool requiresDedicatedAllocation = false;
18669     bool prefersDedicatedAllocation  = false;
18670     allocator->GetImageMemoryRequirements(image, vkMemReq,
18671         requiresDedicatedAllocation, prefersDedicatedAllocation);
18672 
18673     VkResult result = allocator->AllocateReservedMemory(
18674         vkMemReq,
18675         requiresDedicatedAllocation,
18676         prefersDedicatedAllocation,
18677         VK_NULL_HANDLE, // dedicatedBuffer
18678         UINT32_MAX, // dedicatedBufferUsage
18679         image, // dedicatedImage
18680         *pCreateInfo,
18681         VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
18682         1, // allocationCount
18683         pAllocation);
18684 
18685     if(pAllocationInfo && result == VK_SUCCESS)
18686     {
18687         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18688     }
18689 
18690     return result;
18691 }
18692 
vmaGetNewBlockStats(VmaAllocation allocation,bool * pStats)18693 VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetNewBlockStats(
18694     VmaAllocation allocation,
18695     bool* pStats)
18696 {
18697     VMA_ASSERT(allocation);
18698     VMA_DEBUG_LOG("vmaGetNewBlockStats");
18699     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18700 
18701     if (pStats != NULL) {
18702         *pStats = allocation->IsNewBlockFlag();
18703     }
18704     return VK_SUCCESS;
18705 }
18706 
vmaClearNewBlockStats(VmaAllocation allocation)18707 VMA_CALL_PRE VkResult VMA_CALL_POST vmaClearNewBlockStats(
18708     VmaAllocation allocation)
18709 {
18710     VMA_ASSERT(allocation);
18711     VMA_DEBUG_LOG("vmaClearNewBlockStats");
18712     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18713 
18714     allocation->ClearNewBlockFlag();
18715     return VK_SUCCESS;
18716 }
18717 
vmaSwapReservedBlock(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18718 VMA_CALL_PRE VkResult VMA_CALL_POST vmaSwapReservedBlock(
18719     VmaAllocator allocator,
18720     VkImage image,
18721     const VmaAllocationCreateInfo* pCreateInfo,
18722     VmaAllocation* pAllocation,
18723     VmaAllocationInfo* pAllocationInfo)
18724 {
18725     VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18726     VMA_DEBUG_LOG("vmaSwapReservedBlock");
18727     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18728 
18729     return allocator->SwapReservedBlock(image, pCreateInfo, pAllocation, pAllocationInfo);
18730 }
18731 
vmaFreeReservedMemory(VmaAllocator allocator,VmaAllocation allocation)18732 VMA_CALL_PRE void VMA_CALL_POST vmaFreeReservedMemory(
18733     VmaAllocator allocator,
18734     VmaAllocation allocation)
18735 {
18736     VMA_ASSERT(allocator);
18737     if (allocation == VK_NULL_HANDLE)
18738     {
18739         return;
18740     }
18741     VMA_DEBUG_LOG("vmaFreeReservedMemory");
18742     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18743 
18744     allocator->FreeReservedMemory(
18745         1, // allocationCount
18746         &allocation);
18747 }
18748 
vmaGetPreAllocBlockSize(VmaAllocator allocator,uint32_t * pStats)18749 VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetPreAllocBlockSize(VmaAllocator allocator, uint32_t* pStats)
18750 {
18751     VMA_ASSERT(allocator);
18752     VMA_DEBUG_LOG("vmaGetPreAllocBlockSize");
18753 
18754     if (pStats != NULL) {
18755         *pStats = allocator->GetPreAllocBlockSize();
18756     }
18757     return VK_SUCCESS;
18758 }
18759 
vmaResizeAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize newSize)18760 VMA_CALL_PRE VkResult VMA_CALL_POST vmaResizeAllocation(
18761     VmaAllocator allocator,
18762     VmaAllocation allocation,
18763     VkDeviceSize newSize)
18764 {
18765     VMA_ASSERT(allocator && allocation);
18766 
18767     VMA_DEBUG_LOG("vmaResizeAllocation");
18768 
18769     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18770 
18771     return allocator->ResizeAllocation(allocation, newSize);
18772 }
18773 
vmaGetAllocationInfo(VmaAllocator allocator,VmaAllocation allocation,VmaAllocationInfo * pAllocationInfo)18774 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
18775     VmaAllocator allocator,
18776     VmaAllocation allocation,
18777     VmaAllocationInfo* pAllocationInfo)
18778 {
18779     VMA_ASSERT(allocator && allocation && pAllocationInfo);
18780 
18781     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18782 
18783 #if VMA_RECORDING_ENABLED
18784     if(allocator->GetRecorder() != VMA_NULL)
18785     {
18786         allocator->GetRecorder()->RecordGetAllocationInfo(
18787             allocator->GetCurrentFrameIndex(),
18788             allocation);
18789     }
18790 #endif
18791 
18792     allocator->GetAllocationInfo(allocation, pAllocationInfo);
18793 }
18794 
vmaTouchAllocation(VmaAllocator allocator,VmaAllocation allocation)18795 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
18796     VmaAllocator allocator,
18797     VmaAllocation allocation)
18798 {
18799     VMA_ASSERT(allocator && allocation);
18800 
18801     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18802 
18803 #if VMA_RECORDING_ENABLED
18804     if(allocator->GetRecorder() != VMA_NULL)
18805     {
18806         allocator->GetRecorder()->RecordTouchAllocation(
18807             allocator->GetCurrentFrameIndex(),
18808             allocation);
18809     }
18810 #endif
18811 
18812     return allocator->TouchAllocation(allocation);
18813 }
18814 
vmaSetAllocationUserData(VmaAllocator allocator,VmaAllocation allocation,void * pUserData)18815 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
18816     VmaAllocator allocator,
18817     VmaAllocation allocation,
18818     void* pUserData)
18819 {
18820     VMA_ASSERT(allocator && allocation);
18821 
18822     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18823 
18824     allocation->SetUserData(allocator, pUserData);
18825 
18826 #if VMA_RECORDING_ENABLED
18827     if(allocator->GetRecorder() != VMA_NULL)
18828     {
18829         allocator->GetRecorder()->RecordSetAllocationUserData(
18830             allocator->GetCurrentFrameIndex(),
18831             allocation,
18832             pUserData);
18833     }
18834 #endif
18835 }
18836 
vmaCreateLostAllocation(VmaAllocator allocator,VmaAllocation * pAllocation)18837 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
18838     VmaAllocator allocator,
18839     VmaAllocation* pAllocation)
18840 {
18841     VMA_ASSERT(allocator && pAllocation);
18842 
18843     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18844 
18845     allocator->CreateLostAllocation(pAllocation);
18846 
18847 #if VMA_RECORDING_ENABLED
18848     if(allocator->GetRecorder() != VMA_NULL)
18849     {
18850         allocator->GetRecorder()->RecordCreateLostAllocation(
18851             allocator->GetCurrentFrameIndex(),
18852             *pAllocation);
18853     }
18854 #endif
18855 }
18856 
vmaMapMemory(VmaAllocator allocator,VmaAllocation allocation,void ** ppData)18857 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
18858     VmaAllocator allocator,
18859     VmaAllocation allocation,
18860     void** ppData)
18861 {
18862     VMA_ASSERT(allocator && allocation && ppData);
18863 
18864     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18865 
18866     VkResult res = allocator->Map(allocation, ppData);
18867 
18868 #if VMA_RECORDING_ENABLED
18869     if(allocator->GetRecorder() != VMA_NULL)
18870     {
18871         allocator->GetRecorder()->RecordMapMemory(
18872             allocator->GetCurrentFrameIndex(),
18873             allocation);
18874     }
18875 #endif
18876 
18877     return res;
18878 }
18879 
vmaUnmapMemory(VmaAllocator allocator,VmaAllocation allocation)18880 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
18881     VmaAllocator allocator,
18882     VmaAllocation allocation)
18883 {
18884     VMA_ASSERT(allocator && allocation);
18885 
18886     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18887 
18888 #if VMA_RECORDING_ENABLED
18889     if(allocator->GetRecorder() != VMA_NULL)
18890     {
18891         allocator->GetRecorder()->RecordUnmapMemory(
18892             allocator->GetCurrentFrameIndex(),
18893             allocation);
18894     }
18895 #endif
18896 
18897     allocator->Unmap(allocation);
18898 }
18899 
vmaFlushAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)18900 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18901 {
18902     VMA_ASSERT(allocator && allocation);
18903 
18904     VMA_DEBUG_LOG("vmaFlushAllocation");
18905 
18906     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18907 
18908     const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
18909 
18910 #if VMA_RECORDING_ENABLED
18911     if(allocator->GetRecorder() != VMA_NULL)
18912     {
18913         allocator->GetRecorder()->RecordFlushAllocation(
18914             allocator->GetCurrentFrameIndex(),
18915             allocation, offset, size);
18916     }
18917 #endif
18918 
18919     return res;
18920 }
18921 
vmaInvalidateAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)18922 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18923 {
18924     VMA_ASSERT(allocator && allocation);
18925 
18926     VMA_DEBUG_LOG("vmaInvalidateAllocation");
18927 
18928     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18929 
18930     const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
18931 
18932 #if VMA_RECORDING_ENABLED
18933     if(allocator->GetRecorder() != VMA_NULL)
18934     {
18935         allocator->GetRecorder()->RecordInvalidateAllocation(
18936             allocator->GetCurrentFrameIndex(),
18937             allocation, offset, size);
18938     }
18939 #endif
18940 
18941     return res;
18942 }
18943 
vmaFlushAllocations(VmaAllocator allocator,uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes)18944 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
18945     VmaAllocator allocator,
18946     uint32_t allocationCount,
18947     const VmaAllocation* allocations,
18948     const VkDeviceSize* offsets,
18949     const VkDeviceSize* sizes)
18950 {
18951     VMA_ASSERT(allocator);
18952 
18953     if(allocationCount == 0)
18954     {
18955         return VK_SUCCESS;
18956     }
18957 
18958     VMA_ASSERT(allocations);
18959 
18960     VMA_DEBUG_LOG("vmaFlushAllocations");
18961 
18962     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18963 
18964     const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH);
18965 
18966 #if VMA_RECORDING_ENABLED
18967     if(allocator->GetRecorder() != VMA_NULL)
18968     {
18969         //TODO
18970     }
18971 #endif
18972 
18973     return res;
18974 }
18975 
vmaInvalidateAllocations(VmaAllocator allocator,uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes)18976 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
18977     VmaAllocator allocator,
18978     uint32_t allocationCount,
18979     const VmaAllocation* allocations,
18980     const VkDeviceSize* offsets,
18981     const VkDeviceSize* sizes)
18982 {
18983     VMA_ASSERT(allocator);
18984 
18985     if(allocationCount == 0)
18986     {
18987         return VK_SUCCESS;
18988     }
18989 
18990     VMA_ASSERT(allocations);
18991 
18992     VMA_DEBUG_LOG("vmaInvalidateAllocations");
18993 
18994     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18995 
18996     const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE);
18997 
18998 #if VMA_RECORDING_ENABLED
18999     if(allocator->GetRecorder() != VMA_NULL)
19000     {
19001         //TODO
19002     }
19003 #endif
19004 
19005     return res;
19006 }
19007 
vmaCheckCorruption(VmaAllocator allocator,uint32_t memoryTypeBits)19008 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
19009 {
19010     VMA_ASSERT(allocator);
19011 
19012     VMA_DEBUG_LOG("vmaCheckCorruption");
19013 
19014     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19015 
19016     return allocator->CheckCorruption(memoryTypeBits);
19017 }
19018 
vmaDefragment(VmaAllocator allocator,const VmaAllocation * pAllocations,size_t allocationCount,VkBool32 * pAllocationsChanged,const VmaDefragmentationInfo * pDefragmentationInfo,VmaDefragmentationStats * pDefragmentationStats)19019 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
19020     VmaAllocator allocator,
19021     const VmaAllocation* pAllocations,
19022     size_t allocationCount,
19023     VkBool32* pAllocationsChanged,
19024     const VmaDefragmentationInfo *pDefragmentationInfo,
19025     VmaDefragmentationStats* pDefragmentationStats)
19026 {
19027     // Deprecated interface, reimplemented using new one.
19028 
19029     VmaDefragmentationInfo2 info2 = {};
19030     info2.allocationCount = (uint32_t)allocationCount;
19031     info2.pAllocations = pAllocations;
19032     info2.pAllocationsChanged = pAllocationsChanged;
19033     if(pDefragmentationInfo != VMA_NULL)
19034     {
19035         info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
19036         info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
19037     }
19038     else
19039     {
19040         info2.maxCpuAllocationsToMove = UINT32_MAX;
19041         info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
19042     }
19043     // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
19044 
19045     VmaDefragmentationContext ctx;
19046     VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
19047     if(res == VK_NOT_READY)
19048     {
19049         res = vmaDefragmentationEnd( allocator, ctx);
19050     }
19051     return res;
19052 }
19053 
vmaDefragmentationBegin(VmaAllocator allocator,const VmaDefragmentationInfo2 * pInfo,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)19054 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
19055     VmaAllocator allocator,
19056     const VmaDefragmentationInfo2* pInfo,
19057     VmaDefragmentationStats* pStats,
19058     VmaDefragmentationContext *pContext)
19059 {
19060     VMA_ASSERT(allocator && pInfo && pContext);
19061 
19062     // Degenerate case: Nothing to defragment.
19063     if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
19064     {
19065         return VK_SUCCESS;
19066     }
19067 
19068     VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
19069     VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
19070     VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
19071     VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
19072 
19073     VMA_DEBUG_LOG("vmaDefragmentationBegin");
19074 
19075     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19076 
19077     VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
19078 
19079 #if VMA_RECORDING_ENABLED
19080     if(allocator->GetRecorder() != VMA_NULL)
19081     {
19082         allocator->GetRecorder()->RecordDefragmentationBegin(
19083             allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
19084     }
19085 #endif
19086 
19087     return res;
19088 }
19089 
vmaDefragmentationEnd(VmaAllocator allocator,VmaDefragmentationContext context)19090 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
19091     VmaAllocator allocator,
19092     VmaDefragmentationContext context)
19093 {
19094     VMA_ASSERT(allocator);
19095 
19096     VMA_DEBUG_LOG("vmaDefragmentationEnd");
19097 
19098     if(context != VK_NULL_HANDLE)
19099     {
19100         VMA_DEBUG_GLOBAL_MUTEX_LOCK
19101 
19102 #if VMA_RECORDING_ENABLED
19103         if(allocator->GetRecorder() != VMA_NULL)
19104         {
19105             allocator->GetRecorder()->RecordDefragmentationEnd(
19106                 allocator->GetCurrentFrameIndex(), context);
19107         }
19108 #endif
19109 
19110         return allocator->DefragmentationEnd(context);
19111     }
19112     else
19113     {
19114         return VK_SUCCESS;
19115     }
19116 }
19117 
vmaBeginDefragmentationPass(VmaAllocator allocator,VmaDefragmentationContext context,VmaDefragmentationPassInfo * pInfo)19118 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
19119     VmaAllocator allocator,
19120     VmaDefragmentationContext context,
19121     VmaDefragmentationPassInfo* pInfo
19122     )
19123 {
19124     VMA_ASSERT(allocator);
19125     VMA_ASSERT(pInfo);
19126     VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->moveCount, pInfo->pMoves));
19127 
19128     VMA_DEBUG_LOG("vmaBeginDefragmentationPass");
19129 
19130     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19131 
19132     if(context == VK_NULL_HANDLE)
19133     {
19134         pInfo->moveCount = 0;
19135         return VK_SUCCESS;
19136     }
19137 
19138     return allocator->DefragmentationPassBegin(pInfo, context);
19139 }
vmaEndDefragmentationPass(VmaAllocator allocator,VmaDefragmentationContext context)19140 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
19141     VmaAllocator allocator,
19142     VmaDefragmentationContext context)
19143 {
19144     VMA_ASSERT(allocator);
19145 
19146     VMA_DEBUG_LOG("vmaEndDefragmentationPass");
19147     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19148 
19149     if(context == VK_NULL_HANDLE)
19150         return VK_SUCCESS;
19151 
19152     return allocator->DefragmentationPassEnd(context);
19153 }
19154 
vmaBindBufferMemory(VmaAllocator allocator,VmaAllocation allocation,VkBuffer buffer)19155 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
19156     VmaAllocator allocator,
19157     VmaAllocation allocation,
19158     VkBuffer buffer)
19159 {
19160     VMA_ASSERT(allocator && allocation && buffer);
19161 
19162     VMA_DEBUG_LOG("vmaBindBufferMemory");
19163 
19164     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19165 
19166     return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL);
19167 }
19168 
vmaBindBufferMemory2(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize allocationLocalOffset,VkBuffer buffer,const void * pNext)19169 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
19170     VmaAllocator allocator,
19171     VmaAllocation allocation,
19172     VkDeviceSize allocationLocalOffset,
19173     VkBuffer buffer,
19174     const void* pNext)
19175 {
19176     VMA_ASSERT(allocator && allocation && buffer);
19177 
19178     VMA_DEBUG_LOG("vmaBindBufferMemory2");
19179 
19180     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19181 
19182     return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext);
19183 }
19184 
vmaBindImageMemory(VmaAllocator allocator,VmaAllocation allocation,VkImage image)19185 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
19186     VmaAllocator allocator,
19187     VmaAllocation allocation,
19188     VkImage image)
19189 {
19190     VMA_ASSERT(allocator && allocation && image);
19191 
19192     VMA_DEBUG_LOG("vmaBindImageMemory");
19193 
19194     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19195 
19196     return allocator->BindImageMemory(allocation, 0, image, VMA_NULL);
19197 }
19198 
vmaBindImageMemory2(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize allocationLocalOffset,VkImage image,const void * pNext)19199 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
19200     VmaAllocator allocator,
19201     VmaAllocation allocation,
19202     VkDeviceSize allocationLocalOffset,
19203     VkImage image,
19204     const void* pNext)
19205 {
19206     VMA_ASSERT(allocator && allocation && image);
19207 
19208     VMA_DEBUG_LOG("vmaBindImageMemory2");
19209 
19210     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19211 
19212         return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext);
19213 }
19214 
vmaCreateBuffer(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkBuffer * pBuffer,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)19215 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
19216     VmaAllocator allocator,
19217     const VkBufferCreateInfo* pBufferCreateInfo,
19218     const VmaAllocationCreateInfo* pAllocationCreateInfo,
19219     VkBuffer* pBuffer,
19220     VmaAllocation* pAllocation,
19221     VmaAllocationInfo* pAllocationInfo)
19222 {
19223     VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
19224 
19225     if(pBufferCreateInfo->size == 0)
19226     {
19227         return VK_ERROR_VALIDATION_FAILED_EXT;
19228     }
19229     if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
19230         !allocator->m_UseKhrBufferDeviceAddress)
19231     {
19232         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.");
19233         return VK_ERROR_VALIDATION_FAILED_EXT;
19234     }
19235 
19236     VMA_DEBUG_LOG("vmaCreateBuffer");
19237 
19238     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19239 
19240     *pBuffer = VK_NULL_HANDLE;
19241     *pAllocation = VK_NULL_HANDLE;
19242 
19243     // 1. Create VkBuffer.
19244     VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
19245         allocator->m_hDevice,
19246         pBufferCreateInfo,
19247         allocator->GetAllocationCallbacks(),
19248         pBuffer);
19249     if(res >= 0)
19250     {
19251         // 2. vkGetBufferMemoryRequirements.
19252         VkMemoryRequirements vkMemReq = {};
19253         bool requiresDedicatedAllocation = false;
19254         bool prefersDedicatedAllocation  = false;
19255         allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
19256             requiresDedicatedAllocation, prefersDedicatedAllocation);
19257 
19258         // 3. Allocate memory using allocator.
19259         res = allocator->AllocateMemory(
19260             vkMemReq,
19261             requiresDedicatedAllocation,
19262             prefersDedicatedAllocation,
19263             *pBuffer, // dedicatedBuffer
19264             pBufferCreateInfo->usage, // dedicatedBufferUsage
19265             VK_NULL_HANDLE, // dedicatedImage
19266             *pAllocationCreateInfo,
19267             VMA_SUBALLOCATION_TYPE_BUFFER,
19268             1, // allocationCount
19269             pAllocation);
19270 
19271 #if VMA_RECORDING_ENABLED
19272         if(allocator->GetRecorder() != VMA_NULL)
19273         {
19274             allocator->GetRecorder()->RecordCreateBuffer(
19275                 allocator->GetCurrentFrameIndex(),
19276                 *pBufferCreateInfo,
19277                 *pAllocationCreateInfo,
19278                 *pAllocation);
19279         }
19280 #endif
19281 
19282         if(res >= 0)
19283         {
19284             // 3. Bind buffer with memory.
19285             if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
19286             {
19287                 res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
19288             }
19289             if(res >= 0)
19290             {
19291                 // All steps succeeded.
19292                 #if VMA_STATS_STRING_ENABLED
19293                     (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
19294                 #endif
19295                 if(pAllocationInfo != VMA_NULL)
19296                 {
19297                     allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
19298                 }
19299 
19300                 return VK_SUCCESS;
19301             }
19302             allocator->FreeMemory(
19303                 1, // allocationCount
19304                 pAllocation);
19305             *pAllocation = VK_NULL_HANDLE;
19306             (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
19307             *pBuffer = VK_NULL_HANDLE;
19308             return res;
19309         }
19310         (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
19311         *pBuffer = VK_NULL_HANDLE;
19312         return res;
19313     }
19314     return res;
19315 }
19316 
vmaDestroyBuffer(VmaAllocator allocator,VkBuffer buffer,VmaAllocation allocation)19317 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
19318     VmaAllocator allocator,
19319     VkBuffer buffer,
19320     VmaAllocation allocation)
19321 {
19322     VMA_ASSERT(allocator);
19323 
19324     if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
19325     {
19326         return;
19327     }
19328 
19329     VMA_DEBUG_LOG("vmaDestroyBuffer");
19330 
19331     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19332 
19333 #if VMA_RECORDING_ENABLED
19334     if(allocator->GetRecorder() != VMA_NULL)
19335     {
19336         allocator->GetRecorder()->RecordDestroyBuffer(
19337             allocator->GetCurrentFrameIndex(),
19338             allocation);
19339     }
19340 #endif
19341 
19342     if(buffer != VK_NULL_HANDLE)
19343     {
19344         (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
19345     }
19346 
19347     if(allocation != VK_NULL_HANDLE)
19348     {
19349         allocator->FreeMemory(
19350             1, // allocationCount
19351             &allocation);
19352     }
19353 }
19354 
vmaCreateImage(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkImage * pImage,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)19355 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
19356     VmaAllocator allocator,
19357     const VkImageCreateInfo* pImageCreateInfo,
19358     const VmaAllocationCreateInfo* pAllocationCreateInfo,
19359     VkImage* pImage,
19360     VmaAllocation* pAllocation,
19361     VmaAllocationInfo* pAllocationInfo)
19362 {
19363     VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
19364 
19365     if(pImageCreateInfo->extent.width == 0 ||
19366         pImageCreateInfo->extent.height == 0 ||
19367         pImageCreateInfo->extent.depth == 0 ||
19368         pImageCreateInfo->mipLevels == 0 ||
19369         pImageCreateInfo->arrayLayers == 0)
19370     {
19371         return VK_ERROR_VALIDATION_FAILED_EXT;
19372     }
19373 
19374     VMA_DEBUG_LOG("vmaCreateImage");
19375 
19376     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19377 
19378     *pImage = VK_NULL_HANDLE;
19379     *pAllocation = VK_NULL_HANDLE;
19380 
19381     // 1. Create VkImage.
19382     VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
19383         allocator->m_hDevice,
19384         pImageCreateInfo,
19385         allocator->GetAllocationCallbacks(),
19386         pImage);
19387     if(res >= 0)
19388     {
19389         VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
19390             VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
19391             VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
19392 
19393         // 2. Allocate memory using allocator.
19394         VkMemoryRequirements vkMemReq = {};
19395         bool requiresDedicatedAllocation = false;
19396         bool prefersDedicatedAllocation  = false;
19397         allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
19398             requiresDedicatedAllocation, prefersDedicatedAllocation);
19399 
19400         res = allocator->AllocateMemory(
19401             vkMemReq,
19402             requiresDedicatedAllocation,
19403             prefersDedicatedAllocation,
19404             VK_NULL_HANDLE, // dedicatedBuffer
19405             UINT32_MAX, // dedicatedBufferUsage
19406             *pImage, // dedicatedImage
19407             *pAllocationCreateInfo,
19408             suballocType,
19409             1, // allocationCount
19410             pAllocation);
19411 
19412 #if VMA_RECORDING_ENABLED
19413         if(allocator->GetRecorder() != VMA_NULL)
19414         {
19415             allocator->GetRecorder()->RecordCreateImage(
19416                 allocator->GetCurrentFrameIndex(),
19417                 *pImageCreateInfo,
19418                 *pAllocationCreateInfo,
19419                 *pAllocation);
19420         }
19421 #endif
19422 
19423         if(res >= 0)
19424         {
19425             // 3. Bind image with memory.
19426             if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
19427             {
19428                 res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);
19429             }
19430             if(res >= 0)
19431             {
19432                 // All steps succeeded.
19433                 #if VMA_STATS_STRING_ENABLED
19434                     (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
19435                 #endif
19436                 if(pAllocationInfo != VMA_NULL)
19437                 {
19438                     allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
19439                 }
19440 
19441                 return VK_SUCCESS;
19442             }
19443             allocator->FreeMemory(
19444                 1, // allocationCount
19445                 pAllocation);
19446             *pAllocation = VK_NULL_HANDLE;
19447             (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
19448             *pImage = VK_NULL_HANDLE;
19449             return res;
19450         }
19451         (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
19452         *pImage = VK_NULL_HANDLE;
19453         return res;
19454     }
19455     return res;
19456 }
19457 
vmaDestroyImage(VmaAllocator allocator,VkImage image,VmaAllocation allocation)19458 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
19459     VmaAllocator allocator,
19460     VkImage image,
19461     VmaAllocation allocation)
19462 {
19463     VMA_ASSERT(allocator);
19464 
19465     if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
19466     {
19467         return;
19468     }
19469 
19470     VMA_DEBUG_LOG("vmaDestroyImage");
19471 
19472     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19473 
19474 #if VMA_RECORDING_ENABLED
19475     if(allocator->GetRecorder() != VMA_NULL)
19476     {
19477         allocator->GetRecorder()->RecordDestroyImage(
19478             allocator->GetCurrentFrameIndex(),
19479             allocation);
19480     }
19481 #endif
19482 
19483     if(image != VK_NULL_HANDLE)
19484     {
19485         (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
19486     }
19487     if(allocation != VK_NULL_HANDLE)
19488     {
19489         allocator->FreeMemory(
19490             1, // allocationCount
19491             &allocation);
19492     }
19493 }
19494 
19495 // OH ISSUE: VMA preAlloc
vmaCreateFakeImage(VmaAllocator allocator,VkImage * pImage)19496 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateFakeImage(
19497     VmaAllocator allocator,
19498     VkImage* pImage)
19499 {
19500     VMA_ASSERT(allocator && pImage);
19501     VMA_DEBUG_LOG("vmaCreateFakeImage");
19502     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19503 
19504     *pImage = VK_NULL_HANDLE;
19505     const VkImageCreateInfo imageCreateInfo = {
19506         VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,         // sType
19507         nullptr,                                     // pNext
19508         0,                                           // VkImageCreateFlags
19509         VK_IMAGE_TYPE_2D,                            // VkImageType
19510         VK_FORMAT_R8G8B8A8_UNORM,                    // VkFormat
19511         { 10, 10, 1 },                               // VkExtent3D
19512         1,                                           // mipLevels
19513         1,                                           // arrayLayers
19514         VK_SAMPLE_COUNT_1_BIT,                       // samples
19515         VK_IMAGE_TILING_OPTIMAL,                     // VkImageTiling
19516         VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,         // VkImageUsageFlags
19517         VK_SHARING_MODE_EXCLUSIVE,                   // VkSharingMode
19518         0,                                           // queueFamilyCount
19519         nullptr,                                     // pQueueFamilyIndices
19520         VK_IMAGE_LAYOUT_UNDEFINED                    // initialLayout
19521     };
19522 
19523     // 1. Create VkImage.
19524     VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
19525         allocator->m_hDevice,
19526         &imageCreateInfo,
19527         nullptr,
19528         pImage);
19529     return res;
19530 }
19531 
vmaDestroyFakeImage(VmaAllocator VMA_NOT_NULL allocator,VkImage VMA_NULLABLE_NON_DISPATCHABLE image)19532 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyFakeImage(
19533     VmaAllocator VMA_NOT_NULL allocator,
19534     VkImage VMA_NULLABLE_NON_DISPATCHABLE image)
19535 {
19536     VMA_ASSERT(allocator);
19537     if(image == VK_NULL_HANDLE)
19538     {
19539         return;
19540     }
19541     VMA_DEBUG_LOG("vmaDestroyFakeImage");
19542     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19543 
19544     if(image != VK_NULL_HANDLE)
19545     {
19546         (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
19547     }
19548 }
19549 
19550 #endif // #ifdef VMA_IMPLEMENTATION
19551