• 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     bool SwapLastBlock(VmaBlockVector* blockVector);
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 * blockVector)12722 bool VmaBlockVector::SwapLastBlock(VmaBlockVector* blockVector)
12723 {
12724     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12725 
12726     VmaDeviceMemoryBlock* lastBlock1 = m_Blocks.back();
12727     VmaDeviceMemoryBlock* lastBlock2 = blockVector->m_Blocks.back();
12728     if (lastBlock1 == nullptr || lastBlock2 == nullptr) {
12729         return false;
12730     }
12731     m_Blocks.pop_back();
12732     blockVector->m_Blocks.pop_back();
12733 
12734     m_Blocks.push_back(lastBlock2);
12735     blockVector->m_Blocks.push_back(lastBlock1);
12736     return true;
12737 }
12738 
AllocatePage(uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)12739 VkResult VmaBlockVector::AllocatePage(
12740     uint32_t currentFrameIndex,
12741     VkDeviceSize size,
12742     VkDeviceSize alignment,
12743     const VmaAllocationCreateInfo& createInfo,
12744     VmaSuballocationType suballocType,
12745     VmaAllocation* pAllocation)
12746 {
12747     const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
12748     bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
12749     const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
12750     const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
12751 
12752     VkDeviceSize freeMemory;
12753     {
12754         const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
12755         VmaBudget heapBudget = {};
12756         m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
12757         freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0;
12758     }
12759 
12760     const bool canFallbackToDedicated = !IsCustomPool();
12761     const bool canCreateNewBlock =
12762         ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
12763         (m_Blocks.size() < m_MaxBlockCount) &&
12764         (freeMemory >= size || !canFallbackToDedicated);
12765     uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
12766 
12767     // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
12768     // Which in turn is available only when maxBlockCount = 1.
12769     if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
12770     {
12771         canMakeOtherLost = false;
12772     }
12773 
12774     // Upper address can only be used with linear allocator and within single memory block.
12775     if(isUpperAddress &&
12776         (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
12777     {
12778         return VK_ERROR_FEATURE_NOT_PRESENT;
12779     }
12780 
12781     // Validate strategy.
12782     switch(strategy)
12783     {
12784     case 0:
12785         strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
12786         break;
12787     case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
12788     case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
12789     case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
12790         break;
12791     default:
12792         return VK_ERROR_FEATURE_NOT_PRESENT;
12793     }
12794 
12795     // Early reject: requested allocation size is larger that maximum block size for this block vector.
12796     if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
12797     {
12798         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12799     }
12800 
12801     /*
12802     Under certain condition, this whole section can be skipped for optimization, so
12803     we move on directly to trying to allocate with canMakeOtherLost. That's the case
12804     e.g. for custom pools with linear algorithm.
12805     */
12806     if(!canMakeOtherLost || canCreateNewBlock)
12807     {
12808         // 1. Search existing allocations. Try to allocate without making other allocations lost.
12809         VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
12810         allocFlagsCopy &= ~VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
12811 
12812         if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
12813         {
12814             // Use only last block.
12815             if(!m_Blocks.empty())
12816             {
12817                 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
12818                 VMA_ASSERT(pCurrBlock);
12819                 VkResult res = AllocateFromBlock(
12820                     pCurrBlock,
12821                     currentFrameIndex,
12822                     size,
12823                     alignment,
12824                     allocFlagsCopy,
12825                     createInfo.pUserData,
12826                     suballocType,
12827                     strategy,
12828                     pAllocation);
12829                 if(res == VK_SUCCESS)
12830                 {
12831                     VMA_DEBUG_LOG("    Returned from last block #%u", pCurrBlock->GetId());
12832                     return VK_SUCCESS;
12833                 }
12834             }
12835         }
12836         else
12837         {
12838             if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
12839             {
12840                 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12841                 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
12842                 {
12843                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12844                     VMA_ASSERT(pCurrBlock);
12845                     VkResult res = AllocateFromBlock(
12846                         pCurrBlock,
12847                         currentFrameIndex,
12848                         size,
12849                         alignment,
12850                         allocFlagsCopy,
12851                         createInfo.pUserData,
12852                         suballocType,
12853                         strategy,
12854                         pAllocation);
12855                     if(res == VK_SUCCESS)
12856                     {
12857                         VMA_DEBUG_LOG("    Returned from existing block #%u", pCurrBlock->GetId());
12858                         return VK_SUCCESS;
12859                     }
12860                 }
12861             }
12862             else // WORST_FIT, FIRST_FIT
12863             {
12864                 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
12865                 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12866                 {
12867                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12868                     VMA_ASSERT(pCurrBlock);
12869                     VkResult res = AllocateFromBlock(
12870                         pCurrBlock,
12871                         currentFrameIndex,
12872                         size,
12873                         alignment,
12874                         allocFlagsCopy,
12875                         createInfo.pUserData,
12876                         suballocType,
12877                         strategy,
12878                         pAllocation);
12879                     if(res == VK_SUCCESS)
12880                     {
12881                         VMA_DEBUG_LOG("    Returned from existing block #%u", pCurrBlock->GetId());
12882                         return VK_SUCCESS;
12883                     }
12884                 }
12885             }
12886         }
12887 
12888         // 2. Try to create new block.
12889         if(canCreateNewBlock)
12890         {
12891             // Calculate optimal size for new block.
12892             VkDeviceSize newBlockSize = m_PreferredBlockSize;
12893             uint32_t newBlockSizeShift = 0;
12894             const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
12895 
12896             if(!m_ExplicitBlockSize)
12897             {
12898                 // Allocate 1/8, 1/4, 1/2 as first blocks.
12899                 const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
12900                 for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
12901                 {
12902                     const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12903                     if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
12904                     {
12905                         newBlockSize = smallerNewBlockSize;
12906                         ++newBlockSizeShift;
12907                     }
12908                     else
12909                     {
12910                         break;
12911                     }
12912                 }
12913             }
12914 
12915             size_t newBlockIndex = 0;
12916             VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12917                 CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12918             // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
12919             if(!m_ExplicitBlockSize)
12920             {
12921                 while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
12922                 {
12923                     const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12924                     if(smallerNewBlockSize >= size)
12925                     {
12926                         newBlockSize = smallerNewBlockSize;
12927                         ++newBlockSizeShift;
12928                         res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12929                             CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12930                     }
12931                     else
12932                     {
12933                         break;
12934                     }
12935                 }
12936             }
12937 
12938             if(res == VK_SUCCESS)
12939             {
12940                 VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
12941                 VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
12942 
12943                 res = AllocateFromBlock(
12944                     pBlock,
12945                     currentFrameIndex,
12946                     size,
12947                     alignment,
12948                     allocFlagsCopy,
12949                     createInfo.pUserData,
12950                     suballocType,
12951                     strategy,
12952                     pAllocation);
12953                 if(res == VK_SUCCESS)
12954                 {
12955                     VMA_DEBUG_LOG("    Created new block #%u Size=%llu", pBlock->GetId(), newBlockSize);
12956                     return VK_SUCCESS;
12957                 }
12958                 else
12959                 {
12960                     // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
12961                     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12962                 }
12963             }
12964         }
12965     }
12966 
12967     // 3. Try to allocate from existing blocks with making other allocations lost.
12968     if(canMakeOtherLost)
12969     {
12970         uint32_t tryIndex = 0;
12971         for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
12972         {
12973             VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
12974             VmaAllocationRequest bestRequest = {};
12975             VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
12976 
12977             // 1. Search existing allocations.
12978             if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
12979             {
12980                 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12981                 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
12982                 {
12983                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12984                     VMA_ASSERT(pCurrBlock);
12985                     VmaAllocationRequest currRequest = {};
12986                     if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
12987                         currentFrameIndex,
12988                         m_FrameInUseCount,
12989                         m_BufferImageGranularity,
12990                         size,
12991                         alignment,
12992                         (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
12993                         suballocType,
12994                         canMakeOtherLost,
12995                         strategy,
12996                         &currRequest))
12997                     {
12998                         const VkDeviceSize currRequestCost = currRequest.CalcCost();
12999                         if(pBestRequestBlock == VMA_NULL ||
13000                             currRequestCost < bestRequestCost)
13001                         {
13002                             pBestRequestBlock = pCurrBlock;
13003                             bestRequest = currRequest;
13004                             bestRequestCost = currRequestCost;
13005 
13006                             if(bestRequestCost == 0)
13007                             {
13008                                 break;
13009                             }
13010                         }
13011                     }
13012                 }
13013             }
13014             else // WORST_FIT, FIRST_FIT
13015             {
13016                 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
13017                 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
13018                 {
13019                     VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
13020                     VMA_ASSERT(pCurrBlock);
13021                     VmaAllocationRequest currRequest = {};
13022                     if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
13023                         currentFrameIndex,
13024                         m_FrameInUseCount,
13025                         m_BufferImageGranularity,
13026                         size,
13027                         alignment,
13028                         (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
13029                         suballocType,
13030                         canMakeOtherLost,
13031                         strategy,
13032                         &currRequest))
13033                     {
13034                         const VkDeviceSize currRequestCost = currRequest.CalcCost();
13035                         if(pBestRequestBlock == VMA_NULL ||
13036                             currRequestCost < bestRequestCost ||
13037                             strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
13038                         {
13039                             pBestRequestBlock = pCurrBlock;
13040                             bestRequest = currRequest;
13041                             bestRequestCost = currRequestCost;
13042 
13043                             if(bestRequestCost == 0 ||
13044                                 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
13045                             {
13046                                 break;
13047                             }
13048                         }
13049                     }
13050                 }
13051             }
13052 
13053             if(pBestRequestBlock != VMA_NULL)
13054             {
13055                 if(mapped)
13056                 {
13057                     VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
13058                     if(res != VK_SUCCESS)
13059                     {
13060                         return res;
13061                     }
13062                 }
13063 
13064                 if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
13065                     currentFrameIndex,
13066                     m_FrameInUseCount,
13067                     &bestRequest))
13068                 {
13069                     // Allocate from this pBlock.
13070                     *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
13071                     pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, *pAllocation);
13072                     UpdateHasEmptyBlock();
13073                     (*pAllocation)->InitBlockAllocation(
13074                         pBestRequestBlock,
13075                         bestRequest.offset,
13076                         alignment,
13077                         size,
13078                         m_MemoryTypeIndex,
13079                         suballocType,
13080                         mapped,
13081                         (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0,
13082                         IsNewBlockFlag());
13083                     ClearNewBlockFlag();
13084                     VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
13085                     VMA_DEBUG_LOG("    Returned from existing block");
13086                     (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
13087                     m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
13088                     if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13089                     {
13090                         m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13091                     }
13092                     if(IsCorruptionDetectionEnabled())
13093                     {
13094                         VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
13095                         VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
13096                     }
13097                     return VK_SUCCESS;
13098                 }
13099                 // else: Some allocations must have been touched while we are here. Next try.
13100             }
13101             else
13102             {
13103                 // Could not find place in any of the blocks - break outer loop.
13104                 break;
13105             }
13106         }
13107         /* Maximum number of tries exceeded - a very unlike event when many other
13108         threads are simultaneously touching allocations making it impossible to make
13109         lost at the same time as we try to allocate. */
13110         if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
13111         {
13112             return VK_ERROR_TOO_MANY_OBJECTS;
13113         }
13114     }
13115 
13116     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13117 }
13118 
Free(const VmaAllocation hAllocation)13119 void VmaBlockVector::Free(
13120     const VmaAllocation hAllocation)
13121 {
13122     VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
13123 
13124     bool budgetExceeded = false;
13125     {
13126         const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
13127         VmaBudget heapBudget = {};
13128         m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
13129         budgetExceeded = heapBudget.usage >= heapBudget.budget;
13130     }
13131 
13132     // Scope for lock.
13133     {
13134         VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13135 
13136         VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
13137 
13138         if(IsCorruptionDetectionEnabled())
13139         {
13140             VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
13141             VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
13142         }
13143 
13144         if(hAllocation->IsPersistentMap())
13145         {
13146             pBlock->Unmap(m_hAllocator, 1);
13147         }
13148 
13149         pBlock->m_pMetadata->Free(hAllocation);
13150         VMA_HEAVY_ASSERT(pBlock->Validate());
13151 
13152         VMA_DEBUG_LOG("  Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
13153 
13154         const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount;
13155         // pBlock became empty after this deallocation.
13156         if(pBlock->m_pMetadata->IsEmpty())
13157         {
13158             // Already has empty block. We don't want to have two, so delete this one.
13159             if((m_HasEmptyBlock || budgetExceeded) && canDeleteBlock)
13160             {
13161                 pBlockToDelete = pBlock;
13162                 Remove(pBlock);
13163             }
13164             // else: We now have an empty block - leave it.
13165         }
13166         // pBlock didn't become empty, but we have another empty block - find and free that one.
13167         // (This is optional, heuristics.)
13168         else if(m_HasEmptyBlock && canDeleteBlock)
13169         {
13170             VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
13171             if(pLastBlock->m_pMetadata->IsEmpty())
13172             {
13173                 pBlockToDelete = pLastBlock;
13174                 m_Blocks.pop_back();
13175             }
13176         }
13177 
13178         UpdateHasEmptyBlock();
13179         IncrementallySortBlocks();
13180     }
13181 
13182     // Destruction of a free block. Deferred until this point, outside of mutex
13183     // lock, for performance reason.
13184     if(pBlockToDelete != VMA_NULL)
13185     {
13186         VMA_DEBUG_LOG("    Deleted empty block");
13187         pBlockToDelete->Destroy(m_hAllocator);
13188         vma_delete(m_hAllocator, pBlockToDelete);
13189     }
13190 }
13191 
13192 // OH ISSUE: VMA preAlloc
FreeReserved(const VmaAllocation hAllocation)13193 void VmaBlockVector::FreeReserved(
13194     const VmaAllocation hAllocation)
13195 {
13196     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13197 
13198     VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
13199 
13200     if(IsCorruptionDetectionEnabled())
13201     {
13202         VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
13203         VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
13204     }
13205 
13206     if(hAllocation->IsPersistentMap())
13207     {
13208         pBlock->Unmap(m_hAllocator, 1);
13209     }
13210 
13211     pBlock->m_pMetadata->Free(hAllocation);
13212     VMA_HEAVY_ASSERT(pBlock->Validate());
13213 
13214     VMA_DEBUG_LOG("  Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
13215     IncrementallySortBlocks();
13216 }
13217 
CalcMaxBlockSize()13218 VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
13219 {
13220     VkDeviceSize result = 0;
13221     for(size_t i = m_Blocks.size(); i--; )
13222     {
13223         result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
13224         if(result >= m_PreferredBlockSize)
13225         {
13226             break;
13227         }
13228     }
13229     return result;
13230 }
13231 
Remove(VmaDeviceMemoryBlock * pBlock)13232 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
13233 {
13234     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13235     {
13236         if(m_Blocks[blockIndex] == pBlock)
13237         {
13238             VmaVectorRemove(m_Blocks, blockIndex);
13239             return;
13240         }
13241     }
13242     VMA_ASSERT(0);
13243 }
13244 
IncrementallySortBlocks()13245 void VmaBlockVector::IncrementallySortBlocks()
13246 {
13247     if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
13248     {
13249         // Bubble sort only until first swap.
13250         for(size_t i = 1; i < m_Blocks.size(); ++i)
13251         {
13252             if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
13253             {
13254                 VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
13255                 return;
13256             }
13257         }
13258     }
13259 }
13260 
AllocateFromBlock(VmaDeviceMemoryBlock * pBlock,uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,VmaAllocationCreateFlags allocFlags,void * pUserData,VmaSuballocationType suballocType,uint32_t strategy,VmaAllocation * pAllocation)13261 VkResult VmaBlockVector::AllocateFromBlock(
13262     VmaDeviceMemoryBlock* pBlock,
13263     uint32_t currentFrameIndex,
13264     VkDeviceSize size,
13265     VkDeviceSize alignment,
13266     VmaAllocationCreateFlags allocFlags,
13267     void* pUserData,
13268     VmaSuballocationType suballocType,
13269     uint32_t strategy,
13270     VmaAllocation* pAllocation)
13271 {
13272     VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
13273     const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
13274     const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
13275     const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
13276 
13277     VmaAllocationRequest currRequest = {};
13278     if(pBlock->m_pMetadata->CreateAllocationRequest(
13279         currentFrameIndex,
13280         m_FrameInUseCount,
13281         m_BufferImageGranularity,
13282         size,
13283         alignment,
13284         isUpperAddress,
13285         suballocType,
13286         false, // canMakeOtherLost
13287         strategy,
13288         &currRequest))
13289     {
13290         // Allocate from pCurrBlock.
13291         VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
13292 
13293         if(mapped)
13294         {
13295             VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
13296             if(res != VK_SUCCESS)
13297             {
13298                 return res;
13299             }
13300         }
13301 
13302         *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
13303         pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, *pAllocation);
13304         UpdateHasEmptyBlock();
13305         (*pAllocation)->InitBlockAllocation(
13306             pBlock,
13307             currRequest.offset,
13308             alignment,
13309             size,
13310             m_MemoryTypeIndex,
13311             suballocType,
13312             mapped,
13313             (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0,
13314             IsNewBlockFlag());
13315         ClearNewBlockFlag();
13316         VMA_HEAVY_ASSERT(pBlock->Validate());
13317         (*pAllocation)->SetUserData(m_hAllocator, pUserData);
13318         m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
13319         if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13320         {
13321             m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13322         }
13323         if(IsCorruptionDetectionEnabled())
13324         {
13325             VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
13326             VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
13327         }
13328         return VK_SUCCESS;
13329     }
13330     return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13331 }
13332 
CreateBlock(VkDeviceSize blockSize,size_t * pNewBlockIndex)13333 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
13334 {
13335     VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
13336     allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
13337     allocInfo.allocationSize = blockSize;
13338 
13339 #if VMA_BUFFER_DEVICE_ADDRESS
13340     // Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature.
13341     VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
13342     if(m_hAllocator->m_UseKhrBufferDeviceAddress)
13343     {
13344         allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
13345         VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
13346     }
13347 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
13348 
13349     VkDeviceMemory mem = VK_NULL_HANDLE;
13350     VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
13351     if(res < 0)
13352     {
13353         return res;
13354     }
13355 
13356     // New VkDeviceMemory successfully created.
13357 
13358     // Create new Allocation for it.
13359     VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
13360     pBlock->Init(
13361         m_hAllocator,
13362         m_hParentPool,
13363         m_MemoryTypeIndex,
13364         mem,
13365         allocInfo.allocationSize,
13366         m_NextBlockId++,
13367         m_Algorithm);
13368     m_NewBlockFlag = true;
13369 
13370     m_Blocks.push_back(pBlock);
13371     if(pNewBlockIndex != VMA_NULL)
13372     {
13373         *pNewBlockIndex = m_Blocks.size() - 1;
13374     }
13375 
13376     return VK_SUCCESS;
13377 }
13378 
ApplyDefragmentationMovesCpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,const VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves)13379 void VmaBlockVector::ApplyDefragmentationMovesCpu(
13380     class VmaBlockVectorDefragmentationContext* pDefragCtx,
13381     const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
13382 {
13383     const size_t blockCount = m_Blocks.size();
13384     const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
13385 
13386     enum BLOCK_FLAG
13387     {
13388         BLOCK_FLAG_USED = 0x00000001,
13389         BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
13390     };
13391 
13392     struct BlockInfo
13393     {
13394         uint32_t flags;
13395         void* pMappedData;
13396     };
13397     VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
13398         blockInfo(blockCount, BlockInfo(), VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
13399     memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
13400 
13401     // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13402     const size_t moveCount = moves.size();
13403     for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13404     {
13405         const VmaDefragmentationMove& move = moves[moveIndex];
13406         blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
13407         blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
13408     }
13409 
13410     VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13411 
13412     // Go over all blocks. Get mapped pointer or map if necessary.
13413     for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13414     {
13415         BlockInfo& currBlockInfo = blockInfo[blockIndex];
13416         VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13417         if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
13418         {
13419             currBlockInfo.pMappedData = pBlock->GetMappedData();
13420             // It is not originally mapped - map it.
13421             if(currBlockInfo.pMappedData == VMA_NULL)
13422             {
13423                 pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
13424                 if(pDefragCtx->res == VK_SUCCESS)
13425                 {
13426                     currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
13427                 }
13428             }
13429         }
13430     }
13431 
13432     // Go over all moves. Do actual data transfer.
13433     if(pDefragCtx->res == VK_SUCCESS)
13434     {
13435         const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
13436         VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
13437 
13438         for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13439         {
13440             const VmaDefragmentationMove& move = moves[moveIndex];
13441 
13442             const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
13443             const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
13444 
13445             VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
13446 
13447             // Invalidate source.
13448             if(isNonCoherent)
13449             {
13450                 VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
13451                 memRange.memory = pSrcBlock->GetDeviceMemory();
13452                 memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
13453                 memRange.size = VMA_MIN(
13454                     VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
13455                     pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
13456                 (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13457             }
13458 
13459             // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
13460             memmove(
13461                 reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
13462                 reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
13463                 static_cast<size_t>(move.size));
13464 
13465             if(IsCorruptionDetectionEnabled())
13466             {
13467                 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
13468                 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
13469             }
13470 
13471             // Flush destination.
13472             if(isNonCoherent)
13473             {
13474                 VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
13475                 memRange.memory = pDstBlock->GetDeviceMemory();
13476                 memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
13477                 memRange.size = VMA_MIN(
13478                     VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
13479                     pDstBlock->m_pMetadata->GetSize() - memRange.offset);
13480                 (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13481             }
13482         }
13483     }
13484 
13485     // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
13486     // Regardless of pCtx->res == VK_SUCCESS.
13487     for(size_t blockIndex = blockCount; blockIndex--; )
13488     {
13489         const BlockInfo& currBlockInfo = blockInfo[blockIndex];
13490         if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
13491         {
13492             VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13493             pBlock->Unmap(m_hAllocator, 1);
13494         }
13495     }
13496 }
13497 
ApplyDefragmentationMovesGpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkCommandBuffer commandBuffer)13498 void VmaBlockVector::ApplyDefragmentationMovesGpu(
13499     class VmaBlockVectorDefragmentationContext* pDefragCtx,
13500     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13501     VkCommandBuffer commandBuffer)
13502 {
13503     const size_t blockCount = m_Blocks.size();
13504 
13505     pDefragCtx->blockContexts.resize(blockCount);
13506     memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext));
13507 
13508     // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13509     const size_t moveCount = moves.size();
13510     for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13511     {
13512         const VmaDefragmentationMove& move = moves[moveIndex];
13513 
13514         //if(move.type == VMA_ALLOCATION_TYPE_UNKNOWN)
13515         {
13516             // Old school move still require us to map the whole block
13517             pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13518             pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13519         }
13520     }
13521 
13522     VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13523 
13524     // Go over all blocks. Create and bind buffer for whole block if necessary.
13525     {
13526         VkBufferCreateInfo bufCreateInfo;
13527         VmaFillGpuDefragmentationBufferCreateInfo(bufCreateInfo);
13528 
13529         for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13530         {
13531             VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
13532             VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13533             if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
13534             {
13535                 bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
13536                 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
13537                     m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
13538                 if(pDefragCtx->res == VK_SUCCESS)
13539                 {
13540                     pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
13541                         m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
13542                 }
13543             }
13544         }
13545     }
13546 
13547     // Go over all moves. Post data transfer commands to command buffer.
13548     if(pDefragCtx->res == VK_SUCCESS)
13549     {
13550         for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13551         {
13552             const VmaDefragmentationMove& move = moves[moveIndex];
13553 
13554             const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
13555             const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
13556 
13557             VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
13558 
13559             VkBufferCopy region = {
13560                 move.srcOffset,
13561                 move.dstOffset,
13562                 move.size };
13563             (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
13564                 commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, &region);
13565         }
13566     }
13567 
13568     // Save buffers to defrag context for later destruction.
13569     if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
13570     {
13571         pDefragCtx->res = VK_NOT_READY;
13572     }
13573 }
13574 
FreeEmptyBlocks(VmaDefragmentationStats * pDefragmentationStats)13575 void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
13576 {
13577     for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
13578     {
13579         VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13580         if(pBlock->m_pMetadata->IsEmpty())
13581         {
13582             if(m_Blocks.size() > m_MinBlockCount)
13583             {
13584                 if(pDefragmentationStats != VMA_NULL)
13585                 {
13586                     ++pDefragmentationStats->deviceMemoryBlocksFreed;
13587                     pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
13588                 }
13589 
13590                 VmaVectorRemove(m_Blocks, blockIndex);
13591                 pBlock->Destroy(m_hAllocator);
13592                 vma_delete(m_hAllocator, pBlock);
13593             }
13594             else
13595             {
13596                 break;
13597             }
13598         }
13599     }
13600     UpdateHasEmptyBlock();
13601 }
13602 
UpdateHasEmptyBlock()13603 void VmaBlockVector::UpdateHasEmptyBlock()
13604 {
13605     m_HasEmptyBlock = false;
13606     for(size_t index = 0, count = m_Blocks.size(); index < count; ++index)
13607     {
13608         VmaDeviceMemoryBlock* const pBlock = m_Blocks[index];
13609         if(pBlock->m_pMetadata->IsEmpty())
13610         {
13611             m_HasEmptyBlock = true;
13612             break;
13613         }
13614     }
13615 }
13616 
13617 #if VMA_STATS_STRING_ENABLED
13618 
PrintDetailedMap(class VmaJsonWriter & json)13619 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
13620 {
13621     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13622 
13623     json.BeginObject();
13624 
13625     if(IsCustomPool())
13626     {
13627         const char* poolName = m_hParentPool->GetName();
13628         if(poolName != VMA_NULL && poolName[0] != '\0')
13629         {
13630             json.WriteString("Name");
13631             json.WriteString(poolName);
13632         }
13633 
13634         json.WriteString("MemoryTypeIndex");
13635         json.WriteNumber(m_MemoryTypeIndex);
13636 
13637         json.WriteString("BlockSize");
13638         json.WriteNumber(m_PreferredBlockSize);
13639 
13640         json.WriteString("BlockCount");
13641         json.BeginObject(true);
13642         if(m_MinBlockCount > 0)
13643         {
13644             json.WriteString("Min");
13645             json.WriteNumber((uint64_t)m_MinBlockCount);
13646         }
13647         if(m_MaxBlockCount < SIZE_MAX)
13648         {
13649             json.WriteString("Max");
13650             json.WriteNumber((uint64_t)m_MaxBlockCount);
13651         }
13652         json.WriteString("Cur");
13653         json.WriteNumber((uint64_t)m_Blocks.size());
13654         json.EndObject();
13655 
13656         if(m_FrameInUseCount > 0)
13657         {
13658             json.WriteString("FrameInUseCount");
13659             json.WriteNumber(m_FrameInUseCount);
13660         }
13661 
13662         if(m_Algorithm != 0)
13663         {
13664             json.WriteString("Algorithm");
13665             json.WriteString(VmaAlgorithmToStr(m_Algorithm));
13666         }
13667     }
13668     else
13669     {
13670         json.WriteString("PreferredBlockSize");
13671         json.WriteNumber(m_PreferredBlockSize);
13672     }
13673 
13674     json.WriteString("Blocks");
13675     json.BeginObject();
13676     for(size_t i = 0; i < m_Blocks.size(); ++i)
13677     {
13678         json.BeginString();
13679         json.ContinueString(m_Blocks[i]->GetId());
13680         json.EndString();
13681 
13682         m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
13683     }
13684     json.EndObject();
13685 
13686     json.EndObject();
13687 }
13688 
13689 #endif // #if VMA_STATS_STRING_ENABLED
13690 
Defragment(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats,VmaDefragmentationFlags flags,VkDeviceSize & maxCpuBytesToMove,uint32_t & maxCpuAllocationsToMove,VkDeviceSize & maxGpuBytesToMove,uint32_t & maxGpuAllocationsToMove,VkCommandBuffer commandBuffer)13691 void VmaBlockVector::Defragment(
13692     class VmaBlockVectorDefragmentationContext* pCtx,
13693     VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags,
13694     VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
13695     VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
13696     VkCommandBuffer commandBuffer)
13697 {
13698     pCtx->res = VK_SUCCESS;
13699 
13700     const VkMemoryPropertyFlags memPropFlags =
13701         m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
13702     const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
13703 
13704     const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
13705         isHostVisible;
13706     const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
13707         !IsCorruptionDetectionEnabled() &&
13708         ((1u << m_MemoryTypeIndex) & m_hAllocator->GetGpuDefragmentationMemoryTypeBits()) != 0;
13709 
13710     // There are options to defragment this memory type.
13711     if(canDefragmentOnCpu || canDefragmentOnGpu)
13712     {
13713         bool defragmentOnGpu;
13714         // There is only one option to defragment this memory type.
13715         if(canDefragmentOnGpu != canDefragmentOnCpu)
13716         {
13717             defragmentOnGpu = canDefragmentOnGpu;
13718         }
13719         // Both options are available: Heuristics to choose the best one.
13720         else
13721         {
13722             defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
13723                 m_hAllocator->IsIntegratedGpu();
13724         }
13725 
13726         bool overlappingMoveSupported = !defragmentOnGpu;
13727 
13728         if(m_hAllocator->m_UseMutex)
13729         {
13730             if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
13731             {
13732                 if(!m_Mutex.TryLockWrite())
13733                 {
13734                     pCtx->res = VK_ERROR_INITIALIZATION_FAILED;
13735                     return;
13736                 }
13737             }
13738             else
13739             {
13740                 m_Mutex.LockWrite();
13741                 pCtx->mutexLocked = true;
13742             }
13743         }
13744 
13745         pCtx->Begin(overlappingMoveSupported, flags);
13746 
13747         // Defragment.
13748 
13749         const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
13750         const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
13751         pCtx->res = pCtx->GetAlgorithm()->Defragment(pCtx->defragmentationMoves, maxBytesToMove, maxAllocationsToMove, flags);
13752 
13753         // Accumulate statistics.
13754         if(pStats != VMA_NULL)
13755         {
13756             const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
13757             const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
13758             pStats->bytesMoved += bytesMoved;
13759             pStats->allocationsMoved += allocationsMoved;
13760             VMA_ASSERT(bytesMoved <= maxBytesToMove);
13761             VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
13762             if(defragmentOnGpu)
13763             {
13764                 maxGpuBytesToMove -= bytesMoved;
13765                 maxGpuAllocationsToMove -= allocationsMoved;
13766             }
13767             else
13768             {
13769                 maxCpuBytesToMove -= bytesMoved;
13770                 maxCpuAllocationsToMove -= allocationsMoved;
13771             }
13772         }
13773 
13774         if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
13775         {
13776             if(m_hAllocator->m_UseMutex)
13777                 m_Mutex.UnlockWrite();
13778 
13779             if(pCtx->res >= VK_SUCCESS && !pCtx->defragmentationMoves.empty())
13780                 pCtx->res = VK_NOT_READY;
13781 
13782             return;
13783         }
13784 
13785         if(pCtx->res >= VK_SUCCESS)
13786         {
13787             if(defragmentOnGpu)
13788             {
13789                 ApplyDefragmentationMovesGpu(pCtx, pCtx->defragmentationMoves, commandBuffer);
13790             }
13791             else
13792             {
13793                 ApplyDefragmentationMovesCpu(pCtx, pCtx->defragmentationMoves);
13794             }
13795         }
13796     }
13797 }
13798 
DefragmentationEnd(class VmaBlockVectorDefragmentationContext * pCtx,uint32_t flags,VmaDefragmentationStats * pStats)13799 void VmaBlockVector::DefragmentationEnd(
13800     class VmaBlockVectorDefragmentationContext* pCtx,
13801     uint32_t flags,
13802     VmaDefragmentationStats* pStats)
13803 {
13804     if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL && m_hAllocator->m_UseMutex)
13805     {
13806         VMA_ASSERT(pCtx->mutexLocked == false);
13807 
13808         // Incremental defragmentation doesn't hold the lock, so when we enter here we don't actually have any
13809         // lock protecting us. Since we mutate state here, we have to take the lock out now
13810         m_Mutex.LockWrite();
13811         pCtx->mutexLocked = true;
13812     }
13813 
13814     // If the mutex isn't locked we didn't do any work and there is nothing to delete.
13815     if(pCtx->mutexLocked || !m_hAllocator->m_UseMutex)
13816     {
13817         // Destroy buffers.
13818         for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--;)
13819         {
13820             VmaBlockDefragmentationContext &blockCtx = pCtx->blockContexts[blockIndex];
13821             if(blockCtx.hBuffer)
13822             {
13823                 (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
13824             }
13825         }
13826 
13827         if(pCtx->res >= VK_SUCCESS)
13828         {
13829             FreeEmptyBlocks(pStats);
13830         }
13831     }
13832 
13833     if(pCtx->mutexLocked)
13834     {
13835         VMA_ASSERT(m_hAllocator->m_UseMutex);
13836         m_Mutex.UnlockWrite();
13837     }
13838 }
13839 
ProcessDefragmentations(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationPassMoveInfo * pMove,uint32_t maxMoves)13840 uint32_t VmaBlockVector::ProcessDefragmentations(
13841     class VmaBlockVectorDefragmentationContext *pCtx,
13842     VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves)
13843 {
13844     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13845 
13846     const uint32_t moveCount = std::min(uint32_t(pCtx->defragmentationMoves.size()) - pCtx->defragmentationMovesProcessed, maxMoves);
13847 
13848     for(uint32_t i = 0; i < moveCount; ++ i)
13849     {
13850         VmaDefragmentationMove& move = pCtx->defragmentationMoves[pCtx->defragmentationMovesProcessed + i];
13851 
13852         pMove->allocation = move.hAllocation;
13853         pMove->memory = move.pDstBlock->GetDeviceMemory();
13854         pMove->offset = move.dstOffset;
13855 
13856         ++ pMove;
13857     }
13858 
13859     pCtx->defragmentationMovesProcessed += moveCount;
13860 
13861     return moveCount;
13862 }
13863 
CommitDefragmentations(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats)13864 void VmaBlockVector::CommitDefragmentations(
13865     class VmaBlockVectorDefragmentationContext *pCtx,
13866     VmaDefragmentationStats* pStats)
13867 {
13868     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13869 
13870     for(uint32_t i = pCtx->defragmentationMovesCommitted; i < pCtx->defragmentationMovesProcessed; ++ i)
13871     {
13872         const VmaDefragmentationMove &move = pCtx->defragmentationMoves[i];
13873 
13874         move.pSrcBlock->m_pMetadata->FreeAtOffset(move.srcOffset);
13875         move.hAllocation->ChangeBlockAllocation(m_hAllocator, move.pDstBlock, move.dstOffset);
13876     }
13877 
13878     pCtx->defragmentationMovesCommitted = pCtx->defragmentationMovesProcessed;
13879     FreeEmptyBlocks(pStats);
13880 }
13881 
CalcAllocationCount()13882 size_t VmaBlockVector::CalcAllocationCount() const
13883 {
13884     size_t result = 0;
13885     for(size_t i = 0; i < m_Blocks.size(); ++i)
13886     {
13887         result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
13888     }
13889     return result;
13890 }
13891 
IsBufferImageGranularityConflictPossible()13892 bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
13893 {
13894     if(m_BufferImageGranularity == 1)
13895     {
13896         return false;
13897     }
13898     VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
13899     for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
13900     {
13901         VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
13902         VMA_ASSERT(m_Algorithm == 0);
13903         VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
13904         if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
13905         {
13906             return true;
13907         }
13908     }
13909     return false;
13910 }
13911 
MakePoolAllocationsLost(uint32_t currentFrameIndex,size_t * pLostAllocationCount)13912 void VmaBlockVector::MakePoolAllocationsLost(
13913     uint32_t currentFrameIndex,
13914     size_t* pLostAllocationCount)
13915 {
13916     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13917     size_t lostAllocationCount = 0;
13918     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13919     {
13920         VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13921         VMA_ASSERT(pBlock);
13922         lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
13923     }
13924     if(pLostAllocationCount != VMA_NULL)
13925     {
13926         *pLostAllocationCount = lostAllocationCount;
13927     }
13928 }
13929 
CheckCorruption()13930 VkResult VmaBlockVector::CheckCorruption()
13931 {
13932     if(!IsCorruptionDetectionEnabled())
13933     {
13934         return VK_ERROR_FEATURE_NOT_PRESENT;
13935     }
13936 
13937     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13938     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13939     {
13940         VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13941         VMA_ASSERT(pBlock);
13942         VkResult res = pBlock->CheckCorruption(m_hAllocator);
13943         if(res != VK_SUCCESS)
13944         {
13945             return res;
13946         }
13947     }
13948     return VK_SUCCESS;
13949 }
13950 
AddStats(VmaStats * pStats)13951 void VmaBlockVector::AddStats(VmaStats* pStats)
13952 {
13953     const uint32_t memTypeIndex = m_MemoryTypeIndex;
13954     const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
13955 
13956     VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13957 
13958     for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13959     {
13960         const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13961         VMA_ASSERT(pBlock);
13962         VMA_HEAVY_ASSERT(pBlock->Validate());
13963         VmaStatInfo allocationStatInfo;
13964         pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
13965         VmaAddStatInfo(pStats->total, allocationStatInfo);
13966         VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
13967         VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
13968     }
13969 }
13970 
FreeEmptyBlock()13971 void VmaBlockVector::FreeEmptyBlock()
13972 {
13973     VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13974     FreeEmptyBlocks(VMA_NULL);
13975 }
13976 
13977 ////////////////////////////////////////////////////////////////////////////////
13978 // VmaDefragmentationAlgorithm_Generic members definition
13979 
VmaDefragmentationAlgorithm_Generic(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)13980 VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
13981     VmaAllocator hAllocator,
13982     VmaBlockVector* pBlockVector,
13983     uint32_t currentFrameIndex,
13984     bool overlappingMoveSupported) :
13985     VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
13986     m_AllocationCount(0),
13987     m_AllAllocations(false),
13988     m_BytesMoved(0),
13989     m_AllocationsMoved(0),
13990     m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
13991 {
13992     // Create block info for each block.
13993     const size_t blockCount = m_pBlockVector->m_Blocks.size();
13994     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13995     {
13996         BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
13997         pBlockInfo->m_OriginalBlockIndex = blockIndex;
13998         pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
13999         m_Blocks.push_back(pBlockInfo);
14000     }
14001 
14002     // Sort them by m_pBlock pointer value.
14003     VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
14004 }
14005 
~VmaDefragmentationAlgorithm_Generic()14006 VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
14007 {
14008     for(size_t i = m_Blocks.size(); i--; )
14009     {
14010         vma_delete(m_hAllocator, m_Blocks[i]);
14011     }
14012 }
14013 
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)14014 void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
14015 {
14016     // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
14017     if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
14018     {
14019         VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
14020         BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
14021         if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
14022         {
14023             AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
14024             (*it)->m_Allocations.push_back(allocInfo);
14025         }
14026         else
14027         {
14028             VMA_ASSERT(0);
14029         }
14030 
14031         ++m_AllocationCount;
14032     }
14033 }
14034 
DefragmentRound(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,bool freeOldAllocations)14035 VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
14036     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14037     VkDeviceSize maxBytesToMove,
14038     uint32_t maxAllocationsToMove,
14039     bool freeOldAllocations)
14040 {
14041     if(m_Blocks.empty())
14042     {
14043         return VK_SUCCESS;
14044     }
14045 
14046     // This is a choice based on research.
14047     // Option 1:
14048     uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
14049     // Option 2:
14050     //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
14051     // Option 3:
14052     //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
14053 
14054     size_t srcBlockMinIndex = 0;
14055     // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
14056     /*
14057     if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
14058     {
14059         const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
14060         if(blocksWithNonMovableCount > 0)
14061         {
14062             srcBlockMinIndex = blocksWithNonMovableCount - 1;
14063         }
14064     }
14065     */
14066 
14067     size_t srcBlockIndex = m_Blocks.size() - 1;
14068     size_t srcAllocIndex = SIZE_MAX;
14069     for(;;)
14070     {
14071         // 1. Find next allocation to move.
14072         // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
14073         // 1.2. Then start from last to first m_Allocations.
14074         while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
14075         {
14076             if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
14077             {
14078                 // Finished: no more allocations to process.
14079                 if(srcBlockIndex == srcBlockMinIndex)
14080                 {
14081                     return VK_SUCCESS;
14082                 }
14083                 else
14084                 {
14085                     --srcBlockIndex;
14086                     srcAllocIndex = SIZE_MAX;
14087                 }
14088             }
14089             else
14090             {
14091                 srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
14092             }
14093         }
14094 
14095         BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
14096         AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
14097 
14098         const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
14099         const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
14100         const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
14101         const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
14102 
14103         // 2. Try to find new place for this allocation in preceding or current block.
14104         for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
14105         {
14106             BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
14107             VmaAllocationRequest dstAllocRequest;
14108             if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
14109                 m_CurrentFrameIndex,
14110                 m_pBlockVector->GetFrameInUseCount(),
14111                 m_pBlockVector->GetBufferImageGranularity(),
14112                 size,
14113                 alignment,
14114                 false, // upperAddress
14115                 suballocType,
14116                 false, // canMakeOtherLost
14117                 strategy,
14118                 &dstAllocRequest) &&
14119             MoveMakesSense(
14120                 dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
14121             {
14122                 VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
14123 
14124                 // Reached limit on number of allocations or bytes to move.
14125                 if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
14126                     (m_BytesMoved + size > maxBytesToMove))
14127                 {
14128                     return VK_SUCCESS;
14129                 }
14130 
14131                 VmaDefragmentationMove move = {};
14132                 move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
14133                 move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
14134                 move.srcOffset = srcOffset;
14135                 move.dstOffset = dstAllocRequest.offset;
14136                 move.size = size;
14137                 move.hAllocation = allocInfo.m_hAllocation;
14138                 move.pSrcBlock = pSrcBlockInfo->m_pBlock;
14139                 move.pDstBlock = pDstBlockInfo->m_pBlock;
14140 
14141                 moves.push_back(move);
14142 
14143                 pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
14144                     dstAllocRequest,
14145                     suballocType,
14146                     size,
14147                     allocInfo.m_hAllocation);
14148 
14149                 if(freeOldAllocations)
14150                 {
14151                     pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
14152                     allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
14153                 }
14154 
14155                 if(allocInfo.m_pChanged != VMA_NULL)
14156                 {
14157                     *allocInfo.m_pChanged = VK_TRUE;
14158                 }
14159 
14160                 ++m_AllocationsMoved;
14161                 m_BytesMoved += size;
14162 
14163                 VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
14164 
14165                 break;
14166             }
14167         }
14168 
14169         // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
14170 
14171         if(srcAllocIndex > 0)
14172         {
14173             --srcAllocIndex;
14174         }
14175         else
14176         {
14177             if(srcBlockIndex > 0)
14178             {
14179                 --srcBlockIndex;
14180                 srcAllocIndex = SIZE_MAX;
14181             }
14182             else
14183             {
14184                 return VK_SUCCESS;
14185             }
14186         }
14187     }
14188 }
14189 
CalcBlocksWithNonMovableCount()14190 size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
14191 {
14192     size_t result = 0;
14193     for(size_t i = 0; i < m_Blocks.size(); ++i)
14194     {
14195         if(m_Blocks[i]->m_HasNonMovableAllocations)
14196         {
14197             ++result;
14198         }
14199     }
14200     return result;
14201 }
14202 
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,VmaDefragmentationFlags flags)14203 VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
14204     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14205     VkDeviceSize maxBytesToMove,
14206     uint32_t maxAllocationsToMove,
14207     VmaDefragmentationFlags flags)
14208 {
14209     if(!m_AllAllocations && m_AllocationCount == 0)
14210     {
14211         return VK_SUCCESS;
14212     }
14213 
14214     const size_t blockCount = m_Blocks.size();
14215     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14216     {
14217         BlockInfo* pBlockInfo = m_Blocks[blockIndex];
14218 
14219         if(m_AllAllocations)
14220         {
14221             VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
14222             for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
14223                 it != pMetadata->m_Suballocations.end();
14224                 ++it)
14225             {
14226                 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
14227                 {
14228                     AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
14229                     pBlockInfo->m_Allocations.push_back(allocInfo);
14230                 }
14231             }
14232         }
14233 
14234         pBlockInfo->CalcHasNonMovableAllocations();
14235 
14236         // This is a choice based on research.
14237         // Option 1:
14238         pBlockInfo->SortAllocationsByOffsetDescending();
14239         // Option 2:
14240         //pBlockInfo->SortAllocationsBySizeDescending();
14241     }
14242 
14243     // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
14244     VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
14245 
14246     // This is a choice based on research.
14247     const uint32_t roundCount = 2;
14248 
14249     // Execute defragmentation rounds (the main part).
14250     VkResult result = VK_SUCCESS;
14251     for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
14252     {
14253         result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove, !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL));
14254     }
14255 
14256     return result;
14257 }
14258 
MoveMakesSense(size_t dstBlockIndex,VkDeviceSize dstOffset,size_t srcBlockIndex,VkDeviceSize srcOffset)14259 bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
14260         size_t dstBlockIndex, VkDeviceSize dstOffset,
14261         size_t srcBlockIndex, VkDeviceSize srcOffset)
14262 {
14263     if(dstBlockIndex < srcBlockIndex)
14264     {
14265         return true;
14266     }
14267     if(dstBlockIndex > srcBlockIndex)
14268     {
14269         return false;
14270     }
14271     if(dstOffset < srcOffset)
14272     {
14273         return true;
14274     }
14275     return false;
14276 }
14277 
14278 ////////////////////////////////////////////////////////////////////////////////
14279 // VmaDefragmentationAlgorithm_Fast
14280 
VmaDefragmentationAlgorithm_Fast(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)14281 VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
14282     VmaAllocator hAllocator,
14283     VmaBlockVector* pBlockVector,
14284     uint32_t currentFrameIndex,
14285     bool overlappingMoveSupported) :
14286     VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
14287     m_OverlappingMoveSupported(overlappingMoveSupported),
14288     m_AllocationCount(0),
14289     m_AllAllocations(false),
14290     m_BytesMoved(0),
14291     m_AllocationsMoved(0),
14292     m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
14293 {
14294     VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
14295 
14296 }
14297 
~VmaDefragmentationAlgorithm_Fast()14298 VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
14299 {
14300 }
14301 
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,VmaDefragmentationFlags flags)14302 VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
14303     VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14304     VkDeviceSize maxBytesToMove,
14305     uint32_t maxAllocationsToMove,
14306     VmaDefragmentationFlags flags)
14307 {
14308     VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
14309 
14310     const size_t blockCount = m_pBlockVector->GetBlockCount();
14311     if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
14312     {
14313         return VK_SUCCESS;
14314     }
14315 
14316     PreprocessMetadata();
14317 
14318     // Sort blocks in order from most destination.
14319 
14320     m_BlockInfos.resize(blockCount);
14321     for(size_t i = 0; i < blockCount; ++i)
14322     {
14323         m_BlockInfos[i].origBlockIndex = i;
14324     }
14325 
14326     VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
14327         return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
14328             m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
14329     });
14330 
14331     // THE MAIN ALGORITHM
14332 
14333     FreeSpaceDatabase freeSpaceDb;
14334 
14335     size_t dstBlockInfoIndex = 0;
14336     size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14337     VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14338     VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14339     VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
14340     VkDeviceSize dstOffset = 0;
14341 
14342     bool end = false;
14343     for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
14344     {
14345         const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
14346         VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
14347         VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
14348         for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
14349             !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
14350         {
14351             VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
14352             const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
14353             const VkDeviceSize srcAllocSize = srcSuballocIt->size;
14354             if(m_AllocationsMoved == maxAllocationsToMove ||
14355                 m_BytesMoved + srcAllocSize > maxBytesToMove)
14356             {
14357                 end = true;
14358                 break;
14359             }
14360             const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
14361 
14362             VmaDefragmentationMove move = {};
14363             // Try to place it in one of free spaces from the database.
14364             size_t freeSpaceInfoIndex;
14365             VkDeviceSize dstAllocOffset;
14366             if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
14367                 freeSpaceInfoIndex, dstAllocOffset))
14368             {
14369                 size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
14370                 VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
14371                 VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
14372 
14373                 // Same block
14374                 if(freeSpaceInfoIndex == srcBlockInfoIndex)
14375                 {
14376                     VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14377 
14378                     // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14379 
14380                     VmaSuballocation suballoc = *srcSuballocIt;
14381                     suballoc.offset = dstAllocOffset;
14382                     suballoc.hAllocation->ChangeOffset(dstAllocOffset);
14383                     m_BytesMoved += srcAllocSize;
14384                     ++m_AllocationsMoved;
14385 
14386                     VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14387                     ++nextSuballocIt;
14388                     pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14389                     srcSuballocIt = nextSuballocIt;
14390 
14391                     InsertSuballoc(pFreeSpaceMetadata, suballoc);
14392 
14393                     move.srcBlockIndex = srcOrigBlockIndex;
14394                     move.dstBlockIndex = freeSpaceOrigBlockIndex;
14395                     move.srcOffset = srcAllocOffset;
14396                     move.dstOffset = dstAllocOffset;
14397                     move.size = srcAllocSize;
14398 
14399                     moves.push_back(move);
14400                 }
14401                 // Different block
14402                 else
14403                 {
14404                     // MOVE OPTION 2: Move the allocation to a different block.
14405 
14406                     VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
14407 
14408                     VmaSuballocation suballoc = *srcSuballocIt;
14409                     suballoc.offset = dstAllocOffset;
14410                     suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
14411                     m_BytesMoved += srcAllocSize;
14412                     ++m_AllocationsMoved;
14413 
14414                     VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14415                     ++nextSuballocIt;
14416                     pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14417                     srcSuballocIt = nextSuballocIt;
14418 
14419                     InsertSuballoc(pFreeSpaceMetadata, suballoc);
14420 
14421                     move.srcBlockIndex = srcOrigBlockIndex;
14422                     move.dstBlockIndex = freeSpaceOrigBlockIndex;
14423                     move.srcOffset = srcAllocOffset;
14424                     move.dstOffset = dstAllocOffset;
14425                     move.size = srcAllocSize;
14426 
14427                     moves.push_back(move);
14428                 }
14429             }
14430             else
14431             {
14432                 dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
14433 
14434                 // If the allocation doesn't fit before the end of dstBlock, forward to next block.
14435                 while(dstBlockInfoIndex < srcBlockInfoIndex &&
14436                     dstAllocOffset + srcAllocSize > dstBlockSize)
14437                 {
14438                     // But before that, register remaining free space at the end of dst block.
14439                     freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
14440 
14441                     ++dstBlockInfoIndex;
14442                     dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14443                     pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14444                     pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14445                     dstBlockSize = pDstMetadata->GetSize();
14446                     dstOffset = 0;
14447                     dstAllocOffset = 0;
14448                 }
14449 
14450                 // Same block
14451                 if(dstBlockInfoIndex == srcBlockInfoIndex)
14452                 {
14453                     VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14454 
14455                     const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
14456 
14457                     bool skipOver = overlap;
14458                     if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
14459                     {
14460                         // If destination and source place overlap, skip if it would move it
14461                         // by only < 1/64 of its size.
14462                         skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
14463                     }
14464 
14465                     if(skipOver)
14466                     {
14467                         freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
14468 
14469                         dstOffset = srcAllocOffset + srcAllocSize;
14470                         ++srcSuballocIt;
14471                     }
14472                     // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14473                     else
14474                     {
14475                         srcSuballocIt->offset = dstAllocOffset;
14476                         srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);
14477                         dstOffset = dstAllocOffset + srcAllocSize;
14478                         m_BytesMoved += srcAllocSize;
14479                         ++m_AllocationsMoved;
14480                         ++srcSuballocIt;
14481 
14482                         move.srcBlockIndex = srcOrigBlockIndex;
14483                         move.dstBlockIndex = dstOrigBlockIndex;
14484                         move.srcOffset = srcAllocOffset;
14485                         move.dstOffset = dstAllocOffset;
14486                         move.size = srcAllocSize;
14487 
14488                         moves.push_back(move);
14489                     }
14490                 }
14491                 // Different block
14492                 else
14493                 {
14494                     // MOVE OPTION 2: Move the allocation to a different block.
14495 
14496                     VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
14497                     VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
14498 
14499                     VmaSuballocation suballoc = *srcSuballocIt;
14500                     suballoc.offset = dstAllocOffset;
14501                     suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
14502                     dstOffset = dstAllocOffset + srcAllocSize;
14503                     m_BytesMoved += srcAllocSize;
14504                     ++m_AllocationsMoved;
14505 
14506                     VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14507                     ++nextSuballocIt;
14508                     pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14509                     srcSuballocIt = nextSuballocIt;
14510 
14511                     pDstMetadata->m_Suballocations.push_back(suballoc);
14512 
14513                     move.srcBlockIndex = srcOrigBlockIndex;
14514                     move.dstBlockIndex = dstOrigBlockIndex;
14515                     move.srcOffset = srcAllocOffset;
14516                     move.dstOffset = dstAllocOffset;
14517                     move.size = srcAllocSize;
14518 
14519                     moves.push_back(move);
14520                 }
14521             }
14522         }
14523     }
14524 
14525     m_BlockInfos.clear();
14526 
14527     PostprocessMetadata();
14528 
14529     return VK_SUCCESS;
14530 }
14531 
PreprocessMetadata()14532 void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
14533 {
14534     const size_t blockCount = m_pBlockVector->GetBlockCount();
14535     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14536     {
14537         VmaBlockMetadata_Generic* const pMetadata =
14538             (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14539         pMetadata->m_FreeCount = 0;
14540         pMetadata->m_SumFreeSize = pMetadata->GetSize();
14541         pMetadata->m_FreeSuballocationsBySize.clear();
14542         for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14543             it != pMetadata->m_Suballocations.end(); )
14544         {
14545             if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
14546             {
14547                 VmaSuballocationList::iterator nextIt = it;
14548                 ++nextIt;
14549                 pMetadata->m_Suballocations.erase(it);
14550                 it = nextIt;
14551             }
14552             else
14553             {
14554                 ++it;
14555             }
14556         }
14557     }
14558 }
14559 
PostprocessMetadata()14560 void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
14561 {
14562     const size_t blockCount = m_pBlockVector->GetBlockCount();
14563     for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14564     {
14565         VmaBlockMetadata_Generic* const pMetadata =
14566             (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14567         const VkDeviceSize blockSize = pMetadata->GetSize();
14568 
14569         // No allocations in this block - entire area is free.
14570         if(pMetadata->m_Suballocations.empty())
14571         {
14572             pMetadata->m_FreeCount = 1;
14573             //pMetadata->m_SumFreeSize is already set to blockSize.
14574             VmaSuballocation suballoc = {
14575                 0, // offset
14576                 blockSize, // size
14577                 VMA_NULL, // hAllocation
14578                 VMA_SUBALLOCATION_TYPE_FREE };
14579             pMetadata->m_Suballocations.push_back(suballoc);
14580             pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
14581         }
14582         // There are some allocations in this block.
14583         else
14584         {
14585             VkDeviceSize offset = 0;
14586             VmaSuballocationList::iterator it;
14587             for(it = pMetadata->m_Suballocations.begin();
14588                 it != pMetadata->m_Suballocations.end();
14589                 ++it)
14590             {
14591                 VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
14592                 VMA_ASSERT(it->offset >= offset);
14593 
14594                 // Need to insert preceding free space.
14595                 if(it->offset > offset)
14596                 {
14597                     ++pMetadata->m_FreeCount;
14598                     const VkDeviceSize freeSize = it->offset - offset;
14599                     VmaSuballocation suballoc = {
14600                         offset, // offset
14601                         freeSize, // size
14602                         VMA_NULL, // hAllocation
14603                         VMA_SUBALLOCATION_TYPE_FREE };
14604                     VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14605                     if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14606                     {
14607                         pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
14608                     }
14609                 }
14610 
14611                 pMetadata->m_SumFreeSize -= it->size;
14612                 offset = it->offset + it->size;
14613             }
14614 
14615             // Need to insert trailing free space.
14616             if(offset < blockSize)
14617             {
14618                 ++pMetadata->m_FreeCount;
14619                 const VkDeviceSize freeSize = blockSize - offset;
14620                 VmaSuballocation suballoc = {
14621                     offset, // offset
14622                     freeSize, // size
14623                     VMA_NULL, // hAllocation
14624                     VMA_SUBALLOCATION_TYPE_FREE };
14625                 VMA_ASSERT(it == pMetadata->m_Suballocations.end());
14626                 VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14627                 if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14628                 {
14629                     pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
14630                 }
14631             }
14632 
14633             VMA_SORT(
14634                 pMetadata->m_FreeSuballocationsBySize.begin(),
14635                 pMetadata->m_FreeSuballocationsBySize.end(),
14636                 VmaSuballocationItemSizeLess());
14637         }
14638 
14639         VMA_HEAVY_ASSERT(pMetadata->Validate());
14640     }
14641 }
14642 
InsertSuballoc(VmaBlockMetadata_Generic * pMetadata,const VmaSuballocation & suballoc)14643 void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
14644 {
14645     // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
14646     VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14647     while(it != pMetadata->m_Suballocations.end())
14648     {
14649         if(it->offset < suballoc.offset)
14650         {
14651             ++it;
14652         }
14653     }
14654     pMetadata->m_Suballocations.insert(it, suballoc);
14655 }
14656 
14657 ////////////////////////////////////////////////////////////////////////////////
14658 // VmaBlockVectorDefragmentationContext
14659 
VmaBlockVectorDefragmentationContext(VmaAllocator hAllocator,VmaPool hCustomPool,VmaBlockVector * pBlockVector,uint32_t currFrameIndex)14660 VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
14661     VmaAllocator hAllocator,
14662     VmaPool hCustomPool,
14663     VmaBlockVector* pBlockVector,
14664     uint32_t currFrameIndex) :
14665     res(VK_SUCCESS),
14666     mutexLocked(false),
14667     blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
14668     defragmentationMoves(VmaStlAllocator<VmaDefragmentationMove>(hAllocator->GetAllocationCallbacks())),
14669     defragmentationMovesProcessed(0),
14670     defragmentationMovesCommitted(0),
14671     hasDefragmentationPlan(0),
14672     m_hAllocator(hAllocator),
14673     m_hCustomPool(hCustomPool),
14674     m_pBlockVector(pBlockVector),
14675     m_CurrFrameIndex(currFrameIndex),
14676     m_pAlgorithm(VMA_NULL),
14677     m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
14678     m_AllAllocations(false)
14679 {
14680 }
14681 
~VmaBlockVectorDefragmentationContext()14682 VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
14683 {
14684     vma_delete(m_hAllocator, m_pAlgorithm);
14685 }
14686 
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)14687 void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
14688 {
14689     AllocInfo info = { hAlloc, pChanged };
14690     m_Allocations.push_back(info);
14691 }
14692 
Begin(bool overlappingMoveSupported,VmaDefragmentationFlags flags)14693 void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags)
14694 {
14695     const bool allAllocations = m_AllAllocations ||
14696         m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
14697 
14698     /********************************
14699     HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
14700     ********************************/
14701 
14702     /*
14703     Fast algorithm is supported only when certain criteria are met:
14704     - VMA_DEBUG_MARGIN is 0.
14705     - All allocations in this block vector are moveable.
14706     - There is no possibility of image/buffer granularity conflict.
14707     - The defragmentation is not incremental
14708     */
14709     if(VMA_DEBUG_MARGIN == 0 &&
14710         allAllocations &&
14711         !m_pBlockVector->IsBufferImageGranularityConflictPossible() &&
14712         !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL))
14713     {
14714         m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
14715             m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14716     }
14717     else
14718     {
14719         m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
14720             m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14721     }
14722 
14723     if(allAllocations)
14724     {
14725         m_pAlgorithm->AddAll();
14726     }
14727     else
14728     {
14729         for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
14730         {
14731             m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
14732         }
14733     }
14734 }
14735 
14736 ////////////////////////////////////////////////////////////////////////////////
14737 // VmaDefragmentationContext
14738 
VmaDefragmentationContext_T(VmaAllocator hAllocator,uint32_t currFrameIndex,uint32_t flags,VmaDefragmentationStats * pStats)14739 VmaDefragmentationContext_T::VmaDefragmentationContext_T(
14740     VmaAllocator hAllocator,
14741     uint32_t currFrameIndex,
14742     uint32_t flags,
14743     VmaDefragmentationStats* pStats) :
14744     m_hAllocator(hAllocator),
14745     m_CurrFrameIndex(currFrameIndex),
14746     m_Flags(flags),
14747     m_pStats(pStats),
14748     m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
14749 {
14750     memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
14751 }
14752 
~VmaDefragmentationContext_T()14753 VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
14754 {
14755     for(size_t i = m_CustomPoolContexts.size(); i--; )
14756     {
14757         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
14758         pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
14759         vma_delete(m_hAllocator, pBlockVectorCtx);
14760     }
14761     for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
14762     {
14763         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
14764         if(pBlockVectorCtx)
14765         {
14766             pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
14767             vma_delete(m_hAllocator, pBlockVectorCtx);
14768         }
14769     }
14770 }
14771 
AddPools(uint32_t poolCount,const VmaPool * pPools)14772 void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, const VmaPool* pPools)
14773 {
14774     for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
14775     {
14776         VmaPool pool = pPools[poolIndex];
14777         VMA_ASSERT(pool);
14778         // Pools with algorithm other than default are not defragmented.
14779         if(pool->m_BlockVector.GetAlgorithm() == 0)
14780         {
14781             VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
14782 
14783             for(size_t i = m_CustomPoolContexts.size(); i--; )
14784             {
14785                 if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
14786                 {
14787                     pBlockVectorDefragCtx = m_CustomPoolContexts[i];
14788                     break;
14789                 }
14790             }
14791 
14792             if(!pBlockVectorDefragCtx)
14793             {
14794                 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14795                     m_hAllocator,
14796                     pool,
14797                     &pool->m_BlockVector,
14798                     m_CurrFrameIndex);
14799                 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
14800             }
14801 
14802             pBlockVectorDefragCtx->AddAll();
14803         }
14804     }
14805 }
14806 
AddAllocations(uint32_t allocationCount,const VmaAllocation * pAllocations,VkBool32 * pAllocationsChanged)14807 void VmaDefragmentationContext_T::AddAllocations(
14808     uint32_t allocationCount,
14809     const VmaAllocation* pAllocations,
14810     VkBool32* pAllocationsChanged)
14811 {
14812     // Dispatch pAllocations among defragmentators. Create them when necessary.
14813     for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14814     {
14815         const VmaAllocation hAlloc = pAllocations[allocIndex];
14816         VMA_ASSERT(hAlloc);
14817         // DedicatedAlloc cannot be defragmented.
14818         if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
14819             // Lost allocation cannot be defragmented.
14820             (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
14821         {
14822             VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
14823 
14824             const VmaPool hAllocPool = hAlloc->GetBlock()->GetParentPool();
14825             // This allocation belongs to custom pool.
14826             if(hAllocPool != VK_NULL_HANDLE)
14827             {
14828                 // Pools with algorithm other than default are not defragmented.
14829                 if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
14830                 {
14831                     for(size_t i = m_CustomPoolContexts.size(); i--; )
14832                     {
14833                         if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
14834                         {
14835                             pBlockVectorDefragCtx = m_CustomPoolContexts[i];
14836                             break;
14837                         }
14838                     }
14839                     if(!pBlockVectorDefragCtx)
14840                     {
14841                         pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14842                             m_hAllocator,
14843                             hAllocPool,
14844                             &hAllocPool->m_BlockVector,
14845                             m_CurrFrameIndex);
14846                         m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
14847                     }
14848                 }
14849             }
14850             // This allocation belongs to default pool.
14851             else
14852             {
14853                 const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
14854                 pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
14855                 if(!pBlockVectorDefragCtx)
14856                 {
14857                     pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14858                         m_hAllocator,
14859                         VMA_NULL, // hCustomPool
14860                         m_hAllocator->m_pBlockVectors[memTypeIndex],
14861                         m_CurrFrameIndex);
14862                     m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
14863                 }
14864             }
14865 
14866             if(pBlockVectorDefragCtx)
14867             {
14868                 VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
14869                     &pAllocationsChanged[allocIndex] : VMA_NULL;
14870                 pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
14871             }
14872         }
14873     }
14874 }
14875 
Defragment(VkDeviceSize maxCpuBytesToMove,uint32_t maxCpuAllocationsToMove,VkDeviceSize maxGpuBytesToMove,uint32_t maxGpuAllocationsToMove,VkCommandBuffer commandBuffer,VmaDefragmentationStats * pStats,VmaDefragmentationFlags flags)14876 VkResult VmaDefragmentationContext_T::Defragment(
14877     VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
14878     VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
14879     VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags)
14880 {
14881     if(pStats)
14882     {
14883         memset(pStats, 0, sizeof(VmaDefragmentationStats));
14884     }
14885 
14886     if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
14887     {
14888         // For incremental defragmetnations, we just earmark how much we can move
14889         // The real meat is in the defragmentation steps
14890         m_MaxCpuBytesToMove = maxCpuBytesToMove;
14891         m_MaxCpuAllocationsToMove = maxCpuAllocationsToMove;
14892 
14893         m_MaxGpuBytesToMove = maxGpuBytesToMove;
14894         m_MaxGpuAllocationsToMove = maxGpuAllocationsToMove;
14895 
14896         if(m_MaxCpuBytesToMove == 0 && m_MaxCpuAllocationsToMove == 0 &&
14897             m_MaxGpuBytesToMove == 0 && m_MaxGpuAllocationsToMove == 0)
14898             return VK_SUCCESS;
14899 
14900         return VK_NOT_READY;
14901     }
14902 
14903     if(commandBuffer == VK_NULL_HANDLE)
14904     {
14905         maxGpuBytesToMove = 0;
14906         maxGpuAllocationsToMove = 0;
14907     }
14908 
14909     VkResult res = VK_SUCCESS;
14910 
14911     // Process default pools.
14912     for(uint32_t memTypeIndex = 0;
14913         memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
14914         ++memTypeIndex)
14915     {
14916         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14917         if(pBlockVectorCtx)
14918         {
14919             VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14920             pBlockVectorCtx->GetBlockVector()->Defragment(
14921                 pBlockVectorCtx,
14922                 pStats, flags,
14923                 maxCpuBytesToMove, maxCpuAllocationsToMove,
14924                 maxGpuBytesToMove, maxGpuAllocationsToMove,
14925                 commandBuffer);
14926             if(pBlockVectorCtx->res != VK_SUCCESS)
14927             {
14928                 res = pBlockVectorCtx->res;
14929             }
14930         }
14931     }
14932 
14933     // Process custom pools.
14934     for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14935         customCtxIndex < customCtxCount && res >= VK_SUCCESS;
14936         ++customCtxIndex)
14937     {
14938         VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14939         VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14940         pBlockVectorCtx->GetBlockVector()->Defragment(
14941             pBlockVectorCtx,
14942             pStats, flags,
14943             maxCpuBytesToMove, maxCpuAllocationsToMove,
14944             maxGpuBytesToMove, maxGpuAllocationsToMove,
14945             commandBuffer);
14946         if(pBlockVectorCtx->res != VK_SUCCESS)
14947         {
14948             res = pBlockVectorCtx->res;
14949         }
14950     }
14951 
14952     return res;
14953 }
14954 
DefragmentPassBegin(VmaDefragmentationPassInfo * pInfo)14955 VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo)
14956 {
14957     VmaDefragmentationPassMoveInfo* pCurrentMove = pInfo->pMoves;
14958     uint32_t movesLeft = pInfo->moveCount;
14959 
14960     // Process default pools.
14961     for(uint32_t memTypeIndex = 0;
14962         memTypeIndex < m_hAllocator->GetMemoryTypeCount();
14963         ++memTypeIndex)
14964     {
14965         VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14966         if(pBlockVectorCtx)
14967         {
14968             VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14969 
14970             if(!pBlockVectorCtx->hasDefragmentationPlan)
14971             {
14972                 pBlockVectorCtx->GetBlockVector()->Defragment(
14973                     pBlockVectorCtx,
14974                     m_pStats, m_Flags,
14975                     m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
14976                     m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
14977                     VK_NULL_HANDLE);
14978 
14979                 if(pBlockVectorCtx->res < VK_SUCCESS)
14980                     continue;
14981 
14982                 pBlockVectorCtx->hasDefragmentationPlan = true;
14983             }
14984 
14985             const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
14986                 pBlockVectorCtx,
14987                 pCurrentMove, movesLeft);
14988 
14989             movesLeft -= processed;
14990             pCurrentMove += processed;
14991         }
14992     }
14993 
14994     // Process custom pools.
14995     for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14996         customCtxIndex < customCtxCount;
14997         ++customCtxIndex)
14998     {
14999         VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
15000         VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
15001 
15002         if(!pBlockVectorCtx->hasDefragmentationPlan)
15003         {
15004             pBlockVectorCtx->GetBlockVector()->Defragment(
15005                 pBlockVectorCtx,
15006                 m_pStats, m_Flags,
15007                 m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
15008                 m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
15009                 VK_NULL_HANDLE);
15010 
15011             if(pBlockVectorCtx->res < VK_SUCCESS)
15012                 continue;
15013 
15014             pBlockVectorCtx->hasDefragmentationPlan = true;
15015         }
15016 
15017         const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
15018             pBlockVectorCtx,
15019             pCurrentMove, movesLeft);
15020 
15021         movesLeft -= processed;
15022         pCurrentMove += processed;
15023     }
15024 
15025     pInfo->moveCount = pInfo->moveCount - movesLeft;
15026 
15027     return VK_SUCCESS;
15028 }
DefragmentPassEnd()15029 VkResult VmaDefragmentationContext_T::DefragmentPassEnd()
15030 {
15031     VkResult res = VK_SUCCESS;
15032 
15033     // Process default pools.
15034     for(uint32_t memTypeIndex = 0;
15035         memTypeIndex < m_hAllocator->GetMemoryTypeCount();
15036         ++memTypeIndex)
15037     {
15038         VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
15039         if(pBlockVectorCtx)
15040         {
15041             VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
15042 
15043             if(!pBlockVectorCtx->hasDefragmentationPlan)
15044             {
15045                 res = VK_NOT_READY;
15046                 continue;
15047             }
15048 
15049             pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
15050                 pBlockVectorCtx, m_pStats);
15051 
15052             if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
15053                 res = VK_NOT_READY;
15054         }
15055     }
15056 
15057     // Process custom pools.
15058     for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
15059         customCtxIndex < customCtxCount;
15060         ++customCtxIndex)
15061     {
15062         VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
15063         VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
15064 
15065         if(!pBlockVectorCtx->hasDefragmentationPlan)
15066         {
15067             res = VK_NOT_READY;
15068             continue;
15069         }
15070 
15071         pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
15072             pBlockVectorCtx, m_pStats);
15073 
15074         if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
15075             res = VK_NOT_READY;
15076     }
15077 
15078     return res;
15079 }
15080 
15081 ////////////////////////////////////////////////////////////////////////////////
15082 // VmaRecorder
15083 
15084 #if VMA_RECORDING_ENABLED
15085 
VmaRecorder()15086 VmaRecorder::VmaRecorder() :
15087     m_UseMutex(true),
15088     m_Flags(0),
15089     m_File(VMA_NULL),
15090     m_RecordingStartTime(std::chrono::high_resolution_clock::now())
15091 {
15092 }
15093 
Init(const VmaRecordSettings & settings,bool useMutex)15094 VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
15095 {
15096     m_UseMutex = useMutex;
15097     m_Flags = settings.flags;
15098 
15099 #if defined(_WIN32)
15100     // Open file for writing.
15101     errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
15102 
15103     if(err != 0)
15104     {
15105         return VK_ERROR_INITIALIZATION_FAILED;
15106     }
15107 #else
15108     // Open file for writing.
15109     m_File = fopen(settings.pFilePath, "wb");
15110 
15111     if(m_File == 0)
15112     {
15113         return VK_ERROR_INITIALIZATION_FAILED;
15114     }
15115 #endif
15116 
15117     // Write header.
15118     fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
15119     fprintf(m_File, "%s\n", "1,8");
15120 
15121     return VK_SUCCESS;
15122 }
15123 
~VmaRecorder()15124 VmaRecorder::~VmaRecorder()
15125 {
15126     if(m_File != VMA_NULL)
15127     {
15128         fclose(m_File);
15129     }
15130 }
15131 
RecordCreateAllocator(uint32_t frameIndex)15132 void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
15133 {
15134     CallParams callParams;
15135     GetBasicParams(callParams);
15136 
15137     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15138     fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
15139     Flush();
15140 }
15141 
RecordDestroyAllocator(uint32_t frameIndex)15142 void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
15143 {
15144     CallParams callParams;
15145     GetBasicParams(callParams);
15146 
15147     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15148     fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
15149     Flush();
15150 }
15151 
RecordCreatePool(uint32_t frameIndex,const VmaPoolCreateInfo & createInfo,VmaPool pool)15152 void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
15153 {
15154     CallParams callParams;
15155     GetBasicParams(callParams);
15156 
15157     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15158     fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
15159         createInfo.memoryTypeIndex,
15160         createInfo.flags,
15161         createInfo.blockSize,
15162         (uint64_t)createInfo.minBlockCount,
15163         (uint64_t)createInfo.maxBlockCount,
15164         createInfo.frameInUseCount,
15165         pool);
15166     Flush();
15167 }
15168 
RecordDestroyPool(uint32_t frameIndex,VmaPool pool)15169 void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
15170 {
15171     CallParams callParams;
15172     GetBasicParams(callParams);
15173 
15174     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15175     fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
15176         pool);
15177     Flush();
15178 }
15179 
RecordAllocateMemory(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)15180 void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
15181         const VkMemoryRequirements& vkMemReq,
15182         const VmaAllocationCreateInfo& createInfo,
15183         VmaAllocation allocation)
15184 {
15185     CallParams callParams;
15186     GetBasicParams(callParams);
15187 
15188     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15189     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15190     fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15191         vkMemReq.size,
15192         vkMemReq.alignment,
15193         vkMemReq.memoryTypeBits,
15194         createInfo.flags,
15195         createInfo.usage,
15196         createInfo.requiredFlags,
15197         createInfo.preferredFlags,
15198         createInfo.memoryTypeBits,
15199         createInfo.pool,
15200         allocation,
15201         userDataStr.GetString());
15202     Flush();
15203 }
15204 
RecordAllocateMemoryPages(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,uint64_t allocationCount,const VmaAllocation * pAllocations)15205 void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
15206     const VkMemoryRequirements& vkMemReq,
15207     const VmaAllocationCreateInfo& createInfo,
15208     uint64_t allocationCount,
15209     const VmaAllocation* pAllocations)
15210 {
15211     CallParams callParams;
15212     GetBasicParams(callParams);
15213 
15214     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15215     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15216     fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
15217         vkMemReq.size,
15218         vkMemReq.alignment,
15219         vkMemReq.memoryTypeBits,
15220         createInfo.flags,
15221         createInfo.usage,
15222         createInfo.requiredFlags,
15223         createInfo.preferredFlags,
15224         createInfo.memoryTypeBits,
15225         createInfo.pool);
15226     PrintPointerList(allocationCount, pAllocations);
15227     fprintf(m_File, ",%s\n", userDataStr.GetString());
15228     Flush();
15229 }
15230 
RecordAllocateMemoryForBuffer(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)15231 void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
15232     const VkMemoryRequirements& vkMemReq,
15233     bool requiresDedicatedAllocation,
15234     bool prefersDedicatedAllocation,
15235     const VmaAllocationCreateInfo& createInfo,
15236     VmaAllocation allocation)
15237 {
15238     CallParams callParams;
15239     GetBasicParams(callParams);
15240 
15241     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15242     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15243     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,
15244         vkMemReq.size,
15245         vkMemReq.alignment,
15246         vkMemReq.memoryTypeBits,
15247         requiresDedicatedAllocation ? 1 : 0,
15248         prefersDedicatedAllocation ? 1 : 0,
15249         createInfo.flags,
15250         createInfo.usage,
15251         createInfo.requiredFlags,
15252         createInfo.preferredFlags,
15253         createInfo.memoryTypeBits,
15254         createInfo.pool,
15255         allocation,
15256         userDataStr.GetString());
15257     Flush();
15258 }
15259 
RecordAllocateMemoryForImage(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)15260 void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
15261     const VkMemoryRequirements& vkMemReq,
15262     bool requiresDedicatedAllocation,
15263     bool prefersDedicatedAllocation,
15264     const VmaAllocationCreateInfo& createInfo,
15265     VmaAllocation allocation)
15266 {
15267     CallParams callParams;
15268     GetBasicParams(callParams);
15269 
15270     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15271     UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15272     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,
15273         vkMemReq.size,
15274         vkMemReq.alignment,
15275         vkMemReq.memoryTypeBits,
15276         requiresDedicatedAllocation ? 1 : 0,
15277         prefersDedicatedAllocation ? 1 : 0,
15278         createInfo.flags,
15279         createInfo.usage,
15280         createInfo.requiredFlags,
15281         createInfo.preferredFlags,
15282         createInfo.memoryTypeBits,
15283         createInfo.pool,
15284         allocation,
15285         userDataStr.GetString());
15286     Flush();
15287 }
15288 
RecordFreeMemory(uint32_t frameIndex,VmaAllocation allocation)15289 void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
15290     VmaAllocation allocation)
15291 {
15292     CallParams callParams;
15293     GetBasicParams(callParams);
15294 
15295     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15296     fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15297         allocation);
15298     Flush();
15299 }
15300 
RecordFreeMemoryPages(uint32_t frameIndex,uint64_t allocationCount,const VmaAllocation * pAllocations)15301 void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
15302     uint64_t allocationCount,
15303     const VmaAllocation* pAllocations)
15304 {
15305     CallParams callParams;
15306     GetBasicParams(callParams);
15307 
15308     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15309     fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
15310     PrintPointerList(allocationCount, pAllocations);
15311     fprintf(m_File, "\n");
15312     Flush();
15313 }
15314 
RecordSetAllocationUserData(uint32_t frameIndex,VmaAllocation allocation,const void * pUserData)15315 void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
15316     VmaAllocation allocation,
15317     const void* pUserData)
15318 {
15319     CallParams callParams;
15320     GetBasicParams(callParams);
15321 
15322     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15323     UserDataString userDataStr(
15324         allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
15325         pUserData);
15326     fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15327         allocation,
15328         userDataStr.GetString());
15329     Flush();
15330 }
15331 
RecordCreateLostAllocation(uint32_t frameIndex,VmaAllocation allocation)15332 void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
15333     VmaAllocation allocation)
15334 {
15335     CallParams callParams;
15336     GetBasicParams(callParams);
15337 
15338     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15339     fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15340         allocation);
15341     Flush();
15342 }
15343 
RecordMapMemory(uint32_t frameIndex,VmaAllocation allocation)15344 void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
15345     VmaAllocation allocation)
15346 {
15347     CallParams callParams;
15348     GetBasicParams(callParams);
15349 
15350     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15351     fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15352         allocation);
15353     Flush();
15354 }
15355 
RecordUnmapMemory(uint32_t frameIndex,VmaAllocation allocation)15356 void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
15357     VmaAllocation allocation)
15358 {
15359     CallParams callParams;
15360     GetBasicParams(callParams);
15361 
15362     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15363     fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15364         allocation);
15365     Flush();
15366 }
15367 
RecordFlushAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)15368 void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
15369     VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15370 {
15371     CallParams callParams;
15372     GetBasicParams(callParams);
15373 
15374     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15375     fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15376         allocation,
15377         offset,
15378         size);
15379     Flush();
15380 }
15381 
RecordInvalidateAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)15382 void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
15383     VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15384 {
15385     CallParams callParams;
15386     GetBasicParams(callParams);
15387 
15388     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15389     fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15390         allocation,
15391         offset,
15392         size);
15393     Flush();
15394 }
15395 
RecordCreateBuffer(uint32_t frameIndex,const VkBufferCreateInfo & bufCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)15396 void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
15397     const VkBufferCreateInfo& bufCreateInfo,
15398     const VmaAllocationCreateInfo& allocCreateInfo,
15399     VmaAllocation allocation)
15400 {
15401     CallParams callParams;
15402     GetBasicParams(callParams);
15403 
15404     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15405     UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15406     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,
15407         bufCreateInfo.flags,
15408         bufCreateInfo.size,
15409         bufCreateInfo.usage,
15410         bufCreateInfo.sharingMode,
15411         allocCreateInfo.flags,
15412         allocCreateInfo.usage,
15413         allocCreateInfo.requiredFlags,
15414         allocCreateInfo.preferredFlags,
15415         allocCreateInfo.memoryTypeBits,
15416         allocCreateInfo.pool,
15417         allocation,
15418         userDataStr.GetString());
15419     Flush();
15420 }
15421 
RecordCreateImage(uint32_t frameIndex,const VkImageCreateInfo & imageCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)15422 void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
15423     const VkImageCreateInfo& imageCreateInfo,
15424     const VmaAllocationCreateInfo& allocCreateInfo,
15425     VmaAllocation allocation)
15426 {
15427     CallParams callParams;
15428     GetBasicParams(callParams);
15429 
15430     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15431     UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15432     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,
15433         imageCreateInfo.flags,
15434         imageCreateInfo.imageType,
15435         imageCreateInfo.format,
15436         imageCreateInfo.extent.width,
15437         imageCreateInfo.extent.height,
15438         imageCreateInfo.extent.depth,
15439         imageCreateInfo.mipLevels,
15440         imageCreateInfo.arrayLayers,
15441         imageCreateInfo.samples,
15442         imageCreateInfo.tiling,
15443         imageCreateInfo.usage,
15444         imageCreateInfo.sharingMode,
15445         imageCreateInfo.initialLayout,
15446         allocCreateInfo.flags,
15447         allocCreateInfo.usage,
15448         allocCreateInfo.requiredFlags,
15449         allocCreateInfo.preferredFlags,
15450         allocCreateInfo.memoryTypeBits,
15451         allocCreateInfo.pool,
15452         allocation,
15453         userDataStr.GetString());
15454     Flush();
15455 }
15456 
RecordDestroyBuffer(uint32_t frameIndex,VmaAllocation allocation)15457 void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
15458     VmaAllocation allocation)
15459 {
15460     CallParams callParams;
15461     GetBasicParams(callParams);
15462 
15463     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15464     fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
15465         allocation);
15466     Flush();
15467 }
15468 
RecordDestroyImage(uint32_t frameIndex,VmaAllocation allocation)15469 void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
15470     VmaAllocation allocation)
15471 {
15472     CallParams callParams;
15473     GetBasicParams(callParams);
15474 
15475     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15476     fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
15477         allocation);
15478     Flush();
15479 }
15480 
RecordTouchAllocation(uint32_t frameIndex,VmaAllocation allocation)15481 void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
15482     VmaAllocation allocation)
15483 {
15484     CallParams callParams;
15485     GetBasicParams(callParams);
15486 
15487     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15488     fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15489         allocation);
15490     Flush();
15491 }
15492 
RecordGetAllocationInfo(uint32_t frameIndex,VmaAllocation allocation)15493 void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
15494     VmaAllocation allocation)
15495 {
15496     CallParams callParams;
15497     GetBasicParams(callParams);
15498 
15499     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15500     fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
15501         allocation);
15502     Flush();
15503 }
15504 
RecordMakePoolAllocationsLost(uint32_t frameIndex,VmaPool pool)15505 void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
15506     VmaPool pool)
15507 {
15508     CallParams callParams;
15509     GetBasicParams(callParams);
15510 
15511     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15512     fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
15513         pool);
15514     Flush();
15515 }
15516 
RecordDefragmentationBegin(uint32_t frameIndex,const VmaDefragmentationInfo2 & info,VmaDefragmentationContext ctx)15517 void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
15518     const VmaDefragmentationInfo2& info,
15519     VmaDefragmentationContext ctx)
15520 {
15521     CallParams callParams;
15522     GetBasicParams(callParams);
15523 
15524     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15525     fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
15526         info.flags);
15527     PrintPointerList(info.allocationCount, info.pAllocations);
15528     fprintf(m_File, ",");
15529     PrintPointerList(info.poolCount, info.pPools);
15530     fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
15531         info.maxCpuBytesToMove,
15532         info.maxCpuAllocationsToMove,
15533         info.maxGpuBytesToMove,
15534         info.maxGpuAllocationsToMove,
15535         info.commandBuffer,
15536         ctx);
15537     Flush();
15538 }
15539 
RecordDefragmentationEnd(uint32_t frameIndex,VmaDefragmentationContext ctx)15540 void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
15541     VmaDefragmentationContext ctx)
15542 {
15543     CallParams callParams;
15544     GetBasicParams(callParams);
15545 
15546     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15547     fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
15548         ctx);
15549     Flush();
15550 }
15551 
RecordSetPoolName(uint32_t frameIndex,VmaPool pool,const char * name)15552 void VmaRecorder::RecordSetPoolName(uint32_t frameIndex,
15553     VmaPool pool,
15554     const char* name)
15555 {
15556     CallParams callParams;
15557     GetBasicParams(callParams);
15558 
15559     VmaMutexLock lock(m_FileMutex, m_UseMutex);
15560     fprintf(m_File, "%u,%.3f,%u,vmaSetPoolName,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15561         pool, name != VMA_NULL ? name : "");
15562     Flush();
15563 }
15564 
UserDataString(VmaAllocationCreateFlags allocFlags,const void * pUserData)15565 VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
15566 {
15567     if(pUserData != VMA_NULL)
15568     {
15569         if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
15570         {
15571             m_Str = (const char*)pUserData;
15572         }
15573         else
15574         {
15575             // If VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is not specified, convert the string's memory address to a string and store it.
15576             snprintf(m_PtrStr, 17, "%p", pUserData);
15577             m_Str = m_PtrStr;
15578         }
15579     }
15580     else
15581     {
15582         m_Str = "";
15583     }
15584 }
15585 
WriteConfiguration(const VkPhysicalDeviceProperties & devProps,const VkPhysicalDeviceMemoryProperties & memProps,uint32_t vulkanApiVersion,bool dedicatedAllocationExtensionEnabled,bool bindMemory2ExtensionEnabled,bool memoryBudgetExtensionEnabled,bool deviceCoherentMemoryExtensionEnabled)15586 void VmaRecorder::WriteConfiguration(
15587     const VkPhysicalDeviceProperties& devProps,
15588     const VkPhysicalDeviceMemoryProperties& memProps,
15589     uint32_t vulkanApiVersion,
15590     bool dedicatedAllocationExtensionEnabled,
15591     bool bindMemory2ExtensionEnabled,
15592     bool memoryBudgetExtensionEnabled,
15593     bool deviceCoherentMemoryExtensionEnabled)
15594 {
15595     fprintf(m_File, "Config,Begin\n");
15596 
15597     fprintf(m_File, "VulkanApiVersion,%u,%u\n", VK_VERSION_MAJOR(vulkanApiVersion), VK_VERSION_MINOR(vulkanApiVersion));
15598 
15599     fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
15600     fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
15601     fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
15602     fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
15603     fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
15604     fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
15605 
15606     fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
15607     fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
15608     fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
15609 
15610     fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
15611     for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
15612     {
15613         fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
15614         fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
15615     }
15616     fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
15617     for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
15618     {
15619         fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
15620         fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
15621     }
15622 
15623     fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
15624     fprintf(m_File, "Extension,VK_KHR_bind_memory2,%u\n", bindMemory2ExtensionEnabled ? 1 : 0);
15625     fprintf(m_File, "Extension,VK_EXT_memory_budget,%u\n", memoryBudgetExtensionEnabled ? 1 : 0);
15626     fprintf(m_File, "Extension,VK_AMD_device_coherent_memory,%u\n", deviceCoherentMemoryExtensionEnabled ? 1 : 0);
15627 
15628     fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
15629     fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);
15630     fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
15631     fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
15632     fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
15633     fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
15634     fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
15635     fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
15636     fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15637 
15638     fprintf(m_File, "Config,End\n");
15639 }
15640 
GetBasicParams(CallParams & outParams)15641 void VmaRecorder::GetBasicParams(CallParams& outParams)
15642 {
15643     #if defined(_WIN32)
15644         outParams.threadId = GetCurrentThreadId();
15645     #else
15646         // Use C++11 features to get thread id and convert it to uint32_t.
15647         // There is room for optimization since sstream is quite slow.
15648         // Is there a better way to convert std::this_thread::get_id() to uint32_t?
15649         std::thread::id thread_id = std::this_thread::get_id();
15650         stringstream thread_id_to_string_converter;
15651         thread_id_to_string_converter << thread_id;
15652         string thread_id_as_string = thread_id_to_string_converter.str();
15653         outParams.threadId = static_cast<uint32_t>(std::stoi(thread_id_as_string.c_str()));
15654     #endif
15655 
15656     auto current_time = std::chrono::high_resolution_clock::now();
15657 
15658     outParams.time = std::chrono::duration<double, std::chrono::seconds::period>(current_time - m_RecordingStartTime).count();
15659 }
15660 
PrintPointerList(uint64_t count,const VmaAllocation * pItems)15661 void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
15662 {
15663     if(count)
15664     {
15665         fprintf(m_File, "%p", pItems[0]);
15666         for(uint64_t i = 1; i < count; ++i)
15667         {
15668             fprintf(m_File, " %p", pItems[i]);
15669         }
15670     }
15671 }
15672 
Flush()15673 void VmaRecorder::Flush()
15674 {
15675     if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
15676     {
15677         fflush(m_File);
15678     }
15679 }
15680 
15681 #endif // #if VMA_RECORDING_ENABLED
15682 
15683 ////////////////////////////////////////////////////////////////////////////////
15684 // VmaAllocationObjectAllocator
15685 
VmaAllocationObjectAllocator(const VkAllocationCallbacks * pAllocationCallbacks)15686 VmaAllocationObjectAllocator::VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks) :
15687     m_Allocator(pAllocationCallbacks, 1024)
15688 {
15689 }
15690 
Allocate(Types...args)15691 template<typename... Types> VmaAllocation VmaAllocationObjectAllocator::Allocate(Types... args)
15692 {
15693     VmaMutexLock mutexLock(m_Mutex);
15694     return m_Allocator.Alloc<Types...>(std::forward<Types>(args)...);
15695 }
15696 
Free(VmaAllocation hAlloc)15697 void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc)
15698 {
15699     VmaMutexLock mutexLock(m_Mutex);
15700     m_Allocator.Free(hAlloc);
15701 }
15702 
15703 ////////////////////////////////////////////////////////////////////////////////
15704 // VmaAllocator_T
15705 
VmaAllocator_T(const VmaAllocatorCreateInfo * pCreateInfo)15706 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
15707     m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
15708     m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0),
15709     m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
15710     m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0),
15711     m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0),
15712     m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0),
15713     m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0),
15714     m_hDevice(pCreateInfo->device),
15715     m_hInstance(pCreateInfo->instance),
15716     m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
15717     m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
15718         *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
15719     m_AllocationObjectAllocator(&m_AllocationCallbacks),
15720     m_HeapSizeLimitMask(0),
15721     m_PreferredLargeHeapBlockSize(0),
15722     m_PhysicalDevice(pCreateInfo->physicalDevice),
15723     m_CurrentFrameIndex(0),
15724     m_GpuDefragmentationMemoryTypeBits(UINT32_MAX),
15725     m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks())),
15726     m_NextPoolId(0),
15727     m_GlobalMemoryTypeBits(UINT32_MAX)
15728 #if VMA_RECORDING_ENABLED
15729     ,m_pRecorder(VMA_NULL)
15730 #endif
15731 {
15732     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15733     {
15734         m_UseKhrDedicatedAllocation = false;
15735         m_UseKhrBindMemory2 = false;
15736     }
15737 
15738     if(VMA_DEBUG_DETECT_CORRUPTION)
15739     {
15740         // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
15741         VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
15742     }
15743 
15744     VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance);
15745 
15746     if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
15747     {
15748 #if !(VMA_DEDICATED_ALLOCATION)
15749         if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
15750         {
15751             VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
15752         }
15753 #endif
15754 #if !(VMA_BIND_MEMORY2)
15755         if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0)
15756         {
15757             VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros.");
15758         }
15759 #endif
15760     }
15761 #if !(VMA_MEMORY_BUDGET)
15762     if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0)
15763     {
15764         VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros.");
15765     }
15766 #endif
15767 #if !(VMA_BUFFER_DEVICE_ADDRESS)
15768     if(m_UseKhrBufferDeviceAddress)
15769     {
15770         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.");
15771     }
15772 #endif
15773 #if VMA_VULKAN_VERSION < 1002000
15774     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0))
15775     {
15776         VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros.");
15777     }
15778 #endif
15779 #if VMA_VULKAN_VERSION < 1001000
15780     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15781     {
15782         VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros.");
15783     }
15784 #endif
15785 
15786     memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
15787     memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
15788     memset(&m_MemProps, 0, sizeof(m_MemProps));
15789 
15790     memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
15791     memset(&m_pReservedBlockVectors, 0, sizeof(m_pReservedBlockVectors));
15792     memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations));
15793     memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions));
15794 
15795     if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
15796     {
15797         m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData;
15798         m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
15799         m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
15800     }
15801 
15802     ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
15803 
15804     (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
15805     (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
15806 
15807     VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT));
15808     VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
15809     VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
15810     VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
15811 
15812     m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
15813         pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15814 
15815     m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits();
15816 
15817     if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
15818     {
15819         for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
15820         {
15821             const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
15822             if(limit != VK_WHOLE_SIZE)
15823             {
15824                 m_HeapSizeLimitMask |= 1u << heapIndex;
15825                 if(limit < m_MemProps.memoryHeaps[heapIndex].size)
15826                 {
15827                     m_MemProps.memoryHeaps[heapIndex].size = limit;
15828                 }
15829             }
15830         }
15831     }
15832 
15833     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15834     {
15835         const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
15836 
15837         m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
15838             this,
15839             VK_NULL_HANDLE, // hParentPool
15840             memTypeIndex,
15841             preferredBlockSize,
15842             0,
15843             pCreateInfo->maxBlockCount,
15844             GetBufferImageGranularity(),
15845             pCreateInfo->frameInUseCount,
15846             false, // explicitBlockSize
15847             false); // linearAlgorithm
15848         m_pReservedBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
15849             this,
15850             VK_NULL_HANDLE, // hParentPool
15851             memTypeIndex,
15852             preferredBlockSize,
15853             0,
15854             1, // max block count 1
15855             GetBufferImageGranularity(),
15856             pCreateInfo->frameInUseCount,
15857             false, // explicitBlockSize
15858             false); // linearAlgorithm
15859         // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
15860         // becase minBlockCount is 0.
15861         m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
15862 
15863     }
15864 }
15865 
Init(const VmaAllocatorCreateInfo * pCreateInfo)15866 VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
15867 {
15868     VkResult res = VK_SUCCESS;
15869 
15870     if(pCreateInfo->pRecordSettings != VMA_NULL &&
15871         !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
15872     {
15873 #if VMA_RECORDING_ENABLED
15874         m_pRecorder = vma_new(this, VmaRecorder)();
15875         res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
15876         if(res != VK_SUCCESS)
15877         {
15878             return res;
15879         }
15880         m_pRecorder->WriteConfiguration(
15881             m_PhysicalDeviceProperties,
15882             m_MemProps,
15883             m_VulkanApiVersion,
15884             m_UseKhrDedicatedAllocation,
15885             m_UseKhrBindMemory2,
15886             m_UseExtMemoryBudget,
15887             m_UseAmdDeviceCoherentMemory);
15888         m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
15889 #else
15890         VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
15891         return VK_ERROR_FEATURE_NOT_PRESENT;
15892 #endif
15893     }
15894 
15895 #if VMA_MEMORY_BUDGET
15896     if(m_UseExtMemoryBudget)
15897     {
15898         UpdateVulkanBudget();
15899     }
15900 #endif // #if VMA_MEMORY_BUDGET
15901 
15902     return res;
15903 }
15904 
~VmaAllocator_T()15905 VmaAllocator_T::~VmaAllocator_T()
15906 {
15907 #if VMA_RECORDING_ENABLED
15908     if(m_pRecorder != VMA_NULL)
15909     {
15910         m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
15911         vma_delete(this, m_pRecorder);
15912     }
15913 #endif
15914 
15915     VMA_ASSERT(m_Pools.empty());
15916 
15917     for(size_t i = GetMemoryTypeCount(); i--; )
15918     {
15919         if(m_pDedicatedAllocations[i] != VMA_NULL && !m_pDedicatedAllocations[i]->empty())
15920         {
15921             VMA_ASSERT(0 && "Unfreed dedicated allocations found.");
15922         }
15923 
15924         vma_delete(this, m_pDedicatedAllocations[i]);
15925         vma_delete(this, m_pBlockVectors[i]);
15926         vma_delete(this, m_pReservedBlockVectors[i]);
15927     }
15928 }
15929 
ImportVulkanFunctions(const VmaVulkanFunctions * pVulkanFunctions)15930 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
15931 {
15932 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15933     ImportVulkanFunctions_Static();
15934 #endif
15935 
15936     if(pVulkanFunctions != VMA_NULL)
15937     {
15938         ImportVulkanFunctions_Custom(pVulkanFunctions);
15939     }
15940 
15941 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
15942     ImportVulkanFunctions_Dynamic();
15943 #endif
15944 
15945     ValidateVulkanFunctions();
15946 }
15947 
15948 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15949 
ImportVulkanFunctions_Static()15950 void VmaAllocator_T::ImportVulkanFunctions_Static()
15951 {
15952     // Vulkan 1.0
15953     m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties;
15954     m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties;
15955     m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
15956     m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory;
15957     m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory;
15958     m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory;
15959     m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges;
15960     m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges;
15961     m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory;
15962     m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory;
15963     m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements;
15964     m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements;
15965     m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer;
15966     m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer;
15967     m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage;
15968     m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage;
15969     m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer;
15970 
15971     // Vulkan 1.1
15972 #if VMA_VULKAN_VERSION >= 1001000
15973     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15974     {
15975         m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2;
15976         m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2;
15977         m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2;
15978         m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2;
15979         m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2;
15980     }
15981 #endif
15982 }
15983 
15984 #endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15985 
ImportVulkanFunctions_Custom(const VmaVulkanFunctions * pVulkanFunctions)15986 void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions)
15987 {
15988     VMA_ASSERT(pVulkanFunctions != VMA_NULL);
15989 
15990 #define VMA_COPY_IF_NOT_NULL(funcName) \
15991     if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
15992 
15993     VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
15994     VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
15995     VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
15996     VMA_COPY_IF_NOT_NULL(vkFreeMemory);
15997     VMA_COPY_IF_NOT_NULL(vkMapMemory);
15998     VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
15999     VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
16000     VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
16001     VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
16002     VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
16003     VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
16004     VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
16005     VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
16006     VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
16007     VMA_COPY_IF_NOT_NULL(vkCreateImage);
16008     VMA_COPY_IF_NOT_NULL(vkDestroyImage);
16009     VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
16010 
16011 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16012     VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
16013     VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
16014 #endif
16015 
16016 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
16017     VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR);
16018     VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR);
16019 #endif
16020 
16021 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
16022     VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR);
16023 #endif
16024 
16025 #undef VMA_COPY_IF_NOT_NULL
16026 }
16027 
16028 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
16029 
ImportVulkanFunctions_Dynamic()16030 void VmaAllocator_T::ImportVulkanFunctions_Dynamic()
16031 {
16032 #define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \
16033     if(m_VulkanFunctions.memberName == VMA_NULL) \
16034         m_VulkanFunctions.memberName = \
16035             (functionPointerType)vkGetInstanceProcAddr(m_hInstance, functionNameString);
16036 #define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \
16037     if(m_VulkanFunctions.memberName == VMA_NULL) \
16038         m_VulkanFunctions.memberName = \
16039             (functionPointerType)vkGetDeviceProcAddr(m_hDevice, functionNameString);
16040 
16041     VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");
16042     VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties");
16043     VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory");
16044     VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory");
16045     VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory");
16046     VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory");
16047     VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges");
16048     VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges");
16049     VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory");
16050     VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory");
16051     VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements");
16052     VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements");
16053     VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer");
16054     VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer");
16055     VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage");
16056     VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage");
16057     VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer");
16058 
16059 #if VMA_DEDICATED_ALLOCATION
16060     if(m_UseKhrDedicatedAllocation)
16061     {
16062         VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR");
16063         VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR");
16064     }
16065 #endif
16066 
16067 #if VMA_BIND_MEMORY2
16068     if(m_UseKhrBindMemory2)
16069     {
16070         VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR");
16071         VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR");
16072     }
16073 #endif // #if VMA_BIND_MEMORY2
16074 
16075 #if VMA_MEMORY_BUDGET
16076     if(m_UseExtMemoryBudget && m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
16077     {
16078         VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");
16079     }
16080 #endif // #if VMA_MEMORY_BUDGET
16081 
16082 #undef VMA_FETCH_DEVICE_FUNC
16083 #undef VMA_FETCH_INSTANCE_FUNC
16084 }
16085 
16086 #endif // #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
16087 
ValidateVulkanFunctions()16088 void VmaAllocator_T::ValidateVulkanFunctions()
16089 {
16090     VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
16091     VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
16092     VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
16093     VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
16094     VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
16095     VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
16096     VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
16097     VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
16098     VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
16099     VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
16100     VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
16101     VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
16102     VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
16103     VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
16104     VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
16105     VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
16106     VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
16107 
16108 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16109     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation)
16110     {
16111         VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
16112         VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
16113     }
16114 #endif
16115 
16116 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
16117     if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2)
16118     {
16119         VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL);
16120         VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL);
16121     }
16122 #endif
16123 
16124 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
16125     if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16126     {
16127         VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);
16128     }
16129 #endif
16130 }
16131 
CalcPreferredBlockSize(uint32_t memTypeIndex)16132 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
16133 {
16134     const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16135     const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
16136     const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
16137     return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32);
16138 }
16139 
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)16140 VkResult VmaAllocator_T::AllocateMemoryOfType(
16141     VkDeviceSize size,
16142     VkDeviceSize alignment,
16143     bool dedicatedAllocation,
16144     VkBuffer dedicatedBuffer,
16145     VkBufferUsageFlags dedicatedBufferUsage,
16146     VkImage dedicatedImage,
16147     const VmaAllocationCreateInfo& createInfo,
16148     uint32_t memTypeIndex,
16149     VmaSuballocationType suballocType,
16150     size_t allocationCount,
16151     VmaAllocation* pAllocations)
16152 {
16153     VMA_ASSERT(pAllocations != VMA_NULL);
16154     VMA_DEBUG_LOG("  AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size);
16155 
16156     VmaAllocationCreateInfo finalCreateInfo = createInfo;
16157 
16158     // If memory type is not HOST_VISIBLE, disable MAPPED.
16159     if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16160         (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
16161     {
16162         finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
16163     }
16164     // If memory is lazily allocated, it should be always dedicated.
16165     if(finalCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED)
16166     {
16167         finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
16168     }
16169 
16170     VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
16171     VMA_ASSERT(blockVector);
16172 
16173     const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
16174     bool preferDedicatedMemory =
16175         VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
16176         dedicatedAllocation ||
16177         // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
16178         size > preferredBlockSize / 2;
16179 
16180     if(preferDedicatedMemory &&
16181         (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
16182         finalCreateInfo.pool == VK_NULL_HANDLE)
16183     {
16184         finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
16185     }
16186 
16187     if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
16188     {
16189         if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16190         {
16191             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16192         }
16193         else
16194         {
16195             return AllocateDedicatedMemory(
16196                 size,
16197                 suballocType,
16198                 memTypeIndex,
16199                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
16200                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
16201                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
16202                 finalCreateInfo.pUserData,
16203                 dedicatedBuffer,
16204                 dedicatedBufferUsage,
16205                 dedicatedImage,
16206                 allocationCount,
16207                 pAllocations);
16208         }
16209     }
16210     else
16211     {
16212         VkResult res = blockVector->Allocate(
16213             m_CurrentFrameIndex.load(),
16214             size,
16215             alignment,
16216             finalCreateInfo,
16217             suballocType,
16218             allocationCount,
16219             pAllocations);
16220         if(res == VK_SUCCESS)
16221         {
16222             return res;
16223         }
16224 
16225         // 5. Try dedicated memory.
16226         if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16227         {
16228             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16229         }
16230         else
16231         {
16232             res = AllocateDedicatedMemory(
16233                 size,
16234                 suballocType,
16235                 memTypeIndex,
16236                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
16237                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
16238                 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
16239                 finalCreateInfo.pUserData,
16240                 dedicatedBuffer,
16241                 dedicatedBufferUsage,
16242                 dedicatedImage,
16243                 allocationCount,
16244                 pAllocations);
16245             if(res == VK_SUCCESS)
16246             {
16247                 // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
16248                 VMA_DEBUG_LOG("    Allocated as DedicatedMemory");
16249                 return VK_SUCCESS;
16250             }
16251             else
16252             {
16253                 // Everything failed: Return error code.
16254                 VMA_DEBUG_LOG("    vkAllocateMemory FAILED");
16255                 return res;
16256             }
16257         }
16258     }
16259 }
16260 
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)16261 VkResult VmaAllocator_T::AllocateDedicatedMemory(
16262     VkDeviceSize size,
16263     VmaSuballocationType suballocType,
16264     uint32_t memTypeIndex,
16265     bool withinBudget,
16266     bool map,
16267     bool isUserDataString,
16268     void* pUserData,
16269     VkBuffer dedicatedBuffer,
16270     VkBufferUsageFlags dedicatedBufferUsage,
16271     VkImage dedicatedImage,
16272     size_t allocationCount,
16273     VmaAllocation* pAllocations)
16274 {
16275     VMA_ASSERT(allocationCount > 0 && pAllocations);
16276 
16277     if(withinBudget)
16278     {
16279         const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16280         VmaBudget heapBudget = {};
16281         GetBudget(&heapBudget, heapIndex, 1);
16282         if(heapBudget.usage + size * allocationCount > heapBudget.budget)
16283         {
16284             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16285         }
16286     }
16287 
16288     VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
16289     allocInfo.memoryTypeIndex = memTypeIndex;
16290     allocInfo.allocationSize = size;
16291 
16292 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16293     VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
16294     if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16295     {
16296         if(dedicatedBuffer != VK_NULL_HANDLE)
16297         {
16298             VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
16299             dedicatedAllocInfo.buffer = dedicatedBuffer;
16300             VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16301         }
16302         else if(dedicatedImage != VK_NULL_HANDLE)
16303         {
16304             dedicatedAllocInfo.image = dedicatedImage;
16305             VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16306         }
16307     }
16308 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16309 
16310 #if VMA_BUFFER_DEVICE_ADDRESS
16311     VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
16312     if(m_UseKhrBufferDeviceAddress)
16313     {
16314         bool canContainBufferWithDeviceAddress = true;
16315         if(dedicatedBuffer != VK_NULL_HANDLE)
16316         {
16317             canContainBufferWithDeviceAddress = dedicatedBufferUsage == UINT32_MAX || // Usage flags unknown
16318                 (dedicatedBufferUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) != 0;
16319         }
16320         else if(dedicatedImage != VK_NULL_HANDLE)
16321         {
16322             canContainBufferWithDeviceAddress = false;
16323         }
16324         if(canContainBufferWithDeviceAddress)
16325         {
16326             allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
16327             VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
16328         }
16329     }
16330 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
16331 
16332     size_t allocIndex;
16333     VkResult res = VK_SUCCESS;
16334     for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16335     {
16336         res = AllocateDedicatedMemoryPage(
16337             size,
16338             suballocType,
16339             memTypeIndex,
16340             allocInfo,
16341             map,
16342             isUserDataString,
16343             pUserData,
16344             pAllocations + allocIndex);
16345         if(res != VK_SUCCESS)
16346         {
16347             break;
16348         }
16349     }
16350 
16351     if(res == VK_SUCCESS)
16352     {
16353         // Register them in m_pDedicatedAllocations.
16354         {
16355             VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16356             AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
16357             VMA_ASSERT(pDedicatedAllocations);
16358             for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16359             {
16360                 VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, pAllocations[allocIndex]);
16361             }
16362         }
16363 
16364         VMA_DEBUG_LOG("    Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
16365     }
16366     else
16367     {
16368         // Free all already created allocations.
16369         while(allocIndex--)
16370         {
16371             VmaAllocation currAlloc = pAllocations[allocIndex];
16372             VkDeviceMemory hMemory = currAlloc->GetMemory();
16373 
16374             /*
16375             There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
16376             before vkFreeMemory.
16377 
16378             if(currAlloc->GetMappedData() != VMA_NULL)
16379             {
16380                 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
16381             }
16382             */
16383 
16384             FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
16385             m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize());
16386             currAlloc->SetUserData(this, VMA_NULL);
16387             m_AllocationObjectAllocator.Free(currAlloc);
16388         }
16389 
16390         memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16391     }
16392 
16393     return res;
16394 }
16395 
AllocateDedicatedMemoryPage(VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,const VkMemoryAllocateInfo & allocInfo,bool map,bool isUserDataString,void * pUserData,VmaAllocation * pAllocation)16396 VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
16397     VkDeviceSize size,
16398     VmaSuballocationType suballocType,
16399     uint32_t memTypeIndex,
16400     const VkMemoryAllocateInfo& allocInfo,
16401     bool map,
16402     bool isUserDataString,
16403     void* pUserData,
16404     VmaAllocation* pAllocation)
16405 {
16406     VkDeviceMemory hMemory = VK_NULL_HANDLE;
16407     VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
16408     if(res < 0)
16409     {
16410         VMA_DEBUG_LOG("    vkAllocateMemory FAILED");
16411         return res;
16412     }
16413 
16414     void* pMappedData = VMA_NULL;
16415     if(map)
16416     {
16417         res = (*m_VulkanFunctions.vkMapMemory)(
16418             m_hDevice,
16419             hMemory,
16420             0,
16421             VK_WHOLE_SIZE,
16422             0,
16423             &pMappedData);
16424         if(res < 0)
16425         {
16426             VMA_DEBUG_LOG("    vkMapMemory FAILED");
16427             FreeVulkanMemory(memTypeIndex, size, hMemory);
16428             return res;
16429         }
16430     }
16431 
16432     *pAllocation = m_AllocationObjectAllocator.Allocate(m_CurrentFrameIndex.load(), isUserDataString);
16433     (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
16434     (*pAllocation)->SetUserData(this, pUserData);
16435     m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size);
16436     if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16437     {
16438         FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
16439     }
16440 
16441     return VK_SUCCESS;
16442 }
16443 
GetBufferMemoryRequirements(VkBuffer hBuffer,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)16444 void VmaAllocator_T::GetBufferMemoryRequirements(
16445     VkBuffer hBuffer,
16446     VkMemoryRequirements& memReq,
16447     bool& requiresDedicatedAllocation,
16448     bool& prefersDedicatedAllocation) const
16449 {
16450 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16451     if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16452     {
16453         VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
16454         memReqInfo.buffer = hBuffer;
16455 
16456         VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16457 
16458         VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16459         VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16460 
16461         (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16462 
16463         memReq = memReq2.memoryRequirements;
16464         requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16465         prefersDedicatedAllocation  = (memDedicatedReq.prefersDedicatedAllocation  != VK_FALSE);
16466     }
16467     else
16468 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16469     {
16470         (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
16471         requiresDedicatedAllocation = false;
16472         prefersDedicatedAllocation  = false;
16473     }
16474 }
16475 
GetImageMemoryRequirements(VkImage hImage,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)16476 void VmaAllocator_T::GetImageMemoryRequirements(
16477     VkImage hImage,
16478     VkMemoryRequirements& memReq,
16479     bool& requiresDedicatedAllocation,
16480     bool& prefersDedicatedAllocation) const
16481 {
16482 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16483     if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16484     {
16485         VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
16486         memReqInfo.image = hImage;
16487 
16488         VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16489 
16490         VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16491         VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16492 
16493         (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16494 
16495         memReq = memReq2.memoryRequirements;
16496         requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16497         prefersDedicatedAllocation  = (memDedicatedReq.prefersDedicatedAllocation  != VK_FALSE);
16498     }
16499     else
16500 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16501     {
16502         (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
16503         requiresDedicatedAllocation = false;
16504         prefersDedicatedAllocation  = false;
16505     }
16506 }
16507 
AllocateMemory(const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,VkBuffer dedicatedBuffer,VkBufferUsageFlags dedicatedBufferUsage,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)16508 VkResult VmaAllocator_T::AllocateMemory(
16509     const VkMemoryRequirements& vkMemReq,
16510     bool requiresDedicatedAllocation,
16511     bool prefersDedicatedAllocation,
16512     VkBuffer dedicatedBuffer,
16513     VkBufferUsageFlags dedicatedBufferUsage,
16514     VkImage dedicatedImage,
16515     const VmaAllocationCreateInfo& createInfo,
16516     VmaSuballocationType suballocType,
16517     size_t allocationCount,
16518     VmaAllocation* pAllocations)
16519 {
16520     memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16521 
16522     VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
16523 
16524     if(vkMemReq.size == 0)
16525     {
16526         return VK_ERROR_VALIDATION_FAILED_EXT;
16527     }
16528     if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
16529         (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16530     {
16531         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
16532         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16533     }
16534     if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16535         (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
16536     {
16537         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
16538         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16539     }
16540     if(requiresDedicatedAllocation)
16541     {
16542         if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16543         {
16544             VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
16545             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16546         }
16547         if(createInfo.pool != VK_NULL_HANDLE)
16548         {
16549             VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
16550             return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16551         }
16552     }
16553     if((createInfo.pool != VK_NULL_HANDLE) &&
16554         ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
16555     {
16556         VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
16557         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16558     }
16559 
16560     if(createInfo.pool != VK_NULL_HANDLE)
16561     {
16562         const VkDeviceSize alignmentForPool = VMA_MAX(
16563             vkMemReq.alignment,
16564             GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex()));
16565 
16566         VmaAllocationCreateInfo createInfoForPool = createInfo;
16567         // If memory type is not HOST_VISIBLE, disable MAPPED.
16568         if((createInfoForPool.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16569             (m_MemProps.memoryTypes[createInfo.pool->m_BlockVector.GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
16570         {
16571             createInfoForPool.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
16572         }
16573 
16574         return createInfo.pool->m_BlockVector.Allocate(
16575             m_CurrentFrameIndex.load(),
16576             vkMemReq.size,
16577             alignmentForPool,
16578             createInfoForPool,
16579             suballocType,
16580             allocationCount,
16581             pAllocations);
16582     }
16583     else
16584     {
16585         // Bit mask of memory Vulkan types acceptable for this allocation.
16586         uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
16587         uint32_t memTypeIndex = UINT32_MAX;
16588         VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16589         if(res == VK_SUCCESS)
16590         {
16591             VkDeviceSize alignmentForMemType = VMA_MAX(
16592                 vkMemReq.alignment,
16593                 GetMemoryTypeMinAlignment(memTypeIndex));
16594 
16595             res = AllocateMemoryOfType(
16596                 vkMemReq.size,
16597                 alignmentForMemType,
16598                 requiresDedicatedAllocation || prefersDedicatedAllocation,
16599                 dedicatedBuffer,
16600                 dedicatedBufferUsage,
16601                 dedicatedImage,
16602                 createInfo,
16603                 memTypeIndex,
16604                 suballocType,
16605                 allocationCount,
16606                 pAllocations);
16607             // Succeeded on first try.
16608             if(res == VK_SUCCESS)
16609             {
16610                 return res;
16611             }
16612             // Allocation from this memory type failed. Try other compatible memory types.
16613             else
16614             {
16615                 for(;;)
16616                 {
16617                     // Remove old memTypeIndex from list of possibilities.
16618                     memoryTypeBits &= ~(1u << memTypeIndex);
16619                     // Find alternative memTypeIndex.
16620                     res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16621                     if(res == VK_SUCCESS)
16622                     {
16623                         alignmentForMemType = VMA_MAX(
16624                             vkMemReq.alignment,
16625                             GetMemoryTypeMinAlignment(memTypeIndex));
16626 
16627                         res = AllocateMemoryOfType(
16628                             vkMemReq.size,
16629                             alignmentForMemType,
16630                             requiresDedicatedAllocation || prefersDedicatedAllocation,
16631                             dedicatedBuffer,
16632                             dedicatedBufferUsage,
16633                             dedicatedImage,
16634                             createInfo,
16635                             memTypeIndex,
16636                             suballocType,
16637                             allocationCount,
16638                             pAllocations);
16639                         // Allocation from this alternative memory type succeeded.
16640                         if(res == VK_SUCCESS)
16641                         {
16642                             return res;
16643                         }
16644                         // else: Allocation from this memory type failed. Try next one - next loop iteration.
16645                     }
16646                     // No other matching memory type index could be found.
16647                     else
16648                     {
16649                         // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
16650                         return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16651                     }
16652                 }
16653             }
16654         }
16655         // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
16656         else
16657             return res;
16658     }
16659 }
16660 
FreeMemory(size_t allocationCount,const VmaAllocation * pAllocations)16661 void VmaAllocator_T::FreeMemory(
16662     size_t allocationCount,
16663     const VmaAllocation* pAllocations)
16664 {
16665     VMA_ASSERT(pAllocations);
16666 
16667     for(size_t allocIndex = allocationCount; allocIndex--; )
16668     {
16669         VmaAllocation allocation = pAllocations[allocIndex];
16670 
16671         if(allocation != VK_NULL_HANDLE)
16672         {
16673             if(TouchAllocation(allocation))
16674             {
16675                 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16676                 {
16677                     FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
16678                 }
16679 
16680                 switch(allocation->GetType())
16681                 {
16682                 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16683                     {
16684                         VmaBlockVector* pBlockVector = VMA_NULL;
16685                         VmaPool hPool = allocation->GetBlock()->GetParentPool();
16686                         if(hPool != VK_NULL_HANDLE)
16687                         {
16688                             pBlockVector = &hPool->m_BlockVector;
16689                         }
16690                         else
16691                         {
16692                             const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16693                             pBlockVector = m_pBlockVectors[memTypeIndex];
16694                         }
16695                         pBlockVector->Free(allocation);
16696                     }
16697                     break;
16698                 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
16699                     FreeDedicatedMemory(allocation);
16700                     break;
16701                 default:
16702                     VMA_ASSERT(0);
16703                 }
16704             }
16705 
16706             // Do this regardless of whether the allocation is lost. Lost allocations still account to Budget.AllocationBytes.
16707             m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
16708             allocation->SetUserData(this, VMA_NULL);
16709             m_AllocationObjectAllocator.Free(allocation);
16710         }
16711     }
16712 }
16713 
16714 // 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)16715 VkResult VmaAllocator_T::AllocateReservedMemory(
16716     const VkMemoryRequirements& vkMemReq,
16717     bool requiresDedicatedAllocation,
16718     bool prefersDedicatedAllocation,
16719     VkBuffer dedicatedBuffer,
16720     VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown.
16721     VkImage dedicatedImage,
16722     const VmaAllocationCreateInfo& createInfo,
16723     VmaSuballocationType suballocType,
16724     size_t allocationCount,
16725     VmaAllocation* pAllocations)
16726 {
16727     memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16728     if (allocationCount != 1) {
16729         return VK_NOT_READY;
16730     }
16731     uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
16732     uint32_t memTypeIndex = UINT32_MAX;
16733     VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16734     if(res != VK_SUCCESS) {
16735         return res;
16736     }
16737     VkDeviceSize alignmentForMemType = VMA_MAX(
16738         vkMemReq.alignment,
16739         GetMemoryTypeMinAlignment(memTypeIndex));
16740     VmaBlockVector* reservedBlockVector = m_pReservedBlockVectors[memTypeIndex];
16741     VMA_ASSERT(reservedBlockVector);
16742 
16743     res = reservedBlockVector->AllocateReserved(
16744         m_CurrentFrameIndex.load(),
16745         vkMemReq.size,
16746         alignmentForMemType,
16747         createInfo,
16748         suballocType,
16749         pAllocations);
16750     return res;
16751 }
16752 
FreeReservedMemory(size_t allocationCount,const VmaAllocation * pAllocations)16753 void VmaAllocator_T::FreeReservedMemory(
16754     size_t allocationCount,
16755     const VmaAllocation* pAllocations)
16756 {
16757     VMA_ASSERT(pAllocations);
16758 
16759     for(size_t allocIndex = allocationCount; allocIndex--; )
16760     {
16761         VmaAllocation allocation = pAllocations[allocIndex];
16762 
16763         if(allocation != VK_NULL_HANDLE)
16764         {
16765             if(TouchAllocation(allocation))
16766             {
16767                 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16768                 {
16769                     FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
16770                 }
16771 
16772                 switch(allocation->GetType())
16773                 {
16774                 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16775                     {
16776                         VmaBlockVector* pBlockVector = VMA_NULL;
16777                         VmaPool hPool = allocation->GetBlock()->GetParentPool();
16778                         if(hPool != VK_NULL_HANDLE)
16779                         {
16780                             pBlockVector = &hPool->m_BlockVector;
16781                         }
16782                         else
16783                         {
16784                             const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16785                             pBlockVector = m_pReservedBlockVectors[memTypeIndex];
16786                         }
16787                         VMA_ASSERT(pBlockVector);
16788                         pBlockVector->FreeReserved(allocation);
16789                     }
16790                     break;
16791                 default:
16792                     VMA_ASSERT(0);
16793                 }
16794             }
16795             m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
16796         }
16797     }
16798 }
16799 
SwapReservedBlock(VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)16800 VkResult VmaAllocator_T::SwapReservedBlock(
16801     VkImage image,
16802     const VmaAllocationCreateInfo* pCreateInfo,
16803     VmaAllocation* pAllocation,
16804     VmaAllocationInfo* pAllocationInfo)
16805 {
16806     VmaAllocation oldAllocation = *pAllocation;
16807     if (oldAllocation->GetType() != VmaAllocation_T::ALLOCATION_TYPE_BLOCK) {
16808         return VK_ERROR_UNKNOWN;
16809     }
16810     VmaPool hPool = oldAllocation->GetBlock()->GetParentPool();
16811     if(hPool != VK_NULL_HANDLE) {
16812         return VK_ERROR_UNKNOWN;
16813     }
16814     const uint32_t memTypeIndex = oldAllocation->GetMemoryTypeIndex();
16815     VmaBlockVector* reservedBlockVector = m_pReservedBlockVectors[memTypeIndex];
16816     VMA_ASSERT(reservedBlockVector);
16817     if (reservedBlockVector->IsEmpty()) {
16818         return VK_NOT_READY;
16819     }
16820     if (!reservedBlockVector->IsLastBlockBindComplete()) {
16821         return VK_INCOMPLETE;
16822     }
16823 
16824     VmaBlockVector* blockVector = m_pBlockVectors[memTypeIndex];
16825     VMA_ASSERT(blockVector);
16826     if (!blockVector->SwapLastBlock(reservedBlockVector)) {
16827         return VK_ERROR_UNKNOWN;
16828     }
16829     return VK_SUCCESS;
16830 }
16831 
GetPreAllocBlockSize()16832 uint32_t VmaAllocator_T::GetPreAllocBlockSize()
16833 {
16834     VmaBlockVector* reservedBlockVector = m_pReservedBlockVectors[0];
16835     VMA_ASSERT(reservedBlockVector);
16836     return reservedBlockVector->GetBlockCount();
16837 }
16838 
ResizeAllocation(const VmaAllocation alloc,VkDeviceSize newSize)16839 VkResult VmaAllocator_T::ResizeAllocation(
16840     const VmaAllocation alloc,
16841     VkDeviceSize newSize)
16842 {
16843     // This function is deprecated and so it does nothing. It's left for backward compatibility.
16844     if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST)
16845     {
16846         return VK_ERROR_VALIDATION_FAILED_EXT;
16847     }
16848     if(newSize == alloc->GetSize())
16849     {
16850         return VK_SUCCESS;
16851     }
16852     return VK_ERROR_OUT_OF_POOL_MEMORY;
16853 }
16854 
CalculateStats(VmaStats * pStats)16855 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
16856 {
16857     // Initialize.
16858     InitStatInfo(pStats->total);
16859     for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
16860         InitStatInfo(pStats->memoryType[i]);
16861     for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
16862         InitStatInfo(pStats->memoryHeap[i]);
16863 
16864     // Process default pools.
16865     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16866     {
16867         VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
16868         VMA_ASSERT(pBlockVector);
16869         pBlockVector->AddStats(pStats);
16870     }
16871 
16872     // Process custom pools.
16873     {
16874         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
16875         for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
16876         {
16877             m_Pools[poolIndex]->m_BlockVector.AddStats(pStats);
16878         }
16879     }
16880 
16881     // Process dedicated allocations.
16882     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16883     {
16884         const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16885         VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16886         AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
16887         VMA_ASSERT(pDedicatedAllocVector);
16888         for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
16889         {
16890             VmaStatInfo allocationStatInfo;
16891             (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo);
16892             VmaAddStatInfo(pStats->total, allocationStatInfo);
16893             VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
16894             VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
16895         }
16896     }
16897 
16898     // Postprocess.
16899     VmaPostprocessCalcStatInfo(pStats->total);
16900     for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
16901         VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
16902     for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
16903         VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
16904 }
16905 
FreeEmptyBlock()16906 void VmaAllocator_T::FreeEmptyBlock()
16907 {
16908     for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) {
16909         VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
16910         VMA_ASSERT(pBlockVector);
16911         pBlockVector->FreeEmptyBlock();
16912 
16913         VmaBlockVector* const pReservedBlockVector = m_pReservedBlockVectors[memTypeIndex];
16914         VMA_ASSERT(pReservedBlockVector);
16915         pReservedBlockVector->FreeEmptyBlock();
16916     }
16917 }
16918 
GetBudget(VmaBudget * outBudget,uint32_t firstHeap,uint32_t heapCount)16919 void VmaAllocator_T::GetBudget(VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount)
16920 {
16921 #if VMA_MEMORY_BUDGET
16922     if(m_UseExtMemoryBudget)
16923     {
16924         if(m_Budget.m_OperationsSinceBudgetFetch < 30)
16925         {
16926             VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex);
16927             for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
16928             {
16929                 const uint32_t heapIndex = firstHeap + i;
16930 
16931                 outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
16932                 outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
16933 
16934                 if(m_Budget.m_VulkanUsage[heapIndex] + outBudget->blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex])
16935                 {
16936                     outBudget->usage = m_Budget.m_VulkanUsage[heapIndex] +
16937                         outBudget->blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
16938                 }
16939                 else
16940                 {
16941                     outBudget->usage = 0;
16942                 }
16943 
16944                 // Have to take MIN with heap size because explicit HeapSizeLimit is included in it.
16945                 outBudget->budget = VMA_MIN(
16946                     m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size);
16947             }
16948         }
16949         else
16950         {
16951             UpdateVulkanBudget(); // Outside of mutex lock
16952             GetBudget(outBudget, firstHeap, heapCount); // Recursion
16953         }
16954     }
16955     else
16956 #endif
16957     {
16958         for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
16959         {
16960             const uint32_t heapIndex = firstHeap + i;
16961 
16962             outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
16963             outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
16964 
16965             outBudget->usage = outBudget->blockBytes;
16966             outBudget->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
16967         }
16968     }
16969 }
16970 
16971 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
16972 
DefragmentationBegin(const VmaDefragmentationInfo2 & info,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)16973 VkResult VmaAllocator_T::DefragmentationBegin(
16974     const VmaDefragmentationInfo2& info,
16975     VmaDefragmentationStats* pStats,
16976     VmaDefragmentationContext* pContext)
16977 {
16978     if(info.pAllocationsChanged != VMA_NULL)
16979     {
16980         memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
16981     }
16982 
16983     *pContext = vma_new(this, VmaDefragmentationContext_T)(
16984         this, m_CurrentFrameIndex.load(), info.flags, pStats);
16985 
16986     (*pContext)->AddPools(info.poolCount, info.pPools);
16987     (*pContext)->AddAllocations(
16988         info.allocationCount, info.pAllocations, info.pAllocationsChanged);
16989 
16990     VkResult res = (*pContext)->Defragment(
16991         info.maxCpuBytesToMove, info.maxCpuAllocationsToMove,
16992         info.maxGpuBytesToMove, info.maxGpuAllocationsToMove,
16993         info.commandBuffer, pStats, info.flags);
16994 
16995     if(res != VK_NOT_READY)
16996     {
16997         vma_delete(this, *pContext);
16998         *pContext = VMA_NULL;
16999     }
17000 
17001     return res;
17002 }
17003 
DefragmentationEnd(VmaDefragmentationContext context)17004 VkResult VmaAllocator_T::DefragmentationEnd(
17005     VmaDefragmentationContext context)
17006 {
17007     vma_delete(this, context);
17008     return VK_SUCCESS;
17009 }
17010 
DefragmentationPassBegin(VmaDefragmentationPassInfo * pInfo,VmaDefragmentationContext context)17011 VkResult VmaAllocator_T::DefragmentationPassBegin(
17012     VmaDefragmentationPassInfo* pInfo,
17013     VmaDefragmentationContext context)
17014 {
17015     return context->DefragmentPassBegin(pInfo);
17016 }
DefragmentationPassEnd(VmaDefragmentationContext context)17017 VkResult VmaAllocator_T::DefragmentationPassEnd(
17018     VmaDefragmentationContext context)
17019 {
17020     return context->DefragmentPassEnd();
17021 
17022 }
17023 
GetAllocationInfo(VmaAllocation hAllocation,VmaAllocationInfo * pAllocationInfo)17024 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
17025 {
17026     if(hAllocation->CanBecomeLost())
17027     {
17028         /*
17029         Warning: This is a carefully designed algorithm.
17030         Do not modify unless you really know what you're doing :)
17031         */
17032         const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17033         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17034         for(;;)
17035         {
17036             if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
17037             {
17038                 pAllocationInfo->memoryType = UINT32_MAX;
17039                 pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
17040                 pAllocationInfo->offset = 0;
17041                 pAllocationInfo->size = hAllocation->GetSize();
17042                 pAllocationInfo->pMappedData = VMA_NULL;
17043                 pAllocationInfo->pUserData = hAllocation->GetUserData();
17044                 return;
17045             }
17046             else if(localLastUseFrameIndex == localCurrFrameIndex)
17047             {
17048                 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
17049                 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
17050                 pAllocationInfo->offset = hAllocation->GetOffset();
17051                 pAllocationInfo->size = hAllocation->GetSize();
17052                 pAllocationInfo->pMappedData = VMA_NULL;
17053                 pAllocationInfo->pUserData = hAllocation->GetUserData();
17054                 return;
17055             }
17056             else // Last use time earlier than current time.
17057             {
17058                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17059                 {
17060                     localLastUseFrameIndex = localCurrFrameIndex;
17061                 }
17062             }
17063         }
17064     }
17065     else
17066     {
17067 #if VMA_STATS_STRING_ENABLED
17068         uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17069         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17070         for(;;)
17071         {
17072             VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
17073             if(localLastUseFrameIndex == localCurrFrameIndex)
17074             {
17075                 break;
17076             }
17077             else // Last use time earlier than current time.
17078             {
17079                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17080                 {
17081                     localLastUseFrameIndex = localCurrFrameIndex;
17082                 }
17083             }
17084         }
17085 #endif
17086 
17087         pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
17088         pAllocationInfo->deviceMemory = hAllocation->GetMemory();
17089         pAllocationInfo->offset = hAllocation->GetOffset();
17090         pAllocationInfo->size = hAllocation->GetSize();
17091         pAllocationInfo->pMappedData = hAllocation->GetMappedData();
17092         pAllocationInfo->pUserData = hAllocation->GetUserData();
17093     }
17094 }
17095 
TouchAllocation(VmaAllocation hAllocation)17096 bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
17097 {
17098     // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
17099     if(hAllocation->CanBecomeLost())
17100     {
17101         uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17102         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17103         for(;;)
17104         {
17105             if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
17106             {
17107                 return false;
17108             }
17109             else if(localLastUseFrameIndex == localCurrFrameIndex)
17110             {
17111                 return true;
17112             }
17113             else // Last use time earlier than current time.
17114             {
17115                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17116                 {
17117                     localLastUseFrameIndex = localCurrFrameIndex;
17118                 }
17119             }
17120         }
17121     }
17122     else
17123     {
17124 #if VMA_STATS_STRING_ENABLED
17125         uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17126         uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17127         for(;;)
17128         {
17129             VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
17130             if(localLastUseFrameIndex == localCurrFrameIndex)
17131             {
17132                 break;
17133             }
17134             else // Last use time earlier than current time.
17135             {
17136                 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17137                 {
17138                     localLastUseFrameIndex = localCurrFrameIndex;
17139                 }
17140             }
17141         }
17142 #endif
17143 
17144         return true;
17145     }
17146 }
17147 
CreatePool(const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)17148 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
17149 {
17150     VMA_DEBUG_LOG("  CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
17151 
17152     VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
17153 
17154     if(newCreateInfo.maxBlockCount == 0)
17155     {
17156         newCreateInfo.maxBlockCount = SIZE_MAX;
17157     }
17158     if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
17159     {
17160         return VK_ERROR_INITIALIZATION_FAILED;
17161     }
17162     // Memory type index out of range or forbidden.
17163     if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() ||
17164         ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0)
17165     {
17166         return VK_ERROR_FEATURE_NOT_PRESENT;
17167     }
17168 
17169     const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
17170 
17171     *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
17172 
17173     VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
17174     if(res != VK_SUCCESS)
17175     {
17176         vma_delete(this, *pPool);
17177         *pPool = VMA_NULL;
17178         return res;
17179     }
17180 
17181     // Add to m_Pools.
17182     {
17183         VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
17184         (*pPool)->SetId(m_NextPoolId++);
17185         VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool);
17186     }
17187 
17188     return VK_SUCCESS;
17189 }
17190 
DestroyPool(VmaPool pool)17191 void VmaAllocator_T::DestroyPool(VmaPool pool)
17192 {
17193     // Remove from m_Pools.
17194     {
17195         VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
17196         bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
17197         VMA_ASSERT(success && "Pool not found in Allocator.");
17198     }
17199 
17200     vma_delete(this, pool);
17201 }
17202 
GetPoolStats(VmaPool pool,VmaPoolStats * pPoolStats)17203 void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
17204 {
17205     pool->m_BlockVector.GetPoolStats(pPoolStats);
17206 }
17207 
SetCurrentFrameIndex(uint32_t frameIndex)17208 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
17209 {
17210     m_CurrentFrameIndex.store(frameIndex);
17211 
17212 #if VMA_MEMORY_BUDGET
17213     if(m_UseExtMemoryBudget)
17214     {
17215         UpdateVulkanBudget();
17216     }
17217 #endif // #if VMA_MEMORY_BUDGET
17218 }
17219 
MakePoolAllocationsLost(VmaPool hPool,size_t * pLostAllocationCount)17220 void VmaAllocator_T::MakePoolAllocationsLost(
17221     VmaPool hPool,
17222     size_t* pLostAllocationCount)
17223 {
17224     hPool->m_BlockVector.MakePoolAllocationsLost(
17225         m_CurrentFrameIndex.load(),
17226         pLostAllocationCount);
17227 }
17228 
CheckPoolCorruption(VmaPool hPool)17229 VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
17230 {
17231     return hPool->m_BlockVector.CheckCorruption();
17232 }
17233 
CheckCorruption(uint32_t memoryTypeBits)17234 VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
17235 {
17236     VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
17237 
17238     // Process default pools.
17239     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17240     {
17241         if(((1u << memTypeIndex) & memoryTypeBits) != 0)
17242         {
17243             VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
17244             VMA_ASSERT(pBlockVector);
17245             VkResult localRes = pBlockVector->CheckCorruption();
17246             switch(localRes)
17247             {
17248             case VK_ERROR_FEATURE_NOT_PRESENT:
17249                 break;
17250             case VK_SUCCESS:
17251                 finalRes = VK_SUCCESS;
17252                 break;
17253             default:
17254                 return localRes;
17255             }
17256         }
17257     }
17258 
17259     // Process custom pools.
17260     {
17261         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
17262         for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
17263         {
17264             if(((1u << m_Pools[poolIndex]->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
17265             {
17266                 VkResult localRes = m_Pools[poolIndex]->m_BlockVector.CheckCorruption();
17267                 switch(localRes)
17268                 {
17269                 case VK_ERROR_FEATURE_NOT_PRESENT:
17270                     break;
17271                 case VK_SUCCESS:
17272                     finalRes = VK_SUCCESS;
17273                     break;
17274                 default:
17275                     return localRes;
17276                 }
17277             }
17278         }
17279     }
17280 
17281     return finalRes;
17282 }
17283 
CreateLostAllocation(VmaAllocation * pAllocation)17284 void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
17285 {
17286     *pAllocation = m_AllocationObjectAllocator.Allocate(VMA_FRAME_INDEX_LOST, false);
17287     (*pAllocation)->InitLost();
17288 }
17289 
AllocateVulkanMemory(const VkMemoryAllocateInfo * pAllocateInfo,VkDeviceMemory * pMemory)17290 VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
17291 {
17292     const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
17293 
17294     // HeapSizeLimit is in effect for this heap.
17295     if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0)
17296     {
17297         const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
17298         VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex];
17299         for(;;)
17300         {
17301             const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize;
17302             if(blockBytesAfterAllocation > heapSize)
17303             {
17304                 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
17305             }
17306             if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation))
17307             {
17308                 break;
17309             }
17310         }
17311     }
17312     else
17313     {
17314         m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize;
17315     }
17316 
17317     // VULKAN CALL vkAllocateMemory.
17318     VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
17319 
17320     if(res == VK_SUCCESS)
17321     {
17322 #if VMA_MEMORY_BUDGET
17323         ++m_Budget.m_OperationsSinceBudgetFetch;
17324 #endif
17325 
17326         // Informative callback.
17327         if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
17328         {
17329             (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData);
17330         }
17331     }
17332     else
17333     {
17334         m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize;
17335     }
17336 
17337     return res;
17338 }
17339 
FreeVulkanMemory(uint32_t memoryType,VkDeviceSize size,VkDeviceMemory hMemory)17340 void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
17341 {
17342     // Informative callback.
17343     if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
17344     {
17345         (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData);
17346     }
17347 
17348     // VULKAN CALL vkFreeMemory.
17349     (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
17350 
17351     m_Budget.m_BlockBytes[MemoryTypeIndexToHeapIndex(memoryType)] -= size;
17352 }
17353 
BindVulkanBuffer(VkDeviceMemory memory,VkDeviceSize memoryOffset,VkBuffer buffer,const void * pNext)17354 VkResult VmaAllocator_T::BindVulkanBuffer(
17355     VkDeviceMemory memory,
17356     VkDeviceSize memoryOffset,
17357     VkBuffer buffer,
17358     const void* pNext)
17359 {
17360     if(pNext != VMA_NULL)
17361     {
17362 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17363         if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
17364             m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL)
17365         {
17366             VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR };
17367             bindBufferMemoryInfo.pNext = pNext;
17368             bindBufferMemoryInfo.buffer = buffer;
17369             bindBufferMemoryInfo.memory = memory;
17370             bindBufferMemoryInfo.memoryOffset = memoryOffset;
17371             return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17372         }
17373         else
17374 #endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17375         {
17376             return VK_ERROR_EXTENSION_NOT_PRESENT;
17377         }
17378     }
17379     else
17380     {
17381         return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset);
17382     }
17383 }
17384 
BindVulkanImage(VkDeviceMemory memory,VkDeviceSize memoryOffset,VkImage image,const void * pNext)17385 VkResult VmaAllocator_T::BindVulkanImage(
17386     VkDeviceMemory memory,
17387     VkDeviceSize memoryOffset,
17388     VkImage image,
17389     const void* pNext)
17390 {
17391     if(pNext != VMA_NULL)
17392     {
17393 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17394         if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
17395             m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL)
17396         {
17397             VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR };
17398             bindBufferMemoryInfo.pNext = pNext;
17399             bindBufferMemoryInfo.image = image;
17400             bindBufferMemoryInfo.memory = memory;
17401             bindBufferMemoryInfo.memoryOffset = memoryOffset;
17402             return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17403         }
17404         else
17405 #endif // #if VMA_BIND_MEMORY2
17406         {
17407             return VK_ERROR_EXTENSION_NOT_PRESENT;
17408         }
17409     }
17410     else
17411     {
17412         return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset);
17413     }
17414 }
17415 
Map(VmaAllocation hAllocation,void ** ppData)17416 VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
17417 {
17418     if(hAllocation->CanBecomeLost())
17419     {
17420         return VK_ERROR_MEMORY_MAP_FAILED;
17421     }
17422 
17423     switch(hAllocation->GetType())
17424     {
17425     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17426         {
17427             VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17428             char *pBytes = VMA_NULL;
17429             VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
17430             if(res == VK_SUCCESS)
17431             {
17432                 *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
17433                 hAllocation->BlockAllocMap();
17434             }
17435             return res;
17436         }
17437     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17438         return hAllocation->DedicatedAllocMap(this, ppData);
17439     default:
17440         VMA_ASSERT(0);
17441         return VK_ERROR_MEMORY_MAP_FAILED;
17442     }
17443 }
17444 
Unmap(VmaAllocation hAllocation)17445 void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
17446 {
17447     switch(hAllocation->GetType())
17448     {
17449     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17450         {
17451             VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17452             hAllocation->BlockAllocUnmap();
17453             pBlock->Unmap(this, 1);
17454         }
17455         break;
17456     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17457         hAllocation->DedicatedAllocUnmap(this);
17458         break;
17459     default:
17460         VMA_ASSERT(0);
17461     }
17462 }
17463 
BindBufferMemory(VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkBuffer hBuffer,const void * pNext)17464 VkResult VmaAllocator_T::BindBufferMemory(
17465     VmaAllocation hAllocation,
17466     VkDeviceSize allocationLocalOffset,
17467     VkBuffer hBuffer,
17468     const void* pNext)
17469 {
17470     VkResult res = VK_SUCCESS;
17471     switch(hAllocation->GetType())
17472     {
17473     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17474         res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext);
17475         break;
17476     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17477     {
17478         VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17479         VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
17480         res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext);
17481         break;
17482     }
17483     default:
17484         VMA_ASSERT(0);
17485     }
17486     return res;
17487 }
17488 
BindImageMemory(VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkImage hImage,const void * pNext)17489 VkResult VmaAllocator_T::BindImageMemory(
17490     VmaAllocation hAllocation,
17491     VkDeviceSize allocationLocalOffset,
17492     VkImage hImage,
17493     const void* pNext)
17494 {
17495     VkResult res = VK_SUCCESS;
17496     switch(hAllocation->GetType())
17497     {
17498     case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17499         res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext);
17500         break;
17501     case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17502     {
17503         VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
17504         VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
17505         res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext);
17506         if (res == VK_SUCCESS) {
17507             pBlock->SetBindCompleteFlag(true);
17508         }
17509         break;
17510     }
17511     default:
17512         VMA_ASSERT(0);
17513     }
17514     return res;
17515 }
17516 
FlushOrInvalidateAllocation(VmaAllocation hAllocation,VkDeviceSize offset,VkDeviceSize size,VMA_CACHE_OPERATION op)17517 VkResult VmaAllocator_T::FlushOrInvalidateAllocation(
17518     VmaAllocation hAllocation,
17519     VkDeviceSize offset, VkDeviceSize size,
17520     VMA_CACHE_OPERATION op)
17521 {
17522     VkResult res = VK_SUCCESS;
17523 
17524     VkMappedMemoryRange memRange = {};
17525     if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange))
17526     {
17527         switch(op)
17528         {
17529         case VMA_CACHE_FLUSH:
17530             res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
17531             break;
17532         case VMA_CACHE_INVALIDATE:
17533             res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
17534             break;
17535         default:
17536             VMA_ASSERT(0);
17537         }
17538     }
17539     // else: Just ignore this call.
17540     return res;
17541 }
17542 
FlushOrInvalidateAllocations(uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes,VMA_CACHE_OPERATION op)17543 VkResult VmaAllocator_T::FlushOrInvalidateAllocations(
17544     uint32_t allocationCount,
17545     const VmaAllocation* allocations,
17546     const VkDeviceSize* offsets, const VkDeviceSize* sizes,
17547     VMA_CACHE_OPERATION op)
17548 {
17549     typedef VmaStlAllocator<VkMappedMemoryRange> RangeAllocator;
17550     typedef VmaSmallVector<VkMappedMemoryRange, RangeAllocator, 16> RangeVector;
17551     RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks()));
17552 
17553     for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
17554     {
17555         const VmaAllocation alloc = allocations[allocIndex];
17556         const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0;
17557         const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE;
17558         VkMappedMemoryRange newRange;
17559         if(GetFlushOrInvalidateRange(alloc, offset, size, newRange))
17560         {
17561             ranges.push_back(newRange);
17562         }
17563     }
17564 
17565     VkResult res = VK_SUCCESS;
17566     if(!ranges.empty())
17567     {
17568         switch(op)
17569         {
17570         case VMA_CACHE_FLUSH:
17571             res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17572             break;
17573         case VMA_CACHE_INVALIDATE:
17574             res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17575             break;
17576         default:
17577             VMA_ASSERT(0);
17578         }
17579     }
17580     // else: Just ignore this call.
17581     return res;
17582 }
17583 
FreeDedicatedMemory(const VmaAllocation allocation)17584 void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
17585 {
17586     VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
17587 
17588     const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17589     {
17590         VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17591         AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
17592         VMA_ASSERT(pDedicatedAllocations);
17593         bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
17594         VMA_ASSERT(success);
17595     }
17596 
17597     VkDeviceMemory hMemory = allocation->GetMemory();
17598 
17599     /*
17600     There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
17601     before vkFreeMemory.
17602 
17603     if(allocation->GetMappedData() != VMA_NULL)
17604     {
17605         (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
17606     }
17607     */
17608 
17609     FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
17610 
17611     VMA_DEBUG_LOG("    Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
17612 }
17613 
CalculateGpuDefragmentationMemoryTypeBits()17614 uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const
17615 {
17616     VkBufferCreateInfo dummyBufCreateInfo;
17617     VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo);
17618 
17619     uint32_t memoryTypeBits = 0;
17620 
17621     // Create buffer.
17622     VkBuffer buf = VK_NULL_HANDLE;
17623     VkResult res = (*GetVulkanFunctions().vkCreateBuffer)(
17624         m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf);
17625     if(res == VK_SUCCESS)
17626     {
17627         // Query for supported memory types.
17628         VkMemoryRequirements memReq;
17629         (*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq);
17630         memoryTypeBits = memReq.memoryTypeBits;
17631 
17632         // Destroy buffer.
17633         (*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks());
17634     }
17635 
17636     return memoryTypeBits;
17637 }
17638 
CalculateGlobalMemoryTypeBits()17639 uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const
17640 {
17641     // Make sure memory information is already fetched.
17642     VMA_ASSERT(GetMemoryTypeCount() > 0);
17643 
17644     uint32_t memoryTypeBits = UINT32_MAX;
17645 
17646     if(!m_UseAmdDeviceCoherentMemory)
17647     {
17648         // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD.
17649         for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17650         {
17651             if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
17652             {
17653                 memoryTypeBits &= ~(1u << memTypeIndex);
17654             }
17655         }
17656     }
17657 
17658     return memoryTypeBits;
17659 }
17660 
GetFlushOrInvalidateRange(VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size,VkMappedMemoryRange & outRange)17661 bool VmaAllocator_T::GetFlushOrInvalidateRange(
17662     VmaAllocation allocation,
17663     VkDeviceSize offset, VkDeviceSize size,
17664     VkMappedMemoryRange& outRange) const
17665 {
17666     const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17667     if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
17668     {
17669         const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
17670         const VkDeviceSize allocationSize = allocation->GetSize();
17671         VMA_ASSERT(offset <= allocationSize);
17672 
17673         outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
17674         outRange.pNext = VMA_NULL;
17675         outRange.memory = allocation->GetMemory();
17676 
17677         switch(allocation->GetType())
17678         {
17679         case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17680             outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17681             if(size == VK_WHOLE_SIZE)
17682             {
17683                 outRange.size = allocationSize - outRange.offset;
17684             }
17685             else
17686             {
17687                 VMA_ASSERT(offset + size <= allocationSize);
17688                 outRange.size = VMA_MIN(
17689                     VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize),
17690                     allocationSize - outRange.offset);
17691             }
17692             break;
17693         case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17694         {
17695             // 1. Still within this allocation.
17696             outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17697             if(size == VK_WHOLE_SIZE)
17698             {
17699                 size = allocationSize - offset;
17700             }
17701             else
17702             {
17703                 VMA_ASSERT(offset + size <= allocationSize);
17704             }
17705             outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize);
17706 
17707             // 2. Adjust to whole block.
17708             const VkDeviceSize allocationOffset = allocation->GetOffset();
17709             VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
17710             const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize();
17711             outRange.offset += allocationOffset;
17712             outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset);
17713 
17714             break;
17715         }
17716         default:
17717             VMA_ASSERT(0);
17718         }
17719         return true;
17720     }
17721     return false;
17722 }
17723 
17724 #if VMA_MEMORY_BUDGET
17725 
UpdateVulkanBudget()17726 void VmaAllocator_T::UpdateVulkanBudget()
17727 {
17728     VMA_ASSERT(m_UseExtMemoryBudget);
17729 
17730     VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR };
17731 
17732     VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };
17733     VmaPnextChainPushFront(&memProps, &budgetProps);
17734 
17735     GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps);
17736 
17737     {
17738         VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex);
17739 
17740         for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
17741         {
17742             m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex];
17743             m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex];
17744             m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load();
17745 
17746             // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size.
17747             if(m_Budget.m_VulkanBudget[heapIndex] == 0)
17748             {
17749                 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
17750             }
17751             else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size)
17752             {
17753                 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size;
17754             }
17755             if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0)
17756             {
17757                 m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
17758             }
17759         }
17760         m_Budget.m_OperationsSinceBudgetFetch = 0;
17761     }
17762 }
17763 
17764 #endif // #if VMA_MEMORY_BUDGET
17765 
FillAllocation(const VmaAllocation hAllocation,uint8_t pattern)17766 void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
17767 {
17768     if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
17769         !hAllocation->CanBecomeLost() &&
17770         (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
17771     {
17772         void* pData = VMA_NULL;
17773         VkResult res = Map(hAllocation, &pData);
17774         if(res == VK_SUCCESS)
17775         {
17776             memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
17777             FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
17778             Unmap(hAllocation);
17779         }
17780         else
17781         {
17782             VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
17783         }
17784     }
17785 }
17786 
GetGpuDefragmentationMemoryTypeBits()17787 uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits()
17788 {
17789     uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load();
17790     if(memoryTypeBits == UINT32_MAX)
17791     {
17792         memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits();
17793         m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits);
17794     }
17795     return memoryTypeBits;
17796 }
17797 
17798 #if VMA_STATS_STRING_ENABLED
17799 
PrintDetailedMap(VmaJsonWriter & json)17800 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
17801 {
17802     bool dedicatedAllocationsStarted = false;
17803     for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17804     {
17805         VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17806         AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
17807         VMA_ASSERT(pDedicatedAllocVector);
17808         if(pDedicatedAllocVector->empty() == false)
17809         {
17810             if(dedicatedAllocationsStarted == false)
17811             {
17812                 dedicatedAllocationsStarted = true;
17813                 json.WriteString("DedicatedAllocations");
17814                 json.BeginObject();
17815             }
17816 
17817             json.BeginString("Type ");
17818             json.ContinueString(memTypeIndex);
17819             json.EndString();
17820 
17821             json.BeginArray();
17822 
17823             for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
17824             {
17825                 json.BeginObject(true);
17826                 const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
17827                 hAlloc->PrintParameters(json);
17828                 json.EndObject();
17829             }
17830 
17831             json.EndArray();
17832         }
17833     }
17834     if(dedicatedAllocationsStarted)
17835     {
17836         json.EndObject();
17837     }
17838 
17839     {
17840         bool allocationsStarted = false;
17841         for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17842         {
17843             if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
17844             {
17845                 if(allocationsStarted == false)
17846                 {
17847                     allocationsStarted = true;
17848                     json.WriteString("DefaultPools");
17849                     json.BeginObject();
17850                 }
17851 
17852                 json.BeginString("Type ");
17853                 json.ContinueString(memTypeIndex);
17854                 json.EndString();
17855 
17856                 m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
17857             }
17858         }
17859         if(allocationsStarted)
17860         {
17861             json.EndObject();
17862         }
17863     }
17864 
17865     // Custom pools
17866     {
17867         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
17868         const size_t poolCount = m_Pools.size();
17869         if(poolCount > 0)
17870         {
17871             json.WriteString("Pools");
17872             json.BeginObject();
17873             for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
17874             {
17875                 json.BeginString();
17876                 json.ContinueString(m_Pools[poolIndex]->GetId());
17877                 json.EndString();
17878 
17879                 m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
17880             }
17881             json.EndObject();
17882         }
17883     }
17884 }
17885 
17886 #endif // #if VMA_STATS_STRING_ENABLED
17887 
17888 ////////////////////////////////////////////////////////////////////////////////
17889 // Public interface
17890 
vmaCreateAllocator(const VmaAllocatorCreateInfo * pCreateInfo,VmaAllocator * pAllocator)17891 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
17892     const VmaAllocatorCreateInfo* pCreateInfo,
17893     VmaAllocator* pAllocator)
17894 {
17895     VMA_ASSERT(pCreateInfo && pAllocator);
17896     VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 ||
17897         (VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 2));
17898     VMA_DEBUG_LOG("vmaCreateAllocator");
17899     *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
17900     return (*pAllocator)->Init(pCreateInfo);
17901 }
17902 
vmaDestroyAllocator(VmaAllocator allocator)17903 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
17904     VmaAllocator allocator)
17905 {
17906     if(allocator != VK_NULL_HANDLE)
17907     {
17908         VMA_DEBUG_LOG("vmaDestroyAllocator");
17909         VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
17910         vma_delete(&allocationCallbacks, allocator);
17911     }
17912 }
17913 
vmaGetAllocatorInfo(VmaAllocator allocator,VmaAllocatorInfo * pAllocatorInfo)17914 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo)
17915 {
17916     VMA_ASSERT(allocator && pAllocatorInfo);
17917     pAllocatorInfo->instance = allocator->m_hInstance;
17918     pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice();
17919     pAllocatorInfo->device = allocator->m_hDevice;
17920 }
17921 
vmaGetPhysicalDeviceProperties(VmaAllocator allocator,const VkPhysicalDeviceProperties ** ppPhysicalDeviceProperties)17922 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
17923     VmaAllocator allocator,
17924     const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
17925 {
17926     VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
17927     *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
17928 }
17929 
vmaGetMemoryProperties(VmaAllocator allocator,const VkPhysicalDeviceMemoryProperties ** ppPhysicalDeviceMemoryProperties)17930 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
17931     VmaAllocator allocator,
17932     const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
17933 {
17934     VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
17935     *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
17936 }
17937 
vmaGetMemoryTypeProperties(VmaAllocator allocator,uint32_t memoryTypeIndex,VkMemoryPropertyFlags * pFlags)17938 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
17939     VmaAllocator allocator,
17940     uint32_t memoryTypeIndex,
17941     VkMemoryPropertyFlags* pFlags)
17942 {
17943     VMA_ASSERT(allocator && pFlags);
17944     VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
17945     *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
17946 }
17947 
vmaSetCurrentFrameIndex(VmaAllocator allocator,uint32_t frameIndex)17948 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
17949     VmaAllocator allocator,
17950     uint32_t frameIndex)
17951 {
17952     VMA_ASSERT(allocator);
17953     VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
17954 
17955     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17956 
17957     allocator->SetCurrentFrameIndex(frameIndex);
17958 }
17959 
vmaCalculateStats(VmaAllocator allocator,VmaStats * pStats)17960 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
17961     VmaAllocator allocator,
17962     VmaStats* pStats)
17963 {
17964     VMA_ASSERT(allocator && pStats);
17965     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17966     allocator->CalculateStats(pStats);
17967 }
17968 
vmaFreeEmptyBlock(VmaAllocator allocator)17969 VMA_CALL_PRE void VMA_CALL_POST vmaFreeEmptyBlock(
17970     VmaAllocator allocator)
17971 {
17972     VMA_ASSERT(allocator);
17973     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17974     allocator->FreeEmptyBlock();
17975 }
17976 
vmaGetBudget(VmaAllocator allocator,VmaBudget * pBudget)17977 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
17978     VmaAllocator allocator,
17979     VmaBudget* pBudget)
17980 {
17981     VMA_ASSERT(allocator && pBudget);
17982     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17983     allocator->GetBudget(pBudget, 0, allocator->GetMemoryHeapCount());
17984 }
17985 
17986 #if VMA_STATS_STRING_ENABLED
17987 
vmaBuildStatsString(VmaAllocator allocator,char ** ppStatsString,VkBool32 detailedMap)17988 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
17989     VmaAllocator allocator,
17990     char** ppStatsString,
17991     VkBool32 detailedMap)
17992 {
17993     VMA_ASSERT(allocator && ppStatsString);
17994     VMA_DEBUG_GLOBAL_MUTEX_LOCK
17995 
17996     VmaStringBuilder sb(allocator);
17997     {
17998         VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
17999         json.BeginObject();
18000 
18001         VmaBudget budget[VK_MAX_MEMORY_HEAPS];
18002         allocator->GetBudget(budget, 0, allocator->GetMemoryHeapCount());
18003 
18004         VmaStats stats;
18005         allocator->CalculateStats(&stats);
18006 
18007         json.WriteString("Total");
18008         VmaPrintStatInfo(json, stats.total);
18009 
18010         for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
18011         {
18012             json.BeginString("Heap ");
18013             json.ContinueString(heapIndex);
18014             json.EndString();
18015             json.BeginObject();
18016 
18017             json.WriteString("Size");
18018             json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
18019 
18020             json.WriteString("Flags");
18021             json.BeginArray(true);
18022             if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
18023             {
18024                 json.WriteString("DEVICE_LOCAL");
18025             }
18026             json.EndArray();
18027 
18028             json.WriteString("Budget");
18029             json.BeginObject();
18030             {
18031                 json.WriteString("BlockBytes");
18032                 json.WriteNumber(budget[heapIndex].blockBytes);
18033                 json.WriteString("AllocationBytes");
18034                 json.WriteNumber(budget[heapIndex].allocationBytes);
18035                 json.WriteString("Usage");
18036                 json.WriteNumber(budget[heapIndex].usage);
18037                 json.WriteString("Budget");
18038                 json.WriteNumber(budget[heapIndex].budget);
18039             }
18040             json.EndObject();
18041 
18042             if(stats.memoryHeap[heapIndex].blockCount > 0)
18043             {
18044                 json.WriteString("Stats");
18045                 VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
18046             }
18047 
18048             for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
18049             {
18050                 if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
18051                 {
18052                     json.BeginString("Type ");
18053                     json.ContinueString(typeIndex);
18054                     json.EndString();
18055 
18056                     json.BeginObject();
18057 
18058                     json.WriteString("Flags");
18059                     json.BeginArray(true);
18060                     VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
18061                     if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
18062                     {
18063                         json.WriteString("DEVICE_LOCAL");
18064                     }
18065                     if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
18066                     {
18067                         json.WriteString("HOST_VISIBLE");
18068                     }
18069                     if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
18070                     {
18071                         json.WriteString("HOST_COHERENT");
18072                     }
18073                     if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
18074                     {
18075                         json.WriteString("HOST_CACHED");
18076                     }
18077                     if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
18078                     {
18079                         json.WriteString("LAZILY_ALLOCATED");
18080                     }
18081                     if((flags & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0)
18082                     {
18083                         json.WriteString(" PROTECTED");
18084                     }
18085                     if((flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
18086                     {
18087                         json.WriteString(" DEVICE_COHERENT");
18088                     }
18089                     if((flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY) != 0)
18090                     {
18091                         json.WriteString(" DEVICE_UNCACHED");
18092                     }
18093                     json.EndArray();
18094 
18095                     if(stats.memoryType[typeIndex].blockCount > 0)
18096                     {
18097                         json.WriteString("Stats");
18098                         VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
18099                     }
18100 
18101                     json.EndObject();
18102                 }
18103             }
18104 
18105             json.EndObject();
18106         }
18107         if(detailedMap == VK_TRUE)
18108         {
18109             allocator->PrintDetailedMap(json);
18110         }
18111 
18112         json.EndObject();
18113     }
18114 
18115     const size_t len = sb.GetLength();
18116     char* const pChars = vma_new_array(allocator, char, len + 1);
18117     if(len > 0)
18118     {
18119         memcpy(pChars, sb.GetData(), len);
18120     }
18121     pChars[len] = '\0';
18122     *ppStatsString = pChars;
18123 }
18124 
vmaFreeStatsString(VmaAllocator allocator,char * pStatsString)18125 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
18126     VmaAllocator allocator,
18127     char* pStatsString)
18128 {
18129     if(pStatsString != VMA_NULL)
18130     {
18131         VMA_ASSERT(allocator);
18132         size_t len = strlen(pStatsString);
18133         vma_delete_array(allocator, pStatsString, len + 1);
18134     }
18135 }
18136 
18137 #endif // #if VMA_STATS_STRING_ENABLED
18138 
18139 /*
18140 This function is not protected by any mutex because it just reads immutable data.
18141 */
vmaFindMemoryTypeIndex(VmaAllocator allocator,uint32_t memoryTypeBits,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)18142 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
18143     VmaAllocator allocator,
18144     uint32_t memoryTypeBits,
18145     const VmaAllocationCreateInfo* pAllocationCreateInfo,
18146     uint32_t* pMemoryTypeIndex)
18147 {
18148     VMA_ASSERT(allocator != VK_NULL_HANDLE);
18149     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18150     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18151 
18152     memoryTypeBits &= allocator->GetGlobalMemoryTypeBits();
18153 
18154     if(pAllocationCreateInfo->memoryTypeBits != 0)
18155     {
18156         memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
18157     }
18158 
18159     uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
18160     uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
18161     uint32_t notPreferredFlags = 0;
18162 
18163     // Convert usage to requiredFlags and preferredFlags.
18164     switch(pAllocationCreateInfo->usage)
18165     {
18166     case VMA_MEMORY_USAGE_UNKNOWN:
18167         break;
18168     case VMA_MEMORY_USAGE_GPU_ONLY:
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_CPU_ONLY:
18175         requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
18176         break;
18177     case VMA_MEMORY_USAGE_CPU_TO_GPU:
18178         requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
18179         if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
18180         {
18181             preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18182         }
18183         break;
18184     case VMA_MEMORY_USAGE_GPU_TO_CPU:
18185         requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
18186         preferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
18187         break;
18188     case VMA_MEMORY_USAGE_CPU_COPY:
18189         notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18190         break;
18191     case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED:
18192         requiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
18193         break;
18194     default:
18195         VMA_ASSERT(0);
18196         break;
18197     }
18198 
18199     // Avoid DEVICE_COHERENT unless explicitly requested.
18200     if(((pAllocationCreateInfo->requiredFlags | pAllocationCreateInfo->preferredFlags) &
18201         (VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0)
18202     {
18203         notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY;
18204     }
18205 
18206     *pMemoryTypeIndex = UINT32_MAX;
18207     uint32_t minCost = UINT32_MAX;
18208     for(uint32_t memTypeIndex = 0, memTypeBit = 1;
18209         memTypeIndex < allocator->GetMemoryTypeCount();
18210         ++memTypeIndex, memTypeBit <<= 1)
18211     {
18212         // This memory type is acceptable according to memoryTypeBits bitmask.
18213         if((memTypeBit & memoryTypeBits) != 0)
18214         {
18215             const VkMemoryPropertyFlags currFlags =
18216                 allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
18217             // This memory type contains requiredFlags.
18218             if((requiredFlags & ~currFlags) == 0)
18219             {
18220                 // Calculate cost as number of bits from preferredFlags not present in this memory type.
18221                 uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags) +
18222                     VmaCountBitsSet(currFlags & notPreferredFlags);
18223                 // Remember memory type with lowest cost.
18224                 if(currCost < minCost)
18225                 {
18226                     *pMemoryTypeIndex = memTypeIndex;
18227                     if(currCost == 0)
18228                     {
18229                         return VK_SUCCESS;
18230                     }
18231                     minCost = currCost;
18232                 }
18233             }
18234         }
18235     }
18236     return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
18237 }
18238 
vmaFindMemoryTypeIndexForBufferInfo(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)18239 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
18240     VmaAllocator allocator,
18241     const VkBufferCreateInfo* pBufferCreateInfo,
18242     const VmaAllocationCreateInfo* pAllocationCreateInfo,
18243     uint32_t* pMemoryTypeIndex)
18244 {
18245     VMA_ASSERT(allocator != VK_NULL_HANDLE);
18246     VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
18247     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18248     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18249 
18250     const VkDevice hDev = allocator->m_hDevice;
18251     VkBuffer hBuffer = VK_NULL_HANDLE;
18252     VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
18253         hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
18254     if(res == VK_SUCCESS)
18255     {
18256         VkMemoryRequirements memReq = {};
18257         allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
18258             hDev, hBuffer, &memReq);
18259 
18260         res = vmaFindMemoryTypeIndex(
18261             allocator,
18262             memReq.memoryTypeBits,
18263             pAllocationCreateInfo,
18264             pMemoryTypeIndex);
18265 
18266         allocator->GetVulkanFunctions().vkDestroyBuffer(
18267             hDev, hBuffer, allocator->GetAllocationCallbacks());
18268     }
18269     return res;
18270 }
18271 
vmaFindMemoryTypeIndexForImageInfo(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)18272 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
18273     VmaAllocator allocator,
18274     const VkImageCreateInfo* pImageCreateInfo,
18275     const VmaAllocationCreateInfo* pAllocationCreateInfo,
18276     uint32_t* pMemoryTypeIndex)
18277 {
18278     VMA_ASSERT(allocator != VK_NULL_HANDLE);
18279     VMA_ASSERT(pImageCreateInfo != VMA_NULL);
18280     VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18281     VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18282 
18283     const VkDevice hDev = allocator->m_hDevice;
18284     VkImage hImage = VK_NULL_HANDLE;
18285     VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
18286         hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
18287     if(res == VK_SUCCESS)
18288     {
18289         VkMemoryRequirements memReq = {};
18290         allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
18291             hDev, hImage, &memReq);
18292 
18293         res = vmaFindMemoryTypeIndex(
18294             allocator,
18295             memReq.memoryTypeBits,
18296             pAllocationCreateInfo,
18297             pMemoryTypeIndex);
18298 
18299         allocator->GetVulkanFunctions().vkDestroyImage(
18300             hDev, hImage, allocator->GetAllocationCallbacks());
18301     }
18302     return res;
18303 }
18304 
vmaCreatePool(VmaAllocator allocator,const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)18305 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
18306     VmaAllocator allocator,
18307     const VmaPoolCreateInfo* pCreateInfo,
18308     VmaPool* pPool)
18309 {
18310     VMA_ASSERT(allocator && pCreateInfo && pPool);
18311 
18312     VMA_DEBUG_LOG("vmaCreatePool");
18313 
18314     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18315 
18316     VkResult res = allocator->CreatePool(pCreateInfo, pPool);
18317 
18318 #if VMA_RECORDING_ENABLED
18319     if(allocator->GetRecorder() != VMA_NULL)
18320     {
18321         allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
18322     }
18323 #endif
18324 
18325     return res;
18326 }
18327 
vmaDestroyPool(VmaAllocator allocator,VmaPool pool)18328 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
18329     VmaAllocator allocator,
18330     VmaPool pool)
18331 {
18332     VMA_ASSERT(allocator);
18333 
18334     if(pool == VK_NULL_HANDLE)
18335     {
18336         return;
18337     }
18338 
18339     VMA_DEBUG_LOG("vmaDestroyPool");
18340 
18341     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18342 
18343 #if VMA_RECORDING_ENABLED
18344     if(allocator->GetRecorder() != VMA_NULL)
18345     {
18346         allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
18347     }
18348 #endif
18349 
18350     allocator->DestroyPool(pool);
18351 }
18352 
vmaGetPoolStats(VmaAllocator allocator,VmaPool pool,VmaPoolStats * pPoolStats)18353 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
18354     VmaAllocator allocator,
18355     VmaPool pool,
18356     VmaPoolStats* pPoolStats)
18357 {
18358     VMA_ASSERT(allocator && pool && pPoolStats);
18359 
18360     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18361 
18362     allocator->GetPoolStats(pool, pPoolStats);
18363 }
18364 
vmaMakePoolAllocationsLost(VmaAllocator allocator,VmaPool pool,size_t * pLostAllocationCount)18365 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
18366     VmaAllocator allocator,
18367     VmaPool pool,
18368     size_t* pLostAllocationCount)
18369 {
18370     VMA_ASSERT(allocator && pool);
18371 
18372     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18373 
18374 #if VMA_RECORDING_ENABLED
18375     if(allocator->GetRecorder() != VMA_NULL)
18376     {
18377         allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
18378     }
18379 #endif
18380 
18381     allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
18382 }
18383 
vmaCheckPoolCorruption(VmaAllocator allocator,VmaPool pool)18384 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
18385 {
18386     VMA_ASSERT(allocator && pool);
18387 
18388     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18389 
18390     VMA_DEBUG_LOG("vmaCheckPoolCorruption");
18391 
18392     return allocator->CheckPoolCorruption(pool);
18393 }
18394 
vmaGetPoolName(VmaAllocator allocator,VmaPool pool,const char ** ppName)18395 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
18396     VmaAllocator allocator,
18397     VmaPool pool,
18398     const char** ppName)
18399 {
18400     VMA_ASSERT(allocator && pool && ppName);
18401 
18402     VMA_DEBUG_LOG("vmaGetPoolName");
18403 
18404     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18405 
18406     *ppName = pool->GetName();
18407 }
18408 
vmaSetPoolName(VmaAllocator allocator,VmaPool pool,const char * pName)18409 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
18410     VmaAllocator allocator,
18411     VmaPool pool,
18412     const char* pName)
18413 {
18414     VMA_ASSERT(allocator && pool);
18415 
18416     VMA_DEBUG_LOG("vmaSetPoolName");
18417 
18418     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18419 
18420     pool->SetName(pName);
18421 
18422 #if VMA_RECORDING_ENABLED
18423     if(allocator->GetRecorder() != VMA_NULL)
18424     {
18425         allocator->GetRecorder()->RecordSetPoolName(allocator->GetCurrentFrameIndex(), pool, pName);
18426     }
18427 #endif
18428 }
18429 
vmaAllocateMemory(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18430 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
18431     VmaAllocator allocator,
18432     const VkMemoryRequirements* pVkMemoryRequirements,
18433     const VmaAllocationCreateInfo* pCreateInfo,
18434     VmaAllocation* pAllocation,
18435     VmaAllocationInfo* pAllocationInfo)
18436 {
18437     VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
18438 
18439     VMA_DEBUG_LOG("vmaAllocateMemory");
18440 
18441     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18442 
18443     VkResult result = allocator->AllocateMemory(
18444         *pVkMemoryRequirements,
18445         false, // requiresDedicatedAllocation
18446         false, // prefersDedicatedAllocation
18447         VK_NULL_HANDLE, // dedicatedBuffer
18448         UINT32_MAX, // dedicatedBufferUsage
18449         VK_NULL_HANDLE, // dedicatedImage
18450         *pCreateInfo,
18451         VMA_SUBALLOCATION_TYPE_UNKNOWN,
18452         1, // allocationCount
18453         pAllocation);
18454 
18455 #if VMA_RECORDING_ENABLED
18456     if(allocator->GetRecorder() != VMA_NULL)
18457     {
18458         allocator->GetRecorder()->RecordAllocateMemory(
18459             allocator->GetCurrentFrameIndex(),
18460             *pVkMemoryRequirements,
18461             *pCreateInfo,
18462             *pAllocation);
18463     }
18464 #endif
18465 
18466     if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18467     {
18468         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18469     }
18470 
18471     return result;
18472 }
18473 
vmaAllocateMemoryPages(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,size_t allocationCount,VmaAllocation * pAllocations,VmaAllocationInfo * pAllocationInfo)18474 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
18475     VmaAllocator allocator,
18476     const VkMemoryRequirements* pVkMemoryRequirements,
18477     const VmaAllocationCreateInfo* pCreateInfo,
18478     size_t allocationCount,
18479     VmaAllocation* pAllocations,
18480     VmaAllocationInfo* pAllocationInfo)
18481 {
18482     if(allocationCount == 0)
18483     {
18484         return VK_SUCCESS;
18485     }
18486 
18487     VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
18488 
18489     VMA_DEBUG_LOG("vmaAllocateMemoryPages");
18490 
18491     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18492 
18493     VkResult result = allocator->AllocateMemory(
18494         *pVkMemoryRequirements,
18495         false, // requiresDedicatedAllocation
18496         false, // prefersDedicatedAllocation
18497         VK_NULL_HANDLE, // dedicatedBuffer
18498         UINT32_MAX, // dedicatedBufferUsage
18499         VK_NULL_HANDLE, // dedicatedImage
18500         *pCreateInfo,
18501         VMA_SUBALLOCATION_TYPE_UNKNOWN,
18502         allocationCount,
18503         pAllocations);
18504 
18505 #if VMA_RECORDING_ENABLED
18506     if(allocator->GetRecorder() != VMA_NULL)
18507     {
18508         allocator->GetRecorder()->RecordAllocateMemoryPages(
18509             allocator->GetCurrentFrameIndex(),
18510             *pVkMemoryRequirements,
18511             *pCreateInfo,
18512             (uint64_t)allocationCount,
18513             pAllocations);
18514     }
18515 #endif
18516 
18517     if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18518     {
18519         for(size_t i = 0; i < allocationCount; ++i)
18520         {
18521             allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
18522         }
18523     }
18524 
18525     return result;
18526 }
18527 
vmaAllocateMemoryForBuffer(VmaAllocator allocator,VkBuffer buffer,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18528 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
18529     VmaAllocator allocator,
18530     VkBuffer buffer,
18531     const VmaAllocationCreateInfo* pCreateInfo,
18532     VmaAllocation* pAllocation,
18533     VmaAllocationInfo* pAllocationInfo)
18534 {
18535     VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18536 
18537     VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
18538 
18539     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18540 
18541     VkMemoryRequirements vkMemReq = {};
18542     bool requiresDedicatedAllocation = false;
18543     bool prefersDedicatedAllocation = false;
18544     allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
18545         requiresDedicatedAllocation,
18546         prefersDedicatedAllocation);
18547 
18548     VkResult result = allocator->AllocateMemory(
18549         vkMemReq,
18550         requiresDedicatedAllocation,
18551         prefersDedicatedAllocation,
18552         buffer, // dedicatedBuffer
18553         UINT32_MAX, // dedicatedBufferUsage
18554         VK_NULL_HANDLE, // dedicatedImage
18555         *pCreateInfo,
18556         VMA_SUBALLOCATION_TYPE_BUFFER,
18557         1, // allocationCount
18558         pAllocation);
18559 
18560 #if VMA_RECORDING_ENABLED
18561     if(allocator->GetRecorder() != VMA_NULL)
18562     {
18563         allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
18564             allocator->GetCurrentFrameIndex(),
18565             vkMemReq,
18566             requiresDedicatedAllocation,
18567             prefersDedicatedAllocation,
18568             *pCreateInfo,
18569             *pAllocation);
18570     }
18571 #endif
18572 
18573     if(pAllocationInfo && result == VK_SUCCESS)
18574     {
18575         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18576     }
18577 
18578     return result;
18579 }
18580 
vmaAllocateMemoryForImage(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18581 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
18582     VmaAllocator allocator,
18583     VkImage image,
18584     const VmaAllocationCreateInfo* pCreateInfo,
18585     VmaAllocation* pAllocation,
18586     VmaAllocationInfo* pAllocationInfo)
18587 {
18588     VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18589 
18590     VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
18591 
18592     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18593 
18594     VkMemoryRequirements vkMemReq = {};
18595     bool requiresDedicatedAllocation = false;
18596     bool prefersDedicatedAllocation  = false;
18597     allocator->GetImageMemoryRequirements(image, vkMemReq,
18598         requiresDedicatedAllocation, prefersDedicatedAllocation);
18599 
18600     VkResult result = allocator->AllocateMemory(
18601         vkMemReq,
18602         requiresDedicatedAllocation,
18603         prefersDedicatedAllocation,
18604         VK_NULL_HANDLE, // dedicatedBuffer
18605         UINT32_MAX, // dedicatedBufferUsage
18606         image, // dedicatedImage
18607         *pCreateInfo,
18608         VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
18609         1, // allocationCount
18610         pAllocation);
18611 
18612 #if VMA_RECORDING_ENABLED
18613     if(allocator->GetRecorder() != VMA_NULL)
18614     {
18615         allocator->GetRecorder()->RecordAllocateMemoryForImage(
18616             allocator->GetCurrentFrameIndex(),
18617             vkMemReq,
18618             requiresDedicatedAllocation,
18619             prefersDedicatedAllocation,
18620             *pCreateInfo,
18621             *pAllocation);
18622     }
18623 #endif
18624 
18625     if(pAllocationInfo && result == VK_SUCCESS)
18626     {
18627         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18628     }
18629 
18630     return result;
18631 }
18632 
vmaFreeMemory(VmaAllocator allocator,VmaAllocation allocation)18633 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
18634     VmaAllocator allocator,
18635     VmaAllocation allocation)
18636 {
18637     VMA_ASSERT(allocator);
18638 
18639     if(allocation == VK_NULL_HANDLE)
18640     {
18641         return;
18642     }
18643 
18644     VMA_DEBUG_LOG("vmaFreeMemory");
18645 
18646     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18647 
18648 #if VMA_RECORDING_ENABLED
18649     if(allocator->GetRecorder() != VMA_NULL)
18650     {
18651         allocator->GetRecorder()->RecordFreeMemory(
18652             allocator->GetCurrentFrameIndex(),
18653             allocation);
18654     }
18655 #endif
18656 
18657     allocator->FreeMemory(
18658         1, // allocationCount
18659         &allocation);
18660 }
18661 
vmaFreeMemoryPages(VmaAllocator allocator,size_t allocationCount,const VmaAllocation * pAllocations)18662 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
18663     VmaAllocator allocator,
18664     size_t allocationCount,
18665     const VmaAllocation* pAllocations)
18666 {
18667     if(allocationCount == 0)
18668     {
18669         return;
18670     }
18671 
18672     VMA_ASSERT(allocator);
18673 
18674     VMA_DEBUG_LOG("vmaFreeMemoryPages");
18675 
18676     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18677 
18678 #if VMA_RECORDING_ENABLED
18679     if(allocator->GetRecorder() != VMA_NULL)
18680     {
18681         allocator->GetRecorder()->RecordFreeMemoryPages(
18682             allocator->GetCurrentFrameIndex(),
18683             (uint64_t)allocationCount,
18684             pAllocations);
18685     }
18686 #endif
18687 
18688     allocator->FreeMemory(allocationCount, pAllocations);
18689 }
18690 
18691 // 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)18692 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateReservedMemoryForImage(
18693     VmaAllocator VMA_NOT_NULL allocator,
18694     VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
18695     const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
18696     VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
18697     VmaAllocationInfo* VMA_NULLABLE pAllocationInfo)
18698 {
18699     VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18700     VMA_DEBUG_LOG("vmaAllocateReservedMemoryForImage");
18701     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18702 
18703     VkMemoryRequirements vkMemReq = {};
18704     bool requiresDedicatedAllocation = false;
18705     bool prefersDedicatedAllocation  = false;
18706     allocator->GetImageMemoryRequirements(image, vkMemReq,
18707         requiresDedicatedAllocation, prefersDedicatedAllocation);
18708 
18709     VkResult result = allocator->AllocateReservedMemory(
18710         vkMemReq,
18711         requiresDedicatedAllocation,
18712         prefersDedicatedAllocation,
18713         VK_NULL_HANDLE, // dedicatedBuffer
18714         UINT32_MAX, // dedicatedBufferUsage
18715         image, // dedicatedImage
18716         *pCreateInfo,
18717         VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
18718         1, // allocationCount
18719         pAllocation);
18720 
18721     if(pAllocationInfo && result == VK_SUCCESS)
18722     {
18723         allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18724     }
18725 
18726     return result;
18727 }
18728 
vmaGetNewBlockStats(VmaAllocation allocation,bool * pStats)18729 VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetNewBlockStats(
18730     VmaAllocation allocation,
18731     bool* pStats)
18732 {
18733     VMA_ASSERT(allocation);
18734     VMA_DEBUG_LOG("vmaGetNewBlockStats");
18735     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18736 
18737     if (pStats != NULL) {
18738         *pStats = allocation->IsNewBlockFlag();
18739     }
18740     return VK_SUCCESS;
18741 }
18742 
vmaClearNewBlockStats(VmaAllocation allocation)18743 VMA_CALL_PRE VkResult VMA_CALL_POST vmaClearNewBlockStats(
18744     VmaAllocation allocation)
18745 {
18746     VMA_ASSERT(allocation);
18747     VMA_DEBUG_LOG("vmaClearNewBlockStats");
18748     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18749 
18750     allocation->ClearNewBlockFlag();
18751     return VK_SUCCESS;
18752 }
18753 
vmaSwapReservedBlock(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18754 VMA_CALL_PRE VkResult VMA_CALL_POST vmaSwapReservedBlock(
18755     VmaAllocator allocator,
18756     VkImage image,
18757     const VmaAllocationCreateInfo* pCreateInfo,
18758     VmaAllocation* pAllocation,
18759     VmaAllocationInfo* pAllocationInfo)
18760 {
18761     VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18762     VMA_DEBUG_LOG("vmaSwapReservedBlock");
18763     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18764 
18765     return allocator->SwapReservedBlock(image, pCreateInfo, pAllocation, pAllocationInfo);
18766 }
18767 
vmaFreeReservedMemory(VmaAllocator allocator,VmaAllocation allocation)18768 VMA_CALL_PRE void VMA_CALL_POST vmaFreeReservedMemory(
18769     VmaAllocator allocator,
18770     VmaAllocation allocation)
18771 {
18772     VMA_ASSERT(allocator);
18773     if (allocation == VK_NULL_HANDLE)
18774     {
18775         return;
18776     }
18777     VMA_DEBUG_LOG("vmaFreeReservedMemory");
18778     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18779 
18780     allocator->FreeReservedMemory(
18781         1, // allocationCount
18782         &allocation);
18783 }
18784 
vmaGetPreAllocBlockSize(VmaAllocator allocator,uint32_t * pStats)18785 VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetPreAllocBlockSize(VmaAllocator allocator, uint32_t* pStats)
18786 {
18787     VMA_ASSERT(allocator);
18788     VMA_DEBUG_LOG("vmaGetPreAllocBlockSize");
18789 
18790     if (pStats != NULL) {
18791         *pStats = allocator->GetPreAllocBlockSize();
18792     }
18793     return VK_SUCCESS;
18794 }
18795 
vmaResizeAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize newSize)18796 VMA_CALL_PRE VkResult VMA_CALL_POST vmaResizeAllocation(
18797     VmaAllocator allocator,
18798     VmaAllocation allocation,
18799     VkDeviceSize newSize)
18800 {
18801     VMA_ASSERT(allocator && allocation);
18802 
18803     VMA_DEBUG_LOG("vmaResizeAllocation");
18804 
18805     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18806 
18807     return allocator->ResizeAllocation(allocation, newSize);
18808 }
18809 
vmaGetAllocationInfo(VmaAllocator allocator,VmaAllocation allocation,VmaAllocationInfo * pAllocationInfo)18810 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
18811     VmaAllocator allocator,
18812     VmaAllocation allocation,
18813     VmaAllocationInfo* pAllocationInfo)
18814 {
18815     VMA_ASSERT(allocator && allocation && pAllocationInfo);
18816 
18817     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18818 
18819 #if VMA_RECORDING_ENABLED
18820     if(allocator->GetRecorder() != VMA_NULL)
18821     {
18822         allocator->GetRecorder()->RecordGetAllocationInfo(
18823             allocator->GetCurrentFrameIndex(),
18824             allocation);
18825     }
18826 #endif
18827 
18828     allocator->GetAllocationInfo(allocation, pAllocationInfo);
18829 }
18830 
vmaTouchAllocation(VmaAllocator allocator,VmaAllocation allocation)18831 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
18832     VmaAllocator allocator,
18833     VmaAllocation allocation)
18834 {
18835     VMA_ASSERT(allocator && allocation);
18836 
18837     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18838 
18839 #if VMA_RECORDING_ENABLED
18840     if(allocator->GetRecorder() != VMA_NULL)
18841     {
18842         allocator->GetRecorder()->RecordTouchAllocation(
18843             allocator->GetCurrentFrameIndex(),
18844             allocation);
18845     }
18846 #endif
18847 
18848     return allocator->TouchAllocation(allocation);
18849 }
18850 
vmaSetAllocationUserData(VmaAllocator allocator,VmaAllocation allocation,void * pUserData)18851 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
18852     VmaAllocator allocator,
18853     VmaAllocation allocation,
18854     void* pUserData)
18855 {
18856     VMA_ASSERT(allocator && allocation);
18857 
18858     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18859 
18860     allocation->SetUserData(allocator, pUserData);
18861 
18862 #if VMA_RECORDING_ENABLED
18863     if(allocator->GetRecorder() != VMA_NULL)
18864     {
18865         allocator->GetRecorder()->RecordSetAllocationUserData(
18866             allocator->GetCurrentFrameIndex(),
18867             allocation,
18868             pUserData);
18869     }
18870 #endif
18871 }
18872 
vmaCreateLostAllocation(VmaAllocator allocator,VmaAllocation * pAllocation)18873 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
18874     VmaAllocator allocator,
18875     VmaAllocation* pAllocation)
18876 {
18877     VMA_ASSERT(allocator && pAllocation);
18878 
18879     VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18880 
18881     allocator->CreateLostAllocation(pAllocation);
18882 
18883 #if VMA_RECORDING_ENABLED
18884     if(allocator->GetRecorder() != VMA_NULL)
18885     {
18886         allocator->GetRecorder()->RecordCreateLostAllocation(
18887             allocator->GetCurrentFrameIndex(),
18888             *pAllocation);
18889     }
18890 #endif
18891 }
18892 
vmaMapMemory(VmaAllocator allocator,VmaAllocation allocation,void ** ppData)18893 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
18894     VmaAllocator allocator,
18895     VmaAllocation allocation,
18896     void** ppData)
18897 {
18898     VMA_ASSERT(allocator && allocation && ppData);
18899 
18900     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18901 
18902     VkResult res = allocator->Map(allocation, ppData);
18903 
18904 #if VMA_RECORDING_ENABLED
18905     if(allocator->GetRecorder() != VMA_NULL)
18906     {
18907         allocator->GetRecorder()->RecordMapMemory(
18908             allocator->GetCurrentFrameIndex(),
18909             allocation);
18910     }
18911 #endif
18912 
18913     return res;
18914 }
18915 
vmaUnmapMemory(VmaAllocator allocator,VmaAllocation allocation)18916 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
18917     VmaAllocator allocator,
18918     VmaAllocation allocation)
18919 {
18920     VMA_ASSERT(allocator && allocation);
18921 
18922     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18923 
18924 #if VMA_RECORDING_ENABLED
18925     if(allocator->GetRecorder() != VMA_NULL)
18926     {
18927         allocator->GetRecorder()->RecordUnmapMemory(
18928             allocator->GetCurrentFrameIndex(),
18929             allocation);
18930     }
18931 #endif
18932 
18933     allocator->Unmap(allocation);
18934 }
18935 
vmaFlushAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)18936 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18937 {
18938     VMA_ASSERT(allocator && allocation);
18939 
18940     VMA_DEBUG_LOG("vmaFlushAllocation");
18941 
18942     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18943 
18944     const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
18945 
18946 #if VMA_RECORDING_ENABLED
18947     if(allocator->GetRecorder() != VMA_NULL)
18948     {
18949         allocator->GetRecorder()->RecordFlushAllocation(
18950             allocator->GetCurrentFrameIndex(),
18951             allocation, offset, size);
18952     }
18953 #endif
18954 
18955     return res;
18956 }
18957 
vmaInvalidateAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)18958 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18959 {
18960     VMA_ASSERT(allocator && allocation);
18961 
18962     VMA_DEBUG_LOG("vmaInvalidateAllocation");
18963 
18964     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18965 
18966     const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
18967 
18968 #if VMA_RECORDING_ENABLED
18969     if(allocator->GetRecorder() != VMA_NULL)
18970     {
18971         allocator->GetRecorder()->RecordInvalidateAllocation(
18972             allocator->GetCurrentFrameIndex(),
18973             allocation, offset, size);
18974     }
18975 #endif
18976 
18977     return res;
18978 }
18979 
vmaFlushAllocations(VmaAllocator allocator,uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes)18980 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
18981     VmaAllocator allocator,
18982     uint32_t allocationCount,
18983     const VmaAllocation* allocations,
18984     const VkDeviceSize* offsets,
18985     const VkDeviceSize* sizes)
18986 {
18987     VMA_ASSERT(allocator);
18988 
18989     if(allocationCount == 0)
18990     {
18991         return VK_SUCCESS;
18992     }
18993 
18994     VMA_ASSERT(allocations);
18995 
18996     VMA_DEBUG_LOG("vmaFlushAllocations");
18997 
18998     VMA_DEBUG_GLOBAL_MUTEX_LOCK
18999 
19000     const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH);
19001 
19002 #if VMA_RECORDING_ENABLED
19003     if(allocator->GetRecorder() != VMA_NULL)
19004     {
19005         //TODO
19006     }
19007 #endif
19008 
19009     return res;
19010 }
19011 
vmaInvalidateAllocations(VmaAllocator allocator,uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes)19012 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
19013     VmaAllocator allocator,
19014     uint32_t allocationCount,
19015     const VmaAllocation* allocations,
19016     const VkDeviceSize* offsets,
19017     const VkDeviceSize* sizes)
19018 {
19019     VMA_ASSERT(allocator);
19020 
19021     if(allocationCount == 0)
19022     {
19023         return VK_SUCCESS;
19024     }
19025 
19026     VMA_ASSERT(allocations);
19027 
19028     VMA_DEBUG_LOG("vmaInvalidateAllocations");
19029 
19030     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19031 
19032     const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE);
19033 
19034 #if VMA_RECORDING_ENABLED
19035     if(allocator->GetRecorder() != VMA_NULL)
19036     {
19037         //TODO
19038     }
19039 #endif
19040 
19041     return res;
19042 }
19043 
vmaCheckCorruption(VmaAllocator allocator,uint32_t memoryTypeBits)19044 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
19045 {
19046     VMA_ASSERT(allocator);
19047 
19048     VMA_DEBUG_LOG("vmaCheckCorruption");
19049 
19050     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19051 
19052     return allocator->CheckCorruption(memoryTypeBits);
19053 }
19054 
vmaDefragment(VmaAllocator allocator,const VmaAllocation * pAllocations,size_t allocationCount,VkBool32 * pAllocationsChanged,const VmaDefragmentationInfo * pDefragmentationInfo,VmaDefragmentationStats * pDefragmentationStats)19055 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
19056     VmaAllocator allocator,
19057     const VmaAllocation* pAllocations,
19058     size_t allocationCount,
19059     VkBool32* pAllocationsChanged,
19060     const VmaDefragmentationInfo *pDefragmentationInfo,
19061     VmaDefragmentationStats* pDefragmentationStats)
19062 {
19063     // Deprecated interface, reimplemented using new one.
19064 
19065     VmaDefragmentationInfo2 info2 = {};
19066     info2.allocationCount = (uint32_t)allocationCount;
19067     info2.pAllocations = pAllocations;
19068     info2.pAllocationsChanged = pAllocationsChanged;
19069     if(pDefragmentationInfo != VMA_NULL)
19070     {
19071         info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
19072         info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
19073     }
19074     else
19075     {
19076         info2.maxCpuAllocationsToMove = UINT32_MAX;
19077         info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
19078     }
19079     // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
19080 
19081     VmaDefragmentationContext ctx;
19082     VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
19083     if(res == VK_NOT_READY)
19084     {
19085         res = vmaDefragmentationEnd( allocator, ctx);
19086     }
19087     return res;
19088 }
19089 
vmaDefragmentationBegin(VmaAllocator allocator,const VmaDefragmentationInfo2 * pInfo,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)19090 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
19091     VmaAllocator allocator,
19092     const VmaDefragmentationInfo2* pInfo,
19093     VmaDefragmentationStats* pStats,
19094     VmaDefragmentationContext *pContext)
19095 {
19096     VMA_ASSERT(allocator && pInfo && pContext);
19097 
19098     // Degenerate case: Nothing to defragment.
19099     if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
19100     {
19101         return VK_SUCCESS;
19102     }
19103 
19104     VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
19105     VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
19106     VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
19107     VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
19108 
19109     VMA_DEBUG_LOG("vmaDefragmentationBegin");
19110 
19111     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19112 
19113     VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
19114 
19115 #if VMA_RECORDING_ENABLED
19116     if(allocator->GetRecorder() != VMA_NULL)
19117     {
19118         allocator->GetRecorder()->RecordDefragmentationBegin(
19119             allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
19120     }
19121 #endif
19122 
19123     return res;
19124 }
19125 
vmaDefragmentationEnd(VmaAllocator allocator,VmaDefragmentationContext context)19126 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
19127     VmaAllocator allocator,
19128     VmaDefragmentationContext context)
19129 {
19130     VMA_ASSERT(allocator);
19131 
19132     VMA_DEBUG_LOG("vmaDefragmentationEnd");
19133 
19134     if(context != VK_NULL_HANDLE)
19135     {
19136         VMA_DEBUG_GLOBAL_MUTEX_LOCK
19137 
19138 #if VMA_RECORDING_ENABLED
19139         if(allocator->GetRecorder() != VMA_NULL)
19140         {
19141             allocator->GetRecorder()->RecordDefragmentationEnd(
19142                 allocator->GetCurrentFrameIndex(), context);
19143         }
19144 #endif
19145 
19146         return allocator->DefragmentationEnd(context);
19147     }
19148     else
19149     {
19150         return VK_SUCCESS;
19151     }
19152 }
19153 
vmaBeginDefragmentationPass(VmaAllocator allocator,VmaDefragmentationContext context,VmaDefragmentationPassInfo * pInfo)19154 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
19155     VmaAllocator allocator,
19156     VmaDefragmentationContext context,
19157     VmaDefragmentationPassInfo* pInfo
19158     )
19159 {
19160     VMA_ASSERT(allocator);
19161     VMA_ASSERT(pInfo);
19162     VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->moveCount, pInfo->pMoves));
19163 
19164     VMA_DEBUG_LOG("vmaBeginDefragmentationPass");
19165 
19166     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19167 
19168     if(context == VK_NULL_HANDLE)
19169     {
19170         pInfo->moveCount = 0;
19171         return VK_SUCCESS;
19172     }
19173 
19174     return allocator->DefragmentationPassBegin(pInfo, context);
19175 }
vmaEndDefragmentationPass(VmaAllocator allocator,VmaDefragmentationContext context)19176 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
19177     VmaAllocator allocator,
19178     VmaDefragmentationContext context)
19179 {
19180     VMA_ASSERT(allocator);
19181 
19182     VMA_DEBUG_LOG("vmaEndDefragmentationPass");
19183     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19184 
19185     if(context == VK_NULL_HANDLE)
19186         return VK_SUCCESS;
19187 
19188     return allocator->DefragmentationPassEnd(context);
19189 }
19190 
vmaBindBufferMemory(VmaAllocator allocator,VmaAllocation allocation,VkBuffer buffer)19191 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
19192     VmaAllocator allocator,
19193     VmaAllocation allocation,
19194     VkBuffer buffer)
19195 {
19196     VMA_ASSERT(allocator && allocation && buffer);
19197 
19198     VMA_DEBUG_LOG("vmaBindBufferMemory");
19199 
19200     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19201 
19202     return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL);
19203 }
19204 
vmaBindBufferMemory2(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize allocationLocalOffset,VkBuffer buffer,const void * pNext)19205 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
19206     VmaAllocator allocator,
19207     VmaAllocation allocation,
19208     VkDeviceSize allocationLocalOffset,
19209     VkBuffer buffer,
19210     const void* pNext)
19211 {
19212     VMA_ASSERT(allocator && allocation && buffer);
19213 
19214     VMA_DEBUG_LOG("vmaBindBufferMemory2");
19215 
19216     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19217 
19218     return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext);
19219 }
19220 
vmaBindImageMemory(VmaAllocator allocator,VmaAllocation allocation,VkImage image)19221 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
19222     VmaAllocator allocator,
19223     VmaAllocation allocation,
19224     VkImage image)
19225 {
19226     VMA_ASSERT(allocator && allocation && image);
19227 
19228     VMA_DEBUG_LOG("vmaBindImageMemory");
19229 
19230     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19231 
19232     return allocator->BindImageMemory(allocation, 0, image, VMA_NULL);
19233 }
19234 
vmaBindImageMemory2(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize allocationLocalOffset,VkImage image,const void * pNext)19235 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
19236     VmaAllocator allocator,
19237     VmaAllocation allocation,
19238     VkDeviceSize allocationLocalOffset,
19239     VkImage image,
19240     const void* pNext)
19241 {
19242     VMA_ASSERT(allocator && allocation && image);
19243 
19244     VMA_DEBUG_LOG("vmaBindImageMemory2");
19245 
19246     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19247 
19248         return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext);
19249 }
19250 
vmaCreateBuffer(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkBuffer * pBuffer,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)19251 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
19252     VmaAllocator allocator,
19253     const VkBufferCreateInfo* pBufferCreateInfo,
19254     const VmaAllocationCreateInfo* pAllocationCreateInfo,
19255     VkBuffer* pBuffer,
19256     VmaAllocation* pAllocation,
19257     VmaAllocationInfo* pAllocationInfo)
19258 {
19259     VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
19260 
19261     if(pBufferCreateInfo->size == 0)
19262     {
19263         return VK_ERROR_VALIDATION_FAILED_EXT;
19264     }
19265     if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
19266         !allocator->m_UseKhrBufferDeviceAddress)
19267     {
19268         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.");
19269         return VK_ERROR_VALIDATION_FAILED_EXT;
19270     }
19271 
19272     VMA_DEBUG_LOG("vmaCreateBuffer");
19273 
19274     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19275 
19276     *pBuffer = VK_NULL_HANDLE;
19277     *pAllocation = VK_NULL_HANDLE;
19278 
19279     // 1. Create VkBuffer.
19280     VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
19281         allocator->m_hDevice,
19282         pBufferCreateInfo,
19283         allocator->GetAllocationCallbacks(),
19284         pBuffer);
19285     if(res >= 0)
19286     {
19287         // 2. vkGetBufferMemoryRequirements.
19288         VkMemoryRequirements vkMemReq = {};
19289         bool requiresDedicatedAllocation = false;
19290         bool prefersDedicatedAllocation  = false;
19291         allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
19292             requiresDedicatedAllocation, prefersDedicatedAllocation);
19293 
19294         // 3. Allocate memory using allocator.
19295         res = allocator->AllocateMemory(
19296             vkMemReq,
19297             requiresDedicatedAllocation,
19298             prefersDedicatedAllocation,
19299             *pBuffer, // dedicatedBuffer
19300             pBufferCreateInfo->usage, // dedicatedBufferUsage
19301             VK_NULL_HANDLE, // dedicatedImage
19302             *pAllocationCreateInfo,
19303             VMA_SUBALLOCATION_TYPE_BUFFER,
19304             1, // allocationCount
19305             pAllocation);
19306 
19307 #if VMA_RECORDING_ENABLED
19308         if(allocator->GetRecorder() != VMA_NULL)
19309         {
19310             allocator->GetRecorder()->RecordCreateBuffer(
19311                 allocator->GetCurrentFrameIndex(),
19312                 *pBufferCreateInfo,
19313                 *pAllocationCreateInfo,
19314                 *pAllocation);
19315         }
19316 #endif
19317 
19318         if(res >= 0)
19319         {
19320             // 3. Bind buffer with memory.
19321             if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
19322             {
19323                 res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
19324             }
19325             if(res >= 0)
19326             {
19327                 // All steps succeeded.
19328                 #if VMA_STATS_STRING_ENABLED
19329                     (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
19330                 #endif
19331                 if(pAllocationInfo != VMA_NULL)
19332                 {
19333                     allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
19334                 }
19335 
19336                 return VK_SUCCESS;
19337             }
19338             allocator->FreeMemory(
19339                 1, // allocationCount
19340                 pAllocation);
19341             *pAllocation = VK_NULL_HANDLE;
19342             (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
19343             *pBuffer = VK_NULL_HANDLE;
19344             return res;
19345         }
19346         (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
19347         *pBuffer = VK_NULL_HANDLE;
19348         return res;
19349     }
19350     return res;
19351 }
19352 
vmaDestroyBuffer(VmaAllocator allocator,VkBuffer buffer,VmaAllocation allocation)19353 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
19354     VmaAllocator allocator,
19355     VkBuffer buffer,
19356     VmaAllocation allocation)
19357 {
19358     VMA_ASSERT(allocator);
19359 
19360     if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
19361     {
19362         return;
19363     }
19364 
19365     VMA_DEBUG_LOG("vmaDestroyBuffer");
19366 
19367     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19368 
19369 #if VMA_RECORDING_ENABLED
19370     if(allocator->GetRecorder() != VMA_NULL)
19371     {
19372         allocator->GetRecorder()->RecordDestroyBuffer(
19373             allocator->GetCurrentFrameIndex(),
19374             allocation);
19375     }
19376 #endif
19377 
19378     if(buffer != VK_NULL_HANDLE)
19379     {
19380         (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
19381     }
19382 
19383     if(allocation != VK_NULL_HANDLE)
19384     {
19385         allocator->FreeMemory(
19386             1, // allocationCount
19387             &allocation);
19388     }
19389 }
19390 
vmaCreateImage(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkImage * pImage,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)19391 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
19392     VmaAllocator allocator,
19393     const VkImageCreateInfo* pImageCreateInfo,
19394     const VmaAllocationCreateInfo* pAllocationCreateInfo,
19395     VkImage* pImage,
19396     VmaAllocation* pAllocation,
19397     VmaAllocationInfo* pAllocationInfo)
19398 {
19399     VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
19400 
19401     if(pImageCreateInfo->extent.width == 0 ||
19402         pImageCreateInfo->extent.height == 0 ||
19403         pImageCreateInfo->extent.depth == 0 ||
19404         pImageCreateInfo->mipLevels == 0 ||
19405         pImageCreateInfo->arrayLayers == 0)
19406     {
19407         return VK_ERROR_VALIDATION_FAILED_EXT;
19408     }
19409 
19410     VMA_DEBUG_LOG("vmaCreateImage");
19411 
19412     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19413 
19414     *pImage = VK_NULL_HANDLE;
19415     *pAllocation = VK_NULL_HANDLE;
19416 
19417     // 1. Create VkImage.
19418     VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
19419         allocator->m_hDevice,
19420         pImageCreateInfo,
19421         allocator->GetAllocationCallbacks(),
19422         pImage);
19423     if(res >= 0)
19424     {
19425         VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
19426             VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
19427             VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
19428 
19429         // 2. Allocate memory using allocator.
19430         VkMemoryRequirements vkMemReq = {};
19431         bool requiresDedicatedAllocation = false;
19432         bool prefersDedicatedAllocation  = false;
19433         allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
19434             requiresDedicatedAllocation, prefersDedicatedAllocation);
19435 
19436         res = allocator->AllocateMemory(
19437             vkMemReq,
19438             requiresDedicatedAllocation,
19439             prefersDedicatedAllocation,
19440             VK_NULL_HANDLE, // dedicatedBuffer
19441             UINT32_MAX, // dedicatedBufferUsage
19442             *pImage, // dedicatedImage
19443             *pAllocationCreateInfo,
19444             suballocType,
19445             1, // allocationCount
19446             pAllocation);
19447 
19448 #if VMA_RECORDING_ENABLED
19449         if(allocator->GetRecorder() != VMA_NULL)
19450         {
19451             allocator->GetRecorder()->RecordCreateImage(
19452                 allocator->GetCurrentFrameIndex(),
19453                 *pImageCreateInfo,
19454                 *pAllocationCreateInfo,
19455                 *pAllocation);
19456         }
19457 #endif
19458 
19459         if(res >= 0)
19460         {
19461             // 3. Bind image with memory.
19462             if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
19463             {
19464                 res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);
19465             }
19466             if(res >= 0)
19467             {
19468                 // All steps succeeded.
19469                 #if VMA_STATS_STRING_ENABLED
19470                     (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
19471                 #endif
19472                 if(pAllocationInfo != VMA_NULL)
19473                 {
19474                     allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
19475                 }
19476 
19477                 return VK_SUCCESS;
19478             }
19479             allocator->FreeMemory(
19480                 1, // allocationCount
19481                 pAllocation);
19482             *pAllocation = VK_NULL_HANDLE;
19483             (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
19484             *pImage = VK_NULL_HANDLE;
19485             return res;
19486         }
19487         (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
19488         *pImage = VK_NULL_HANDLE;
19489         return res;
19490     }
19491     return res;
19492 }
19493 
vmaDestroyImage(VmaAllocator allocator,VkImage image,VmaAllocation allocation)19494 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
19495     VmaAllocator allocator,
19496     VkImage image,
19497     VmaAllocation allocation)
19498 {
19499     VMA_ASSERT(allocator);
19500 
19501     if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
19502     {
19503         return;
19504     }
19505 
19506     VMA_DEBUG_LOG("vmaDestroyImage");
19507 
19508     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19509 
19510 #if VMA_RECORDING_ENABLED
19511     if(allocator->GetRecorder() != VMA_NULL)
19512     {
19513         allocator->GetRecorder()->RecordDestroyImage(
19514             allocator->GetCurrentFrameIndex(),
19515             allocation);
19516     }
19517 #endif
19518 
19519     if(image != VK_NULL_HANDLE)
19520     {
19521         (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
19522     }
19523     if(allocation != VK_NULL_HANDLE)
19524     {
19525         allocator->FreeMemory(
19526             1, // allocationCount
19527             &allocation);
19528     }
19529 }
19530 
19531 // OH ISSUE: VMA preAlloc
vmaCreateFakeImage(VmaAllocator allocator,VkImage * pImage)19532 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateFakeImage(
19533     VmaAllocator allocator,
19534     VkImage* pImage)
19535 {
19536     VMA_ASSERT(allocator && pImage);
19537     VMA_DEBUG_LOG("vmaCreateFakeImage");
19538     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19539 
19540     *pImage = VK_NULL_HANDLE;
19541     const VkImageCreateInfo imageCreateInfo = {
19542         VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,         // sType
19543         nullptr,                                     // pNext
19544         0,                                           // VkImageCreateFlags
19545         VK_IMAGE_TYPE_2D,                            // VkImageType
19546         VK_FORMAT_R8G8B8A8_UNORM,                    // VkFormat
19547         { 10, 10, 1 },                               // VkExtent3D
19548         1,                                           // mipLevels
19549         1,                                           // arrayLayers
19550         VK_SAMPLE_COUNT_1_BIT,                       // samples
19551         VK_IMAGE_TILING_OPTIMAL,                     // VkImageTiling
19552         VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,         // VkImageUsageFlags
19553         VK_SHARING_MODE_EXCLUSIVE,                   // VkSharingMode
19554         0,                                           // queueFamilyCount
19555         nullptr,                                     // pQueueFamilyIndices
19556         VK_IMAGE_LAYOUT_UNDEFINED                    // initialLayout
19557     };
19558 
19559     // 1. Create VkImage.
19560     VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
19561         allocator->m_hDevice,
19562         &imageCreateInfo,
19563         nullptr,
19564         pImage);
19565     return res;
19566 }
19567 
vmaDestroyFakeImage(VmaAllocator VMA_NOT_NULL allocator,VkImage VMA_NULLABLE_NON_DISPATCHABLE image)19568 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyFakeImage(
19569     VmaAllocator VMA_NOT_NULL allocator,
19570     VkImage VMA_NULLABLE_NON_DISPATCHABLE image)
19571 {
19572     VMA_ASSERT(allocator);
19573     if(image == VK_NULL_HANDLE)
19574     {
19575         return;
19576     }
19577     VMA_DEBUG_LOG("vmaDestroyFakeImage");
19578     VMA_DEBUG_GLOBAL_MUTEX_LOCK
19579 
19580     if(image != VK_NULL_HANDLE)
19581     {
19582         (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
19583     }
19584 }
19585 
19586 #endif // #ifdef VMA_IMPLEMENTATION
19587