• 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 
SetBindCompleteFlag(bool flag)6808     void SetBindCompleteFlag(bool flag) { m_bindComplete = flag; }
GetBindCompleteFlag()6809     bool GetBindCompleteFlag() const { return m_bindComplete; }
6810 
GetParentPool()6811     VmaPool GetParentPool() const { return m_hParentPool; }
GetDeviceMemory()6812     VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
GetMemoryTypeIndex()6813     uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetId()6814     uint32_t GetId() const { return m_Id; }
GetMappedData()6815     void* GetMappedData() const { return m_pMappedData; }
6816 
6817     // Validates all data structures inside this object. If not valid, returns false.
6818     bool Validate() const;
6819 
6820     VkResult CheckCorruption(VmaAllocator hAllocator);
6821 
6822     // ppData can be null.
6823     VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
6824     void Unmap(VmaAllocator hAllocator, uint32_t count);
6825 
6826     VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
6827     VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
6828 
6829     VkResult BindBufferMemory(
6830         const VmaAllocator hAllocator,
6831         const VmaAllocation hAllocation,
6832         VkDeviceSize allocationLocalOffset,
6833         VkBuffer hBuffer,
6834         const void* pNext);
6835     VkResult BindImageMemory(
6836         const VmaAllocator hAllocator,
6837         const VmaAllocation hAllocation,
6838         VkDeviceSize allocationLocalOffset,
6839         VkImage hImage,
6840         const void* pNext);
6841 
6842 private:
6843     VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
6844     uint32_t m_MemoryTypeIndex;
6845     uint32_t m_Id;
6846     VkDeviceMemory m_hMemory;
6847 
6848     /*
6849     Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
6850     Also protects m_MapCount, m_pMappedData.
6851     Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
6852     */
6853     VMA_MUTEX m_Mutex;
6854     uint32_t m_MapCount;
6855     void* m_pMappedData;
6856     bool m_bindComplete;
6857 };
6858 
6859 struct VmaPointerLess
6860 {
operatorVmaPointerLess6861     bool operator()(const void* lhs, const void* rhs) const
6862     {
6863         return lhs < rhs;
6864     }
6865 };
6866 
6867 struct VmaDefragmentationMove
6868 {
6869     size_t srcBlockIndex;
6870     size_t dstBlockIndex;
6871     VkDeviceSize srcOffset;
6872     VkDeviceSize dstOffset;
6873     VkDeviceSize size;
6874     VmaAllocation hAllocation;
6875     VmaDeviceMemoryBlock* pSrcBlock;
6876     VmaDeviceMemoryBlock* pDstBlock;
6877 };
6878 
6879 class VmaDefragmentationAlgorithm;
6880 
6881 /*
6882 Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
6883 Vulkan memory type.
6884 
6885 Synchronized internally with a mutex.
6886 */
6887 struct VmaBlockVector
6888 {
6889     VMA_CLASS_NO_COPY(VmaBlockVector)
6890 public:
6891     VmaBlockVector(
6892         VmaAllocator hAllocator,
6893         VmaPool hParentPool,
6894         uint32_t memoryTypeIndex,
6895         VkDeviceSize preferredBlockSize,
6896         size_t minBlockCount,
6897         size_t maxBlockCount,
6898         VkDeviceSize bufferImageGranularity,
6899         uint32_t frameInUseCount,
6900         bool explicitBlockSize,
6901         uint32_t algorithm);
6902     ~VmaBlockVector();
6903 
6904     VkResult CreateMinBlocks();
6905 
GetAllocatorVmaBlockVector6906     VmaAllocator GetAllocator() const { return m_hAllocator; }
GetParentPoolVmaBlockVector6907     VmaPool GetParentPool() const { return m_hParentPool; }
IsCustomPoolVmaBlockVector6908     bool IsCustomPool() const { return m_hParentPool != VMA_NULL; }
GetMemoryTypeIndexVmaBlockVector6909     uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetPreferredBlockSizeVmaBlockVector6910     VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
GetBufferImageGranularityVmaBlockVector6911     VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
GetFrameInUseCountVmaBlockVector6912     uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
GetAlgorithmVmaBlockVector6913     uint32_t GetAlgorithm() const { return m_Algorithm; }
6914 
6915     void GetPoolStats(VmaPoolStats* pStats);
6916 
6917     bool IsEmpty();
6918     bool IsLastBlockBindComplete();
6919     bool IsCorruptionDetectionEnabled() const;
6920 
6921     VkResult Allocate(
6922         uint32_t currentFrameIndex,
6923         VkDeviceSize size,
6924         VkDeviceSize alignment,
6925         const VmaAllocationCreateInfo& createInfo,
6926         VmaSuballocationType suballocType,
6927         size_t allocationCount,
6928         VmaAllocation* pAllocations);
6929 
6930     // OH ISSUE: VMA preAlloc
6931     VkResult AllocateReserved(
6932         uint32_t currentFrameIndex,
6933         VkDeviceSize size,
6934         VkDeviceSize alignment,
6935         const VmaAllocationCreateInfo& createInfo,
6936         VmaSuballocationType suballocType,
6937         VmaAllocation* pAllocation);
6938 
6939     friend void SwapLastBlock(VmaBlockVector* blockVector1, VmaBlockVector* blockVector2);
6940 
6941     void Free(const VmaAllocation hAllocation);
6942 
6943     void FreeReserved(const VmaAllocation hAllocation);
6944 
6945     // Adds statistics of this BlockVector to pStats.
6946     void AddStats(VmaStats* pStats);
6947 
6948     void FreeEmptyBlock();
6949 
6950 #if VMA_STATS_STRING_ENABLED
6951     void PrintDetailedMap(class VmaJsonWriter& json);
6952 #endif
6953 
6954     void MakePoolAllocationsLost(
6955         uint32_t currentFrameIndex,
6956         size_t* pLostAllocationCount);
6957     VkResult CheckCorruption();
6958 
6959     // Saves results in pCtx->res.
6960     void Defragment(
6961         class VmaBlockVectorDefragmentationContext* pCtx,
6962         VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags,
6963         VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
6964         VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
6965         VkCommandBuffer commandBuffer);
6966     void DefragmentationEnd(
6967         class VmaBlockVectorDefragmentationContext* pCtx,
6968         uint32_t flags,
6969         VmaDefragmentationStats* pStats);
6970 
6971     uint32_t ProcessDefragmentations(
6972         class VmaBlockVectorDefragmentationContext *pCtx,
6973         VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves);
6974 
6975     void CommitDefragmentations(
6976         class VmaBlockVectorDefragmentationContext *pCtx,
6977         VmaDefragmentationStats* pStats);
6978 
6979     ////////////////////////////////////////////////////////////////////////////////
6980     // To be used only while the m_Mutex is locked. Used during defragmentation.
6981 
GetBlockCountVmaBlockVector6982     size_t GetBlockCount() const { return m_Blocks.size(); }
GetBlockVmaBlockVector6983     VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
6984     size_t CalcAllocationCount() const;
6985     bool IsBufferImageGranularityConflictPossible() const;
IsNewBlockFlagVmaBlockVector6986     bool IsNewBlockFlag() const { return m_NewBlockFlag; }
ClearNewBlockFlagVmaBlockVector6987     void ClearNewBlockFlag() { m_NewBlockFlag = false; }
6988 
6989 private:
6990     friend class VmaDefragmentationAlgorithm_Generic;
6991 
6992     const VmaAllocator m_hAllocator;
6993     const VmaPool m_hParentPool;
6994     const uint32_t m_MemoryTypeIndex;
6995     const VkDeviceSize m_PreferredBlockSize;
6996     const size_t m_MinBlockCount;
6997     const size_t m_MaxBlockCount;
6998     const VkDeviceSize m_BufferImageGranularity;
6999     const uint32_t m_FrameInUseCount;
7000     const bool m_ExplicitBlockSize;
7001     const uint32_t m_Algorithm;
7002     VMA_RW_MUTEX m_Mutex;
7003 
7004     /* There can be at most one allocation that is completely empty (except when minBlockCount > 0) -
7005     a hysteresis to avoid pessimistic case of alternating creation and destruction of a VkDeviceMemory. */
7006     bool m_HasEmptyBlock;
7007     // Incrementally sorted by sumFreeSize, ascending.
7008     VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
7009     uint32_t m_NextBlockId;
7010     bool m_NewBlockFlag = false;
7011 
7012     VkDeviceSize CalcMaxBlockSize() const;
7013 
7014     // Finds and removes given block from vector.
7015     void Remove(VmaDeviceMemoryBlock* pBlock);
7016 
7017     // Performs single step in sorting m_Blocks. They may not be fully sorted
7018     // after this call.
7019     void IncrementallySortBlocks();
7020 
7021     VkResult AllocatePage(
7022         uint32_t currentFrameIndex,
7023         VkDeviceSize size,
7024         VkDeviceSize alignment,
7025         const VmaAllocationCreateInfo& createInfo,
7026         VmaSuballocationType suballocType,
7027         VmaAllocation* pAllocation);
7028 
7029     // To be used only without CAN_MAKE_OTHER_LOST flag.
7030     VkResult AllocateFromBlock(
7031         VmaDeviceMemoryBlock* pBlock,
7032         uint32_t currentFrameIndex,
7033         VkDeviceSize size,
7034         VkDeviceSize alignment,
7035         VmaAllocationCreateFlags allocFlags,
7036         void* pUserData,
7037         VmaSuballocationType suballocType,
7038         uint32_t strategy,
7039         VmaAllocation* pAllocation);
7040 
7041     VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
7042 
7043     // Saves result to pCtx->res.
7044     void ApplyDefragmentationMovesCpu(
7045         class VmaBlockVectorDefragmentationContext* pDefragCtx,
7046         const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
7047     // Saves result to pCtx->res.
7048     void ApplyDefragmentationMovesGpu(
7049         class VmaBlockVectorDefragmentationContext* pDefragCtx,
7050         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7051         VkCommandBuffer commandBuffer);
7052 
7053     /*
7054     Used during defragmentation. pDefragmentationStats is optional. It's in/out
7055     - updated with new data.
7056     */
7057     void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
7058 
7059     void UpdateHasEmptyBlock();
7060 };
7061 
7062 struct VmaPool_T
7063 {
7064     VMA_CLASS_NO_COPY(VmaPool_T)
7065 public:
7066     VmaBlockVector m_BlockVector;
7067 
7068     VmaPool_T(
7069         VmaAllocator hAllocator,
7070         const VmaPoolCreateInfo& createInfo,
7071         VkDeviceSize preferredBlockSize);
7072     ~VmaPool_T();
7073 
GetIdVmaPool_T7074     uint32_t GetId() const { return m_Id; }
SetIdVmaPool_T7075     void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
7076 
GetNameVmaPool_T7077     const char* GetName() const { return m_Name; }
7078     void SetName(const char* pName);
7079 
7080 #if VMA_STATS_STRING_ENABLED
7081     //void PrintDetailedMap(class VmaStringBuilder& sb);
7082 #endif
7083 
7084 private:
7085     uint32_t m_Id;
7086     char* m_Name;
7087 };
7088 
7089 /*
7090 Performs defragmentation:
7091 
7092 - Updates `pBlockVector->m_pMetadata`.
7093 - Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
7094 - Does not move actual data, only returns requested moves as `moves`.
7095 */
7096 class VmaDefragmentationAlgorithm
7097 {
VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)7098     VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
7099 public:
7100     VmaDefragmentationAlgorithm(
7101         VmaAllocator hAllocator,
7102         VmaBlockVector* pBlockVector,
7103         uint32_t currentFrameIndex) :
7104         m_hAllocator(hAllocator),
7105         m_pBlockVector(pBlockVector),
7106         m_CurrentFrameIndex(currentFrameIndex)
7107     {
7108     }
~VmaDefragmentationAlgorithm()7109     virtual ~VmaDefragmentationAlgorithm()
7110     {
7111     }
7112 
7113     virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
7114     virtual void AddAll() = 0;
7115 
7116     virtual VkResult Defragment(
7117         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7118         VkDeviceSize maxBytesToMove,
7119         uint32_t maxAllocationsToMove,
7120         VmaDefragmentationFlags flags) = 0;
7121 
7122     virtual VkDeviceSize GetBytesMoved() const = 0;
7123     virtual uint32_t GetAllocationsMoved() const = 0;
7124 
7125 protected:
7126     VmaAllocator const m_hAllocator;
7127     VmaBlockVector* const m_pBlockVector;
7128     const uint32_t m_CurrentFrameIndex;
7129 
7130     struct AllocationInfo
7131     {
7132         VmaAllocation m_hAllocation;
7133         VkBool32* m_pChanged;
7134 
AllocationInfoAllocationInfo7135         AllocationInfo() :
7136             m_hAllocation(VK_NULL_HANDLE),
7137             m_pChanged(VMA_NULL)
7138         {
7139         }
AllocationInfoAllocationInfo7140         AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
7141             m_hAllocation(hAlloc),
7142             m_pChanged(pChanged)
7143         {
7144         }
7145     };
7146 };
7147 
7148 class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
7149 {
7150     VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
7151 public:
7152     VmaDefragmentationAlgorithm_Generic(
7153         VmaAllocator hAllocator,
7154         VmaBlockVector* pBlockVector,
7155         uint32_t currentFrameIndex,
7156         bool overlappingMoveSupported);
7157     virtual ~VmaDefragmentationAlgorithm_Generic();
7158 
7159     virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
AddAll()7160     virtual void AddAll() { m_AllAllocations = true; }
7161 
7162     virtual VkResult Defragment(
7163         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7164         VkDeviceSize maxBytesToMove,
7165         uint32_t maxAllocationsToMove,
7166         VmaDefragmentationFlags flags);
7167 
GetBytesMoved()7168     virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
GetAllocationsMoved()7169     virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
7170 
7171 private:
7172     uint32_t m_AllocationCount;
7173     bool m_AllAllocations;
7174 
7175     VkDeviceSize m_BytesMoved;
7176     uint32_t m_AllocationsMoved;
7177 
7178     struct AllocationInfoSizeGreater
7179     {
operatorAllocationInfoSizeGreater7180         bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
7181         {
7182             return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
7183         }
7184     };
7185 
7186     struct AllocationInfoOffsetGreater
7187     {
operatorAllocationInfoOffsetGreater7188         bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
7189         {
7190             return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
7191         }
7192     };
7193 
7194     struct BlockInfo
7195     {
7196         size_t m_OriginalBlockIndex;
7197         VmaDeviceMemoryBlock* m_pBlock;
7198         bool m_HasNonMovableAllocations;
7199         VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
7200 
BlockInfoBlockInfo7201         BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
7202             m_OriginalBlockIndex(SIZE_MAX),
7203             m_pBlock(VMA_NULL),
7204             m_HasNonMovableAllocations(true),
7205             m_Allocations(pAllocationCallbacks)
7206         {
7207         }
7208 
CalcHasNonMovableAllocationsBlockInfo7209         void CalcHasNonMovableAllocations()
7210         {
7211             const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
7212             const size_t defragmentAllocCount = m_Allocations.size();
7213             m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
7214         }
7215 
SortAllocationsBySizeDescendingBlockInfo7216         void SortAllocationsBySizeDescending()
7217         {
7218             VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
7219         }
7220 
SortAllocationsByOffsetDescendingBlockInfo7221         void SortAllocationsByOffsetDescending()
7222         {
7223             VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
7224         }
7225     };
7226 
7227     struct BlockPointerLess
7228     {
operatorBlockPointerLess7229         bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
7230         {
7231             return pLhsBlockInfo->m_pBlock < pRhsBlock;
7232         }
operatorBlockPointerLess7233         bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
7234         {
7235             return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
7236         }
7237     };
7238 
7239     // 1. Blocks with some non-movable allocations go first.
7240     // 2. Blocks with smaller sumFreeSize go first.
7241     struct BlockInfoCompareMoveDestination
7242     {
operatorBlockInfoCompareMoveDestination7243         bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
7244         {
7245             if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
7246             {
7247                 return true;
7248             }
7249             if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
7250             {
7251                 return false;
7252             }
7253             if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
7254             {
7255                 return true;
7256             }
7257             return false;
7258         }
7259     };
7260 
7261     typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
7262     BlockInfoVector m_Blocks;
7263 
7264     VkResult DefragmentRound(
7265         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7266         VkDeviceSize maxBytesToMove,
7267         uint32_t maxAllocationsToMove,
7268         bool freeOldAllocations);
7269 
7270     size_t CalcBlocksWithNonMovableCount() const;
7271 
7272     static bool MoveMakesSense(
7273         size_t dstBlockIndex, VkDeviceSize dstOffset,
7274         size_t srcBlockIndex, VkDeviceSize srcOffset);
7275 };
7276 
7277 class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
7278 {
7279     VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
7280 public:
7281     VmaDefragmentationAlgorithm_Fast(
7282         VmaAllocator hAllocator,
7283         VmaBlockVector* pBlockVector,
7284         uint32_t currentFrameIndex,
7285         bool overlappingMoveSupported);
7286     virtual ~VmaDefragmentationAlgorithm_Fast();
7287 
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)7288     virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { ++m_AllocationCount; }
AddAll()7289     virtual void AddAll() { m_AllAllocations = true; }
7290 
7291     virtual VkResult Defragment(
7292         VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7293         VkDeviceSize maxBytesToMove,
7294         uint32_t maxAllocationsToMove,
7295         VmaDefragmentationFlags flags);
7296 
GetBytesMoved()7297     virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
GetAllocationsMoved()7298     virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
7299 
7300 private:
7301     struct BlockInfo
7302     {
7303         size_t origBlockIndex;
7304     };
7305 
7306     class FreeSpaceDatabase
7307     {
7308     public:
FreeSpaceDatabase()7309         FreeSpaceDatabase()
7310         {
7311             FreeSpace s = {};
7312             s.blockInfoIndex = SIZE_MAX;
7313             for(size_t i = 0; i < MAX_COUNT; ++i)
7314             {
7315                 m_FreeSpaces[i] = s;
7316             }
7317         }
7318 
Register(size_t blockInfoIndex,VkDeviceSize offset,VkDeviceSize size)7319         void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
7320         {
7321             if(size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7322             {
7323                 return;
7324             }
7325 
7326             // Find first invalid or the smallest structure.
7327             size_t bestIndex = SIZE_MAX;
7328             for(size_t i = 0; i < MAX_COUNT; ++i)
7329             {
7330                 // Empty structure.
7331                 if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
7332                 {
7333                     bestIndex = i;
7334                     break;
7335                 }
7336                 if(m_FreeSpaces[i].size < size &&
7337                     (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
7338                 {
7339                     bestIndex = i;
7340                 }
7341             }
7342 
7343             if(bestIndex != SIZE_MAX)
7344             {
7345                 m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
7346                 m_FreeSpaces[bestIndex].offset = offset;
7347                 m_FreeSpaces[bestIndex].size = size;
7348             }
7349         }
7350 
Fetch(VkDeviceSize alignment,VkDeviceSize size,size_t & outBlockInfoIndex,VkDeviceSize & outDstOffset)7351         bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
7352             size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
7353         {
7354             size_t bestIndex = SIZE_MAX;
7355             VkDeviceSize bestFreeSpaceAfter = 0;
7356             for(size_t i = 0; i < MAX_COUNT; ++i)
7357             {
7358                 // Structure is valid.
7359                 if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
7360                 {
7361                     const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment);
7362                     // Allocation fits into this structure.
7363                     if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
7364                     {
7365                         const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
7366                             (dstOffset + size);
7367                         if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
7368                         {
7369                             bestIndex = i;
7370                             bestFreeSpaceAfter = freeSpaceAfter;
7371                         }
7372                     }
7373                 }
7374             }
7375 
7376             if(bestIndex != SIZE_MAX)
7377             {
7378                 outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
7379                 outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment);
7380 
7381                 if(bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7382                 {
7383                     // Leave this structure for remaining empty space.
7384                     const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
7385                     m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
7386                     m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
7387                 }
7388                 else
7389                 {
7390                     // This structure becomes invalid.
7391                     m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX;
7392                 }
7393 
7394                 return true;
7395             }
7396 
7397             return false;
7398         }
7399 
7400     private:
7401         static const size_t MAX_COUNT = 4;
7402 
7403         struct FreeSpace
7404         {
7405             size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
7406             VkDeviceSize offset;
7407             VkDeviceSize size;
7408         } m_FreeSpaces[MAX_COUNT];
7409     };
7410 
7411     const bool m_OverlappingMoveSupported;
7412 
7413     uint32_t m_AllocationCount;
7414     bool m_AllAllocations;
7415 
7416     VkDeviceSize m_BytesMoved;
7417     uint32_t m_AllocationsMoved;
7418 
7419     VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
7420 
7421     void PreprocessMetadata();
7422     void PostprocessMetadata();
7423     void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
7424 };
7425 
7426 struct VmaBlockDefragmentationContext
7427 {
7428     enum BLOCK_FLAG
7429     {
7430         BLOCK_FLAG_USED = 0x00000001,
7431     };
7432     uint32_t flags;
7433     VkBuffer hBuffer;
7434 };
7435 
7436 class VmaBlockVectorDefragmentationContext
7437 {
7438     VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
7439 public:
7440     VkResult res;
7441     bool mutexLocked;
7442     VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
7443     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > defragmentationMoves;
7444     uint32_t defragmentationMovesProcessed;
7445     uint32_t defragmentationMovesCommitted;
7446     bool hasDefragmentationPlan;
7447 
7448     VmaBlockVectorDefragmentationContext(
7449         VmaAllocator hAllocator,
7450         VmaPool hCustomPool, // Optional.
7451         VmaBlockVector* pBlockVector,
7452         uint32_t currFrameIndex);
7453     ~VmaBlockVectorDefragmentationContext();
7454 
GetCustomPool()7455     VmaPool GetCustomPool() const { return m_hCustomPool; }
GetBlockVector()7456     VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
GetAlgorithm()7457     VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
7458 
7459     void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
AddAll()7460     void AddAll() { m_AllAllocations = true; }
7461 
7462     void Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags);
7463 
7464 private:
7465     const VmaAllocator m_hAllocator;
7466     // Null if not from custom pool.
7467     const VmaPool m_hCustomPool;
7468     // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
7469     VmaBlockVector* const m_pBlockVector;
7470     const uint32_t m_CurrFrameIndex;
7471     // Owner of this object.
7472     VmaDefragmentationAlgorithm* m_pAlgorithm;
7473 
7474     struct AllocInfo
7475     {
7476         VmaAllocation hAlloc;
7477         VkBool32* pChanged;
7478     };
7479     // Used between constructor and Begin.
7480     VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
7481     bool m_AllAllocations;
7482 };
7483 
7484 struct VmaDefragmentationContext_T
7485 {
7486 private:
7487     VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
7488 public:
7489     VmaDefragmentationContext_T(
7490         VmaAllocator hAllocator,
7491         uint32_t currFrameIndex,
7492         uint32_t flags,
7493         VmaDefragmentationStats* pStats);
7494     ~VmaDefragmentationContext_T();
7495 
7496     void AddPools(uint32_t poolCount, const VmaPool* pPools);
7497     void AddAllocations(
7498         uint32_t allocationCount,
7499         const VmaAllocation* pAllocations,
7500         VkBool32* pAllocationsChanged);
7501 
7502     /*
7503     Returns:
7504     - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
7505     - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
7506     - Negative value if error occured and object can be destroyed immediately.
7507     */
7508     VkResult Defragment(
7509         VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
7510         VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
7511         VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags);
7512 
7513     VkResult DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo);
7514     VkResult DefragmentPassEnd();
7515 
7516 private:
7517     const VmaAllocator m_hAllocator;
7518     const uint32_t m_CurrFrameIndex;
7519     const uint32_t m_Flags;
7520     VmaDefragmentationStats* const m_pStats;
7521 
7522     VkDeviceSize m_MaxCpuBytesToMove;
7523     uint32_t m_MaxCpuAllocationsToMove;
7524     VkDeviceSize m_MaxGpuBytesToMove;
7525     uint32_t m_MaxGpuAllocationsToMove;
7526 
7527     // Owner of these objects.
7528     VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
7529     // Owner of these objects.
7530     VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
7531 };
7532 
7533 #if VMA_RECORDING_ENABLED
7534 
7535 class VmaRecorder
7536 {
7537 public:
7538     VmaRecorder();
7539     VkResult Init(const VmaRecordSettings& settings, bool useMutex);
7540     void WriteConfiguration(
7541         const VkPhysicalDeviceProperties& devProps,
7542         const VkPhysicalDeviceMemoryProperties& memProps,
7543         uint32_t vulkanApiVersion,
7544         bool dedicatedAllocationExtensionEnabled,
7545         bool bindMemory2ExtensionEnabled,
7546         bool memoryBudgetExtensionEnabled,
7547         bool deviceCoherentMemoryExtensionEnabled);
7548     ~VmaRecorder();
7549 
7550     void RecordCreateAllocator(uint32_t frameIndex);
7551     void RecordDestroyAllocator(uint32_t frameIndex);
7552     void RecordCreatePool(uint32_t frameIndex,
7553         const VmaPoolCreateInfo& createInfo,
7554         VmaPool pool);
7555     void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
7556     void RecordAllocateMemory(uint32_t frameIndex,
7557         const VkMemoryRequirements& vkMemReq,
7558         const VmaAllocationCreateInfo& createInfo,
7559         VmaAllocation allocation);
7560     void RecordAllocateMemoryPages(uint32_t frameIndex,
7561         const VkMemoryRequirements& vkMemReq,
7562         const VmaAllocationCreateInfo& createInfo,
7563         uint64_t allocationCount,
7564         const VmaAllocation* pAllocations);
7565     void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
7566         const VkMemoryRequirements& vkMemReq,
7567         bool requiresDedicatedAllocation,
7568         bool prefersDedicatedAllocation,
7569         const VmaAllocationCreateInfo& createInfo,
7570         VmaAllocation allocation);
7571     void RecordAllocateMemoryForImage(uint32_t frameIndex,
7572         const VkMemoryRequirements& vkMemReq,
7573         bool requiresDedicatedAllocation,
7574         bool prefersDedicatedAllocation,
7575         const VmaAllocationCreateInfo& createInfo,
7576         VmaAllocation allocation);
7577     void RecordFreeMemory(uint32_t frameIndex,
7578         VmaAllocation allocation);
7579     void RecordFreeMemoryPages(uint32_t frameIndex,
7580         uint64_t allocationCount,
7581         const VmaAllocation* pAllocations);
7582     void RecordSetAllocationUserData(uint32_t frameIndex,
7583         VmaAllocation allocation,
7584         const void* pUserData);
7585     void RecordCreateLostAllocation(uint32_t frameIndex,
7586         VmaAllocation allocation);
7587     void RecordMapMemory(uint32_t frameIndex,
7588         VmaAllocation allocation);
7589     void RecordUnmapMemory(uint32_t frameIndex,
7590         VmaAllocation allocation);
7591     void RecordFlushAllocation(uint32_t frameIndex,
7592         VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
7593     void RecordInvalidateAllocation(uint32_t frameIndex,
7594         VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
7595     void RecordCreateBuffer(uint32_t frameIndex,
7596         const VkBufferCreateInfo& bufCreateInfo,
7597         const VmaAllocationCreateInfo& allocCreateInfo,
7598         VmaAllocation allocation);
7599     void RecordCreateImage(uint32_t frameIndex,
7600         const VkImageCreateInfo& imageCreateInfo,
7601         const VmaAllocationCreateInfo& allocCreateInfo,
7602         VmaAllocation allocation);
7603     void RecordDestroyBuffer(uint32_t frameIndex,
7604         VmaAllocation allocation);
7605     void RecordDestroyImage(uint32_t frameIndex,
7606         VmaAllocation allocation);
7607     void RecordTouchAllocation(uint32_t frameIndex,
7608         VmaAllocation allocation);
7609     void RecordGetAllocationInfo(uint32_t frameIndex,
7610         VmaAllocation allocation);
7611     void RecordMakePoolAllocationsLost(uint32_t frameIndex,
7612         VmaPool pool);
7613     void RecordDefragmentationBegin(uint32_t frameIndex,
7614         const VmaDefragmentationInfo2& info,
7615         VmaDefragmentationContext ctx);
7616     void RecordDefragmentationEnd(uint32_t frameIndex,
7617         VmaDefragmentationContext ctx);
7618     void RecordSetPoolName(uint32_t frameIndex,
7619         VmaPool pool,
7620         const char* name);
7621 
7622 private:
7623     struct CallParams
7624     {
7625         uint32_t threadId;
7626         double time;
7627     };
7628 
7629     class UserDataString
7630     {
7631     public:
7632         UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
GetString()7633         const char* GetString() const { return m_Str; }
7634 
7635     private:
7636         char m_PtrStr[17];
7637         const char* m_Str;
7638     };
7639 
7640     bool m_UseMutex;
7641     VmaRecordFlags m_Flags;
7642     FILE* m_File;
7643     VMA_MUTEX m_FileMutex;
7644     std::chrono::time_point<std::chrono::high_resolution_clock> m_RecordingStartTime;
7645 
7646     void GetBasicParams(CallParams& outParams);
7647 
7648     // T must be a pointer type, e.g. VmaAllocation, VmaPool.
7649     template<typename T>
PrintPointerList(uint64_t count,const T * pItems)7650     void PrintPointerList(uint64_t count, const T* pItems)
7651     {
7652         if(count)
7653         {
7654             fprintf(m_File, "%p", pItems[0]);
7655             for(uint64_t i = 1; i < count; ++i)
7656             {
7657                 fprintf(m_File, " %p", pItems[i]);
7658             }
7659         }
7660     }
7661 
7662     void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
7663     void Flush();
7664 };
7665 
7666 #endif // #if VMA_RECORDING_ENABLED
7667 
7668 /*
7669 Thread-safe wrapper over VmaPoolAllocator free list, for allocation of VmaAllocation_T objects.
7670 */
7671 class VmaAllocationObjectAllocator
7672 {
7673     VMA_CLASS_NO_COPY(VmaAllocationObjectAllocator)
7674 public:
7675     VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks);
7676 
7677     template<typename... Types> VmaAllocation Allocate(Types... args);
7678     void Free(VmaAllocation hAlloc);
7679 
7680 private:
7681     VMA_MUTEX m_Mutex;
7682     VmaPoolAllocator<VmaAllocation_T> m_Allocator;
7683 };
7684 
7685 struct VmaCurrentBudgetData
7686 {
7687     VMA_ATOMIC_UINT64 m_BlockBytes[VK_MAX_MEMORY_HEAPS];
7688     VMA_ATOMIC_UINT64 m_AllocationBytes[VK_MAX_MEMORY_HEAPS];
7689 
7690 #if VMA_MEMORY_BUDGET
7691     VMA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch;
7692     VMA_RW_MUTEX m_BudgetMutex;
7693     uint64_t m_VulkanUsage[VK_MAX_MEMORY_HEAPS];
7694     uint64_t m_VulkanBudget[VK_MAX_MEMORY_HEAPS];
7695     uint64_t m_BlockBytesAtBudgetFetch[VK_MAX_MEMORY_HEAPS];
7696 #endif // #if VMA_MEMORY_BUDGET
7697 
VmaCurrentBudgetDataVmaCurrentBudgetData7698     VmaCurrentBudgetData()
7699     {
7700         for(uint32_t heapIndex = 0; heapIndex < VK_MAX_MEMORY_HEAPS; ++heapIndex)
7701         {
7702             m_BlockBytes[heapIndex] = 0;
7703             m_AllocationBytes[heapIndex] = 0;
7704 #if VMA_MEMORY_BUDGET
7705             m_VulkanUsage[heapIndex] = 0;
7706             m_VulkanBudget[heapIndex] = 0;
7707             m_BlockBytesAtBudgetFetch[heapIndex] = 0;
7708 #endif
7709         }
7710 
7711 #if VMA_MEMORY_BUDGET
7712         m_OperationsSinceBudgetFetch = 0;
7713 #endif
7714     }
7715 
AddAllocationVmaCurrentBudgetData7716     void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
7717     {
7718         m_AllocationBytes[heapIndex] += allocationSize;
7719 #if VMA_MEMORY_BUDGET
7720         ++m_OperationsSinceBudgetFetch;
7721 #endif
7722     }
7723 
RemoveAllocationVmaCurrentBudgetData7724     void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
7725     {
7726         VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize); // DELME
7727         m_AllocationBytes[heapIndex] -= allocationSize;
7728 #if VMA_MEMORY_BUDGET
7729         ++m_OperationsSinceBudgetFetch;
7730 #endif
7731     }
7732 };
7733 
7734 // Main allocator object.
7735 struct VmaAllocator_T
7736 {
7737     VMA_CLASS_NO_COPY(VmaAllocator_T)
7738 public:
7739     bool m_UseMutex;
7740     uint32_t m_VulkanApiVersion;
7741     bool m_UseKhrDedicatedAllocation; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
7742     bool m_UseKhrBindMemory2; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
7743     bool m_UseExtMemoryBudget;
7744     bool m_UseAmdDeviceCoherentMemory;
7745     bool m_UseKhrBufferDeviceAddress;
7746     VkDevice m_hDevice;
7747     VkInstance m_hInstance;
7748     bool m_AllocationCallbacksSpecified;
7749     VkAllocationCallbacks m_AllocationCallbacks;
7750     VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
7751     VmaAllocationObjectAllocator m_AllocationObjectAllocator;
7752 
7753     // Each bit (1 << i) is set if HeapSizeLimit is enabled for that heap, so cannot allocate more than the heap size.
7754     uint32_t m_HeapSizeLimitMask;
7755 
7756     VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
7757     VkPhysicalDeviceMemoryProperties m_MemProps;
7758 
7759     // Default pools.
7760     VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
7761     // Reserved pools.
7762     VmaBlockVector* m_pReservedBlockVectors[VK_MAX_MEMORY_TYPES];
7763 
7764     // Each vector is sorted by memory (handle value).
7765     typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
7766     AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES];
7767     VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
7768 
7769     VmaCurrentBudgetData m_Budget;
7770 
7771     VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
7772     VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
7773     ~VmaAllocator_T();
7774 
GetAllocationCallbacksVmaAllocator_T7775     const VkAllocationCallbacks* GetAllocationCallbacks() const
7776     {
7777         return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
7778     }
GetVulkanFunctionsVmaAllocator_T7779     const VmaVulkanFunctions& GetVulkanFunctions() const
7780     {
7781         return m_VulkanFunctions;
7782     }
7783 
GetPhysicalDeviceVmaAllocator_T7784     VkPhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; }
7785 
GetBufferImageGranularityVmaAllocator_T7786     VkDeviceSize GetBufferImageGranularity() const
7787     {
7788         return VMA_MAX(
7789             static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
7790             m_PhysicalDeviceProperties.limits.bufferImageGranularity);
7791     }
7792 
GetMemoryHeapCountVmaAllocator_T7793     uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
GetMemoryTypeCountVmaAllocator_T7794     uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
7795 
MemoryTypeIndexToHeapIndexVmaAllocator_T7796     uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
7797     {
7798         VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
7799         return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
7800     }
7801     // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
IsMemoryTypeNonCoherentVmaAllocator_T7802     bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
7803     {
7804         return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
7805             VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
7806     }
7807     // Minimum alignment for all allocations in specific memory type.
GetMemoryTypeMinAlignmentVmaAllocator_T7808     VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
7809     {
7810         return IsMemoryTypeNonCoherent(memTypeIndex) ?
7811             VMA_MAX((VkDeviceSize)VMA_DEBUG_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
7812             (VkDeviceSize)VMA_DEBUG_ALIGNMENT;
7813     }
7814 
IsIntegratedGpuVmaAllocator_T7815     bool IsIntegratedGpu() const
7816     {
7817         return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
7818     }
7819 
GetGlobalMemoryTypeBitsVmaAllocator_T7820     uint32_t GetGlobalMemoryTypeBits() const { return m_GlobalMemoryTypeBits; }
7821 
7822 #if VMA_RECORDING_ENABLED
GetRecorderVmaAllocator_T7823     VmaRecorder* GetRecorder() const { return m_pRecorder; }
7824 #endif
7825 
7826     void GetBufferMemoryRequirements(
7827         VkBuffer hBuffer,
7828         VkMemoryRequirements& memReq,
7829         bool& requiresDedicatedAllocation,
7830         bool& prefersDedicatedAllocation) const;
7831     void GetImageMemoryRequirements(
7832         VkImage hImage,
7833         VkMemoryRequirements& memReq,
7834         bool& requiresDedicatedAllocation,
7835         bool& prefersDedicatedAllocation) const;
7836 
7837     // Main allocation function.
7838     VkResult AllocateMemory(
7839         const VkMemoryRequirements& vkMemReq,
7840         bool requiresDedicatedAllocation,
7841         bool prefersDedicatedAllocation,
7842         VkBuffer dedicatedBuffer,
7843         VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown.
7844         VkImage dedicatedImage,
7845         const VmaAllocationCreateInfo& createInfo,
7846         VmaSuballocationType suballocType,
7847         size_t allocationCount,
7848         VmaAllocation* pAllocations);
7849 
7850     // Main deallocation function.
7851     void FreeMemory(
7852         size_t allocationCount,
7853         const VmaAllocation* pAllocations);
7854 
7855     // OH ISSUE: VMA preAlloc
7856     // pre-allocation
7857     VkResult AllocateReservedMemory(
7858         const VkMemoryRequirements& vkMemReq,
7859         bool requiresDedicatedAllocation,
7860         bool prefersDedicatedAllocation,
7861         VkBuffer dedicatedBuffer,
7862         VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown.
7863         VkImage dedicatedImage,
7864         const VmaAllocationCreateInfo& createInfo,
7865         VmaSuballocationType suballocType,
7866         size_t allocationCount,
7867         VmaAllocation* pAllocations);
7868 
7869     // pre-deallocation function.
7870     void FreeReservedMemory(
7871         size_t allocationCount,
7872         const VmaAllocation* pAllocations);
7873 
7874     VkResult SwapReservedBlock(
7875         VkImage image,
7876         const VmaAllocationCreateInfo* pCreateInfo,
7877         VmaAllocation* pAllocation,
7878         VmaAllocationInfo* pAllocationInfo);
7879 
7880     uint32_t GetPreAllocBlockSize();
7881 
7882     VkResult ResizeAllocation(
7883         const VmaAllocation alloc,
7884         VkDeviceSize newSize);
7885 
7886     void CalculateStats(VmaStats* pStats);
7887 
7888     void FreeEmptyBlock();
7889 
7890     void GetBudget(
7891         VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount);
7892 
7893 #if VMA_STATS_STRING_ENABLED
7894     void PrintDetailedMap(class VmaJsonWriter& json);
7895 #endif
7896 
7897     VkResult DefragmentationBegin(
7898         const VmaDefragmentationInfo2& info,
7899         VmaDefragmentationStats* pStats,
7900         VmaDefragmentationContext* pContext);
7901     VkResult DefragmentationEnd(
7902         VmaDefragmentationContext context);
7903 
7904     VkResult DefragmentationPassBegin(
7905         VmaDefragmentationPassInfo* pInfo,
7906         VmaDefragmentationContext context);
7907     VkResult DefragmentationPassEnd(
7908         VmaDefragmentationContext context);
7909 
7910     void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
7911     bool TouchAllocation(VmaAllocation hAllocation);
7912 
7913     VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
7914     void DestroyPool(VmaPool pool);
7915     void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
7916 
7917     void SetCurrentFrameIndex(uint32_t frameIndex);
GetCurrentFrameIndexVmaAllocator_T7918     uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
7919 
7920     void MakePoolAllocationsLost(
7921         VmaPool hPool,
7922         size_t* pLostAllocationCount);
7923     VkResult CheckPoolCorruption(VmaPool hPool);
7924     VkResult CheckCorruption(uint32_t memoryTypeBits);
7925 
7926     void CreateLostAllocation(VmaAllocation* pAllocation);
7927 
7928     // Call to Vulkan function vkAllocateMemory with accompanying bookkeeping.
7929     VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
7930     // Call to Vulkan function vkFreeMemory with accompanying bookkeeping.
7931     void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
7932     // Call to Vulkan function vkBindBufferMemory or vkBindBufferMemory2KHR.
7933     VkResult BindVulkanBuffer(
7934         VkDeviceMemory memory,
7935         VkDeviceSize memoryOffset,
7936         VkBuffer buffer,
7937         const void* pNext);
7938     // Call to Vulkan function vkBindImageMemory or vkBindImageMemory2KHR.
7939     VkResult BindVulkanImage(
7940         VkDeviceMemory memory,
7941         VkDeviceSize memoryOffset,
7942         VkImage image,
7943         const void* pNext);
7944 
7945     VkResult Map(VmaAllocation hAllocation, void** ppData);
7946     void Unmap(VmaAllocation hAllocation);
7947 
7948     VkResult BindBufferMemory(
7949         VmaAllocation hAllocation,
7950         VkDeviceSize allocationLocalOffset,
7951         VkBuffer hBuffer,
7952         const void* pNext);
7953     VkResult BindImageMemory(
7954         VmaAllocation hAllocation,
7955         VkDeviceSize allocationLocalOffset,
7956         VkImage hImage,
7957         const void* pNext);
7958 
7959     VkResult FlushOrInvalidateAllocation(
7960         VmaAllocation hAllocation,
7961         VkDeviceSize offset, VkDeviceSize size,
7962         VMA_CACHE_OPERATION op);
7963     VkResult FlushOrInvalidateAllocations(
7964         uint32_t allocationCount,
7965         const VmaAllocation* allocations,
7966         const VkDeviceSize* offsets, const VkDeviceSize* sizes,
7967         VMA_CACHE_OPERATION op);
7968 
7969     void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
7970 
7971     /*
7972     Returns bit mask of memory types that can support defragmentation on GPU as
7973     they support creation of required buffer for copy operations.
7974     */
7975     uint32_t GetGpuDefragmentationMemoryTypeBits();
7976 
7977 private:
7978     VkDeviceSize m_PreferredLargeHeapBlockSize;
7979 
7980     VkPhysicalDevice m_PhysicalDevice;
7981     VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
7982     VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized.
7983 
7984     VMA_RW_MUTEX m_PoolsMutex;
7985     // Protected by m_PoolsMutex. Sorted by pointer value.
7986     VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools;
7987     uint32_t m_NextPoolId;
7988 
7989     VmaVulkanFunctions m_VulkanFunctions;
7990 
7991     // Global bit mask AND-ed with any memoryTypeBits to disallow certain memory types.
7992     uint32_t m_GlobalMemoryTypeBits;
7993 
7994 #if VMA_RECORDING_ENABLED
7995     VmaRecorder* m_pRecorder;
7996 #endif
7997 
7998     void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
7999 
8000 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
8001     void ImportVulkanFunctions_Static();
8002 #endif
8003 
8004     void ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions);
8005 
8006 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
8007     void ImportVulkanFunctions_Dynamic();
8008 #endif
8009 
8010     void ValidateVulkanFunctions();
8011 
8012     VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
8013 
8014     VkResult AllocateMemoryOfType(
8015         VkDeviceSize size,
8016         VkDeviceSize alignment,
8017         bool dedicatedAllocation,
8018         VkBuffer dedicatedBuffer,
8019         VkBufferUsageFlags dedicatedBufferUsage,
8020         VkImage dedicatedImage,
8021         const VmaAllocationCreateInfo& createInfo,
8022         uint32_t memTypeIndex,
8023         VmaSuballocationType suballocType,
8024         size_t allocationCount,
8025         VmaAllocation* pAllocations);
8026 
8027     // Helper function only to be used inside AllocateDedicatedMemory.
8028     VkResult AllocateDedicatedMemoryPage(
8029         VkDeviceSize size,
8030         VmaSuballocationType suballocType,
8031         uint32_t memTypeIndex,
8032         const VkMemoryAllocateInfo& allocInfo,
8033         bool map,
8034         bool isUserDataString,
8035         void* pUserData,
8036         VmaAllocation* pAllocation);
8037 
8038     // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
8039     VkResult AllocateDedicatedMemory(
8040         VkDeviceSize size,
8041         VmaSuballocationType suballocType,
8042         uint32_t memTypeIndex,
8043         bool withinBudget,
8044         bool map,
8045         bool isUserDataString,
8046         void* pUserData,
8047         VkBuffer dedicatedBuffer,
8048         VkBufferUsageFlags dedicatedBufferUsage,
8049         VkImage dedicatedImage,
8050         size_t allocationCount,
8051         VmaAllocation* pAllocations);
8052 
8053     void FreeDedicatedMemory(const VmaAllocation allocation);
8054 
8055     /*
8056     Calculates and returns bit mask of memory types that can support defragmentation
8057     on GPU as they support creation of required buffer for copy operations.
8058     */
8059     uint32_t CalculateGpuDefragmentationMemoryTypeBits() const;
8060 
8061     uint32_t CalculateGlobalMemoryTypeBits() const;
8062 
8063     bool GetFlushOrInvalidateRange(
8064         VmaAllocation allocation,
8065         VkDeviceSize offset, VkDeviceSize size,
8066         VkMappedMemoryRange& outRange) const;
8067 
8068 #if VMA_MEMORY_BUDGET
8069     void UpdateVulkanBudget();
8070 #endif // #if VMA_MEMORY_BUDGET
8071 };
8072 
8073 ////////////////////////////////////////////////////////////////////////////////
8074 // Memory allocation #2 after VmaAllocator_T definition
8075 
VmaMalloc(VmaAllocator hAllocator,size_t size,size_t alignment)8076 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
8077 {
8078     return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
8079 }
8080 
VmaFree(VmaAllocator hAllocator,void * ptr)8081 static void VmaFree(VmaAllocator hAllocator, void* ptr)
8082 {
8083     VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
8084 }
8085 
8086 template<typename T>
VmaAllocate(VmaAllocator hAllocator)8087 static T* VmaAllocate(VmaAllocator hAllocator)
8088 {
8089     return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
8090 }
8091 
8092 template<typename T>
VmaAllocateArray(VmaAllocator hAllocator,size_t count)8093 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
8094 {
8095     return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
8096 }
8097 
8098 template<typename T>
vma_delete(VmaAllocator hAllocator,T * ptr)8099 static void vma_delete(VmaAllocator hAllocator, T* ptr)
8100 {
8101     if(ptr != VMA_NULL)
8102     {
8103         ptr->~T();
8104         VmaFree(hAllocator, ptr);
8105     }
8106 }
8107 
8108 template<typename T>
vma_delete_array(VmaAllocator hAllocator,T * ptr,size_t count)8109 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
8110 {
8111     if(ptr != VMA_NULL)
8112     {
8113         for(size_t i = count; i--; )
8114             ptr[i].~T();
8115         VmaFree(hAllocator, ptr);
8116     }
8117 }
8118 
8119 ////////////////////////////////////////////////////////////////////////////////
8120 // VmaStringBuilder
8121 
8122 #if VMA_STATS_STRING_ENABLED
8123 
8124 class VmaStringBuilder
8125 {
8126 public:
VmaStringBuilder(VmaAllocator alloc)8127     VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
GetLength()8128     size_t GetLength() const { return m_Data.size(); }
GetData()8129     const char* GetData() const { return m_Data.data(); }
8130 
Add(char ch)8131     void Add(char ch) { m_Data.push_back(ch); }
8132     void Add(const char* pStr);
AddNewLine()8133     void AddNewLine() { Add('\n'); }
8134     void AddNumber(uint32_t num);
8135     void AddNumber(uint64_t num);
8136     void AddPointer(const void* ptr);
8137 
8138 private:
8139     VmaVector< char, VmaStlAllocator<char> > m_Data;
8140 };
8141 
Add(const char * pStr)8142 void VmaStringBuilder::Add(const char* pStr)
8143 {
8144     const size_t strLen = strlen(pStr);
8145     if(strLen > 0)
8146     {
8147         const size_t oldCount = m_Data.size();
8148         m_Data.resize(oldCount + strLen);
8149         memcpy(m_Data.data() + oldCount, pStr, strLen);
8150     }
8151 }
8152 
AddNumber(uint32_t num)8153 void VmaStringBuilder::AddNumber(uint32_t num)
8154 {
8155     char buf[11];
8156     buf[10] = '\0';
8157     char *p = &buf[10];
8158     do
8159     {
8160         *--p = '0' + (num % 10);
8161         num /= 10;
8162     }
8163     while(num);
8164     Add(p);
8165 }
8166 
AddNumber(uint64_t num)8167 void VmaStringBuilder::AddNumber(uint64_t num)
8168 {
8169     char buf[21];
8170     buf[20] = '\0';
8171     char *p = &buf[20];
8172     do
8173     {
8174         *--p = '0' + (num % 10);
8175         num /= 10;
8176     }
8177     while(num);
8178     Add(p);
8179 }
8180 
AddPointer(const void * ptr)8181 void VmaStringBuilder::AddPointer(const void* ptr)
8182 {
8183     char buf[21];
8184     VmaPtrToStr(buf, sizeof(buf), ptr);
8185     Add(buf);
8186 }
8187 
8188 #endif // #if VMA_STATS_STRING_ENABLED
8189 
8190 ////////////////////////////////////////////////////////////////////////////////
8191 // VmaJsonWriter
8192 
8193 #if VMA_STATS_STRING_ENABLED
8194 
8195 class VmaJsonWriter
8196 {
8197     VMA_CLASS_NO_COPY(VmaJsonWriter)
8198 public:
8199     VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
8200     ~VmaJsonWriter();
8201 
8202     void BeginObject(bool singleLine = false);
8203     void EndObject();
8204 
8205     void BeginArray(bool singleLine = false);
8206     void EndArray();
8207 
8208     void WriteString(const char* pStr);
8209     void BeginString(const char* pStr = VMA_NULL);
8210     void ContinueString(const char* pStr);
8211     void ContinueString(uint32_t n);
8212     void ContinueString(uint64_t n);
8213     void ContinueString_Pointer(const void* ptr);
8214     void EndString(const char* pStr = VMA_NULL);
8215 
8216     void WriteNumber(uint32_t n);
8217     void WriteNumber(uint64_t n);
8218     void WriteBool(bool b);
8219     void WriteNull();
8220 
8221 private:
8222     static const char* const INDENT;
8223 
8224     enum COLLECTION_TYPE
8225     {
8226         COLLECTION_TYPE_OBJECT,
8227         COLLECTION_TYPE_ARRAY,
8228     };
8229     struct StackItem
8230     {
8231         COLLECTION_TYPE type;
8232         uint32_t valueCount;
8233         bool singleLineMode;
8234     };
8235 
8236     VmaStringBuilder& m_SB;
8237     VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
8238     bool m_InsideString;
8239 
8240     void BeginValue(bool isString);
8241     void WriteIndent(bool oneLess = false);
8242 };
8243 
8244 const char* const VmaJsonWriter::INDENT = "  ";
8245 
VmaJsonWriter(const VkAllocationCallbacks * pAllocationCallbacks,VmaStringBuilder & sb)8246 VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
8247     m_SB(sb),
8248     m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
8249     m_InsideString(false)
8250 {
8251 }
8252 
~VmaJsonWriter()8253 VmaJsonWriter::~VmaJsonWriter()
8254 {
8255     VMA_ASSERT(!m_InsideString);
8256     VMA_ASSERT(m_Stack.empty());
8257 }
8258 
BeginObject(bool singleLine)8259 void VmaJsonWriter::BeginObject(bool singleLine)
8260 {
8261     VMA_ASSERT(!m_InsideString);
8262 
8263     BeginValue(false);
8264     m_SB.Add('{');
8265 
8266     StackItem item;
8267     item.type = COLLECTION_TYPE_OBJECT;
8268     item.valueCount = 0;
8269     item.singleLineMode = singleLine;
8270     m_Stack.push_back(item);
8271 }
8272 
EndObject()8273 void VmaJsonWriter::EndObject()
8274 {
8275     VMA_ASSERT(!m_InsideString);
8276 
8277     WriteIndent(true);
8278     m_SB.Add('}');
8279 
8280     VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
8281     m_Stack.pop_back();
8282 }
8283 
BeginArray(bool singleLine)8284 void VmaJsonWriter::BeginArray(bool singleLine)
8285 {
8286     VMA_ASSERT(!m_InsideString);
8287 
8288     BeginValue(false);
8289     m_SB.Add('[');
8290 
8291     StackItem item;
8292     item.type = COLLECTION_TYPE_ARRAY;
8293     item.valueCount = 0;
8294     item.singleLineMode = singleLine;
8295     m_Stack.push_back(item);
8296 }
8297 
EndArray()8298 void VmaJsonWriter::EndArray()
8299 {
8300     VMA_ASSERT(!m_InsideString);
8301 
8302     WriteIndent(true);
8303     m_SB.Add(']');
8304 
8305     VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
8306     m_Stack.pop_back();
8307 }
8308 
WriteString(const char * pStr)8309 void VmaJsonWriter::WriteString(const char* pStr)
8310 {
8311     BeginString(pStr);
8312     EndString();
8313 }
8314 
BeginString(const char * pStr)8315 void VmaJsonWriter::BeginString(const char* pStr)
8316 {
8317     VMA_ASSERT(!m_InsideString);
8318 
8319     BeginValue(true);
8320     m_SB.Add('"');
8321     m_InsideString = true;
8322     if(pStr != VMA_NULL && pStr[0] != '\0')
8323     {
8324         ContinueString(pStr);
8325     }
8326 }
8327 
ContinueString(const char * pStr)8328 void VmaJsonWriter::ContinueString(const char* pStr)
8329 {
8330     VMA_ASSERT(m_InsideString);
8331 
8332     const size_t strLen = strlen(pStr);
8333     for(size_t i = 0; i < strLen; ++i)
8334     {
8335         char ch = pStr[i];
8336         if(ch == '\\')
8337         {
8338             m_SB.Add("\\\\");
8339         }
8340         else if(ch == '"')
8341         {
8342             m_SB.Add("\\\"");
8343         }
8344         else if(ch >= 32)
8345         {
8346             m_SB.Add(ch);
8347         }
8348         else switch(ch)
8349         {
8350         case '\b':
8351             m_SB.Add("\\b");
8352             break;
8353         case '\f':
8354             m_SB.Add("\\f");
8355             break;
8356         case '\n':
8357             m_SB.Add("\\n");
8358             break;
8359         case '\r':
8360             m_SB.Add("\\r");
8361             break;
8362         case '\t':
8363             m_SB.Add("\\t");
8364             break;
8365         default:
8366             VMA_ASSERT(0 && "Character not currently supported.");
8367             break;
8368         }
8369     }
8370 }
8371 
ContinueString(uint32_t n)8372 void VmaJsonWriter::ContinueString(uint32_t n)
8373 {
8374     VMA_ASSERT(m_InsideString);
8375     m_SB.AddNumber(n);
8376 }
8377 
ContinueString(uint64_t n)8378 void VmaJsonWriter::ContinueString(uint64_t n)
8379 {
8380     VMA_ASSERT(m_InsideString);
8381     m_SB.AddNumber(n);
8382 }
8383 
ContinueString_Pointer(const void * ptr)8384 void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
8385 {
8386     VMA_ASSERT(m_InsideString);
8387     m_SB.AddPointer(ptr);
8388 }
8389 
EndString(const char * pStr)8390 void VmaJsonWriter::EndString(const char* pStr)
8391 {
8392     VMA_ASSERT(m_InsideString);
8393     if(pStr != VMA_NULL && pStr[0] != '\0')
8394     {
8395         ContinueString(pStr);
8396     }
8397     m_SB.Add('"');
8398     m_InsideString = false;
8399 }
8400 
WriteNumber(uint32_t n)8401 void VmaJsonWriter::WriteNumber(uint32_t n)
8402 {
8403     VMA_ASSERT(!m_InsideString);
8404     BeginValue(false);
8405     m_SB.AddNumber(n);
8406 }
8407 
WriteNumber(uint64_t n)8408 void VmaJsonWriter::WriteNumber(uint64_t n)
8409 {
8410     VMA_ASSERT(!m_InsideString);
8411     BeginValue(false);
8412     m_SB.AddNumber(n);
8413 }
8414 
WriteBool(bool b)8415 void VmaJsonWriter::WriteBool(bool b)
8416 {
8417     VMA_ASSERT(!m_InsideString);
8418     BeginValue(false);
8419     m_SB.Add(b ? "true" : "false");
8420 }
8421 
WriteNull()8422 void VmaJsonWriter::WriteNull()
8423 {
8424     VMA_ASSERT(!m_InsideString);
8425     BeginValue(false);
8426     m_SB.Add("null");
8427 }
8428 
BeginValue(bool isString)8429 void VmaJsonWriter::BeginValue(bool isString)
8430 {
8431     if(!m_Stack.empty())
8432     {
8433         StackItem& currItem = m_Stack.back();
8434         if(currItem.type == COLLECTION_TYPE_OBJECT &&
8435             currItem.valueCount % 2 == 0)
8436         {
8437             VMA_ASSERT(isString);
8438         }
8439 
8440         if(currItem.type == COLLECTION_TYPE_OBJECT &&
8441             currItem.valueCount % 2 != 0)
8442         {
8443             m_SB.Add(": ");
8444         }
8445         else if(currItem.valueCount > 0)
8446         {
8447             m_SB.Add(", ");
8448             WriteIndent();
8449         }
8450         else
8451         {
8452             WriteIndent();
8453         }
8454         ++currItem.valueCount;
8455     }
8456 }
8457 
WriteIndent(bool oneLess)8458 void VmaJsonWriter::WriteIndent(bool oneLess)
8459 {
8460     if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
8461     {
8462         m_SB.AddNewLine();
8463 
8464         size_t count = m_Stack.size();
8465         if(count > 0 && oneLess)
8466         {
8467             --count;
8468         }
8469         for(size_t i = 0; i < count; ++i)
8470         {
8471             m_SB.Add(INDENT);
8472         }
8473     }
8474 }
8475 
8476 #endif // #if VMA_STATS_STRING_ENABLED
8477 
8478 ////////////////////////////////////////////////////////////////////////////////
8479 
SetUserData(VmaAllocator hAllocator,void * pUserData)8480 void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
8481 {
8482     if(IsUserDataString())
8483     {
8484         VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
8485 
8486         FreeUserDataString(hAllocator);
8487 
8488         if(pUserData != VMA_NULL)
8489         {
8490             m_pUserData = VmaCreateStringCopy(hAllocator->GetAllocationCallbacks(), (const char*)pUserData);
8491         }
8492     }
8493     else
8494     {
8495         m_pUserData = pUserData;
8496     }
8497 }
8498 
ChangeBlockAllocation(VmaAllocator hAllocator,VmaDeviceMemoryBlock * block,VkDeviceSize offset)8499 void VmaAllocation_T::ChangeBlockAllocation(
8500     VmaAllocator hAllocator,
8501     VmaDeviceMemoryBlock* block,
8502     VkDeviceSize offset)
8503 {
8504     VMA_ASSERT(block != VMA_NULL);
8505     VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
8506 
8507     // Move mapping reference counter from old block to new block.
8508     if(block != m_BlockAllocation.m_Block)
8509     {
8510         uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
8511         if(IsPersistentMap())
8512             ++mapRefCount;
8513         m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
8514         block->Map(hAllocator, mapRefCount, VMA_NULL);
8515     }
8516 
8517     m_BlockAllocation.m_Block = block;
8518     m_BlockAllocation.m_Offset = offset;
8519 }
8520 
ChangeOffset(VkDeviceSize newOffset)8521 void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
8522 {
8523     VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
8524     m_BlockAllocation.m_Offset = newOffset;
8525 }
8526 
GetOffset()8527 VkDeviceSize VmaAllocation_T::GetOffset() const
8528 {
8529     switch(m_Type)
8530     {
8531     case ALLOCATION_TYPE_BLOCK:
8532         return m_BlockAllocation.m_Offset;
8533     case ALLOCATION_TYPE_DEDICATED:
8534         return 0;
8535     default:
8536         VMA_ASSERT(0);
8537         return 0;
8538     }
8539 }
8540 
GetMemory()8541 VkDeviceMemory VmaAllocation_T::GetMemory() const
8542 {
8543     switch(m_Type)
8544     {
8545     case ALLOCATION_TYPE_BLOCK:
8546         return m_BlockAllocation.m_Block->GetDeviceMemory();
8547     case ALLOCATION_TYPE_DEDICATED:
8548         return m_DedicatedAllocation.m_hMemory;
8549     default:
8550         VMA_ASSERT(0);
8551         return VK_NULL_HANDLE;
8552     }
8553 }
8554 
GetMappedData()8555 void* VmaAllocation_T::GetMappedData() const
8556 {
8557     switch(m_Type)
8558     {
8559     case ALLOCATION_TYPE_BLOCK:
8560         if(m_MapCount != 0)
8561         {
8562             void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
8563             VMA_ASSERT(pBlockData != VMA_NULL);
8564             return (char*)pBlockData + m_BlockAllocation.m_Offset;
8565         }
8566         else
8567         {
8568             return VMA_NULL;
8569         }
8570         break;
8571     case ALLOCATION_TYPE_DEDICATED:
8572         VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
8573         return m_DedicatedAllocation.m_pMappedData;
8574     default:
8575         VMA_ASSERT(0);
8576         return VMA_NULL;
8577     }
8578 }
8579 
CanBecomeLost()8580 bool VmaAllocation_T::CanBecomeLost() const
8581 {
8582     switch(m_Type)
8583     {
8584     case ALLOCATION_TYPE_BLOCK:
8585         return m_BlockAllocation.m_CanBecomeLost;
8586     case ALLOCATION_TYPE_DEDICATED:
8587         return false;
8588     default:
8589         VMA_ASSERT(0);
8590         return false;
8591     }
8592 }
8593 
MakeLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)8594 bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
8595 {
8596     VMA_ASSERT(CanBecomeLost());
8597 
8598     /*
8599     Warning: This is a carefully designed algorithm.
8600     Do not modify unless you really know what you're doing :)
8601     */
8602     uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
8603     for(;;)
8604     {
8605         if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
8606         {
8607             VMA_ASSERT(0);
8608             return false;
8609         }
8610         else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
8611         {
8612             return false;
8613         }
8614         else // Last use time earlier than current time.
8615         {
8616             if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
8617             {
8618                 // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
8619                 // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
8620                 return true;
8621             }
8622         }
8623     }
8624 }
8625 
8626 #if VMA_STATS_STRING_ENABLED
8627 
8628 // Correspond to values of enum VmaSuballocationType.
8629 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
8630     "FREE",
8631     "UNKNOWN",
8632     "BUFFER",
8633     "IMAGE_UNKNOWN",
8634     "IMAGE_LINEAR",
8635     "IMAGE_OPTIMAL",
8636 };
8637 
PrintParameters(class VmaJsonWriter & json)8638 void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
8639 {
8640     json.WriteString("Type");
8641     json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
8642 
8643     json.WriteString("Size");
8644     json.WriteNumber(m_Size);
8645 
8646     if(m_pUserData != VMA_NULL)
8647     {
8648         json.WriteString("UserData");
8649         if(IsUserDataString())
8650         {
8651             json.WriteString((const char*)m_pUserData);
8652         }
8653         else
8654         {
8655             json.BeginString();
8656             json.ContinueString_Pointer(m_pUserData);
8657             json.EndString();
8658         }
8659     }
8660 
8661     json.WriteString("CreationFrameIndex");
8662     json.WriteNumber(m_CreationFrameIndex);
8663 
8664     json.WriteString("LastUseFrameIndex");
8665     json.WriteNumber(GetLastUseFrameIndex());
8666 
8667     if(m_BufferImageUsage != 0)
8668     {
8669         json.WriteString("Usage");
8670         json.WriteNumber(m_BufferImageUsage);
8671     }
8672 }
8673 
8674 #endif
8675 
FreeUserDataString(VmaAllocator hAllocator)8676 void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
8677 {
8678     VMA_ASSERT(IsUserDataString());
8679     VmaFreeString(hAllocator->GetAllocationCallbacks(), (char*)m_pUserData);
8680     m_pUserData = VMA_NULL;
8681 }
8682 
BlockAllocMap()8683 void VmaAllocation_T::BlockAllocMap()
8684 {
8685     VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
8686 
8687     if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
8688     {
8689         ++m_MapCount;
8690     }
8691     else
8692     {
8693         VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
8694     }
8695 }
8696 
BlockAllocUnmap()8697 void VmaAllocation_T::BlockAllocUnmap()
8698 {
8699     VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
8700 
8701     if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
8702     {
8703         --m_MapCount;
8704     }
8705     else
8706     {
8707         VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
8708     }
8709 }
8710 
DedicatedAllocMap(VmaAllocator hAllocator,void ** ppData)8711 VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
8712 {
8713     VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
8714 
8715     if(m_MapCount != 0)
8716     {
8717         if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
8718         {
8719             VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
8720             *ppData = m_DedicatedAllocation.m_pMappedData;
8721             ++m_MapCount;
8722             return VK_SUCCESS;
8723         }
8724         else
8725         {
8726             VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
8727             return VK_ERROR_MEMORY_MAP_FAILED;
8728         }
8729     }
8730     else
8731     {
8732         VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
8733             hAllocator->m_hDevice,
8734             m_DedicatedAllocation.m_hMemory,
8735             0, // offset
8736             VK_WHOLE_SIZE,
8737             0, // flags
8738             ppData);
8739         if(result == VK_SUCCESS)
8740         {
8741             m_DedicatedAllocation.m_pMappedData = *ppData;
8742             m_MapCount = 1;
8743         }
8744         return result;
8745     }
8746 }
8747 
DedicatedAllocUnmap(VmaAllocator hAllocator)8748 void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
8749 {
8750     VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
8751 
8752     if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
8753     {
8754         --m_MapCount;
8755         if(m_MapCount == 0)
8756         {
8757             m_DedicatedAllocation.m_pMappedData = VMA_NULL;
8758             (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
8759                 hAllocator->m_hDevice,
8760                 m_DedicatedAllocation.m_hMemory);
8761         }
8762     }
8763     else
8764     {
8765         VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
8766     }
8767 }
8768 
8769 #if VMA_STATS_STRING_ENABLED
8770 
VmaPrintStatInfo(VmaJsonWriter & json,const VmaStatInfo & stat)8771 static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
8772 {
8773     json.BeginObject();
8774 
8775     json.WriteString("Blocks");
8776     json.WriteNumber(stat.blockCount);
8777 
8778     json.WriteString("Allocations");
8779     json.WriteNumber(stat.allocationCount);
8780 
8781     json.WriteString("UnusedRanges");
8782     json.WriteNumber(stat.unusedRangeCount);
8783 
8784     json.WriteString("UsedBytes");
8785     json.WriteNumber(stat.usedBytes);
8786 
8787     json.WriteString("UnusedBytes");
8788     json.WriteNumber(stat.unusedBytes);
8789 
8790     if(stat.allocationCount > 1)
8791     {
8792         json.WriteString("AllocationSize");
8793         json.BeginObject(true);
8794         json.WriteString("Min");
8795         json.WriteNumber(stat.allocationSizeMin);
8796         json.WriteString("Avg");
8797         json.WriteNumber(stat.allocationSizeAvg);
8798         json.WriteString("Max");
8799         json.WriteNumber(stat.allocationSizeMax);
8800         json.EndObject();
8801     }
8802 
8803     if(stat.unusedRangeCount > 1)
8804     {
8805         json.WriteString("UnusedRangeSize");
8806         json.BeginObject(true);
8807         json.WriteString("Min");
8808         json.WriteNumber(stat.unusedRangeSizeMin);
8809         json.WriteString("Avg");
8810         json.WriteNumber(stat.unusedRangeSizeAvg);
8811         json.WriteString("Max");
8812         json.WriteNumber(stat.unusedRangeSizeMax);
8813         json.EndObject();
8814     }
8815 
8816     json.EndObject();
8817 }
8818 
8819 #endif // #if VMA_STATS_STRING_ENABLED
8820 
8821 struct VmaSuballocationItemSizeLess
8822 {
operatorVmaSuballocationItemSizeLess8823     bool operator()(
8824         const VmaSuballocationList::iterator lhs,
8825         const VmaSuballocationList::iterator rhs) const
8826     {
8827         return lhs->size < rhs->size;
8828     }
operatorVmaSuballocationItemSizeLess8829     bool operator()(
8830         const VmaSuballocationList::iterator lhs,
8831         VkDeviceSize rhsSize) const
8832     {
8833         return lhs->size < rhsSize;
8834     }
8835 };
8836 
8837 
8838 ////////////////////////////////////////////////////////////////////////////////
8839 // class VmaBlockMetadata
8840 
VmaBlockMetadata(VmaAllocator hAllocator)8841 VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
8842     m_Size(0),
8843     m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks())
8844 {
8845 }
8846 
8847 #if VMA_STATS_STRING_ENABLED
8848 
PrintDetailedMap_Begin(class VmaJsonWriter & json,VkDeviceSize unusedBytes,size_t allocationCount,size_t unusedRangeCount)8849 void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
8850     VkDeviceSize unusedBytes,
8851     size_t allocationCount,
8852     size_t unusedRangeCount) const
8853 {
8854     json.BeginObject();
8855 
8856     json.WriteString("TotalBytes");
8857     json.WriteNumber(GetSize());
8858 
8859     json.WriteString("UnusedBytes");
8860     json.WriteNumber(unusedBytes);
8861 
8862     json.WriteString("Allocations");
8863     json.WriteNumber((uint64_t)allocationCount);
8864 
8865     json.WriteString("UnusedRanges");
8866     json.WriteNumber((uint64_t)unusedRangeCount);
8867 
8868     json.WriteString("Suballocations");
8869     json.BeginArray();
8870 }
8871 
PrintDetailedMap_Allocation(class VmaJsonWriter & json,VkDeviceSize offset,VmaAllocation hAllocation)8872 void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
8873     VkDeviceSize offset,
8874     VmaAllocation hAllocation) const
8875 {
8876     json.BeginObject(true);
8877 
8878     json.WriteString("Offset");
8879     json.WriteNumber(offset);
8880 
8881     hAllocation->PrintParameters(json);
8882 
8883     json.EndObject();
8884 }
8885 
PrintDetailedMap_UnusedRange(class VmaJsonWriter & json,VkDeviceSize offset,VkDeviceSize size)8886 void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
8887     VkDeviceSize offset,
8888     VkDeviceSize size) const
8889 {
8890     json.BeginObject(true);
8891 
8892     json.WriteString("Offset");
8893     json.WriteNumber(offset);
8894 
8895     json.WriteString("Type");
8896     json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
8897 
8898     json.WriteString("Size");
8899     json.WriteNumber(size);
8900 
8901     json.EndObject();
8902 }
8903 
PrintDetailedMap_End(class VmaJsonWriter & json)8904 void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
8905 {
8906     json.EndArray();
8907     json.EndObject();
8908 }
8909 
8910 #endif // #if VMA_STATS_STRING_ENABLED
8911 
8912 ////////////////////////////////////////////////////////////////////////////////
8913 // class VmaBlockMetadata_Generic
8914 
VmaBlockMetadata_Generic(VmaAllocator hAllocator)8915 VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) :
8916     VmaBlockMetadata(hAllocator),
8917     m_FreeCount(0),
8918     m_SumFreeSize(0),
8919     m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8920     m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
8921 {
8922 }
8923 
~VmaBlockMetadata_Generic()8924 VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
8925 {
8926 }
8927 
Init(VkDeviceSize size)8928 void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
8929 {
8930     VmaBlockMetadata::Init(size);
8931 
8932     m_FreeCount = 1;
8933     m_SumFreeSize = size;
8934 
8935     VmaSuballocation suballoc = {};
8936     suballoc.offset = 0;
8937     suballoc.size = size;
8938     suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8939     suballoc.hAllocation = VK_NULL_HANDLE;
8940 
8941     VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
8942     m_Suballocations.push_back(suballoc);
8943     VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
8944     --suballocItem;
8945     m_FreeSuballocationsBySize.push_back(suballocItem);
8946 }
8947 
Validate()8948 bool VmaBlockMetadata_Generic::Validate() const
8949 {
8950     VMA_VALIDATE(!m_Suballocations.empty());
8951 
8952     // Expected offset of new suballocation as calculated from previous ones.
8953     VkDeviceSize calculatedOffset = 0;
8954     // Expected number of free suballocations as calculated from traversing their list.
8955     uint32_t calculatedFreeCount = 0;
8956     // Expected sum size of free suballocations as calculated from traversing their list.
8957     VkDeviceSize calculatedSumFreeSize = 0;
8958     // Expected number of free suballocations that should be registered in
8959     // m_FreeSuballocationsBySize calculated from traversing their list.
8960     size_t freeSuballocationsToRegister = 0;
8961     // True if previous visited suballocation was free.
8962     bool prevFree = false;
8963 
8964     for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
8965         suballocItem != m_Suballocations.cend();
8966         ++suballocItem)
8967     {
8968         const VmaSuballocation& subAlloc = *suballocItem;
8969 
8970         // Actual offset of this suballocation doesn't match expected one.
8971         VMA_VALIDATE(subAlloc.offset == calculatedOffset);
8972 
8973         const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
8974         // Two adjacent free suballocations are invalid. They should be merged.
8975         VMA_VALIDATE(!prevFree || !currFree);
8976 
8977         VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE));
8978 
8979         if(currFree)
8980         {
8981             calculatedSumFreeSize += subAlloc.size;
8982             ++calculatedFreeCount;
8983             if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8984             {
8985                 ++freeSuballocationsToRegister;
8986             }
8987 
8988             // Margin required between allocations - every free space must be at least that large.
8989             VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);
8990         }
8991         else
8992         {
8993             VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset);
8994             VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size);
8995 
8996             // Margin required between allocations - previous allocation must be free.
8997             VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);
8998         }
8999 
9000         calculatedOffset += subAlloc.size;
9001         prevFree = currFree;
9002     }
9003 
9004     // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
9005     // match expected one.
9006     VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
9007 
9008     VkDeviceSize lastSize = 0;
9009     for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
9010     {
9011         VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
9012 
9013         // Only free suballocations can be registered in m_FreeSuballocationsBySize.
9014         VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
9015         // They must be sorted by size ascending.
9016         VMA_VALIDATE(suballocItem->size >= lastSize);
9017 
9018         lastSize = suballocItem->size;
9019     }
9020 
9021     // Check if totals match calculacted values.
9022     VMA_VALIDATE(ValidateFreeSuballocationList());
9023     VMA_VALIDATE(calculatedOffset == GetSize());
9024     VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
9025     VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
9026 
9027     return true;
9028 }
9029 
GetUnusedRangeSizeMax()9030 VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
9031 {
9032     if(!m_FreeSuballocationsBySize.empty())
9033     {
9034         return m_FreeSuballocationsBySize.back()->size;
9035     }
9036     else
9037     {
9038         return 0;
9039     }
9040 }
9041 
IsEmpty()9042 bool VmaBlockMetadata_Generic::IsEmpty() const
9043 {
9044     return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
9045 }
9046 
CalcAllocationStatInfo(VmaStatInfo & outInfo)9047 void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
9048 {
9049     outInfo.blockCount = 1;
9050 
9051     const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
9052     outInfo.allocationCount = rangeCount - m_FreeCount;
9053     outInfo.unusedRangeCount = m_FreeCount;
9054 
9055     outInfo.unusedBytes = m_SumFreeSize;
9056     outInfo.usedBytes = GetSize() - outInfo.unusedBytes;
9057 
9058     outInfo.allocationSizeMin = UINT64_MAX;
9059     outInfo.allocationSizeMax = 0;
9060     outInfo.unusedRangeSizeMin = UINT64_MAX;
9061     outInfo.unusedRangeSizeMax = 0;
9062 
9063     for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
9064         suballocItem != m_Suballocations.cend();
9065         ++suballocItem)
9066     {
9067         const VmaSuballocation& suballoc = *suballocItem;
9068         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
9069         {
9070             outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9071             outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
9072         }
9073         else
9074         {
9075             outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
9076             outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
9077         }
9078     }
9079 }
9080 
AddPoolStats(VmaPoolStats & inoutStats)9081 void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
9082 {
9083     const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
9084 
9085     inoutStats.size += GetSize();
9086     inoutStats.unusedSize += m_SumFreeSize;
9087     inoutStats.allocationCount += rangeCount - m_FreeCount;
9088     inoutStats.unusedRangeCount += m_FreeCount;
9089     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
9090 }
9091 
9092 #if VMA_STATS_STRING_ENABLED
9093 
PrintDetailedMap(class VmaJsonWriter & json)9094 void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
9095 {
9096     PrintDetailedMap_Begin(json,
9097         m_SumFreeSize, // unusedBytes
9098         m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
9099         m_FreeCount); // unusedRangeCount
9100 
9101     size_t i = 0;
9102     for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
9103         suballocItem != m_Suballocations.cend();
9104         ++suballocItem, ++i)
9105     {
9106         if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9107         {
9108             PrintDetailedMap_UnusedRange(json, suballocItem->offset, suballocItem->size);
9109         }
9110         else
9111         {
9112             PrintDetailedMap_Allocation(json, suballocItem->offset, suballocItem->hAllocation);
9113         }
9114     }
9115 
9116     PrintDetailedMap_End(json);
9117 }
9118 
9119 #endif // #if VMA_STATS_STRING_ENABLED
9120 
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)9121 bool VmaBlockMetadata_Generic::CreateAllocationRequest(
9122     uint32_t currentFrameIndex,
9123     uint32_t frameInUseCount,
9124     VkDeviceSize bufferImageGranularity,
9125     VkDeviceSize allocSize,
9126     VkDeviceSize allocAlignment,
9127     bool upperAddress,
9128     VmaSuballocationType allocType,
9129     bool canMakeOtherLost,
9130     uint32_t strategy,
9131     VmaAllocationRequest* pAllocationRequest)
9132 {
9133     VMA_ASSERT(allocSize > 0);
9134     VMA_ASSERT(!upperAddress);
9135     VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9136     VMA_ASSERT(pAllocationRequest != VMA_NULL);
9137     VMA_HEAVY_ASSERT(Validate());
9138 
9139     pAllocationRequest->type = VmaAllocationRequestType::Normal;
9140 
9141     // There is not enough total free space in this block to fullfill the request: Early return.
9142     if(canMakeOtherLost == false &&
9143         m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN)
9144     {
9145         return false;
9146     }
9147 
9148     // New algorithm, efficiently searching freeSuballocationsBySize.
9149     const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
9150     if(freeSuballocCount > 0)
9151     {
9152         if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
9153         {
9154             // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN.
9155             VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
9156                 m_FreeSuballocationsBySize.data(),
9157                 m_FreeSuballocationsBySize.data() + freeSuballocCount,
9158                 allocSize + 2 * VMA_DEBUG_MARGIN,
9159                 VmaSuballocationItemSizeLess());
9160             size_t index = it - m_FreeSuballocationsBySize.data();
9161             for(; index < freeSuballocCount; ++index)
9162             {
9163                 if(CheckAllocation(
9164                     currentFrameIndex,
9165                     frameInUseCount,
9166                     bufferImageGranularity,
9167                     allocSize,
9168                     allocAlignment,
9169                     allocType,
9170                     m_FreeSuballocationsBySize[index],
9171                     false, // canMakeOtherLost
9172                     &pAllocationRequest->offset,
9173                     &pAllocationRequest->itemsToMakeLostCount,
9174                     &pAllocationRequest->sumFreeSize,
9175                     &pAllocationRequest->sumItemSize))
9176                 {
9177                     pAllocationRequest->item = m_FreeSuballocationsBySize[index];
9178                     return true;
9179                 }
9180             }
9181         }
9182         else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
9183         {
9184             for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9185                 it != m_Suballocations.end();
9186                 ++it)
9187             {
9188                 if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
9189                     currentFrameIndex,
9190                     frameInUseCount,
9191                     bufferImageGranularity,
9192                     allocSize,
9193                     allocAlignment,
9194                     allocType,
9195                     it,
9196                     false, // canMakeOtherLost
9197                     &pAllocationRequest->offset,
9198                     &pAllocationRequest->itemsToMakeLostCount,
9199                     &pAllocationRequest->sumFreeSize,
9200                     &pAllocationRequest->sumItemSize))
9201                 {
9202                     pAllocationRequest->item = it;
9203                     return true;
9204                 }
9205             }
9206         }
9207         else // WORST_FIT, FIRST_FIT
9208         {
9209             // Search staring from biggest suballocations.
9210             for(size_t index = freeSuballocCount; index--; )
9211             {
9212                 if(CheckAllocation(
9213                     currentFrameIndex,
9214                     frameInUseCount,
9215                     bufferImageGranularity,
9216                     allocSize,
9217                     allocAlignment,
9218                     allocType,
9219                     m_FreeSuballocationsBySize[index],
9220                     false, // canMakeOtherLost
9221                     &pAllocationRequest->offset,
9222                     &pAllocationRequest->itemsToMakeLostCount,
9223                     &pAllocationRequest->sumFreeSize,
9224                     &pAllocationRequest->sumItemSize))
9225                 {
9226                     pAllocationRequest->item = m_FreeSuballocationsBySize[index];
9227                     return true;
9228                 }
9229             }
9230         }
9231     }
9232 
9233     if(canMakeOtherLost)
9234     {
9235         // Brute-force algorithm. TODO: Come up with something better.
9236 
9237         bool found = false;
9238         VmaAllocationRequest tmpAllocRequest = {};
9239         tmpAllocRequest.type = VmaAllocationRequestType::Normal;
9240         for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
9241             suballocIt != m_Suballocations.end();
9242             ++suballocIt)
9243         {
9244             if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
9245                 suballocIt->hAllocation->CanBecomeLost())
9246             {
9247                 if(CheckAllocation(
9248                     currentFrameIndex,
9249                     frameInUseCount,
9250                     bufferImageGranularity,
9251                     allocSize,
9252                     allocAlignment,
9253                     allocType,
9254                     suballocIt,
9255                     canMakeOtherLost,
9256                     &tmpAllocRequest.offset,
9257                     &tmpAllocRequest.itemsToMakeLostCount,
9258                     &tmpAllocRequest.sumFreeSize,
9259                     &tmpAllocRequest.sumItemSize))
9260                 {
9261                     if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
9262                     {
9263                         *pAllocationRequest = tmpAllocRequest;
9264                         pAllocationRequest->item = suballocIt;
9265                         break;
9266                     }
9267                     if(!found || tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost())
9268                     {
9269                         *pAllocationRequest = tmpAllocRequest;
9270                         pAllocationRequest->item = suballocIt;
9271                         found = true;
9272                     }
9273                 }
9274             }
9275         }
9276 
9277         return found;
9278     }
9279 
9280     return false;
9281 }
9282 
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)9283 bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
9284     uint32_t currentFrameIndex,
9285     uint32_t frameInUseCount,
9286     VmaAllocationRequest* pAllocationRequest)
9287 {
9288     VMA_ASSERT(pAllocationRequest && pAllocationRequest->type == VmaAllocationRequestType::Normal);
9289 
9290     while(pAllocationRequest->itemsToMakeLostCount > 0)
9291     {
9292         if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
9293         {
9294             ++pAllocationRequest->item;
9295         }
9296         VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
9297         VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
9298         VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
9299         if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
9300         {
9301             pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
9302             --pAllocationRequest->itemsToMakeLostCount;
9303         }
9304         else
9305         {
9306             return false;
9307         }
9308     }
9309 
9310     VMA_HEAVY_ASSERT(Validate());
9311     VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
9312     VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
9313 
9314     return true;
9315 }
9316 
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)9317 uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
9318 {
9319     uint32_t lostAllocationCount = 0;
9320     for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9321         it != m_Suballocations.end();
9322         ++it)
9323     {
9324         if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
9325             it->hAllocation->CanBecomeLost() &&
9326             it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
9327         {
9328             it = FreeSuballocation(it);
9329             ++lostAllocationCount;
9330         }
9331     }
9332     return lostAllocationCount;
9333 }
9334 
CheckCorruption(const void * pBlockData)9335 VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
9336 {
9337     for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9338         it != m_Suballocations.end();
9339         ++it)
9340     {
9341         if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
9342         {
9343             if(!VmaValidateMagicValue(pBlockData, it->offset - VMA_DEBUG_MARGIN))
9344             {
9345                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
9346                 return VK_ERROR_VALIDATION_FAILED_EXT;
9347             }
9348             if(!VmaValidateMagicValue(pBlockData, it->offset + it->size))
9349             {
9350                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
9351                 return VK_ERROR_VALIDATION_FAILED_EXT;
9352             }
9353         }
9354     }
9355 
9356     return VK_SUCCESS;
9357 }
9358 
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,VmaAllocation hAllocation)9359 void VmaBlockMetadata_Generic::Alloc(
9360     const VmaAllocationRequest& request,
9361     VmaSuballocationType type,
9362     VkDeviceSize allocSize,
9363     VmaAllocation hAllocation)
9364 {
9365     VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
9366     VMA_ASSERT(request.item != m_Suballocations.end());
9367     VmaSuballocation& suballoc = *request.item;
9368     // Given suballocation is a free block.
9369     VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9370     // Given offset is inside this suballocation.
9371     VMA_ASSERT(request.offset >= suballoc.offset);
9372     const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
9373     VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
9374     const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
9375 
9376     // Unregister this free suballocation from m_FreeSuballocationsBySize and update
9377     // it to become used.
9378     UnregisterFreeSuballocation(request.item);
9379 
9380     suballoc.offset = request.offset;
9381     suballoc.size = allocSize;
9382     suballoc.type = type;
9383     suballoc.hAllocation = hAllocation;
9384 
9385     // If there are any free bytes remaining at the end, insert new free suballocation after current one.
9386     if(paddingEnd)
9387     {
9388         VmaSuballocation paddingSuballoc = {};
9389         paddingSuballoc.offset = request.offset + allocSize;
9390         paddingSuballoc.size = paddingEnd;
9391         paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9392         VmaSuballocationList::iterator next = request.item;
9393         ++next;
9394         const VmaSuballocationList::iterator paddingEndItem =
9395             m_Suballocations.insert(next, paddingSuballoc);
9396         RegisterFreeSuballocation(paddingEndItem);
9397     }
9398 
9399     // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
9400     if(paddingBegin)
9401     {
9402         VmaSuballocation paddingSuballoc = {};
9403         paddingSuballoc.offset = request.offset - paddingBegin;
9404         paddingSuballoc.size = paddingBegin;
9405         paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9406         const VmaSuballocationList::iterator paddingBeginItem =
9407             m_Suballocations.insert(request.item, paddingSuballoc);
9408         RegisterFreeSuballocation(paddingBeginItem);
9409     }
9410 
9411     // Update totals.
9412     m_FreeCount = m_FreeCount - 1;
9413     if(paddingBegin > 0)
9414     {
9415         ++m_FreeCount;
9416     }
9417     if(paddingEnd > 0)
9418     {
9419         ++m_FreeCount;
9420     }
9421     m_SumFreeSize -= allocSize;
9422 }
9423 
Free(const VmaAllocation allocation)9424 void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation)
9425 {
9426     for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
9427         suballocItem != m_Suballocations.end();
9428         ++suballocItem)
9429     {
9430         VmaSuballocation& suballoc = *suballocItem;
9431         if(suballoc.hAllocation == allocation)
9432         {
9433             FreeSuballocation(suballocItem);
9434             VMA_HEAVY_ASSERT(Validate());
9435             return;
9436         }
9437     }
9438     VMA_ASSERT(0 && "Not found!");
9439 }
9440 
FreeAtOffset(VkDeviceSize offset)9441 void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
9442 {
9443     for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
9444         suballocItem != m_Suballocations.end();
9445         ++suballocItem)
9446     {
9447         VmaSuballocation& suballoc = *suballocItem;
9448         if(suballoc.offset == offset)
9449         {
9450             FreeSuballocation(suballocItem);
9451             return;
9452         }
9453     }
9454     VMA_ASSERT(0 && "Not found!");
9455 }
9456 
ValidateFreeSuballocationList()9457 bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
9458 {
9459     VkDeviceSize lastSize = 0;
9460     for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
9461     {
9462         const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
9463 
9464         VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
9465         VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
9466         VMA_VALIDATE(it->size >= lastSize);
9467         lastSize = it->size;
9468     }
9469     return true;
9470 }
9471 
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)9472 bool VmaBlockMetadata_Generic::CheckAllocation(
9473     uint32_t currentFrameIndex,
9474     uint32_t frameInUseCount,
9475     VkDeviceSize bufferImageGranularity,
9476     VkDeviceSize allocSize,
9477     VkDeviceSize allocAlignment,
9478     VmaSuballocationType allocType,
9479     VmaSuballocationList::const_iterator suballocItem,
9480     bool canMakeOtherLost,
9481     VkDeviceSize* pOffset,
9482     size_t* itemsToMakeLostCount,
9483     VkDeviceSize* pSumFreeSize,
9484     VkDeviceSize* pSumItemSize) const
9485 {
9486     VMA_ASSERT(allocSize > 0);
9487     VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9488     VMA_ASSERT(suballocItem != m_Suballocations.cend());
9489     VMA_ASSERT(pOffset != VMA_NULL);
9490 
9491     *itemsToMakeLostCount = 0;
9492     *pSumFreeSize = 0;
9493     *pSumItemSize = 0;
9494 
9495     if(canMakeOtherLost)
9496     {
9497         if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9498         {
9499             *pSumFreeSize = suballocItem->size;
9500         }
9501         else
9502         {
9503             if(suballocItem->hAllocation->CanBecomeLost() &&
9504                 suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9505             {
9506                 ++*itemsToMakeLostCount;
9507                 *pSumItemSize = suballocItem->size;
9508             }
9509             else
9510             {
9511                 return false;
9512             }
9513         }
9514 
9515         // Remaining size is too small for this request: Early return.
9516         if(GetSize() - suballocItem->offset < allocSize)
9517         {
9518             return false;
9519         }
9520 
9521         // Start from offset equal to beginning of this suballocation.
9522         *pOffset = suballocItem->offset;
9523 
9524         // Apply VMA_DEBUG_MARGIN at the beginning.
9525         if(VMA_DEBUG_MARGIN > 0)
9526         {
9527             *pOffset += VMA_DEBUG_MARGIN;
9528         }
9529 
9530         // Apply alignment.
9531         *pOffset = VmaAlignUp(*pOffset, allocAlignment);
9532 
9533         // Check previous suballocations for BufferImageGranularity conflicts.
9534         // Make bigger alignment if necessary.
9535         if(bufferImageGranularity > 1)
9536         {
9537             bool bufferImageGranularityConflict = false;
9538             VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
9539             while(prevSuballocItem != m_Suballocations.cbegin())
9540             {
9541                 --prevSuballocItem;
9542                 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
9543                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
9544                 {
9545                     if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9546                     {
9547                         bufferImageGranularityConflict = true;
9548                         break;
9549                     }
9550                 }
9551                 else
9552                     // Already on previous page.
9553                     break;
9554             }
9555             if(bufferImageGranularityConflict)
9556             {
9557                 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
9558             }
9559         }
9560 
9561         // Now that we have final *pOffset, check if we are past suballocItem.
9562         // If yes, return false - this function should be called for another suballocItem as starting point.
9563         if(*pOffset >= suballocItem->offset + suballocItem->size)
9564         {
9565             return false;
9566         }
9567 
9568         // Calculate padding at the beginning based on current offset.
9569         const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
9570 
9571         // Calculate required margin at the end.
9572         const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
9573 
9574         const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
9575         // Another early return check.
9576         if(suballocItem->offset + totalSize > GetSize())
9577         {
9578             return false;
9579         }
9580 
9581         // Advance lastSuballocItem until desired size is reached.
9582         // Update itemsToMakeLostCount.
9583         VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
9584         if(totalSize > suballocItem->size)
9585         {
9586             VkDeviceSize remainingSize = totalSize - suballocItem->size;
9587             while(remainingSize > 0)
9588             {
9589                 ++lastSuballocItem;
9590                 if(lastSuballocItem == m_Suballocations.cend())
9591                 {
9592                     return false;
9593                 }
9594                 if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9595                 {
9596                     *pSumFreeSize += lastSuballocItem->size;
9597                 }
9598                 else
9599                 {
9600                     VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
9601                     if(lastSuballocItem->hAllocation->CanBecomeLost() &&
9602                         lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9603                     {
9604                         ++*itemsToMakeLostCount;
9605                         *pSumItemSize += lastSuballocItem->size;
9606                     }
9607                     else
9608                     {
9609                         return false;
9610                     }
9611                 }
9612                 remainingSize = (lastSuballocItem->size < remainingSize) ?
9613                     remainingSize - lastSuballocItem->size : 0;
9614             }
9615         }
9616 
9617         // Check next suballocations for BufferImageGranularity conflicts.
9618         // If conflict exists, we must mark more allocations lost or fail.
9619         if(bufferImageGranularity > 1)
9620         {
9621             VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
9622             ++nextSuballocItem;
9623             while(nextSuballocItem != m_Suballocations.cend())
9624             {
9625                 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
9626                 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9627                 {
9628                     if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9629                     {
9630                         VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
9631                         if(nextSuballoc.hAllocation->CanBecomeLost() &&
9632                             nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9633                         {
9634                             ++*itemsToMakeLostCount;
9635                         }
9636                         else
9637                         {
9638                             return false;
9639                         }
9640                     }
9641                 }
9642                 else
9643                 {
9644                     // Already on next page.
9645                     break;
9646                 }
9647                 ++nextSuballocItem;
9648             }
9649         }
9650     }
9651     else
9652     {
9653         const VmaSuballocation& suballoc = *suballocItem;
9654         VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9655 
9656         *pSumFreeSize = suballoc.size;
9657 
9658         // Size of this suballocation is too small for this request: Early return.
9659         if(suballoc.size < allocSize)
9660         {
9661             return false;
9662         }
9663 
9664         // Start from offset equal to beginning of this suballocation.
9665         *pOffset = suballoc.offset;
9666 
9667         // Apply VMA_DEBUG_MARGIN at the beginning.
9668         if(VMA_DEBUG_MARGIN > 0)
9669         {
9670             *pOffset += VMA_DEBUG_MARGIN;
9671         }
9672 
9673         // Apply alignment.
9674         *pOffset = VmaAlignUp(*pOffset, allocAlignment);
9675 
9676         // Check previous suballocations for BufferImageGranularity conflicts.
9677         // Make bigger alignment if necessary.
9678         if(bufferImageGranularity > 1)
9679         {
9680             bool bufferImageGranularityConflict = false;
9681             VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
9682             while(prevSuballocItem != m_Suballocations.cbegin())
9683             {
9684                 --prevSuballocItem;
9685                 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
9686                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
9687                 {
9688                     if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9689                     {
9690                         bufferImageGranularityConflict = true;
9691                         break;
9692                     }
9693                 }
9694                 else
9695                     // Already on previous page.
9696                     break;
9697             }
9698             if(bufferImageGranularityConflict)
9699             {
9700                 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
9701             }
9702         }
9703 
9704         // Calculate padding at the beginning based on current offset.
9705         const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
9706 
9707         // Calculate required margin at the end.
9708         const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
9709 
9710         // Fail if requested size plus margin before and after is bigger than size of this suballocation.
9711         if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
9712         {
9713             return false;
9714         }
9715 
9716         // Check next suballocations for BufferImageGranularity conflicts.
9717         // If conflict exists, allocation cannot be made here.
9718         if(bufferImageGranularity > 1)
9719         {
9720             VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
9721             ++nextSuballocItem;
9722             while(nextSuballocItem != m_Suballocations.cend())
9723             {
9724                 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
9725                 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9726                 {
9727                     if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9728                     {
9729                         return false;
9730                     }
9731                 }
9732                 else
9733                 {
9734                     // Already on next page.
9735                     break;
9736                 }
9737                 ++nextSuballocItem;
9738             }
9739         }
9740     }
9741 
9742     // All tests passed: Success. pOffset is already filled.
9743     return true;
9744 }
9745 
MergeFreeWithNext(VmaSuballocationList::iterator item)9746 void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
9747 {
9748     VMA_ASSERT(item != m_Suballocations.end());
9749     VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9750 
9751     VmaSuballocationList::iterator nextItem = item;
9752     ++nextItem;
9753     VMA_ASSERT(nextItem != m_Suballocations.end());
9754     VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
9755 
9756     item->size += nextItem->size;
9757     --m_FreeCount;
9758     m_Suballocations.erase(nextItem);
9759 }
9760 
FreeSuballocation(VmaSuballocationList::iterator suballocItem)9761 VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
9762 {
9763     // Change this suballocation to be marked as free.
9764     VmaSuballocation& suballoc = *suballocItem;
9765     suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9766     suballoc.hAllocation = VK_NULL_HANDLE;
9767 
9768     // Update totals.
9769     ++m_FreeCount;
9770     m_SumFreeSize += suballoc.size;
9771 
9772     // Merge with previous and/or next suballocation if it's also free.
9773     bool mergeWithNext = false;
9774     bool mergeWithPrev = false;
9775 
9776     VmaSuballocationList::iterator nextItem = suballocItem;
9777     ++nextItem;
9778     if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
9779     {
9780         mergeWithNext = true;
9781     }
9782 
9783     VmaSuballocationList::iterator prevItem = suballocItem;
9784     if(suballocItem != m_Suballocations.begin())
9785     {
9786         --prevItem;
9787         if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9788         {
9789             mergeWithPrev = true;
9790         }
9791     }
9792 
9793     if(mergeWithNext)
9794     {
9795         UnregisterFreeSuballocation(nextItem);
9796         MergeFreeWithNext(suballocItem);
9797     }
9798 
9799     if(mergeWithPrev)
9800     {
9801         UnregisterFreeSuballocation(prevItem);
9802         MergeFreeWithNext(prevItem);
9803         RegisterFreeSuballocation(prevItem);
9804         return prevItem;
9805     }
9806     else
9807     {
9808         RegisterFreeSuballocation(suballocItem);
9809         return suballocItem;
9810     }
9811 }
9812 
RegisterFreeSuballocation(VmaSuballocationList::iterator item)9813 void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
9814 {
9815     VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9816     VMA_ASSERT(item->size > 0);
9817 
9818     // You may want to enable this validation at the beginning or at the end of
9819     // this function, depending on what do you want to check.
9820     VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9821 
9822     if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
9823     {
9824         if(m_FreeSuballocationsBySize.empty())
9825         {
9826             m_FreeSuballocationsBySize.push_back(item);
9827         }
9828         else
9829         {
9830             VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
9831         }
9832     }
9833 
9834     //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9835 }
9836 
9837 
UnregisterFreeSuballocation(VmaSuballocationList::iterator item)9838 void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
9839 {
9840     VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9841     VMA_ASSERT(item->size > 0);
9842 
9843     // You may want to enable this validation at the beginning or at the end of
9844     // this function, depending on what do you want to check.
9845     VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9846 
9847     if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
9848     {
9849         VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
9850             m_FreeSuballocationsBySize.data(),
9851             m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
9852             item,
9853             VmaSuballocationItemSizeLess());
9854         for(size_t index = it - m_FreeSuballocationsBySize.data();
9855             index < m_FreeSuballocationsBySize.size();
9856             ++index)
9857         {
9858             if(m_FreeSuballocationsBySize[index] == item)
9859             {
9860                 VmaVectorRemove(m_FreeSuballocationsBySize, index);
9861                 return;
9862             }
9863             VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
9864         }
9865         VMA_ASSERT(0 && "Not found.");
9866     }
9867 
9868     //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9869 }
9870 
IsBufferImageGranularityConflictPossible(VkDeviceSize bufferImageGranularity,VmaSuballocationType & inOutPrevSuballocType)9871 bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
9872     VkDeviceSize bufferImageGranularity,
9873     VmaSuballocationType& inOutPrevSuballocType) const
9874 {
9875     if(bufferImageGranularity == 1 || IsEmpty())
9876     {
9877         return false;
9878     }
9879 
9880     VkDeviceSize minAlignment = VK_WHOLE_SIZE;
9881     bool typeConflictFound = false;
9882     for(VmaSuballocationList::const_iterator it = m_Suballocations.cbegin();
9883         it != m_Suballocations.cend();
9884         ++it)
9885     {
9886         const VmaSuballocationType suballocType = it->type;
9887         if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)
9888         {
9889             minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment());
9890             if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))
9891             {
9892                 typeConflictFound = true;
9893             }
9894             inOutPrevSuballocType = suballocType;
9895         }
9896     }
9897 
9898     return typeConflictFound || minAlignment >= bufferImageGranularity;
9899 }
9900 
9901 ////////////////////////////////////////////////////////////////////////////////
9902 // class VmaBlockMetadata_Linear
9903 
VmaBlockMetadata_Linear(VmaAllocator hAllocator)9904 VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) :
9905     VmaBlockMetadata(hAllocator),
9906     m_SumFreeSize(0),
9907     m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
9908     m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
9909     m_1stVectorIndex(0),
9910     m_2ndVectorMode(SECOND_VECTOR_EMPTY),
9911     m_1stNullItemsBeginCount(0),
9912     m_1stNullItemsMiddleCount(0),
9913     m_2ndNullItemsCount(0)
9914 {
9915 }
9916 
~VmaBlockMetadata_Linear()9917 VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
9918 {
9919 }
9920 
Init(VkDeviceSize size)9921 void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
9922 {
9923     VmaBlockMetadata::Init(size);
9924     m_SumFreeSize = size;
9925 }
9926 
Validate()9927 bool VmaBlockMetadata_Linear::Validate() const
9928 {
9929     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9930     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9931 
9932     VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
9933     VMA_VALIDATE(!suballocations1st.empty() ||
9934         suballocations2nd.empty() ||
9935         m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
9936 
9937     if(!suballocations1st.empty())
9938     {
9939         // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
9940         VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE);
9941         // Null item at the end should be just pop_back().
9942         VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE);
9943     }
9944     if(!suballocations2nd.empty())
9945     {
9946         // Null item at the end should be just pop_back().
9947         VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE);
9948     }
9949 
9950     VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
9951     VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
9952 
9953     VkDeviceSize sumUsedSize = 0;
9954     const size_t suballoc1stCount = suballocations1st.size();
9955     VkDeviceSize offset = VMA_DEBUG_MARGIN;
9956 
9957     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9958     {
9959         const size_t suballoc2ndCount = suballocations2nd.size();
9960         size_t nullItem2ndCount = 0;
9961         for(size_t i = 0; i < suballoc2ndCount; ++i)
9962         {
9963             const VmaSuballocation& suballoc = suballocations2nd[i];
9964             const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9965 
9966             VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
9967             VMA_VALIDATE(suballoc.offset >= offset);
9968 
9969             if(!currFree)
9970             {
9971                 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
9972                 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
9973                 sumUsedSize += suballoc.size;
9974             }
9975             else
9976             {
9977                 ++nullItem2ndCount;
9978             }
9979 
9980             offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
9981         }
9982 
9983         VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
9984     }
9985 
9986     for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
9987     {
9988         const VmaSuballocation& suballoc = suballocations1st[i];
9989         VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
9990             suballoc.hAllocation == VK_NULL_HANDLE);
9991     }
9992 
9993     size_t nullItem1stCount = m_1stNullItemsBeginCount;
9994 
9995     for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
9996     {
9997         const VmaSuballocation& suballoc = suballocations1st[i];
9998         const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9999 
10000         VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
10001         VMA_VALIDATE(suballoc.offset >= offset);
10002         VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
10003 
10004         if(!currFree)
10005         {
10006             VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
10007             VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
10008             sumUsedSize += suballoc.size;
10009         }
10010         else
10011         {
10012             ++nullItem1stCount;
10013         }
10014 
10015         offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
10016     }
10017     VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
10018 
10019     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10020     {
10021         const size_t suballoc2ndCount = suballocations2nd.size();
10022         size_t nullItem2ndCount = 0;
10023         for(size_t i = suballoc2ndCount; i--; )
10024         {
10025             const VmaSuballocation& suballoc = suballocations2nd[i];
10026             const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
10027 
10028             VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
10029             VMA_VALIDATE(suballoc.offset >= offset);
10030 
10031             if(!currFree)
10032             {
10033                 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
10034                 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
10035                 sumUsedSize += suballoc.size;
10036             }
10037             else
10038             {
10039                 ++nullItem2ndCount;
10040             }
10041 
10042             offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
10043         }
10044 
10045         VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
10046     }
10047 
10048     VMA_VALIDATE(offset <= GetSize());
10049     VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
10050 
10051     return true;
10052 }
10053 
GetAllocationCount()10054 size_t VmaBlockMetadata_Linear::GetAllocationCount() const
10055 {
10056     return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) +
10057         AccessSuballocations2nd().size() - m_2ndNullItemsCount;
10058 }
10059 
GetUnusedRangeSizeMax()10060 VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
10061 {
10062     const VkDeviceSize size = GetSize();
10063 
10064     /*
10065     We don't consider gaps inside allocation vectors with freed allocations because
10066     they are not suitable for reuse in linear allocator. We consider only space that
10067     is available for new allocations.
10068     */
10069     if(IsEmpty())
10070     {
10071         return size;
10072     }
10073 
10074     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10075 
10076     switch(m_2ndVectorMode)
10077     {
10078     case SECOND_VECTOR_EMPTY:
10079         /*
10080         Available space is after end of 1st, as well as before beginning of 1st (which
10081         whould make it a ring buffer).
10082         */
10083         {
10084             const size_t suballocations1stCount = suballocations1st.size();
10085             VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
10086             const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
10087             const VmaSuballocation& lastSuballoc  = suballocations1st[suballocations1stCount - 1];
10088             return VMA_MAX(
10089                 firstSuballoc.offset,
10090                 size - (lastSuballoc.offset + lastSuballoc.size));
10091         }
10092         break;
10093 
10094     case SECOND_VECTOR_RING_BUFFER:
10095         /*
10096         Available space is only between end of 2nd and beginning of 1st.
10097         */
10098         {
10099             const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10100             const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
10101             const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
10102             return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
10103         }
10104         break;
10105 
10106     case SECOND_VECTOR_DOUBLE_STACK:
10107         /*
10108         Available space is only between end of 1st and top of 2nd.
10109         */
10110         {
10111             const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10112             const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
10113             const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
10114             return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
10115         }
10116         break;
10117 
10118     default:
10119         VMA_ASSERT(0);
10120         return 0;
10121     }
10122 }
10123 
CalcAllocationStatInfo(VmaStatInfo & outInfo)10124 void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
10125 {
10126     const VkDeviceSize size = GetSize();
10127     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10128     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10129     const size_t suballoc1stCount = suballocations1st.size();
10130     const size_t suballoc2ndCount = suballocations2nd.size();
10131 
10132     outInfo.blockCount = 1;
10133     outInfo.allocationCount = (uint32_t)GetAllocationCount();
10134     outInfo.unusedRangeCount = 0;
10135     outInfo.usedBytes = 0;
10136     outInfo.allocationSizeMin = UINT64_MAX;
10137     outInfo.allocationSizeMax = 0;
10138     outInfo.unusedRangeSizeMin = UINT64_MAX;
10139     outInfo.unusedRangeSizeMax = 0;
10140 
10141     VkDeviceSize lastOffset = 0;
10142 
10143     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10144     {
10145         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10146         size_t nextAlloc2ndIndex = 0;
10147         while(lastOffset < freeSpace2ndTo1stEnd)
10148         {
10149             // Find next non-null allocation or move nextAllocIndex to the end.
10150             while(nextAlloc2ndIndex < suballoc2ndCount &&
10151                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10152             {
10153                 ++nextAlloc2ndIndex;
10154             }
10155 
10156             // Found non-null allocation.
10157             if(nextAlloc2ndIndex < suballoc2ndCount)
10158             {
10159                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10160 
10161                 // 1. Process free space before this allocation.
10162                 if(lastOffset < suballoc.offset)
10163                 {
10164                     // There is free space from lastOffset to suballoc.offset.
10165                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10166                     ++outInfo.unusedRangeCount;
10167                     outInfo.unusedBytes += unusedRangeSize;
10168                     outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10169                     outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10170                 }
10171 
10172                 // 2. Process this allocation.
10173                 // There is allocation with suballoc.offset, suballoc.size.
10174                 outInfo.usedBytes += suballoc.size;
10175                 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10176                 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10177 
10178                 // 3. Prepare for next iteration.
10179                 lastOffset = suballoc.offset + suballoc.size;
10180                 ++nextAlloc2ndIndex;
10181             }
10182             // We are at the end.
10183             else
10184             {
10185                 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10186                 if(lastOffset < freeSpace2ndTo1stEnd)
10187                 {
10188                     const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10189                     ++outInfo.unusedRangeCount;
10190                     outInfo.unusedBytes += unusedRangeSize;
10191                     outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10192                     outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10193                }
10194 
10195                 // End of loop.
10196                 lastOffset = freeSpace2ndTo1stEnd;
10197             }
10198         }
10199     }
10200 
10201     size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10202     const VkDeviceSize freeSpace1stTo2ndEnd =
10203         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10204     while(lastOffset < freeSpace1stTo2ndEnd)
10205     {
10206         // Find next non-null allocation or move nextAllocIndex to the end.
10207         while(nextAlloc1stIndex < suballoc1stCount &&
10208             suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10209         {
10210             ++nextAlloc1stIndex;
10211         }
10212 
10213         // Found non-null allocation.
10214         if(nextAlloc1stIndex < suballoc1stCount)
10215         {
10216             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10217 
10218             // 1. Process free space before this allocation.
10219             if(lastOffset < suballoc.offset)
10220             {
10221                 // There is free space from lastOffset to suballoc.offset.
10222                 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10223                 ++outInfo.unusedRangeCount;
10224                 outInfo.unusedBytes += unusedRangeSize;
10225                 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10226                 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10227             }
10228 
10229             // 2. Process this allocation.
10230             // There is allocation with suballoc.offset, suballoc.size.
10231             outInfo.usedBytes += suballoc.size;
10232             outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10233             outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10234 
10235             // 3. Prepare for next iteration.
10236             lastOffset = suballoc.offset + suballoc.size;
10237             ++nextAlloc1stIndex;
10238         }
10239         // We are at the end.
10240         else
10241         {
10242             // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10243             if(lastOffset < freeSpace1stTo2ndEnd)
10244             {
10245                 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10246                 ++outInfo.unusedRangeCount;
10247                 outInfo.unusedBytes += unusedRangeSize;
10248                 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10249                 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10250            }
10251 
10252             // End of loop.
10253             lastOffset = freeSpace1stTo2ndEnd;
10254         }
10255     }
10256 
10257     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10258     {
10259         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10260         while(lastOffset < size)
10261         {
10262             // Find next non-null allocation or move nextAllocIndex to the end.
10263             while(nextAlloc2ndIndex != SIZE_MAX &&
10264                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10265             {
10266                 --nextAlloc2ndIndex;
10267             }
10268 
10269             // Found non-null allocation.
10270             if(nextAlloc2ndIndex != SIZE_MAX)
10271             {
10272                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10273 
10274                 // 1. Process free space before this allocation.
10275                 if(lastOffset < suballoc.offset)
10276                 {
10277                     // There is free space from lastOffset to suballoc.offset.
10278                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10279                     ++outInfo.unusedRangeCount;
10280                     outInfo.unusedBytes += unusedRangeSize;
10281                     outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10282                     outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10283                 }
10284 
10285                 // 2. Process this allocation.
10286                 // There is allocation with suballoc.offset, suballoc.size.
10287                 outInfo.usedBytes += suballoc.size;
10288                 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10289                 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10290 
10291                 // 3. Prepare for next iteration.
10292                 lastOffset = suballoc.offset + suballoc.size;
10293                 --nextAlloc2ndIndex;
10294             }
10295             // We are at the end.
10296             else
10297             {
10298                 // There is free space from lastOffset to size.
10299                 if(lastOffset < size)
10300                 {
10301                     const VkDeviceSize unusedRangeSize = size - lastOffset;
10302                     ++outInfo.unusedRangeCount;
10303                     outInfo.unusedBytes += unusedRangeSize;
10304                     outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10305                     outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10306                }
10307 
10308                 // End of loop.
10309                 lastOffset = size;
10310             }
10311         }
10312     }
10313 
10314     outInfo.unusedBytes = size - outInfo.usedBytes;
10315 }
10316 
AddPoolStats(VmaPoolStats & inoutStats)10317 void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
10318 {
10319     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10320     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10321     const VkDeviceSize size = GetSize();
10322     const size_t suballoc1stCount = suballocations1st.size();
10323     const size_t suballoc2ndCount = suballocations2nd.size();
10324 
10325     inoutStats.size += size;
10326 
10327     VkDeviceSize lastOffset = 0;
10328 
10329     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10330     {
10331         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10332         size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
10333         while(lastOffset < freeSpace2ndTo1stEnd)
10334         {
10335             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10336             while(nextAlloc2ndIndex < suballoc2ndCount &&
10337                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10338             {
10339                 ++nextAlloc2ndIndex;
10340             }
10341 
10342             // Found non-null allocation.
10343             if(nextAlloc2ndIndex < suballoc2ndCount)
10344             {
10345                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10346 
10347                 // 1. Process free space before this allocation.
10348                 if(lastOffset < suballoc.offset)
10349                 {
10350                     // There is free space from lastOffset to suballoc.offset.
10351                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10352                     inoutStats.unusedSize += unusedRangeSize;
10353                     ++inoutStats.unusedRangeCount;
10354                     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10355                 }
10356 
10357                 // 2. Process this allocation.
10358                 // There is allocation with suballoc.offset, suballoc.size.
10359                 ++inoutStats.allocationCount;
10360 
10361                 // 3. Prepare for next iteration.
10362                 lastOffset = suballoc.offset + suballoc.size;
10363                 ++nextAlloc2ndIndex;
10364             }
10365             // We are at the end.
10366             else
10367             {
10368                 if(lastOffset < freeSpace2ndTo1stEnd)
10369                 {
10370                     // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10371                     const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10372                     inoutStats.unusedSize += unusedRangeSize;
10373                     ++inoutStats.unusedRangeCount;
10374                     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10375                 }
10376 
10377                 // End of loop.
10378                 lastOffset = freeSpace2ndTo1stEnd;
10379             }
10380         }
10381     }
10382 
10383     size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10384     const VkDeviceSize freeSpace1stTo2ndEnd =
10385         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10386     while(lastOffset < freeSpace1stTo2ndEnd)
10387     {
10388         // Find next non-null allocation or move nextAllocIndex to the end.
10389         while(nextAlloc1stIndex < suballoc1stCount &&
10390             suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10391         {
10392             ++nextAlloc1stIndex;
10393         }
10394 
10395         // Found non-null allocation.
10396         if(nextAlloc1stIndex < suballoc1stCount)
10397         {
10398             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10399 
10400             // 1. Process free space before this allocation.
10401             if(lastOffset < suballoc.offset)
10402             {
10403                 // There is free space from lastOffset to suballoc.offset.
10404                 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10405                 inoutStats.unusedSize += unusedRangeSize;
10406                 ++inoutStats.unusedRangeCount;
10407                 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10408             }
10409 
10410             // 2. Process this allocation.
10411             // There is allocation with suballoc.offset, suballoc.size.
10412             ++inoutStats.allocationCount;
10413 
10414             // 3. Prepare for next iteration.
10415             lastOffset = suballoc.offset + suballoc.size;
10416             ++nextAlloc1stIndex;
10417         }
10418         // We are at the end.
10419         else
10420         {
10421             if(lastOffset < freeSpace1stTo2ndEnd)
10422             {
10423                 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10424                 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10425                 inoutStats.unusedSize += unusedRangeSize;
10426                 ++inoutStats.unusedRangeCount;
10427                 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10428             }
10429 
10430             // End of loop.
10431             lastOffset = freeSpace1stTo2ndEnd;
10432         }
10433     }
10434 
10435     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10436     {
10437         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10438         while(lastOffset < size)
10439         {
10440             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10441             while(nextAlloc2ndIndex != SIZE_MAX &&
10442                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10443             {
10444                 --nextAlloc2ndIndex;
10445             }
10446 
10447             // Found non-null allocation.
10448             if(nextAlloc2ndIndex != SIZE_MAX)
10449             {
10450                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10451 
10452                 // 1. Process free space before this allocation.
10453                 if(lastOffset < suballoc.offset)
10454                 {
10455                     // There is free space from lastOffset to suballoc.offset.
10456                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10457                     inoutStats.unusedSize += unusedRangeSize;
10458                     ++inoutStats.unusedRangeCount;
10459                     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10460                 }
10461 
10462                 // 2. Process this allocation.
10463                 // There is allocation with suballoc.offset, suballoc.size.
10464                 ++inoutStats.allocationCount;
10465 
10466                 // 3. Prepare for next iteration.
10467                 lastOffset = suballoc.offset + suballoc.size;
10468                 --nextAlloc2ndIndex;
10469             }
10470             // We are at the end.
10471             else
10472             {
10473                 if(lastOffset < size)
10474                 {
10475                     // There is free space from lastOffset to size.
10476                     const VkDeviceSize unusedRangeSize = size - lastOffset;
10477                     inoutStats.unusedSize += unusedRangeSize;
10478                     ++inoutStats.unusedRangeCount;
10479                     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10480                 }
10481 
10482                 // End of loop.
10483                 lastOffset = size;
10484             }
10485         }
10486     }
10487 }
10488 
10489 #if VMA_STATS_STRING_ENABLED
PrintDetailedMap(class VmaJsonWriter & json)10490 void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
10491 {
10492     const VkDeviceSize size = GetSize();
10493     const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10494     const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10495     const size_t suballoc1stCount = suballocations1st.size();
10496     const size_t suballoc2ndCount = suballocations2nd.size();
10497 
10498     // FIRST PASS
10499 
10500     size_t unusedRangeCount = 0;
10501     VkDeviceSize usedBytes = 0;
10502 
10503     VkDeviceSize lastOffset = 0;
10504 
10505     size_t alloc2ndCount = 0;
10506     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10507     {
10508         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10509         size_t nextAlloc2ndIndex = 0;
10510         while(lastOffset < freeSpace2ndTo1stEnd)
10511         {
10512             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10513             while(nextAlloc2ndIndex < suballoc2ndCount &&
10514                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10515             {
10516                 ++nextAlloc2ndIndex;
10517             }
10518 
10519             // Found non-null allocation.
10520             if(nextAlloc2ndIndex < suballoc2ndCount)
10521             {
10522                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10523 
10524                 // 1. Process free space before this allocation.
10525                 if(lastOffset < suballoc.offset)
10526                 {
10527                     // There is free space from lastOffset to suballoc.offset.
10528                     ++unusedRangeCount;
10529                 }
10530 
10531                 // 2. Process this allocation.
10532                 // There is allocation with suballoc.offset, suballoc.size.
10533                 ++alloc2ndCount;
10534                 usedBytes += suballoc.size;
10535 
10536                 // 3. Prepare for next iteration.
10537                 lastOffset = suballoc.offset + suballoc.size;
10538                 ++nextAlloc2ndIndex;
10539             }
10540             // We are at the end.
10541             else
10542             {
10543                 if(lastOffset < freeSpace2ndTo1stEnd)
10544                 {
10545                     // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10546                     ++unusedRangeCount;
10547                 }
10548 
10549                 // End of loop.
10550                 lastOffset = freeSpace2ndTo1stEnd;
10551             }
10552         }
10553     }
10554 
10555     size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10556     size_t alloc1stCount = 0;
10557     const VkDeviceSize freeSpace1stTo2ndEnd =
10558         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10559     while(lastOffset < freeSpace1stTo2ndEnd)
10560     {
10561         // Find next non-null allocation or move nextAllocIndex to the end.
10562         while(nextAlloc1stIndex < suballoc1stCount &&
10563             suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10564         {
10565             ++nextAlloc1stIndex;
10566         }
10567 
10568         // Found non-null allocation.
10569         if(nextAlloc1stIndex < suballoc1stCount)
10570         {
10571             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10572 
10573             // 1. Process free space before this allocation.
10574             if(lastOffset < suballoc.offset)
10575             {
10576                 // There is free space from lastOffset to suballoc.offset.
10577                 ++unusedRangeCount;
10578             }
10579 
10580             // 2. Process this allocation.
10581             // There is allocation with suballoc.offset, suballoc.size.
10582             ++alloc1stCount;
10583             usedBytes += suballoc.size;
10584 
10585             // 3. Prepare for next iteration.
10586             lastOffset = suballoc.offset + suballoc.size;
10587             ++nextAlloc1stIndex;
10588         }
10589         // We are at the end.
10590         else
10591         {
10592             if(lastOffset < size)
10593             {
10594                 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10595                 ++unusedRangeCount;
10596             }
10597 
10598             // End of loop.
10599             lastOffset = freeSpace1stTo2ndEnd;
10600         }
10601     }
10602 
10603     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10604     {
10605         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10606         while(lastOffset < size)
10607         {
10608             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10609             while(nextAlloc2ndIndex != SIZE_MAX &&
10610                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10611             {
10612                 --nextAlloc2ndIndex;
10613             }
10614 
10615             // Found non-null allocation.
10616             if(nextAlloc2ndIndex != SIZE_MAX)
10617             {
10618                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10619 
10620                 // 1. Process free space before this allocation.
10621                 if(lastOffset < suballoc.offset)
10622                 {
10623                     // There is free space from lastOffset to suballoc.offset.
10624                     ++unusedRangeCount;
10625                 }
10626 
10627                 // 2. Process this allocation.
10628                 // There is allocation with suballoc.offset, suballoc.size.
10629                 ++alloc2ndCount;
10630                 usedBytes += suballoc.size;
10631 
10632                 // 3. Prepare for next iteration.
10633                 lastOffset = suballoc.offset + suballoc.size;
10634                 --nextAlloc2ndIndex;
10635             }
10636             // We are at the end.
10637             else
10638             {
10639                 if(lastOffset < size)
10640                 {
10641                     // There is free space from lastOffset to size.
10642                     ++unusedRangeCount;
10643                 }
10644 
10645                 // End of loop.
10646                 lastOffset = size;
10647             }
10648         }
10649     }
10650 
10651     const VkDeviceSize unusedBytes = size - usedBytes;
10652     PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
10653 
10654     // SECOND PASS
10655     lastOffset = 0;
10656 
10657     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10658     {
10659         const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10660         size_t nextAlloc2ndIndex = 0;
10661         while(lastOffset < freeSpace2ndTo1stEnd)
10662         {
10663             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10664             while(nextAlloc2ndIndex < suballoc2ndCount &&
10665                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10666             {
10667                 ++nextAlloc2ndIndex;
10668             }
10669 
10670             // Found non-null allocation.
10671             if(nextAlloc2ndIndex < suballoc2ndCount)
10672             {
10673                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10674 
10675                 // 1. Process free space before this allocation.
10676                 if(lastOffset < suballoc.offset)
10677                 {
10678                     // There is free space from lastOffset to suballoc.offset.
10679                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10680                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10681                 }
10682 
10683                 // 2. Process this allocation.
10684                 // There is allocation with suballoc.offset, suballoc.size.
10685                 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10686 
10687                 // 3. Prepare for next iteration.
10688                 lastOffset = suballoc.offset + suballoc.size;
10689                 ++nextAlloc2ndIndex;
10690             }
10691             // We are at the end.
10692             else
10693             {
10694                 if(lastOffset < freeSpace2ndTo1stEnd)
10695                 {
10696                     // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10697                     const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10698                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10699                 }
10700 
10701                 // End of loop.
10702                 lastOffset = freeSpace2ndTo1stEnd;
10703             }
10704         }
10705     }
10706 
10707     nextAlloc1stIndex = m_1stNullItemsBeginCount;
10708     while(lastOffset < freeSpace1stTo2ndEnd)
10709     {
10710         // Find next non-null allocation or move nextAllocIndex to the end.
10711         while(nextAlloc1stIndex < suballoc1stCount &&
10712             suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10713         {
10714             ++nextAlloc1stIndex;
10715         }
10716 
10717         // Found non-null allocation.
10718         if(nextAlloc1stIndex < suballoc1stCount)
10719         {
10720             const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10721 
10722             // 1. Process free space before this allocation.
10723             if(lastOffset < suballoc.offset)
10724             {
10725                 // There is free space from lastOffset to suballoc.offset.
10726                 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10727                 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10728             }
10729 
10730             // 2. Process this allocation.
10731             // There is allocation with suballoc.offset, suballoc.size.
10732             PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10733 
10734             // 3. Prepare for next iteration.
10735             lastOffset = suballoc.offset + suballoc.size;
10736             ++nextAlloc1stIndex;
10737         }
10738         // We are at the end.
10739         else
10740         {
10741             if(lastOffset < freeSpace1stTo2ndEnd)
10742             {
10743                 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10744                 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10745                 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10746             }
10747 
10748             // End of loop.
10749             lastOffset = freeSpace1stTo2ndEnd;
10750         }
10751     }
10752 
10753     if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10754     {
10755         size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10756         while(lastOffset < size)
10757         {
10758             // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10759             while(nextAlloc2ndIndex != SIZE_MAX &&
10760                 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10761             {
10762                 --nextAlloc2ndIndex;
10763             }
10764 
10765             // Found non-null allocation.
10766             if(nextAlloc2ndIndex != SIZE_MAX)
10767             {
10768                 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10769 
10770                 // 1. Process free space before this allocation.
10771                 if(lastOffset < suballoc.offset)
10772                 {
10773                     // There is free space from lastOffset to suballoc.offset.
10774                     const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10775                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10776                 }
10777 
10778                 // 2. Process this allocation.
10779                 // There is allocation with suballoc.offset, suballoc.size.
10780                 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10781 
10782                 // 3. Prepare for next iteration.
10783                 lastOffset = suballoc.offset + suballoc.size;
10784                 --nextAlloc2ndIndex;
10785             }
10786             // We are at the end.
10787             else
10788             {
10789                 if(lastOffset < size)
10790                 {
10791                     // There is free space from lastOffset to size.
10792                     const VkDeviceSize unusedRangeSize = size - lastOffset;
10793                     PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10794                 }
10795 
10796                 // End of loop.
10797                 lastOffset = size;
10798             }
10799         }
10800     }
10801 
10802     PrintDetailedMap_End(json);
10803 }
10804 #endif // #if VMA_STATS_STRING_ENABLED
10805 
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)10806 bool VmaBlockMetadata_Linear::CreateAllocationRequest(
10807     uint32_t currentFrameIndex,
10808     uint32_t frameInUseCount,
10809     VkDeviceSize bufferImageGranularity,
10810     VkDeviceSize allocSize,
10811     VkDeviceSize allocAlignment,
10812     bool upperAddress,
10813     VmaSuballocationType allocType,
10814     bool canMakeOtherLost,
10815     uint32_t strategy,
10816     VmaAllocationRequest* pAllocationRequest)
10817 {
10818     VMA_ASSERT(allocSize > 0);
10819     VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
10820     VMA_ASSERT(pAllocationRequest != VMA_NULL);
10821     VMA_HEAVY_ASSERT(Validate());
10822     return upperAddress ?
10823         CreateAllocationRequest_UpperAddress(
10824             currentFrameIndex, frameInUseCount, bufferImageGranularity,
10825             allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest) :
10826         CreateAllocationRequest_LowerAddress(
10827             currentFrameIndex, frameInUseCount, bufferImageGranularity,
10828             allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest);
10829 }
10830 
CreateAllocationRequest_UpperAddress(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)10831 bool VmaBlockMetadata_Linear::CreateAllocationRequest_UpperAddress(
10832     uint32_t currentFrameIndex,
10833     uint32_t frameInUseCount,
10834     VkDeviceSize bufferImageGranularity,
10835     VkDeviceSize allocSize,
10836     VkDeviceSize allocAlignment,
10837     VmaSuballocationType allocType,
10838     bool canMakeOtherLost,
10839     uint32_t strategy,
10840     VmaAllocationRequest* pAllocationRequest)
10841 {
10842     const VkDeviceSize size = GetSize();
10843     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10844     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10845 
10846     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10847     {
10848         VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
10849         return false;
10850     }
10851 
10852     // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
10853     if(allocSize > size)
10854     {
10855         return false;
10856     }
10857     VkDeviceSize resultBaseOffset = size - allocSize;
10858     if(!suballocations2nd.empty())
10859     {
10860         const VmaSuballocation& lastSuballoc = suballocations2nd.back();
10861         resultBaseOffset = lastSuballoc.offset - allocSize;
10862         if(allocSize > lastSuballoc.offset)
10863         {
10864             return false;
10865         }
10866     }
10867 
10868     // Start from offset equal to end of free space.
10869     VkDeviceSize resultOffset = resultBaseOffset;
10870 
10871     // Apply VMA_DEBUG_MARGIN at the end.
10872     if(VMA_DEBUG_MARGIN > 0)
10873     {
10874         if(resultOffset < VMA_DEBUG_MARGIN)
10875         {
10876             return false;
10877         }
10878         resultOffset -= VMA_DEBUG_MARGIN;
10879     }
10880 
10881     // Apply alignment.
10882     resultOffset = VmaAlignDown(resultOffset, allocAlignment);
10883 
10884     // Check next suballocations from 2nd for BufferImageGranularity conflicts.
10885     // Make bigger alignment if necessary.
10886     if(bufferImageGranularity > 1 && !suballocations2nd.empty())
10887     {
10888         bool bufferImageGranularityConflict = false;
10889         for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
10890         {
10891             const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
10892             if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
10893             {
10894                 if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
10895                 {
10896                     bufferImageGranularityConflict = true;
10897                     break;
10898                 }
10899             }
10900             else
10901                 // Already on previous page.
10902                 break;
10903         }
10904         if(bufferImageGranularityConflict)
10905         {
10906             resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
10907         }
10908     }
10909 
10910     // There is enough free space.
10911     const VkDeviceSize endOf1st = !suballocations1st.empty() ?
10912         suballocations1st.back().offset + suballocations1st.back().size :
10913         0;
10914     if(endOf1st + VMA_DEBUG_MARGIN <= resultOffset)
10915     {
10916         // Check previous suballocations for BufferImageGranularity conflicts.
10917         // If conflict exists, allocation cannot be made here.
10918         if(bufferImageGranularity > 1)
10919         {
10920             for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
10921             {
10922                 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
10923                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
10924                 {
10925                     if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
10926                     {
10927                         return false;
10928                     }
10929                 }
10930                 else
10931                 {
10932                     // Already on next page.
10933                     break;
10934                 }
10935             }
10936         }
10937 
10938         // All tests passed: Success.
10939         pAllocationRequest->offset = resultOffset;
10940         pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
10941         pAllocationRequest->sumItemSize = 0;
10942         // pAllocationRequest->item unused.
10943         pAllocationRequest->itemsToMakeLostCount = 0;
10944         pAllocationRequest->type = VmaAllocationRequestType::UpperAddress;
10945         return true;
10946     }
10947 
10948     return false;
10949 }
10950 
CreateAllocationRequest_LowerAddress(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)10951 bool VmaBlockMetadata_Linear::CreateAllocationRequest_LowerAddress(
10952     uint32_t currentFrameIndex,
10953     uint32_t frameInUseCount,
10954     VkDeviceSize bufferImageGranularity,
10955     VkDeviceSize allocSize,
10956     VkDeviceSize allocAlignment,
10957     VmaSuballocationType allocType,
10958     bool canMakeOtherLost,
10959     uint32_t strategy,
10960     VmaAllocationRequest* pAllocationRequest)
10961 {
10962     const VkDeviceSize size = GetSize();
10963     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10964     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10965 
10966     if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10967     {
10968         // Try to allocate at the end of 1st vector.
10969 
10970         VkDeviceSize resultBaseOffset = 0;
10971         if(!suballocations1st.empty())
10972         {
10973             const VmaSuballocation& lastSuballoc = suballocations1st.back();
10974             resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
10975         }
10976 
10977         // Start from offset equal to beginning of free space.
10978         VkDeviceSize resultOffset = resultBaseOffset;
10979 
10980         // Apply VMA_DEBUG_MARGIN at the beginning.
10981         if(VMA_DEBUG_MARGIN > 0)
10982         {
10983             resultOffset += VMA_DEBUG_MARGIN;
10984         }
10985 
10986         // Apply alignment.
10987         resultOffset = VmaAlignUp(resultOffset, allocAlignment);
10988 
10989         // Check previous suballocations for BufferImageGranularity conflicts.
10990         // Make bigger alignment if necessary.
10991         if(bufferImageGranularity > 1 && !suballocations1st.empty())
10992         {
10993             bool bufferImageGranularityConflict = false;
10994             for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
10995             {
10996                 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
10997                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
10998                 {
10999                     if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
11000                     {
11001                         bufferImageGranularityConflict = true;
11002                         break;
11003                     }
11004                 }
11005                 else
11006                     // Already on previous page.
11007                     break;
11008             }
11009             if(bufferImageGranularityConflict)
11010             {
11011                 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
11012             }
11013         }
11014 
11015         const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
11016             suballocations2nd.back().offset : size;
11017 
11018         // There is enough free space at the end after alignment.
11019         if(resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd)
11020         {
11021             // Check next suballocations for BufferImageGranularity conflicts.
11022             // If conflict exists, allocation cannot be made here.
11023             if(bufferImageGranularity > 1 && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11024             {
11025                 for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
11026                 {
11027                     const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
11028                     if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
11029                     {
11030                         if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
11031                         {
11032                             return false;
11033                         }
11034                     }
11035                     else
11036                     {
11037                         // Already on previous page.
11038                         break;
11039                     }
11040                 }
11041             }
11042 
11043             // All tests passed: Success.
11044             pAllocationRequest->offset = resultOffset;
11045             pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
11046             pAllocationRequest->sumItemSize = 0;
11047             // pAllocationRequest->item, customData unused.
11048             pAllocationRequest->type = VmaAllocationRequestType::EndOf1st;
11049             pAllocationRequest->itemsToMakeLostCount = 0;
11050             return true;
11051         }
11052     }
11053 
11054     // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
11055     // beginning of 1st vector as the end of free space.
11056     if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11057     {
11058         VMA_ASSERT(!suballocations1st.empty());
11059 
11060         VkDeviceSize resultBaseOffset = 0;
11061         if(!suballocations2nd.empty())
11062         {
11063             const VmaSuballocation& lastSuballoc = suballocations2nd.back();
11064             resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
11065         }
11066 
11067         // Start from offset equal to beginning of free space.
11068         VkDeviceSize resultOffset = resultBaseOffset;
11069 
11070         // Apply VMA_DEBUG_MARGIN at the beginning.
11071         if(VMA_DEBUG_MARGIN > 0)
11072         {
11073             resultOffset += VMA_DEBUG_MARGIN;
11074         }
11075 
11076         // Apply alignment.
11077         resultOffset = VmaAlignUp(resultOffset, allocAlignment);
11078 
11079         // Check previous suballocations for BufferImageGranularity conflicts.
11080         // Make bigger alignment if necessary.
11081         if(bufferImageGranularity > 1 && !suballocations2nd.empty())
11082         {
11083             bool bufferImageGranularityConflict = false;
11084             for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
11085             {
11086                 const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
11087                 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
11088                 {
11089                     if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
11090                     {
11091                         bufferImageGranularityConflict = true;
11092                         break;
11093                     }
11094                 }
11095                 else
11096                     // Already on previous page.
11097                     break;
11098             }
11099             if(bufferImageGranularityConflict)
11100             {
11101                 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
11102             }
11103         }
11104 
11105         pAllocationRequest->itemsToMakeLostCount = 0;
11106         pAllocationRequest->sumItemSize = 0;
11107         size_t index1st = m_1stNullItemsBeginCount;
11108 
11109         if(canMakeOtherLost)
11110         {
11111             while(index1st < suballocations1st.size() &&
11112                 resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset)
11113             {
11114                 // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
11115                 const VmaSuballocation& suballoc = suballocations1st[index1st];
11116                 if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
11117                 {
11118                     // No problem.
11119                 }
11120                 else
11121                 {
11122                     VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
11123                     if(suballoc.hAllocation->CanBecomeLost() &&
11124                         suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
11125                     {
11126                         ++pAllocationRequest->itemsToMakeLostCount;
11127                         pAllocationRequest->sumItemSize += suballoc.size;
11128                     }
11129                     else
11130                     {
11131                         return false;
11132                     }
11133                 }
11134                 ++index1st;
11135             }
11136 
11137             // Check next suballocations for BufferImageGranularity conflicts.
11138             // If conflict exists, we must mark more allocations lost or fail.
11139             if(bufferImageGranularity > 1)
11140             {
11141                 while(index1st < suballocations1st.size())
11142                 {
11143                     const VmaSuballocation& suballoc = suballocations1st[index1st];
11144                     if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))
11145                     {
11146                         if(suballoc.hAllocation != VK_NULL_HANDLE)
11147                         {
11148                             // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
11149                             if(suballoc.hAllocation->CanBecomeLost() &&
11150                                 suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
11151                             {
11152                                 ++pAllocationRequest->itemsToMakeLostCount;
11153                                 pAllocationRequest->sumItemSize += suballoc.size;
11154                             }
11155                             else
11156                             {
11157                                 return false;
11158                             }
11159                         }
11160                     }
11161                     else
11162                     {
11163                         // Already on next page.
11164                         break;
11165                     }
11166                     ++index1st;
11167                 }
11168             }
11169 
11170             // Special case: There is not enough room at the end for this allocation, even after making all from the 1st lost.
11171             if(index1st == suballocations1st.size() &&
11172                 resultOffset + allocSize + VMA_DEBUG_MARGIN > size)
11173             {
11174                 // TODO: This is a known bug that it's not yet implemented and the allocation is failing.
11175                 VMA_DEBUG_LOG("Unsupported special case in custom pool with linear allocation algorithm used as ring buffer with allocations that can be lost.");
11176             }
11177         }
11178 
11179         // There is enough free space at the end after alignment.
11180         if((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= size) ||
11181             (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset))
11182         {
11183             // Check next suballocations for BufferImageGranularity conflicts.
11184             // If conflict exists, allocation cannot be made here.
11185             if(bufferImageGranularity > 1)
11186             {
11187                 for(size_t nextSuballocIndex = index1st;
11188                     nextSuballocIndex < suballocations1st.size();
11189                     nextSuballocIndex++)
11190                 {
11191                     const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
11192                     if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
11193                     {
11194                         if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
11195                         {
11196                             return false;
11197                         }
11198                     }
11199                     else
11200                     {
11201                         // Already on next page.
11202                         break;
11203                     }
11204                 }
11205             }
11206 
11207             // All tests passed: Success.
11208             pAllocationRequest->offset = resultOffset;
11209             pAllocationRequest->sumFreeSize =
11210                 (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size)
11211                 - resultBaseOffset
11212                 - pAllocationRequest->sumItemSize;
11213             pAllocationRequest->type = VmaAllocationRequestType::EndOf2nd;
11214             // pAllocationRequest->item, customData unused.
11215             return true;
11216         }
11217     }
11218 
11219     return false;
11220 }
11221 
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)11222 bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
11223     uint32_t currentFrameIndex,
11224     uint32_t frameInUseCount,
11225     VmaAllocationRequest* pAllocationRequest)
11226 {
11227     if(pAllocationRequest->itemsToMakeLostCount == 0)
11228     {
11229         return true;
11230     }
11231 
11232     VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
11233 
11234     // We always start from 1st.
11235     SuballocationVectorType* suballocations = &AccessSuballocations1st();
11236     size_t index = m_1stNullItemsBeginCount;
11237     size_t madeLostCount = 0;
11238     while(madeLostCount < pAllocationRequest->itemsToMakeLostCount)
11239     {
11240         if(index == suballocations->size())
11241         {
11242             index = 0;
11243             // If we get to the end of 1st, we wrap around to beginning of 2nd of 1st.
11244             if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11245             {
11246                 suballocations = &AccessSuballocations2nd();
11247             }
11248             // else: m_2ndVectorMode == SECOND_VECTOR_EMPTY:
11249             // suballocations continues pointing at AccessSuballocations1st().
11250             VMA_ASSERT(!suballocations->empty());
11251         }
11252         VmaSuballocation& suballoc = (*suballocations)[index];
11253         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11254         {
11255             VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
11256             VMA_ASSERT(suballoc.hAllocation->CanBecomeLost());
11257             if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11258             {
11259                 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11260                 suballoc.hAllocation = VK_NULL_HANDLE;
11261                 m_SumFreeSize += suballoc.size;
11262                 if(suballocations == &AccessSuballocations1st())
11263                 {
11264                     ++m_1stNullItemsMiddleCount;
11265                 }
11266                 else
11267                 {
11268                     ++m_2ndNullItemsCount;
11269                 }
11270                 ++madeLostCount;
11271             }
11272             else
11273             {
11274                 return false;
11275             }
11276         }
11277         ++index;
11278     }
11279 
11280     CleanupAfterFree();
11281     //VMA_HEAVY_ASSERT(Validate()); // Already called by ClanupAfterFree().
11282 
11283     return true;
11284 }
11285 
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)11286 uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
11287 {
11288     uint32_t lostAllocationCount = 0;
11289 
11290     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11291     for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
11292     {
11293         VmaSuballocation& suballoc = suballocations1st[i];
11294         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
11295             suballoc.hAllocation->CanBecomeLost() &&
11296             suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11297         {
11298             suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11299             suballoc.hAllocation = VK_NULL_HANDLE;
11300             ++m_1stNullItemsMiddleCount;
11301             m_SumFreeSize += suballoc.size;
11302             ++lostAllocationCount;
11303         }
11304     }
11305 
11306     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11307     for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
11308     {
11309         VmaSuballocation& suballoc = suballocations2nd[i];
11310         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
11311             suballoc.hAllocation->CanBecomeLost() &&
11312             suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11313         {
11314             suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11315             suballoc.hAllocation = VK_NULL_HANDLE;
11316             ++m_2ndNullItemsCount;
11317             m_SumFreeSize += suballoc.size;
11318             ++lostAllocationCount;
11319         }
11320     }
11321 
11322     if(lostAllocationCount)
11323     {
11324         CleanupAfterFree();
11325     }
11326 
11327     return lostAllocationCount;
11328 }
11329 
CheckCorruption(const void * pBlockData)11330 VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
11331 {
11332     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11333     for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
11334     {
11335         const VmaSuballocation& suballoc = suballocations1st[i];
11336         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11337         {
11338             if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
11339             {
11340                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
11341                 return VK_ERROR_VALIDATION_FAILED_EXT;
11342             }
11343             if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
11344             {
11345                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
11346                 return VK_ERROR_VALIDATION_FAILED_EXT;
11347             }
11348         }
11349     }
11350 
11351     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11352     for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
11353     {
11354         const VmaSuballocation& suballoc = suballocations2nd[i];
11355         if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11356         {
11357             if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
11358             {
11359                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
11360                 return VK_ERROR_VALIDATION_FAILED_EXT;
11361             }
11362             if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
11363             {
11364                 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
11365                 return VK_ERROR_VALIDATION_FAILED_EXT;
11366             }
11367         }
11368     }
11369 
11370     return VK_SUCCESS;
11371 }
11372 
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,VmaAllocation hAllocation)11373 void VmaBlockMetadata_Linear::Alloc(
11374     const VmaAllocationRequest& request,
11375     VmaSuballocationType type,
11376     VkDeviceSize allocSize,
11377     VmaAllocation hAllocation)
11378 {
11379     const VmaSuballocation newSuballoc = { request.offset, allocSize, hAllocation, type };
11380 
11381     switch(request.type)
11382     {
11383     case VmaAllocationRequestType::UpperAddress:
11384         {
11385             VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
11386                 "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
11387             SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11388             suballocations2nd.push_back(newSuballoc);
11389             m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
11390         }
11391         break;
11392     case VmaAllocationRequestType::EndOf1st:
11393         {
11394             SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11395 
11396             VMA_ASSERT(suballocations1st.empty() ||
11397                 request.offset >= suballocations1st.back().offset + suballocations1st.back().size);
11398             // Check if it fits before the end of the block.
11399             VMA_ASSERT(request.offset + allocSize <= GetSize());
11400 
11401             suballocations1st.push_back(newSuballoc);
11402         }
11403         break;
11404     case VmaAllocationRequestType::EndOf2nd:
11405         {
11406             SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11407             // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
11408             VMA_ASSERT(!suballocations1st.empty() &&
11409                 request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset);
11410             SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11411 
11412             switch(m_2ndVectorMode)
11413             {
11414             case SECOND_VECTOR_EMPTY:
11415                 // First allocation from second part ring buffer.
11416                 VMA_ASSERT(suballocations2nd.empty());
11417                 m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
11418                 break;
11419             case SECOND_VECTOR_RING_BUFFER:
11420                 // 2-part ring buffer is already started.
11421                 VMA_ASSERT(!suballocations2nd.empty());
11422                 break;
11423             case SECOND_VECTOR_DOUBLE_STACK:
11424                 VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
11425                 break;
11426             default:
11427                 VMA_ASSERT(0);
11428             }
11429 
11430             suballocations2nd.push_back(newSuballoc);
11431         }
11432         break;
11433     default:
11434         VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
11435     }
11436 
11437     m_SumFreeSize -= newSuballoc.size;
11438 }
11439 
Free(const VmaAllocation allocation)11440 void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation)
11441 {
11442     FreeAtOffset(allocation->GetOffset());
11443 }
11444 
FreeAtOffset(VkDeviceSize offset)11445 void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
11446 {
11447     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11448     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11449 
11450     if(!suballocations1st.empty())
11451     {
11452         // First allocation: Mark it as next empty at the beginning.
11453         VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
11454         if(firstSuballoc.offset == offset)
11455         {
11456             firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11457             firstSuballoc.hAllocation = VK_NULL_HANDLE;
11458             m_SumFreeSize += firstSuballoc.size;
11459             ++m_1stNullItemsBeginCount;
11460             CleanupAfterFree();
11461             return;
11462         }
11463     }
11464 
11465     // Last allocation in 2-part ring buffer or top of upper stack (same logic).
11466     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
11467         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11468     {
11469         VmaSuballocation& lastSuballoc = suballocations2nd.back();
11470         if(lastSuballoc.offset == offset)
11471         {
11472             m_SumFreeSize += lastSuballoc.size;
11473             suballocations2nd.pop_back();
11474             CleanupAfterFree();
11475             return;
11476         }
11477     }
11478     // Last allocation in 1st vector.
11479     else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY)
11480     {
11481         VmaSuballocation& lastSuballoc = suballocations1st.back();
11482         if(lastSuballoc.offset == offset)
11483         {
11484             m_SumFreeSize += lastSuballoc.size;
11485             suballocations1st.pop_back();
11486             CleanupAfterFree();
11487             return;
11488         }
11489     }
11490 
11491     // Item from the middle of 1st vector.
11492     {
11493         VmaSuballocation refSuballoc;
11494         refSuballoc.offset = offset;
11495         // Rest of members stays uninitialized intentionally for better performance.
11496         SuballocationVectorType::iterator it = VmaBinaryFindSorted(
11497             suballocations1st.begin() + m_1stNullItemsBeginCount,
11498             suballocations1st.end(),
11499             refSuballoc,
11500             VmaSuballocationOffsetLess());
11501         if(it != suballocations1st.end())
11502         {
11503             it->type = VMA_SUBALLOCATION_TYPE_FREE;
11504             it->hAllocation = VK_NULL_HANDLE;
11505             ++m_1stNullItemsMiddleCount;
11506             m_SumFreeSize += it->size;
11507             CleanupAfterFree();
11508             return;
11509         }
11510     }
11511 
11512     if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
11513     {
11514         // Item from the middle of 2nd vector.
11515         VmaSuballocation refSuballoc;
11516         refSuballoc.offset = offset;
11517         // Rest of members stays uninitialized intentionally for better performance.
11518         SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
11519             VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :
11520             VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());
11521         if(it != suballocations2nd.end())
11522         {
11523             it->type = VMA_SUBALLOCATION_TYPE_FREE;
11524             it->hAllocation = VK_NULL_HANDLE;
11525             ++m_2ndNullItemsCount;
11526             m_SumFreeSize += it->size;
11527             CleanupAfterFree();
11528             return;
11529         }
11530     }
11531 
11532     VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
11533 }
11534 
ShouldCompact1st()11535 bool VmaBlockMetadata_Linear::ShouldCompact1st() const
11536 {
11537     const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
11538     const size_t suballocCount = AccessSuballocations1st().size();
11539     return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
11540 }
11541 
CleanupAfterFree()11542 void VmaBlockMetadata_Linear::CleanupAfterFree()
11543 {
11544     SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11545     SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11546 
11547     if(IsEmpty())
11548     {
11549         suballocations1st.clear();
11550         suballocations2nd.clear();
11551         m_1stNullItemsBeginCount = 0;
11552         m_1stNullItemsMiddleCount = 0;
11553         m_2ndNullItemsCount = 0;
11554         m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11555     }
11556     else
11557     {
11558         const size_t suballoc1stCount = suballocations1st.size();
11559         const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
11560         VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
11561 
11562         // Find more null items at the beginning of 1st vector.
11563         while(m_1stNullItemsBeginCount < suballoc1stCount &&
11564             suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
11565         {
11566             ++m_1stNullItemsBeginCount;
11567             --m_1stNullItemsMiddleCount;
11568         }
11569 
11570         // Find more null items at the end of 1st vector.
11571         while(m_1stNullItemsMiddleCount > 0 &&
11572             suballocations1st.back().hAllocation == VK_NULL_HANDLE)
11573         {
11574             --m_1stNullItemsMiddleCount;
11575             suballocations1st.pop_back();
11576         }
11577 
11578         // Find more null items at the end of 2nd vector.
11579         while(m_2ndNullItemsCount > 0 &&
11580             suballocations2nd.back().hAllocation == VK_NULL_HANDLE)
11581         {
11582             --m_2ndNullItemsCount;
11583             suballocations2nd.pop_back();
11584         }
11585 
11586         // Find more null items at the beginning of 2nd vector.
11587         while(m_2ndNullItemsCount > 0 &&
11588             suballocations2nd[0].hAllocation == VK_NULL_HANDLE)
11589         {
11590             --m_2ndNullItemsCount;
11591             VmaVectorRemove(suballocations2nd, 0);
11592         }
11593 
11594         if(ShouldCompact1st())
11595         {
11596             const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
11597             size_t srcIndex = m_1stNullItemsBeginCount;
11598             for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
11599             {
11600                 while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE)
11601                 {
11602                     ++srcIndex;
11603                 }
11604                 if(dstIndex != srcIndex)
11605                 {
11606                     suballocations1st[dstIndex] = suballocations1st[srcIndex];
11607                 }
11608                 ++srcIndex;
11609             }
11610             suballocations1st.resize(nonNullItemCount);
11611             m_1stNullItemsBeginCount = 0;
11612             m_1stNullItemsMiddleCount = 0;
11613         }
11614 
11615         // 2nd vector became empty.
11616         if(suballocations2nd.empty())
11617         {
11618             m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11619         }
11620 
11621         // 1st vector became empty.
11622         if(suballocations1st.size() - m_1stNullItemsBeginCount == 0)
11623         {
11624             suballocations1st.clear();
11625             m_1stNullItemsBeginCount = 0;
11626 
11627             if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11628             {
11629                 // Swap 1st with 2nd. Now 2nd is empty.
11630                 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11631                 m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
11632                 while(m_1stNullItemsBeginCount < suballocations2nd.size() &&
11633                     suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
11634                 {
11635                     ++m_1stNullItemsBeginCount;
11636                     --m_1stNullItemsMiddleCount;
11637                 }
11638                 m_2ndNullItemsCount = 0;
11639                 m_1stVectorIndex ^= 1;
11640             }
11641         }
11642     }
11643 
11644     VMA_HEAVY_ASSERT(Validate());
11645 }
11646 
11647 
11648 ////////////////////////////////////////////////////////////////////////////////
11649 // class VmaBlockMetadata_Buddy
11650 
VmaBlockMetadata_Buddy(VmaAllocator hAllocator)11651 VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :
11652     VmaBlockMetadata(hAllocator),
11653     m_Root(VMA_NULL),
11654     m_AllocationCount(0),
11655     m_FreeCount(1),
11656     m_SumFreeSize(0)
11657 {
11658     memset(m_FreeList, 0, sizeof(m_FreeList));
11659 }
11660 
~VmaBlockMetadata_Buddy()11661 VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
11662 {
11663     DeleteNode(m_Root);
11664 }
11665 
Init(VkDeviceSize size)11666 void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
11667 {
11668     VmaBlockMetadata::Init(size);
11669 
11670     m_UsableSize = VmaPrevPow2(size);
11671     m_SumFreeSize = m_UsableSize;
11672 
11673     // Calculate m_LevelCount.
11674     m_LevelCount = 1;
11675     while(m_LevelCount < MAX_LEVELS &&
11676         LevelToNodeSize(m_LevelCount) >= MIN_NODE_SIZE)
11677     {
11678         ++m_LevelCount;
11679     }
11680 
11681     Node* rootNode = vma_new(GetAllocationCallbacks(), Node)();
11682     rootNode->offset = 0;
11683     rootNode->type = Node::TYPE_FREE;
11684     rootNode->parent = VMA_NULL;
11685     rootNode->buddy = VMA_NULL;
11686 
11687     m_Root = rootNode;
11688     AddToFreeListFront(0, rootNode);
11689 }
11690 
Validate()11691 bool VmaBlockMetadata_Buddy::Validate() const
11692 {
11693     // Validate tree.
11694     ValidationContext ctx;
11695     if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
11696     {
11697         VMA_VALIDATE(false && "ValidateNode failed.");
11698     }
11699     VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
11700     VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
11701 
11702     // Validate free node lists.
11703     for(uint32_t level = 0; level < m_LevelCount; ++level)
11704     {
11705         VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
11706             m_FreeList[level].front->free.prev == VMA_NULL);
11707 
11708         for(Node* node = m_FreeList[level].front;
11709             node != VMA_NULL;
11710             node = node->free.next)
11711         {
11712             VMA_VALIDATE(node->type == Node::TYPE_FREE);
11713 
11714             if(node->free.next == VMA_NULL)
11715             {
11716                 VMA_VALIDATE(m_FreeList[level].back == node);
11717             }
11718             else
11719             {
11720                 VMA_VALIDATE(node->free.next->free.prev == node);
11721             }
11722         }
11723     }
11724 
11725     // Validate that free lists ar higher levels are empty.
11726     for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
11727     {
11728         VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
11729     }
11730 
11731     return true;
11732 }
11733 
GetUnusedRangeSizeMax()11734 VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
11735 {
11736     for(uint32_t level = 0; level < m_LevelCount; ++level)
11737     {
11738         if(m_FreeList[level].front != VMA_NULL)
11739         {
11740             return LevelToNodeSize(level);
11741         }
11742     }
11743     return 0;
11744 }
11745 
CalcAllocationStatInfo(VmaStatInfo & outInfo)11746 void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
11747 {
11748     const VkDeviceSize unusableSize = GetUnusableSize();
11749 
11750     outInfo.blockCount = 1;
11751 
11752     outInfo.allocationCount = outInfo.unusedRangeCount = 0;
11753     outInfo.usedBytes = outInfo.unusedBytes = 0;
11754 
11755     outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
11756     outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
11757     outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused.
11758 
11759     CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0));
11760 
11761     if(unusableSize > 0)
11762     {
11763         ++outInfo.unusedRangeCount;
11764         outInfo.unusedBytes += unusableSize;
11765         outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize);
11766         outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize);
11767     }
11768 }
11769 
AddPoolStats(VmaPoolStats & inoutStats)11770 void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
11771 {
11772     const VkDeviceSize unusableSize = GetUnusableSize();
11773 
11774     inoutStats.size += GetSize();
11775     inoutStats.unusedSize += m_SumFreeSize + unusableSize;
11776     inoutStats.allocationCount += m_AllocationCount;
11777     inoutStats.unusedRangeCount += m_FreeCount;
11778     inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
11779 
11780     if(unusableSize > 0)
11781     {
11782         ++inoutStats.unusedRangeCount;
11783         // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
11784     }
11785 }
11786 
11787 #if VMA_STATS_STRING_ENABLED
11788 
PrintDetailedMap(class VmaJsonWriter & json)11789 void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
11790 {
11791     // TODO optimize
11792     VmaStatInfo stat;
11793     CalcAllocationStatInfo(stat);
11794 
11795     PrintDetailedMap_Begin(
11796         json,
11797         stat.unusedBytes,
11798         stat.allocationCount,
11799         stat.unusedRangeCount);
11800 
11801     PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
11802 
11803     const VkDeviceSize unusableSize = GetUnusableSize();
11804     if(unusableSize > 0)
11805     {
11806         PrintDetailedMap_UnusedRange(json,
11807             m_UsableSize, // offset
11808             unusableSize); // size
11809     }
11810 
11811     PrintDetailedMap_End(json);
11812 }
11813 
11814 #endif // #if VMA_STATS_STRING_ENABLED
11815 
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)11816 bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
11817     uint32_t currentFrameIndex,
11818     uint32_t frameInUseCount,
11819     VkDeviceSize bufferImageGranularity,
11820     VkDeviceSize allocSize,
11821     VkDeviceSize allocAlignment,
11822     bool upperAddress,
11823     VmaSuballocationType allocType,
11824     bool canMakeOtherLost,
11825     uint32_t strategy,
11826     VmaAllocationRequest* pAllocationRequest)
11827 {
11828     VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
11829 
11830     // Simple way to respect bufferImageGranularity. May be optimized some day.
11831     // Whenever it might be an OPTIMAL image...
11832     if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
11833         allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
11834         allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
11835     {
11836         allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity);
11837         allocSize = VMA_MAX(allocSize, bufferImageGranularity);
11838     }
11839 
11840     if(allocSize > m_UsableSize)
11841     {
11842         return false;
11843     }
11844 
11845     const uint32_t targetLevel = AllocSizeToLevel(allocSize);
11846     for(uint32_t level = targetLevel + 1; level--; )
11847     {
11848         for(Node* freeNode = m_FreeList[level].front;
11849             freeNode != VMA_NULL;
11850             freeNode = freeNode->free.next)
11851         {
11852             if(freeNode->offset % allocAlignment == 0)
11853             {
11854                 pAllocationRequest->type = VmaAllocationRequestType::Normal;
11855                 pAllocationRequest->offset = freeNode->offset;
11856                 pAllocationRequest->sumFreeSize = LevelToNodeSize(level);
11857                 pAllocationRequest->sumItemSize = 0;
11858                 pAllocationRequest->itemsToMakeLostCount = 0;
11859                 pAllocationRequest->customData = (void*)(uintptr_t)level;
11860                 return true;
11861             }
11862         }
11863     }
11864 
11865     return false;
11866 }
11867 
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)11868 bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
11869     uint32_t currentFrameIndex,
11870     uint32_t frameInUseCount,
11871     VmaAllocationRequest* pAllocationRequest)
11872 {
11873     /*
11874     Lost allocations are not supported in buddy allocator at the moment.
11875     Support might be added in the future.
11876     */
11877     return pAllocationRequest->itemsToMakeLostCount == 0;
11878 }
11879 
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)11880 uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
11881 {
11882     /*
11883     Lost allocations are not supported in buddy allocator at the moment.
11884     Support might be added in the future.
11885     */
11886     return 0;
11887 }
11888 
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,VmaAllocation hAllocation)11889 void VmaBlockMetadata_Buddy::Alloc(
11890     const VmaAllocationRequest& request,
11891     VmaSuballocationType type,
11892     VkDeviceSize allocSize,
11893     VmaAllocation hAllocation)
11894 {
11895     VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
11896 
11897     const uint32_t targetLevel = AllocSizeToLevel(allocSize);
11898     uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
11899 
11900     Node* currNode = m_FreeList[currLevel].front;
11901     VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
11902     while(currNode->offset != request.offset)
11903     {
11904         currNode = currNode->free.next;
11905         VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
11906     }
11907 
11908     // Go down, splitting free nodes.
11909     while(currLevel < targetLevel)
11910     {
11911         // currNode is already first free node at currLevel.
11912         // Remove it from list of free nodes at this currLevel.
11913         RemoveFromFreeList(currLevel, currNode);
11914 
11915         const uint32_t childrenLevel = currLevel + 1;
11916 
11917         // Create two free sub-nodes.
11918         Node* leftChild = vma_new(GetAllocationCallbacks(), Node)();
11919         Node* rightChild = vma_new(GetAllocationCallbacks(), Node)();
11920 
11921         leftChild->offset = currNode->offset;
11922         leftChild->type = Node::TYPE_FREE;
11923         leftChild->parent = currNode;
11924         leftChild->buddy = rightChild;
11925 
11926         rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
11927         rightChild->type = Node::TYPE_FREE;
11928         rightChild->parent = currNode;
11929         rightChild->buddy = leftChild;
11930 
11931         // Convert current currNode to split type.
11932         currNode->type = Node::TYPE_SPLIT;
11933         currNode->split.leftChild = leftChild;
11934 
11935         // Add child nodes to free list. Order is important!
11936         AddToFreeListFront(childrenLevel, rightChild);
11937         AddToFreeListFront(childrenLevel, leftChild);
11938 
11939         ++m_FreeCount;
11940         //m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2.
11941         ++currLevel;
11942         currNode = m_FreeList[currLevel].front;
11943 
11944         /*
11945         We can be sure that currNode, as left child of node previously split,
11946         also fullfills the alignment requirement.
11947         */
11948     }
11949 
11950     // Remove from free list.
11951     VMA_ASSERT(currLevel == targetLevel &&
11952         currNode != VMA_NULL &&
11953         currNode->type == Node::TYPE_FREE);
11954     RemoveFromFreeList(currLevel, currNode);
11955 
11956     // Convert to allocation node.
11957     currNode->type = Node::TYPE_ALLOCATION;
11958     currNode->allocation.alloc = hAllocation;
11959 
11960     ++m_AllocationCount;
11961     --m_FreeCount;
11962     m_SumFreeSize -= allocSize;
11963 }
11964 
DeleteNode(Node * node)11965 void VmaBlockMetadata_Buddy::DeleteNode(Node* node)
11966 {
11967     if(node->type == Node::TYPE_SPLIT)
11968     {
11969         DeleteNode(node->split.leftChild->buddy);
11970         DeleteNode(node->split.leftChild);
11971     }
11972 
11973     vma_delete(GetAllocationCallbacks(), node);
11974 }
11975 
ValidateNode(ValidationContext & ctx,const Node * parent,const Node * curr,uint32_t level,VkDeviceSize levelNodeSize)11976 bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
11977 {
11978     VMA_VALIDATE(level < m_LevelCount);
11979     VMA_VALIDATE(curr->parent == parent);
11980     VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
11981     VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
11982     switch(curr->type)
11983     {
11984     case Node::TYPE_FREE:
11985         // curr->free.prev, next are validated separately.
11986         ctx.calculatedSumFreeSize += levelNodeSize;
11987         ++ctx.calculatedFreeCount;
11988         break;
11989     case Node::TYPE_ALLOCATION:
11990         ++ctx.calculatedAllocationCount;
11991         ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize();
11992         VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);
11993         break;
11994     case Node::TYPE_SPLIT:
11995         {
11996             const uint32_t childrenLevel = level + 1;
11997             const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2;
11998             const Node* const leftChild = curr->split.leftChild;
11999             VMA_VALIDATE(leftChild != VMA_NULL);
12000             VMA_VALIDATE(leftChild->offset == curr->offset);
12001             if(!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
12002             {
12003                 VMA_VALIDATE(false && "ValidateNode for left child failed.");
12004             }
12005             const Node* const rightChild = leftChild->buddy;
12006             VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
12007             if(!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
12008             {
12009                 VMA_VALIDATE(false && "ValidateNode for right child failed.");
12010             }
12011         }
12012         break;
12013     default:
12014         return false;
12015     }
12016 
12017     return true;
12018 }
12019 
AllocSizeToLevel(VkDeviceSize allocSize)12020 uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
12021 {
12022     // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
12023     uint32_t level = 0;
12024     VkDeviceSize currLevelNodeSize = m_UsableSize;
12025     VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
12026     while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
12027     {
12028         ++level;
12029         currLevelNodeSize = nextLevelNodeSize;
12030         nextLevelNodeSize = currLevelNodeSize >> 1;
12031     }
12032     return level;
12033 }
12034 
FreeAtOffset(VmaAllocation alloc,VkDeviceSize offset)12035 void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset)
12036 {
12037     // Find node and level.
12038     Node* node = m_Root;
12039     VkDeviceSize nodeOffset = 0;
12040     uint32_t level = 0;
12041     VkDeviceSize levelNodeSize = LevelToNodeSize(0);
12042     while(node->type == Node::TYPE_SPLIT)
12043     {
12044         const VkDeviceSize nextLevelSize = levelNodeSize >> 1;
12045         if(offset < nodeOffset + nextLevelSize)
12046         {
12047             node = node->split.leftChild;
12048         }
12049         else
12050         {
12051             node = node->split.leftChild->buddy;
12052             nodeOffset += nextLevelSize;
12053         }
12054         ++level;
12055         levelNodeSize = nextLevelSize;
12056     }
12057 
12058     VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
12059     VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc);
12060 
12061     ++m_FreeCount;
12062     --m_AllocationCount;
12063     m_SumFreeSize += alloc->GetSize();
12064 
12065     node->type = Node::TYPE_FREE;
12066 
12067     // Join free nodes if possible.
12068     while(level > 0 && node->buddy->type == Node::TYPE_FREE)
12069     {
12070         RemoveFromFreeList(level, node->buddy);
12071         Node* const parent = node->parent;
12072 
12073         vma_delete(GetAllocationCallbacks(), node->buddy);
12074         vma_delete(GetAllocationCallbacks(), node);
12075         parent->type = Node::TYPE_FREE;
12076 
12077         node = parent;
12078         --level;
12079         //m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2.
12080         --m_FreeCount;
12081     }
12082 
12083     AddToFreeListFront(level, node);
12084 }
12085 
CalcAllocationStatInfoNode(VmaStatInfo & outInfo,const Node * node,VkDeviceSize levelNodeSize)12086 void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const
12087 {
12088     switch(node->type)
12089     {
12090     case Node::TYPE_FREE:
12091         ++outInfo.unusedRangeCount;
12092         outInfo.unusedBytes += levelNodeSize;
12093         outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, levelNodeSize);
12094         outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize);
12095         break;
12096     case Node::TYPE_ALLOCATION:
12097         {
12098             const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
12099             ++outInfo.allocationCount;
12100             outInfo.usedBytes += allocSize;
12101             outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize);
12102             outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize);
12103 
12104             const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize;
12105             if(unusedRangeSize > 0)
12106             {
12107                 ++outInfo.unusedRangeCount;
12108                 outInfo.unusedBytes += unusedRangeSize;
12109                 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize);
12110                 outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize);
12111             }
12112         }
12113         break;
12114     case Node::TYPE_SPLIT:
12115         {
12116             const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
12117             const Node* const leftChild = node->split.leftChild;
12118             CalcAllocationStatInfoNode(outInfo, leftChild, childrenNodeSize);
12119             const Node* const rightChild = leftChild->buddy;
12120             CalcAllocationStatInfoNode(outInfo, rightChild, childrenNodeSize);
12121         }
12122         break;
12123     default:
12124         VMA_ASSERT(0);
12125     }
12126 }
12127 
AddToFreeListFront(uint32_t level,Node * node)12128 void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
12129 {
12130     VMA_ASSERT(node->type == Node::TYPE_FREE);
12131 
12132     // List is empty.
12133     Node* const frontNode = m_FreeList[level].front;
12134     if(frontNode == VMA_NULL)
12135     {
12136         VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
12137         node->free.prev = node->free.next = VMA_NULL;
12138         m_FreeList[level].front = m_FreeList[level].back = node;
12139     }
12140     else
12141     {
12142         VMA_ASSERT(frontNode->free.prev == VMA_NULL);
12143         node->free.prev = VMA_NULL;
12144         node->free.next = frontNode;
12145         frontNode->free.prev = node;
12146         m_FreeList[level].front = node;
12147     }
12148 }
12149 
RemoveFromFreeList(uint32_t level,Node * node)12150 void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
12151 {
12152     VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
12153 
12154     // It is at the front.
12155     if(node->free.prev == VMA_NULL)
12156     {
12157         VMA_ASSERT(m_FreeList[level].front == node);
12158         m_FreeList[level].front = node->free.next;
12159     }
12160     else
12161     {
12162         Node* const prevFreeNode = node->free.prev;
12163         VMA_ASSERT(prevFreeNode->free.next == node);
12164         prevFreeNode->free.next = node->free.next;
12165     }
12166 
12167     // It is at the back.
12168     if(node->free.next == VMA_NULL)
12169     {
12170         VMA_ASSERT(m_FreeList[level].back == node);
12171         m_FreeList[level].back = node->free.prev;
12172     }
12173     else
12174     {
12175         Node* const nextFreeNode = node->free.next;
12176         VMA_ASSERT(nextFreeNode->free.prev == node);
12177         nextFreeNode->free.prev = node->free.prev;
12178     }
12179 }
12180 
12181 #if VMA_STATS_STRING_ENABLED
PrintDetailedMapNode(class VmaJsonWriter & json,const Node * node,VkDeviceSize levelNodeSize)12182 void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
12183 {
12184     switch(node->type)
12185     {
12186     case Node::TYPE_FREE:
12187         PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
12188         break;
12189     case Node::TYPE_ALLOCATION:
12190         {
12191             PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);
12192             const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
12193             if(allocSize < levelNodeSize)
12194             {
12195                 PrintDetailedMap_UnusedRange(json, node->offset + allocSize, levelNodeSize - allocSize);
12196             }
12197         }
12198         break;
12199     case Node::TYPE_SPLIT:
12200         {
12201             const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
12202             const Node* const leftChild = node->split.leftChild;
12203             PrintDetailedMapNode(json, leftChild, childrenNodeSize);
12204             const Node* const rightChild = leftChild->buddy;
12205             PrintDetailedMapNode(json, rightChild, childrenNodeSize);
12206         }
12207         break;
12208     default:
12209         VMA_ASSERT(0);
12210     }
12211 }
12212 #endif // #if VMA_STATS_STRING_ENABLED
12213 
12214 
12215 ////////////////////////////////////////////////////////////////////////////////
12216 // class VmaDeviceMemoryBlock
12217 
VmaDeviceMemoryBlock(VmaAllocator hAllocator)12218 VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) :
12219     m_pMetadata(VMA_NULL),
12220     m_MemoryTypeIndex(UINT32_MAX),
12221     m_Id(0),
12222     m_hMemory(VK_NULL_HANDLE),
12223     m_MapCount(0),
12224     m_pMappedData(VMA_NULL),
12225     m_bindComplete(false)
12226 {
12227 }
12228 
Init(VmaAllocator hAllocator,VmaPool hParentPool,uint32_t newMemoryTypeIndex,VkDeviceMemory newMemory,VkDeviceSize newSize,uint32_t id,uint32_t algorithm)12229 void VmaDeviceMemoryBlock::Init(
12230     VmaAllocator hAllocator,
12231     VmaPool hParentPool,
12232     uint32_t newMemoryTypeIndex,
12233     VkDeviceMemory newMemory,
12234     VkDeviceSize newSize,
12235     uint32_t id,
12236     uint32_t algorithm)
12237 {
12238     VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
12239 
12240     m_hParentPool = hParentPool;
12241     m_MemoryTypeIndex = newMemoryTypeIndex;
12242     m_Id = id;
12243     m_hMemory = newMemory;
12244 
12245     switch(algorithm)
12246     {
12247     case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
12248         m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator);
12249         break;
12250     case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
12251         m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator);
12252         break;
12253     default:
12254         VMA_ASSERT(0);
12255         // Fall-through.
12256     case 0:
12257         m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator);
12258     }
12259     m_pMetadata->Init(newSize);
12260 }
12261 
Destroy(VmaAllocator allocator)12262 void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
12263 {
12264     // This is the most important assert in the entire library.
12265     // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
12266     VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
12267 
12268     VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
12269     allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
12270     m_hMemory = VK_NULL_HANDLE;
12271 
12272     vma_delete(allocator, m_pMetadata);
12273     m_pMetadata = VMA_NULL;
12274 }
12275 
Validate()12276 bool VmaDeviceMemoryBlock::Validate() const
12277 {
12278     VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
12279         (m_pMetadata->GetSize() != 0));
12280 
12281     return m_pMetadata->Validate();
12282 }
12283 
CheckCorruption(VmaAllocator hAllocator)12284 VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
12285 {
12286     void* pData = nullptr;
12287     VkResult res = Map(hAllocator, 1, &pData);
12288     if(res != VK_SUCCESS)
12289     {
12290         return res;
12291     }
12292 
12293     res = m_pMetadata->CheckCorruption(pData);
12294 
12295     Unmap(hAllocator, 1);
12296 
12297     return res;
12298 }
12299 
Map(VmaAllocator hAllocator,uint32_t count,void ** ppData)12300 VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
12301 {
12302     if(count == 0)
12303     {
12304         return VK_SUCCESS;
12305     }
12306 
12307     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12308     if(m_MapCount != 0)
12309     {
12310         m_MapCount += count;
12311         VMA_ASSERT(m_pMappedData != VMA_NULL);
12312         if(ppData != VMA_NULL)
12313         {
12314             *ppData = m_pMappedData;
12315         }
12316         return VK_SUCCESS;
12317     }
12318     else
12319     {
12320         VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
12321             hAllocator->m_hDevice,
12322             m_hMemory,
12323             0, // offset
12324             VK_WHOLE_SIZE,
12325             0, // flags
12326             &m_pMappedData);
12327         if(result == VK_SUCCESS)
12328         {
12329             if(ppData != VMA_NULL)
12330             {
12331                 *ppData = m_pMappedData;
12332             }
12333             m_MapCount = count;
12334         }
12335         return result;
12336     }
12337 }
12338 
Unmap(VmaAllocator hAllocator,uint32_t count)12339 void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
12340 {
12341     if(count == 0)
12342     {
12343         return;
12344     }
12345 
12346     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12347     if(m_MapCount >= count)
12348     {
12349         m_MapCount -= count;
12350         if(m_MapCount == 0)
12351         {
12352             m_pMappedData = VMA_NULL;
12353             (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
12354         }
12355     }
12356     else
12357     {
12358         VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
12359     }
12360 }
12361 
WriteMagicValueAroundAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)12362 VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
12363 {
12364     VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
12365     VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
12366 
12367     void* pData;
12368     VkResult res = Map(hAllocator, 1, &pData);
12369     if(res != VK_SUCCESS)
12370     {
12371         return res;
12372     }
12373 
12374     VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN);
12375     VmaWriteMagicValue(pData, allocOffset + allocSize);
12376 
12377     Unmap(hAllocator, 1);
12378 
12379     return VK_SUCCESS;
12380 }
12381 
ValidateMagicValueAroundAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)12382 VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
12383 {
12384     VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
12385     VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
12386 
12387     void* pData;
12388     VkResult res = Map(hAllocator, 1, &pData);
12389     if(res != VK_SUCCESS)
12390     {
12391         return res;
12392     }
12393 
12394     if(!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN))
12395     {
12396         VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
12397     }
12398     else if(!VmaValidateMagicValue(pData, allocOffset + allocSize))
12399     {
12400         VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
12401     }
12402 
12403     Unmap(hAllocator, 1);
12404 
12405     return VK_SUCCESS;
12406 }
12407 
BindBufferMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkBuffer hBuffer,const void * pNext)12408 VkResult VmaDeviceMemoryBlock::BindBufferMemory(
12409     const VmaAllocator hAllocator,
12410     const VmaAllocation hAllocation,
12411     VkDeviceSize allocationLocalOffset,
12412     VkBuffer hBuffer,
12413     const void* pNext)
12414 {
12415     VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
12416         hAllocation->GetBlock() == this);
12417     VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
12418         "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
12419     const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
12420     // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
12421     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12422     return hAllocator->BindVulkanBuffer(m_hMemory, memoryOffset, hBuffer, pNext);
12423 }
12424 
BindImageMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkImage hImage,const void * pNext)12425 VkResult VmaDeviceMemoryBlock::BindImageMemory(
12426     const VmaAllocator hAllocator,
12427     const VmaAllocation hAllocation,
12428     VkDeviceSize allocationLocalOffset,
12429     VkImage hImage,
12430     const void* pNext)
12431 {
12432     VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
12433         hAllocation->GetBlock() == this);
12434     VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
12435         "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
12436     const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
12437     // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
12438     VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12439     return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext);
12440 }
12441 
InitStatInfo(VmaStatInfo & outInfo)12442 static void InitStatInfo(VmaStatInfo& outInfo)
12443 {
12444     memset(&outInfo, 0, sizeof(outInfo));
12445     outInfo.allocationSizeMin = UINT64_MAX;
12446     outInfo.unusedRangeSizeMin = UINT64_MAX;
12447 }
12448 
12449 // Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
VmaAddStatInfo(VmaStatInfo & inoutInfo,const VmaStatInfo & srcInfo)12450 static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
12451 {
12452     inoutInfo.blockCount += srcInfo.blockCount;
12453     inoutInfo.allocationCount += srcInfo.allocationCount;
12454     inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
12455     inoutInfo.usedBytes += srcInfo.usedBytes;
12456     inoutInfo.unusedBytes += srcInfo.unusedBytes;
12457     inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
12458     inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
12459     inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
12460     inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
12461 }
12462 
VmaPostprocessCalcStatInfo(VmaStatInfo & inoutInfo)12463 static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
12464 {
12465     inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
12466         VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
12467     inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
12468         VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
12469 }
12470 
VmaPool_T(VmaAllocator hAllocator,const VmaPoolCreateInfo & createInfo,VkDeviceSize preferredBlockSize)12471 VmaPool_T::VmaPool_T(
12472     VmaAllocator hAllocator,
12473     const VmaPoolCreateInfo& createInfo,
12474     VkDeviceSize preferredBlockSize) :
12475     m_BlockVector(
12476         hAllocator,
12477         this, // hParentPool
12478         createInfo.memoryTypeIndex,
12479         createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
12480         createInfo.minBlockCount,
12481         createInfo.maxBlockCount,
12482         (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
12483         createInfo.frameInUseCount,
12484         createInfo.blockSize != 0, // explicitBlockSize
12485         createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK), // algorithm
12486     m_Id(0),
12487     m_Name(VMA_NULL)
12488 {
12489 }
12490 
~VmaPool_T()12491 VmaPool_T::~VmaPool_T()
12492 {
12493 }
12494 
SetName(const char * pName)12495 void VmaPool_T::SetName(const char* pName)
12496 {
12497     const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks();
12498     VmaFreeString(allocs, m_Name);
12499 
12500     if(pName != VMA_NULL)
12501     {
12502         m_Name = VmaCreateStringCopy(allocs, pName);
12503     }
12504     else
12505     {
12506         m_Name = VMA_NULL;
12507     }
12508 }
12509 
12510 #if VMA_STATS_STRING_ENABLED
12511 
12512 #endif // #if VMA_STATS_STRING_ENABLED
12513 
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)12514 VmaBlockVector::VmaBlockVector(
12515     VmaAllocator hAllocator,
12516     VmaPool hParentPool,
12517     uint32_t memoryTypeIndex,
12518     VkDeviceSize preferredBlockSize,
12519     size_t minBlockCount,
12520     size_t maxBlockCount,
12521     VkDeviceSize bufferImageGranularity,
12522     uint32_t frameInUseCount,
12523     bool explicitBlockSize,
12524     uint32_t algorithm) :
12525     m_hAllocator(hAllocator),
12526     m_hParentPool(hParentPool),
12527     m_MemoryTypeIndex(memoryTypeIndex),
12528     m_PreferredBlockSize(preferredBlockSize),
12529     m_MinBlockCount(minBlockCount),
12530     m_MaxBlockCount(maxBlockCount),
12531     m_BufferImageGranularity(bufferImageGranularity),
12532     m_FrameInUseCount(frameInUseCount),
12533     m_ExplicitBlockSize(explicitBlockSize),
12534     m_Algorithm(algorithm),
12535     m_HasEmptyBlock(false),
12536     m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
12537     m_NextBlockId(0)
12538 {
12539 }
12540 
~VmaBlockVector()12541 VmaBlockVector::~VmaBlockVector()
12542 {
12543     for(size_t i = m_Blocks.size(); i--; )
12544     {
12545         m_Blocks[i]->Destroy(m_hAllocator);
12546         vma_delete(m_hAllocator, m_Blocks[i]);
12547     }
12548 }
12549 
CreateMinBlocks()12550 VkResult VmaBlockVector::CreateMinBlocks()
12551 {
12552     for(size_t i = 0; i < m_MinBlockCount; ++i)
12553     {
12554         VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
12555         if(res != VK_SUCCESS)
12556         {
12557             return res;
12558         }
12559     }
12560     return VK_SUCCESS;
12561 }
12562 
GetPoolStats(VmaPoolStats * pStats)12563 void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
12564 {
12565     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12566 
12567     const size_t blockCount = m_Blocks.size();
12568 
12569     pStats->size = 0;
12570     pStats->unusedSize = 0;
12571     pStats->allocationCount = 0;
12572     pStats->unusedRangeCount = 0;
12573     pStats->unusedRangeSizeMax = 0;
12574     pStats->blockCount = blockCount;
12575 
12576     for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12577     {
12578         const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12579         VMA_ASSERT(pBlock);
12580         VMA_HEAVY_ASSERT(pBlock->Validate());
12581         pBlock->m_pMetadata->AddPoolStats(*pStats);
12582     }
12583 }
12584 
IsEmpty()12585 bool VmaBlockVector::IsEmpty()
12586 {
12587     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12588     return m_Blocks.empty();
12589 }
12590 
IsLastBlockBindComplete()12591 bool VmaBlockVector::IsLastBlockBindComplete()
12592 {
12593     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12594     if (m_Blocks.empty()) {
12595         return false;
12596     }
12597     VmaDeviceMemoryBlock* lastBlock = m_Blocks.back();
12598     if (!lastBlock) {
12599         return false;
12600     }
12601     return lastBlock->GetBindCompleteFlag();
12602 }
12603 
IsCorruptionDetectionEnabled()12604 bool VmaBlockVector::IsCorruptionDetectionEnabled() const
12605 {
12606     const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
12607     return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
12608         (VMA_DEBUG_MARGIN > 0) &&
12609         (m_Algorithm == 0 || m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) &&
12610         (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
12611 }
12612 
12613 static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
12614 
Allocate(uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)12615 VkResult VmaBlockVector::Allocate(
12616     uint32_t currentFrameIndex,
12617     VkDeviceSize size,
12618     VkDeviceSize alignment,
12619     const VmaAllocationCreateInfo& createInfo,
12620     VmaSuballocationType suballocType,
12621     size_t allocationCount,
12622     VmaAllocation* pAllocations)
12623 {
12624     size_t allocIndex;
12625     VkResult res = VK_SUCCESS;
12626 
12627     if(IsCorruptionDetectionEnabled())
12628     {
12629         size = VmaAlignUp<VkDeviceSize>(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12630         alignment = VmaAlignUp<VkDeviceSize>(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12631     }
12632 
12633     {
12634         VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12635         for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
12636         {
12637             res = AllocatePage(
12638                 currentFrameIndex,
12639                 size,
12640                 alignment,
12641                 createInfo,
12642                 suballocType,
12643                 pAllocations + allocIndex);
12644             if(res != VK_SUCCESS)
12645             {
12646                 break;
12647             }
12648         }
12649     }
12650 
12651     if(res != VK_SUCCESS)
12652     {
12653         // Free all already created allocations.
12654         while(allocIndex--)
12655         {
12656             Free(pAllocations[allocIndex]);
12657         }
12658         memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
12659     }
12660 
12661     return res;
12662 }
12663 
12664 // OH ISSUE: VMA preAlloc
AllocateReserved(uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)12665 VkResult VmaBlockVector::AllocateReserved(
12666     uint32_t currentFrameIndex,
12667     VkDeviceSize size,
12668     VkDeviceSize alignment,
12669     const VmaAllocationCreateInfo& createInfo,
12670     VmaSuballocationType suballocType,
12671     VmaAllocation* pAllocation)
12672 {
12673     size_t allocIndex;
12674     VkResult res = VK_SUCCESS;
12675 
12676     if(IsCorruptionDetectionEnabled())
12677     {
12678         size = VmaAlignUp<VkDeviceSize>(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12679         alignment = VmaAlignUp<VkDeviceSize>(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12680     }
12681     uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
12682 
12683     // Validate strategy.
12684     switch(strategy)
12685     {
12686     case 0:
12687         strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
12688         break;
12689     case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
12690     case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
12691     case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
12692         break;
12693     default:
12694         return VK_ERROR_FEATURE_NOT_PRESENT;
12695     }
12696 
12697     {
12698         VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12699         size_t newBlockIndex = 0;
12700         res = CreateBlock(m_PreferredBlockSize, &newBlockIndex);
12701         if(res != VK_SUCCESS)
12702         {
12703             return res;
12704         }
12705         VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
12706         VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
12707 
12708         res = AllocateFromBlock(
12709             pBlock,
12710             currentFrameIndex,
12711             size,
12712             alignment,
12713             createInfo.flags,
12714             createInfo.pUserData,
12715             suballocType,
12716             strategy,
12717             pAllocation);
12718     }
12719     return res;
12720 }
12721 
SwapLastBlock(VmaBlockVector * blockVector1,VmaBlockVector * blockVector2)12722 void SwapLastBlock(VmaBlockVector* blockVector1, VmaBlockVector* blockVector2)
12723 {
12724     VmaDeviceMemoryBlock* lastBlock1 = blockVector1->m_Blocks.back();
12725     blockVector1->m_Blocks.pop_back();
12726     VmaDeviceMemoryBlock* lastBlock2 = blockVector2->m_Blocks.back();
12727     blockVector2->m_Blocks.pop_back();
12728 
12729     blockVector1->m_Blocks.push_back(lastBlock2);
12730     blockVector2->m_Blocks.push_back(lastBlock1);
12731 }
12732 
AllocatePage(uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)12733 VkResult VmaBlockVector::AllocatePage(
12734     uint32_t currentFrameIndex,
12735     VkDeviceSize size,
12736     VkDeviceSize alignment,
12737     const VmaAllocationCreateInfo& createInfo,
12738     VmaSuballocationType suballocType,
12739     VmaAllocation* pAllocation)
12740 {
12741     const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
12742     bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
12743     const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
12744     const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
12745 
12746     VkDeviceSize freeMemory;
12747     {
12748         const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
12749         VmaBudget heapBudget = {};
12750         m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
12751         freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0;
12752     }
12753 
12754     const bool canFallbackToDedicated = !IsCustomPool();
12755     const bool canCreateNewBlock =
12756         ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
12757         (m_Blocks.size() < m_MaxBlockCount) &&
12758         (freeMemory >= size || !canFallbackToDedicated);
12759     uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
12760 
12761     // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
12762     // Which in turn is available only when maxBlockCount = 1.
12763     if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
12764     {
12765         canMakeOtherLost = false;
12766     }
12767 
12768     // Upper address can only be used with linear allocator and within single memory block.
12769     if(isUpperAddress &&
12770         (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
12771     {
12772         return VK_ERROR_FEATURE_NOT_PRESENT;
12773     }
12774 
12775     // Validate strategy.
12776     switch(strategy)
12777     {
12778     case 0:
12779         strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
12780         break;
12781     case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
12782     case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
12783     case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
12784         break;
12785     default:
12786         return VK_ERROR_FEATURE_NOT_PRESENT;
12787     }
12788 
12789     // Early reject: requested allocation size is larger that maximum block size for this block vector.
12790     if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
12791     {
12792         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12793     }
12794 
12795     /*
12796     Under certain condition, this whole section can be skipped for optimization, so
12797     we move on directly to trying to allocate with canMakeOtherLost. That's the case
12798     e.g. for custom pools with linear algorithm.
12799     */
12800     if(!canMakeOtherLost || canCreateNewBlock)
12801     {
12802         // 1. Search existing allocations. Try to allocate without making other allocations lost.
12803         VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
12804         allocFlagsCopy &= ~VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
12805 
12806         if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
12807         {
12808             // Use only last block.
12809             if(!m_Blocks.empty())
12810             {
12811                 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
12812                 VMA_ASSERT(pCurrBlock);
12813                 VkResult res = AllocateFromBlock(
12814                     pCurrBlock,
12815                     currentFrameIndex,
12816                     size,
12817                     alignment,
12818                     allocFlagsCopy,
12819                     createInfo.pUserData,
12820                     suballocType,
12821                     strategy,
12822                     pAllocation);
12823                 if(res == VK_SUCCESS)
12824                 {
12825                     VMA_DEBUG_LOG("    Returned from last block #%u", pCurrBlock->GetId());
12826                     return VK_SUCCESS;
12827                 }
12828             }
12829         }
12830         else
12831         {
12832             if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
12833             {
12834                 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12835                 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
12836                 {
12837                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12838                     VMA_ASSERT(pCurrBlock);
12839                     VkResult res = AllocateFromBlock(
12840                         pCurrBlock,
12841                         currentFrameIndex,
12842                         size,
12843                         alignment,
12844                         allocFlagsCopy,
12845                         createInfo.pUserData,
12846                         suballocType,
12847                         strategy,
12848                         pAllocation);
12849                     if(res == VK_SUCCESS)
12850                     {
12851                         VMA_DEBUG_LOG("    Returned from existing block #%u", pCurrBlock->GetId());
12852                         return VK_SUCCESS;
12853                     }
12854                 }
12855             }
12856             else // WORST_FIT, FIRST_FIT
12857             {
12858                 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
12859                 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12860                 {
12861                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12862                     VMA_ASSERT(pCurrBlock);
12863                     VkResult res = AllocateFromBlock(
12864                         pCurrBlock,
12865                         currentFrameIndex,
12866                         size,
12867                         alignment,
12868                         allocFlagsCopy,
12869                         createInfo.pUserData,
12870                         suballocType,
12871                         strategy,
12872                         pAllocation);
12873                     if(res == VK_SUCCESS)
12874                     {
12875                         VMA_DEBUG_LOG("    Returned from existing block #%u", pCurrBlock->GetId());
12876                         return VK_SUCCESS;
12877                     }
12878                 }
12879             }
12880         }
12881 
12882         // 2. Try to create new block.
12883         if(canCreateNewBlock)
12884         {
12885             // Calculate optimal size for new block.
12886             VkDeviceSize newBlockSize = m_PreferredBlockSize;
12887             uint32_t newBlockSizeShift = 0;
12888             const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
12889 
12890             if(!m_ExplicitBlockSize)
12891             {
12892                 // Allocate 1/8, 1/4, 1/2 as first blocks.
12893                 const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
12894                 for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
12895                 {
12896                     const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12897                     if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
12898                     {
12899                         newBlockSize = smallerNewBlockSize;
12900                         ++newBlockSizeShift;
12901                     }
12902                     else
12903                     {
12904                         break;
12905                     }
12906                 }
12907             }
12908 
12909             size_t newBlockIndex = 0;
12910             VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12911                 CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12912             // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
12913             if(!m_ExplicitBlockSize)
12914             {
12915                 while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
12916                 {
12917                     const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12918                     if(smallerNewBlockSize >= size)
12919                     {
12920                         newBlockSize = smallerNewBlockSize;
12921                         ++newBlockSizeShift;
12922                         res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12923                             CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12924                     }
12925                     else
12926                     {
12927                         break;
12928                     }
12929                 }
12930             }
12931 
12932             if(res == VK_SUCCESS)
12933             {
12934                 VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
12935                 VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
12936 
12937                 res = AllocateFromBlock(
12938                     pBlock,
12939                     currentFrameIndex,
12940                     size,
12941                     alignment,
12942                     allocFlagsCopy,
12943                     createInfo.pUserData,
12944                     suballocType,
12945                     strategy,
12946                     pAllocation);
12947                 if(res == VK_SUCCESS)
12948                 {
12949                     VMA_DEBUG_LOG("    Created new block #%u Size=%llu", pBlock->GetId(), newBlockSize);
12950                     return VK_SUCCESS;
12951                 }
12952                 else
12953                 {
12954                     // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
12955                     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12956                 }
12957             }
12958         }
12959     }
12960 
12961     // 3. Try to allocate from existing blocks with making other allocations lost.
12962     if(canMakeOtherLost)
12963     {
12964         uint32_t tryIndex = 0;
12965         for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
12966         {
12967             VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
12968             VmaAllocationRequest bestRequest = {};
12969             VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
12970 
12971             // 1. Search existing allocations.
12972             if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
12973             {
12974                 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12975                 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
12976                 {
12977                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12978                     VMA_ASSERT(pCurrBlock);
12979                     VmaAllocationRequest currRequest = {};
12980                     if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
12981                         currentFrameIndex,
12982                         m_FrameInUseCount,
12983                         m_BufferImageGranularity,
12984                         size,
12985                         alignment,
12986                         (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
12987                         suballocType,
12988                         canMakeOtherLost,
12989                         strategy,
12990                         &currRequest))
12991                     {
12992                         const VkDeviceSize currRequestCost = currRequest.CalcCost();
12993                         if(pBestRequestBlock == VMA_NULL ||
12994                             currRequestCost < bestRequestCost)
12995                         {
12996                             pBestRequestBlock = pCurrBlock;
12997                             bestRequest = currRequest;
12998                             bestRequestCost = currRequestCost;
12999 
13000                             if(bestRequestCost == 0)
13001                             {
13002                                 break;
13003                             }
13004                         }
13005                     }
13006                 }
13007             }
13008             else // WORST_FIT, FIRST_FIT
13009             {
13010                 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
13011                 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
13012                 {
13013                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
13014                     VMA_ASSERT(pCurrBlock);
13015                     VmaAllocationRequest currRequest = {};
13016                     if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
13017                         currentFrameIndex,
13018                         m_FrameInUseCount,
13019                         m_BufferImageGranularity,
13020                         size,
13021                         alignment,
13022                         (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
13023                         suballocType,
13024                         canMakeOtherLost,
13025                         strategy,
13026                         &currRequest))
13027                     {
13028                         const VkDeviceSize currRequestCost = currRequest.CalcCost();
13029                         if(pBestRequestBlock == VMA_NULL ||
13030                             currRequestCost < bestRequestCost ||
13031                             strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
13032                         {
13033                             pBestRequestBlock = pCurrBlock;
13034                             bestRequest = currRequest;
13035                             bestRequestCost = currRequestCost;
13036 
13037                             if(bestRequestCost == 0 ||
13038                                 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
13039                             {
13040                                 break;
13041                             }
13042                         }
13043                     }
13044                 }
13045             }
13046 
13047             if(pBestRequestBlock != VMA_NULL)
13048             {
13049                 if(mapped)
13050                 {
13051                     VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
13052                     if(res != VK_SUCCESS)
13053                     {
13054                         return res;
13055                     }
13056                 }
13057 
13058                 if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
13059                     currentFrameIndex,
13060                     m_FrameInUseCount,
13061                     &bestRequest))
13062                 {
13063                     // Allocate from this pBlock.
13064                     *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
13065                     pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, *pAllocation);
13066                     UpdateHasEmptyBlock();
13067                     (*pAllocation)->InitBlockAllocation(
13068                         pBestRequestBlock,
13069                         bestRequest.offset,
13070                         alignment,
13071                         size,
13072                         m_MemoryTypeIndex,
13073                         suballocType,
13074                         mapped,
13075                         (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0,
13076                         IsNewBlockFlag());
13077                     ClearNewBlockFlag();
13078                     VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
13079                     VMA_DEBUG_LOG("    Returned from existing block");
13080                     (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
13081                     m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
13082                     if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13083                     {
13084                         m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13085                     }
13086                     if(IsCorruptionDetectionEnabled())
13087                     {
13088                         VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
13089                         VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
13090                     }
13091                     return VK_SUCCESS;
13092                 }
13093                 // else: Some allocations must have been touched while we are here. Next try.
13094             }
13095             else
13096             {
13097                 // Could not find place in any of the blocks - break outer loop.
13098                 break;
13099             }
13100         }
13101         /* Maximum number of tries exceeded - a very unlike event when many other
13102         threads are simultaneously touching allocations making it impossible to make
13103         lost at the same time as we try to allocate. */
13104         if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
13105         {
13106             return VK_ERROR_TOO_MANY_OBJECTS;
13107         }
13108     }
13109 
13110     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13111 }
13112 
Free(const VmaAllocation hAllocation)13113 void VmaBlockVector::Free(
13114     const VmaAllocation hAllocation)
13115 {
13116     VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
13117 
13118     bool budgetExceeded = false;
13119     {
13120         const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
13121         VmaBudget heapBudget = {};
13122         m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
13123         budgetExceeded = heapBudget.usage >= heapBudget.budget;
13124     }
13125 
13126     // Scope for lock.
13127     {
13128         VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13129 
13130         VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
13131 
13132         if(IsCorruptionDetectionEnabled())
13133         {
13134             VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
13135             VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
13136         }
13137 
13138         if(hAllocation->IsPersistentMap())
13139         {
13140             pBlock->Unmap(m_hAllocator, 1);
13141         }
13142 
13143         pBlock->m_pMetadata->Free(hAllocation);
13144         VMA_HEAVY_ASSERT(pBlock->Validate());
13145 
13146         VMA_DEBUG_LOG("  Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
13147 
13148         const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount;
13149         // pBlock became empty after this deallocation.
13150         if(pBlock->m_pMetadata->IsEmpty())
13151         {
13152             // Already has empty block. We don't want to have two, so delete this one.
13153             if((m_HasEmptyBlock || budgetExceeded) && canDeleteBlock)
13154             {
13155                 pBlockToDelete = pBlock;
13156                 Remove(pBlock);
13157             }
13158             // else: We now have an empty block - leave it.
13159         }
13160         // pBlock didn't become empty, but we have another empty block - find and free that one.
13161         // (This is optional, heuristics.)
13162         else if(m_HasEmptyBlock && canDeleteBlock)
13163         {
13164             VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
13165             if(pLastBlock->m_pMetadata->IsEmpty())
13166             {
13167                 pBlockToDelete = pLastBlock;
13168                 m_Blocks.pop_back();
13169             }
13170         }
13171 
13172         UpdateHasEmptyBlock();
13173         IncrementallySortBlocks();
13174     }
13175 
13176     // Destruction of a free block. Deferred until this point, outside of mutex
13177     // lock, for performance reason.
13178     if(pBlockToDelete != VMA_NULL)
13179     {
13180         VMA_DEBUG_LOG("    Deleted empty block");
13181         pBlockToDelete->Destroy(m_hAllocator);
13182         vma_delete(m_hAllocator, pBlockToDelete);
13183     }
13184 }
13185 
13186 // OH ISSUE: VMA preAlloc
FreeReserved(const VmaAllocation hAllocation)13187 void VmaBlockVector::FreeReserved(
13188     const VmaAllocation hAllocation)
13189 {
13190     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13191 
13192     VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
13193 
13194     if(IsCorruptionDetectionEnabled())
13195     {
13196         VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
13197         VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
13198     }
13199 
13200     if(hAllocation->IsPersistentMap())
13201     {
13202         pBlock->Unmap(m_hAllocator, 1);
13203     }
13204 
13205     pBlock->m_pMetadata->Free(hAllocation);
13206     VMA_HEAVY_ASSERT(pBlock->Validate());
13207 
13208     VMA_DEBUG_LOG("  Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
13209     IncrementallySortBlocks();
13210 }
13211 
CalcMaxBlockSize()13212 VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
13213 {
13214     VkDeviceSize result = 0;
13215     for(size_t i = m_Blocks.size(); i--; )
13216     {
13217         result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
13218         if(result >= m_PreferredBlockSize)
13219         {
13220             break;
13221         }
13222     }
13223     return result;
13224 }
13225 
Remove(VmaDeviceMemoryBlock * pBlock)13226 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
13227 {
13228     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13229     {
13230         if(m_Blocks[blockIndex] == pBlock)
13231         {
13232             VmaVectorRemove(m_Blocks, blockIndex);
13233             return;
13234         }
13235     }
13236     VMA_ASSERT(0);
13237 }
13238 
IncrementallySortBlocks()13239 void VmaBlockVector::IncrementallySortBlocks()
13240 {
13241     if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
13242     {
13243         // Bubble sort only until first swap.
13244         for(size_t i = 1; i < m_Blocks.size(); ++i)
13245         {
13246             if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
13247             {
13248                 VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
13249                 return;
13250             }
13251         }
13252     }
13253 }
13254 
AllocateFromBlock(VmaDeviceMemoryBlock * pBlock,uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,VmaAllocationCreateFlags allocFlags,void * pUserData,VmaSuballocationType suballocType,uint32_t strategy,VmaAllocation * pAllocation)13255 VkResult VmaBlockVector::AllocateFromBlock(
13256     VmaDeviceMemoryBlock* pBlock,
13257     uint32_t currentFrameIndex,
13258     VkDeviceSize size,
13259     VkDeviceSize alignment,
13260     VmaAllocationCreateFlags allocFlags,
13261     void* pUserData,
13262     VmaSuballocationType suballocType,
13263     uint32_t strategy,
13264     VmaAllocation* pAllocation)
13265 {
13266     VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
13267     const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
13268     const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
13269     const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
13270 
13271     VmaAllocationRequest currRequest = {};
13272     if(pBlock->m_pMetadata->CreateAllocationRequest(
13273         currentFrameIndex,
13274         m_FrameInUseCount,
13275         m_BufferImageGranularity,
13276         size,
13277         alignment,
13278         isUpperAddress,
13279         suballocType,
13280         false, // canMakeOtherLost
13281         strategy,
13282         &currRequest))
13283     {
13284         // Allocate from pCurrBlock.
13285         VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
13286 
13287         if(mapped)
13288         {
13289             VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
13290             if(res != VK_SUCCESS)
13291             {
13292                 return res;
13293             }
13294         }
13295 
13296         *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
13297         pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, *pAllocation);
13298         UpdateHasEmptyBlock();
13299         (*pAllocation)->InitBlockAllocation(
13300             pBlock,
13301             currRequest.offset,
13302             alignment,
13303             size,
13304             m_MemoryTypeIndex,
13305             suballocType,
13306             mapped,
13307             (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0,
13308             IsNewBlockFlag());
13309         ClearNewBlockFlag();
13310         VMA_HEAVY_ASSERT(pBlock->Validate());
13311         (*pAllocation)->SetUserData(m_hAllocator, pUserData);
13312         m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
13313         if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13314         {
13315             m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13316         }
13317         if(IsCorruptionDetectionEnabled())
13318         {
13319             VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
13320             VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
13321         }
13322         return VK_SUCCESS;
13323     }
13324     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13325 }
13326 
CreateBlock(VkDeviceSize blockSize,size_t * pNewBlockIndex)13327 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
13328 {
13329     VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
13330     allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
13331     allocInfo.allocationSize = blockSize;
13332 
13333 #if VMA_BUFFER_DEVICE_ADDRESS
13334     // Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature.
13335     VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
13336     if(m_hAllocator->m_UseKhrBufferDeviceAddress)
13337     {
13338         allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
13339         VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
13340     }
13341 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
13342 
13343     VkDeviceMemory mem = VK_NULL_HANDLE;
13344     VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
13345     if(res < 0)
13346     {
13347         return res;
13348     }
13349 
13350     // New VkDeviceMemory successfully created.
13351 
13352     // Create new Allocation for it.
13353     VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
13354     pBlock->Init(
13355         m_hAllocator,
13356         m_hParentPool,
13357         m_MemoryTypeIndex,
13358         mem,
13359         allocInfo.allocationSize,
13360         m_NextBlockId++,
13361         m_Algorithm);
13362     m_NewBlockFlag = true;
13363 
13364     m_Blocks.push_back(pBlock);
13365     if(pNewBlockIndex != VMA_NULL)
13366     {
13367         *pNewBlockIndex = m_Blocks.size() - 1;
13368     }
13369 
13370     return VK_SUCCESS;
13371 }
13372 
ApplyDefragmentationMovesCpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,const VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves)13373 void VmaBlockVector::ApplyDefragmentationMovesCpu(
13374     class VmaBlockVectorDefragmentationContext* pDefragCtx,
13375     const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
13376 {
13377     const size_t blockCount = m_Blocks.size();
13378     const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
13379 
13380     enum BLOCK_FLAG
13381     {
13382         BLOCK_FLAG_USED = 0x00000001,
13383         BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
13384     };
13385 
13386     struct BlockInfo
13387     {
13388         uint32_t flags;
13389         void* pMappedData;
13390     };
13391     VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
13392         blockInfo(blockCount, BlockInfo(), VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
13393     memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
13394 
13395     // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13396     const size_t moveCount = moves.size();
13397     for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13398     {
13399         const VmaDefragmentationMove& move = moves[moveIndex];
13400         blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
13401         blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
13402     }
13403 
13404     VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13405 
13406     // Go over all blocks. Get mapped pointer or map if necessary.
13407     for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13408     {
13409         BlockInfo& currBlockInfo = blockInfo[blockIndex];
13410         VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13411         if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
13412         {
13413             currBlockInfo.pMappedData = pBlock->GetMappedData();
13414             // It is not originally mapped - map it.
13415             if(currBlockInfo.pMappedData == VMA_NULL)
13416             {
13417                 pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
13418                 if(pDefragCtx->res == VK_SUCCESS)
13419                 {
13420                     currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
13421                 }
13422             }
13423         }
13424     }
13425 
13426     // Go over all moves. Do actual data transfer.
13427     if(pDefragCtx->res == VK_SUCCESS)
13428     {
13429         const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
13430         VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
13431 
13432         for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13433         {
13434             const VmaDefragmentationMove& move = moves[moveIndex];
13435 
13436             const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
13437             const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
13438 
13439             VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
13440 
13441             // Invalidate source.
13442             if(isNonCoherent)
13443             {
13444                 VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
13445                 memRange.memory = pSrcBlock->GetDeviceMemory();
13446                 memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
13447                 memRange.size = VMA_MIN(
13448                     VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
13449                     pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
13450                 (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13451             }
13452 
13453             // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
13454             memmove(
13455                 reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
13456                 reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
13457                 static_cast<size_t>(move.size));
13458 
13459             if(IsCorruptionDetectionEnabled())
13460             {
13461                 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
13462                 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
13463             }
13464 
13465             // Flush destination.
13466             if(isNonCoherent)
13467             {
13468                 VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
13469                 memRange.memory = pDstBlock->GetDeviceMemory();
13470                 memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
13471                 memRange.size = VMA_MIN(
13472                     VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
13473                     pDstBlock->m_pMetadata->GetSize() - memRange.offset);
13474                 (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13475             }
13476         }
13477     }
13478 
13479     // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
13480     // Regardless of pCtx->res == VK_SUCCESS.
13481     for(size_t blockIndex = blockCount; blockIndex--; )
13482     {
13483         const BlockInfo& currBlockInfo = blockInfo[blockIndex];
13484         if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
13485         {
13486             VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13487             pBlock->Unmap(m_hAllocator, 1);
13488         }
13489     }
13490 }
13491 
ApplyDefragmentationMovesGpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkCommandBuffer commandBuffer)13492 void VmaBlockVector::ApplyDefragmentationMovesGpu(
13493     class VmaBlockVectorDefragmentationContext* pDefragCtx,
13494     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13495     VkCommandBuffer commandBuffer)
13496 {
13497     const size_t blockCount = m_Blocks.size();
13498 
13499     pDefragCtx->blockContexts.resize(blockCount);
13500     memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext));
13501 
13502     // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13503     const size_t moveCount = moves.size();
13504     for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13505     {
13506         const VmaDefragmentationMove& move = moves[moveIndex];
13507 
13508         //if(move.type == VMA_ALLOCATION_TYPE_UNKNOWN)
13509         {
13510             // Old school move still require us to map the whole block
13511             pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13512             pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13513         }
13514     }
13515 
13516     VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13517 
13518     // Go over all blocks. Create and bind buffer for whole block if necessary.
13519     {
13520         VkBufferCreateInfo bufCreateInfo;
13521         VmaFillGpuDefragmentationBufferCreateInfo(bufCreateInfo);
13522 
13523         for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13524         {
13525             VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
13526             VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13527             if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
13528             {
13529                 bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
13530                 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
13531                     m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
13532                 if(pDefragCtx->res == VK_SUCCESS)
13533                 {
13534                     pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
13535                         m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
13536                 }
13537             }
13538         }
13539     }
13540 
13541     // Go over all moves. Post data transfer commands to command buffer.
13542     if(pDefragCtx->res == VK_SUCCESS)
13543     {
13544         for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13545         {
13546             const VmaDefragmentationMove& move = moves[moveIndex];
13547 
13548             const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
13549             const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
13550 
13551             VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
13552 
13553             VkBufferCopy region = {
13554                 move.srcOffset,
13555                 move.dstOffset,
13556                 move.size };
13557             (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
13558                 commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, &region);
13559         }
13560     }
13561 
13562     // Save buffers to defrag context for later destruction.
13563     if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
13564     {
13565         pDefragCtx->res = VK_NOT_READY;
13566     }
13567 }
13568 
FreeEmptyBlocks(VmaDefragmentationStats * pDefragmentationStats)13569 void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
13570 {
13571     for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
13572     {
13573         VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13574         if(pBlock->m_pMetadata->IsEmpty())
13575         {
13576             if(m_Blocks.size() > m_MinBlockCount)
13577             {
13578                 if(pDefragmentationStats != VMA_NULL)
13579                 {
13580                     ++pDefragmentationStats->deviceMemoryBlocksFreed;
13581                     pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
13582                 }
13583 
13584                 VmaVectorRemove(m_Blocks, blockIndex);
13585                 pBlock->Destroy(m_hAllocator);
13586                 vma_delete(m_hAllocator, pBlock);
13587             }
13588             else
13589             {
13590                 break;
13591             }
13592         }
13593     }
13594     UpdateHasEmptyBlock();
13595 }
13596 
UpdateHasEmptyBlock()13597 void VmaBlockVector::UpdateHasEmptyBlock()
13598 {
13599     m_HasEmptyBlock = false;
13600     for(size_t index = 0, count = m_Blocks.size(); index < count; ++index)
13601     {
13602         VmaDeviceMemoryBlock* const pBlock = m_Blocks[index];
13603         if(pBlock->m_pMetadata->IsEmpty())
13604         {
13605             m_HasEmptyBlock = true;
13606             break;
13607         }
13608     }
13609 }
13610 
13611 #if VMA_STATS_STRING_ENABLED
13612 
PrintDetailedMap(class VmaJsonWriter & json)13613 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
13614 {
13615     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13616 
13617     json.BeginObject();
13618 
13619     if(IsCustomPool())
13620     {
13621         const char* poolName = m_hParentPool->GetName();
13622         if(poolName != VMA_NULL && poolName[0] != '\0')
13623         {
13624             json.WriteString("Name");
13625             json.WriteString(poolName);
13626         }
13627 
13628         json.WriteString("MemoryTypeIndex");
13629         json.WriteNumber(m_MemoryTypeIndex);
13630 
13631         json.WriteString("BlockSize");
13632         json.WriteNumber(m_PreferredBlockSize);
13633 
13634         json.WriteString("BlockCount");
13635         json.BeginObject(true);
13636         if(m_MinBlockCount > 0)
13637         {
13638             json.WriteString("Min");
13639             json.WriteNumber((uint64_t)m_MinBlockCount);
13640         }
13641         if(m_MaxBlockCount < SIZE_MAX)
13642         {
13643             json.WriteString("Max");
13644             json.WriteNumber((uint64_t)m_MaxBlockCount);
13645         }
13646         json.WriteString("Cur");
13647         json.WriteNumber((uint64_t)m_Blocks.size());
13648         json.EndObject();
13649 
13650         if(m_FrameInUseCount > 0)
13651         {
13652             json.WriteString("FrameInUseCount");
13653             json.WriteNumber(m_FrameInUseCount);
13654         }
13655 
13656         if(m_Algorithm != 0)
13657         {
13658             json.WriteString("Algorithm");
13659             json.WriteString(VmaAlgorithmToStr(m_Algorithm));
13660         }
13661     }
13662     else
13663     {
13664         json.WriteString("PreferredBlockSize");
13665         json.WriteNumber(m_PreferredBlockSize);
13666     }
13667 
13668     json.WriteString("Blocks");
13669     json.BeginObject();
13670     for(size_t i = 0; i < m_Blocks.size(); ++i)
13671     {
13672         json.BeginString();
13673         json.ContinueString(m_Blocks[i]->GetId());
13674         json.EndString();
13675 
13676         m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
13677     }
13678     json.EndObject();
13679 
13680     json.EndObject();
13681 }
13682 
13683 #endif // #if VMA_STATS_STRING_ENABLED
13684 
Defragment(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats,VmaDefragmentationFlags flags,VkDeviceSize & maxCpuBytesToMove,uint32_t & maxCpuAllocationsToMove,VkDeviceSize & maxGpuBytesToMove,uint32_t & maxGpuAllocationsToMove,VkCommandBuffer commandBuffer)13685 void VmaBlockVector::Defragment(
13686     class VmaBlockVectorDefragmentationContext* pCtx,
13687     VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags,
13688     VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
13689     VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
13690     VkCommandBuffer commandBuffer)
13691 {
13692     pCtx->res = VK_SUCCESS;
13693 
13694     const VkMemoryPropertyFlags memPropFlags =
13695         m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
13696     const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
13697 
13698     const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
13699         isHostVisible;
13700     const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
13701         !IsCorruptionDetectionEnabled() &&
13702         ((1u << m_MemoryTypeIndex) & m_hAllocator->GetGpuDefragmentationMemoryTypeBits()) != 0;
13703 
13704     // There are options to defragment this memory type.
13705     if(canDefragmentOnCpu || canDefragmentOnGpu)
13706     {
13707         bool defragmentOnGpu;
13708         // There is only one option to defragment this memory type.
13709         if(canDefragmentOnGpu != canDefragmentOnCpu)
13710         {
13711             defragmentOnGpu = canDefragmentOnGpu;
13712         }
13713         // Both options are available: Heuristics to choose the best one.
13714         else
13715         {
13716             defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
13717                 m_hAllocator->IsIntegratedGpu();
13718         }
13719 
13720         bool overlappingMoveSupported = !defragmentOnGpu;
13721 
13722         if(m_hAllocator->m_UseMutex)
13723         {
13724             if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
13725             {
13726                 if(!m_Mutex.TryLockWrite())
13727                 {
13728                     pCtx->res = VK_ERROR_INITIALIZATION_FAILED;
13729                     return;
13730                 }
13731             }
13732             else
13733             {
13734                 m_Mutex.LockWrite();
13735                 pCtx->mutexLocked = true;
13736             }
13737         }
13738 
13739         pCtx->Begin(overlappingMoveSupported, flags);
13740 
13741         // Defragment.
13742 
13743         const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
13744         const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
13745         pCtx->res = pCtx->GetAlgorithm()->Defragment(pCtx->defragmentationMoves, maxBytesToMove, maxAllocationsToMove, flags);
13746 
13747         // Accumulate statistics.
13748         if(pStats != VMA_NULL)
13749         {
13750             const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
13751             const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
13752             pStats->bytesMoved += bytesMoved;
13753             pStats->allocationsMoved += allocationsMoved;
13754             VMA_ASSERT(bytesMoved <= maxBytesToMove);
13755             VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
13756             if(defragmentOnGpu)
13757             {
13758                 maxGpuBytesToMove -= bytesMoved;
13759                 maxGpuAllocationsToMove -= allocationsMoved;
13760             }
13761             else
13762             {
13763                 maxCpuBytesToMove -= bytesMoved;
13764                 maxCpuAllocationsToMove -= allocationsMoved;
13765             }
13766         }
13767 
13768         if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
13769         {
13770             if(m_hAllocator->m_UseMutex)
13771                 m_Mutex.UnlockWrite();
13772 
13773             if(pCtx->res >= VK_SUCCESS && !pCtx->defragmentationMoves.empty())
13774                 pCtx->res = VK_NOT_READY;
13775 
13776             return;
13777         }
13778 
13779         if(pCtx->res >= VK_SUCCESS)
13780         {
13781             if(defragmentOnGpu)
13782             {
13783                 ApplyDefragmentationMovesGpu(pCtx, pCtx->defragmentationMoves, commandBuffer);
13784             }
13785             else
13786             {
13787                 ApplyDefragmentationMovesCpu(pCtx, pCtx->defragmentationMoves);
13788             }
13789         }
13790     }
13791 }
13792 
DefragmentationEnd(class VmaBlockVectorDefragmentationContext * pCtx,uint32_t flags,VmaDefragmentationStats * pStats)13793 void VmaBlockVector::DefragmentationEnd(
13794     class VmaBlockVectorDefragmentationContext* pCtx,
13795     uint32_t flags,
13796     VmaDefragmentationStats* pStats)
13797 {
13798     if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL && m_hAllocator->m_UseMutex)
13799     {
13800         VMA_ASSERT(pCtx->mutexLocked == false);
13801 
13802         // Incremental defragmentation doesn't hold the lock, so when we enter here we don't actually have any
13803         // lock protecting us. Since we mutate state here, we have to take the lock out now
13804         m_Mutex.LockWrite();
13805         pCtx->mutexLocked = true;
13806     }
13807 
13808     // If the mutex isn't locked we didn't do any work and there is nothing to delete.
13809     if(pCtx->mutexLocked || !m_hAllocator->m_UseMutex)
13810     {
13811         // Destroy buffers.
13812         for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--;)
13813         {
13814             VmaBlockDefragmentationContext &blockCtx = pCtx->blockContexts[blockIndex];
13815             if(blockCtx.hBuffer)
13816             {
13817                 (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
13818             }
13819         }
13820 
13821         if(pCtx->res >= VK_SUCCESS)
13822         {
13823             FreeEmptyBlocks(pStats);
13824         }
13825     }
13826 
13827     if(pCtx->mutexLocked)
13828     {
13829         VMA_ASSERT(m_hAllocator->m_UseMutex);
13830         m_Mutex.UnlockWrite();
13831     }
13832 }
13833 
ProcessDefragmentations(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationPassMoveInfo * pMove,uint32_t maxMoves)13834 uint32_t VmaBlockVector::ProcessDefragmentations(
13835     class VmaBlockVectorDefragmentationContext *pCtx,
13836     VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves)
13837 {
13838     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13839 
13840     const uint32_t moveCount = std::min(uint32_t(pCtx->defragmentationMoves.size()) - pCtx->defragmentationMovesProcessed, maxMoves);
13841 
13842     for(uint32_t i = 0; i < moveCount; ++ i)
13843     {
13844         VmaDefragmentationMove& move = pCtx->defragmentationMoves[pCtx->defragmentationMovesProcessed + i];
13845 
13846         pMove->allocation = move.hAllocation;
13847         pMove->memory = move.pDstBlock->GetDeviceMemory();
13848         pMove->offset = move.dstOffset;
13849 
13850         ++ pMove;
13851     }
13852 
13853     pCtx->defragmentationMovesProcessed += moveCount;
13854 
13855     return moveCount;
13856 }
13857 
CommitDefragmentations(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats)13858 void VmaBlockVector::CommitDefragmentations(
13859     class VmaBlockVectorDefragmentationContext *pCtx,
13860     VmaDefragmentationStats* pStats)
13861 {
13862     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13863 
13864     for(uint32_t i = pCtx->defragmentationMovesCommitted; i < pCtx->defragmentationMovesProcessed; ++ i)
13865     {
13866         const VmaDefragmentationMove &move = pCtx->defragmentationMoves[i];
13867 
13868         move.pSrcBlock->m_pMetadata->FreeAtOffset(move.srcOffset);
13869         move.hAllocation->ChangeBlockAllocation(m_hAllocator, move.pDstBlock, move.dstOffset);
13870     }
13871 
13872     pCtx->defragmentationMovesCommitted = pCtx->defragmentationMovesProcessed;
13873     FreeEmptyBlocks(pStats);
13874 }
13875 
CalcAllocationCount()13876 size_t VmaBlockVector::CalcAllocationCount() const
13877 {
13878     size_t result = 0;
13879     for(size_t i = 0; i < m_Blocks.size(); ++i)
13880     {
13881         result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
13882     }
13883     return result;
13884 }
13885 
IsBufferImageGranularityConflictPossible()13886 bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
13887 {
13888     if(m_BufferImageGranularity == 1)
13889     {
13890         return false;
13891     }
13892     VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
13893     for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
13894     {
13895         VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
13896         VMA_ASSERT(m_Algorithm == 0);
13897         VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
13898         if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
13899         {
13900             return true;
13901         }
13902     }
13903     return false;
13904 }
13905 
MakePoolAllocationsLost(uint32_t currentFrameIndex,size_t * pLostAllocationCount)13906 void VmaBlockVector::MakePoolAllocationsLost(
13907     uint32_t currentFrameIndex,
13908     size_t* pLostAllocationCount)
13909 {
13910     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13911     size_t lostAllocationCount = 0;
13912     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13913     {
13914         VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13915         VMA_ASSERT(pBlock);
13916         lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
13917     }
13918     if(pLostAllocationCount != VMA_NULL)
13919     {
13920         *pLostAllocationCount = lostAllocationCount;
13921     }
13922 }
13923 
CheckCorruption()13924 VkResult VmaBlockVector::CheckCorruption()
13925 {
13926     if(!IsCorruptionDetectionEnabled())
13927     {
13928         return VK_ERROR_FEATURE_NOT_PRESENT;
13929     }
13930 
13931     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13932     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13933     {
13934         VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13935         VMA_ASSERT(pBlock);
13936         VkResult res = pBlock->CheckCorruption(m_hAllocator);
13937         if(res != VK_SUCCESS)
13938         {
13939             return res;
13940         }
13941     }
13942     return VK_SUCCESS;
13943 }
13944 
AddStats(VmaStats * pStats)13945 void VmaBlockVector::AddStats(VmaStats* pStats)
13946 {
13947     const uint32_t memTypeIndex = m_MemoryTypeIndex;
13948     const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
13949 
13950     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13951 
13952     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13953     {
13954         const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13955         VMA_ASSERT(pBlock);
13956         VMA_HEAVY_ASSERT(pBlock->Validate());
13957         VmaStatInfo allocationStatInfo;
13958         pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
13959         VmaAddStatInfo(pStats->total, allocationStatInfo);
13960         VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
13961         VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
13962     }
13963 }
13964 
FreeEmptyBlock()13965 void VmaBlockVector::FreeEmptyBlock()
13966 {
13967     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13968     FreeEmptyBlocks(VMA_NULL);
13969 }
13970 
13971 ////////////////////////////////////////////////////////////////////////////////
13972 // VmaDefragmentationAlgorithm_Generic members definition
13973 
VmaDefragmentationAlgorithm_Generic(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)13974 VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
13975     VmaAllocator hAllocator,
13976     VmaBlockVector* pBlockVector,
13977     uint32_t currentFrameIndex,
13978     bool overlappingMoveSupported) :
13979     VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
13980     m_AllocationCount(0),
13981     m_AllAllocations(false),
13982     m_BytesMoved(0),
13983     m_AllocationsMoved(0),
13984     m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
13985 {
13986     // Create block info for each block.
13987     const size_t blockCount = m_pBlockVector->m_Blocks.size();
13988     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13989     {
13990         BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
13991         pBlockInfo->m_OriginalBlockIndex = blockIndex;
13992         pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
13993         m_Blocks.push_back(pBlockInfo);
13994     }
13995 
13996     // Sort them by m_pBlock pointer value.
13997     VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
13998 }
13999 
~VmaDefragmentationAlgorithm_Generic()14000 VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
14001 {
14002     for(size_t i = m_Blocks.size(); i--; )
14003     {
14004         vma_delete(m_hAllocator, m_Blocks[i]);
14005     }
14006 }
14007 
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)14008 void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
14009 {
14010     // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
14011     if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
14012     {
14013         VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
14014         BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
14015         if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
14016         {
14017             AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
14018             (*it)->m_Allocations.push_back(allocInfo);
14019         }
14020         else
14021         {
14022             VMA_ASSERT(0);
14023         }
14024 
14025         ++m_AllocationCount;
14026     }
14027 }
14028 
DefragmentRound(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,bool freeOldAllocations)14029 VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
14030     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14031     VkDeviceSize maxBytesToMove,
14032     uint32_t maxAllocationsToMove,
14033     bool freeOldAllocations)
14034 {
14035     if(m_Blocks.empty())
14036     {
14037         return VK_SUCCESS;
14038     }
14039 
14040     // This is a choice based on research.
14041     // Option 1:
14042     uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
14043     // Option 2:
14044     //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
14045     // Option 3:
14046     //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
14047 
14048     size_t srcBlockMinIndex = 0;
14049     // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
14050     /*
14051     if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
14052     {
14053         const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
14054         if(blocksWithNonMovableCount > 0)
14055         {
14056             srcBlockMinIndex = blocksWithNonMovableCount - 1;
14057         }
14058     }
14059     */
14060 
14061     size_t srcBlockIndex = m_Blocks.size() - 1;
14062     size_t srcAllocIndex = SIZE_MAX;
14063     for(;;)
14064     {
14065         // 1. Find next allocation to move.
14066         // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
14067         // 1.2. Then start from last to first m_Allocations.
14068         while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
14069         {
14070             if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
14071             {
14072                 // Finished: no more allocations to process.
14073                 if(srcBlockIndex == srcBlockMinIndex)
14074                 {
14075                     return VK_SUCCESS;
14076                 }
14077                 else
14078                 {
14079                     --srcBlockIndex;
14080                     srcAllocIndex = SIZE_MAX;
14081                 }
14082             }
14083             else
14084             {
14085                 srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
14086             }
14087         }
14088 
14089         BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
14090         AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
14091 
14092         const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
14093         const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
14094         const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
14095         const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
14096 
14097         // 2. Try to find new place for this allocation in preceding or current block.
14098         for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
14099         {
14100             BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
14101             VmaAllocationRequest dstAllocRequest;
14102             if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
14103                 m_CurrentFrameIndex,
14104                 m_pBlockVector->GetFrameInUseCount(),
14105                 m_pBlockVector->GetBufferImageGranularity(),
14106                 size,
14107                 alignment,
14108                 false, // upperAddress
14109                 suballocType,
14110                 false, // canMakeOtherLost
14111                 strategy,
14112                 &dstAllocRequest) &&
14113             MoveMakesSense(
14114                 dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
14115             {
14116                 VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
14117 
14118                 // Reached limit on number of allocations or bytes to move.
14119                 if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
14120                     (m_BytesMoved + size > maxBytesToMove))
14121                 {
14122                     return VK_SUCCESS;
14123                 }
14124 
14125                 VmaDefragmentationMove move = {};
14126                 move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
14127                 move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
14128                 move.srcOffset = srcOffset;
14129                 move.dstOffset = dstAllocRequest.offset;
14130                 move.size = size;
14131                 move.hAllocation = allocInfo.m_hAllocation;
14132                 move.pSrcBlock = pSrcBlockInfo->m_pBlock;
14133                 move.pDstBlock = pDstBlockInfo->m_pBlock;
14134 
14135                 moves.push_back(move);
14136 
14137                 pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
14138                     dstAllocRequest,
14139                     suballocType,
14140                     size,
14141                     allocInfo.m_hAllocation);
14142 
14143                 if(freeOldAllocations)
14144                 {
14145                     pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
14146                     allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
14147                 }
14148 
14149                 if(allocInfo.m_pChanged != VMA_NULL)
14150                 {
14151                     *allocInfo.m_pChanged = VK_TRUE;
14152                 }
14153 
14154                 ++m_AllocationsMoved;
14155                 m_BytesMoved += size;
14156 
14157                 VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
14158 
14159                 break;
14160             }
14161         }
14162 
14163         // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
14164 
14165         if(srcAllocIndex > 0)
14166         {
14167             --srcAllocIndex;
14168         }
14169         else
14170         {
14171             if(srcBlockIndex > 0)
14172             {
14173                 --srcBlockIndex;
14174                 srcAllocIndex = SIZE_MAX;
14175             }
14176             else
14177             {
14178                 return VK_SUCCESS;
14179             }
14180         }
14181     }
14182 }
14183 
CalcBlocksWithNonMovableCount()14184 size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
14185 {
14186     size_t result = 0;
14187     for(size_t i = 0; i < m_Blocks.size(); ++i)
14188     {
14189         if(m_Blocks[i]->m_HasNonMovableAllocations)
14190         {
14191             ++result;
14192         }
14193     }
14194     return result;
14195 }
14196 
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,VmaDefragmentationFlags flags)14197 VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
14198     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14199     VkDeviceSize maxBytesToMove,
14200     uint32_t maxAllocationsToMove,
14201     VmaDefragmentationFlags flags)
14202 {
14203     if(!m_AllAllocations && m_AllocationCount == 0)
14204     {
14205         return VK_SUCCESS;
14206     }
14207 
14208     const size_t blockCount = m_Blocks.size();
14209     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14210     {
14211         BlockInfo* pBlockInfo = m_Blocks[blockIndex];
14212 
14213         if(m_AllAllocations)
14214         {
14215             VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
14216             for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
14217                 it != pMetadata->m_Suballocations.end();
14218                 ++it)
14219             {
14220                 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
14221                 {
14222                     AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
14223                     pBlockInfo->m_Allocations.push_back(allocInfo);
14224                 }
14225             }
14226         }
14227 
14228         pBlockInfo->CalcHasNonMovableAllocations();
14229 
14230         // This is a choice based on research.
14231         // Option 1:
14232         pBlockInfo->SortAllocationsByOffsetDescending();
14233         // Option 2:
14234         //pBlockInfo->SortAllocationsBySizeDescending();
14235     }
14236 
14237     // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
14238     VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
14239 
14240     // This is a choice based on research.
14241     const uint32_t roundCount = 2;
14242 
14243     // Execute defragmentation rounds (the main part).
14244     VkResult result = VK_SUCCESS;
14245     for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
14246     {
14247         result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove, !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL));
14248     }
14249 
14250     return result;
14251 }
14252 
MoveMakesSense(size_t dstBlockIndex,VkDeviceSize dstOffset,size_t srcBlockIndex,VkDeviceSize srcOffset)14253 bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
14254         size_t dstBlockIndex, VkDeviceSize dstOffset,
14255         size_t srcBlockIndex, VkDeviceSize srcOffset)
14256 {
14257     if(dstBlockIndex < srcBlockIndex)
14258     {
14259         return true;
14260     }
14261     if(dstBlockIndex > srcBlockIndex)
14262     {
14263         return false;
14264     }
14265     if(dstOffset < srcOffset)
14266     {
14267         return true;
14268     }
14269     return false;
14270 }
14271 
14272 ////////////////////////////////////////////////////////////////////////////////
14273 // VmaDefragmentationAlgorithm_Fast
14274 
VmaDefragmentationAlgorithm_Fast(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)14275 VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
14276     VmaAllocator hAllocator,
14277     VmaBlockVector* pBlockVector,
14278     uint32_t currentFrameIndex,
14279     bool overlappingMoveSupported) :
14280     VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
14281     m_OverlappingMoveSupported(overlappingMoveSupported),
14282     m_AllocationCount(0),
14283     m_AllAllocations(false),
14284     m_BytesMoved(0),
14285     m_AllocationsMoved(0),
14286     m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
14287 {
14288     VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
14289 
14290 }
14291 
~VmaDefragmentationAlgorithm_Fast()14292 VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
14293 {
14294 }
14295 
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,VmaDefragmentationFlags flags)14296 VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
14297     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14298     VkDeviceSize maxBytesToMove,
14299     uint32_t maxAllocationsToMove,
14300     VmaDefragmentationFlags flags)
14301 {
14302     VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
14303 
14304     const size_t blockCount = m_pBlockVector->GetBlockCount();
14305     if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
14306     {
14307         return VK_SUCCESS;
14308     }
14309 
14310     PreprocessMetadata();
14311 
14312     // Sort blocks in order from most destination.
14313 
14314     m_BlockInfos.resize(blockCount);
14315     for(size_t i = 0; i < blockCount; ++i)
14316     {
14317         m_BlockInfos[i].origBlockIndex = i;
14318     }
14319 
14320     VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
14321         return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
14322             m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
14323     });
14324 
14325     // THE MAIN ALGORITHM
14326 
14327     FreeSpaceDatabase freeSpaceDb;
14328 
14329     size_t dstBlockInfoIndex = 0;
14330     size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14331     VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14332     VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14333     VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
14334     VkDeviceSize dstOffset = 0;
14335 
14336     bool end = false;
14337     for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
14338     {
14339         const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
14340         VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
14341         VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
14342         for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
14343             !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
14344         {
14345             VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
14346             const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
14347             const VkDeviceSize srcAllocSize = srcSuballocIt->size;
14348             if(m_AllocationsMoved == maxAllocationsToMove ||
14349                 m_BytesMoved + srcAllocSize > maxBytesToMove)
14350             {
14351                 end = true;
14352                 break;
14353             }
14354             const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
14355 
14356             VmaDefragmentationMove move = {};
14357             // Try to place it in one of free spaces from the database.
14358             size_t freeSpaceInfoIndex;
14359             VkDeviceSize dstAllocOffset;
14360             if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
14361                 freeSpaceInfoIndex, dstAllocOffset))
14362             {
14363                 size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
14364                 VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
14365                 VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
14366 
14367                 // Same block
14368                 if(freeSpaceInfoIndex == srcBlockInfoIndex)
14369                 {
14370                     VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14371 
14372                     // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14373 
14374                     VmaSuballocation suballoc = *srcSuballocIt;
14375                     suballoc.offset = dstAllocOffset;
14376                     suballoc.hAllocation->ChangeOffset(dstAllocOffset);
14377                     m_BytesMoved += srcAllocSize;
14378                     ++m_AllocationsMoved;
14379 
14380                     VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14381                     ++nextSuballocIt;
14382                     pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14383                     srcSuballocIt = nextSuballocIt;
14384 
14385                     InsertSuballoc(pFreeSpaceMetadata, suballoc);
14386 
14387                     move.srcBlockIndex = srcOrigBlockIndex;
14388                     move.dstBlockIndex = freeSpaceOrigBlockIndex;
14389                     move.srcOffset = srcAllocOffset;
14390                     move.dstOffset = dstAllocOffset;
14391                     move.size = srcAllocSize;
14392 
14393                     moves.push_back(move);
14394                 }
14395                 // Different block
14396                 else
14397                 {
14398                     // MOVE OPTION 2: Move the allocation to a different block.
14399 
14400                     VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
14401 
14402                     VmaSuballocation suballoc = *srcSuballocIt;
14403                     suballoc.offset = dstAllocOffset;
14404                     suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
14405                     m_BytesMoved += srcAllocSize;
14406                     ++m_AllocationsMoved;
14407 
14408                     VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14409                     ++nextSuballocIt;
14410                     pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14411                     srcSuballocIt = nextSuballocIt;
14412 
14413                     InsertSuballoc(pFreeSpaceMetadata, suballoc);
14414 
14415                     move.srcBlockIndex = srcOrigBlockIndex;
14416                     move.dstBlockIndex = freeSpaceOrigBlockIndex;
14417                     move.srcOffset = srcAllocOffset;
14418                     move.dstOffset = dstAllocOffset;
14419                     move.size = srcAllocSize;
14420 
14421                     moves.push_back(move);
14422                 }
14423             }
14424             else
14425             {
14426                 dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
14427 
14428                 // If the allocation doesn't fit before the end of dstBlock, forward to next block.
14429                 while(dstBlockInfoIndex < srcBlockInfoIndex &&
14430                     dstAllocOffset + srcAllocSize > dstBlockSize)
14431                 {
14432                     // But before that, register remaining free space at the end of dst block.
14433                     freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
14434 
14435                     ++dstBlockInfoIndex;
14436                     dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14437                     pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14438                     pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14439                     dstBlockSize = pDstMetadata->GetSize();
14440                     dstOffset = 0;
14441                     dstAllocOffset = 0;
14442                 }
14443 
14444                 // Same block
14445                 if(dstBlockInfoIndex == srcBlockInfoIndex)
14446                 {
14447                     VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14448 
14449                     const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
14450 
14451                     bool skipOver = overlap;
14452                     if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
14453                     {
14454                         // If destination and source place overlap, skip if it would move it
14455                         // by only < 1/64 of its size.
14456                         skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
14457                     }
14458 
14459                     if(skipOver)
14460                     {
14461                         freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
14462 
14463                         dstOffset = srcAllocOffset + srcAllocSize;
14464                         ++srcSuballocIt;
14465                     }
14466                     // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14467                     else
14468                     {
14469                         srcSuballocIt->offset = dstAllocOffset;
14470                         srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);
14471                         dstOffset = dstAllocOffset + srcAllocSize;
14472                         m_BytesMoved += srcAllocSize;
14473                         ++m_AllocationsMoved;
14474                         ++srcSuballocIt;
14475 
14476                         move.srcBlockIndex = srcOrigBlockIndex;
14477                         move.dstBlockIndex = dstOrigBlockIndex;
14478                         move.srcOffset = srcAllocOffset;
14479                         move.dstOffset = dstAllocOffset;
14480                         move.size = srcAllocSize;
14481 
14482                         moves.push_back(move);
14483                     }
14484                 }
14485                 // Different block
14486                 else
14487                 {
14488                     // MOVE OPTION 2: Move the allocation to a different block.
14489 
14490                     VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
14491                     VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
14492 
14493                     VmaSuballocation suballoc = *srcSuballocIt;
14494                     suballoc.offset = dstAllocOffset;
14495                     suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
14496                     dstOffset = dstAllocOffset + srcAllocSize;
14497                     m_BytesMoved += srcAllocSize;
14498                     ++m_AllocationsMoved;
14499 
14500                     VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14501                     ++nextSuballocIt;
14502                     pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14503                     srcSuballocIt = nextSuballocIt;
14504 
14505                     pDstMetadata->m_Suballocations.push_back(suballoc);
14506 
14507                     move.srcBlockIndex = srcOrigBlockIndex;
14508                     move.dstBlockIndex = dstOrigBlockIndex;
14509                     move.srcOffset = srcAllocOffset;
14510                     move.dstOffset = dstAllocOffset;
14511                     move.size = srcAllocSize;
14512 
14513                     moves.push_back(move);
14514                 }
14515             }
14516         }
14517     }
14518 
14519     m_BlockInfos.clear();
14520 
14521     PostprocessMetadata();
14522 
14523     return VK_SUCCESS;
14524 }
14525 
PreprocessMetadata()14526 void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
14527 {
14528     const size_t blockCount = m_pBlockVector->GetBlockCount();
14529     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14530     {
14531         VmaBlockMetadata_Generic* const pMetadata =
14532             (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14533         pMetadata->m_FreeCount = 0;
14534         pMetadata->m_SumFreeSize = pMetadata->GetSize();
14535         pMetadata->m_FreeSuballocationsBySize.clear();
14536         for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14537             it != pMetadata->m_Suballocations.end(); )
14538         {
14539             if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
14540             {
14541                 VmaSuballocationList::iterator nextIt = it;
14542                 ++nextIt;
14543                 pMetadata->m_Suballocations.erase(it);
14544                 it = nextIt;
14545             }
14546             else
14547             {
14548                 ++it;
14549             }
14550         }
14551     }
14552 }
14553 
PostprocessMetadata()14554 void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
14555 {
14556     const size_t blockCount = m_pBlockVector->GetBlockCount();
14557     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14558     {
14559         VmaBlockMetadata_Generic* const pMetadata =
14560             (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14561         const VkDeviceSize blockSize = pMetadata->GetSize();
14562 
14563         // No allocations in this block - entire area is free.
14564         if(pMetadata->m_Suballocations.empty())
14565         {
14566             pMetadata->m_FreeCount = 1;
14567             //pMetadata->m_SumFreeSize is already set to blockSize.
14568             VmaSuballocation suballoc = {
14569                 0, // offset
14570                 blockSize, // size
14571                 VMA_NULL, // hAllocation
14572                 VMA_SUBALLOCATION_TYPE_FREE };
14573             pMetadata->m_Suballocations.push_back(suballoc);
14574             pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
14575         }
14576         // There are some allocations in this block.
14577         else
14578         {
14579             VkDeviceSize offset = 0;
14580             VmaSuballocationList::iterator it;
14581             for(it = pMetadata->m_Suballocations.begin();
14582                 it != pMetadata->m_Suballocations.end();
14583                 ++it)
14584             {
14585                 VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
14586                 VMA_ASSERT(it->offset >= offset);
14587 
14588                 // Need to insert preceding free space.
14589                 if(it->offset > offset)
14590                 {
14591                     ++pMetadata->m_FreeCount;
14592                     const VkDeviceSize freeSize = it->offset - offset;
14593                     VmaSuballocation suballoc = {
14594                         offset, // offset
14595                         freeSize, // size
14596                         VMA_NULL, // hAllocation
14597                         VMA_SUBALLOCATION_TYPE_FREE };
14598                     VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14599                     if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14600                     {
14601                         pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
14602                     }
14603                 }
14604 
14605                 pMetadata->m_SumFreeSize -= it->size;
14606                 offset = it->offset + it->size;
14607             }
14608 
14609             // Need to insert trailing free space.
14610             if(offset < blockSize)
14611             {
14612                 ++pMetadata->m_FreeCount;
14613                 const VkDeviceSize freeSize = blockSize - offset;
14614                 VmaSuballocation suballoc = {
14615                     offset, // offset
14616                     freeSize, // size
14617                     VMA_NULL, // hAllocation
14618                     VMA_SUBALLOCATION_TYPE_FREE };
14619                 VMA_ASSERT(it == pMetadata->m_Suballocations.end());
14620                 VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14621                 if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14622                 {
14623                     pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
14624                 }
14625             }
14626 
14627             VMA_SORT(
14628                 pMetadata->m_FreeSuballocationsBySize.begin(),
14629                 pMetadata->m_FreeSuballocationsBySize.end(),
14630                 VmaSuballocationItemSizeLess());
14631         }
14632 
14633         VMA_HEAVY_ASSERT(pMetadata->Validate());
14634     }
14635 }
14636 
InsertSuballoc(VmaBlockMetadata_Generic * pMetadata,const VmaSuballocation & suballoc)14637 void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
14638 {
14639     // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
14640     VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14641     while(it != pMetadata->m_Suballocations.end())
14642     {
14643         if(it->offset < suballoc.offset)
14644         {
14645             ++it;
14646         }
14647     }
14648     pMetadata->m_Suballocations.insert(it, suballoc);
14649 }
14650 
14651 ////////////////////////////////////////////////////////////////////////////////
14652 // VmaBlockVectorDefragmentationContext
14653 
VmaBlockVectorDefragmentationContext(VmaAllocator hAllocator,VmaPool hCustomPool,VmaBlockVector * pBlockVector,uint32_t currFrameIndex)14654 VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
14655     VmaAllocator hAllocator,
14656     VmaPool hCustomPool,
14657     VmaBlockVector* pBlockVector,
14658     uint32_t currFrameIndex) :
14659     res(VK_SUCCESS),
14660     mutexLocked(false),
14661     blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
14662     defragmentationMoves(VmaStlAllocator<VmaDefragmentationMove>(hAllocator->GetAllocationCallbacks())),
14663     defragmentationMovesProcessed(0),
14664     defragmentationMovesCommitted(0),
14665     hasDefragmentationPlan(0),
14666     m_hAllocator(hAllocator),
14667     m_hCustomPool(hCustomPool),
14668     m_pBlockVector(pBlockVector),
14669     m_CurrFrameIndex(currFrameIndex),
14670     m_pAlgorithm(VMA_NULL),
14671     m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
14672     m_AllAllocations(false)
14673 {
14674 }
14675 
~VmaBlockVectorDefragmentationContext()14676 VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
14677 {
14678     vma_delete(m_hAllocator, m_pAlgorithm);
14679 }
14680 
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)14681 void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
14682 {
14683     AllocInfo info = { hAlloc, pChanged };
14684     m_Allocations.push_back(info);
14685 }
14686 
Begin(bool overlappingMoveSupported,VmaDefragmentationFlags flags)14687 void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags)
14688 {
14689     const bool allAllocations = m_AllAllocations ||
14690         m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
14691 
14692     /********************************
14693     HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
14694     ********************************/
14695 
14696     /*
14697     Fast algorithm is supported only when certain criteria are met:
14698     - VMA_DEBUG_MARGIN is 0.
14699     - All allocations in this block vector are moveable.
14700     - There is no possibility of image/buffer granularity conflict.
14701     - The defragmentation is not incremental
14702     */
14703     if(VMA_DEBUG_MARGIN == 0 &&
14704         allAllocations &&
14705         !m_pBlockVector->IsBufferImageGranularityConflictPossible() &&
14706         !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL))
14707     {
14708         m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
14709             m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14710     }
14711     else
14712     {
14713         m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
14714             m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14715     }
14716 
14717     if(allAllocations)
14718     {
14719         m_pAlgorithm->AddAll();
14720     }
14721     else
14722     {
14723         for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
14724         {
14725             m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
14726         }
14727     }
14728 }
14729 
14730 ////////////////////////////////////////////////////////////////////////////////
14731 // VmaDefragmentationContext
14732 
VmaDefragmentationContext_T(VmaAllocator hAllocator,uint32_t currFrameIndex,uint32_t flags,VmaDefragmentationStats * pStats)14733 VmaDefragmentationContext_T::VmaDefragmentationContext_T(
14734     VmaAllocator hAllocator,
14735     uint32_t currFrameIndex,
14736     uint32_t flags,
14737     VmaDefragmentationStats* pStats) :
14738     m_hAllocator(hAllocator),
14739     m_CurrFrameIndex(currFrameIndex),
14740     m_Flags(flags),
14741     m_pStats(pStats),
14742     m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
14743 {
14744     memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
14745 }
14746 
~VmaDefragmentationContext_T()14747 VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
14748 {
14749     for(size_t i = m_CustomPoolContexts.size(); i--; )
14750     {
14751         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
14752         pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
14753         vma_delete(m_hAllocator, pBlockVectorCtx);
14754     }
14755     for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
14756     {
14757         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
14758         if(pBlockVectorCtx)
14759         {
14760             pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
14761             vma_delete(m_hAllocator, pBlockVectorCtx);
14762         }
14763     }
14764 }
14765 
AddPools(uint32_t poolCount,const VmaPool * pPools)14766 void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, const VmaPool* pPools)
14767 {
14768     for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
14769     {
14770         VmaPool pool = pPools[poolIndex];
14771         VMA_ASSERT(pool);
14772         // Pools with algorithm other than default are not defragmented.
14773         if(pool->m_BlockVector.GetAlgorithm() == 0)
14774         {
14775             VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
14776 
14777             for(size_t i = m_CustomPoolContexts.size(); i--; )
14778             {
14779                 if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
14780                 {
14781                     pBlockVectorDefragCtx = m_CustomPoolContexts[i];
14782                     break;
14783                 }
14784             }
14785 
14786             if(!pBlockVectorDefragCtx)
14787             {
14788                 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14789                     m_hAllocator,
14790                     pool,
14791                     &pool->m_BlockVector,
14792                     m_CurrFrameIndex);
14793                 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
14794             }
14795 
14796             pBlockVectorDefragCtx->AddAll();
14797         }
14798     }
14799 }
14800 
AddAllocations(uint32_t allocationCount,const VmaAllocation * pAllocations,VkBool32 * pAllocationsChanged)14801 void VmaDefragmentationContext_T::AddAllocations(
14802     uint32_t allocationCount,
14803     const VmaAllocation* pAllocations,
14804     VkBool32* pAllocationsChanged)
14805 {
14806     // Dispatch pAllocations among defragmentators. Create them when necessary.
14807     for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14808     {
14809         const VmaAllocation hAlloc = pAllocations[allocIndex];
14810         VMA_ASSERT(hAlloc);
14811         // DedicatedAlloc cannot be defragmented.
14812         if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
14813             // Lost allocation cannot be defragmented.
14814             (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
14815         {
14816             VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
14817 
14818             const VmaPool hAllocPool = hAlloc->GetBlock()->GetParentPool();
14819             // This allocation belongs to custom pool.
14820             if(hAllocPool != VK_NULL_HANDLE)
14821             {
14822                 // Pools with algorithm other than default are not defragmented.
14823                 if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
14824                 {
14825                     for(size_t i = m_CustomPoolContexts.size(); i--; )
14826                     {
14827                         if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
14828                         {
14829                             pBlockVectorDefragCtx = m_CustomPoolContexts[i];
14830                             break;
14831                         }
14832                     }
14833                     if(!pBlockVectorDefragCtx)
14834                     {
14835                         pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14836                             m_hAllocator,
14837                             hAllocPool,
14838                             &hAllocPool->m_BlockVector,
14839                             m_CurrFrameIndex);
14840                         m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
14841                     }
14842                 }
14843             }
14844             // This allocation belongs to default pool.
14845             else
14846             {
14847                 const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
14848                 pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
14849                 if(!pBlockVectorDefragCtx)
14850                 {
14851                     pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14852                         m_hAllocator,
14853                         VMA_NULL, // hCustomPool
14854                         m_hAllocator->m_pBlockVectors[memTypeIndex],
14855                         m_CurrFrameIndex);
14856                     m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
14857                 }
14858             }
14859 
14860             if(pBlockVectorDefragCtx)
14861             {
14862                 VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
14863                     &pAllocationsChanged[allocIndex] : VMA_NULL;
14864                 pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
14865             }
14866         }
14867     }
14868 }
14869 
Defragment(VkDeviceSize maxCpuBytesToMove,uint32_t maxCpuAllocationsToMove,VkDeviceSize maxGpuBytesToMove,uint32_t maxGpuAllocationsToMove,VkCommandBuffer commandBuffer,VmaDefragmentationStats * pStats,VmaDefragmentationFlags flags)14870 VkResult VmaDefragmentationContext_T::Defragment(
14871     VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
14872     VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
14873     VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags)
14874 {
14875     if(pStats)
14876     {
14877         memset(pStats, 0, sizeof(VmaDefragmentationStats));
14878     }
14879 
14880     if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
14881     {
14882         // For incremental defragmetnations, we just earmark how much we can move
14883         // The real meat is in the defragmentation steps
14884         m_MaxCpuBytesToMove = maxCpuBytesToMove;
14885         m_MaxCpuAllocationsToMove = maxCpuAllocationsToMove;
14886 
14887         m_MaxGpuBytesToMove = maxGpuBytesToMove;
14888         m_MaxGpuAllocationsToMove = maxGpuAllocationsToMove;
14889 
14890         if(m_MaxCpuBytesToMove == 0 && m_MaxCpuAllocationsToMove == 0 &&
14891             m_MaxGpuBytesToMove == 0 && m_MaxGpuAllocationsToMove == 0)
14892             return VK_SUCCESS;
14893 
14894         return VK_NOT_READY;
14895     }
14896 
14897     if(commandBuffer == VK_NULL_HANDLE)
14898     {
14899         maxGpuBytesToMove = 0;
14900         maxGpuAllocationsToMove = 0;
14901     }
14902 
14903     VkResult res = VK_SUCCESS;
14904 
14905     // Process default pools.
14906     for(uint32_t memTypeIndex = 0;
14907         memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
14908         ++memTypeIndex)
14909     {
14910         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14911         if(pBlockVectorCtx)
14912         {
14913             VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14914             pBlockVectorCtx->GetBlockVector()->Defragment(
14915                 pBlockVectorCtx,
14916                 pStats, flags,
14917                 maxCpuBytesToMove, maxCpuAllocationsToMove,
14918                 maxGpuBytesToMove, maxGpuAllocationsToMove,
14919                 commandBuffer);
14920             if(pBlockVectorCtx->res != VK_SUCCESS)
14921             {
14922                 res = pBlockVectorCtx->res;
14923             }
14924         }
14925     }
14926 
14927     // Process custom pools.
14928     for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14929         customCtxIndex < customCtxCount && res >= VK_SUCCESS;
14930         ++customCtxIndex)
14931     {
14932         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14933         VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14934         pBlockVectorCtx->GetBlockVector()->Defragment(
14935             pBlockVectorCtx,
14936             pStats, flags,
14937             maxCpuBytesToMove, maxCpuAllocationsToMove,
14938             maxGpuBytesToMove, maxGpuAllocationsToMove,
14939             commandBuffer);
14940         if(pBlockVectorCtx->res != VK_SUCCESS)
14941         {
14942             res = pBlockVectorCtx->res;
14943         }
14944     }
14945 
14946     return res;
14947 }
14948 
DefragmentPassBegin(VmaDefragmentationPassInfo * pInfo)14949 VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo)
14950 {
14951     VmaDefragmentationPassMoveInfo* pCurrentMove = pInfo->pMoves;
14952     uint32_t movesLeft = pInfo->moveCount;
14953 
14954     // Process default pools.
14955     for(uint32_t memTypeIndex = 0;
14956         memTypeIndex < m_hAllocator->GetMemoryTypeCount();
14957         ++memTypeIndex)
14958     {
14959         VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14960         if(pBlockVectorCtx)
14961         {
14962             VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14963 
14964             if(!pBlockVectorCtx->hasDefragmentationPlan)
14965             {
14966                 pBlockVectorCtx->GetBlockVector()->Defragment(
14967                     pBlockVectorCtx,
14968                     m_pStats, m_Flags,
14969                     m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
14970                     m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
14971                     VK_NULL_HANDLE);
14972 
14973                 if(pBlockVectorCtx->res < VK_SUCCESS)
14974                     continue;
14975 
14976                 pBlockVectorCtx->hasDefragmentationPlan = true;
14977             }
14978 
14979             const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
14980                 pBlockVectorCtx,
14981                 pCurrentMove, movesLeft);
14982 
14983             movesLeft -= processed;
14984             pCurrentMove += processed;
14985         }
14986     }
14987 
14988     // Process custom pools.
14989     for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14990         customCtxIndex < customCtxCount;
14991         ++customCtxIndex)
14992     {
14993         VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14994         VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14995 
14996         if(!pBlockVectorCtx->hasDefragmentationPlan)
14997         {
14998             pBlockVectorCtx->GetBlockVector()->Defragment(
14999                 pBlockVectorCtx,
15000                 m_pStats, m_Flags,
15001                 m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
15002                 m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
15003                 VK_NULL_HANDLE);
15004 
15005             if(pBlockVectorCtx->res < VK_SUCCESS)
15006                 continue;
15007 
15008             pBlockVectorCtx->hasDefragmentationPlan = true;
15009         }
15010 
15011         const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
15012             pBlockVectorCtx,
15013             pCurrentMove, movesLeft);
15014 
15015         movesLeft -= processed;
15016         pCurrentMove += processed;
15017     }
15018 
15019     pInfo->moveCount = pInfo->moveCount - movesLeft;
15020 
15021     return VK_SUCCESS;
15022 }
DefragmentPassEnd()15023 VkResult VmaDefragmentationContext_T::DefragmentPassEnd()
15024 {
15025     VkResult res = VK_SUCCESS;
15026 
15027     // Process default pools.
15028     for(uint32_t memTypeIndex = 0;
15029         memTypeIndex < m_hAllocator->GetMemoryTypeCount();
15030         ++memTypeIndex)
15031     {
15032         VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
15033         if(pBlockVectorCtx)
15034         {
15035             VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
15036 
15037             if(!pBlockVectorCtx->hasDefragmentationPlan)
15038             {
15039                 res = VK_NOT_READY;
15040                 continue;
15041             }
15042 
15043             pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
15044                 pBlockVectorCtx, m_pStats);
15045 
15046             if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
15047                 res = VK_NOT_READY;
15048         }
15049     }
15050 
15051     // Process custom pools.
15052     for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
15053         customCtxIndex < customCtxCount;
15054         ++customCtxIndex)
15055     {
15056         VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
15057         VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
15058 
15059         if(!pBlockVectorCtx->hasDefragmentationPlan)
15060         {
15061             res = VK_NOT_READY;
15062             continue;
15063         }
15064 
15065         pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
15066             pBlockVectorCtx, m_pStats);
15067 
15068         if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
15069             res = VK_NOT_READY;
15070     }
15071 
15072     return res;
15073 }
15074 
15075 ////////////////////////////////////////////////////////////////////////////////
15076 // VmaRecorder
15077 
15078 #if VMA_RECORDING_ENABLED
15079 
VmaRecorder()15080 VmaRecorder::VmaRecorder() :
15081     m_UseMutex(true),
15082     m_Flags(0),
15083     m_File(VMA_NULL),
15084     m_RecordingStartTime(std::chrono::high_resolution_clock::now())
15085 {
15086 }
15087 
Init(const VmaRecordSettings & settings,bool useMutex)15088 VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
15089 {
15090     m_UseMutex = useMutex;
15091     m_Flags = settings.flags;
15092 
15093 #if defined(_WIN32)
15094     // Open file for writing.
15095     errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
15096 
15097     if(err != 0)
15098     {
15099         return VK_ERROR_INITIALIZATION_FAILED;
15100     }
15101 #else
15102     // Open file for writing.
15103     m_File = fopen(settings.pFilePath, "wb");
15104 
15105     if(m_File == 0)
15106     {
15107         return VK_ERROR_INITIALIZATION_FAILED;
15108     }
15109 #endif
15110 
15111     // Write header.
15112     fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
15113     fprintf(m_File, "%s\n", "1,8");
15114 
15115     return VK_SUCCESS;
15116 }
15117 
~VmaRecorder()15118 VmaRecorder::~VmaRecorder()
15119 {
15120     if(m_File != VMA_NULL)
15121     {
15122         fclose(m_File);
15123     }
15124 }
15125 
RecordCreateAllocator(uint32_t frameIndex)15126 void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
15127 {
15128     CallParams callParams;
15129     GetBasicParams(callParams);
15130 
15131     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15132     fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
15133     Flush();
15134 }
15135 
RecordDestroyAllocator(uint32_t frameIndex)15136 void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
15137 {
15138     CallParams callParams;
15139     GetBasicParams(callParams);
15140 
15141     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15142     fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
15143     Flush();
15144 }
15145 
RecordCreatePool(uint32_t frameIndex,const VmaPoolCreateInfo & createInfo,VmaPool pool)15146 void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
15147 {
15148     CallParams callParams;
15149     GetBasicParams(callParams);
15150 
15151     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15152     fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
15153         createInfo.memoryTypeIndex,
15154         createInfo.flags,
15155         createInfo.blockSize,
15156         (uint64_t)createInfo.minBlockCount,
15157         (uint64_t)createInfo.maxBlockCount,
15158         createInfo.frameInUseCount,
15159         pool);
15160     Flush();
15161 }
15162 
RecordDestroyPool(uint32_t frameIndex,VmaPool pool)15163 void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
15164 {
15165     CallParams callParams;
15166     GetBasicParams(callParams);
15167 
15168     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15169     fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
15170         pool);
15171     Flush();
15172 }
15173 
RecordAllocateMemory(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)15174 void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
15175         const VkMemoryRequirements& vkMemReq,
15176         const VmaAllocationCreateInfo& createInfo,
15177         VmaAllocation allocation)
15178 {
15179     CallParams callParams;
15180     GetBasicParams(callParams);
15181 
15182     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15183     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15184     fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15185         vkMemReq.size,
15186         vkMemReq.alignment,
15187         vkMemReq.memoryTypeBits,
15188         createInfo.flags,
15189         createInfo.usage,
15190         createInfo.requiredFlags,
15191         createInfo.preferredFlags,
15192         createInfo.memoryTypeBits,
15193         createInfo.pool,
15194         allocation,
15195         userDataStr.GetString());
15196     Flush();
15197 }
15198 
RecordAllocateMemoryPages(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,uint64_t allocationCount,const VmaAllocation * pAllocations)15199 void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
15200     const VkMemoryRequirements& vkMemReq,
15201     const VmaAllocationCreateInfo& createInfo,
15202     uint64_t allocationCount,
15203     const VmaAllocation* pAllocations)
15204 {
15205     CallParams callParams;
15206     GetBasicParams(callParams);
15207 
15208     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15209     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15210     fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
15211         vkMemReq.size,
15212         vkMemReq.alignment,
15213         vkMemReq.memoryTypeBits,
15214         createInfo.flags,
15215         createInfo.usage,
15216         createInfo.requiredFlags,
15217         createInfo.preferredFlags,
15218         createInfo.memoryTypeBits,
15219         createInfo.pool);
15220     PrintPointerList(allocationCount, pAllocations);
15221     fprintf(m_File, ",%s\n", userDataStr.GetString());
15222     Flush();
15223 }
15224 
RecordAllocateMemoryForBuffer(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)15225 void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
15226     const VkMemoryRequirements& vkMemReq,
15227     bool requiresDedicatedAllocation,
15228     bool prefersDedicatedAllocation,
15229     const VmaAllocationCreateInfo& createInfo,
15230     VmaAllocation allocation)
15231 {
15232     CallParams callParams;
15233     GetBasicParams(callParams);
15234 
15235     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15236     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15237     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,
15238         vkMemReq.size,
15239         vkMemReq.alignment,
15240         vkMemReq.memoryTypeBits,
15241         requiresDedicatedAllocation ? 1 : 0,
15242         prefersDedicatedAllocation ? 1 : 0,
15243         createInfo.flags,
15244         createInfo.usage,
15245         createInfo.requiredFlags,
15246         createInfo.preferredFlags,
15247         createInfo.memoryTypeBits,
15248         createInfo.pool,
15249         allocation,
15250         userDataStr.GetString());
15251     Flush();
15252 }
15253 
RecordAllocateMemoryForImage(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)15254 void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
15255     const VkMemoryRequirements& vkMemReq,
15256     bool requiresDedicatedAllocation,
15257     bool prefersDedicatedAllocation,
15258     const VmaAllocationCreateInfo& createInfo,
15259     VmaAllocation allocation)
15260 {
15261     CallParams callParams;
15262     GetBasicParams(callParams);
15263 
15264     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15265     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15266     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,
15267         vkMemReq.size,
15268         vkMemReq.alignment,
15269         vkMemReq.memoryTypeBits,
15270         requiresDedicatedAllocation ? 1 : 0,
15271         prefersDedicatedAllocation ? 1 : 0,
15272         createInfo.flags,
15273         createInfo.usage,
15274         createInfo.requiredFlags,
15275         createInfo.preferredFlags,
15276         createInfo.memoryTypeBits,
15277         createInfo.pool,
15278         allocation,
15279         userDataStr.GetString());
15280     Flush();
15281 }
15282 
RecordFreeMemory(uint32_t frameIndex,VmaAllocation allocation)15283 void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
15284     VmaAllocation allocation)
15285 {
15286     CallParams callParams;
15287     GetBasicParams(callParams);
15288 
15289     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15290     fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15291         allocation);
15292     Flush();
15293 }
15294 
RecordFreeMemoryPages(uint32_t frameIndex,uint64_t allocationCount,const VmaAllocation * pAllocations)15295 void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
15296     uint64_t allocationCount,
15297     const VmaAllocation* pAllocations)
15298 {
15299     CallParams callParams;
15300     GetBasicParams(callParams);
15301 
15302     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15303     fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
15304     PrintPointerList(allocationCount, pAllocations);
15305     fprintf(m_File, "\n");
15306     Flush();
15307 }
15308 
RecordSetAllocationUserData(uint32_t frameIndex,VmaAllocation allocation,const void * pUserData)15309 void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
15310     VmaAllocation allocation,
15311     const void* pUserData)
15312 {
15313     CallParams callParams;
15314     GetBasicParams(callParams);
15315 
15316     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15317     UserDataString userDataStr(
15318         allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
15319         pUserData);
15320     fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15321         allocation,
15322         userDataStr.GetString());
15323     Flush();
15324 }
15325 
RecordCreateLostAllocation(uint32_t frameIndex,VmaAllocation allocation)15326 void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
15327     VmaAllocation allocation)
15328 {
15329     CallParams callParams;
15330     GetBasicParams(callParams);
15331 
15332     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15333     fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15334         allocation);
15335     Flush();
15336 }
15337 
RecordMapMemory(uint32_t frameIndex,VmaAllocation allocation)15338 void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
15339     VmaAllocation allocation)
15340 {
15341     CallParams callParams;
15342     GetBasicParams(callParams);
15343 
15344     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15345     fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15346         allocation);
15347     Flush();
15348 }
15349 
RecordUnmapMemory(uint32_t frameIndex,VmaAllocation allocation)15350 void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
15351     VmaAllocation allocation)
15352 {
15353     CallParams callParams;
15354     GetBasicParams(callParams);
15355 
15356     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15357     fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15358         allocation);
15359     Flush();
15360 }
15361 
RecordFlushAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)15362 void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
15363     VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15364 {
15365     CallParams callParams;
15366     GetBasicParams(callParams);
15367 
15368     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15369     fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15370         allocation,
15371         offset,
15372         size);
15373     Flush();
15374 }
15375 
RecordInvalidateAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)15376 void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
15377     VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15378 {
15379     CallParams callParams;
15380     GetBasicParams(callParams);
15381 
15382     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15383     fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15384         allocation,
15385         offset,
15386         size);
15387     Flush();
15388 }
15389 
RecordCreateBuffer(uint32_t frameIndex,const VkBufferCreateInfo & bufCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)15390 void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
15391     const VkBufferCreateInfo& bufCreateInfo,
15392     const VmaAllocationCreateInfo& allocCreateInfo,
15393     VmaAllocation allocation)
15394 {
15395     CallParams callParams;
15396     GetBasicParams(callParams);
15397 
15398     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15399     UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15400     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,
15401         bufCreateInfo.flags,
15402         bufCreateInfo.size,
15403         bufCreateInfo.usage,
15404         bufCreateInfo.sharingMode,
15405         allocCreateInfo.flags,
15406         allocCreateInfo.usage,
15407         allocCreateInfo.requiredFlags,
15408         allocCreateInfo.preferredFlags,
15409         allocCreateInfo.memoryTypeBits,
15410         allocCreateInfo.pool,
15411         allocation,
15412         userDataStr.GetString());
15413     Flush();
15414 }
15415 
RecordCreateImage(uint32_t frameIndex,const VkImageCreateInfo & imageCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)15416 void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
15417     const VkImageCreateInfo& imageCreateInfo,
15418     const VmaAllocationCreateInfo& allocCreateInfo,
15419     VmaAllocation allocation)
15420 {
15421     CallParams callParams;
15422     GetBasicParams(callParams);
15423 
15424     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15425     UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15426     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,
15427         imageCreateInfo.flags,
15428         imageCreateInfo.imageType,
15429         imageCreateInfo.format,
15430         imageCreateInfo.extent.width,
15431         imageCreateInfo.extent.height,
15432         imageCreateInfo.extent.depth,
15433         imageCreateInfo.mipLevels,
15434         imageCreateInfo.arrayLayers,
15435         imageCreateInfo.samples,
15436         imageCreateInfo.tiling,
15437         imageCreateInfo.usage,
15438         imageCreateInfo.sharingMode,
15439         imageCreateInfo.initialLayout,
15440         allocCreateInfo.flags,
15441         allocCreateInfo.usage,
15442         allocCreateInfo.requiredFlags,
15443         allocCreateInfo.preferredFlags,
15444         allocCreateInfo.memoryTypeBits,
15445         allocCreateInfo.pool,
15446         allocation,
15447         userDataStr.GetString());
15448     Flush();
15449 }
15450 
RecordDestroyBuffer(uint32_t frameIndex,VmaAllocation allocation)15451 void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
15452     VmaAllocation allocation)
15453 {
15454     CallParams callParams;
15455     GetBasicParams(callParams);
15456 
15457     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15458     fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
15459         allocation);
15460     Flush();
15461 }
15462 
RecordDestroyImage(uint32_t frameIndex,VmaAllocation allocation)15463 void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
15464     VmaAllocation allocation)
15465 {
15466     CallParams callParams;
15467     GetBasicParams(callParams);
15468 
15469     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15470     fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
15471         allocation);
15472     Flush();
15473 }
15474 
RecordTouchAllocation(uint32_t frameIndex,VmaAllocation allocation)15475 void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
15476     VmaAllocation allocation)
15477 {
15478     CallParams callParams;
15479     GetBasicParams(callParams);
15480 
15481     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15482     fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15483         allocation);
15484     Flush();
15485 }
15486 
RecordGetAllocationInfo(uint32_t frameIndex,VmaAllocation allocation)15487 void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
15488     VmaAllocation allocation)
15489 {
15490     CallParams callParams;
15491     GetBasicParams(callParams);
15492 
15493     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15494     fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
15495         allocation);
15496     Flush();
15497 }
15498 
RecordMakePoolAllocationsLost(uint32_t frameIndex,VmaPool pool)15499 void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
15500     VmaPool pool)
15501 {
15502     CallParams callParams;
15503     GetBasicParams(callParams);
15504 
15505     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15506     fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
15507         pool);
15508     Flush();
15509 }
15510 
RecordDefragmentationBegin(uint32_t frameIndex,const VmaDefragmentationInfo2 & info,VmaDefragmentationContext ctx)15511 void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
15512     const VmaDefragmentationInfo2& info,
15513     VmaDefragmentationContext ctx)
15514 {
15515     CallParams callParams;
15516     GetBasicParams(callParams);
15517 
15518     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15519     fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
15520         info.flags);
15521     PrintPointerList(info.allocationCount, info.pAllocations);
15522     fprintf(m_File, ",");
15523     PrintPointerList(info.poolCount, info.pPools);
15524     fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
15525         info.maxCpuBytesToMove,
15526         info.maxCpuAllocationsToMove,
15527         info.maxGpuBytesToMove,
15528         info.maxGpuAllocationsToMove,
15529         info.commandBuffer,
15530         ctx);
15531     Flush();
15532 }
15533 
RecordDefragmentationEnd(uint32_t frameIndex,VmaDefragmentationContext ctx)15534 void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
15535     VmaDefragmentationContext ctx)
15536 {
15537     CallParams callParams;
15538     GetBasicParams(callParams);
15539 
15540     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15541     fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
15542         ctx);
15543     Flush();
15544 }
15545 
RecordSetPoolName(uint32_t frameIndex,VmaPool pool,const char * name)15546 void VmaRecorder::RecordSetPoolName(uint32_t frameIndex,
15547     VmaPool pool,
15548     const char* name)
15549 {
15550     CallParams callParams;
15551     GetBasicParams(callParams);
15552 
15553     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15554     fprintf(m_File, "%u,%.3f,%u,vmaSetPoolName,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15555         pool, name != VMA_NULL ? name : "");
15556     Flush();
15557 }
15558 
UserDataString(VmaAllocationCreateFlags allocFlags,const void * pUserData)15559 VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
15560 {
15561     if(pUserData != VMA_NULL)
15562     {
15563         if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
15564         {
15565             m_Str = (const char*)pUserData;
15566         }
15567         else
15568         {
15569             // If VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is not specified, convert the string's memory address to a string and store it.
15570             snprintf(m_PtrStr, 17, "%p", pUserData);
15571             m_Str = m_PtrStr;
15572         }
15573     }
15574     else
15575     {
15576         m_Str = "";
15577     }
15578 }
15579 
WriteConfiguration(const VkPhysicalDeviceProperties & devProps,const VkPhysicalDeviceMemoryProperties & memProps,uint32_t vulkanApiVersion,bool dedicatedAllocationExtensionEnabled,bool bindMemory2ExtensionEnabled,bool memoryBudgetExtensionEnabled,bool deviceCoherentMemoryExtensionEnabled)15580 void VmaRecorder::WriteConfiguration(
15581     const VkPhysicalDeviceProperties& devProps,
15582     const VkPhysicalDeviceMemoryProperties& memProps,
15583     uint32_t vulkanApiVersion,
15584     bool dedicatedAllocationExtensionEnabled,
15585     bool bindMemory2ExtensionEnabled,
15586     bool memoryBudgetExtensionEnabled,
15587     bool deviceCoherentMemoryExtensionEnabled)
15588 {
15589     fprintf(m_File, "Config,Begin\n");
15590 
15591     fprintf(m_File, "VulkanApiVersion,%u,%u\n", VK_VERSION_MAJOR(vulkanApiVersion), VK_VERSION_MINOR(vulkanApiVersion));
15592 
15593     fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
15594     fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
15595     fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
15596     fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
15597     fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
15598     fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
15599 
15600     fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
15601     fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
15602     fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
15603 
15604     fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
15605     for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
15606     {
15607         fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
15608         fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
15609     }
15610     fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
15611     for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
15612     {
15613         fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
15614         fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
15615     }
15616 
15617     fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
15618     fprintf(m_File, "Extension,VK_KHR_bind_memory2,%u\n", bindMemory2ExtensionEnabled ? 1 : 0);
15619     fprintf(m_File, "Extension,VK_EXT_memory_budget,%u\n", memoryBudgetExtensionEnabled ? 1 : 0);
15620     fprintf(m_File, "Extension,VK_AMD_device_coherent_memory,%u\n", deviceCoherentMemoryExtensionEnabled ? 1 : 0);
15621 
15622     fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
15623     fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);
15624     fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
15625     fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
15626     fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
15627     fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
15628     fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
15629     fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
15630     fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15631 
15632     fprintf(m_File, "Config,End\n");
15633 }
15634 
GetBasicParams(CallParams & outParams)15635 void VmaRecorder::GetBasicParams(CallParams& outParams)
15636 {
15637     #if defined(_WIN32)
15638         outParams.threadId = GetCurrentThreadId();
15639     #else
15640         // Use C++11 features to get thread id and convert it to uint32_t.
15641         // There is room for optimization since sstream is quite slow.
15642         // Is there a better way to convert std::this_thread::get_id() to uint32_t?
15643         std::thread::id thread_id = std::this_thread::get_id();
15644         stringstream thread_id_to_string_converter;
15645         thread_id_to_string_converter << thread_id;
15646         string thread_id_as_string = thread_id_to_string_converter.str();
15647         outParams.threadId = static_cast<uint32_t>(std::stoi(thread_id_as_string.c_str()));
15648     #endif
15649 
15650     auto current_time = std::chrono::high_resolution_clock::now();
15651 
15652     outParams.time = std::chrono::duration<double, std::chrono::seconds::period>(current_time - m_RecordingStartTime).count();
15653 }
15654 
PrintPointerList(uint64_t count,const VmaAllocation * pItems)15655 void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
15656 {
15657     if(count)
15658     {
15659         fprintf(m_File, "%p", pItems[0]);
15660         for(uint64_t i = 1; i < count; ++i)
15661         {
15662             fprintf(m_File, " %p", pItems[i]);
15663         }
15664     }
15665 }
15666 
Flush()15667 void VmaRecorder::Flush()
15668 {
15669     if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
15670     {
15671         fflush(m_File);
15672     }
15673 }
15674 
15675 #endif // #if VMA_RECORDING_ENABLED
15676 
15677 ////////////////////////////////////////////////////////////////////////////////
15678 // VmaAllocationObjectAllocator
15679 
VmaAllocationObjectAllocator(const VkAllocationCallbacks * pAllocationCallbacks)15680 VmaAllocationObjectAllocator::VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks) :
15681     m_Allocator(pAllocationCallbacks, 1024)
15682 {
15683 }
15684 
Allocate(Types...args)15685 template<typename... Types> VmaAllocation VmaAllocationObjectAllocator::Allocate(Types... args)
15686 {
15687     VmaMutexLock mutexLock(m_Mutex);
15688     return m_Allocator.Alloc<Types...>(std::forward<Types>(args)...);
15689 }
15690 
Free(VmaAllocation hAlloc)15691 void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc)
15692 {
15693     VmaMutexLock mutexLock(m_Mutex);
15694     m_Allocator.Free(hAlloc);
15695 }
15696 
15697 ////////////////////////////////////////////////////////////////////////////////
15698 // VmaAllocator_T
15699 
VmaAllocator_T(const VmaAllocatorCreateInfo * pCreateInfo)15700 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
15701     m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
15702     m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0),
15703     m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
15704     m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0),
15705     m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0),
15706     m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0),
15707     m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0),
15708     m_hDevice(pCreateInfo->device),
15709     m_hInstance(pCreateInfo->instance),
15710     m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
15711     m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
15712         *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
15713     m_AllocationObjectAllocator(&m_AllocationCallbacks),
15714     m_HeapSizeLimitMask(0),
15715     m_PreferredLargeHeapBlockSize(0),
15716     m_PhysicalDevice(pCreateInfo->physicalDevice),
15717     m_CurrentFrameIndex(0),
15718     m_GpuDefragmentationMemoryTypeBits(UINT32_MAX),
15719     m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks())),
15720     m_NextPoolId(0),
15721     m_GlobalMemoryTypeBits(UINT32_MAX)
15722 #if VMA_RECORDING_ENABLED
15723     ,m_pRecorder(VMA_NULL)
15724 #endif
15725 {
15726     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15727     {
15728         m_UseKhrDedicatedAllocation = false;
15729         m_UseKhrBindMemory2 = false;
15730     }
15731 
15732     if(VMA_DEBUG_DETECT_CORRUPTION)
15733     {
15734         // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
15735         VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
15736     }
15737 
15738     VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance);
15739 
15740     if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
15741     {
15742 #if !(VMA_DEDICATED_ALLOCATION)
15743         if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
15744         {
15745             VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
15746         }
15747 #endif
15748 #if !(VMA_BIND_MEMORY2)
15749         if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0)
15750         {
15751             VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros.");
15752         }
15753 #endif
15754     }
15755 #if !(VMA_MEMORY_BUDGET)
15756     if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0)
15757     {
15758         VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros.");
15759     }
15760 #endif
15761 #if !(VMA_BUFFER_DEVICE_ADDRESS)
15762     if(m_UseKhrBufferDeviceAddress)
15763     {
15764         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.");
15765     }
15766 #endif
15767 #if VMA_VULKAN_VERSION < 1002000
15768     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0))
15769     {
15770         VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros.");
15771     }
15772 #endif
15773 #if VMA_VULKAN_VERSION < 1001000
15774     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15775     {
15776         VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros.");
15777     }
15778 #endif
15779 
15780     memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
15781     memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
15782     memset(&m_MemProps, 0, sizeof(m_MemProps));
15783 
15784     memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
15785     memset(&m_pReservedBlockVectors, 0, sizeof(m_pReservedBlockVectors));
15786     memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations));
15787     memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions));
15788 
15789     if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
15790     {
15791         m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData;
15792         m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
15793         m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
15794     }
15795 
15796     ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
15797 
15798     (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
15799     (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
15800 
15801     VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT));
15802     VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
15803     VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
15804     VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
15805 
15806     m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
15807         pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15808 
15809     m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits();
15810 
15811     if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
15812     {
15813         for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
15814         {
15815             const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
15816             if(limit != VK_WHOLE_SIZE)
15817             {
15818                 m_HeapSizeLimitMask |= 1u << heapIndex;
15819                 if(limit < m_MemProps.memoryHeaps[heapIndex].size)
15820                 {
15821                     m_MemProps.memoryHeaps[heapIndex].size = limit;
15822                 }
15823             }
15824         }
15825     }
15826 
15827     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15828     {
15829         const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
15830 
15831         m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
15832             this,
15833             VK_NULL_HANDLE, // hParentPool
15834             memTypeIndex,
15835             preferredBlockSize,
15836             0,
15837             pCreateInfo->maxBlockCount,
15838             GetBufferImageGranularity(),
15839             pCreateInfo->frameInUseCount,
15840             false, // explicitBlockSize
15841             false); // linearAlgorithm
15842         m_pReservedBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
15843             this,
15844             VK_NULL_HANDLE, // hParentPool
15845             memTypeIndex,
15846             preferredBlockSize,
15847             0,
15848             1, // max block count 1
15849             GetBufferImageGranularity(),
15850             pCreateInfo->frameInUseCount,
15851             false, // explicitBlockSize
15852             false); // linearAlgorithm
15853         // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
15854         // becase minBlockCount is 0.
15855         m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
15856 
15857     }
15858 }
15859 
Init(const VmaAllocatorCreateInfo * pCreateInfo)15860 VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
15861 {
15862     VkResult res = VK_SUCCESS;
15863 
15864     if(pCreateInfo->pRecordSettings != VMA_NULL &&
15865         !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
15866     {
15867 #if VMA_RECORDING_ENABLED
15868         m_pRecorder = vma_new(this, VmaRecorder)();
15869         res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
15870         if(res != VK_SUCCESS)
15871         {
15872             return res;
15873         }
15874         m_pRecorder->WriteConfiguration(
15875             m_PhysicalDeviceProperties,
15876             m_MemProps,
15877             m_VulkanApiVersion,
15878             m_UseKhrDedicatedAllocation,
15879             m_UseKhrBindMemory2,
15880             m_UseExtMemoryBudget,
15881             m_UseAmdDeviceCoherentMemory);
15882         m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
15883 #else
15884         VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
15885         return VK_ERROR_FEATURE_NOT_PRESENT;
15886 #endif
15887     }
15888 
15889 #if VMA_MEMORY_BUDGET
15890     if(m_UseExtMemoryBudget)
15891     {
15892         UpdateVulkanBudget();
15893     }
15894 #endif // #if VMA_MEMORY_BUDGET
15895 
15896     return res;
15897 }
15898 
~VmaAllocator_T()15899 VmaAllocator_T::~VmaAllocator_T()
15900 {
15901 #if VMA_RECORDING_ENABLED
15902     if(m_pRecorder != VMA_NULL)
15903     {
15904         m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
15905         vma_delete(this, m_pRecorder);
15906     }
15907 #endif
15908 
15909     VMA_ASSERT(m_Pools.empty());
15910 
15911     for(size_t i = GetMemoryTypeCount(); i--; )
15912     {
15913         if(m_pDedicatedAllocations[i] != VMA_NULL && !m_pDedicatedAllocations[i]->empty())
15914         {
15915             VMA_ASSERT(0 && "Unfreed dedicated allocations found.");
15916         }
15917 
15918         vma_delete(this, m_pDedicatedAllocations[i]);
15919         vma_delete(this, m_pBlockVectors[i]);
15920         vma_delete(this, m_pReservedBlockVectors[i]);
15921     }
15922 }
15923 
ImportVulkanFunctions(const VmaVulkanFunctions * pVulkanFunctions)15924 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
15925 {
15926 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15927     ImportVulkanFunctions_Static();
15928 #endif
15929 
15930     if(pVulkanFunctions != VMA_NULL)
15931     {
15932         ImportVulkanFunctions_Custom(pVulkanFunctions);
15933     }
15934 
15935 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
15936     ImportVulkanFunctions_Dynamic();
15937 #endif
15938 
15939     ValidateVulkanFunctions();
15940 }
15941 
15942 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15943 
ImportVulkanFunctions_Static()15944 void VmaAllocator_T::ImportVulkanFunctions_Static()
15945 {
15946     // Vulkan 1.0
15947     m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties;
15948     m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties;
15949     m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
15950     m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory;
15951     m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory;
15952     m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory;
15953     m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges;
15954     m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges;
15955     m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory;
15956     m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory;
15957     m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements;
15958     m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements;
15959     m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer;
15960     m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer;
15961     m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage;
15962     m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage;
15963     m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer;
15964 
15965     // Vulkan 1.1
15966 #if VMA_VULKAN_VERSION >= 1001000
15967     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15968     {
15969         m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2;
15970         m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2;
15971         m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2;
15972         m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2;
15973         m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2;
15974     }
15975 #endif
15976 }
15977 
15978 #endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15979 
ImportVulkanFunctions_Custom(const VmaVulkanFunctions * pVulkanFunctions)15980 void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions)
15981 {
15982     VMA_ASSERT(pVulkanFunctions != VMA_NULL);
15983 
15984 #define VMA_COPY_IF_NOT_NULL(funcName) \
15985     if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
15986 
15987     VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
15988     VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
15989     VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
15990     VMA_COPY_IF_NOT_NULL(vkFreeMemory);
15991     VMA_COPY_IF_NOT_NULL(vkMapMemory);
15992     VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
15993     VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
15994     VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
15995     VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
15996     VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
15997     VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
15998     VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
15999     VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
16000     VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
16001     VMA_COPY_IF_NOT_NULL(vkCreateImage);
16002     VMA_COPY_IF_NOT_NULL(vkDestroyImage);
16003     VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
16004 
16005 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16006     VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
16007     VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
16008 #endif
16009 
16010 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
16011     VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR);
16012     VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR);
16013 #endif
16014 
16015 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
16016     VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR);
16017 #endif
16018 
16019 #undef VMA_COPY_IF_NOT_NULL
16020 }
16021 
16022 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
16023 
ImportVulkanFunctions_Dynamic()16024 void VmaAllocator_T::ImportVulkanFunctions_Dynamic()
16025 {
16026 #define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \
16027     if(m_VulkanFunctions.memberName == VMA_NULL) \
16028         m_VulkanFunctions.memberName = \
16029             (functionPointerType)vkGetInstanceProcAddr(m_hInstance, functionNameString);
16030 #define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \
16031     if(m_VulkanFunctions.memberName == VMA_NULL) \
16032         m_VulkanFunctions.memberName = \
16033             (functionPointerType)vkGetDeviceProcAddr(m_hDevice, functionNameString);
16034 
16035     VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");
16036     VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties");
16037     VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory");
16038     VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory");
16039     VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory");
16040     VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory");
16041     VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges");
16042     VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges");
16043     VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory");
16044     VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory");
16045     VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements");
16046     VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements");
16047     VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer");
16048     VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer");
16049     VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage");
16050     VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage");
16051     VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer");
16052 
16053 #if VMA_DEDICATED_ALLOCATION
16054     if(m_UseKhrDedicatedAllocation)
16055     {
16056         VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR");
16057         VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR");
16058     }
16059 #endif
16060 
16061 #if VMA_BIND_MEMORY2
16062     if(m_UseKhrBindMemory2)
16063     {
16064         VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR");
16065         VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR");
16066     }
16067 #endif // #if VMA_BIND_MEMORY2
16068 
16069 #if VMA_MEMORY_BUDGET
16070     if(m_UseExtMemoryBudget && m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
16071     {
16072         VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");
16073     }
16074 #endif // #if VMA_MEMORY_BUDGET
16075 
16076 #undef VMA_FETCH_DEVICE_FUNC
16077 #undef VMA_FETCH_INSTANCE_FUNC
16078 }
16079 
16080 #endif // #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
16081 
ValidateVulkanFunctions()16082 void VmaAllocator_T::ValidateVulkanFunctions()
16083 {
16084     VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
16085     VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
16086     VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
16087     VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
16088     VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
16089     VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
16090     VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
16091     VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
16092     VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
16093     VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
16094     VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
16095     VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
16096     VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
16097     VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
16098     VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
16099     VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
16100     VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
16101 
16102 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16103     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation)
16104     {
16105         VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
16106         VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
16107     }
16108 #endif
16109 
16110 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
16111     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2)
16112     {
16113         VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL);
16114         VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL);
16115     }
16116 #endif
16117 
16118 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
16119     if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16120     {
16121         VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);
16122     }
16123 #endif
16124 }
16125 
CalcPreferredBlockSize(uint32_t memTypeIndex)16126 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
16127 {
16128     const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16129     const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
16130     const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
16131     return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32);
16132 }
16133 
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)16134 VkResult VmaAllocator_T::AllocateMemoryOfType(
16135     VkDeviceSize size,
16136     VkDeviceSize alignment,
16137     bool dedicatedAllocation,
16138     VkBuffer dedicatedBuffer,
16139     VkBufferUsageFlags dedicatedBufferUsage,
16140     VkImage dedicatedImage,
16141     const VmaAllocationCreateInfo& createInfo,
16142     uint32_t memTypeIndex,
16143     VmaSuballocationType suballocType,
16144     size_t allocationCount,
16145     VmaAllocation* pAllocations)
16146 {
16147     VMA_ASSERT(pAllocations != VMA_NULL);
16148     VMA_DEBUG_LOG("  AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size);
16149 
16150     VmaAllocationCreateInfo finalCreateInfo = createInfo;
16151 
16152     // If memory type is not HOST_VISIBLE, disable MAPPED.
16153     if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16154         (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
16155     {
16156         finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
16157     }
16158     // If memory is lazily allocated, it should be always dedicated.
16159     if(finalCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED)
16160     {
16161         finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
16162     }
16163 
16164     VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
16165     VMA_ASSERT(blockVector);
16166 
16167     const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
16168     bool preferDedicatedMemory =
16169         VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
16170         dedicatedAllocation ||
16171         // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
16172         size > preferredBlockSize / 2;
16173 
16174     if(preferDedicatedMemory &&
16175         (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
16176         finalCreateInfo.pool == VK_NULL_HANDLE)
16177     {
16178         finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
16179     }
16180 
16181     if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
16182     {
16183         if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16184         {
16185             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16186         }
16187         else
16188         {
16189             return AllocateDedicatedMemory(
16190                 size,
16191                 suballocType,
16192                 memTypeIndex,
16193                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
16194                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
16195                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
16196                 finalCreateInfo.pUserData,
16197                 dedicatedBuffer,
16198                 dedicatedBufferUsage,
16199                 dedicatedImage,
16200                 allocationCount,
16201                 pAllocations);
16202         }
16203     }
16204     else
16205     {
16206         VkResult res = blockVector->Allocate(
16207             m_CurrentFrameIndex.load(),
16208             size,
16209             alignment,
16210             finalCreateInfo,
16211             suballocType,
16212             allocationCount,
16213             pAllocations);
16214         if(res == VK_SUCCESS)
16215         {
16216             return res;
16217         }
16218 
16219         // 5. Try dedicated memory.
16220         if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16221         {
16222             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16223         }
16224         else
16225         {
16226             res = AllocateDedicatedMemory(
16227                 size,
16228                 suballocType,
16229                 memTypeIndex,
16230                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
16231                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
16232                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
16233                 finalCreateInfo.pUserData,
16234                 dedicatedBuffer,
16235                 dedicatedBufferUsage,
16236                 dedicatedImage,
16237                 allocationCount,
16238                 pAllocations);
16239             if(res == VK_SUCCESS)
16240             {
16241                 // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
16242                 VMA_DEBUG_LOG("    Allocated as DedicatedMemory");
16243                 return VK_SUCCESS;
16244             }
16245             else
16246             {
16247                 // Everything failed: Return error code.
16248                 VMA_DEBUG_LOG("    vkAllocateMemory FAILED");
16249                 return res;
16250             }
16251         }
16252     }
16253 }
16254 
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)16255 VkResult VmaAllocator_T::AllocateDedicatedMemory(
16256     VkDeviceSize size,
16257     VmaSuballocationType suballocType,
16258     uint32_t memTypeIndex,
16259     bool withinBudget,
16260     bool map,
16261     bool isUserDataString,
16262     void* pUserData,
16263     VkBuffer dedicatedBuffer,
16264     VkBufferUsageFlags dedicatedBufferUsage,
16265     VkImage dedicatedImage,
16266     size_t allocationCount,
16267     VmaAllocation* pAllocations)
16268 {
16269     VMA_ASSERT(allocationCount > 0 && pAllocations);
16270 
16271     if(withinBudget)
16272     {
16273         const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16274         VmaBudget heapBudget = {};
16275         GetBudget(&heapBudget, heapIndex, 1);
16276         if(heapBudget.usage + size * allocationCount > heapBudget.budget)
16277         {
16278             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16279         }
16280     }
16281 
16282     VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
16283     allocInfo.memoryTypeIndex = memTypeIndex;
16284     allocInfo.allocationSize = size;
16285 
16286 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16287     VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
16288     if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16289     {
16290         if(dedicatedBuffer != VK_NULL_HANDLE)
16291         {
16292             VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
16293             dedicatedAllocInfo.buffer = dedicatedBuffer;
16294             VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16295         }
16296         else if(dedicatedImage != VK_NULL_HANDLE)
16297         {
16298             dedicatedAllocInfo.image = dedicatedImage;
16299             VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16300         }
16301     }
16302 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16303 
16304 #if VMA_BUFFER_DEVICE_ADDRESS
16305     VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
16306     if(m_UseKhrBufferDeviceAddress)
16307     {
16308         bool canContainBufferWithDeviceAddress = true;
16309         if(dedicatedBuffer != VK_NULL_HANDLE)
16310         {
16311             canContainBufferWithDeviceAddress = dedicatedBufferUsage == UINT32_MAX || // Usage flags unknown
16312                 (dedicatedBufferUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) != 0;
16313         }
16314         else if(dedicatedImage != VK_NULL_HANDLE)
16315         {
16316             canContainBufferWithDeviceAddress = false;
16317         }
16318         if(canContainBufferWithDeviceAddress)
16319         {
16320             allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
16321             VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
16322         }
16323     }
16324 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
16325 
16326     size_t allocIndex;
16327     VkResult res = VK_SUCCESS;
16328     for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16329     {
16330         res = AllocateDedicatedMemoryPage(
16331             size,
16332             suballocType,
16333             memTypeIndex,
16334             allocInfo,
16335             map,
16336             isUserDataString,
16337             pUserData,
16338             pAllocations + allocIndex);
16339         if(res != VK_SUCCESS)
16340         {
16341             break;
16342         }
16343     }
16344 
16345     if(res == VK_SUCCESS)
16346     {
16347         // Register them in m_pDedicatedAllocations.
16348         {
16349             VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16350             AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
16351             VMA_ASSERT(pDedicatedAllocations);
16352             for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16353             {
16354                 VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, pAllocations[allocIndex]);
16355             }
16356         }
16357 
16358         VMA_DEBUG_LOG("    Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
16359     }
16360     else
16361     {
16362         // Free all already created allocations.
16363         while(allocIndex--)
16364         {
16365             VmaAllocation currAlloc = pAllocations[allocIndex];
16366             VkDeviceMemory hMemory = currAlloc->GetMemory();
16367 
16368             /*
16369             There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
16370             before vkFreeMemory.
16371 
16372             if(currAlloc->GetMappedData() != VMA_NULL)
16373             {
16374                 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
16375             }
16376             */
16377 
16378             FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
16379             m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize());
16380             currAlloc->SetUserData(this, VMA_NULL);
16381             m_AllocationObjectAllocator.Free(currAlloc);
16382         }
16383 
16384         memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16385     }
16386 
16387     return res;
16388 }
16389 
AllocateDedicatedMemoryPage(VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,const VkMemoryAllocateInfo & allocInfo,bool map,bool isUserDataString,void * pUserData,VmaAllocation * pAllocation)16390 VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
16391     VkDeviceSize size,
16392     VmaSuballocationType suballocType,
16393     uint32_t memTypeIndex,
16394     const VkMemoryAllocateInfo& allocInfo,
16395     bool map,
16396     bool isUserDataString,
16397     void* pUserData,
16398     VmaAllocation* pAllocation)
16399 {
16400     VkDeviceMemory hMemory = VK_NULL_HANDLE;
16401     VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
16402     if(res < 0)
16403     {
16404         VMA_DEBUG_LOG("    vkAllocateMemory FAILED");
16405         return res;
16406     }
16407 
16408     void* pMappedData = VMA_NULL;
16409     if(map)
16410     {
16411         res = (*m_VulkanFunctions.vkMapMemory)(
16412             m_hDevice,
16413             hMemory,
16414             0,
16415             VK_WHOLE_SIZE,
16416             0,
16417             &pMappedData);
16418         if(res < 0)
16419         {
16420             VMA_DEBUG_LOG("    vkMapMemory FAILED");
16421             FreeVulkanMemory(memTypeIndex, size, hMemory);
16422             return res;
16423         }
16424     }
16425 
16426     *pAllocation = m_AllocationObjectAllocator.Allocate(m_CurrentFrameIndex.load(), isUserDataString);
16427     (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
16428     (*pAllocation)->SetUserData(this, pUserData);
16429     m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size);
16430     if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16431     {
16432         FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
16433     }
16434 
16435     return VK_SUCCESS;
16436 }
16437 
GetBufferMemoryRequirements(VkBuffer hBuffer,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)16438 void VmaAllocator_T::GetBufferMemoryRequirements(
16439     VkBuffer hBuffer,
16440     VkMemoryRequirements& memReq,
16441     bool& requiresDedicatedAllocation,
16442     bool& prefersDedicatedAllocation) const
16443 {
16444 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16445     if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16446     {
16447         VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
16448         memReqInfo.buffer = hBuffer;
16449 
16450         VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16451 
16452         VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16453         VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16454 
16455         (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16456 
16457         memReq = memReq2.memoryRequirements;
16458         requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16459         prefersDedicatedAllocation  = (memDedicatedReq.prefersDedicatedAllocation  != VK_FALSE);
16460     }
16461     else
16462 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16463     {
16464         (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
16465         requiresDedicatedAllocation = false;
16466         prefersDedicatedAllocation  = false;
16467     }
16468 }
16469 
GetImageMemoryRequirements(VkImage hImage,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)16470 void VmaAllocator_T::GetImageMemoryRequirements(
16471     VkImage hImage,
16472     VkMemoryRequirements& memReq,
16473     bool& requiresDedicatedAllocation,
16474     bool& prefersDedicatedAllocation) const
16475 {
16476 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16477     if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16478     {
16479         VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
16480         memReqInfo.image = hImage;
16481 
16482         VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16483 
16484         VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16485         VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16486 
16487         (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16488 
16489         memReq = memReq2.memoryRequirements;
16490         requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16491         prefersDedicatedAllocation  = (memDedicatedReq.prefersDedicatedAllocation  != VK_FALSE);
16492     }
16493     else
16494 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16495     {
16496         (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
16497         requiresDedicatedAllocation = false;
16498         prefersDedicatedAllocation  = false;
16499     }
16500 }
16501 
AllocateMemory(const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,VkBuffer dedicatedBuffer,VkBufferUsageFlags dedicatedBufferUsage,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)16502 VkResult VmaAllocator_T::AllocateMemory(
16503     const VkMemoryRequirements& vkMemReq,
16504     bool requiresDedicatedAllocation,
16505     bool prefersDedicatedAllocation,
16506     VkBuffer dedicatedBuffer,
16507     VkBufferUsageFlags dedicatedBufferUsage,
16508     VkImage dedicatedImage,
16509     const VmaAllocationCreateInfo& createInfo,
16510     VmaSuballocationType suballocType,
16511     size_t allocationCount,
16512     VmaAllocation* pAllocations)
16513 {
16514     memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16515 
16516     VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
16517 
16518     if(vkMemReq.size == 0)
16519     {
16520         return VK_ERROR_VALIDATION_FAILED_EXT;
16521     }
16522     if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
16523         (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16524     {
16525         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
16526         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16527     }
16528     if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16529         (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
16530     {
16531         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
16532         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16533     }
16534     if(requiresDedicatedAllocation)
16535     {
16536         if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16537         {
16538             VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
16539             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16540         }
16541         if(createInfo.pool != VK_NULL_HANDLE)
16542         {
16543             VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
16544             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16545         }
16546     }
16547     if((createInfo.pool != VK_NULL_HANDLE) &&
16548         ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
16549     {
16550         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
16551         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16552     }
16553 
16554     if(createInfo.pool != VK_NULL_HANDLE)
16555     {
16556         const VkDeviceSize alignmentForPool = VMA_MAX(
16557             vkMemReq.alignment,
16558             GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex()));
16559 
16560         VmaAllocationCreateInfo createInfoForPool = createInfo;
16561         // If memory type is not HOST_VISIBLE, disable MAPPED.
16562         if((createInfoForPool.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16563             (m_MemProps.memoryTypes[createInfo.pool->m_BlockVector.GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
16564         {
16565             createInfoForPool.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
16566         }
16567 
16568         return createInfo.pool->m_BlockVector.Allocate(
16569             m_CurrentFrameIndex.load(),
16570             vkMemReq.size,
16571             alignmentForPool,
16572             createInfoForPool,
16573             suballocType,
16574             allocationCount,
16575             pAllocations);
16576     }
16577     else
16578     {
16579         // Bit mask of memory Vulkan types acceptable for this allocation.
16580         uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
16581         uint32_t memTypeIndex = UINT32_MAX;
16582         VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16583         if(res == VK_SUCCESS)
16584         {
16585             VkDeviceSize alignmentForMemType = VMA_MAX(
16586                 vkMemReq.alignment,
16587                 GetMemoryTypeMinAlignment(memTypeIndex));
16588 
16589             res = AllocateMemoryOfType(
16590                 vkMemReq.size,
16591                 alignmentForMemType,
16592                 requiresDedicatedAllocation || prefersDedicatedAllocation,
16593                 dedicatedBuffer,
16594                 dedicatedBufferUsage,
16595                 dedicatedImage,
16596                 createInfo,
16597                 memTypeIndex,
16598                 suballocType,
16599                 allocationCount,
16600                 pAllocations);
16601             // Succeeded on first try.
16602             if(res == VK_SUCCESS)
16603             {
16604                 return res;
16605             }
16606             // Allocation from this memory type failed. Try other compatible memory types.
16607             else
16608             {
16609                 for(;;)
16610                 {
16611                     // Remove old memTypeIndex from list of possibilities.
16612                     memoryTypeBits &= ~(1u << memTypeIndex);
16613                     // Find alternative memTypeIndex.
16614                     res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16615                     if(res == VK_SUCCESS)
16616                     {
16617                         alignmentForMemType = VMA_MAX(
16618                             vkMemReq.alignment,
16619                             GetMemoryTypeMinAlignment(memTypeIndex));
16620 
16621                         res = AllocateMemoryOfType(
16622                             vkMemReq.size,
16623                             alignmentForMemType,
16624                             requiresDedicatedAllocation || prefersDedicatedAllocation,
16625                             dedicatedBuffer,
16626                             dedicatedBufferUsage,
16627                             dedicatedImage,
16628                             createInfo,
16629                             memTypeIndex,
16630                             suballocType,
16631                             allocationCount,
16632                             pAllocations);
16633                         // Allocation from this alternative memory type succeeded.
16634                         if(res == VK_SUCCESS)
16635                         {
16636                             return res;
16637                         }
16638                         // else: Allocation from this memory type failed. Try next one - next loop iteration.
16639                     }
16640                     // No other matching memory type index could be found.
16641                     else
16642                     {
16643                         // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
16644                         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16645                     }
16646                 }
16647             }
16648         }
16649         // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
16650         else
16651             return res;
16652     }
16653 }
16654 
FreeMemory(size_t allocationCount,const VmaAllocation * pAllocations)16655 void VmaAllocator_T::FreeMemory(
16656     size_t allocationCount,
16657     const VmaAllocation* pAllocations)
16658 {
16659     VMA_ASSERT(pAllocations);
16660 
16661     for(size_t allocIndex = allocationCount; allocIndex--; )
16662     {
16663         VmaAllocation allocation = pAllocations[allocIndex];
16664 
16665         if(allocation != VK_NULL_HANDLE)
16666         {
16667             if(TouchAllocation(allocation))
16668             {
16669                 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16670                 {
16671                     FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
16672                 }
16673 
16674                 switch(allocation->GetType())
16675                 {
16676                 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16677                     {
16678                         VmaBlockVector* pBlockVector = VMA_NULL;
16679                         VmaPool hPool = allocation->GetBlock()->GetParentPool();
16680                         if(hPool != VK_NULL_HANDLE)
16681                         {
16682                             pBlockVector = &hPool->m_BlockVector;
16683                         }
16684                         else
16685                         {
16686                             const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16687                             pBlockVector = m_pBlockVectors[memTypeIndex];
16688                         }
16689                         pBlockVector->Free(allocation);
16690                     }
16691                     break;
16692                 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
16693                     FreeDedicatedMemory(allocation);
16694                     break;
16695                 default:
16696                     VMA_ASSERT(0);
16697                 }
16698             }
16699 
16700             // Do this regardless of whether the allocation is lost. Lost allocations still account to Budget.AllocationBytes.
16701             m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
16702             allocation->SetUserData(this, VMA_NULL);
16703             m_AllocationObjectAllocator.Free(allocation);
16704         }
16705     }
16706 }
16707 
16708 // 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)16709 VkResult VmaAllocator_T::AllocateReservedMemory(
16710     const VkMemoryRequirements& vkMemReq,
16711     bool requiresDedicatedAllocation,
16712     bool prefersDedicatedAllocation,
16713     VkBuffer dedicatedBuffer,
16714     VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown.
16715     VkImage dedicatedImage,
16716     const VmaAllocationCreateInfo& createInfo,
16717     VmaSuballocationType suballocType,
16718     size_t allocationCount,
16719     VmaAllocation* pAllocations)
16720 {
16721     memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16722     if (allocationCount != 1) {
16723         return VK_NOT_READY;
16724     }
16725     uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
16726     uint32_t memTypeIndex = UINT32_MAX;
16727     VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16728     if(res != VK_SUCCESS) {
16729         return res;
16730     }
16731     VkDeviceSize alignmentForMemType = VMA_MAX(
16732         vkMemReq.alignment,
16733         GetMemoryTypeMinAlignment(memTypeIndex));
16734     VmaBlockVector* reservedBlockVector = m_pReservedBlockVectors[memTypeIndex];
16735     VMA_ASSERT(reservedBlockVector);
16736 
16737     res = reservedBlockVector->AllocateReserved(
16738         m_CurrentFrameIndex.load(),
16739         vkMemReq.size,
16740         alignmentForMemType,
16741         createInfo,
16742         suballocType,
16743         pAllocations);
16744     return res;
16745 }
16746 
FreeReservedMemory(size_t allocationCount,const VmaAllocation * pAllocations)16747 void VmaAllocator_T::FreeReservedMemory(
16748     size_t allocationCount,
16749     const VmaAllocation* pAllocations)
16750 {
16751     VMA_ASSERT(pAllocations);
16752 
16753     for(size_t allocIndex = allocationCount; allocIndex--; )
16754     {
16755         VmaAllocation allocation = pAllocations[allocIndex];
16756 
16757         if(allocation != VK_NULL_HANDLE)
16758         {
16759             if(TouchAllocation(allocation))
16760             {
16761                 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16762                 {
16763                     FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
16764                 }
16765 
16766                 switch(allocation->GetType())
16767                 {
16768                 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16769                     {
16770                         VmaBlockVector* pBlockVector = VMA_NULL;
16771                         VmaPool hPool = allocation->GetBlock()->GetParentPool();
16772                         if(hPool != VK_NULL_HANDLE)
16773                         {
16774                             pBlockVector = &hPool->m_BlockVector;
16775                         }
16776                         else
16777                         {
16778                             const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16779                             pBlockVector = m_pReservedBlockVectors[memTypeIndex];
16780                         }
16781                         VMA_ASSERT(pBlockVector);
16782                         pBlockVector->FreeReserved(allocation);
16783                     }
16784                     break;
16785                 default:
16786                     VMA_ASSERT(0);
16787                 }
16788             }
16789             m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
16790         }
16791     }
16792 }
16793 
SwapReservedBlock(VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)16794 VkResult VmaAllocator_T::SwapReservedBlock(
16795     VkImage image,
16796     const VmaAllocationCreateInfo* pCreateInfo,
16797     VmaAllocation* pAllocation,
16798     VmaAllocationInfo* pAllocationInfo)
16799 {
16800     VmaAllocation oldAllocation = *pAllocation;
16801     if (oldAllocation->GetType() != VmaAllocation_T::ALLOCATION_TYPE_BLOCK) {
16802         return VK_ERROR_UNKNOWN;
16803     }
16804     VmaPool hPool = oldAllocation->GetBlock()->GetParentPool();
16805     if(hPool != VK_NULL_HANDLE) {
16806         return VK_ERROR_UNKNOWN;
16807     }
16808     const uint32_t memTypeIndex = oldAllocation->GetMemoryTypeIndex();
16809     VmaBlockVector* reservedBlockVector = m_pReservedBlockVectors[memTypeIndex];
16810     if (reservedBlockVector->IsEmpty()) {
16811         return VK_NOT_READY;
16812     }
16813     if (!reservedBlockVector->IsLastBlockBindComplete()) {
16814         return VK_INCOMPLETE;
16815     }
16816 
16817     VmaBlockVector* blockVector = m_pBlockVectors[memTypeIndex];
16818     SwapLastBlock(blockVector, reservedBlockVector);
16819     return VK_SUCCESS;
16820 }
16821 
GetPreAllocBlockSize()16822 uint32_t VmaAllocator_T::GetPreAllocBlockSize()
16823 {
16824     VmaBlockVector* reservedBlockVector = m_pReservedBlockVectors[0];
16825     VMA_ASSERT(reservedBlockVector);
16826     return reservedBlockVector->GetBlockCount();
16827 }
16828 
ResizeAllocation(const VmaAllocation alloc,VkDeviceSize newSize)16829 VkResult VmaAllocator_T::ResizeAllocation(
16830     const VmaAllocation alloc,
16831     VkDeviceSize newSize)
16832 {
16833     // This function is deprecated and so it does nothing. It's left for backward compatibility.
16834     if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST)
16835     {
16836         return VK_ERROR_VALIDATION_FAILED_EXT;
16837     }
16838     if(newSize == alloc->GetSize())
16839     {
16840         return VK_SUCCESS;
16841     }
16842     return VK_ERROR_OUT_OF_POOL_MEMORY;
16843 }
16844 
CalculateStats(VmaStats * pStats)16845 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
16846 {
16847     // Initialize.
16848     InitStatInfo(pStats->total);
16849     for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
16850         InitStatInfo(pStats->memoryType[i]);
16851     for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
16852         InitStatInfo(pStats->memoryHeap[i]);
16853 
16854     // Process default pools.
16855     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16856     {
16857         VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
16858         VMA_ASSERT(pBlockVector);
16859         pBlockVector->AddStats(pStats);
16860     }
16861 
16862     // Process custom pools.
16863     {
16864         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
16865         for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
16866         {
16867             m_Pools[poolIndex]->m_BlockVector.AddStats(pStats);
16868         }
16869     }
16870 
16871     // Process dedicated allocations.
16872     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16873     {
16874         const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16875         VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16876         AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
16877         VMA_ASSERT(pDedicatedAllocVector);
16878         for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
16879         {
16880             VmaStatInfo allocationStatInfo;
16881             (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo);
16882             VmaAddStatInfo(pStats->total, allocationStatInfo);
16883             VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
16884             VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
16885         }
16886     }
16887 
16888     // Postprocess.
16889     VmaPostprocessCalcStatInfo(pStats->total);
16890     for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
16891         VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
16892     for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
16893         VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
16894 }
16895 
FreeEmptyBlock()16896 void VmaAllocator_T::FreeEmptyBlock()
16897 {
16898     for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) {
16899         VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
16900         VMA_ASSERT(pBlockVector);
16901         pBlockVector->FreeEmptyBlock();
16902 
16903         VmaBlockVector* const pReservedBlockVector = m_pReservedBlockVectors[memTypeIndex];
16904         VMA_ASSERT(pReservedBlockVector);
16905         pReservedBlockVector->FreeEmptyBlock();
16906     }
16907 }
16908 
GetBudget(VmaBudget * outBudget,uint32_t firstHeap,uint32_t heapCount)16909 void VmaAllocator_T::GetBudget(VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount)
16910 {
16911 #if VMA_MEMORY_BUDGET
16912     if(m_UseExtMemoryBudget)
16913     {
16914         if(m_Budget.m_OperationsSinceBudgetFetch < 30)
16915         {
16916             VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex);
16917             for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
16918             {
16919                 const uint32_t heapIndex = firstHeap + i;
16920 
16921                 outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
16922                 outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
16923 
16924                 if(m_Budget.m_VulkanUsage[heapIndex] + outBudget->blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex])
16925                 {
16926                     outBudget->usage = m_Budget.m_VulkanUsage[heapIndex] +
16927                         outBudget->blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
16928                 }
16929                 else
16930                 {
16931                     outBudget->usage = 0;
16932                 }
16933 
16934                 // Have to take MIN with heap size because explicit HeapSizeLimit is included in it.
16935                 outBudget->budget = VMA_MIN(
16936                     m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size);
16937             }
16938         }
16939         else
16940         {
16941             UpdateVulkanBudget(); // Outside of mutex lock
16942             GetBudget(outBudget, firstHeap, heapCount); // Recursion
16943         }
16944     }
16945     else
16946 #endif
16947     {
16948         for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
16949         {
16950             const uint32_t heapIndex = firstHeap + i;
16951 
16952             outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
16953             outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
16954 
16955             outBudget->usage = outBudget->blockBytes;
16956             outBudget->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
16957         }
16958     }
16959 }
16960 
16961 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
16962 
DefragmentationBegin(const VmaDefragmentationInfo2 & info,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)16963 VkResult VmaAllocator_T::DefragmentationBegin(
16964     const VmaDefragmentationInfo2& info,
16965     VmaDefragmentationStats* pStats,
16966     VmaDefragmentationContext* pContext)
16967 {
16968     if(info.pAllocationsChanged != VMA_NULL)
16969     {
16970         memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
16971     }
16972 
16973     *pContext = vma_new(this, VmaDefragmentationContext_T)(
16974         this, m_CurrentFrameIndex.load(), info.flags, pStats);
16975 
16976     (*pContext)->AddPools(info.poolCount, info.pPools);
16977     (*pContext)->AddAllocations(
16978         info.allocationCount, info.pAllocations, info.pAllocationsChanged);
16979 
16980     VkResult res = (*pContext)->Defragment(
16981         info.maxCpuBytesToMove, info.maxCpuAllocationsToMove,
16982         info.maxGpuBytesToMove, info.maxGpuAllocationsToMove,
16983         info.commandBuffer, pStats, info.flags);
16984 
16985     if(res != VK_NOT_READY)
16986     {
16987         vma_delete(this, *pContext);
16988         *pContext = VMA_NULL;
16989     }
16990 
16991     return res;
16992 }
16993 
DefragmentationEnd(VmaDefragmentationContext context)16994 VkResult VmaAllocator_T::DefragmentationEnd(
16995     VmaDefragmentationContext context)
16996 {
16997     vma_delete(this, context);
16998     return VK_SUCCESS;
16999 }
17000 
DefragmentationPassBegin(VmaDefragmentationPassInfo * pInfo,VmaDefragmentationContext context)17001 VkResult VmaAllocator_T::DefragmentationPassBegin(
17002     VmaDefragmentationPassInfo* pInfo,
17003     VmaDefragmentationContext context)
17004 {
17005     return context->DefragmentPassBegin(pInfo);
17006 }
DefragmentationPassEnd(VmaDefragmentationContext context)17007 VkResult VmaAllocator_T::DefragmentationPassEnd(
17008     VmaDefragmentationContext context)
17009 {
17010     return context->DefragmentPassEnd();
17011 
17012 }
17013 
GetAllocationInfo(VmaAllocation hAllocation,VmaAllocationInfo * pAllocationInfo)17014 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
17015 {
17016     if(hAllocation->CanBecomeLost())
17017     {
17018         /*
17019         Warning: This is a carefully designed algorithm.
17020         Do not modify unless you really know what you're doing :)
17021         */
17022         const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17023         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17024         for(;;)
17025         {
17026             if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
17027             {
17028                 pAllocationInfo->memoryType = UINT32_MAX;
17029                 pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
17030                 pAllocationInfo->offset = 0;
17031                 pAllocationInfo->size = hAllocation->GetSize();
17032                 pAllocationInfo->pMappedData = VMA_NULL;
17033                 pAllocationInfo->pUserData = hAllocation->GetUserData();
17034                 return;
17035             }
17036             else if(localLastUseFrameIndex == localCurrFrameIndex)
17037             {
17038                 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
17039                 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
17040                 pAllocationInfo->offset = hAllocation->GetOffset();
17041                 pAllocationInfo->size = hAllocation->GetSize();
17042                 pAllocationInfo->pMappedData = VMA_NULL;
17043                 pAllocationInfo->pUserData = hAllocation->GetUserData();
17044                 return;
17045             }
17046             else // Last use time earlier than current time.
17047             {
17048                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17049                 {
17050                     localLastUseFrameIndex = localCurrFrameIndex;
17051                 }
17052             }
17053         }
17054     }
17055     else
17056     {
17057 #if VMA_STATS_STRING_ENABLED
17058         uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17059         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17060         for(;;)
17061         {
17062             VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
17063             if(localLastUseFrameIndex == localCurrFrameIndex)
17064             {
17065                 break;
17066             }
17067             else // Last use time earlier than current time.
17068             {
17069                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17070                 {
17071                     localLastUseFrameIndex = localCurrFrameIndex;
17072                 }
17073             }
17074         }
17075 #endif
17076 
17077         pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
17078         pAllocationInfo->deviceMemory = hAllocation->GetMemory();
17079         pAllocationInfo->offset = hAllocation->GetOffset();
17080         pAllocationInfo->size = hAllocation->GetSize();
17081         pAllocationInfo->pMappedData = hAllocation->GetMappedData();
17082         pAllocationInfo->pUserData = hAllocation->GetUserData();
17083     }
17084 }
17085 
TouchAllocation(VmaAllocation hAllocation)17086 bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
17087 {
17088     // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
17089     if(hAllocation->CanBecomeLost())
17090     {
17091         uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17092         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17093         for(;;)
17094         {
17095             if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
17096             {
17097                 return false;
17098             }
17099             else if(localLastUseFrameIndex == localCurrFrameIndex)
17100             {
17101                 return true;
17102             }
17103             else // Last use time earlier than current time.
17104             {
17105                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17106                 {
17107                     localLastUseFrameIndex = localCurrFrameIndex;
17108                 }
17109             }
17110         }
17111     }
17112     else
17113     {
17114 #if VMA_STATS_STRING_ENABLED
17115         uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17116         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17117         for(;;)
17118         {
17119             VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
17120             if(localLastUseFrameIndex == localCurrFrameIndex)
17121             {
17122                 break;
17123             }
17124             else // Last use time earlier than current time.
17125             {
17126                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17127                 {
17128                     localLastUseFrameIndex = localCurrFrameIndex;
17129                 }
17130             }
17131         }
17132 #endif
17133 
17134         return true;
17135     }
17136 }
17137 
CreatePool(const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)17138 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
17139 {
17140     VMA_DEBUG_LOG("  CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
17141 
17142     VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
17143 
17144     if(newCreateInfo.maxBlockCount == 0)
17145     {
17146         newCreateInfo.maxBlockCount = SIZE_MAX;
17147     }
17148     if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
17149     {
17150         return VK_ERROR_INITIALIZATION_FAILED;
17151     }
17152     // Memory type index out of range or forbidden.
17153     if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() ||
17154         ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0)
17155     {
17156         return VK_ERROR_FEATURE_NOT_PRESENT;
17157     }
17158 
17159     const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
17160 
17161     *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
17162 
17163     VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
17164     if(res != VK_SUCCESS)
17165     {
17166         vma_delete(this, *pPool);
17167         *pPool = VMA_NULL;
17168         return res;
17169     }
17170 
17171     // Add to m_Pools.
17172     {
17173         VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
17174         (*pPool)->SetId(m_NextPoolId++);
17175         VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool);
17176     }
17177 
17178     return VK_SUCCESS;
17179 }
17180 
DestroyPool(VmaPool pool)17181 void VmaAllocator_T::DestroyPool(VmaPool pool)
17182 {
17183     // Remove from m_Pools.
17184     {
17185         VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
17186         bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
17187         VMA_ASSERT(success && "Pool not found in Allocator.");
17188     }
17189 
17190     vma_delete(this, pool);
17191 }
17192 
GetPoolStats(VmaPool pool,VmaPoolStats * pPoolStats)17193 void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
17194 {
17195     pool->m_BlockVector.GetPoolStats(pPoolStats);
17196 }
17197 
SetCurrentFrameIndex(uint32_t frameIndex)17198 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
17199 {
17200     m_CurrentFrameIndex.store(frameIndex);
17201 
17202 #if VMA_MEMORY_BUDGET
17203     if(m_UseExtMemoryBudget)
17204     {
17205         UpdateVulkanBudget();
17206     }
17207 #endif // #if VMA_MEMORY_BUDGET
17208 }
17209 
MakePoolAllocationsLost(VmaPool hPool,size_t * pLostAllocationCount)17210 void VmaAllocator_T::MakePoolAllocationsLost(
17211     VmaPool hPool,
17212     size_t* pLostAllocationCount)
17213 {
17214     hPool->m_BlockVector.MakePoolAllocationsLost(
17215         m_CurrentFrameIndex.load(),
17216         pLostAllocationCount);
17217 }
17218 
CheckPoolCorruption(VmaPool hPool)17219 VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
17220 {
17221     return hPool->m_BlockVector.CheckCorruption();
17222 }
17223 
CheckCorruption(uint32_t memoryTypeBits)17224 VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
17225 {
17226     VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
17227 
17228     // Process default pools.
17229     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17230     {
17231         if(((1u << memTypeIndex) & memoryTypeBits) != 0)
17232         {
17233             VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
17234             VMA_ASSERT(pBlockVector);
17235             VkResult localRes = pBlockVector->CheckCorruption();
17236             switch(localRes)
17237             {
17238             case VK_ERROR_FEATURE_NOT_PRESENT:
17239                 break;
17240             case VK_SUCCESS:
17241                 finalRes = VK_SUCCESS;
17242                 break;
17243             default:
17244                 return localRes;
17245             }
17246         }
17247     }
17248 
17249     // Process custom pools.
17250     {
17251         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
17252         for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
17253         {
17254             if(((1u << m_Pools[poolIndex]->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
17255             {
17256                 VkResult localRes = m_Pools[poolIndex]->m_BlockVector.CheckCorruption();
17257                 switch(localRes)
17258                 {
17259                 case VK_ERROR_FEATURE_NOT_PRESENT:
17260                     break;
17261                 case VK_SUCCESS:
17262                     finalRes = VK_SUCCESS;
17263                     break;
17264                 default:
17265                     return localRes;
17266                 }
17267             }
17268         }
17269     }
17270 
17271     return finalRes;
17272 }
17273 
CreateLostAllocation(VmaAllocation * pAllocation)17274 void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
17275 {
17276     *pAllocation = m_AllocationObjectAllocator.Allocate(VMA_FRAME_INDEX_LOST, false);
17277     (*pAllocation)->InitLost();
17278 }
17279 
AllocateVulkanMemory(const VkMemoryAllocateInfo * pAllocateInfo,VkDeviceMemory * pMemory)17280 VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
17281 {
17282     const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
17283 
17284     // HeapSizeLimit is in effect for this heap.
17285     if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0)
17286     {
17287         const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
17288         VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex];
17289         for(;;)
17290         {
17291             const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize;
17292             if(blockBytesAfterAllocation > heapSize)
17293             {
17294                 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
17295             }
17296             if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation))
17297             {
17298                 break;
17299             }
17300         }
17301     }
17302     else
17303     {
17304         m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize;
17305     }
17306 
17307     // VULKAN CALL vkAllocateMemory.
17308     VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
17309 
17310     if(res == VK_SUCCESS)
17311     {
17312 #if VMA_MEMORY_BUDGET
17313         ++m_Budget.m_OperationsSinceBudgetFetch;
17314 #endif
17315 
17316         // Informative callback.
17317         if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
17318         {
17319             (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData);
17320         }
17321     }
17322     else
17323     {
17324         m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize;
17325     }
17326 
17327     return res;
17328 }
17329 
FreeVulkanMemory(uint32_t memoryType,VkDeviceSize size,VkDeviceMemory hMemory)17330 void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
17331 {
17332     // Informative callback.
17333     if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
17334     {
17335         (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData);
17336     }
17337 
17338     // VULKAN CALL vkFreeMemory.
17339     (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
17340 
17341     m_Budget.m_BlockBytes[MemoryTypeIndexToHeapIndex(memoryType)] -= size;
17342 }
17343 
BindVulkanBuffer(VkDeviceMemory memory,VkDeviceSize memoryOffset,VkBuffer buffer,const void * pNext)17344 VkResult VmaAllocator_T::BindVulkanBuffer(
17345     VkDeviceMemory memory,
17346     VkDeviceSize memoryOffset,
17347     VkBuffer buffer,
17348     const void* pNext)
17349 {
17350     if(pNext != VMA_NULL)
17351     {
17352 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17353         if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
17354             m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL)
17355         {
17356             VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR };
17357             bindBufferMemoryInfo.pNext = pNext;
17358             bindBufferMemoryInfo.buffer = buffer;
17359             bindBufferMemoryInfo.memory = memory;
17360             bindBufferMemoryInfo.memoryOffset = memoryOffset;
17361             return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17362         }
17363         else
17364 #endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17365         {
17366             return VK_ERROR_EXTENSION_NOT_PRESENT;
17367         }
17368     }
17369     else
17370     {
17371         return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset);
17372     }
17373 }
17374 
BindVulkanImage(VkDeviceMemory memory,VkDeviceSize memoryOffset,VkImage image,const void * pNext)17375 VkResult VmaAllocator_T::BindVulkanImage(
17376     VkDeviceMemory memory,
17377     VkDeviceSize memoryOffset,
17378     VkImage image,
17379     const void* pNext)
17380 {
17381     if(pNext != VMA_NULL)
17382     {
17383 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17384         if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
17385             m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL)
17386         {
17387             VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR };
17388             bindBufferMemoryInfo.pNext = pNext;
17389             bindBufferMemoryInfo.image = image;
17390             bindBufferMemoryInfo.memory = memory;
17391             bindBufferMemoryInfo.memoryOffset = memoryOffset;
17392             return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17393         }
17394         else
17395 #endif // #if VMA_BIND_MEMORY2
17396         {
17397             return VK_ERROR_EXTENSION_NOT_PRESENT;
17398         }
17399     }
17400     else
17401     {
17402         return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset);
17403     }
17404 }
17405 
Map(VmaAllocation hAllocation,void ** ppData)17406 VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
17407 {
17408     if(hAllocation->CanBecomeLost())
17409     {
17410         return VK_ERROR_MEMORY_MAP_FAILED;
17411     }
17412 
17413     switch(hAllocation->GetType())
17414     {
17415     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17416         {
17417             VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17418             char *pBytes = VMA_NULL;
17419             VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
17420             if(res == VK_SUCCESS)
17421             {
17422                 *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
17423                 hAllocation->BlockAllocMap();
17424             }
17425             return res;
17426         }
17427     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17428         return hAllocation->DedicatedAllocMap(this, ppData);
17429     default:
17430         VMA_ASSERT(0);
17431         return VK_ERROR_MEMORY_MAP_FAILED;
17432     }
17433 }
17434 
Unmap(VmaAllocation hAllocation)17435 void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
17436 {
17437     switch(hAllocation->GetType())
17438     {
17439     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17440         {
17441             VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17442             hAllocation->BlockAllocUnmap();
17443             pBlock->Unmap(this, 1);
17444         }
17445         break;
17446     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17447         hAllocation->DedicatedAllocUnmap(this);
17448         break;
17449     default:
17450         VMA_ASSERT(0);
17451     }
17452 }
17453 
BindBufferMemory(VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkBuffer hBuffer,const void * pNext)17454 VkResult VmaAllocator_T::BindBufferMemory(
17455     VmaAllocation hAllocation,
17456     VkDeviceSize allocationLocalOffset,
17457     VkBuffer hBuffer,
17458     const void* pNext)
17459 {
17460     VkResult res = VK_SUCCESS;
17461     switch(hAllocation->GetType())
17462     {
17463     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17464         res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext);
17465         break;
17466     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17467     {
17468         VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17469         VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
17470         res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext);
17471         break;
17472     }
17473     default:
17474         VMA_ASSERT(0);
17475     }
17476     return res;
17477 }
17478 
BindImageMemory(VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkImage hImage,const void * pNext)17479 VkResult VmaAllocator_T::BindImageMemory(
17480     VmaAllocation hAllocation,
17481     VkDeviceSize allocationLocalOffset,
17482     VkImage hImage,
17483     const void* pNext)
17484 {
17485     VkResult res = VK_SUCCESS;
17486     switch(hAllocation->GetType())
17487     {
17488     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17489         res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext);
17490         break;
17491     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17492     {
17493         VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
17494         VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
17495         res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext);
17496         if (res == VK_SUCCESS) {
17497             pBlock->SetBindCompleteFlag(true);
17498         }
17499         break;
17500     }
17501     default:
17502         VMA_ASSERT(0);
17503     }
17504     return res;
17505 }
17506 
FlushOrInvalidateAllocation(VmaAllocation hAllocation,VkDeviceSize offset,VkDeviceSize size,VMA_CACHE_OPERATION op)17507 VkResult VmaAllocator_T::FlushOrInvalidateAllocation(
17508     VmaAllocation hAllocation,
17509     VkDeviceSize offset, VkDeviceSize size,
17510     VMA_CACHE_OPERATION op)
17511 {
17512     VkResult res = VK_SUCCESS;
17513 
17514     VkMappedMemoryRange memRange = {};
17515     if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange))
17516     {
17517         switch(op)
17518         {
17519         case VMA_CACHE_FLUSH:
17520             res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
17521             break;
17522         case VMA_CACHE_INVALIDATE:
17523             res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
17524             break;
17525         default:
17526             VMA_ASSERT(0);
17527         }
17528     }
17529     // else: Just ignore this call.
17530     return res;
17531 }
17532 
FlushOrInvalidateAllocations(uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes,VMA_CACHE_OPERATION op)17533 VkResult VmaAllocator_T::FlushOrInvalidateAllocations(
17534     uint32_t allocationCount,
17535     const VmaAllocation* allocations,
17536     const VkDeviceSize* offsets, const VkDeviceSize* sizes,
17537     VMA_CACHE_OPERATION op)
17538 {
17539     typedef VmaStlAllocator<VkMappedMemoryRange> RangeAllocator;
17540     typedef VmaSmallVector<VkMappedMemoryRange, RangeAllocator, 16> RangeVector;
17541     RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks()));
17542 
17543     for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
17544     {
17545         const VmaAllocation alloc = allocations[allocIndex];
17546         const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0;
17547         const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE;
17548         VkMappedMemoryRange newRange;
17549         if(GetFlushOrInvalidateRange(alloc, offset, size, newRange))
17550         {
17551             ranges.push_back(newRange);
17552         }
17553     }
17554 
17555     VkResult res = VK_SUCCESS;
17556     if(!ranges.empty())
17557     {
17558         switch(op)
17559         {
17560         case VMA_CACHE_FLUSH:
17561             res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17562             break;
17563         case VMA_CACHE_INVALIDATE:
17564             res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17565             break;
17566         default:
17567             VMA_ASSERT(0);
17568         }
17569     }
17570     // else: Just ignore this call.
17571     return res;
17572 }
17573 
FreeDedicatedMemory(const VmaAllocation allocation)17574 void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
17575 {
17576     VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
17577 
17578     const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17579     {
17580         VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17581         AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
17582         VMA_ASSERT(pDedicatedAllocations);
17583         bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
17584         VMA_ASSERT(success);
17585     }
17586 
17587     VkDeviceMemory hMemory = allocation->GetMemory();
17588 
17589     /*
17590     There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
17591     before vkFreeMemory.
17592 
17593     if(allocation->GetMappedData() != VMA_NULL)
17594     {
17595         (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
17596     }
17597     */
17598 
17599     FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
17600 
17601     VMA_DEBUG_LOG("    Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
17602 }
17603 
CalculateGpuDefragmentationMemoryTypeBits()17604 uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const
17605 {
17606     VkBufferCreateInfo dummyBufCreateInfo;
17607     VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo);
17608 
17609     uint32_t memoryTypeBits = 0;
17610 
17611     // Create buffer.
17612     VkBuffer buf = VK_NULL_HANDLE;
17613     VkResult res = (*GetVulkanFunctions().vkCreateBuffer)(
17614         m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf);
17615     if(res == VK_SUCCESS)
17616     {
17617         // Query for supported memory types.
17618         VkMemoryRequirements memReq;
17619         (*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq);
17620         memoryTypeBits = memReq.memoryTypeBits;
17621 
17622         // Destroy buffer.
17623         (*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks());
17624     }
17625 
17626     return memoryTypeBits;
17627 }
17628 
CalculateGlobalMemoryTypeBits()17629 uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const
17630 {
17631     // Make sure memory information is already fetched.
17632     VMA_ASSERT(GetMemoryTypeCount() > 0);
17633 
17634     uint32_t memoryTypeBits = UINT32_MAX;
17635 
17636     if(!m_UseAmdDeviceCoherentMemory)
17637     {
17638         // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD.
17639         for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17640         {
17641             if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
17642             {
17643                 memoryTypeBits &= ~(1u << memTypeIndex);
17644             }
17645         }
17646     }
17647 
17648     return memoryTypeBits;
17649 }
17650 
GetFlushOrInvalidateRange(VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size,VkMappedMemoryRange & outRange)17651 bool VmaAllocator_T::GetFlushOrInvalidateRange(
17652     VmaAllocation allocation,
17653     VkDeviceSize offset, VkDeviceSize size,
17654     VkMappedMemoryRange& outRange) const
17655 {
17656     const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17657     if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
17658     {
17659         const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
17660         const VkDeviceSize allocationSize = allocation->GetSize();
17661         VMA_ASSERT(offset <= allocationSize);
17662 
17663         outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
17664         outRange.pNext = VMA_NULL;
17665         outRange.memory = allocation->GetMemory();
17666 
17667         switch(allocation->GetType())
17668         {
17669         case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17670             outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17671             if(size == VK_WHOLE_SIZE)
17672             {
17673                 outRange.size = allocationSize - outRange.offset;
17674             }
17675             else
17676             {
17677                 VMA_ASSERT(offset + size <= allocationSize);
17678                 outRange.size = VMA_MIN(
17679                     VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize),
17680                     allocationSize - outRange.offset);
17681             }
17682             break;
17683         case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17684         {
17685             // 1. Still within this allocation.
17686             outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17687             if(size == VK_WHOLE_SIZE)
17688             {
17689                 size = allocationSize - offset;
17690             }
17691             else
17692             {
17693                 VMA_ASSERT(offset + size <= allocationSize);
17694             }
17695             outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize);
17696 
17697             // 2. Adjust to whole block.
17698             const VkDeviceSize allocationOffset = allocation->GetOffset();
17699             VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
17700             const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize();
17701             outRange.offset += allocationOffset;
17702             outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset);
17703 
17704             break;
17705         }
17706         default:
17707             VMA_ASSERT(0);
17708         }
17709         return true;
17710     }
17711     return false;
17712 }
17713 
17714 #if VMA_MEMORY_BUDGET
17715 
UpdateVulkanBudget()17716 void VmaAllocator_T::UpdateVulkanBudget()
17717 {
17718     VMA_ASSERT(m_UseExtMemoryBudget);
17719 
17720     VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR };
17721 
17722     VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };
17723     VmaPnextChainPushFront(&memProps, &budgetProps);
17724 
17725     GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps);
17726 
17727     {
17728         VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex);
17729 
17730         for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
17731         {
17732             m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex];
17733             m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex];
17734             m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load();
17735 
17736             // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size.
17737             if(m_Budget.m_VulkanBudget[heapIndex] == 0)
17738             {
17739                 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
17740             }
17741             else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size)
17742             {
17743                 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size;
17744             }
17745             if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0)
17746             {
17747                 m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
17748             }
17749         }
17750         m_Budget.m_OperationsSinceBudgetFetch = 0;
17751     }
17752 }
17753 
17754 #endif // #if VMA_MEMORY_BUDGET
17755 
FillAllocation(const VmaAllocation hAllocation,uint8_t pattern)17756 void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
17757 {
17758     if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
17759         !hAllocation->CanBecomeLost() &&
17760         (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
17761     {
17762         void* pData = VMA_NULL;
17763         VkResult res = Map(hAllocation, &pData);
17764         if(res == VK_SUCCESS)
17765         {
17766             memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
17767             FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
17768             Unmap(hAllocation);
17769         }
17770         else
17771         {
17772             VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
17773         }
17774     }
17775 }
17776 
GetGpuDefragmentationMemoryTypeBits()17777 uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits()
17778 {
17779     uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load();
17780     if(memoryTypeBits == UINT32_MAX)
17781     {
17782         memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits();
17783         m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits);
17784     }
17785     return memoryTypeBits;
17786 }
17787 
17788 #if VMA_STATS_STRING_ENABLED
17789 
PrintDetailedMap(VmaJsonWriter & json)17790 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
17791 {
17792     bool dedicatedAllocationsStarted = false;
17793     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17794     {
17795         VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17796         AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
17797         VMA_ASSERT(pDedicatedAllocVector);
17798         if(pDedicatedAllocVector->empty() == false)
17799         {
17800             if(dedicatedAllocationsStarted == false)
17801             {
17802                 dedicatedAllocationsStarted = true;
17803                 json.WriteString("DedicatedAllocations");
17804                 json.BeginObject();
17805             }
17806 
17807             json.BeginString("Type ");
17808             json.ContinueString(memTypeIndex);
17809             json.EndString();
17810 
17811             json.BeginArray();
17812 
17813             for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
17814             {
17815                 json.BeginObject(true);
17816                 const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
17817                 hAlloc->PrintParameters(json);
17818                 json.EndObject();
17819             }
17820 
17821             json.EndArray();
17822         }
17823     }
17824     if(dedicatedAllocationsStarted)
17825     {
17826         json.EndObject();
17827     }
17828 
17829     {
17830         bool allocationsStarted = false;
17831         for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17832         {
17833             if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
17834             {
17835                 if(allocationsStarted == false)
17836                 {
17837                     allocationsStarted = true;
17838                     json.WriteString("DefaultPools");
17839                     json.BeginObject();
17840                 }
17841 
17842                 json.BeginString("Type ");
17843                 json.ContinueString(memTypeIndex);
17844                 json.EndString();
17845 
17846                 m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
17847             }
17848         }
17849         if(allocationsStarted)
17850         {
17851             json.EndObject();
17852         }
17853     }
17854 
17855     // Custom pools
17856     {
17857         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
17858         const size_t poolCount = m_Pools.size();
17859         if(poolCount > 0)
17860         {
17861             json.WriteString("Pools");
17862             json.BeginObject();
17863             for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
17864             {
17865                 json.BeginString();
17866                 json.ContinueString(m_Pools[poolIndex]->GetId());
17867                 json.EndString();
17868 
17869                 m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
17870             }
17871             json.EndObject();
17872         }
17873     }
17874 }
17875 
17876 #endif // #if VMA_STATS_STRING_ENABLED
17877 
17878 ////////////////////////////////////////////////////////////////////////////////
17879 // Public interface
17880 
vmaCreateAllocator(const VmaAllocatorCreateInfo * pCreateInfo,VmaAllocator * pAllocator)17881 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
17882     const VmaAllocatorCreateInfo* pCreateInfo,
17883     VmaAllocator* pAllocator)
17884 {
17885     VMA_ASSERT(pCreateInfo && pAllocator);
17886     VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 ||
17887         (VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 2));
17888     VMA_DEBUG_LOG("vmaCreateAllocator");
17889     *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
17890     return (*pAllocator)->Init(pCreateInfo);
17891 }
17892 
vmaDestroyAllocator(VmaAllocator allocator)17893 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
17894     VmaAllocator allocator)
17895 {
17896     if(allocator != VK_NULL_HANDLE)
17897     {
17898         VMA_DEBUG_LOG("vmaDestroyAllocator");
17899         VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
17900         vma_delete(&allocationCallbacks, allocator);
17901     }
17902 }
17903 
vmaGetAllocatorInfo(VmaAllocator allocator,VmaAllocatorInfo * pAllocatorInfo)17904 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo)
17905 {
17906     VMA_ASSERT(allocator && pAllocatorInfo);
17907     pAllocatorInfo->instance = allocator->m_hInstance;
17908     pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice();
17909     pAllocatorInfo->device = allocator->m_hDevice;
17910 }
17911 
vmaGetPhysicalDeviceProperties(VmaAllocator allocator,const VkPhysicalDeviceProperties ** ppPhysicalDeviceProperties)17912 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
17913     VmaAllocator allocator,
17914     const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
17915 {
17916     VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
17917     *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
17918 }
17919 
vmaGetMemoryProperties(VmaAllocator allocator,const VkPhysicalDeviceMemoryProperties ** ppPhysicalDeviceMemoryProperties)17920 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
17921     VmaAllocator allocator,
17922     const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
17923 {
17924     VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
17925     *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
17926 }
17927 
vmaGetMemoryTypeProperties(VmaAllocator allocator,uint32_t memoryTypeIndex,VkMemoryPropertyFlags * pFlags)17928 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
17929     VmaAllocator allocator,
17930     uint32_t memoryTypeIndex,
17931     VkMemoryPropertyFlags* pFlags)
17932 {
17933     VMA_ASSERT(allocator && pFlags);
17934     VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
17935     *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
17936 }
17937 
vmaSetCurrentFrameIndex(VmaAllocator allocator,uint32_t frameIndex)17938 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
17939     VmaAllocator allocator,
17940     uint32_t frameIndex)
17941 {
17942     VMA_ASSERT(allocator);
17943     VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
17944 
17945     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17946 
17947     allocator->SetCurrentFrameIndex(frameIndex);
17948 }
17949 
vmaCalculateStats(VmaAllocator allocator,VmaStats * pStats)17950 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
17951     VmaAllocator allocator,
17952     VmaStats* pStats)
17953 {
17954     VMA_ASSERT(allocator && pStats);
17955     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17956     allocator->CalculateStats(pStats);
17957 }
17958 
vmaFreeEmptyBlock(VmaAllocator allocator)17959 VMA_CALL_PRE void VMA_CALL_POST vmaFreeEmptyBlock(
17960     VmaAllocator allocator)
17961 {
17962     VMA_ASSERT(allocator);
17963     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17964     allocator->FreeEmptyBlock();
17965 }
17966 
vmaGetBudget(VmaAllocator allocator,VmaBudget * pBudget)17967 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
17968     VmaAllocator allocator,
17969     VmaBudget* pBudget)
17970 {
17971     VMA_ASSERT(allocator && pBudget);
17972     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17973     allocator->GetBudget(pBudget, 0, allocator->GetMemoryHeapCount());
17974 }
17975 
17976 #if VMA_STATS_STRING_ENABLED
17977 
vmaBuildStatsString(VmaAllocator allocator,char ** ppStatsString,VkBool32 detailedMap)17978 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
17979     VmaAllocator allocator,
17980     char** ppStatsString,
17981     VkBool32 detailedMap)
17982 {
17983     VMA_ASSERT(allocator && ppStatsString);
17984     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17985 
17986     VmaStringBuilder sb(allocator);
17987     {
17988         VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
17989         json.BeginObject();
17990 
17991         VmaBudget budget[VK_MAX_MEMORY_HEAPS];
17992         allocator->GetBudget(budget, 0, allocator->GetMemoryHeapCount());
17993 
17994         VmaStats stats;
17995         allocator->CalculateStats(&stats);
17996 
17997         json.WriteString("Total");
17998         VmaPrintStatInfo(json, stats.total);
17999 
18000         for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
18001         {
18002             json.BeginString("Heap ");
18003             json.ContinueString(heapIndex);
18004             json.EndString();
18005             json.BeginObject();
18006 
18007             json.WriteString("Size");
18008             json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
18009 
18010             json.WriteString("Flags");
18011             json.BeginArray(true);
18012             if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
18013             {
18014                 json.WriteString("DEVICE_LOCAL");
18015             }
18016             json.EndArray();
18017 
18018             json.WriteString("Budget");
18019             json.BeginObject();
18020             {
18021                 json.WriteString("BlockBytes");
18022                 json.WriteNumber(budget[heapIndex].blockBytes);
18023                 json.WriteString("AllocationBytes");
18024                 json.WriteNumber(budget[heapIndex].allocationBytes);
18025                 json.WriteString("Usage");
18026                 json.WriteNumber(budget[heapIndex].usage);
18027                 json.WriteString("Budget");
18028                 json.WriteNumber(budget[heapIndex].budget);
18029             }
18030             json.EndObject();
18031 
18032             if(stats.memoryHeap[heapIndex].blockCount > 0)
18033             {
18034                 json.WriteString("Stats");
18035                 VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
18036             }
18037 
18038             for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
18039             {
18040                 if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
18041                 {
18042                     json.BeginString("Type ");
18043                     json.ContinueString(typeIndex);
18044                     json.EndString();
18045 
18046                     json.BeginObject();
18047 
18048                     json.WriteString("Flags");
18049                     json.BeginArray(true);
18050                     VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
18051                     if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
18052                     {
18053                         json.WriteString("DEVICE_LOCAL");
18054                     }
18055                     if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
18056                     {
18057                         json.WriteString("HOST_VISIBLE");
18058                     }
18059                     if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
18060                     {
18061                         json.WriteString("HOST_COHERENT");
18062                     }
18063                     if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
18064                     {
18065                         json.WriteString("HOST_CACHED");
18066                     }
18067                     if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
18068                     {
18069                         json.WriteString("LAZILY_ALLOCATED");
18070                     }
18071                     if((flags & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0)
18072                     {
18073                         json.WriteString(" PROTECTED");
18074                     }
18075                     if((flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
18076                     {
18077                         json.WriteString(" DEVICE_COHERENT");
18078                     }
18079                     if((flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY) != 0)
18080                     {
18081                         json.WriteString(" DEVICE_UNCACHED");
18082                     }
18083                     json.EndArray();
18084 
18085                     if(stats.memoryType[typeIndex].blockCount > 0)
18086                     {
18087                         json.WriteString("Stats");
18088                         VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
18089                     }
18090 
18091                     json.EndObject();
18092                 }
18093             }
18094 
18095             json.EndObject();
18096         }
18097         if(detailedMap == VK_TRUE)
18098         {
18099             allocator->PrintDetailedMap(json);
18100         }
18101 
18102         json.EndObject();
18103     }
18104 
18105     const size_t len = sb.GetLength();
18106     char* const pChars = vma_new_array(allocator, char, len + 1);
18107     if(len > 0)
18108     {
18109         memcpy(pChars, sb.GetData(), len);
18110     }
18111     pChars[len] = '\0';
18112     *ppStatsString = pChars;
18113 }
18114 
vmaFreeStatsString(VmaAllocator allocator,char * pStatsString)18115 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
18116     VmaAllocator allocator,
18117     char* pStatsString)
18118 {
18119     if(pStatsString != VMA_NULL)
18120     {
18121         VMA_ASSERT(allocator);
18122         size_t len = strlen(pStatsString);
18123         vma_delete_array(allocator, pStatsString, len + 1);
18124     }
18125 }
18126 
18127 #endif // #if VMA_STATS_STRING_ENABLED
18128 
18129 /*
18130 This function is not protected by any mutex because it just reads immutable data.
18131 */
vmaFindMemoryTypeIndex(VmaAllocator allocator,uint32_t memoryTypeBits,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)18132 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
18133     VmaAllocator allocator,
18134     uint32_t memoryTypeBits,
18135     const VmaAllocationCreateInfo* pAllocationCreateInfo,
18136     uint32_t* pMemoryTypeIndex)
18137 {
18138     VMA_ASSERT(allocator != VK_NULL_HANDLE);
18139     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18140     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18141 
18142     memoryTypeBits &= allocator->GetGlobalMemoryTypeBits();
18143 
18144     if(pAllocationCreateInfo->memoryTypeBits != 0)
18145     {
18146         memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
18147     }
18148 
18149     uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
18150     uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
18151     uint32_t notPreferredFlags = 0;
18152 
18153     // Convert usage to requiredFlags and preferredFlags.
18154     switch(pAllocationCreateInfo->usage)
18155     {
18156     case VMA_MEMORY_USAGE_UNKNOWN:
18157         break;
18158     case VMA_MEMORY_USAGE_GPU_ONLY:
18159         if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
18160         {
18161             preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18162         }
18163         break;
18164     case VMA_MEMORY_USAGE_CPU_ONLY:
18165         requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
18166         break;
18167     case VMA_MEMORY_USAGE_CPU_TO_GPU:
18168         requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
18169         if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
18170         {
18171             preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18172         }
18173         break;
18174     case VMA_MEMORY_USAGE_GPU_TO_CPU:
18175         requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
18176         preferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
18177         break;
18178     case VMA_MEMORY_USAGE_CPU_COPY:
18179         notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18180         break;
18181     case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED:
18182         requiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
18183         break;
18184     default:
18185         VMA_ASSERT(0);
18186         break;
18187     }
18188 
18189     // Avoid DEVICE_COHERENT unless explicitly requested.
18190     if(((pAllocationCreateInfo->requiredFlags | pAllocationCreateInfo->preferredFlags) &
18191         (VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0)
18192     {
18193         notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY;
18194     }
18195 
18196     *pMemoryTypeIndex = UINT32_MAX;
18197     uint32_t minCost = UINT32_MAX;
18198     for(uint32_t memTypeIndex = 0, memTypeBit = 1;
18199         memTypeIndex < allocator->GetMemoryTypeCount();
18200         ++memTypeIndex, memTypeBit <<= 1)
18201     {
18202         // This memory type is acceptable according to memoryTypeBits bitmask.
18203         if((memTypeBit & memoryTypeBits) != 0)
18204         {
18205             const VkMemoryPropertyFlags currFlags =
18206                 allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
18207             // This memory type contains requiredFlags.
18208             if((requiredFlags & ~currFlags) == 0)
18209             {
18210                 // Calculate cost as number of bits from preferredFlags not present in this memory type.
18211                 uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags) +
18212                     VmaCountBitsSet(currFlags & notPreferredFlags);
18213                 // Remember memory type with lowest cost.
18214                 if(currCost < minCost)
18215                 {
18216                     *pMemoryTypeIndex = memTypeIndex;
18217                     if(currCost == 0)
18218                     {
18219                         return VK_SUCCESS;
18220                     }
18221                     minCost = currCost;
18222                 }
18223             }
18224         }
18225     }
18226     return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
18227 }
18228 
vmaFindMemoryTypeIndexForBufferInfo(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)18229 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
18230     VmaAllocator allocator,
18231     const VkBufferCreateInfo* pBufferCreateInfo,
18232     const VmaAllocationCreateInfo* pAllocationCreateInfo,
18233     uint32_t* pMemoryTypeIndex)
18234 {
18235     VMA_ASSERT(allocator != VK_NULL_HANDLE);
18236     VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
18237     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18238     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18239 
18240     const VkDevice hDev = allocator->m_hDevice;
18241     VkBuffer hBuffer = VK_NULL_HANDLE;
18242     VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
18243         hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
18244     if(res == VK_SUCCESS)
18245     {
18246         VkMemoryRequirements memReq = {};
18247         allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
18248             hDev, hBuffer, &memReq);
18249 
18250         res = vmaFindMemoryTypeIndex(
18251             allocator,
18252             memReq.memoryTypeBits,
18253             pAllocationCreateInfo,
18254             pMemoryTypeIndex);
18255 
18256         allocator->GetVulkanFunctions().vkDestroyBuffer(
18257             hDev, hBuffer, allocator->GetAllocationCallbacks());
18258     }
18259     return res;
18260 }
18261 
vmaFindMemoryTypeIndexForImageInfo(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)18262 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
18263     VmaAllocator allocator,
18264     const VkImageCreateInfo* pImageCreateInfo,
18265     const VmaAllocationCreateInfo* pAllocationCreateInfo,
18266     uint32_t* pMemoryTypeIndex)
18267 {
18268     VMA_ASSERT(allocator != VK_NULL_HANDLE);
18269     VMA_ASSERT(pImageCreateInfo != VMA_NULL);
18270     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18271     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18272 
18273     const VkDevice hDev = allocator->m_hDevice;
18274     VkImage hImage = VK_NULL_HANDLE;
18275     VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
18276         hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
18277     if(res == VK_SUCCESS)
18278     {
18279         VkMemoryRequirements memReq = {};
18280         allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
18281             hDev, hImage, &memReq);
18282 
18283         res = vmaFindMemoryTypeIndex(
18284             allocator,
18285             memReq.memoryTypeBits,
18286             pAllocationCreateInfo,
18287             pMemoryTypeIndex);
18288 
18289         allocator->GetVulkanFunctions().vkDestroyImage(
18290             hDev, hImage, allocator->GetAllocationCallbacks());
18291     }
18292     return res;
18293 }
18294 
vmaCreatePool(VmaAllocator allocator,const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)18295 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
18296     VmaAllocator allocator,
18297     const VmaPoolCreateInfo* pCreateInfo,
18298     VmaPool* pPool)
18299 {
18300     VMA_ASSERT(allocator && pCreateInfo && pPool);
18301 
18302     VMA_DEBUG_LOG("vmaCreatePool");
18303 
18304     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18305 
18306     VkResult res = allocator->CreatePool(pCreateInfo, pPool);
18307 
18308 #if VMA_RECORDING_ENABLED
18309     if(allocator->GetRecorder() != VMA_NULL)
18310     {
18311         allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
18312     }
18313 #endif
18314 
18315     return res;
18316 }
18317 
vmaDestroyPool(VmaAllocator allocator,VmaPool pool)18318 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
18319     VmaAllocator allocator,
18320     VmaPool pool)
18321 {
18322     VMA_ASSERT(allocator);
18323 
18324     if(pool == VK_NULL_HANDLE)
18325     {
18326         return;
18327     }
18328 
18329     VMA_DEBUG_LOG("vmaDestroyPool");
18330 
18331     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18332 
18333 #if VMA_RECORDING_ENABLED
18334     if(allocator->GetRecorder() != VMA_NULL)
18335     {
18336         allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
18337     }
18338 #endif
18339 
18340     allocator->DestroyPool(pool);
18341 }
18342 
vmaGetPoolStats(VmaAllocator allocator,VmaPool pool,VmaPoolStats * pPoolStats)18343 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
18344     VmaAllocator allocator,
18345     VmaPool pool,
18346     VmaPoolStats* pPoolStats)
18347 {
18348     VMA_ASSERT(allocator && pool && pPoolStats);
18349 
18350     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18351 
18352     allocator->GetPoolStats(pool, pPoolStats);
18353 }
18354 
vmaMakePoolAllocationsLost(VmaAllocator allocator,VmaPool pool,size_t * pLostAllocationCount)18355 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
18356     VmaAllocator allocator,
18357     VmaPool pool,
18358     size_t* pLostAllocationCount)
18359 {
18360     VMA_ASSERT(allocator && pool);
18361 
18362     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18363 
18364 #if VMA_RECORDING_ENABLED
18365     if(allocator->GetRecorder() != VMA_NULL)
18366     {
18367         allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
18368     }
18369 #endif
18370 
18371     allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
18372 }
18373 
vmaCheckPoolCorruption(VmaAllocator allocator,VmaPool pool)18374 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
18375 {
18376     VMA_ASSERT(allocator && pool);
18377 
18378     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18379 
18380     VMA_DEBUG_LOG("vmaCheckPoolCorruption");
18381 
18382     return allocator->CheckPoolCorruption(pool);
18383 }
18384 
vmaGetPoolName(VmaAllocator allocator,VmaPool pool,const char ** ppName)18385 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
18386     VmaAllocator allocator,
18387     VmaPool pool,
18388     const char** ppName)
18389 {
18390     VMA_ASSERT(allocator && pool && ppName);
18391 
18392     VMA_DEBUG_LOG("vmaGetPoolName");
18393 
18394     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18395 
18396     *ppName = pool->GetName();
18397 }
18398 
vmaSetPoolName(VmaAllocator allocator,VmaPool pool,const char * pName)18399 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
18400     VmaAllocator allocator,
18401     VmaPool pool,
18402     const char* pName)
18403 {
18404     VMA_ASSERT(allocator && pool);
18405 
18406     VMA_DEBUG_LOG("vmaSetPoolName");
18407 
18408     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18409 
18410     pool->SetName(pName);
18411 
18412 #if VMA_RECORDING_ENABLED
18413     if(allocator->GetRecorder() != VMA_NULL)
18414     {
18415         allocator->GetRecorder()->RecordSetPoolName(allocator->GetCurrentFrameIndex(), pool, pName);
18416     }
18417 #endif
18418 }
18419 
vmaAllocateMemory(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18420 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
18421     VmaAllocator allocator,
18422     const VkMemoryRequirements* pVkMemoryRequirements,
18423     const VmaAllocationCreateInfo* pCreateInfo,
18424     VmaAllocation* pAllocation,
18425     VmaAllocationInfo* pAllocationInfo)
18426 {
18427     VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
18428 
18429     VMA_DEBUG_LOG("vmaAllocateMemory");
18430 
18431     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18432 
18433     VkResult result = allocator->AllocateMemory(
18434         *pVkMemoryRequirements,
18435         false, // requiresDedicatedAllocation
18436         false, // prefersDedicatedAllocation
18437         VK_NULL_HANDLE, // dedicatedBuffer
18438         UINT32_MAX, // dedicatedBufferUsage
18439         VK_NULL_HANDLE, // dedicatedImage
18440         *pCreateInfo,
18441         VMA_SUBALLOCATION_TYPE_UNKNOWN,
18442         1, // allocationCount
18443         pAllocation);
18444 
18445 #if VMA_RECORDING_ENABLED
18446     if(allocator->GetRecorder() != VMA_NULL)
18447     {
18448         allocator->GetRecorder()->RecordAllocateMemory(
18449             allocator->GetCurrentFrameIndex(),
18450             *pVkMemoryRequirements,
18451             *pCreateInfo,
18452             *pAllocation);
18453     }
18454 #endif
18455 
18456     if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18457     {
18458         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18459     }
18460 
18461     return result;
18462 }
18463 
vmaAllocateMemoryPages(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,size_t allocationCount,VmaAllocation * pAllocations,VmaAllocationInfo * pAllocationInfo)18464 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
18465     VmaAllocator allocator,
18466     const VkMemoryRequirements* pVkMemoryRequirements,
18467     const VmaAllocationCreateInfo* pCreateInfo,
18468     size_t allocationCount,
18469     VmaAllocation* pAllocations,
18470     VmaAllocationInfo* pAllocationInfo)
18471 {
18472     if(allocationCount == 0)
18473     {
18474         return VK_SUCCESS;
18475     }
18476 
18477     VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
18478 
18479     VMA_DEBUG_LOG("vmaAllocateMemoryPages");
18480 
18481     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18482 
18483     VkResult result = allocator->AllocateMemory(
18484         *pVkMemoryRequirements,
18485         false, // requiresDedicatedAllocation
18486         false, // prefersDedicatedAllocation
18487         VK_NULL_HANDLE, // dedicatedBuffer
18488         UINT32_MAX, // dedicatedBufferUsage
18489         VK_NULL_HANDLE, // dedicatedImage
18490         *pCreateInfo,
18491         VMA_SUBALLOCATION_TYPE_UNKNOWN,
18492         allocationCount,
18493         pAllocations);
18494 
18495 #if VMA_RECORDING_ENABLED
18496     if(allocator->GetRecorder() != VMA_NULL)
18497     {
18498         allocator->GetRecorder()->RecordAllocateMemoryPages(
18499             allocator->GetCurrentFrameIndex(),
18500             *pVkMemoryRequirements,
18501             *pCreateInfo,
18502             (uint64_t)allocationCount,
18503             pAllocations);
18504     }
18505 #endif
18506 
18507     if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18508     {
18509         for(size_t i = 0; i < allocationCount; ++i)
18510         {
18511             allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
18512         }
18513     }
18514 
18515     return result;
18516 }
18517 
vmaAllocateMemoryForBuffer(VmaAllocator allocator,VkBuffer buffer,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18518 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
18519     VmaAllocator allocator,
18520     VkBuffer buffer,
18521     const VmaAllocationCreateInfo* pCreateInfo,
18522     VmaAllocation* pAllocation,
18523     VmaAllocationInfo* pAllocationInfo)
18524 {
18525     VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18526 
18527     VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
18528 
18529     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18530 
18531     VkMemoryRequirements vkMemReq = {};
18532     bool requiresDedicatedAllocation = false;
18533     bool prefersDedicatedAllocation = false;
18534     allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
18535         requiresDedicatedAllocation,
18536         prefersDedicatedAllocation);
18537 
18538     VkResult result = allocator->AllocateMemory(
18539         vkMemReq,
18540         requiresDedicatedAllocation,
18541         prefersDedicatedAllocation,
18542         buffer, // dedicatedBuffer
18543         UINT32_MAX, // dedicatedBufferUsage
18544         VK_NULL_HANDLE, // dedicatedImage
18545         *pCreateInfo,
18546         VMA_SUBALLOCATION_TYPE_BUFFER,
18547         1, // allocationCount
18548         pAllocation);
18549 
18550 #if VMA_RECORDING_ENABLED
18551     if(allocator->GetRecorder() != VMA_NULL)
18552     {
18553         allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
18554             allocator->GetCurrentFrameIndex(),
18555             vkMemReq,
18556             requiresDedicatedAllocation,
18557             prefersDedicatedAllocation,
18558             *pCreateInfo,
18559             *pAllocation);
18560     }
18561 #endif
18562 
18563     if(pAllocationInfo && result == VK_SUCCESS)
18564     {
18565         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18566     }
18567 
18568     return result;
18569 }
18570 
vmaAllocateMemoryForImage(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18571 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
18572     VmaAllocator allocator,
18573     VkImage image,
18574     const VmaAllocationCreateInfo* pCreateInfo,
18575     VmaAllocation* pAllocation,
18576     VmaAllocationInfo* pAllocationInfo)
18577 {
18578     VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18579 
18580     VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
18581 
18582     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18583 
18584     VkMemoryRequirements vkMemReq = {};
18585     bool requiresDedicatedAllocation = false;
18586     bool prefersDedicatedAllocation  = false;
18587     allocator->GetImageMemoryRequirements(image, vkMemReq,
18588         requiresDedicatedAllocation, prefersDedicatedAllocation);
18589 
18590     VkResult result = allocator->AllocateMemory(
18591         vkMemReq,
18592         requiresDedicatedAllocation,
18593         prefersDedicatedAllocation,
18594         VK_NULL_HANDLE, // dedicatedBuffer
18595         UINT32_MAX, // dedicatedBufferUsage
18596         image, // dedicatedImage
18597         *pCreateInfo,
18598         VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
18599         1, // allocationCount
18600         pAllocation);
18601 
18602 #if VMA_RECORDING_ENABLED
18603     if(allocator->GetRecorder() != VMA_NULL)
18604     {
18605         allocator->GetRecorder()->RecordAllocateMemoryForImage(
18606             allocator->GetCurrentFrameIndex(),
18607             vkMemReq,
18608             requiresDedicatedAllocation,
18609             prefersDedicatedAllocation,
18610             *pCreateInfo,
18611             *pAllocation);
18612     }
18613 #endif
18614 
18615     if(pAllocationInfo && result == VK_SUCCESS)
18616     {
18617         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18618     }
18619 
18620     return result;
18621 }
18622 
vmaFreeMemory(VmaAllocator allocator,VmaAllocation allocation)18623 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
18624     VmaAllocator allocator,
18625     VmaAllocation allocation)
18626 {
18627     VMA_ASSERT(allocator);
18628 
18629     if(allocation == VK_NULL_HANDLE)
18630     {
18631         return;
18632     }
18633 
18634     VMA_DEBUG_LOG("vmaFreeMemory");
18635 
18636     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18637 
18638 #if VMA_RECORDING_ENABLED
18639     if(allocator->GetRecorder() != VMA_NULL)
18640     {
18641         allocator->GetRecorder()->RecordFreeMemory(
18642             allocator->GetCurrentFrameIndex(),
18643             allocation);
18644     }
18645 #endif
18646 
18647     allocator->FreeMemory(
18648         1, // allocationCount
18649         &allocation);
18650 }
18651 
vmaFreeMemoryPages(VmaAllocator allocator,size_t allocationCount,const VmaAllocation * pAllocations)18652 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
18653     VmaAllocator allocator,
18654     size_t allocationCount,
18655     const VmaAllocation* pAllocations)
18656 {
18657     if(allocationCount == 0)
18658     {
18659         return;
18660     }
18661 
18662     VMA_ASSERT(allocator);
18663 
18664     VMA_DEBUG_LOG("vmaFreeMemoryPages");
18665 
18666     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18667 
18668 #if VMA_RECORDING_ENABLED
18669     if(allocator->GetRecorder() != VMA_NULL)
18670     {
18671         allocator->GetRecorder()->RecordFreeMemoryPages(
18672             allocator->GetCurrentFrameIndex(),
18673             (uint64_t)allocationCount,
18674             pAllocations);
18675     }
18676 #endif
18677 
18678     allocator->FreeMemory(allocationCount, pAllocations);
18679 }
18680 
18681 // 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)18682 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateReservedMemoryForImage(
18683     VmaAllocator VMA_NOT_NULL allocator,
18684     VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
18685     const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
18686     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
18687     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo)
18688 {
18689     VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18690     VMA_DEBUG_LOG("vmaAllocateReservedMemoryForImage");
18691     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18692 
18693     VkMemoryRequirements vkMemReq = {};
18694     bool requiresDedicatedAllocation = false;
18695     bool prefersDedicatedAllocation  = false;
18696     allocator->GetImageMemoryRequirements(image, vkMemReq,
18697         requiresDedicatedAllocation, prefersDedicatedAllocation);
18698 
18699     VkResult result = allocator->AllocateReservedMemory(
18700         vkMemReq,
18701         requiresDedicatedAllocation,
18702         prefersDedicatedAllocation,
18703         VK_NULL_HANDLE, // dedicatedBuffer
18704         UINT32_MAX, // dedicatedBufferUsage
18705         image, // dedicatedImage
18706         *pCreateInfo,
18707         VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
18708         1, // allocationCount
18709         pAllocation);
18710 
18711     if(pAllocationInfo && result == VK_SUCCESS)
18712     {
18713         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18714     }
18715 
18716     return result;
18717 }
18718 
vmaGetNewBlockStats(VmaAllocation allocation,bool * pStats)18719 VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetNewBlockStats(
18720     VmaAllocation allocation,
18721     bool* pStats)
18722 {
18723     VMA_ASSERT(allocation);
18724     VMA_DEBUG_LOG("vmaGetNewBlockStats");
18725     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18726 
18727     if (pStats != NULL) {
18728         *pStats = allocation->IsNewBlockFlag();
18729     }
18730     return VK_SUCCESS;
18731 }
18732 
vmaClearNewBlockStats(VmaAllocation allocation)18733 VMA_CALL_PRE VkResult VMA_CALL_POST vmaClearNewBlockStats(
18734     VmaAllocation allocation)
18735 {
18736     VMA_ASSERT(allocation);
18737     VMA_DEBUG_LOG("vmaClearNewBlockStats");
18738     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18739 
18740     allocation->ClearNewBlockFlag();
18741     return VK_SUCCESS;
18742 }
18743 
vmaSwapReservedBlock(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18744 VMA_CALL_PRE VkResult VMA_CALL_POST vmaSwapReservedBlock(
18745     VmaAllocator allocator,
18746     VkImage image,
18747     const VmaAllocationCreateInfo* pCreateInfo,
18748     VmaAllocation* pAllocation,
18749     VmaAllocationInfo* pAllocationInfo)
18750 {
18751     VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18752     VMA_DEBUG_LOG("vmaSwapReservedBlock");
18753     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18754 
18755     return allocator->SwapReservedBlock(image, pCreateInfo, pAllocation, pAllocationInfo);
18756 }
18757 
vmaFreeReservedMemory(VmaAllocator allocator,VmaAllocation allocation)18758 VMA_CALL_PRE void VMA_CALL_POST vmaFreeReservedMemory(
18759     VmaAllocator allocator,
18760     VmaAllocation allocation)
18761 {
18762     VMA_ASSERT(allocator);
18763     if (allocation == VK_NULL_HANDLE)
18764     {
18765         return;
18766     }
18767     VMA_DEBUG_LOG("vmaFreeReservedMemory");
18768     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18769 
18770     allocator->FreeReservedMemory(
18771         1, // allocationCount
18772         &allocation);
18773 }
18774 
vmaGetPreAllocBlockSize(VmaAllocator allocator,uint32_t * pStats)18775 VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetPreAllocBlockSize(VmaAllocator allocator, uint32_t* pStats)
18776 {
18777     VMA_ASSERT(allocator);
18778     VMA_DEBUG_LOG("vmaGetPreAllocBlockSize");
18779 
18780     if (pStats != NULL) {
18781         *pStats = allocator->GetPreAllocBlockSize();
18782     }
18783     return VK_SUCCESS;
18784 }
18785 
vmaResizeAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize newSize)18786 VMA_CALL_PRE VkResult VMA_CALL_POST vmaResizeAllocation(
18787     VmaAllocator allocator,
18788     VmaAllocation allocation,
18789     VkDeviceSize newSize)
18790 {
18791     VMA_ASSERT(allocator && allocation);
18792 
18793     VMA_DEBUG_LOG("vmaResizeAllocation");
18794 
18795     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18796 
18797     return allocator->ResizeAllocation(allocation, newSize);
18798 }
18799 
vmaGetAllocationInfo(VmaAllocator allocator,VmaAllocation allocation,VmaAllocationInfo * pAllocationInfo)18800 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
18801     VmaAllocator allocator,
18802     VmaAllocation allocation,
18803     VmaAllocationInfo* pAllocationInfo)
18804 {
18805     VMA_ASSERT(allocator && allocation && pAllocationInfo);
18806 
18807     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18808 
18809 #if VMA_RECORDING_ENABLED
18810     if(allocator->GetRecorder() != VMA_NULL)
18811     {
18812         allocator->GetRecorder()->RecordGetAllocationInfo(
18813             allocator->GetCurrentFrameIndex(),
18814             allocation);
18815     }
18816 #endif
18817 
18818     allocator->GetAllocationInfo(allocation, pAllocationInfo);
18819 }
18820 
vmaTouchAllocation(VmaAllocator allocator,VmaAllocation allocation)18821 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
18822     VmaAllocator allocator,
18823     VmaAllocation allocation)
18824 {
18825     VMA_ASSERT(allocator && allocation);
18826 
18827     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18828 
18829 #if VMA_RECORDING_ENABLED
18830     if(allocator->GetRecorder() != VMA_NULL)
18831     {
18832         allocator->GetRecorder()->RecordTouchAllocation(
18833             allocator->GetCurrentFrameIndex(),
18834             allocation);
18835     }
18836 #endif
18837 
18838     return allocator->TouchAllocation(allocation);
18839 }
18840 
vmaSetAllocationUserData(VmaAllocator allocator,VmaAllocation allocation,void * pUserData)18841 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
18842     VmaAllocator allocator,
18843     VmaAllocation allocation,
18844     void* pUserData)
18845 {
18846     VMA_ASSERT(allocator && allocation);
18847 
18848     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18849 
18850     allocation->SetUserData(allocator, pUserData);
18851 
18852 #if VMA_RECORDING_ENABLED
18853     if(allocator->GetRecorder() != VMA_NULL)
18854     {
18855         allocator->GetRecorder()->RecordSetAllocationUserData(
18856             allocator->GetCurrentFrameIndex(),
18857             allocation,
18858             pUserData);
18859     }
18860 #endif
18861 }
18862 
vmaCreateLostAllocation(VmaAllocator allocator,VmaAllocation * pAllocation)18863 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
18864     VmaAllocator allocator,
18865     VmaAllocation* pAllocation)
18866 {
18867     VMA_ASSERT(allocator && pAllocation);
18868 
18869     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18870 
18871     allocator->CreateLostAllocation(pAllocation);
18872 
18873 #if VMA_RECORDING_ENABLED
18874     if(allocator->GetRecorder() != VMA_NULL)
18875     {
18876         allocator->GetRecorder()->RecordCreateLostAllocation(
18877             allocator->GetCurrentFrameIndex(),
18878             *pAllocation);
18879     }
18880 #endif
18881 }
18882 
vmaMapMemory(VmaAllocator allocator,VmaAllocation allocation,void ** ppData)18883 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
18884     VmaAllocator allocator,
18885     VmaAllocation allocation,
18886     void** ppData)
18887 {
18888     VMA_ASSERT(allocator && allocation && ppData);
18889 
18890     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18891 
18892     VkResult res = allocator->Map(allocation, ppData);
18893 
18894 #if VMA_RECORDING_ENABLED
18895     if(allocator->GetRecorder() != VMA_NULL)
18896     {
18897         allocator->GetRecorder()->RecordMapMemory(
18898             allocator->GetCurrentFrameIndex(),
18899             allocation);
18900     }
18901 #endif
18902 
18903     return res;
18904 }
18905 
vmaUnmapMemory(VmaAllocator allocator,VmaAllocation allocation)18906 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
18907     VmaAllocator allocator,
18908     VmaAllocation allocation)
18909 {
18910     VMA_ASSERT(allocator && allocation);
18911 
18912     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18913 
18914 #if VMA_RECORDING_ENABLED
18915     if(allocator->GetRecorder() != VMA_NULL)
18916     {
18917         allocator->GetRecorder()->RecordUnmapMemory(
18918             allocator->GetCurrentFrameIndex(),
18919             allocation);
18920     }
18921 #endif
18922 
18923     allocator->Unmap(allocation);
18924 }
18925 
vmaFlushAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)18926 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18927 {
18928     VMA_ASSERT(allocator && allocation);
18929 
18930     VMA_DEBUG_LOG("vmaFlushAllocation");
18931 
18932     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18933 
18934     const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
18935 
18936 #if VMA_RECORDING_ENABLED
18937     if(allocator->GetRecorder() != VMA_NULL)
18938     {
18939         allocator->GetRecorder()->RecordFlushAllocation(
18940             allocator->GetCurrentFrameIndex(),
18941             allocation, offset, size);
18942     }
18943 #endif
18944 
18945     return res;
18946 }
18947 
vmaInvalidateAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)18948 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18949 {
18950     VMA_ASSERT(allocator && allocation);
18951 
18952     VMA_DEBUG_LOG("vmaInvalidateAllocation");
18953 
18954     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18955 
18956     const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
18957 
18958 #if VMA_RECORDING_ENABLED
18959     if(allocator->GetRecorder() != VMA_NULL)
18960     {
18961         allocator->GetRecorder()->RecordInvalidateAllocation(
18962             allocator->GetCurrentFrameIndex(),
18963             allocation, offset, size);
18964     }
18965 #endif
18966 
18967     return res;
18968 }
18969 
vmaFlushAllocations(VmaAllocator allocator,uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes)18970 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
18971     VmaAllocator allocator,
18972     uint32_t allocationCount,
18973     const VmaAllocation* allocations,
18974     const VkDeviceSize* offsets,
18975     const VkDeviceSize* sizes)
18976 {
18977     VMA_ASSERT(allocator);
18978 
18979     if(allocationCount == 0)
18980     {
18981         return VK_SUCCESS;
18982     }
18983 
18984     VMA_ASSERT(allocations);
18985 
18986     VMA_DEBUG_LOG("vmaFlushAllocations");
18987 
18988     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18989 
18990     const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH);
18991 
18992 #if VMA_RECORDING_ENABLED
18993     if(allocator->GetRecorder() != VMA_NULL)
18994     {
18995         //TODO
18996     }
18997 #endif
18998 
18999     return res;
19000 }
19001 
vmaInvalidateAllocations(VmaAllocator allocator,uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes)19002 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
19003     VmaAllocator allocator,
19004     uint32_t allocationCount,
19005     const VmaAllocation* allocations,
19006     const VkDeviceSize* offsets,
19007     const VkDeviceSize* sizes)
19008 {
19009     VMA_ASSERT(allocator);
19010 
19011     if(allocationCount == 0)
19012     {
19013         return VK_SUCCESS;
19014     }
19015 
19016     VMA_ASSERT(allocations);
19017 
19018     VMA_DEBUG_LOG("vmaInvalidateAllocations");
19019 
19020     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19021 
19022     const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE);
19023 
19024 #if VMA_RECORDING_ENABLED
19025     if(allocator->GetRecorder() != VMA_NULL)
19026     {
19027         //TODO
19028     }
19029 #endif
19030 
19031     return res;
19032 }
19033 
vmaCheckCorruption(VmaAllocator allocator,uint32_t memoryTypeBits)19034 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
19035 {
19036     VMA_ASSERT(allocator);
19037 
19038     VMA_DEBUG_LOG("vmaCheckCorruption");
19039 
19040     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19041 
19042     return allocator->CheckCorruption(memoryTypeBits);
19043 }
19044 
vmaDefragment(VmaAllocator allocator,const VmaAllocation * pAllocations,size_t allocationCount,VkBool32 * pAllocationsChanged,const VmaDefragmentationInfo * pDefragmentationInfo,VmaDefragmentationStats * pDefragmentationStats)19045 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
19046     VmaAllocator allocator,
19047     const VmaAllocation* pAllocations,
19048     size_t allocationCount,
19049     VkBool32* pAllocationsChanged,
19050     const VmaDefragmentationInfo *pDefragmentationInfo,
19051     VmaDefragmentationStats* pDefragmentationStats)
19052 {
19053     // Deprecated interface, reimplemented using new one.
19054 
19055     VmaDefragmentationInfo2 info2 = {};
19056     info2.allocationCount = (uint32_t)allocationCount;
19057     info2.pAllocations = pAllocations;
19058     info2.pAllocationsChanged = pAllocationsChanged;
19059     if(pDefragmentationInfo != VMA_NULL)
19060     {
19061         info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
19062         info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
19063     }
19064     else
19065     {
19066         info2.maxCpuAllocationsToMove = UINT32_MAX;
19067         info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
19068     }
19069     // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
19070 
19071     VmaDefragmentationContext ctx;
19072     VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
19073     if(res == VK_NOT_READY)
19074     {
19075         res = vmaDefragmentationEnd( allocator, ctx);
19076     }
19077     return res;
19078 }
19079 
vmaDefragmentationBegin(VmaAllocator allocator,const VmaDefragmentationInfo2 * pInfo,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)19080 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
19081     VmaAllocator allocator,
19082     const VmaDefragmentationInfo2* pInfo,
19083     VmaDefragmentationStats* pStats,
19084     VmaDefragmentationContext *pContext)
19085 {
19086     VMA_ASSERT(allocator && pInfo && pContext);
19087 
19088     // Degenerate case: Nothing to defragment.
19089     if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
19090     {
19091         return VK_SUCCESS;
19092     }
19093 
19094     VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
19095     VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
19096     VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
19097     VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
19098 
19099     VMA_DEBUG_LOG("vmaDefragmentationBegin");
19100 
19101     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19102 
19103     VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
19104 
19105 #if VMA_RECORDING_ENABLED
19106     if(allocator->GetRecorder() != VMA_NULL)
19107     {
19108         allocator->GetRecorder()->RecordDefragmentationBegin(
19109             allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
19110     }
19111 #endif
19112 
19113     return res;
19114 }
19115 
vmaDefragmentationEnd(VmaAllocator allocator,VmaDefragmentationContext context)19116 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
19117     VmaAllocator allocator,
19118     VmaDefragmentationContext context)
19119 {
19120     VMA_ASSERT(allocator);
19121 
19122     VMA_DEBUG_LOG("vmaDefragmentationEnd");
19123 
19124     if(context != VK_NULL_HANDLE)
19125     {
19126         VMA_DEBUG_GLOBAL_MUTEX_LOCK
19127 
19128 #if VMA_RECORDING_ENABLED
19129         if(allocator->GetRecorder() != VMA_NULL)
19130         {
19131             allocator->GetRecorder()->RecordDefragmentationEnd(
19132                 allocator->GetCurrentFrameIndex(), context);
19133         }
19134 #endif
19135 
19136         return allocator->DefragmentationEnd(context);
19137     }
19138     else
19139     {
19140         return VK_SUCCESS;
19141     }
19142 }
19143 
vmaBeginDefragmentationPass(VmaAllocator allocator,VmaDefragmentationContext context,VmaDefragmentationPassInfo * pInfo)19144 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
19145     VmaAllocator allocator,
19146     VmaDefragmentationContext context,
19147     VmaDefragmentationPassInfo* pInfo
19148     )
19149 {
19150     VMA_ASSERT(allocator);
19151     VMA_ASSERT(pInfo);
19152     VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->moveCount, pInfo->pMoves));
19153 
19154     VMA_DEBUG_LOG("vmaBeginDefragmentationPass");
19155 
19156     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19157 
19158     if(context == VK_NULL_HANDLE)
19159     {
19160         pInfo->moveCount = 0;
19161         return VK_SUCCESS;
19162     }
19163 
19164     return allocator->DefragmentationPassBegin(pInfo, context);
19165 }
vmaEndDefragmentationPass(VmaAllocator allocator,VmaDefragmentationContext context)19166 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
19167     VmaAllocator allocator,
19168     VmaDefragmentationContext context)
19169 {
19170     VMA_ASSERT(allocator);
19171 
19172     VMA_DEBUG_LOG("vmaEndDefragmentationPass");
19173     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19174 
19175     if(context == VK_NULL_HANDLE)
19176         return VK_SUCCESS;
19177 
19178     return allocator->DefragmentationPassEnd(context);
19179 }
19180 
vmaBindBufferMemory(VmaAllocator allocator,VmaAllocation allocation,VkBuffer buffer)19181 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
19182     VmaAllocator allocator,
19183     VmaAllocation allocation,
19184     VkBuffer buffer)
19185 {
19186     VMA_ASSERT(allocator && allocation && buffer);
19187 
19188     VMA_DEBUG_LOG("vmaBindBufferMemory");
19189 
19190     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19191 
19192     return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL);
19193 }
19194 
vmaBindBufferMemory2(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize allocationLocalOffset,VkBuffer buffer,const void * pNext)19195 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
19196     VmaAllocator allocator,
19197     VmaAllocation allocation,
19198     VkDeviceSize allocationLocalOffset,
19199     VkBuffer buffer,
19200     const void* pNext)
19201 {
19202     VMA_ASSERT(allocator && allocation && buffer);
19203 
19204     VMA_DEBUG_LOG("vmaBindBufferMemory2");
19205 
19206     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19207 
19208     return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext);
19209 }
19210 
vmaBindImageMemory(VmaAllocator allocator,VmaAllocation allocation,VkImage image)19211 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
19212     VmaAllocator allocator,
19213     VmaAllocation allocation,
19214     VkImage image)
19215 {
19216     VMA_ASSERT(allocator && allocation && image);
19217 
19218     VMA_DEBUG_LOG("vmaBindImageMemory");
19219 
19220     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19221 
19222     return allocator->BindImageMemory(allocation, 0, image, VMA_NULL);
19223 }
19224 
vmaBindImageMemory2(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize allocationLocalOffset,VkImage image,const void * pNext)19225 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
19226     VmaAllocator allocator,
19227     VmaAllocation allocation,
19228     VkDeviceSize allocationLocalOffset,
19229     VkImage image,
19230     const void* pNext)
19231 {
19232     VMA_ASSERT(allocator && allocation && image);
19233 
19234     VMA_DEBUG_LOG("vmaBindImageMemory2");
19235 
19236     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19237 
19238         return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext);
19239 }
19240 
vmaCreateBuffer(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkBuffer * pBuffer,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)19241 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
19242     VmaAllocator allocator,
19243     const VkBufferCreateInfo* pBufferCreateInfo,
19244     const VmaAllocationCreateInfo* pAllocationCreateInfo,
19245     VkBuffer* pBuffer,
19246     VmaAllocation* pAllocation,
19247     VmaAllocationInfo* pAllocationInfo)
19248 {
19249     VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
19250 
19251     if(pBufferCreateInfo->size == 0)
19252     {
19253         return VK_ERROR_VALIDATION_FAILED_EXT;
19254     }
19255     if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
19256         !allocator->m_UseKhrBufferDeviceAddress)
19257     {
19258         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.");
19259         return VK_ERROR_VALIDATION_FAILED_EXT;
19260     }
19261 
19262     VMA_DEBUG_LOG("vmaCreateBuffer");
19263 
19264     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19265 
19266     *pBuffer = VK_NULL_HANDLE;
19267     *pAllocation = VK_NULL_HANDLE;
19268 
19269     // 1. Create VkBuffer.
19270     VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
19271         allocator->m_hDevice,
19272         pBufferCreateInfo,
19273         allocator->GetAllocationCallbacks(),
19274         pBuffer);
19275     if(res >= 0)
19276     {
19277         // 2. vkGetBufferMemoryRequirements.
19278         VkMemoryRequirements vkMemReq = {};
19279         bool requiresDedicatedAllocation = false;
19280         bool prefersDedicatedAllocation  = false;
19281         allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
19282             requiresDedicatedAllocation, prefersDedicatedAllocation);
19283 
19284         // 3. Allocate memory using allocator.
19285         res = allocator->AllocateMemory(
19286             vkMemReq,
19287             requiresDedicatedAllocation,
19288             prefersDedicatedAllocation,
19289             *pBuffer, // dedicatedBuffer
19290             pBufferCreateInfo->usage, // dedicatedBufferUsage
19291             VK_NULL_HANDLE, // dedicatedImage
19292             *pAllocationCreateInfo,
19293             VMA_SUBALLOCATION_TYPE_BUFFER,
19294             1, // allocationCount
19295             pAllocation);
19296 
19297 #if VMA_RECORDING_ENABLED
19298         if(allocator->GetRecorder() != VMA_NULL)
19299         {
19300             allocator->GetRecorder()->RecordCreateBuffer(
19301                 allocator->GetCurrentFrameIndex(),
19302                 *pBufferCreateInfo,
19303                 *pAllocationCreateInfo,
19304                 *pAllocation);
19305         }
19306 #endif
19307 
19308         if(res >= 0)
19309         {
19310             // 3. Bind buffer with memory.
19311             if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
19312             {
19313                 res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
19314             }
19315             if(res >= 0)
19316             {
19317                 // All steps succeeded.
19318                 #if VMA_STATS_STRING_ENABLED
19319                     (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
19320                 #endif
19321                 if(pAllocationInfo != VMA_NULL)
19322                 {
19323                     allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
19324                 }
19325 
19326                 return VK_SUCCESS;
19327             }
19328             allocator->FreeMemory(
19329                 1, // allocationCount
19330                 pAllocation);
19331             *pAllocation = VK_NULL_HANDLE;
19332             (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
19333             *pBuffer = VK_NULL_HANDLE;
19334             return res;
19335         }
19336         (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
19337         *pBuffer = VK_NULL_HANDLE;
19338         return res;
19339     }
19340     return res;
19341 }
19342 
vmaDestroyBuffer(VmaAllocator allocator,VkBuffer buffer,VmaAllocation allocation)19343 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
19344     VmaAllocator allocator,
19345     VkBuffer buffer,
19346     VmaAllocation allocation)
19347 {
19348     VMA_ASSERT(allocator);
19349 
19350     if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
19351     {
19352         return;
19353     }
19354 
19355     VMA_DEBUG_LOG("vmaDestroyBuffer");
19356 
19357     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19358 
19359 #if VMA_RECORDING_ENABLED
19360     if(allocator->GetRecorder() != VMA_NULL)
19361     {
19362         allocator->GetRecorder()->RecordDestroyBuffer(
19363             allocator->GetCurrentFrameIndex(),
19364             allocation);
19365     }
19366 #endif
19367 
19368     if(buffer != VK_NULL_HANDLE)
19369     {
19370         (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
19371     }
19372 
19373     if(allocation != VK_NULL_HANDLE)
19374     {
19375         allocator->FreeMemory(
19376             1, // allocationCount
19377             &allocation);
19378     }
19379 }
19380 
vmaCreateImage(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkImage * pImage,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)19381 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
19382     VmaAllocator allocator,
19383     const VkImageCreateInfo* pImageCreateInfo,
19384     const VmaAllocationCreateInfo* pAllocationCreateInfo,
19385     VkImage* pImage,
19386     VmaAllocation* pAllocation,
19387     VmaAllocationInfo* pAllocationInfo)
19388 {
19389     VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
19390 
19391     if(pImageCreateInfo->extent.width == 0 ||
19392         pImageCreateInfo->extent.height == 0 ||
19393         pImageCreateInfo->extent.depth == 0 ||
19394         pImageCreateInfo->mipLevels == 0 ||
19395         pImageCreateInfo->arrayLayers == 0)
19396     {
19397         return VK_ERROR_VALIDATION_FAILED_EXT;
19398     }
19399 
19400     VMA_DEBUG_LOG("vmaCreateImage");
19401 
19402     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19403 
19404     *pImage = VK_NULL_HANDLE;
19405     *pAllocation = VK_NULL_HANDLE;
19406 
19407     // 1. Create VkImage.
19408     VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
19409         allocator->m_hDevice,
19410         pImageCreateInfo,
19411         allocator->GetAllocationCallbacks(),
19412         pImage);
19413     if(res >= 0)
19414     {
19415         VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
19416             VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
19417             VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
19418 
19419         // 2. Allocate memory using allocator.
19420         VkMemoryRequirements vkMemReq = {};
19421         bool requiresDedicatedAllocation = false;
19422         bool prefersDedicatedAllocation  = false;
19423         allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
19424             requiresDedicatedAllocation, prefersDedicatedAllocation);
19425 
19426         res = allocator->AllocateMemory(
19427             vkMemReq,
19428             requiresDedicatedAllocation,
19429             prefersDedicatedAllocation,
19430             VK_NULL_HANDLE, // dedicatedBuffer
19431             UINT32_MAX, // dedicatedBufferUsage
19432             *pImage, // dedicatedImage
19433             *pAllocationCreateInfo,
19434             suballocType,
19435             1, // allocationCount
19436             pAllocation);
19437 
19438 #if VMA_RECORDING_ENABLED
19439         if(allocator->GetRecorder() != VMA_NULL)
19440         {
19441             allocator->GetRecorder()->RecordCreateImage(
19442                 allocator->GetCurrentFrameIndex(),
19443                 *pImageCreateInfo,
19444                 *pAllocationCreateInfo,
19445                 *pAllocation);
19446         }
19447 #endif
19448 
19449         if(res >= 0)
19450         {
19451             // 3. Bind image with memory.
19452             if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
19453             {
19454                 res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);
19455             }
19456             if(res >= 0)
19457             {
19458                 // All steps succeeded.
19459                 #if VMA_STATS_STRING_ENABLED
19460                     (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
19461                 #endif
19462                 if(pAllocationInfo != VMA_NULL)
19463                 {
19464                     allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
19465                 }
19466 
19467                 return VK_SUCCESS;
19468             }
19469             allocator->FreeMemory(
19470                 1, // allocationCount
19471                 pAllocation);
19472             *pAllocation = VK_NULL_HANDLE;
19473             (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
19474             *pImage = VK_NULL_HANDLE;
19475             return res;
19476         }
19477         (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
19478         *pImage = VK_NULL_HANDLE;
19479         return res;
19480     }
19481     return res;
19482 }
19483 
vmaDestroyImage(VmaAllocator allocator,VkImage image,VmaAllocation allocation)19484 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
19485     VmaAllocator allocator,
19486     VkImage image,
19487     VmaAllocation allocation)
19488 {
19489     VMA_ASSERT(allocator);
19490 
19491     if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
19492     {
19493         return;
19494     }
19495 
19496     VMA_DEBUG_LOG("vmaDestroyImage");
19497 
19498     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19499 
19500 #if VMA_RECORDING_ENABLED
19501     if(allocator->GetRecorder() != VMA_NULL)
19502     {
19503         allocator->GetRecorder()->RecordDestroyImage(
19504             allocator->GetCurrentFrameIndex(),
19505             allocation);
19506     }
19507 #endif
19508 
19509     if(image != VK_NULL_HANDLE)
19510     {
19511         (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
19512     }
19513     if(allocation != VK_NULL_HANDLE)
19514     {
19515         allocator->FreeMemory(
19516             1, // allocationCount
19517             &allocation);
19518     }
19519 }
19520 
19521 // OH ISSUE: VMA preAlloc
vmaCreateFakeImage(VmaAllocator allocator,VkImage * pImage)19522 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateFakeImage(
19523     VmaAllocator allocator,
19524     VkImage* pImage)
19525 {
19526     VMA_ASSERT(allocator && pImage);
19527     VMA_DEBUG_LOG("vmaCreateFakeImage");
19528     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19529 
19530     *pImage = VK_NULL_HANDLE;
19531     const VkImageCreateInfo imageCreateInfo = {
19532         VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,         // sType
19533         nullptr,                                     // pNext
19534         0,                                           // VkImageCreateFlags
19535         VK_IMAGE_TYPE_2D,                            // VkImageType
19536         VK_FORMAT_R8G8B8A8_UNORM,                    // VkFormat
19537         { 10, 10, 1 },                               // VkExtent3D
19538         1,                                           // mipLevels
19539         1,                                           // arrayLayers
19540         VK_SAMPLE_COUNT_1_BIT,                       // samples
19541         VK_IMAGE_TILING_OPTIMAL,                     // VkImageTiling
19542         VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,         // VkImageUsageFlags
19543         VK_SHARING_MODE_EXCLUSIVE,                   // VkSharingMode
19544         0,                                           // queueFamilyCount
19545         nullptr,                                     // pQueueFamilyIndices
19546         VK_IMAGE_LAYOUT_UNDEFINED                    // initialLayout
19547     };
19548 
19549     // 1. Create VkImage.
19550     VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
19551         allocator->m_hDevice,
19552         &imageCreateInfo,
19553         nullptr,
19554         pImage);
19555     return res;
19556 }
19557 
vmaDestroyFakeImage(VmaAllocator VMA_NOT_NULL allocator,VkImage VMA_NULLABLE_NON_DISPATCHABLE image)19558 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyFakeImage(
19559     VmaAllocator VMA_NOT_NULL allocator,
19560     VkImage VMA_NULLABLE_NON_DISPATCHABLE image)
19561 {
19562     VMA_ASSERT(allocator);
19563     if(image == VK_NULL_HANDLE)
19564     {
19565         return;
19566     }
19567     VMA_DEBUG_LOG("vmaDestroyFakeImage");
19568     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19569 
19570     if(image != VK_NULL_HANDLE)
19571     {
19572         (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
19573     }
19574 }
19575 
19576 #endif // #ifdef VMA_IMPLEMENTATION
19577