GCC Code Coverage Report


Directory: ./
File: src/tide/dependencies/vma/vk_mem_alloc.h
Date: 2023-04-27 00:55:30
Exec Total Coverage
Lines: 0 4785 0.0%
Functions: 0 729 0.0%
Branches: 0 4200 0.0%

Line Branch Exec Source
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 #include <vulkan/vulkan.h>
1949 #endif
1950
1951 // Define this macro to declare maximum supported Vulkan version in format AAABBBCCC,
1952 // where AAA = major, BBB = minor, CCC = patch.
1953 // If you want to use version > 1.0, it still needs to be enabled via VmaAllocatorCreateInfo::vulkanApiVersion.
1954 #if !defined(VMA_VULKAN_VERSION)
1955 #if defined(VK_VERSION_1_2)
1956 #define VMA_VULKAN_VERSION 1002000
1957 #elif defined(VK_VERSION_1_1)
1958 #define VMA_VULKAN_VERSION 1001000
1959 #else
1960 #define VMA_VULKAN_VERSION 1000000
1961 #endif
1962 #endif
1963
1964 #if !defined(VMA_DEDICATED_ALLOCATION)
1965 #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation
1966 #define VMA_DEDICATED_ALLOCATION 1
1967 #else
1968 #define VMA_DEDICATED_ALLOCATION 0
1969 #endif
1970 #endif
1971
1972 #if !defined(VMA_BIND_MEMORY2)
1973 #if VK_KHR_bind_memory2
1974 #define VMA_BIND_MEMORY2 1
1975 #else
1976 #define VMA_BIND_MEMORY2 0
1977 #endif
1978 #endif
1979
1980 #if !defined(VMA_MEMORY_BUDGET)
1981 #if VK_EXT_memory_budget && (VK_KHR_get_physical_device_properties2 || VMA_VULKAN_VERSION >= 1001000)
1982 #define VMA_MEMORY_BUDGET 1
1983 #else
1984 #define VMA_MEMORY_BUDGET 0
1985 #endif
1986 #endif
1987
1988 // Defined to 1 when VK_KHR_buffer_device_address device extension or equivalent core Vulkan 1.2 feature is defined in its headers.
1989 #if !defined(VMA_BUFFER_DEVICE_ADDRESS)
1990 #if VK_KHR_buffer_device_address || VMA_VULKAN_VERSION >= 1002000
1991 #define VMA_BUFFER_DEVICE_ADDRESS 1
1992 #else
1993 #define VMA_BUFFER_DEVICE_ADDRESS 0
1994 #endif
1995 #endif
1996
1997 // Define these macros to decorate all public functions with additional code,
1998 // before and after returned type, appropriately. This may be useful for
1999 // exporing the functions when compiling VMA as a separate library. Example:
2000 // #define VMA_CALL_PRE __declspec(dllexport)
2001 // #define VMA_CALL_POST __cdecl
2002 #ifndef VMA_CALL_PRE
2003 #define VMA_CALL_PRE
2004 #endif
2005 #ifndef VMA_CALL_POST
2006 #define VMA_CALL_POST
2007 #endif
2008
2009 // Define this macro to decorate pointers with an attribute specifying the
2010 // length of the array they point to if they are not null.
2011 //
2012 // The length may be one of
2013 // - The name of another parameter in the argument list where the pointer is declared
2014 // - The name of another member in the struct where the pointer is declared
2015 // - The name of a member of a struct type, meaning the value of that member in
2016 // the context of the call. For example
2017 // VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount"),
2018 // this means the number of memory heaps available in the device associated
2019 // with the VmaAllocator being dealt with.
2020 #ifndef VMA_LEN_IF_NOT_NULL
2021 #define VMA_LEN_IF_NOT_NULL(len)
2022 #endif
2023
2024 // The VMA_NULLABLE macro is defined to be _Nullable when compiling with Clang.
2025 // see: https://clang.llvm.org/docs/AttributeReference.html#nullable
2026 #ifndef VMA_NULLABLE
2027 #ifdef __clang__
2028 #define VMA_NULLABLE _Nullable
2029 #else
2030 #define VMA_NULLABLE
2031 #endif
2032 #endif
2033
2034 // The VMA_NOT_NULL macro is defined to be _Nonnull when compiling with Clang.
2035 // see: https://clang.llvm.org/docs/AttributeReference.html#nonnull
2036 #ifndef VMA_NOT_NULL
2037 #ifdef __clang__
2038 #define VMA_NOT_NULL _Nonnull
2039 #else
2040 #define VMA_NOT_NULL
2041 #endif
2042 #endif
2043
2044 // If non-dispatchable handles are represented as pointers then we can give
2045 // then nullability annotations
2046 #ifndef VMA_NOT_NULL_NON_DISPATCHABLE
2047 #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
2048 #define VMA_NOT_NULL_NON_DISPATCHABLE VMA_NOT_NULL
2049 #else
2050 #define VMA_NOT_NULL_NON_DISPATCHABLE
2051 #endif
2052 #endif
2053
2054 #ifndef VMA_NULLABLE_NON_DISPATCHABLE
2055 #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
2056 #define VMA_NULLABLE_NON_DISPATCHABLE VMA_NULLABLE
2057 #else
2058 #define VMA_NULLABLE_NON_DISPATCHABLE
2059 #endif
2060 #endif
2061
2062 /** \struct VmaAllocator
2063 \brief Represents main object of this library initialized.
2064
2065 Fill structure #VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it.
2066 Call function vmaDestroyAllocator() to destroy it.
2067
2068 It is recommended to create just one object of this type per `VkDevice` object,
2069 right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed.
2070 */
2071 VK_DEFINE_HANDLE(VmaAllocator)
2072
2073 /// Callback function called after successful vkAllocateMemory.
2074 typedef void (VKAPI_PTR* PFN_vmaAllocateDeviceMemoryFunction)(
2075 VmaAllocator VMA_NOT_NULL allocator,
2076 uint32_t memoryType,
2077 VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,
2078 VkDeviceSize size,
2079 void* VMA_NULLABLE pUserData);
2080 /// Callback function called before vkFreeMemory.
2081 typedef void (VKAPI_PTR* PFN_vmaFreeDeviceMemoryFunction)(
2082 VmaAllocator VMA_NOT_NULL allocator,
2083 uint32_t memoryType,
2084 VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,
2085 VkDeviceSize size,
2086 void* VMA_NULLABLE pUserData);
2087
2088 /** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`.
2089
2090 Provided for informative purpose, e.g. to gather statistics about number of
2091 allocations or total amount of memory allocated in Vulkan.
2092
2093 Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
2094 */
2095 typedef struct VmaDeviceMemoryCallbacks {
2096 /// Optional, can be null.
2097 PFN_vmaAllocateDeviceMemoryFunction VMA_NULLABLE pfnAllocate;
2098 /// Optional, can be null.
2099 PFN_vmaFreeDeviceMemoryFunction VMA_NULLABLE pfnFree;
2100 /// Optional, can be null.
2101 void* VMA_NULLABLE pUserData;
2102 } VmaDeviceMemoryCallbacks;
2103
2104 /// Flags for created #VmaAllocator.
2105 typedef enum VmaAllocatorCreateFlagBits {
2106 /** \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.
2107
2108 Using this flag may increase performance because internal mutexes are not used.
2109 */
2110 VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001,
2111 /** \brief Enables usage of VK_KHR_dedicated_allocation extension.
2112
2113 The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`.
2114 When it's `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1.
2115
2116 Using this extenion will automatically allocate dedicated blocks of memory for
2117 some buffers and images instead of suballocating place for them out of bigger
2118 memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
2119 flag) when it is recommended by the driver. It may improve performance on some
2120 GPUs.
2121
2122 You may set this flag only if you found out that following device extensions are
2123 supported, you enabled them while creating Vulkan device passed as
2124 VmaAllocatorCreateInfo::device, and you want them to be used internally by this
2125 library:
2126
2127 - VK_KHR_get_memory_requirements2 (device extension)
2128 - VK_KHR_dedicated_allocation (device extension)
2129
2130 When this flag is set, you can experience following warnings reported by Vulkan
2131 validation layer. You can ignore them.
2132
2133 > vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer.
2134 */
2135 VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002,
2136 /**
2137 Enables usage of VK_KHR_bind_memory2 extension.
2138
2139 The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`.
2140 When it's `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1.
2141
2142 You may set this flag only if you found out that this device extension is supported,
2143 you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,
2144 and you want it to be used internally by this library.
2145
2146 The extension provides functions `vkBindBufferMemory2KHR` and `vkBindImageMemory2KHR`,
2147 which allow to pass a chain of `pNext` structures while binding.
2148 This flag is required if you use `pNext` parameter in vmaBindBufferMemory2() or vmaBindImageMemory2().
2149 */
2150 VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT = 0x00000004,
2151 /**
2152 Enables usage of VK_EXT_memory_budget extension.
2153
2154 You may set this flag only if you found out that this device extension is supported,
2155 you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,
2156 and you want it to be used internally by this library, along with another instance extension
2157 VK_KHR_get_physical_device_properties2, which is required by it (or Vulkan 1.1, where this extension is promoted).
2158
2159 The extension provides query for current memory usage and budget, which will probably
2160 be more accurate than an estimation used by the library otherwise.
2161 */
2162 VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT = 0x00000008,
2163 /**
2164 Enables usage of VK_AMD_device_coherent_memory extension.
2165
2166 You may set this flag only if you:
2167
2168 - found out that this device extension is supported and enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,
2169 - checked that `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true and set it while creating the Vulkan device,
2170 - want it to be used internally by this library.
2171
2172 The extension and accompanying device feature provide access to memory types with
2173 `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flags.
2174 They are useful mostly for writing breadcrumb markers - a common method for debugging GPU crash/hang/TDR.
2175
2176 When the extension is not enabled, such memory types are still enumerated, but their usage is illegal.
2177 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,
2178 returning `VK_ERROR_FEATURE_NOT_PRESENT`.
2179 */
2180 VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT = 0x00000010,
2181 /**
2182 Enables usage of "buffer device address" feature, which allows you to use function
2183 `vkGetBufferDeviceAddress*` to get raw GPU pointer to a buffer and pass it for usage inside a shader.
2184
2185 You may set this flag only if you:
2186
2187 1. (For Vulkan version < 1.2) Found as available and enabled device extension
2188 VK_KHR_buffer_device_address.
2189 This extension is promoted to core Vulkan 1.2.
2190 2. Found as available and enabled device feature `VkPhysicalDeviceBufferDeviceAddressFeatures*::bufferDeviceAddress`.
2191
2192 When this flag is set, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*` using VMA.
2193 The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT*` to
2194 allocated memory blocks wherever it might be needed.
2195
2196 For more information, see documentation chapter \ref enabling_buffer_device_address.
2197 */
2198 VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT = 0x00000020,
2199
2200 VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2201 } VmaAllocatorCreateFlagBits;
2202 typedef VkFlags VmaAllocatorCreateFlags;
2203
2204 /** \brief Pointers to some Vulkan functions - a subset used by the library.
2205
2206 Used in VmaAllocatorCreateInfo::pVulkanFunctions.
2207 */
2208 typedef struct VmaVulkanFunctions {
2209 PFN_vkGetPhysicalDeviceProperties VMA_NULLABLE vkGetPhysicalDeviceProperties;
2210 PFN_vkGetPhysicalDeviceMemoryProperties VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties;
2211 PFN_vkAllocateMemory VMA_NULLABLE vkAllocateMemory;
2212 PFN_vkFreeMemory VMA_NULLABLE vkFreeMemory;
2213 PFN_vkMapMemory VMA_NULLABLE vkMapMemory;
2214 PFN_vkUnmapMemory VMA_NULLABLE vkUnmapMemory;
2215 PFN_vkFlushMappedMemoryRanges VMA_NULLABLE vkFlushMappedMemoryRanges;
2216 PFN_vkInvalidateMappedMemoryRanges VMA_NULLABLE vkInvalidateMappedMemoryRanges;
2217 PFN_vkBindBufferMemory VMA_NULLABLE vkBindBufferMemory;
2218 PFN_vkBindImageMemory VMA_NULLABLE vkBindImageMemory;
2219 PFN_vkGetBufferMemoryRequirements VMA_NULLABLE vkGetBufferMemoryRequirements;
2220 PFN_vkGetImageMemoryRequirements VMA_NULLABLE vkGetImageMemoryRequirements;
2221 PFN_vkCreateBuffer VMA_NULLABLE vkCreateBuffer;
2222 PFN_vkDestroyBuffer VMA_NULLABLE vkDestroyBuffer;
2223 PFN_vkCreateImage VMA_NULLABLE vkCreateImage;
2224 PFN_vkDestroyImage VMA_NULLABLE vkDestroyImage;
2225 PFN_vkCmdCopyBuffer VMA_NULLABLE vkCmdCopyBuffer;
2226 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
2227 PFN_vkGetBufferMemoryRequirements2KHR VMA_NULLABLE vkGetBufferMemoryRequirements2KHR;
2228 PFN_vkGetImageMemoryRequirements2KHR VMA_NULLABLE vkGetImageMemoryRequirements2KHR;
2229 #endif
2230 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
2231 PFN_vkBindBufferMemory2KHR VMA_NULLABLE vkBindBufferMemory2KHR;
2232 PFN_vkBindImageMemory2KHR VMA_NULLABLE vkBindImageMemory2KHR;
2233 #endif
2234 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
2235 PFN_vkGetPhysicalDeviceMemoryProperties2KHR VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties2KHR;
2236 #endif
2237 } VmaVulkanFunctions;
2238
2239 /// Flags to be used in VmaRecordSettings::flags.
2240 typedef enum VmaRecordFlagBits {
2241 /** \brief Enables flush after recording every function call.
2242
2243 Enable it if you expect your application to crash, which may leave recording file truncated.
2244 It may degrade performance though.
2245 */
2246 VMA_RECORD_FLUSH_AFTER_CALL_BIT = 0x00000001,
2247
2248 VMA_RECORD_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2249 } VmaRecordFlagBits;
2250 typedef VkFlags VmaRecordFlags;
2251
2252 /// Parameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSettings.
2253 typedef struct VmaRecordSettings
2254 {
2255 /// Flags for recording. Use #VmaRecordFlagBits enum.
2256 VmaRecordFlags flags;
2257 /** \brief Path to the file that should be written by the recording.
2258
2259 Suggested extension: "csv".
2260 If the file already exists, it will be overwritten.
2261 It will be opened for the whole time #VmaAllocator object is alive.
2262 If opening this file fails, creation of the whole allocator object fails.
2263 */
2264 const char* VMA_NOT_NULL pFilePath;
2265 } VmaRecordSettings;
2266
2267 /// Description of a Allocator to be created.
2268 typedef struct VmaAllocatorCreateInfo
2269 {
2270 /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum.
2271 VmaAllocatorCreateFlags flags;
2272 /// Vulkan physical device.
2273 /** It must be valid throughout whole lifetime of created allocator. */
2274 VkPhysicalDevice VMA_NOT_NULL physicalDevice;
2275 /// Vulkan device.
2276 /** It must be valid throughout whole lifetime of created allocator. */
2277 VkDevice VMA_NOT_NULL device;
2278 /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional.
2279 /** Set to 0 to use default, which is currently 256 MiB. */
2280 VkDeviceSize preferredLargeHeapBlockSize;
2281 /// Custom CPU memory allocation callbacks. Optional.
2282 /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */
2283 const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks;
2284 /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional.
2285 /** Optional, can be null. */
2286 const VmaDeviceMemoryCallbacks* VMA_NULLABLE pDeviceMemoryCallbacks;
2287 /** \brief Maximum number of additional frames that are in use at the same time as current frame.
2288
2289 This value is used only when you make allocations with
2290 VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
2291 lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
2292
2293 For example, if you double-buffer your command buffers, so resources used for
2294 rendering in previous frame may still be in use by the GPU at the moment you
2295 allocate resources needed for the current frame, set this value to 1.
2296
2297 If you want to allow any allocations other than used in the current frame to
2298 become lost, set this value to 0.
2299 */
2300 uint32_t frameInUseCount;
2301 /** \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.
2302
2303 If not NULL, it must be a pointer to an array of
2304 `VkPhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on
2305 maximum number of bytes that can be allocated out of particular Vulkan memory
2306 heap.
2307
2308 Any of the elements may be equal to `VK_WHOLE_SIZE`, which means no limit on that
2309 heap. This is also the default in case of `pHeapSizeLimit` = NULL.
2310
2311 If there is a limit defined for a heap:
2312
2313 - If user tries to allocate more memory from that heap using this allocator,
2314 the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
2315 - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the
2316 value of this limit will be reported instead when using vmaGetMemoryProperties().
2317
2318 Warning! Using this feature may not be equivalent to installing a GPU with
2319 smaller amount of memory, because graphics driver doesn't necessary fail new
2320 allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is
2321 exceeded. It may return success and just silently migrate some device memory
2322 blocks to system RAM. This driver behavior can also be controlled using
2323 VK_AMD_memory_overallocation_behavior extension.
2324 */
2325 const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pHeapSizeLimit;
2326
2327 /** \brief Pointers to Vulkan functions. Can be null.
2328
2329 For details see [Pointers to Vulkan functions](@ref config_Vulkan_functions).
2330 */
2331 const VmaVulkanFunctions* VMA_NULLABLE pVulkanFunctions;
2332 /** \brief Parameters for recording of VMA calls. Can be null.
2333
2334 If not null, it enables recording of calls to VMA functions to a file.
2335 If support for recording is not enabled using `VMA_RECORDING_ENABLED` macro,
2336 creation of the allocator object fails with `VK_ERROR_FEATURE_NOT_PRESENT`.
2337 */
2338 const VmaRecordSettings* VMA_NULLABLE pRecordSettings;
2339 /** \brief Handle to Vulkan instance object.
2340
2341 Starting from version 3.0.0 this member is no longer optional, it must be set!
2342 */
2343 VkInstance VMA_NOT_NULL instance;
2344 /** \brief Optional. The highest version of Vulkan that the application is designed to use.
2345
2346 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`.
2347 The patch version number specified is ignored. Only the major and minor versions are considered.
2348 It must be less or equal (preferably equal) to value as passed to `vkCreateInstance` as `VkApplicationInfo::apiVersion`.
2349 Only versions 1.0 and 1.1 are supported by the current implementation.
2350 Leaving it initialized to zero is equivalent to `VK_API_VERSION_1_0`.
2351 */
2352 uint32_t vulkanApiVersion;
2353 } VmaAllocatorCreateInfo;
2354
2355 /// Creates Allocator object.
2356 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
2357 const VmaAllocatorCreateInfo* VMA_NOT_NULL pCreateInfo,
2358 VmaAllocator VMA_NULLABLE* VMA_NOT_NULL pAllocator);
2359
2360 /// Destroys allocator object.
2361 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
2362 VmaAllocator VMA_NULLABLE allocator);
2363
2364 /** \brief Information about existing #VmaAllocator object.
2365 */
2366 typedef struct VmaAllocatorInfo
2367 {
2368 /** \brief Handle to Vulkan instance object.
2369
2370 This is the same value as has been passed through VmaAllocatorCreateInfo::instance.
2371 */
2372 VkInstance VMA_NOT_NULL instance;
2373 /** \brief Handle to Vulkan physical device object.
2374
2375 This is the same value as has been passed through VmaAllocatorCreateInfo::physicalDevice.
2376 */
2377 VkPhysicalDevice VMA_NOT_NULL physicalDevice;
2378 /** \brief Handle to Vulkan device object.
2379
2380 This is the same value as has been passed through VmaAllocatorCreateInfo::device.
2381 */
2382 VkDevice VMA_NOT_NULL device;
2383 } VmaAllocatorInfo;
2384
2385 /** \brief Returns information about existing #VmaAllocator object - handle to Vulkan device etc.
2386
2387 It might be useful if you want to keep just the #VmaAllocator handle and fetch other required handles to
2388 `VkPhysicalDevice`, `VkDevice` etc. every time using this function.
2389 */
2390 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator VMA_NOT_NULL allocator, VmaAllocatorInfo* VMA_NOT_NULL pAllocatorInfo);
2391
2392 /**
2393 PhysicalDeviceProperties are fetched from physicalDevice by the allocator.
2394 You can access it here, without fetching it again on your own.
2395 */
2396 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
2397 VmaAllocator VMA_NOT_NULL allocator,
2398 const VkPhysicalDeviceProperties* VMA_NULLABLE* VMA_NOT_NULL ppPhysicalDeviceProperties);
2399
2400 /**
2401 PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator.
2402 You can access it here, without fetching it again on your own.
2403 */
2404 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
2405 VmaAllocator VMA_NOT_NULL allocator,
2406 const VkPhysicalDeviceMemoryProperties* VMA_NULLABLE* VMA_NOT_NULL ppPhysicalDeviceMemoryProperties);
2407
2408 /**
2409 \brief Given Memory Type Index, returns Property Flags of this memory type.
2410
2411 This is just a convenience function. Same information can be obtained using
2412 vmaGetMemoryProperties().
2413 */
2414 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
2415 VmaAllocator VMA_NOT_NULL allocator,
2416 uint32_t memoryTypeIndex,
2417 VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);
2418
2419 /** \brief Sets index of the current frame.
2420
2421 This function must be used if you make allocations with
2422 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT and
2423 #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flags to inform the allocator
2424 when a new frame begins. Allocations queried using vmaGetAllocationInfo() cannot
2425 become lost in the current frame.
2426 */
2427 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
2428 VmaAllocator VMA_NOT_NULL allocator,
2429 uint32_t frameIndex);
2430
2431 /** \brief Calculated statistics of memory usage in entire allocator.
2432 */
2433 typedef struct VmaStatInfo
2434 {
2435 /// Number of `VkDeviceMemory` Vulkan memory blocks allocated.
2436 uint32_t blockCount;
2437 /// Number of #VmaAllocation allocation objects allocated.
2438 uint32_t allocationCount;
2439 /// Number of free ranges of memory between allocations.
2440 uint32_t unusedRangeCount;
2441 /// Total number of bytes occupied by all allocations.
2442 VkDeviceSize usedBytes;
2443 /// Total number of bytes occupied by unused ranges.
2444 VkDeviceSize unusedBytes;
2445 VkDeviceSize allocationSizeMin, allocationSizeAvg, allocationSizeMax;
2446 VkDeviceSize unusedRangeSizeMin, unusedRangeSizeAvg, unusedRangeSizeMax;
2447 } VmaStatInfo;
2448
2449 /// General statistics from current state of Allocator.
2450 typedef struct VmaStats
2451 {
2452 VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
2453 VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
2454 VmaStatInfo total;
2455 } VmaStats;
2456
2457 /** \brief Retrieves statistics from current state of the Allocator.
2458
2459 This function is called "calculate" not "get" because it has to traverse all
2460 internal data structures, so it may be quite slow. For faster but more brief statistics
2461 suitable to be called every frame or every allocation, use vmaGetBudget().
2462
2463 Note that when using allocator from multiple threads, returned information may immediately
2464 become outdated.
2465 */
2466 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
2467 VmaAllocator VMA_NOT_NULL allocator,
2468 VmaStats* VMA_NOT_NULL pStats);
2469
2470 /** \brief Statistics of current memory usage and available budget, in bytes, for specific memory heap.
2471 */
2472 typedef struct VmaBudget
2473 {
2474 /** \brief Sum size of all `VkDeviceMemory` blocks allocated from particular heap, in bytes.
2475 */
2476 VkDeviceSize blockBytes;
2477
2478 /** \brief Sum size of all allocations created in particular heap, in bytes.
2479
2480 Usually less or equal than `blockBytes`.
2481 Difference `blockBytes - allocationBytes` is the amount of memory allocated but unused -
2482 available for new allocations or wasted due to fragmentation.
2483
2484 It might be greater than `blockBytes` if there are some allocations in lost state, as they account
2485 to this value as well.
2486 */
2487 VkDeviceSize allocationBytes;
2488
2489 /** \brief Estimated current memory usage of the program, in bytes.
2490
2491 Fetched from system using `VK_EXT_memory_budget` extension if enabled.
2492
2493 It might be different than `blockBytes` (usually higher) due to additional implicit objects
2494 also occupying the memory, like swapchain, pipelines, descriptor heaps, command buffers, or
2495 `VkDeviceMemory` blocks allocated outside of this library, if any.
2496 */
2497 VkDeviceSize usage;
2498
2499 /** \brief Estimated amount of memory available to the program, in bytes.
2500
2501 Fetched from system using `VK_EXT_memory_budget` extension if enabled.
2502
2503 It might be different (most probably smaller) than `VkMemoryHeap::size[heapIndex]` due to factors
2504 external to the program, like other programs also consuming system resources.
2505 Difference `budget - usage` is the amount of additional memory that can probably
2506 be allocated without problems. Exceeding the budget may result in various problems.
2507 */
2508 VkDeviceSize budget;
2509 } VmaBudget;
2510
2511 /** \brief Retrieves information about current memory budget for all memory heaps.
2512
2513 \param[out] pBudget Must point to array with number of elements at least equal to number of memory heaps in physical device used.
2514
2515 This function is called "get" not "calculate" because it is very fast, suitable to be called
2516 every frame or every allocation. For more detailed statistics use vmaCalculateStats().
2517
2518 Note that when using allocator from multiple threads, returned information may immediately
2519 become outdated.
2520 */
2521 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
2522 VmaAllocator VMA_NOT_NULL allocator,
2523 VmaBudget* VMA_NOT_NULL pBudget);
2524
2525 #ifndef VMA_STATS_STRING_ENABLED
2526 #define VMA_STATS_STRING_ENABLED 1
2527 #endif
2528
2529 #if VMA_STATS_STRING_ENABLED
2530
2531 /// Builds and returns statistics as string in JSON format.
2532 /** @param[out] ppStatsString Must be freed using vmaFreeStatsString() function.
2533 */
2534 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
2535 VmaAllocator VMA_NOT_NULL allocator,
2536 char* VMA_NULLABLE* VMA_NOT_NULL ppStatsString,
2537 VkBool32 detailedMap);
2538
2539 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
2540 VmaAllocator VMA_NOT_NULL allocator,
2541 char* VMA_NULLABLE pStatsString);
2542
2543 #endif // #if VMA_STATS_STRING_ENABLED
2544
2545 /** \struct VmaPool
2546 \brief Represents custom memory pool
2547
2548 Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it.
2549 Call function vmaDestroyPool() to destroy it.
2550
2551 For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools).
2552 */
2553 VK_DEFINE_HANDLE(VmaPool)
2554
2555 typedef enum VmaMemoryUsage
2556 {
2557 /** No intended memory usage specified.
2558 Use other members of VmaAllocationCreateInfo to specify your requirements.
2559 */
2560 VMA_MEMORY_USAGE_UNKNOWN = 0,
2561 /** Memory will be used on device only, so fast access from the device is preferred.
2562 It usually means device-local GPU (video) memory.
2563 No need to be mappable on host.
2564 It is roughly equivalent of `D3D12_HEAP_TYPE_DEFAULT`.
2565
2566 Usage:
2567
2568 - Resources written and read by device, e.g. images used as attachments.
2569 - Resources transferred from host once (immutable) or infrequently and read by
2570 device multiple times, e.g. textures to be sampled, vertex buffers, uniform
2571 (constant) buffers, and majority of other types of resources used on GPU.
2572
2573 Allocation may still end up in `HOST_VISIBLE` memory on some implementations.
2574 In such case, you are free to map it.
2575 You can use #VMA_ALLOCATION_CREATE_MAPPED_BIT with this usage type.
2576 */
2577 VMA_MEMORY_USAGE_GPU_ONLY = 1,
2578 /** Memory will be mappable on host.
2579 It usually means CPU (system) memory.
2580 Guarantees to be `HOST_VISIBLE` and `HOST_COHERENT`.
2581 CPU access is typically uncached. Writes may be write-combined.
2582 Resources created in this pool may still be accessible to the device, but access to them can be slow.
2583 It is roughly equivalent of `D3D12_HEAP_TYPE_UPLOAD`.
2584
2585 Usage: Staging copy of resources used as transfer source.
2586 */
2587 VMA_MEMORY_USAGE_CPU_ONLY = 2,
2588 /**
2589 Memory that is both mappable on host (guarantees to be `HOST_VISIBLE`) and preferably fast to access by GPU.
2590 CPU access is typically uncached. Writes may be write-combined.
2591
2592 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.
2593 */
2594 VMA_MEMORY_USAGE_CPU_TO_GPU = 3,
2595 /** Memory mappable on host (guarantees to be `HOST_VISIBLE`) and cached.
2596 It is roughly equivalent of `D3D12_HEAP_TYPE_READBACK`.
2597
2598 Usage:
2599
2600 - Resources written by device, read by host - results of some computations, e.g. screen capture, average scene luminance for HDR tone mapping.
2601 - 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.
2602 */
2603 VMA_MEMORY_USAGE_GPU_TO_CPU = 4,
2604 /** CPU memory - memory that is preferably not `DEVICE_LOCAL`, but also not guaranteed to be `HOST_VISIBLE`.
2605
2606 Usage: Staging copy of resources moved from GPU memory to CPU memory as part
2607 of custom paging/residency mechanism, to be moved back to GPU memory when needed.
2608 */
2609 VMA_MEMORY_USAGE_CPU_COPY = 5,
2610 /** Lazily allocated GPU memory having `VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT`.
2611 Exists mostly on mobile platforms. Using it on desktop PC or other GPUs with no such memory type present will fail the allocation.
2612
2613 Usage: Memory for transient attachment images (color attachments, depth attachments etc.), created with `VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT`.
2614
2615 Allocations with this usage are always created as dedicated - it implies #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
2616 */
2617 VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED = 6,
2618
2619 VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
2620 } VmaMemoryUsage;
2621
2622 /// Flags to be passed as VmaAllocationCreateInfo::flags.
2623 typedef enum VmaAllocationCreateFlagBits {
2624 /** \brief Set this flag if the allocation should have its own memory block.
2625
2626 Use it for special, big resources, like fullscreen images used as attachments.
2627
2628 You should not use this flag if VmaAllocationCreateInfo::pool is not null.
2629 */
2630 VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001,
2631
2632 /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block.
2633
2634 If new allocation cannot be placed in any of the existing blocks, allocation
2635 fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
2636
2637 You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and
2638 #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense.
2639
2640 If VmaAllocationCreateInfo::pool is not null, this flag is implied and ignored. */
2641 VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002,
2642 /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it.
2643
2644 Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData.
2645
2646 Is it valid to use this flag for allocation made from memory type that is not
2647 `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is
2648 useful if you need an allocation that is efficient to use on GPU
2649 (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that
2650 support it (e.g. Intel GPU).
2651
2652 You should not use this flag together with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT.
2653 */
2654 VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004,
2655 /** Allocation created with this flag can become lost as a result of another
2656 allocation with #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag, so you
2657 must check it before use.
2658
2659 To check if allocation is not lost, call vmaGetAllocationInfo() and check if
2660 VmaAllocationInfo::deviceMemory is not `VK_NULL_HANDLE`.
2661
2662 For details about supporting lost allocations, see Lost Allocations
2663 chapter of User Guide on Main Page.
2664
2665 You should not use this flag together with #VMA_ALLOCATION_CREATE_MAPPED_BIT.
2666 */
2667 VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT = 0x00000008,
2668 /** While creating allocation using this flag, other allocations that were
2669 created with flag #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT can become lost.
2670
2671 For details about supporting lost allocations, see Lost Allocations
2672 chapter of User Guide on Main Page.
2673 */
2674 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT = 0x00000010,
2675 /** Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a
2676 null-terminated string. Instead of copying pointer value, a local copy of the
2677 string is made and stored in allocation's `pUserData`. The string is automatically
2678 freed together with the allocation. It is also used in vmaBuildStatsString().
2679 */
2680 VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020,
2681 /** Allocation will be created from upper stack in a double stack pool.
2682
2683 This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag.
2684 */
2685 VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040,
2686 /** Create both buffer/image and allocation, but don't bind them together.
2687 It is useful when you want to bind yourself to do some more advanced binding, e.g. using some extensions.
2688 The flag is meaningful only with functions that bind by default: vmaCreateBuffer(), vmaCreateImage().
2689 Otherwise it is ignored.
2690 */
2691 VMA_ALLOCATION_CREATE_DONT_BIND_BIT = 0x00000080,
2692 /** Create allocation only if additional device memory required for it, if any, won't exceed
2693 memory budget. Otherwise return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
2694 */
2695 VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT = 0x00000100,
2696
2697 /** Allocation strategy that chooses smallest possible free range for the
2698 allocation.
2699 */
2700 VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT = 0x00010000,
2701 /** Allocation strategy that chooses biggest possible free range for the
2702 allocation.
2703 */
2704 VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT = 0x00020000,
2705 /** Allocation strategy that chooses first suitable free range for the
2706 allocation.
2707
2708 "First" doesn't necessarily means the one with smallest offset in memory,
2709 but rather the one that is easiest and fastest to find.
2710 */
2711 VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = 0x00040000,
2712
2713 /** Allocation strategy that tries to minimize memory usage.
2714 */
2715 VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT,
2716 /** Allocation strategy that tries to minimize allocation time.
2717 */
2718 VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
2719 /** Allocation strategy that tries to minimize memory fragmentation.
2720 */
2721 VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT,
2722
2723 /** A bit mask to extract only `STRATEGY` bits from entire set of flags.
2724 */
2725 VMA_ALLOCATION_CREATE_STRATEGY_MASK =
2726 VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT |
2727 VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT |
2728 VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
2729
2730 VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2731 } VmaAllocationCreateFlagBits;
2732 typedef VkFlags VmaAllocationCreateFlags;
2733
2734 typedef struct VmaAllocationCreateInfo
2735 {
2736 /// Use #VmaAllocationCreateFlagBits enum.
2737 VmaAllocationCreateFlags flags;
2738 /** \brief Intended usage of memory.
2739
2740 You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n
2741 If `pool` is not null, this member is ignored.
2742 */
2743 VmaMemoryUsage usage;
2744 /** \brief Flags that must be set in a Memory Type chosen for an allocation.
2745
2746 Leave 0 if you specify memory requirements in other way. \n
2747 If `pool` is not null, this member is ignored.*/
2748 VkMemoryPropertyFlags requiredFlags;
2749 /** \brief Flags that preferably should be set in a memory type chosen for an allocation.
2750
2751 Set to 0 if no additional flags are prefered. \n
2752 If `pool` is not null, this member is ignored. */
2753 VkMemoryPropertyFlags preferredFlags;
2754 /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation.
2755
2756 Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if
2757 it meets other requirements specified by this structure, with no further
2758 restrictions on memory type index. \n
2759 If `pool` is not null, this member is ignored.
2760 */
2761 uint32_t memoryTypeBits;
2762 /** \brief Pool that this allocation should be created in.
2763
2764 Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members:
2765 `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.
2766 */
2767 VmaPool VMA_NULLABLE pool;
2768 /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().
2769
2770 If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either
2771 null or pointer to a null-terminated string. The string will be then copied to
2772 internal buffer, so it doesn't need to be valid after allocation call.
2773 */
2774 void* VMA_NULLABLE pUserData;
2775 } VmaAllocationCreateInfo;
2776
2777 /**
2778 \brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.
2779
2780 This algorithm tries to find a memory type that:
2781
2782 - Is allowed by memoryTypeBits.
2783 - Contains all the flags from pAllocationCreateInfo->requiredFlags.
2784 - Matches intended usage.
2785 - Has as many flags from pAllocationCreateInfo->preferredFlags as possible.
2786
2787 \return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result
2788 from this function or any other allocating function probably means that your
2789 device doesn't support any memory type with requested features for the specific
2790 type of resource you want to use it for. Please check parameters of your
2791 resource, like image layout (OPTIMAL versus LINEAR) or mip level count.
2792 */
2793 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
2794 VmaAllocator VMA_NOT_NULL allocator,
2795 uint32_t memoryTypeBits,
2796 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2797 uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2798
2799 /**
2800 \brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.
2801
2802 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
2803 It internally creates a temporary, dummy buffer that never has memory bound.
2804 It is just a convenience function, equivalent to calling:
2805
2806 - `vkCreateBuffer`
2807 - `vkGetBufferMemoryRequirements`
2808 - `vmaFindMemoryTypeIndex`
2809 - `vkDestroyBuffer`
2810 */
2811 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
2812 VmaAllocator VMA_NOT_NULL allocator,
2813 const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
2814 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2815 uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2816
2817 /**
2818 \brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.
2819
2820 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
2821 It internally creates a temporary, dummy image that never has memory bound.
2822 It is just a convenience function, equivalent to calling:
2823
2824 - `vkCreateImage`
2825 - `vkGetImageMemoryRequirements`
2826 - `vmaFindMemoryTypeIndex`
2827 - `vkDestroyImage`
2828 */
2829 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
2830 VmaAllocator VMA_NOT_NULL allocator,
2831 const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
2832 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2833 uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2834
2835 /// Flags to be passed as VmaPoolCreateInfo::flags.
2836 typedef enum VmaPoolCreateFlagBits {
2837 /** \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.
2838
2839 This is an optional optimization flag.
2840
2841 If you always allocate using vmaCreateBuffer(), vmaCreateImage(),
2842 vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator
2843 knows exact type of your allocations so it can handle Buffer-Image Granularity
2844 in the optimal way.
2845
2846 If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(),
2847 exact type of such allocations is not known, so allocator must be conservative
2848 in handling Buffer-Image Granularity, which can lead to suboptimal allocation
2849 (wasted memory). In that case, if you can make sure you always allocate only
2850 buffers and linear images or only optimal images out of this pool, use this flag
2851 to make allocator disregard Buffer-Image Granularity and so make allocations
2852 faster and more optimal.
2853 */
2854 VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002,
2855
2856 /** \brief Enables alternative, linear allocation algorithm in this pool.
2857
2858 Specify this flag to enable linear allocation algorithm, which always creates
2859 new allocations after last one and doesn't reuse space from allocations freed in
2860 between. It trades memory consumption for simplified algorithm and data
2861 structure, which has better performance and uses less memory for metadata.
2862
2863 By using this flag, you can achieve behavior of free-at-once, stack,
2864 ring buffer, and double stack. For details, see documentation chapter
2865 \ref linear_algorithm.
2866
2867 When using this flag, you must specify VmaPoolCreateInfo::maxBlockCount == 1 (or 0 for default).
2868
2869 For more details, see [Linear allocation algorithm](@ref linear_algorithm).
2870 */
2871 VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004,
2872
2873 /** \brief Enables alternative, buddy allocation algorithm in this pool.
2874
2875 It operates on a tree of blocks, each having size that is a power of two and
2876 a half of its parent's size. Comparing to default algorithm, this one provides
2877 faster allocation and deallocation and decreased external fragmentation,
2878 at the expense of more memory wasted (internal fragmentation).
2879
2880 For more details, see [Buddy allocation algorithm](@ref buddy_algorithm).
2881 */
2882 VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT = 0x00000008,
2883
2884 /** Bit mask to extract only `ALGORITHM` bits from entire set of flags.
2885 */
2886 VMA_POOL_CREATE_ALGORITHM_MASK =
2887 VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT |
2888 VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT,
2889
2890 VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2891 } VmaPoolCreateFlagBits;
2892 typedef VkFlags VmaPoolCreateFlags;
2893
2894 /** \brief Describes parameter of created #VmaPool.
2895 */
2896 typedef struct VmaPoolCreateInfo {
2897 /** \brief Vulkan memory type index to allocate this pool from.
2898 */
2899 uint32_t memoryTypeIndex;
2900 /** \brief Use combination of #VmaPoolCreateFlagBits.
2901 */
2902 VmaPoolCreateFlags flags;
2903 /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. Optional.
2904
2905 Specify nonzero to set explicit, constant size of memory blocks used by this
2906 pool.
2907
2908 Leave 0 to use default and let the library manage block sizes automatically.
2909 Sizes of particular blocks may vary.
2910 */
2911 VkDeviceSize blockSize;
2912 /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty.
2913
2914 Set to 0 to have no preallocated blocks and allow the pool be completely empty.
2915 */
2916 size_t minBlockCount;
2917 /** \brief Maximum number of blocks that can be allocated in this pool. Optional.
2918
2919 Set to 0 to use default, which is `SIZE_MAX`, which means no limit.
2920
2921 Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated
2922 throughout whole lifetime of this pool.
2923 */
2924 size_t maxBlockCount;
2925 /** \brief Maximum number of additional frames that are in use at the same time as current frame.
2926
2927 This value is used only when you make allocations with
2928 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
2929 lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
2930
2931 For example, if you double-buffer your command buffers, so resources used for
2932 rendering in previous frame may still be in use by the GPU at the moment you
2933 allocate resources needed for the current frame, set this value to 1.
2934
2935 If you want to allow any allocations other than used in the current frame to
2936 become lost, set this value to 0.
2937 */
2938 uint32_t frameInUseCount;
2939 } VmaPoolCreateInfo;
2940
2941 /** \brief Describes parameter of existing #VmaPool.
2942 */
2943 typedef struct VmaPoolStats {
2944 /** \brief Total amount of `VkDeviceMemory` allocated from Vulkan for this pool, in bytes.
2945 */
2946 VkDeviceSize size;
2947 /** \brief Total number of bytes in the pool not used by any #VmaAllocation.
2948 */
2949 VkDeviceSize unusedSize;
2950 /** \brief Number of #VmaAllocation objects created from this pool that were not destroyed or lost.
2951 */
2952 size_t allocationCount;
2953 /** \brief Number of continuous memory ranges in the pool not used by any #VmaAllocation.
2954 */
2955 size_t unusedRangeCount;
2956 /** \brief Size of the largest continuous free memory region available for new allocation.
2957
2958 Making a new allocation of that size is not guaranteed to succeed because of
2959 possible additional margin required to respect alignment and buffer/image
2960 granularity.
2961 */
2962 VkDeviceSize unusedRangeSizeMax;
2963 /** \brief Number of `VkDeviceMemory` blocks allocated for this pool.
2964 */
2965 size_t blockCount;
2966 } VmaPoolStats;
2967
2968 /** \brief Allocates Vulkan device memory and creates #VmaPool object.
2969
2970 @param allocator Allocator object.
2971 @param pCreateInfo Parameters of pool to create.
2972 @param[out] pPool Handle to created pool.
2973 */
2974 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
2975 VmaAllocator VMA_NOT_NULL allocator,
2976 const VmaPoolCreateInfo* VMA_NOT_NULL pCreateInfo,
2977 VmaPool VMA_NULLABLE* VMA_NOT_NULL pPool);
2978
2979 /** \brief Destroys #VmaPool object and frees Vulkan device memory.
2980 */
2981 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
2982 VmaAllocator VMA_NOT_NULL allocator,
2983 VmaPool VMA_NULLABLE pool);
2984
2985 /** \brief Retrieves statistics of existing #VmaPool object.
2986
2987 @param allocator Allocator object.
2988 @param pool Pool object.
2989 @param[out] pPoolStats Statistics of specified pool.
2990 */
2991 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
2992 VmaAllocator VMA_NOT_NULL allocator,
2993 VmaPool VMA_NOT_NULL pool,
2994 VmaPoolStats* VMA_NOT_NULL pPoolStats);
2995
2996 /** \brief Marks all allocations in given pool as lost if they are not used in current frame or VmaPoolCreateInfo::frameInUseCount back from now.
2997
2998 @param allocator Allocator object.
2999 @param pool Pool.
3000 @param[out] pLostAllocationCount Number of allocations marked as lost. Optional - pass null if you don't need this information.
3001 */
3002 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
3003 VmaAllocator VMA_NOT_NULL allocator,
3004 VmaPool VMA_NOT_NULL pool,
3005 size_t* VMA_NULLABLE pLostAllocationCount);
3006
3007 /** \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions.
3008
3009 Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
3010 `VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is
3011 `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
3012
3013 Possible return values:
3014
3015 - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool.
3016 - `VK_SUCCESS` - corruption detection has been performed and succeeded.
3017 - `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
3018 `VMA_ASSERT` is also fired in that case.
3019 - Other value: Error returned by Vulkan, e.g. memory mapping failure.
3020 */
3021 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator VMA_NOT_NULL allocator, VmaPool VMA_NOT_NULL pool);
3022
3023 /** \brief Retrieves name of a custom pool.
3024
3025 After the call `ppName` is either null or points to an internally-owned null-terminated string
3026 containing name of the pool that was previously set. The pointer becomes invalid when the pool is
3027 destroyed or its name is changed using vmaSetPoolName().
3028 */
3029 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
3030 VmaAllocator VMA_NOT_NULL allocator,
3031 VmaPool VMA_NOT_NULL pool,
3032 const char* VMA_NULLABLE* VMA_NOT_NULL ppName);
3033
3034 /** \brief Sets name of a custom pool.
3035
3036 `pName` can be either null or pointer to a null-terminated string with new name for the pool.
3037 Function makes internal copy of the string, so it can be changed or freed immediately after this call.
3038 */
3039 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
3040 VmaAllocator VMA_NOT_NULL allocator,
3041 VmaPool VMA_NOT_NULL pool,
3042 const char* VMA_NULLABLE pName);
3043
3044 /** \struct VmaAllocation
3045 \brief Represents single memory allocation.
3046
3047 It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type
3048 plus unique offset.
3049
3050 There are multiple ways to create such object.
3051 You need to fill structure VmaAllocationCreateInfo.
3052 For more information see [Choosing memory type](@ref choosing_memory_type).
3053
3054 Although the library provides convenience functions that create Vulkan buffer or image,
3055 allocate memory for it and bind them together,
3056 binding of the allocation to a buffer or an image is out of scope of the allocation itself.
3057 Allocation object can exist without buffer/image bound,
3058 binding can be done manually by the user, and destruction of it can be done
3059 independently of destruction of the allocation.
3060
3061 The object also remembers its size and some other information.
3062 To retrieve this information, use function vmaGetAllocationInfo() and inspect
3063 returned structure VmaAllocationInfo.
3064
3065 Some kinds allocations can be in lost state.
3066 For more information, see [Lost allocations](@ref lost_allocations).
3067 */
3068 VK_DEFINE_HANDLE(VmaAllocation)
3069
3070 /** \brief Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
3071 */
3072 typedef struct VmaAllocationInfo {
3073 /** \brief Memory type index that this allocation was allocated from.
3074
3075 It never changes.
3076 */
3077 uint32_t memoryType;
3078 /** \brief Handle to Vulkan memory object.
3079
3080 Same memory object can be shared by multiple allocations.
3081
3082 It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
3083
3084 If the allocation is lost, it is equal to `VK_NULL_HANDLE`.
3085 */
3086 VkDeviceMemory VMA_NULLABLE_NON_DISPATCHABLE deviceMemory;
3087 /** \brief Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation.
3088
3089 It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
3090 */
3091 VkDeviceSize offset;
3092 /** \brief Size of this allocation, in bytes.
3093
3094 It never changes, unless allocation is lost.
3095 */
3096 VkDeviceSize size;
3097 /** \brief Pointer to the beginning of this allocation as mapped data.
3098
3099 If the allocation hasn't been mapped using vmaMapMemory() and hasn't been
3100 created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value is null.
3101
3102 It can change after call to vmaMapMemory(), vmaUnmapMemory().
3103 It can also change after call to vmaDefragment() if this allocation is passed to the function.
3104 */
3105 void* VMA_NULLABLE pMappedData;
3106 /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData().
3107
3108 It can change after call to vmaSetAllocationUserData() for this allocation.
3109 */
3110 void* VMA_NULLABLE pUserData;
3111 } VmaAllocationInfo;
3112
3113 /** \brief General purpose memory allocation.
3114
3115 @param[out] pAllocation Handle to allocated memory.
3116 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
3117
3118 You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
3119
3120 It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(),
3121 vmaCreateBuffer(), vmaCreateImage() instead whenever possible.
3122 */
3123 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
3124 VmaAllocator VMA_NOT_NULL allocator,
3125 const VkMemoryRequirements* VMA_NOT_NULL pVkMemoryRequirements,
3126 const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3127 VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,
3128 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3129
3130 /** \brief General purpose memory allocation for multiple allocation objects at once.
3131
3132 @param allocator Allocator object.
3133 @param pVkMemoryRequirements Memory requirements for each allocation.
3134 @param pCreateInfo Creation parameters for each alloction.
3135 @param allocationCount Number of allocations to make.
3136 @param[out] pAllocations Pointer to array that will be filled with handles to created allocations.
3137 @param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations.
3138
3139 You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
3140
3141 Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding.
3142 It is just a general purpose allocation function able to make multiple allocations at once.
3143 It may be internally optimized to be more efficient than calling vmaAllocateMemory() `allocationCount` times.
3144
3145 All allocations are made using same parameters. All of them are created out of the same memory pool and type.
3146 If any allocation fails, all allocations already made within this function call are also freed, so that when
3147 returned result is not `VK_SUCCESS`, `pAllocation` array is always entirely filled with `VK_NULL_HANDLE`.
3148 */
3149 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
3150 VmaAllocator VMA_NOT_NULL allocator,
3151 const VkMemoryRequirements* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pVkMemoryRequirements,
3152 const VmaAllocationCreateInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pCreateInfo,
3153 size_t allocationCount,
3154 VmaAllocation VMA_NULLABLE* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
3155 VmaAllocationInfo* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationInfo);
3156
3157 /**
3158 @param[out] pAllocation Handle to allocated memory.
3159 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
3160
3161 You should free the memory using vmaFreeMemory().
3162 */
3163 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
3164 VmaAllocator VMA_NOT_NULL allocator,
3165 VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
3166 const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3167 VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,
3168 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3169
3170 /// Function similar to vmaAllocateMemoryForBuffer().
3171 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
3172 VmaAllocator VMA_NOT_NULL allocator,
3173 VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
3174 const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3175 VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,
3176 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3177
3178 /** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().
3179
3180 Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped.
3181 */
3182 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
3183 VmaAllocator VMA_NOT_NULL allocator,
3184 const VmaAllocation VMA_NULLABLE allocation);
3185
3186 /** \brief Frees memory and destroys multiple allocations.
3187
3188 Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding.
3189 It is just a general purpose function to free memory and destroy allocations made using e.g. vmaAllocateMemory(),
3190 vmaAllocateMemoryPages() and other functions.
3191 It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times.
3192
3193 Allocations in `pAllocations` array can come from any memory pools and types.
3194 Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped.
3195 */
3196 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
3197 VmaAllocator VMA_NOT_NULL allocator,
3198 size_t allocationCount,
3199 const VmaAllocation VMA_NULLABLE* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations);
3200
3201 /** \brief Deprecated.
3202
3203 \deprecated
3204 In version 2.2.0 it used to try to change allocation's size without moving or reallocating it.
3205 In current version it returns `VK_SUCCESS` only if `newSize` equals current allocation's size.
3206 Otherwise returns `VK_ERROR_OUT_OF_POOL_MEMORY`, indicating that allocation's size could not be changed.
3207 */
3208 VMA_CALL_PRE VkResult VMA_CALL_POST vmaResizeAllocation(
3209 VmaAllocator VMA_NOT_NULL allocator,
3210 VmaAllocation VMA_NOT_NULL allocation,
3211 VkDeviceSize newSize);
3212
3213 /** \brief Returns current information about specified allocation and atomically marks it as used in current frame.
3214
3215 Current paramters of given allocation are returned in `pAllocationInfo`.
3216
3217 This function also atomically "touches" allocation - marks it as used in current frame,
3218 just like vmaTouchAllocation().
3219 If the allocation is in lost state, `pAllocationInfo->deviceMemory == VK_NULL_HANDLE`.
3220
3221 Although this function uses atomics and doesn't lock any mutex, so it should be quite efficient,
3222 you can avoid calling it too often.
3223
3224 - You can retrieve same VmaAllocationInfo structure while creating your resource, from function
3225 vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change
3226 (e.g. due to defragmentation or allocation becoming lost).
3227 - If you just want to check if allocation is not lost, vmaTouchAllocation() will work faster.
3228 */
3229 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
3230 VmaAllocator VMA_NOT_NULL allocator,
3231 VmaAllocation VMA_NOT_NULL allocation,
3232 VmaAllocationInfo* VMA_NOT_NULL pAllocationInfo);
3233
3234 /** \brief Returns `VK_TRUE` if allocation is not lost and atomically marks it as used in current frame.
3235
3236 If the allocation has been created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
3237 this function returns `VK_TRUE` if it's not in lost state, so it can still be used.
3238 It then also atomically "touches" the allocation - marks it as used in current frame,
3239 so that you can be sure it won't become lost in current frame or next `frameInUseCount` frames.
3240
3241 If the allocation is in lost state, the function returns `VK_FALSE`.
3242 Memory of such allocation, as well as buffer or image bound to it, should not be used.
3243 Lost allocation and the buffer/image still need to be destroyed.
3244
3245 If the allocation has been created without #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
3246 this function always returns `VK_TRUE`.
3247 */
3248 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
3249 VmaAllocator VMA_NOT_NULL allocator,
3250 VmaAllocation VMA_NOT_NULL allocation);
3251
3252 /** \brief Sets pUserData in given allocation to new value.
3253
3254 If the allocation was created with VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT,
3255 pUserData must be either null, or pointer to a null-terminated string. The function
3256 makes local copy of the string and sets it as allocation's `pUserData`. String
3257 passed as pUserData doesn't need to be valid for whole lifetime of the allocation -
3258 you can free it after this call. String previously pointed by allocation's
3259 pUserData is freed from memory.
3260
3261 If the flag was not used, the value of pointer `pUserData` is just copied to
3262 allocation's `pUserData`. It is opaque, so you can use it however you want - e.g.
3263 as a pointer, ordinal number or some handle to you own data.
3264 */
3265 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
3266 VmaAllocator VMA_NOT_NULL allocator,
3267 VmaAllocation VMA_NOT_NULL allocation,
3268 void* VMA_NULLABLE pUserData);
3269
3270 /** \brief Creates new allocation that is in lost state from the beginning.
3271
3272 It can be useful if you need a dummy, non-null allocation.
3273
3274 You still need to destroy created object using vmaFreeMemory().
3275
3276 Returned allocation is not tied to any specific memory pool or memory type and
3277 not bound to any image or buffer. It has size = 0. It cannot be turned into
3278 a real, non-empty allocation.
3279 */
3280 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
3281 VmaAllocator VMA_NOT_NULL allocator,
3282 VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation);
3283
3284 /** \brief Maps memory represented by given allocation and returns pointer to it.
3285
3286 Maps memory represented by given allocation to make it accessible to CPU code.
3287 When succeeded, `*ppData` contains pointer to first byte of this memory.
3288 If the allocation is part of bigger `VkDeviceMemory` block, the pointer is
3289 correctly offseted to the beginning of region assigned to this particular
3290 allocation.
3291
3292 Mapping is internally reference-counted and synchronized, so despite raw Vulkan
3293 function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory`
3294 multiple times simultaneously, it is safe to call this function on allocations
3295 assigned to the same memory block. Actual Vulkan memory will be mapped on first
3296 mapping and unmapped on last unmapping.
3297
3298 If the function succeeded, you must call vmaUnmapMemory() to unmap the
3299 allocation when mapping is no longer needed or before freeing the allocation, at
3300 the latest.
3301
3302 It also safe to call this function multiple times on the same allocation. You
3303 must call vmaUnmapMemory() same number of times as you called vmaMapMemory().
3304
3305 It is also safe to call this function on allocation created with
3306 #VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time.
3307 You must still call vmaUnmapMemory() same number of times as you called
3308 vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the
3309 "0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag.
3310
3311 This function fails when used on allocation made in memory type that is not
3312 `HOST_VISIBLE`.
3313
3314 This function always fails when called for allocation that was created with
3315 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocations cannot be
3316 mapped.
3317
3318 This function doesn't automatically flush or invalidate caches.
3319 If the allocation is made from a memory types that is not `HOST_COHERENT`,
3320 you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification.
3321 */
3322 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
3323 VmaAllocator VMA_NOT_NULL allocator,
3324 VmaAllocation VMA_NOT_NULL allocation,
3325 void* VMA_NULLABLE* VMA_NOT_NULL ppData);
3326
3327 /** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
3328
3329 For details, see description of vmaMapMemory().
3330
3331 This function doesn't automatically flush or invalidate caches.
3332 If the allocation is made from a memory types that is not `HOST_COHERENT`,
3333 you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification.
3334 */
3335 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
3336 VmaAllocator VMA_NOT_NULL allocator,
3337 VmaAllocation VMA_NOT_NULL allocation);
3338
3339 /** \brief Flushes memory of given allocation.
3340
3341 Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation.
3342 It needs to be called after writing to a mapped memory for memory types that are not `HOST_COHERENT`.
3343 Unmap operation doesn't do that automatically.
3344
3345 - `offset` must be relative to the beginning of allocation.
3346 - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
3347 - `offset` and `size` don't have to be aligned.
3348 They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
3349 - If `size` is 0, this call is ignored.
3350 - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
3351 this call is ignored.
3352
3353 Warning! `offset` and `size` are relative to the contents of given `allocation`.
3354 If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively.
3355 Do not pass allocation's offset as `offset`!!!
3356
3357 This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is
3358 called, otherwise `VK_SUCCESS`.
3359 */
3360 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(
3361 VmaAllocator VMA_NOT_NULL allocator,
3362 VmaAllocation VMA_NOT_NULL allocation,
3363 VkDeviceSize offset,
3364 VkDeviceSize size);
3365
3366 /** \brief Invalidates memory of given allocation.
3367
3368 Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation.
3369 It needs to be called before reading from a mapped memory for memory types that are not `HOST_COHERENT`.
3370 Map operation doesn't do that automatically.
3371
3372 - `offset` must be relative to the beginning of allocation.
3373 - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
3374 - `offset` and `size` don't have to be aligned.
3375 They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
3376 - If `size` is 0, this call is ignored.
3377 - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
3378 this call is ignored.
3379
3380 Warning! `offset` and `size` are relative to the contents of given `allocation`.
3381 If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively.
3382 Do not pass allocation's offset as `offset`!!!
3383
3384 This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if
3385 it is called, otherwise `VK_SUCCESS`.
3386 */
3387 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(
3388 VmaAllocator VMA_NOT_NULL allocator,
3389 VmaAllocation VMA_NOT_NULL allocation,
3390 VkDeviceSize offset,
3391 VkDeviceSize size);
3392
3393 /** \brief Flushes memory of given set of allocations.
3394
3395 Calls `vkFlushMappedMemoryRanges()` for memory associated with given ranges of given allocations.
3396 For more information, see documentation of vmaFlushAllocation().
3397
3398 \param allocator
3399 \param allocationCount
3400 \param allocations
3401 \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.
3402 \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.
3403
3404 This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is
3405 called, otherwise `VK_SUCCESS`.
3406 */
3407 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
3408 VmaAllocator VMA_NOT_NULL allocator,
3409 uint32_t allocationCount,
3410 const VmaAllocation VMA_NOT_NULL* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
3411 const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
3412 const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
3413
3414 /** \brief Invalidates memory of given set of allocations.
3415
3416 Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given ranges of given allocations.
3417 For more information, see documentation of vmaInvalidateAllocation().
3418
3419 \param allocator
3420 \param allocationCount
3421 \param allocations
3422 \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.
3423 \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.
3424
3425 This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if it is
3426 called, otherwise `VK_SUCCESS`.
3427 */
3428 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
3429 VmaAllocator VMA_NOT_NULL allocator,
3430 uint32_t allocationCount,
3431 const VmaAllocation VMA_NOT_NULL* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
3432 const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
3433 const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
3434
3435 /** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions.
3436
3437 @param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked.
3438
3439 Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
3440 `VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are
3441 `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
3442
3443 Possible return values:
3444
3445 - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types.
3446 - `VK_SUCCESS` - corruption detection has been performed and succeeded.
3447 - `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
3448 `VMA_ASSERT` is also fired in that case.
3449 - Other value: Error returned by Vulkan, e.g. memory mapping failure.
3450 */
3451 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator VMA_NOT_NULL allocator, uint32_t memoryTypeBits);
3452
3453 /** \struct VmaDefragmentationContext
3454 \brief Represents Opaque object that represents started defragmentation process.
3455
3456 Fill structure #VmaDefragmentationInfo2 and call function vmaDefragmentationBegin() to create it.
3457 Call function vmaDefragmentationEnd() to destroy it.
3458 */
3459 VK_DEFINE_HANDLE(VmaDefragmentationContext)
3460
3461 /// Flags to be used in vmaDefragmentationBegin(). None at the moment. Reserved for future use.
3462 typedef enum VmaDefragmentationFlagBits {
3463 VMA_DEFRAGMENTATION_FLAG_INCREMENTAL = 0x1,
3464 VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
3465 } VmaDefragmentationFlagBits;
3466 typedef VkFlags VmaDefragmentationFlags;
3467
3468 /** \brief Parameters for defragmentation.
3469
3470 To be used with function vmaDefragmentationBegin().
3471 */
3472 typedef struct VmaDefragmentationInfo2 {
3473 /** \brief Reserved for future use. Should be 0.
3474 */
3475 VmaDefragmentationFlags flags;
3476 /** \brief Number of allocations in `pAllocations` array.
3477 */
3478 uint32_t allocationCount;
3479 /** \brief Pointer to array of allocations that can be defragmented.
3480
3481 The array should have `allocationCount` elements.
3482 The array should not contain nulls.
3483 Elements in the array should be unique - same allocation cannot occur twice.
3484 It is safe to pass allocations that are in the lost state - they are ignored.
3485 All allocations not present in this array are considered non-moveable during this defragmentation.
3486 */
3487 const VmaAllocation VMA_NOT_NULL* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations;
3488 /** \brief Optional, output. Pointer to array that will be filled with information whether the allocation at certain index has been changed during defragmentation.
3489
3490 The array should have `allocationCount` elements.
3491 You can pass null if you are not interested in this information.
3492 */
3493 VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged;
3494 /** \brief Numer of pools in `pPools` array.
3495 */
3496 uint32_t poolCount;
3497 /** \brief Either null or pointer to array of pools to be defragmented.
3498
3499 All the allocations in the specified pools can be moved during defragmentation
3500 and there is no way to check if they were really moved as in `pAllocationsChanged`,
3501 so you must query all the allocations in all these pools for new `VkDeviceMemory`
3502 and offset using vmaGetAllocationInfo() if you might need to recreate buffers
3503 and images bound to them.
3504
3505 The array should have `poolCount` elements.
3506 The array should not contain nulls.
3507 Elements in the array should be unique - same pool cannot occur twice.
3508
3509 Using this array is equivalent to specifying all allocations from the pools in `pAllocations`.
3510 It might be more efficient.
3511 */
3512 const VmaPool VMA_NOT_NULL* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(poolCount) pPools;
3513 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on CPU side, like `memcpy()`, `memmove()`.
3514
3515 `VK_WHOLE_SIZE` means no limit.
3516 */
3517 VkDeviceSize maxCpuBytesToMove;
3518 /** \brief Maximum number of allocations that can be moved to a different place using transfers on CPU side, like `memcpy()`, `memmove()`.
3519
3520 `UINT32_MAX` means no limit.
3521 */
3522 uint32_t maxCpuAllocationsToMove;
3523 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on GPU side, posted to `commandBuffer`.
3524
3525 `VK_WHOLE_SIZE` means no limit.
3526 */
3527 VkDeviceSize maxGpuBytesToMove;
3528 /** \brief Maximum number of allocations that can be moved to a different place using transfers on GPU side, posted to `commandBuffer`.
3529
3530 `UINT32_MAX` means no limit.
3531 */
3532 uint32_t maxGpuAllocationsToMove;
3533 /** \brief Optional. Command buffer where GPU copy commands will be posted.
3534
3535 If not null, it must be a valid command buffer handle that supports Transfer queue type.
3536 It must be in the recording state and outside of a render pass instance.
3537 You need to submit it and make sure it finished execution before calling vmaDefragmentationEnd().
3538
3539 Passing null means that only CPU defragmentation will be performed.
3540 */
3541 VkCommandBuffer VMA_NULLABLE commandBuffer;
3542 } VmaDefragmentationInfo2;
3543
3544 typedef struct VmaDefragmentationPassMoveInfo {
3545 VmaAllocation VMA_NOT_NULL allocation;
3546 VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory;
3547 VkDeviceSize offset;
3548 } VmaDefragmentationPassMoveInfo;
3549
3550 /** \brief Parameters for incremental defragmentation steps.
3551
3552 To be used with function vmaBeginDefragmentationPass().
3553 */
3554 typedef struct VmaDefragmentationPassInfo {
3555 uint32_t moveCount;
3556 VmaDefragmentationPassMoveInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(moveCount) pMoves;
3557 } VmaDefragmentationPassInfo;
3558
3559 /** \brief Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
3560
3561 \deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
3562 */
3563 typedef struct VmaDefragmentationInfo {
3564 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places.
3565
3566 Default is `VK_WHOLE_SIZE`, which means no limit.
3567 */
3568 VkDeviceSize maxBytesToMove;
3569 /** \brief Maximum number of allocations that can be moved to different place.
3570
3571 Default is `UINT32_MAX`, which means no limit.
3572 */
3573 uint32_t maxAllocationsToMove;
3574 } VmaDefragmentationInfo;
3575
3576 /** \brief Statistics returned by function vmaDefragment(). */
3577 typedef struct VmaDefragmentationStats {
3578 /// Total number of bytes that have been copied while moving allocations to different places.
3579 VkDeviceSize bytesMoved;
3580 /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects.
3581 VkDeviceSize bytesFreed;
3582 /// Number of allocations that have been moved to different places.
3583 uint32_t allocationsMoved;
3584 /// Number of empty `VkDeviceMemory` objects that have been released to the system.
3585 uint32_t deviceMemoryBlocksFreed;
3586 } VmaDefragmentationStats;
3587
3588 /** \brief Begins defragmentation process.
3589
3590 @param allocator Allocator object.
3591 @param pInfo Structure filled with parameters of defragmentation.
3592 @param[out] pStats Optional. Statistics of defragmentation. You can pass null if you are not interested in this information.
3593 @param[out] pContext Context object that must be passed to vmaDefragmentationEnd() to finish defragmentation.
3594 @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.
3595
3596 Use this function instead of old, deprecated vmaDefragment().
3597
3598 Warning! Between the call to vmaDefragmentationBegin() and vmaDefragmentationEnd():
3599
3600 - You should not use any of allocations passed as `pInfo->pAllocations` or
3601 any allocations that belong to pools passed as `pInfo->pPools`,
3602 including calling vmaGetAllocationInfo(), vmaTouchAllocation(), or access
3603 their data.
3604 - Some mutexes protecting internal data structures may be locked, so trying to
3605 make or free any allocations, bind buffers or images, map memory, or launch
3606 another simultaneous defragmentation in between may cause stall (when done on
3607 another thread) or deadlock (when done on the same thread), unless you are
3608 100% sure that defragmented allocations are in different pools.
3609 - Information returned via `pStats` and `pInfo->pAllocationsChanged` are undefined.
3610 They become valid after call to vmaDefragmentationEnd().
3611 - If `pInfo->commandBuffer` is not null, you must submit that command buffer
3612 and make sure it finished execution before calling vmaDefragmentationEnd().
3613
3614 For more information and important limitations regarding defragmentation, see documentation chapter:
3615 [Defragmentation](@ref defragmentation).
3616 */
3617 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
3618 VmaAllocator VMA_NOT_NULL allocator,
3619 const VmaDefragmentationInfo2* VMA_NOT_NULL pInfo,
3620 VmaDefragmentationStats* VMA_NULLABLE pStats,
3621 VmaDefragmentationContext VMA_NULLABLE* VMA_NOT_NULL pContext);
3622
3623 /** \brief Ends defragmentation process.
3624
3625 Use this function to finish defragmentation started by vmaDefragmentationBegin().
3626 It is safe to pass `context == null`. The function then does nothing.
3627 */
3628 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
3629 VmaAllocator VMA_NOT_NULL allocator,
3630 VmaDefragmentationContext VMA_NULLABLE context);
3631
3632 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
3633 VmaAllocator VMA_NOT_NULL allocator,
3634 VmaDefragmentationContext VMA_NULLABLE context,
3635 VmaDefragmentationPassInfo* VMA_NOT_NULL pInfo
3636 );
3637 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
3638 VmaAllocator VMA_NOT_NULL allocator,
3639 VmaDefragmentationContext VMA_NULLABLE context
3640 );
3641
3642 /** \brief Deprecated. Compacts memory by moving allocations.
3643
3644 @param pAllocations Array of allocations that can be moved during this compation.
3645 @param allocationCount Number of elements in pAllocations and pAllocationsChanged arrays.
3646 @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.
3647 @param pDefragmentationInfo Configuration parameters. Optional - pass null to use default values.
3648 @param[out] pDefragmentationStats Statistics returned by the function. Optional - pass null if you don't need this information.
3649 @return `VK_SUCCESS` if completed, negative error code in case of error.
3650
3651 \deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
3652
3653 This function works by moving allocations to different places (different
3654 `VkDeviceMemory` objects and/or different offsets) in order to optimize memory
3655 usage. Only allocations that are in `pAllocations` array can be moved. All other
3656 allocations are considered nonmovable in this call. Basic rules:
3657
3658 - Only allocations made in memory types that have
3659 `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`
3660 flags can be compacted. You may pass other allocations but it makes no sense -
3661 these will never be moved.
3662 - Custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT or
3663 #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag are not defragmented. Allocations
3664 passed to this function that come from such pools are ignored.
3665 - Allocations created with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT or
3666 created as dedicated allocations for any other reason are also ignored.
3667 - Both allocations made with or without #VMA_ALLOCATION_CREATE_MAPPED_BIT
3668 flag can be compacted. If not persistently mapped, memory will be mapped
3669 temporarily inside this function if needed.
3670 - You must not pass same #VmaAllocation object multiple times in `pAllocations` array.
3671
3672 The function also frees empty `VkDeviceMemory` blocks.
3673
3674 Warning: This function may be time-consuming, so you shouldn't call it too often
3675 (like after every resource creation/destruction).
3676 You can call it on special occasions (like when reloading a game level or
3677 when you just destroyed a lot of objects). Calling it every frame may be OK, but
3678 you should measure that on your platform.
3679
3680 For more information, see [Defragmentation](@ref defragmentation) chapter.
3681 */
3682 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
3683 VmaAllocator VMA_NOT_NULL allocator,
3684 const VmaAllocation VMA_NOT_NULL* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
3685 size_t allocationCount,
3686 VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged,
3687 const VmaDefragmentationInfo* VMA_NULLABLE pDefragmentationInfo,
3688 VmaDefragmentationStats* VMA_NULLABLE pDefragmentationStats);
3689
3690 /** \brief Binds buffer to allocation.
3691
3692 Binds specified buffer to region of memory represented by specified allocation.
3693 Gets `VkDeviceMemory` handle and offset from the allocation.
3694 If you want to create a buffer, allocate memory for it and bind them together separately,
3695 you should use this function for binding instead of standard `vkBindBufferMemory()`,
3696 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
3697 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
3698 (which is illegal in Vulkan).
3699
3700 It is recommended to use function vmaCreateBuffer() instead of this one.
3701 */
3702 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
3703 VmaAllocator VMA_NOT_NULL allocator,
3704 VmaAllocation VMA_NOT_NULL allocation,
3705 VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer);
3706
3707 /** \brief Binds buffer to allocation with additional parameters.
3708
3709 @param allocationLocalOffset Additional offset to be added while binding, relative to the beginnig of the `allocation`. Normally it should be 0.
3710 @param pNext A chain of structures to be attached to `VkBindBufferMemoryInfoKHR` structure used internally. Normally it should be null.
3711
3712 This function is similar to vmaBindBufferMemory(), but it provides additional parameters.
3713
3714 If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag
3715 or with VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_1`. Otherwise the call fails.
3716 */
3717 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
3718 VmaAllocator VMA_NOT_NULL allocator,
3719 VmaAllocation VMA_NOT_NULL allocation,
3720 VkDeviceSize allocationLocalOffset,
3721 VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
3722 const void* VMA_NULLABLE pNext);
3723
3724 /** \brief Binds image to allocation.
3725
3726 Binds specified image to region of memory represented by specified allocation.
3727 Gets `VkDeviceMemory` handle and offset from the allocation.
3728 If you want to create an image, allocate memory for it and bind them together separately,
3729 you should use this function for binding instead of standard `vkBindImageMemory()`,
3730 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
3731 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
3732 (which is illegal in Vulkan).
3733
3734 It is recommended to use function vmaCreateImage() instead of this one.
3735 */
3736 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
3737 VmaAllocator VMA_NOT_NULL allocator,
3738 VmaAllocation VMA_NOT_NULL allocation,
3739 VkImage VMA_NOT_NULL_NON_DISPATCHABLE image);
3740
3741 /** \brief Binds image to allocation with additional parameters.
3742
3743 @param allocationLocalOffset Additional offset to be added while binding, relative to the beginnig of the `allocation`. Normally it should be 0.
3744 @param pNext A chain of structures to be attached to `VkBindImageMemoryInfoKHR` structure used internally. Normally it should be null.
3745
3746 This function is similar to vmaBindImageMemory(), but it provides additional parameters.
3747
3748 If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag
3749 or with VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_1`. Otherwise the call fails.
3750 */
3751 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
3752 VmaAllocator VMA_NOT_NULL allocator,
3753 VmaAllocation VMA_NOT_NULL allocation,
3754 VkDeviceSize allocationLocalOffset,
3755 VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
3756 const void* VMA_NULLABLE pNext);
3757
3758 /**
3759 @param[out] pBuffer Buffer that was created.
3760 @param[out] pAllocation Allocation that was created.
3761 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
3762
3763 This function automatically:
3764
3765 -# Creates buffer.
3766 -# Allocates appropriate memory for it.
3767 -# Binds the buffer with the memory.
3768
3769 If any of these operations fail, buffer and allocation are not created,
3770 returned value is negative error code, *pBuffer and *pAllocation are null.
3771
3772 If the function succeeded, you must destroy both buffer and allocation when you
3773 no longer need them using either convenience function vmaDestroyBuffer() or
3774 separately, using `vkDestroyBuffer()` and vmaFreeMemory().
3775
3776 If VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used,
3777 VK_KHR_dedicated_allocation extension is used internally to query driver whether
3778 it requires or prefers the new buffer to have dedicated allocation. If yes,
3779 and if dedicated allocation is possible (VmaAllocationCreateInfo::pool is null
3780 and VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated
3781 allocation for this buffer, just like when using
3782 VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
3783 */
3784 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
3785 VmaAllocator VMA_NOT_NULL allocator,
3786 const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
3787 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
3788 VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer,
3789 VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,
3790 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3791
3792 /** \brief Destroys Vulkan buffer and frees allocated memory.
3793
3794 This is just a convenience function equivalent to:
3795
3796 \code
3797 vkDestroyBuffer(device, buffer, allocationCallbacks);
3798 vmaFreeMemory(allocator, allocation);
3799 \endcode
3800
3801 It it safe to pass null as buffer and/or allocation.
3802 */
3803 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
3804 VmaAllocator VMA_NOT_NULL allocator,
3805 VkBuffer VMA_NULLABLE_NON_DISPATCHABLE buffer,
3806 VmaAllocation VMA_NULLABLE allocation);
3807
3808 /// Function similar to vmaCreateBuffer().
3809 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
3810 VmaAllocator VMA_NOT_NULL allocator,
3811 const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
3812 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
3813 VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage,
3814 VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,
3815 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3816
3817 /** \brief Destroys Vulkan image and frees allocated memory.
3818
3819 This is just a convenience function equivalent to:
3820
3821 \code
3822 vkDestroyImage(device, image, allocationCallbacks);
3823 vmaFreeMemory(allocator, allocation);
3824 \endcode
3825
3826 It it safe to pass null as image and/or allocation.
3827 */
3828 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
3829 VmaAllocator VMA_NOT_NULL allocator,
3830 VkImage VMA_NULLABLE_NON_DISPATCHABLE image,
3831 VmaAllocation VMA_NULLABLE allocation);
3832
3833 #ifdef __cplusplus
3834 }
3835 #endif
3836
3837 #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
3838
3839 // For Visual Studio IntelliSense.
3840 #if defined(__cplusplus) && defined(__INTELLISENSE__)
3841 #define VMA_IMPLEMENTATION
3842 #endif
3843
3844 #ifdef VMA_IMPLEMENTATION
3845 #undef VMA_IMPLEMENTATION
3846
3847 #include <cstdint>
3848 #include <cstdlib>
3849 #include <cstring>
3850 #include <utility>
3851
3852 /*******************************************************************************
3853 CONFIGURATION SECTION
3854
3855 Define some of these macros before each #include of this header or change them
3856 here if you need other then default behavior depending on your environment.
3857 */
3858
3859 /*
3860 Define this macro to 1 to make the library fetch pointers to Vulkan functions
3861 internally, like:
3862
3863 vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
3864 */
3865 #if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
3866 #define VMA_STATIC_VULKAN_FUNCTIONS 1
3867 #endif
3868
3869 /*
3870 Define this macro to 1 to make the library fetch pointers to Vulkan functions
3871 internally, like:
3872
3873 vulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkGetDeviceProcAddr(m_hDevice, vkAllocateMemory);
3874 */
3875 #if !defined(VMA_DYNAMIC_VULKAN_FUNCTIONS)
3876 #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
3877 #endif
3878
3879 // Define this macro to 1 to make the library use STL containers instead of its own implementation.
3880 //#define VMA_USE_STL_CONTAINERS 1
3881
3882 /* Set this macro to 1 to make the library including and using STL containers:
3883 std::pair, std::vector, std::list, std::unordered_map.
3884
3885 Set it to 0 or undefined to make the library using its own implementation of
3886 the containers.
3887 */
3888 #if VMA_USE_STL_CONTAINERS
3889 #define VMA_USE_STL_VECTOR 1
3890 #define VMA_USE_STL_UNORDERED_MAP 1
3891 #define VMA_USE_STL_LIST 1
3892 #endif
3893
3894 #ifndef VMA_USE_STL_SHARED_MUTEX
3895 // Compiler conforms to C++17.
3896 #if __cplusplus >= 201703L
3897 #define VMA_USE_STL_SHARED_MUTEX 1
3898 // Visual studio defines __cplusplus properly only when passed additional parameter: /Zc:__cplusplus
3899 // Otherwise it's always 199711L, despite shared_mutex works since Visual Studio 2015 Update 2.
3900 // See: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/
3901 #elif defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && __cplusplus == 199711L && _MSVC_LANG >= 201703L
3902 #define VMA_USE_STL_SHARED_MUTEX 1
3903 #else
3904 #define VMA_USE_STL_SHARED_MUTEX 0
3905 #endif
3906 #endif
3907
3908 /*
3909 THESE INCLUDES ARE NOT ENABLED BY DEFAULT.
3910 Library has its own container implementation.
3911 */
3912 #if VMA_USE_STL_VECTOR
3913 #include <vector>
3914 #endif
3915
3916 #if VMA_USE_STL_UNORDERED_MAP
3917 #include <unordered_map>
3918 #endif
3919
3920 #if VMA_USE_STL_LIST
3921 #include <list>
3922 #endif
3923
3924 /*
3925 Following headers are used in this CONFIGURATION section only, so feel free to
3926 remove them if not needed.
3927 */
3928 #include <cassert> // for assert
3929 #include <algorithm> // for min, max
3930 #include <mutex>
3931
3932 #ifndef VMA_NULL
3933 // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
3934 #define VMA_NULL nullptr
3935 #endif
3936
3937 #if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
3938 #include <cstdlib>
3939 void* aligned_alloc(size_t alignment, size_t size)
3940 {
3941 // alignment must be >= sizeof(void*)
3942 if (alignment < sizeof(void*))
3943 {
3944 alignment = sizeof(void*);
3945 }
3946
3947 return memalign(alignment, size);
3948 }
3949 #elif defined(__APPLE__) || defined(__ANDROID__) || (defined(__linux__) && defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC))
3950 #include <cstdlib>
3951 void* aligned_alloc(size_t alignment, size_t size)
3952 {
3953 // alignment must be >= sizeof(void*)
3954 if (alignment < sizeof(void*))
3955 {
3956 alignment = sizeof(void*);
3957 }
3958
3959 void* pointer;
3960 if (posix_memalign(&pointer, alignment, size) == 0)
3961 return pointer;
3962 return VMA_NULL;
3963 }
3964 #endif
3965
3966 // If your compiler is not compatible with C++11 and definition of
3967 // aligned_alloc() function is missing, uncommeting following line may help:
3968
3969 //#include <malloc.h>
3970
3971 // Normal assert to check for programmer's errors, especially in Debug configuration.
3972 #ifndef VMA_ASSERT
3973 #ifdef NDEBUG
3974 #define VMA_ASSERT(expr)
3975 #else
3976 #define VMA_ASSERT(expr) assert(expr)
3977 #endif
3978 #endif
3979
3980 // Assert that will be called very often, like inside data structures e.g. operator[].
3981 // Making it non-empty can make program slow.
3982 #ifndef VMA_HEAVY_ASSERT
3983 #ifdef NDEBUG
3984 #define VMA_HEAVY_ASSERT(expr)
3985 #else
3986 #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
3987 #endif
3988 #endif
3989
3990 #ifndef VMA_ALIGN_OF
3991 #define VMA_ALIGN_OF(type) (__alignof(type))
3992 #endif
3993
3994 #ifndef VMA_SYSTEM_ALIGNED_MALLOC
3995 #if defined(_WIN32)
3996 #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment)))
3997 #else
3998 #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (aligned_alloc((alignment), (size) ))
3999 #endif
4000 #endif
4001
4002 #ifndef VMA_SYSTEM_FREE
4003 #if defined(_WIN32)
4004 #define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr)
4005 #else
4006 #define VMA_SYSTEM_FREE(ptr) free(ptr)
4007 #endif
4008 #endif
4009
4010 #ifndef VMA_MIN
4011 #define VMA_MIN(v1, v2) (std::min((v1), (v2)))
4012 #endif
4013
4014 #ifndef VMA_MAX
4015 #define VMA_MAX(v1, v2) (std::max((v1), (v2)))
4016 #endif
4017
4018 #ifndef VMA_SWAP
4019 #define VMA_SWAP(v1, v2) std::swap((v1), (v2))
4020 #endif
4021
4022 #ifndef VMA_SORT
4023 #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp)
4024 #endif
4025
4026 #ifndef VMA_DEBUG_LOG
4027 #define VMA_DEBUG_LOG(format, ...)
4028 /*
4029 #define VMA_DEBUG_LOG(format, ...) do { \
4030 printf(format, __VA_ARGS__); \
4031 printf("\n"); \
4032 } while(false)
4033 */
4034 #endif
4035
4036 // Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
4037 #if VMA_STATS_STRING_ENABLED
4038 static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num)
4039 {
4040 snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
4041 }
4042 static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
4043 {
4044 snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
4045 }
4046 static inline void VmaPtrToStr(char* outStr, size_t strLen, const void* ptr)
4047 {
4048 snprintf(outStr, strLen, "%p", ptr);
4049 }
4050 #endif
4051
4052 #ifndef VMA_MUTEX
4053 class VmaMutex
4054 {
4055 public:
4056 void Lock() { m_Mutex.lock(); }
4057 void Unlock() { m_Mutex.unlock(); }
4058 bool TryLock() { return m_Mutex.try_lock(); }
4059 private:
4060 std::mutex m_Mutex;
4061 };
4062 #define VMA_MUTEX VmaMutex
4063 #endif
4064
4065 // Read-write mutex, where "read" is shared access, "write" is exclusive access.
4066 #ifndef VMA_RW_MUTEX
4067 #if VMA_USE_STL_SHARED_MUTEX
4068 // Use std::shared_mutex from C++17.
4069 #include <shared_mutex>
4070 class VmaRWMutex
4071 {
4072 public:
4073 void LockRead() { m_Mutex.lock_shared(); }
4074 void UnlockRead() { m_Mutex.unlock_shared(); }
4075 bool TryLockRead() { return m_Mutex.try_lock_shared(); }
4076 void LockWrite() { m_Mutex.lock(); }
4077 void UnlockWrite() { m_Mutex.unlock(); }
4078 bool TryLockWrite() { return m_Mutex.try_lock(); }
4079 private:
4080 std::shared_mutex m_Mutex;
4081 };
4082 #define VMA_RW_MUTEX VmaRWMutex
4083 #elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600
4084 // Use SRWLOCK from WinAPI.
4085 // Minimum supported client = Windows Vista, server = Windows Server 2008.
4086 class VmaRWMutex
4087 {
4088 public:
4089 VmaRWMutex() { InitializeSRWLock(&m_Lock); }
4090 void LockRead() { AcquireSRWLockShared(&m_Lock); }
4091 void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
4092 bool TryLockRead() { return TryAcquireSRWLockShared(&m_Lock) != FALSE; }
4093 void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
4094 void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
4095 bool TryLockWrite() { return TryAcquireSRWLockExclusive(&m_Lock) != FALSE; }
4096 private:
4097 SRWLOCK m_Lock;
4098 };
4099 #define VMA_RW_MUTEX VmaRWMutex
4100 #else
4101 // Less efficient fallback: Use normal mutex.
4102 class VmaRWMutex
4103 {
4104 public:
4105 void LockRead() { m_Mutex.Lock(); }
4106 void UnlockRead() { m_Mutex.Unlock(); }
4107 bool TryLockRead() { return m_Mutex.TryLock(); }
4108 void LockWrite() { m_Mutex.Lock(); }
4109 void UnlockWrite() { m_Mutex.Unlock(); }
4110 bool TryLockWrite() { return m_Mutex.TryLock(); }
4111 private:
4112 VMA_MUTEX m_Mutex;
4113 };
4114 #define VMA_RW_MUTEX VmaRWMutex
4115 #endif // #if VMA_USE_STL_SHARED_MUTEX
4116 #endif // #ifndef VMA_RW_MUTEX
4117
4118 /*
4119 If providing your own implementation, you need to implement a subset of std::atomic.
4120 */
4121 #ifndef VMA_ATOMIC_UINT32
4122 #include <atomic>
4123 #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
4124 #endif
4125
4126 #ifndef VMA_ATOMIC_UINT64
4127 #include <atomic>
4128 #define VMA_ATOMIC_UINT64 std::atomic<uint64_t>
4129 #endif
4130
4131 #ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
4132 /**
4133 Every allocation will have its own memory block.
4134 Define to 1 for debugging purposes only.
4135 */
4136 #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
4137 #endif
4138
4139 #ifndef VMA_DEBUG_ALIGNMENT
4140 /**
4141 Minimum alignment of all allocations, in bytes.
4142 Set to more than 1 for debugging purposes only. Must be power of two.
4143 */
4144 #define VMA_DEBUG_ALIGNMENT (1)
4145 #endif
4146
4147 #ifndef VMA_DEBUG_MARGIN
4148 /**
4149 Minimum margin before and after every allocation, in bytes.
4150 Set nonzero for debugging purposes only.
4151 */
4152 #define VMA_DEBUG_MARGIN (0)
4153 #endif
4154
4155 #ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
4156 /**
4157 Define this macro to 1 to automatically fill new allocations and destroyed
4158 allocations with some bit pattern.
4159 */
4160 #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)
4161 #endif
4162
4163 #ifndef VMA_DEBUG_DETECT_CORRUPTION
4164 /**
4165 Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to
4166 enable writing magic value to the margin before and after every allocation and
4167 validating it, so that memory corruptions (out-of-bounds writes) are detected.
4168 */
4169 #define VMA_DEBUG_DETECT_CORRUPTION (0)
4170 #endif
4171
4172 #ifndef VMA_DEBUG_GLOBAL_MUTEX
4173 /**
4174 Set this to 1 for debugging purposes only, to enable single mutex protecting all
4175 entry calls to the library. Can be useful for debugging multithreading issues.
4176 */
4177 #define VMA_DEBUG_GLOBAL_MUTEX (0)
4178 #endif
4179
4180 #ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
4181 /**
4182 Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.
4183 Set to more than 1 for debugging purposes only. Must be power of two.
4184 */
4185 #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
4186 #endif
4187
4188 #ifndef VMA_SMALL_HEAP_MAX_SIZE
4189 /// Maximum size of a memory heap in Vulkan to consider it "small".
4190 #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
4191 #endif
4192
4193 #ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
4194 /// Default size of a block allocated as single VkDeviceMemory from a "large" heap.
4195 #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
4196 #endif
4197
4198 #ifndef VMA_CLASS_NO_COPY
4199 #define VMA_CLASS_NO_COPY(className) \
4200 private: \
4201 className(const className&) = delete; \
4202 className& operator=(const className&) = delete;
4203 #endif
4204
4205 static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX;
4206
4207 // Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.
4208 static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
4209
4210 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC;
4211 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
4212
4213 /*******************************************************************************
4214 END OF CONFIGURATION
4215 */
4216
4217 // # Copy of some Vulkan definitions so we don't need to check their existence just to handle few constants.
4218
4219 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY = 0x00000040;
4220 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY = 0x00000080;
4221 static const uint32_t VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY = 0x00020000;
4222
4223 static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
4224
4225 static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
4226 VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
4227
4228 // Returns number of bits set to 1 in (v).
4229 static inline uint32_t VmaCountBitsSet(uint32_t v)
4230 {
4231 uint32_t c = v - ((v >> 1) & 0x55555555);
4232 c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
4233 c = ((c >> 4) + c) & 0x0F0F0F0F;
4234 c = ((c >> 8) + c) & 0x00FF00FF;
4235 c = ((c >> 16) + c) & 0x0000FFFF;
4236 return c;
4237 }
4238
4239 // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
4240 // Use types like uint32_t, uint64_t as T.
4241 template <typename T>
4242 static inline T VmaAlignUp(T val, T align)
4243 {
4244 return (val + align - 1) / align * align;
4245 }
4246 // Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8.
4247 // Use types like uint32_t, uint64_t as T.
4248 template <typename T>
4249 static inline T VmaAlignDown(T val, T align)
4250 {
4251 return val / align * align;
4252 }
4253
4254 // Division with mathematical rounding to nearest number.
4255 template <typename T>
4256 static inline T VmaRoundDiv(T x, T y)
4257 {
4258 return (x + (y / (T)2)) / y;
4259 }
4260
4261 /*
4262 Returns true if given number is a power of two.
4263 T must be unsigned integer number or signed integer but always nonnegative.
4264 For 0 returns true.
4265 */
4266 template <typename T>
4267 inline bool VmaIsPow2(T x)
4268 {
4269 return (x & (x - 1)) == 0;
4270 }
4271
4272 // Returns smallest power of 2 greater or equal to v.
4273 static inline uint32_t VmaNextPow2(uint32_t v)
4274 {
4275 v--;
4276 v |= v >> 1;
4277 v |= v >> 2;
4278 v |= v >> 4;
4279 v |= v >> 8;
4280 v |= v >> 16;
4281 v++;
4282 return v;
4283 }
4284 static inline uint64_t VmaNextPow2(uint64_t v)
4285 {
4286 v--;
4287 v |= v >> 1;
4288 v |= v >> 2;
4289 v |= v >> 4;
4290 v |= v >> 8;
4291 v |= v >> 16;
4292 v |= v >> 32;
4293 v++;
4294 return v;
4295 }
4296
4297 // Returns largest power of 2 less or equal to v.
4298 static inline uint32_t VmaPrevPow2(uint32_t v)
4299 {
4300 v |= v >> 1;
4301 v |= v >> 2;
4302 v |= v >> 4;
4303 v |= v >> 8;
4304 v |= v >> 16;
4305 v = v ^ (v >> 1);
4306 return v;
4307 }
4308 static inline uint64_t VmaPrevPow2(uint64_t v)
4309 {
4310 v |= v >> 1;
4311 v |= v >> 2;
4312 v |= v >> 4;
4313 v |= v >> 8;
4314 v |= v >> 16;
4315 v |= v >> 32;
4316 v = v ^ (v >> 1);
4317 return v;
4318 }
4319
4320 static inline bool VmaStrIsEmpty(const char* pStr)
4321 {
4322 return pStr == VMA_NULL || *pStr == '\0';
4323 }
4324
4325 #if VMA_STATS_STRING_ENABLED
4326
4327 static const char* VmaAlgorithmToStr(uint32_t algorithm)
4328 {
4329 switch (algorithm)
4330 {
4331 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
4332 return "Linear";
4333 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
4334 return "Buddy";
4335 case 0:
4336 return "Default";
4337 default:
4338 VMA_ASSERT(0);
4339 return "";
4340 }
4341 }
4342
4343 #endif // #if VMA_STATS_STRING_ENABLED
4344
4345 #ifndef VMA_SORT
4346
4347 template<typename Iterator, typename Compare>
4348 Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
4349 {
4350 Iterator centerValue = end; --centerValue;
4351 Iterator insertIndex = beg;
4352 for (Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
4353 {
4354 if (cmp(*memTypeIndex, *centerValue))
4355 {
4356 if (insertIndex != memTypeIndex)
4357 {
4358 VMA_SWAP(*memTypeIndex, *insertIndex);
4359 }
4360 ++insertIndex;
4361 }
4362 }
4363 if (insertIndex != centerValue)
4364 {
4365 VMA_SWAP(*insertIndex, *centerValue);
4366 }
4367 return insertIndex;
4368 }
4369
4370 template<typename Iterator, typename Compare>
4371 void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
4372 {
4373 if (beg < end)
4374 {
4375 Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
4376 VmaQuickSort<Iterator, Compare>(beg, it, cmp);
4377 VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
4378 }
4379 }
4380
4381 #define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
4382
4383 #endif // #ifndef VMA_SORT
4384
4385 /*
4386 Returns true if two memory blocks occupy overlapping pages.
4387 ResourceA must be in less memory offset than ResourceB.
4388
4389 Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
4390 chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
4391 */
4392 static inline bool VmaBlocksOnSamePage(
4393 VkDeviceSize resourceAOffset,
4394 VkDeviceSize resourceASize,
4395 VkDeviceSize resourceBOffset,
4396 VkDeviceSize pageSize)
4397 {
4398 VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
4399 VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
4400 VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
4401 VkDeviceSize resourceBStart = resourceBOffset;
4402 VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
4403 return resourceAEndPage == resourceBStartPage;
4404 }
4405
4406 enum VmaSuballocationType
4407 {
4408 VMA_SUBALLOCATION_TYPE_FREE = 0,
4409 VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
4410 VMA_SUBALLOCATION_TYPE_BUFFER = 2,
4411 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
4412 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
4413 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
4414 VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
4415 };
4416
4417 /*
4418 Returns true if given suballocation types could conflict and must respect
4419 VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
4420 or linear image and another one is optimal image. If type is unknown, behave
4421 conservatively.
4422 */
4423 static inline bool VmaIsBufferImageGranularityConflict(
4424 VmaSuballocationType suballocType1,
4425 VmaSuballocationType suballocType2)
4426 {
4427 if (suballocType1 > suballocType2)
4428 {
4429 VMA_SWAP(suballocType1, suballocType2);
4430 }
4431
4432 switch (suballocType1)
4433 {
4434 case VMA_SUBALLOCATION_TYPE_FREE:
4435 return false;
4436 case VMA_SUBALLOCATION_TYPE_UNKNOWN:
4437 return true;
4438 case VMA_SUBALLOCATION_TYPE_BUFFER:
4439 return
4440 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
4441 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4442 case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
4443 return
4444 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
4445 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
4446 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4447 case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
4448 return
4449 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4450 case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
4451 return false;
4452 default:
4453 VMA_ASSERT(0);
4454 return true;
4455 }
4456 }
4457
4458 static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
4459 {
4460 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
4461 uint32_t* pDst = (uint32_t*)((char*)pData + offset);
4462 const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
4463 for (size_t i = 0; i < numberCount; ++i, ++pDst)
4464 {
4465 *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
4466 }
4467 #else
4468 // no-op
4469 #endif
4470 }
4471
4472 static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
4473 {
4474 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
4475 const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
4476 const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
4477 for (size_t i = 0; i < numberCount; ++i, ++pSrc)
4478 {
4479 if (*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
4480 {
4481 return false;
4482 }
4483 }
4484 #endif
4485 return true;
4486 }
4487
4488 /*
4489 Fills structure with parameters of an example buffer to be used for transfers
4490 during GPU memory defragmentation.
4491 */
4492 static void VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo& outBufCreateInfo)
4493 {
4494 memset(&outBufCreateInfo, 0, sizeof(outBufCreateInfo));
4495 outBufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
4496 outBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
4497 outBufCreateInfo.size = (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE; // Example size.
4498 }
4499
4500 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
4501 struct VmaMutexLock
4502 {
4503 VMA_CLASS_NO_COPY(VmaMutexLock)
4504 public:
4505 VmaMutexLock(VMA_MUTEX& mutex, bool useMutex = true) :
4506 m_pMutex(useMutex ? &mutex : VMA_NULL)
4507 {
4508 if (m_pMutex) { m_pMutex->Lock(); }
4509 }
4510 ~VmaMutexLock()
4511 {
4512 if (m_pMutex) { m_pMutex->Unlock(); }
4513 }
4514 private:
4515 VMA_MUTEX* m_pMutex;
4516 };
4517
4518 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
4519 struct VmaMutexLockRead
4520 {
4521 VMA_CLASS_NO_COPY(VmaMutexLockRead)
4522 public:
4523 VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
4524 m_pMutex(useMutex ? &mutex : VMA_NULL)
4525 {
4526 if (m_pMutex) { m_pMutex->LockRead(); }
4527 }
4528 ~VmaMutexLockRead() { if (m_pMutex) { m_pMutex->UnlockRead(); } }
4529 private:
4530 VMA_RW_MUTEX* m_pMutex;
4531 };
4532
4533 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
4534 struct VmaMutexLockWrite
4535 {
4536 VMA_CLASS_NO_COPY(VmaMutexLockWrite)
4537 public:
4538 VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) :
4539 m_pMutex(useMutex ? &mutex : VMA_NULL)
4540 {
4541 if (m_pMutex) { m_pMutex->LockWrite(); }
4542 }
4543 ~VmaMutexLockWrite() { if (m_pMutex) { m_pMutex->UnlockWrite(); } }
4544 private:
4545 VMA_RW_MUTEX* m_pMutex;
4546 };
4547
4548 #if VMA_DEBUG_GLOBAL_MUTEX
4549 static VMA_MUTEX gDebugGlobalMutex;
4550 #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
4551 #else
4552 #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
4553 #endif
4554
4555 // Minimum size of a free suballocation to register it in the free suballocation collection.
4556 static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
4557
4558 /*
4559 Performs binary search and returns iterator to first element that is greater or
4560 equal to (key), according to comparison (cmp).
4561
4562 Cmp should return true if first argument is less than second argument.
4563
4564 Returned value is the found element, if present in the collection or place where
4565 new element with value (key) should be inserted.
4566 */
4567 template <typename CmpLess, typename IterT, typename KeyT>
4568 static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT& key, const CmpLess& cmp)
4569 {
4570 size_t down = 0, up = (end - beg);
4571 while (down < up)
4572 {
4573 const size_t mid = (down + up) / 2;
4574 if (cmp(*(beg + mid), key))
4575 {
4576 down = mid + 1;
4577 }
4578 else
4579 {
4580 up = mid;
4581 }
4582 }
4583 return beg + down;
4584 }
4585
4586 template<typename CmpLess, typename IterT, typename KeyT>
4587 IterT VmaBinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp)
4588 {
4589 IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
4590 beg, end, value, cmp);
4591 if (it == end ||
4592 (!cmp(*it, value) && !cmp(value, *it)))
4593 {
4594 return it;
4595 }
4596 return end;
4597 }
4598
4599 /*
4600 Returns true if all pointers in the array are not-null and unique.
4601 Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
4602 T must be pointer type, e.g. VmaAllocation, VmaPool.
4603 */
4604 template<typename T>
4605 static bool VmaValidatePointerArray(uint32_t count, const T* arr)
4606 {
4607 for (uint32_t i = 0; i < count; ++i)
4608 {
4609 const T iPtr = arr[i];
4610 if (iPtr == VMA_NULL)
4611 {
4612 return false;
4613 }
4614 for (uint32_t j = i + 1; j < count; ++j)
4615 {
4616 if (iPtr == arr[j])
4617 {
4618 return false;
4619 }
4620 }
4621 }
4622 return true;
4623 }
4624
4625 template<typename MainT, typename NewT>
4626 static inline void VmaPnextChainPushFront(MainT* mainStruct, NewT* newStruct)
4627 {
4628 newStruct->pNext = mainStruct->pNext;
4629 mainStruct->pNext = newStruct;
4630 }
4631
4632 ////////////////////////////////////////////////////////////////////////////////
4633 // Memory allocation
4634
4635 static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
4636 {
4637 if ((pAllocationCallbacks != VMA_NULL) &&
4638 (pAllocationCallbacks->pfnAllocation != VMA_NULL))
4639 {
4640 return (*pAllocationCallbacks->pfnAllocation)(
4641 pAllocationCallbacks->pUserData,
4642 size,
4643 alignment,
4644 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
4645 }
4646 else
4647 {
4648 return VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
4649 }
4650 }
4651
4652 static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
4653 {
4654 if ((pAllocationCallbacks != VMA_NULL) &&
4655 (pAllocationCallbacks->pfnFree != VMA_NULL))
4656 {
4657 (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
4658 }
4659 else
4660 {
4661 VMA_SYSTEM_FREE(ptr);
4662 }
4663 }
4664
4665 template<typename T>
4666 static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
4667 {
4668 return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
4669 }
4670
4671 template<typename T>
4672 static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
4673 {
4674 return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
4675 }
4676
4677 #define vma_new(allocator, type) new(VmaAllocate<type>(allocator))(type)
4678
4679 #define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type)
4680
4681 template<typename T>
4682 static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
4683 {
4684 ptr->~T();
4685 VmaFree(pAllocationCallbacks, ptr);
4686 }
4687
4688 template<typename T>
4689 static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
4690 {
4691 if (ptr != VMA_NULL)
4692 {
4693 for (size_t i = count; i--; )
4694 {
4695 ptr[i].~T();
4696 }
4697 VmaFree(pAllocationCallbacks, ptr);
4698 }
4699 }
4700
4701 static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr)
4702 {
4703 if (srcStr != VMA_NULL)
4704 {
4705 const size_t len = strlen(srcStr);
4706 char* const result = vma_new_array(allocs, char, len + 1);
4707 memcpy(result, srcStr, len + 1);
4708 return result;
4709 }
4710 else
4711 {
4712 return VMA_NULL;
4713 }
4714 }
4715
4716 static void VmaFreeString(const VkAllocationCallbacks* allocs, char* str)
4717 {
4718 if (str != VMA_NULL)
4719 {
4720 const size_t len = strlen(str);
4721 vma_delete_array(allocs, str, len + 1);
4722 }
4723 }
4724
4725 // STL-compatible allocator.
4726 template<typename T>
4727 class VmaStlAllocator
4728 {
4729 public:
4730 const VkAllocationCallbacks* const m_pCallbacks;
4731 typedef T value_type;
4732
4733 VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
4734 template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
4735
4736 T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
4737 void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
4738
4739 template<typename U>
4740 bool operator==(const VmaStlAllocator<U>& rhs) const
4741 {
4742 return m_pCallbacks == rhs.m_pCallbacks;
4743 }
4744 template<typename U>
4745 bool operator!=(const VmaStlAllocator<U>& rhs) const
4746 {
4747 return m_pCallbacks != rhs.m_pCallbacks;
4748 }
4749
4750 VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
4751 };
4752
4753 #if VMA_USE_STL_VECTOR
4754
4755 #define VmaVector std::vector
4756
4757 template<typename T, typename allocatorT>
4758 static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
4759 {
4760 vec.insert(vec.begin() + index, item);
4761 }
4762
4763 template<typename T, typename allocatorT>
4764 static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
4765 {
4766 vec.erase(vec.begin() + index);
4767 }
4768
4769 #else // #if VMA_USE_STL_VECTOR
4770
4771 /* Class with interface compatible with subset of std::vector.
4772 T must be POD because constructors and destructors are not called and memcpy is
4773 used for these objects. */
4774 template<typename T, typename AllocatorT>
4775 class VmaVector
4776 {
4777 public:
4778 typedef T value_type;
4779
4780 VmaVector(const AllocatorT& allocator) :
4781 m_Allocator(allocator),
4782 m_pArray(VMA_NULL),
4783 m_Count(0),
4784 m_Capacity(0)
4785 {
4786 }
4787
4788 VmaVector(size_t count, const AllocatorT& allocator) :
4789 m_Allocator(allocator),
4790 m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
4791 m_Count(count),
4792 m_Capacity(count)
4793 {
4794 }
4795
4796 // This version of the constructor is here for compatibility with pre-C++14 std::vector.
4797 // value is unused.
4798 VmaVector(size_t count, const T& value, const AllocatorT& allocator)
4799 : VmaVector(count, allocator) {}
4800
4801 VmaVector(const VmaVector<T, AllocatorT>& src) :
4802 m_Allocator(src.m_Allocator),
4803 m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
4804 m_Count(src.m_Count),
4805 m_Capacity(src.m_Count)
4806 {
4807 if (m_Count != 0)
4808 {
4809 memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
4810 }
4811 }
4812
4813 ~VmaVector()
4814 {
4815 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4816 }
4817
4818 VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
4819 {
4820 if (&rhs != this)
4821 {
4822 resize(rhs.m_Count);
4823 if (m_Count != 0)
4824 {
4825 memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
4826 }
4827 }
4828 return *this;
4829 }
4830
4831 bool empty() const { return m_Count == 0; }
4832 size_t size() const { return m_Count; }
4833 T* data() { return m_pArray; }
4834 const T* data() const { return m_pArray; }
4835
4836 T& operator[](size_t index)
4837 {
4838 VMA_HEAVY_ASSERT(index < m_Count);
4839 return m_pArray[index];
4840 }
4841 const T& operator[](size_t index) const
4842 {
4843 VMA_HEAVY_ASSERT(index < m_Count);
4844 return m_pArray[index];
4845 }
4846
4847 T& front()
4848 {
4849 VMA_HEAVY_ASSERT(m_Count > 0);
4850 return m_pArray[0];
4851 }
4852 const T& front() const
4853 {
4854 VMA_HEAVY_ASSERT(m_Count > 0);
4855 return m_pArray[0];
4856 }
4857 T& back()
4858 {
4859 VMA_HEAVY_ASSERT(m_Count > 0);
4860 return m_pArray[m_Count - 1];
4861 }
4862 const T& back() const
4863 {
4864 VMA_HEAVY_ASSERT(m_Count > 0);
4865 return m_pArray[m_Count - 1];
4866 }
4867
4868 void reserve(size_t newCapacity, bool freeMemory = false)
4869 {
4870 newCapacity = VMA_MAX(newCapacity, m_Count);
4871
4872 if ((newCapacity < m_Capacity) && !freeMemory)
4873 {
4874 newCapacity = m_Capacity;
4875 }
4876
4877 if (newCapacity != m_Capacity)
4878 {
4879 T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
4880 if (m_Count != 0)
4881 {
4882 memcpy(newArray, m_pArray, m_Count * sizeof(T));
4883 }
4884 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4885 m_Capacity = newCapacity;
4886 m_pArray = newArray;
4887 }
4888 }
4889
4890 void resize(size_t newCount, bool freeMemory = false)
4891 {
4892 size_t newCapacity = m_Capacity;
4893 if (newCount > m_Capacity)
4894 {
4895 newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
4896 }
4897 else if (freeMemory)
4898 {
4899 newCapacity = newCount;
4900 }
4901
4902 if (newCapacity != m_Capacity)
4903 {
4904 T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
4905 const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
4906 if (elementsToCopy != 0)
4907 {
4908 memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
4909 }
4910 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4911 m_Capacity = newCapacity;
4912 m_pArray = newArray;
4913 }
4914
4915 m_Count = newCount;
4916 }
4917
4918 void clear(bool freeMemory = false)
4919 {
4920 resize(0, freeMemory);
4921 }
4922
4923 void insert(size_t index, const T& src)
4924 {
4925 VMA_HEAVY_ASSERT(index <= m_Count);
4926 const size_t oldCount = size();
4927 resize(oldCount + 1);
4928 if (index < oldCount)
4929 {
4930 memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
4931 }
4932 m_pArray[index] = src;
4933 }
4934
4935 void remove(size_t index)
4936 {
4937 VMA_HEAVY_ASSERT(index < m_Count);
4938 const size_t oldCount = size();
4939 if (index < oldCount - 1)
4940 {
4941 memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
4942 }
4943 resize(oldCount - 1);
4944 }
4945
4946 void push_back(const T& src)
4947 {
4948 const size_t newIndex = size();
4949 resize(newIndex + 1);
4950 m_pArray[newIndex] = src;
4951 }
4952
4953 void pop_back()
4954 {
4955 VMA_HEAVY_ASSERT(m_Count > 0);
4956 resize(size() - 1);
4957 }
4958
4959 void push_front(const T& src)
4960 {
4961 insert(0, src);
4962 }
4963
4964 void pop_front()
4965 {
4966 VMA_HEAVY_ASSERT(m_Count > 0);
4967 remove(0);
4968 }
4969
4970 typedef T* iterator;
4971
4972 iterator begin() { return m_pArray; }
4973 iterator end() { return m_pArray + m_Count; }
4974
4975 private:
4976 AllocatorT m_Allocator;
4977 T* m_pArray;
4978 size_t m_Count;
4979 size_t m_Capacity;
4980 };
4981
4982 template<typename T, typename allocatorT>
4983 static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
4984 {
4985 vec.insert(index, item);
4986 }
4987
4988 template<typename T, typename allocatorT>
4989 static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
4990 {
4991 vec.remove(index);
4992 }
4993
4994 #endif // #if VMA_USE_STL_VECTOR
4995
4996 template<typename CmpLess, typename VectorT>
4997 size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
4998 {
4999 const size_t indexToInsert = VmaBinaryFindFirstNotLess(
5000 vector.data(),
5001 vector.data() + vector.size(),
5002 value,
5003 CmpLess()) - vector.data();
5004 VmaVectorInsert(vector, indexToInsert, value);
5005 return indexToInsert;
5006 }
5007
5008 template<typename CmpLess, typename VectorT>
5009 bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
5010 {
5011 CmpLess comparator;
5012 typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
5013 vector.begin(),
5014 vector.end(),
5015 value,
5016 comparator);
5017 if ((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
5018 {
5019 size_t indexToRemove = it - vector.begin();
5020 VmaVectorRemove(vector, indexToRemove);
5021 return true;
5022 }
5023 return false;
5024 }
5025
5026 ////////////////////////////////////////////////////////////////////////////////
5027 // class VmaSmallVector
5028
5029 /*
5030 This is a vector (a variable-sized array), optimized for the case when the array is small.
5031
5032 It contains some number of elements in-place, which allows it to avoid heap allocation
5033 when the actual number of elements is below that threshold. This allows normal "small"
5034 cases to be fast without losing generality for large inputs.
5035 */
5036
5037 template<typename T, typename AllocatorT, size_t N>
5038 class VmaSmallVector
5039 {
5040 public:
5041 typedef T value_type;
5042
5043 VmaSmallVector(const AllocatorT& allocator) :
5044 m_Count(0),
5045 m_DynamicArray(allocator)
5046 {
5047 }
5048 VmaSmallVector(size_t count, const AllocatorT& allocator) :
5049 m_Count(count),
5050 m_DynamicArray(count > N ? count : 0, allocator)
5051 {
5052 }
5053 template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
5054 VmaSmallVector(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>& src) = delete;
5055 template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
5056 VmaSmallVector<T, AllocatorT, N>& operator=(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>& rhs) = delete;
5057
5058 bool empty() const { return m_Count == 0; }
5059 size_t size() const { return m_Count; }
5060 T* data() { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
5061 const T* data() const { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
5062
5063 T& operator[](size_t index)
5064 {
5065 VMA_HEAVY_ASSERT(index < m_Count);
5066 return data()[index];
5067 }
5068 const T& operator[](size_t index) const
5069 {
5070 VMA_HEAVY_ASSERT(index < m_Count);
5071 return data()[index];
5072 }
5073
5074 T& front()
5075 {
5076 VMA_HEAVY_ASSERT(m_Count > 0);
5077 return data()[0];
5078 }
5079 const T& front() const
5080 {
5081 VMA_HEAVY_ASSERT(m_Count > 0);
5082 return data()[0];
5083 }
5084 T& back()
5085 {
5086 VMA_HEAVY_ASSERT(m_Count > 0);
5087 return data()[m_Count - 1];
5088 }
5089 const T& back() const
5090 {
5091 VMA_HEAVY_ASSERT(m_Count > 0);
5092 return data()[m_Count - 1];
5093 }
5094
5095 void resize(size_t newCount, bool freeMemory = false)
5096 {
5097 if (newCount > N && m_Count > N)
5098 {
5099 // Any direction, staying in m_DynamicArray
5100 m_DynamicArray.resize(newCount, freeMemory);
5101 }
5102 else if (newCount > N && m_Count <= N)
5103 {
5104 // Growing, moving from m_StaticArray to m_DynamicArray
5105 m_DynamicArray.resize(newCount, freeMemory);
5106 if (m_Count > 0)
5107 {
5108 memcpy(m_DynamicArray.data(), m_StaticArray, m_Count * sizeof(T));
5109 }
5110 }
5111 else if (newCount <= N && m_Count > N)
5112 {
5113 // Shrinking, moving from m_DynamicArray to m_StaticArray
5114 if (newCount > 0)
5115 {
5116 memcpy(m_StaticArray, m_DynamicArray.data(), newCount * sizeof(T));
5117 }
5118 m_DynamicArray.resize(0, freeMemory);
5119 }
5120 else
5121 {
5122 // Any direction, staying in m_StaticArray - nothing to do here
5123 }
5124 m_Count = newCount;
5125 }
5126
5127 void clear(bool freeMemory = false)
5128 {
5129 m_DynamicArray.clear(freeMemory);
5130 m_Count = 0;
5131 }
5132
5133 void insert(size_t index, const T& src)
5134 {
5135 VMA_HEAVY_ASSERT(index <= m_Count);
5136 const size_t oldCount = size();
5137 resize(oldCount + 1);
5138 T* const dataPtr = data();
5139 if (index < oldCount)
5140 {
5141 // I know, this could be more optimal for case where memmove can be memcpy directly from m_StaticArray to m_DynamicArray.
5142 memmove(dataPtr + (index + 1), dataPtr + index, (oldCount - index) * sizeof(T));
5143 }
5144 dataPtr[index] = src;
5145 }
5146
5147 void remove(size_t index)
5148 {
5149 VMA_HEAVY_ASSERT(index < m_Count);
5150 const size_t oldCount = size();
5151 if (index < oldCount - 1)
5152 {
5153 // I know, this could be more optimal for case where memmove can be memcpy directly from m_DynamicArray to m_StaticArray.
5154 T* const dataPtr = data();
5155 memmove(dataPtr + index, dataPtr + (index + 1), (oldCount - index - 1) * sizeof(T));
5156 }
5157 resize(oldCount - 1);
5158 }
5159
5160 void push_back(const T& src)
5161 {
5162 const size_t newIndex = size();
5163 resize(newIndex + 1);
5164 data()[newIndex] = src;
5165 }
5166
5167 void pop_back()
5168 {
5169 VMA_HEAVY_ASSERT(m_Count > 0);
5170 resize(size() - 1);
5171 }
5172
5173 void push_front(const T& src)
5174 {
5175 insert(0, src);
5176 }
5177
5178 void pop_front()
5179 {
5180 VMA_HEAVY_ASSERT(m_Count > 0);
5181 remove(0);
5182 }
5183
5184 typedef T* iterator;
5185
5186 iterator begin() { return data(); }
5187 iterator end() { return data() + m_Count; }
5188
5189 private:
5190 size_t m_Count;
5191 T m_StaticArray[N]; // Used when m_Size <= N
5192 VmaVector<T, AllocatorT> m_DynamicArray; // Used when m_Size > N
5193 };
5194
5195 ////////////////////////////////////////////////////////////////////////////////
5196 // class VmaPoolAllocator
5197
5198 /*
5199 Allocator for objects of type T using a list of arrays (pools) to speed up
5200 allocation. Number of elements that can be allocated is not bounded because
5201 allocator can create multiple blocks.
5202 */
5203 template<typename T>
5204 class VmaPoolAllocator
5205 {
5206 VMA_CLASS_NO_COPY(VmaPoolAllocator)
5207 public:
5208 VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity);
5209 ~VmaPoolAllocator();
5210 template<typename... Types> T* Alloc(Types... args);
5211 void Free(T* ptr);
5212
5213 private:
5214 union Item
5215 {
5216 uint32_t NextFreeIndex;
5217 alignas(T) char Value[sizeof(T)];
5218 };
5219
5220 struct ItemBlock
5221 {
5222 Item* pItems;
5223 uint32_t Capacity;
5224 uint32_t FirstFreeIndex;
5225 };
5226
5227 const VkAllocationCallbacks* m_pAllocationCallbacks;
5228 const uint32_t m_FirstBlockCapacity;
5229 VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
5230
5231 ItemBlock& CreateNewBlock();
5232 };
5233
5234 template<typename T>
5235 VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity) :
5236 m_pAllocationCallbacks(pAllocationCallbacks),
5237 m_FirstBlockCapacity(firstBlockCapacity),
5238 m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
5239 {
5240 VMA_ASSERT(m_FirstBlockCapacity > 1);
5241 }
5242
5243 template<typename T>
5244 VmaPoolAllocator<T>::~VmaPoolAllocator()
5245 {
5246 for (size_t i = m_ItemBlocks.size(); i--; )
5247 vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity);
5248 m_ItemBlocks.clear();
5249 }
5250
5251 template<typename T>
5252 template<typename... Types> T* VmaPoolAllocator<T>::Alloc(Types... args)
5253 {
5254 for (size_t i = m_ItemBlocks.size(); i--; )
5255 {
5256 ItemBlock& block = m_ItemBlocks[i];
5257 // This block has some free items: Use first one.
5258 if (block.FirstFreeIndex != UINT32_MAX)
5259 {
5260 Item* const pItem = &block.pItems[block.FirstFreeIndex];
5261 block.FirstFreeIndex = pItem->NextFreeIndex;
5262 T* result = (T*)&pItem->Value;
5263 new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
5264 return result;
5265 }
5266 }
5267
5268 // No block has free item: Create new one and use it.
5269 ItemBlock& newBlock = CreateNewBlock();
5270 Item* const pItem = &newBlock.pItems[0];
5271 newBlock.FirstFreeIndex = pItem->NextFreeIndex;
5272 T* result = (T*)&pItem->Value;
5273 new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
5274 return result;
5275 }
5276
5277 template<typename T>
5278 void VmaPoolAllocator<T>::Free(T* ptr)
5279 {
5280 // Search all memory blocks to find ptr.
5281 for (size_t i = m_ItemBlocks.size(); i--; )
5282 {
5283 ItemBlock& block = m_ItemBlocks[i];
5284
5285 // Casting to union.
5286 Item* pItemPtr;
5287 memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
5288
5289 // Check if pItemPtr is in address range of this block.
5290 if ((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity))
5291 {
5292 ptr->~T(); // Explicit destructor call.
5293 const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
5294 pItemPtr->NextFreeIndex = block.FirstFreeIndex;
5295 block.FirstFreeIndex = index;
5296 return;
5297 }
5298 }
5299 VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
5300 }
5301
5302 template<typename T>
5303 typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
5304 {
5305 const uint32_t newBlockCapacity = m_ItemBlocks.empty() ?
5306 m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2;
5307
5308 const ItemBlock newBlock = {
5309 vma_new_array(m_pAllocationCallbacks, Item, newBlockCapacity),
5310 newBlockCapacity,
5311 0 };
5312
5313 m_ItemBlocks.push_back(newBlock);
5314
5315 // Setup singly-linked list of all free items in this block.
5316 for (uint32_t i = 0; i < newBlockCapacity - 1; ++i)
5317 newBlock.pItems[i].NextFreeIndex = i + 1;
5318 newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX;
5319 return m_ItemBlocks.back();
5320 }
5321
5322 ////////////////////////////////////////////////////////////////////////////////
5323 // class VmaRawList, VmaList
5324
5325 #if VMA_USE_STL_LIST
5326
5327 #define VmaList std::list
5328
5329 #else // #if VMA_USE_STL_LIST
5330
5331 template<typename T>
5332 struct VmaListItem
5333 {
5334 VmaListItem* pPrev;
5335 VmaListItem* pNext;
5336 T Value;
5337 };
5338
5339 // Doubly linked list.
5340 template<typename T>
5341 class VmaRawList
5342 {
5343 VMA_CLASS_NO_COPY(VmaRawList)
5344 public:
5345 typedef VmaListItem<T> ItemType;
5346
5347 VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
5348 ~VmaRawList();
5349 void Clear();
5350
5351 size_t GetCount() const { return m_Count; }
5352 bool IsEmpty() const { return m_Count == 0; }
5353
5354 ItemType* Front() { return m_pFront; }
5355 const ItemType* Front() const { return m_pFront; }
5356 ItemType* Back() { return m_pBack; }
5357 const ItemType* Back() const { return m_pBack; }
5358
5359 ItemType* PushBack();
5360 ItemType* PushFront();
5361 ItemType* PushBack(const T& value);
5362 ItemType* PushFront(const T& value);
5363 void PopBack();
5364 void PopFront();
5365
5366 // Item can be null - it means PushBack.
5367 ItemType* InsertBefore(ItemType* pItem);
5368 // Item can be null - it means PushFront.
5369 ItemType* InsertAfter(ItemType* pItem);
5370
5371 ItemType* InsertBefore(ItemType* pItem, const T& value);
5372 ItemType* InsertAfter(ItemType* pItem, const T& value);
5373
5374 void Remove(ItemType* pItem);
5375
5376 private:
5377 const VkAllocationCallbacks* const m_pAllocationCallbacks;
5378 VmaPoolAllocator<ItemType> m_ItemAllocator;
5379 ItemType* m_pFront;
5380 ItemType* m_pBack;
5381 size_t m_Count;
5382 };
5383
5384 template<typename T>
5385 VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
5386 m_pAllocationCallbacks(pAllocationCallbacks),
5387 m_ItemAllocator(pAllocationCallbacks, 128),
5388 m_pFront(VMA_NULL),
5389 m_pBack(VMA_NULL),
5390 m_Count(0)
5391 {
5392 }
5393
5394 template<typename T>
5395 VmaRawList<T>::~VmaRawList()
5396 {
5397 // Intentionally not calling Clear, because that would be unnecessary
5398 // computations to return all items to m_ItemAllocator as free.
5399 }
5400
5401 template<typename T>
5402 void VmaRawList<T>::Clear()
5403 {
5404 if (IsEmpty() == false)
5405 {
5406 ItemType* pItem = m_pBack;
5407 while (pItem != VMA_NULL)
5408 {
5409 ItemType* const pPrevItem = pItem->pPrev;
5410 m_ItemAllocator.Free(pItem);
5411 pItem = pPrevItem;
5412 }
5413 m_pFront = VMA_NULL;
5414 m_pBack = VMA_NULL;
5415 m_Count = 0;
5416 }
5417 }
5418
5419 template<typename T>
5420 VmaListItem<T>* VmaRawList<T>::PushBack()
5421 {
5422 ItemType* const pNewItem = m_ItemAllocator.Alloc();
5423 pNewItem->pNext = VMA_NULL;
5424 if (IsEmpty())
5425 {
5426 pNewItem->pPrev = VMA_NULL;
5427 m_pFront = pNewItem;
5428 m_pBack = pNewItem;
5429 m_Count = 1;
5430 }
5431 else
5432 {
5433 pNewItem->pPrev = m_pBack;
5434 m_pBack->pNext = pNewItem;
5435 m_pBack = pNewItem;
5436 ++m_Count;
5437 }
5438 return pNewItem;
5439 }
5440
5441 template<typename T>
5442 VmaListItem<T>* VmaRawList<T>::PushFront()
5443 {
5444 ItemType* const pNewItem = m_ItemAllocator.Alloc();
5445 pNewItem->pPrev = VMA_NULL;
5446 if (IsEmpty())
5447 {
5448 pNewItem->pNext = VMA_NULL;
5449 m_pFront = pNewItem;
5450 m_pBack = pNewItem;
5451 m_Count = 1;
5452 }
5453 else
5454 {
5455 pNewItem->pNext = m_pFront;
5456 m_pFront->pPrev = pNewItem;
5457 m_pFront = pNewItem;
5458 ++m_Count;
5459 }
5460 return pNewItem;
5461 }
5462
5463 template<typename T>
5464 VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
5465 {
5466 ItemType* const pNewItem = PushBack();
5467 pNewItem->Value = value;
5468 return pNewItem;
5469 }
5470
5471 template<typename T>
5472 VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
5473 {
5474 ItemType* const pNewItem = PushFront();
5475 pNewItem->Value = value;
5476 return pNewItem;
5477 }
5478
5479 template<typename T>
5480 void VmaRawList<T>::PopBack()
5481 {
5482 VMA_HEAVY_ASSERT(m_Count > 0);
5483 ItemType* const pBackItem = m_pBack;
5484 ItemType* const pPrevItem = pBackItem->pPrev;
5485 if (pPrevItem != VMA_NULL)
5486 {
5487 pPrevItem->pNext = VMA_NULL;
5488 }
5489 m_pBack = pPrevItem;
5490 m_ItemAllocator.Free(pBackItem);
5491 --m_Count;
5492 }
5493
5494 template<typename T>
5495 void VmaRawList<T>::PopFront()
5496 {
5497 VMA_HEAVY_ASSERT(m_Count > 0);
5498 ItemType* const pFrontItem = m_pFront;
5499 ItemType* const pNextItem = pFrontItem->pNext;
5500 if (pNextItem != VMA_NULL)
5501 {
5502 pNextItem->pPrev = VMA_NULL;
5503 }
5504 m_pFront = pNextItem;
5505 m_ItemAllocator.Free(pFrontItem);
5506 --m_Count;
5507 }
5508
5509 template<typename T>
5510 void VmaRawList<T>::Remove(ItemType* pItem)
5511 {
5512 VMA_HEAVY_ASSERT(pItem != VMA_NULL);
5513 VMA_HEAVY_ASSERT(m_Count > 0);
5514
5515 if (pItem->pPrev != VMA_NULL)
5516 {
5517 pItem->pPrev->pNext = pItem->pNext;
5518 }
5519 else
5520 {
5521 VMA_HEAVY_ASSERT(m_pFront == pItem);
5522 m_pFront = pItem->pNext;
5523 }
5524
5525 if (pItem->pNext != VMA_NULL)
5526 {
5527 pItem->pNext->pPrev = pItem->pPrev;
5528 }
5529 else
5530 {
5531 VMA_HEAVY_ASSERT(m_pBack == pItem);
5532 m_pBack = pItem->pPrev;
5533 }
5534
5535 m_ItemAllocator.Free(pItem);
5536 --m_Count;
5537 }
5538
5539 template<typename T>
5540 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
5541 {
5542 if (pItem != VMA_NULL)
5543 {
5544 ItemType* const prevItem = pItem->pPrev;
5545 ItemType* const newItem = m_ItemAllocator.Alloc();
5546 newItem->pPrev = prevItem;
5547 newItem->pNext = pItem;
5548 pItem->pPrev = newItem;
5549 if (prevItem != VMA_NULL)
5550 {
5551 prevItem->pNext = newItem;
5552 }
5553 else
5554 {
5555 VMA_HEAVY_ASSERT(m_pFront == pItem);
5556 m_pFront = newItem;
5557 }
5558 ++m_Count;
5559 return newItem;
5560 }
5561 else
5562 return PushBack();
5563 }
5564
5565 template<typename T>
5566 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
5567 {
5568 if (pItem != VMA_NULL)
5569 {
5570 ItemType* const nextItem = pItem->pNext;
5571 ItemType* const newItem = m_ItemAllocator.Alloc();
5572 newItem->pNext = nextItem;
5573 newItem->pPrev = pItem;
5574 pItem->pNext = newItem;
5575 if (nextItem != VMA_NULL)
5576 {
5577 nextItem->pPrev = newItem;
5578 }
5579 else
5580 {
5581 VMA_HEAVY_ASSERT(m_pBack == pItem);
5582 m_pBack = newItem;
5583 }
5584 ++m_Count;
5585 return newItem;
5586 }
5587 else
5588 return PushFront();
5589 }
5590
5591 template<typename T>
5592 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
5593 {
5594 ItemType* const newItem = InsertBefore(pItem);
5595 newItem->Value = value;
5596 return newItem;
5597 }
5598
5599 template<typename T>
5600 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
5601 {
5602 ItemType* const newItem = InsertAfter(pItem);
5603 newItem->Value = value;
5604 return newItem;
5605 }
5606
5607 template<typename T, typename AllocatorT>
5608 class VmaList
5609 {
5610 VMA_CLASS_NO_COPY(VmaList)
5611 public:
5612 class iterator
5613 {
5614 public:
5615 iterator() :
5616 m_pList(VMA_NULL),
5617 m_pItem(VMA_NULL)
5618 {
5619 }
5620
5621 T& operator*() const
5622 {
5623 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5624 return m_pItem->Value;
5625 }
5626 T* operator->() const
5627 {
5628 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5629 return &m_pItem->Value;
5630 }
5631
5632 iterator& operator++()
5633 {
5634 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5635 m_pItem = m_pItem->pNext;
5636 return *this;
5637 }
5638 iterator& operator--()
5639 {
5640 if (m_pItem != VMA_NULL)
5641 {
5642 m_pItem = m_pItem->pPrev;
5643 }
5644 else
5645 {
5646 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
5647 m_pItem = m_pList->Back();
5648 }
5649 return *this;
5650 }
5651
5652 iterator operator++(int)
5653 {
5654 iterator result = *this;
5655 ++* this;
5656 return result;
5657 }
5658 iterator operator--(int)
5659 {
5660 iterator result = *this;
5661 --* this;
5662 return result;
5663 }
5664
5665 bool operator==(const iterator& rhs) const
5666 {
5667 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5668 return m_pItem == rhs.m_pItem;
5669 }
5670 bool operator!=(const iterator& rhs) const
5671 {
5672 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5673 return m_pItem != rhs.m_pItem;
5674 }
5675
5676 private:
5677 VmaRawList<T>* m_pList;
5678 VmaListItem<T>* m_pItem;
5679
5680 iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
5681 m_pList(pList),
5682 m_pItem(pItem)
5683 {
5684 }
5685
5686 friend class VmaList<T, AllocatorT>;
5687 };
5688
5689 class const_iterator
5690 {
5691 public:
5692 const_iterator() :
5693 m_pList(VMA_NULL),
5694 m_pItem(VMA_NULL)
5695 {
5696 }
5697
5698 const_iterator(const iterator& src) :
5699 m_pList(src.m_pList),
5700 m_pItem(src.m_pItem)
5701 {
5702 }
5703
5704 const T& operator*() const
5705 {
5706 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5707 return m_pItem->Value;
5708 }
5709 const T* operator->() const
5710 {
5711 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5712 return &m_pItem->Value;
5713 }
5714
5715 const_iterator& operator++()
5716 {
5717 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5718 m_pItem = m_pItem->pNext;
5719 return *this;
5720 }
5721 const_iterator& operator--()
5722 {
5723 if (m_pItem != VMA_NULL)
5724 {
5725 m_pItem = m_pItem->pPrev;
5726 }
5727 else
5728 {
5729 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
5730 m_pItem = m_pList->Back();
5731 }
5732 return *this;
5733 }
5734
5735 const_iterator operator++(int)
5736 {
5737 const_iterator result = *this;
5738 ++* this;
5739 return result;
5740 }
5741 const_iterator operator--(int)
5742 {
5743 const_iterator result = *this;
5744 --* this;
5745 return result;
5746 }
5747
5748 bool operator==(const const_iterator& rhs) const
5749 {
5750 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5751 return m_pItem == rhs.m_pItem;
5752 }
5753 bool operator!=(const const_iterator& rhs) const
5754 {
5755 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5756 return m_pItem != rhs.m_pItem;
5757 }
5758
5759 private:
5760 const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
5761 m_pList(pList),
5762 m_pItem(pItem)
5763 {
5764 }
5765
5766 const VmaRawList<T>* m_pList;
5767 const VmaListItem<T>* m_pItem;
5768
5769 friend class VmaList<T, AllocatorT>;
5770 };
5771
5772 VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
5773
5774 bool empty() const { return m_RawList.IsEmpty(); }
5775 size_t size() const { return m_RawList.GetCount(); }
5776
5777 iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
5778 iterator end() { return iterator(&m_RawList, VMA_NULL); }
5779
5780 const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
5781 const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
5782
5783 void clear() { m_RawList.Clear(); }
5784 void push_back(const T& value) { m_RawList.PushBack(value); }
5785 void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
5786 iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
5787
5788 private:
5789 VmaRawList<T> m_RawList;
5790 };
5791
5792 #endif // #if VMA_USE_STL_LIST
5793
5794 ////////////////////////////////////////////////////////////////////////////////
5795 // class VmaMap
5796
5797 // Unused in this version.
5798 #if 0
5799
5800 #if VMA_USE_STL_UNORDERED_MAP
5801
5802 #define VmaPair std::pair
5803
5804 #define VMA_MAP_TYPE(KeyT, ValueT) \
5805 std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
5806
5807 #else // #if VMA_USE_STL_UNORDERED_MAP
5808
5809 template<typename T1, typename T2>
5810 struct VmaPair
5811 {
5812 T1 first;
5813 T2 second;
5814
5815 VmaPair() : first(), second() { }
5816 VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
5817 };
5818
5819 /* Class compatible with subset of interface of std::unordered_map.
5820 KeyT, ValueT must be POD because they will be stored in VmaVector.
5821 */
5822 template<typename KeyT, typename ValueT>
5823 class VmaMap
5824 {
5825 public:
5826 typedef VmaPair<KeyT, ValueT> PairType;
5827 typedef PairType* iterator;
5828
5829 VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
5830
5831 iterator begin() { return m_Vector.begin(); }
5832 iterator end() { return m_Vector.end(); }
5833
5834 void insert(const PairType& pair);
5835 iterator find(const KeyT& key);
5836 void erase(iterator it);
5837
5838 private:
5839 VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
5840 };
5841
5842 #define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
5843
5844 template<typename FirstT, typename SecondT>
5845 struct VmaPairFirstLess
5846 {
5847 bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
5848 {
5849 return lhs.first < rhs.first;
5850 }
5851 bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
5852 {
5853 return lhs.first < rhsFirst;
5854 }
5855 };
5856
5857 template<typename KeyT, typename ValueT>
5858 void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
5859 {
5860 const size_t indexToInsert = VmaBinaryFindFirstNotLess(
5861 m_Vector.data(),
5862 m_Vector.data() + m_Vector.size(),
5863 pair,
5864 VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
5865 VmaVectorInsert(m_Vector, indexToInsert, pair);
5866 }
5867
5868 template<typename KeyT, typename ValueT>
5869 VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
5870 {
5871 PairType* it = VmaBinaryFindFirstNotLess(
5872 m_Vector.data(),
5873 m_Vector.data() + m_Vector.size(),
5874 key,
5875 VmaPairFirstLess<KeyT, ValueT>());
5876 if ((it != m_Vector.end()) && (it->first == key))
5877 {
5878 return it;
5879 }
5880 else
5881 {
5882 return m_Vector.end();
5883 }
5884 }
5885
5886 template<typename KeyT, typename ValueT>
5887 void VmaMap<KeyT, ValueT>::erase(iterator it)
5888 {
5889 VmaVectorRemove(m_Vector, it - m_Vector.begin());
5890 }
5891
5892 #endif // #if VMA_USE_STL_UNORDERED_MAP
5893
5894 #endif // #if 0
5895
5896 ////////////////////////////////////////////////////////////////////////////////
5897
5898 class VmaDeviceMemoryBlock;
5899
5900 enum VMA_CACHE_OPERATION { VMA_CACHE_FLUSH, VMA_CACHE_INVALIDATE };
5901
5902 struct VmaAllocation_T
5903 {
5904 private:
5905 static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
5906
5907 enum FLAGS
5908 {
5909 FLAG_USER_DATA_STRING = 0x01,
5910 };
5911
5912 public:
5913 enum ALLOCATION_TYPE
5914 {
5915 ALLOCATION_TYPE_NONE,
5916 ALLOCATION_TYPE_BLOCK,
5917 ALLOCATION_TYPE_DEDICATED,
5918 };
5919
5920 /*
5921 This struct is allocated using VmaPoolAllocator.
5922 */
5923
5924 VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
5925 m_Alignment{ 1 },
5926 m_Size{ 0 },
5927 m_pUserData{ VMA_NULL },
5928 m_LastUseFrameIndex{ currentFrameIndex },
5929 m_MemoryTypeIndex{ 0 },
5930 m_Type{ (uint8_t)ALLOCATION_TYPE_NONE },
5931 m_SuballocationType{ (uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN },
5932 m_MapCount{ 0 },
5933 m_Flags{ userDataString ? (uint8_t)FLAG_USER_DATA_STRING : (uint8_t)0 }
5934 {
5935 #if VMA_STATS_STRING_ENABLED
5936 m_CreationFrameIndex = currentFrameIndex;
5937 m_BufferImageUsage = 0;
5938 #endif
5939 }
5940
5941 ~VmaAllocation_T()
5942 {
5943 VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
5944
5945 // Check if owned string was freed.
5946 VMA_ASSERT(m_pUserData == VMA_NULL);
5947 }
5948
5949 void InitBlockAllocation(
5950 VmaDeviceMemoryBlock* block,
5951 VkDeviceSize offset,
5952 VkDeviceSize alignment,
5953 VkDeviceSize size,
5954 uint32_t memoryTypeIndex,
5955 VmaSuballocationType suballocationType,
5956 bool mapped,
5957 bool canBecomeLost)
5958 {
5959 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
5960 VMA_ASSERT(block != VMA_NULL);
5961 m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
5962 m_Alignment = alignment;
5963 m_Size = size;
5964 m_MemoryTypeIndex = memoryTypeIndex;
5965 m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
5966 m_SuballocationType = (uint8_t)suballocationType;
5967 m_BlockAllocation.m_Block = block;
5968 m_BlockAllocation.m_Offset = offset;
5969 m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
5970 }
5971
5972 void InitLost()
5973 {
5974 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
5975 VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
5976 m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
5977 m_MemoryTypeIndex = 0;
5978 m_BlockAllocation.m_Block = VMA_NULL;
5979 m_BlockAllocation.m_Offset = 0;
5980 m_BlockAllocation.m_CanBecomeLost = true;
5981 }
5982
5983 void ChangeBlockAllocation(
5984 VmaAllocator hAllocator,
5985 VmaDeviceMemoryBlock* block,
5986 VkDeviceSize offset);
5987
5988 void ChangeOffset(VkDeviceSize newOffset);
5989
5990 // pMappedData not null means allocation is created with MAPPED flag.
5991 void InitDedicatedAllocation(
5992 uint32_t memoryTypeIndex,
5993 VkDeviceMemory hMemory,
5994 VmaSuballocationType suballocationType,
5995 void* pMappedData,
5996 VkDeviceSize size)
5997 {
5998 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
5999 VMA_ASSERT(hMemory != VK_NULL_HANDLE);
6000 m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
6001 m_Alignment = 0;
6002 m_Size = size;
6003 m_MemoryTypeIndex = memoryTypeIndex;
6004 m_SuballocationType = (uint8_t)suballocationType;
6005 m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
6006 m_DedicatedAllocation.m_hMemory = hMemory;
6007 m_DedicatedAllocation.m_pMappedData = pMappedData;
6008 }
6009
6010 ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
6011 VkDeviceSize GetAlignment() const { return m_Alignment; }
6012 VkDeviceSize GetSize() const { return m_Size; }
6013 bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
6014 void* GetUserData() const { return m_pUserData; }
6015 void SetUserData(VmaAllocator hAllocator, void* pUserData);
6016 VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
6017
6018 VmaDeviceMemoryBlock* GetBlock() const
6019 {
6020 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
6021 return m_BlockAllocation.m_Block;
6022 }
6023 VkDeviceSize GetOffset() const;
6024 VkDeviceMemory GetMemory() const;
6025 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
6026 bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
6027 void* GetMappedData() const;
6028 bool CanBecomeLost() const;
6029
6030 uint32_t GetLastUseFrameIndex() const
6031 {
6032 return m_LastUseFrameIndex.load();
6033 }
6034 bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
6035 {
6036 return m_LastUseFrameIndex.compare_exchange_weak(expected, desired);
6037 }
6038 /*
6039 - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
6040 makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
6041 - Else, returns false.
6042
6043 If hAllocation is already lost, assert - you should not call it then.
6044 If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
6045 */
6046 bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6047
6048 void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
6049 {
6050 VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
6051 outInfo.blockCount = 1;
6052 outInfo.allocationCount = 1;
6053 outInfo.unusedRangeCount = 0;
6054 outInfo.usedBytes = m_Size;
6055 outInfo.unusedBytes = 0;
6056 outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
6057 outInfo.unusedRangeSizeMin = UINT64_MAX;
6058 outInfo.unusedRangeSizeMax = 0;
6059 }
6060
6061 void BlockAllocMap();
6062 void BlockAllocUnmap();
6063 VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
6064 void DedicatedAllocUnmap(VmaAllocator hAllocator);
6065
6066 #if VMA_STATS_STRING_ENABLED
6067 uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; }
6068 uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
6069
6070 void InitBufferImageUsage(uint32_t bufferImageUsage)
6071 {
6072 VMA_ASSERT(m_BufferImageUsage == 0);
6073 m_BufferImageUsage = bufferImageUsage;
6074 }
6075
6076 void PrintParameters(class VmaJsonWriter& json) const;
6077 #endif
6078
6079 private:
6080 VkDeviceSize m_Alignment;
6081 VkDeviceSize m_Size;
6082 void* m_pUserData;
6083 VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
6084 uint32_t m_MemoryTypeIndex;
6085 uint8_t m_Type; // ALLOCATION_TYPE
6086 uint8_t m_SuballocationType; // VmaSuballocationType
6087 // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
6088 // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
6089 uint8_t m_MapCount;
6090 uint8_t m_Flags; // enum FLAGS
6091
6092 // Allocation out of VmaDeviceMemoryBlock.
6093 struct BlockAllocation
6094 {
6095 VmaDeviceMemoryBlock* m_Block;
6096 VkDeviceSize m_Offset;
6097 bool m_CanBecomeLost;
6098 };
6099
6100 // Allocation for an object that has its own private VkDeviceMemory.
6101 struct DedicatedAllocation
6102 {
6103 VkDeviceMemory m_hMemory;
6104 void* m_pMappedData; // Not null means memory is mapped.
6105 };
6106
6107 union
6108 {
6109 // Allocation out of VmaDeviceMemoryBlock.
6110 BlockAllocation m_BlockAllocation;
6111 // Allocation for an object that has its own private VkDeviceMemory.
6112 DedicatedAllocation m_DedicatedAllocation;
6113 };
6114
6115 #if VMA_STATS_STRING_ENABLED
6116 uint32_t m_CreationFrameIndex;
6117 uint32_t m_BufferImageUsage; // 0 if unknown.
6118 #endif
6119
6120 void FreeUserDataString(VmaAllocator hAllocator);
6121 };
6122
6123 /*
6124 Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
6125 allocated memory block or free.
6126 */
6127 struct VmaSuballocation
6128 {
6129 VkDeviceSize offset;
6130 VkDeviceSize size;
6131 VmaAllocation hAllocation;
6132 VmaSuballocationType type;
6133 };
6134
6135 // Comparator for offsets.
6136 struct VmaSuballocationOffsetLess
6137 {
6138 bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6139 {
6140 return lhs.offset < rhs.offset;
6141 }
6142 };
6143 struct VmaSuballocationOffsetGreater
6144 {
6145 bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6146 {
6147 return lhs.offset > rhs.offset;
6148 }
6149 };
6150
6151 typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
6152
6153 // Cost of one additional allocation lost, as equivalent in bytes.
6154 static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
6155
6156 enum class VmaAllocationRequestType
6157 {
6158 Normal,
6159 // Used by "Linear" algorithm.
6160 UpperAddress,
6161 EndOf1st,
6162 EndOf2nd,
6163 };
6164
6165 /*
6166 Parameters of planned allocation inside a VmaDeviceMemoryBlock.
6167
6168 If canMakeOtherLost was false:
6169 - item points to a FREE suballocation.
6170 - itemsToMakeLostCount is 0.
6171
6172 If canMakeOtherLost was true:
6173 - item points to first of sequence of suballocations, which are either FREE,
6174 or point to VmaAllocations that can become lost.
6175 - itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
6176 the requested allocation to succeed.
6177 */
6178 struct VmaAllocationRequest
6179 {
6180 VkDeviceSize offset;
6181 VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
6182 VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
6183 VmaSuballocationList::iterator item;
6184 size_t itemsToMakeLostCount;
6185 void* customData;
6186 VmaAllocationRequestType type;
6187
6188 VkDeviceSize CalcCost() const
6189 {
6190 return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
6191 }
6192 };
6193
6194 /*
6195 Data structure used for bookkeeping of allocations and unused ranges of memory
6196 in a single VkDeviceMemory block.
6197 */
6198 class VmaBlockMetadata
6199 {
6200 public:
6201 VmaBlockMetadata(VmaAllocator hAllocator);
6202 virtual ~VmaBlockMetadata() { }
6203 virtual void Init(VkDeviceSize size) { m_Size = size; }
6204
6205 // Validates all data structures inside this object. If not valid, returns false.
6206 virtual bool Validate() const = 0;
6207 VkDeviceSize GetSize() const { return m_Size; }
6208 virtual size_t GetAllocationCount() const = 0;
6209 virtual VkDeviceSize GetSumFreeSize() const = 0;
6210 virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0;
6211 // Returns true if this block is empty - contains only single free suballocation.
6212 virtual bool IsEmpty() const = 0;
6213
6214 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
6215 // Shouldn't modify blockCount.
6216 virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
6217
6218 #if VMA_STATS_STRING_ENABLED
6219 virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
6220 #endif
6221
6222 // Tries to find a place for suballocation with given parameters inside this block.
6223 // If succeeded, fills pAllocationRequest and returns true.
6224 // If failed, returns false.
6225 virtual bool CreateAllocationRequest(
6226 uint32_t currentFrameIndex,
6227 uint32_t frameInUseCount,
6228 VkDeviceSize bufferImageGranularity,
6229 VkDeviceSize allocSize,
6230 VkDeviceSize allocAlignment,
6231 bool upperAddress,
6232 VmaSuballocationType allocType,
6233 bool canMakeOtherLost,
6234 // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
6235 uint32_t strategy,
6236 VmaAllocationRequest* pAllocationRequest) = 0;
6237
6238 virtual bool MakeRequestedAllocationsLost(
6239 uint32_t currentFrameIndex,
6240 uint32_t frameInUseCount,
6241 VmaAllocationRequest* pAllocationRequest) = 0;
6242
6243 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0;
6244
6245 virtual VkResult CheckCorruption(const void* pBlockData) = 0;
6246
6247 // Makes actual allocation based on request. Request must already be checked and valid.
6248 virtual void Alloc(
6249 const VmaAllocationRequest& request,
6250 VmaSuballocationType type,
6251 VkDeviceSize allocSize,
6252 VmaAllocation hAllocation) = 0;
6253
6254 // Frees suballocation assigned to given memory region.
6255 virtual void Free(const VmaAllocation allocation) = 0;
6256 virtual void FreeAtOffset(VkDeviceSize offset) = 0;
6257
6258 protected:
6259 const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
6260
6261 #if VMA_STATS_STRING_ENABLED
6262 void PrintDetailedMap_Begin(class VmaJsonWriter& json,
6263 VkDeviceSize unusedBytes,
6264 size_t allocationCount,
6265 size_t unusedRangeCount) const;
6266 void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
6267 VkDeviceSize offset,
6268 VmaAllocation hAllocation) const;
6269 void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
6270 VkDeviceSize offset,
6271 VkDeviceSize size) const;
6272 void PrintDetailedMap_End(class VmaJsonWriter& json) const;
6273 #endif
6274
6275 private:
6276 VkDeviceSize m_Size;
6277 const VkAllocationCallbacks* m_pAllocationCallbacks;
6278 };
6279
6280 #define VMA_VALIDATE(cond) do { if(!(cond)) { \
6281 VMA_ASSERT(0 && "Validation failed: " #cond); \
6282 return false; \
6283 } } while(false)
6284
6285 class VmaBlockMetadata_Generic : public VmaBlockMetadata
6286 {
6287 VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
6288 public:
6289 VmaBlockMetadata_Generic(VmaAllocator hAllocator);
6290 virtual ~VmaBlockMetadata_Generic();
6291 virtual void Init(VkDeviceSize size);
6292
6293 virtual bool Validate() const;
6294 virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
6295 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
6296 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
6297 virtual bool IsEmpty() const;
6298
6299 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6300 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6301
6302 #if VMA_STATS_STRING_ENABLED
6303 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6304 #endif
6305
6306 virtual bool CreateAllocationRequest(
6307 uint32_t currentFrameIndex,
6308 uint32_t frameInUseCount,
6309 VkDeviceSize bufferImageGranularity,
6310 VkDeviceSize allocSize,
6311 VkDeviceSize allocAlignment,
6312 bool upperAddress,
6313 VmaSuballocationType allocType,
6314 bool canMakeOtherLost,
6315 uint32_t strategy,
6316 VmaAllocationRequest* pAllocationRequest);
6317
6318 virtual bool MakeRequestedAllocationsLost(
6319 uint32_t currentFrameIndex,
6320 uint32_t frameInUseCount,
6321 VmaAllocationRequest* pAllocationRequest);
6322
6323 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6324
6325 virtual VkResult CheckCorruption(const void* pBlockData);
6326
6327 virtual void Alloc(
6328 const VmaAllocationRequest& request,
6329 VmaSuballocationType type,
6330 VkDeviceSize allocSize,
6331 VmaAllocation hAllocation);
6332
6333 virtual void Free(const VmaAllocation allocation);
6334 virtual void FreeAtOffset(VkDeviceSize offset);
6335
6336 ////////////////////////////////////////////////////////////////////////////////
6337 // For defragmentation
6338
6339 bool IsBufferImageGranularityConflictPossible(
6340 VkDeviceSize bufferImageGranularity,
6341 VmaSuballocationType& inOutPrevSuballocType) const;
6342
6343 private:
6344 friend class VmaDefragmentationAlgorithm_Generic;
6345 friend class VmaDefragmentationAlgorithm_Fast;
6346
6347 uint32_t m_FreeCount;
6348 VkDeviceSize m_SumFreeSize;
6349 VmaSuballocationList m_Suballocations;
6350 // Suballocations that are free and have size greater than certain threshold.
6351 // Sorted by size, ascending.
6352 VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
6353
6354 bool ValidateFreeSuballocationList() const;
6355
6356 // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
6357 // If yes, fills pOffset and returns true. If no, returns false.
6358 bool CheckAllocation(
6359 uint32_t currentFrameIndex,
6360 uint32_t frameInUseCount,
6361 VkDeviceSize bufferImageGranularity,
6362 VkDeviceSize allocSize,
6363 VkDeviceSize allocAlignment,
6364 VmaSuballocationType allocType,
6365 VmaSuballocationList::const_iterator suballocItem,
6366 bool canMakeOtherLost,
6367 VkDeviceSize* pOffset,
6368 size_t* itemsToMakeLostCount,
6369 VkDeviceSize* pSumFreeSize,
6370 VkDeviceSize* pSumItemSize) const;
6371 // Given free suballocation, it merges it with following one, which must also be free.
6372 void MergeFreeWithNext(VmaSuballocationList::iterator item);
6373 // Releases given suballocation, making it free.
6374 // Merges it with adjacent free suballocations if applicable.
6375 // Returns iterator to new free suballocation at this place.
6376 VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
6377 // Given free suballocation, it inserts it into sorted list of
6378 // m_FreeSuballocationsBySize if it's suitable.
6379 void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
6380 // Given free suballocation, it removes it from sorted list of
6381 // m_FreeSuballocationsBySize if it's suitable.
6382 void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
6383 };
6384
6385 /*
6386 Allocations and their references in internal data structure look like this:
6387
6388 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
6389
6390 0 +-------+
6391 | |
6392 | |
6393 | |
6394 +-------+
6395 | Alloc | 1st[m_1stNullItemsBeginCount]
6396 +-------+
6397 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
6398 +-------+
6399 | ... |
6400 +-------+
6401 | Alloc | 1st[1st.size() - 1]
6402 +-------+
6403 | |
6404 | |
6405 | |
6406 GetSize() +-------+
6407
6408 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
6409
6410 0 +-------+
6411 | Alloc | 2nd[0]
6412 +-------+
6413 | Alloc | 2nd[1]
6414 +-------+
6415 | ... |
6416 +-------+
6417 | Alloc | 2nd[2nd.size() - 1]
6418 +-------+
6419 | |
6420 | |
6421 | |
6422 +-------+
6423 | Alloc | 1st[m_1stNullItemsBeginCount]
6424 +-------+
6425 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
6426 +-------+
6427 | ... |
6428 +-------+
6429 | Alloc | 1st[1st.size() - 1]
6430 +-------+
6431 | |
6432 GetSize() +-------+
6433
6434 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
6435
6436 0 +-------+
6437 | |
6438 | |
6439 | |
6440 +-------+
6441 | Alloc | 1st[m_1stNullItemsBeginCount]
6442 +-------+
6443 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
6444 +-------+
6445 | ... |
6446 +-------+
6447 | Alloc | 1st[1st.size() - 1]
6448 +-------+
6449 | |
6450 | |
6451 | |
6452 +-------+
6453 | Alloc | 2nd[2nd.size() - 1]
6454 +-------+
6455 | ... |
6456 +-------+
6457 | Alloc | 2nd[1]
6458 +-------+
6459 | Alloc | 2nd[0]
6460 GetSize() +-------+
6461
6462 */
6463 class VmaBlockMetadata_Linear : public VmaBlockMetadata
6464 {
6465 VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
6466 public:
6467 VmaBlockMetadata_Linear(VmaAllocator hAllocator);
6468 virtual ~VmaBlockMetadata_Linear();
6469 virtual void Init(VkDeviceSize size);
6470
6471 virtual bool Validate() const;
6472 virtual size_t GetAllocationCount() const;
6473 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
6474 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
6475 virtual bool IsEmpty() const { return GetAllocationCount() == 0; }
6476
6477 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6478 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6479
6480 #if VMA_STATS_STRING_ENABLED
6481 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6482 #endif
6483
6484 virtual bool CreateAllocationRequest(
6485 uint32_t currentFrameIndex,
6486 uint32_t frameInUseCount,
6487 VkDeviceSize bufferImageGranularity,
6488 VkDeviceSize allocSize,
6489 VkDeviceSize allocAlignment,
6490 bool upperAddress,
6491 VmaSuballocationType allocType,
6492 bool canMakeOtherLost,
6493 uint32_t strategy,
6494 VmaAllocationRequest* pAllocationRequest);
6495
6496 virtual bool MakeRequestedAllocationsLost(
6497 uint32_t currentFrameIndex,
6498 uint32_t frameInUseCount,
6499 VmaAllocationRequest* pAllocationRequest);
6500
6501 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6502
6503 virtual VkResult CheckCorruption(const void* pBlockData);
6504
6505 virtual void Alloc(
6506 const VmaAllocationRequest& request,
6507 VmaSuballocationType type,
6508 VkDeviceSize allocSize,
6509 VmaAllocation hAllocation);
6510
6511 virtual void Free(const VmaAllocation allocation);
6512 virtual void FreeAtOffset(VkDeviceSize offset);
6513
6514 private:
6515 /*
6516 There are two suballocation vectors, used in ping-pong way.
6517 The one with index m_1stVectorIndex is called 1st.
6518 The one with index (m_1stVectorIndex ^ 1) is called 2nd.
6519 2nd can be non-empty only when 1st is not empty.
6520 When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
6521 */
6522 typedef VmaVector< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > SuballocationVectorType;
6523
6524 enum SECOND_VECTOR_MODE
6525 {
6526 SECOND_VECTOR_EMPTY,
6527 /*
6528 Suballocations in 2nd vector are created later than the ones in 1st, but they
6529 all have smaller offset.
6530 */
6531 SECOND_VECTOR_RING_BUFFER,
6532 /*
6533 Suballocations in 2nd vector are upper side of double stack.
6534 They all have offsets higher than those in 1st vector.
6535 Top of this stack means smaller offsets, but higher indices in this vector.
6536 */
6537 SECOND_VECTOR_DOUBLE_STACK,
6538 };
6539
6540 VkDeviceSize m_SumFreeSize;
6541 SuballocationVectorType m_Suballocations0, m_Suballocations1;
6542 uint32_t m_1stVectorIndex;
6543 SECOND_VECTOR_MODE m_2ndVectorMode;
6544
6545 SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
6546 SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
6547 const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
6548 const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
6549
6550 // Number of items in 1st vector with hAllocation = null at the beginning.
6551 size_t m_1stNullItemsBeginCount;
6552 // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
6553 size_t m_1stNullItemsMiddleCount;
6554 // Number of items in 2nd vector with hAllocation = null.
6555 size_t m_2ndNullItemsCount;
6556
6557 bool ShouldCompact1st() const;
6558 void CleanupAfterFree();
6559
6560 bool CreateAllocationRequest_LowerAddress(
6561 uint32_t currentFrameIndex,
6562 uint32_t frameInUseCount,
6563 VkDeviceSize bufferImageGranularity,
6564 VkDeviceSize allocSize,
6565 VkDeviceSize allocAlignment,
6566 VmaSuballocationType allocType,
6567 bool canMakeOtherLost,
6568 uint32_t strategy,
6569 VmaAllocationRequest* pAllocationRequest);
6570 bool CreateAllocationRequest_UpperAddress(
6571 uint32_t currentFrameIndex,
6572 uint32_t frameInUseCount,
6573 VkDeviceSize bufferImageGranularity,
6574 VkDeviceSize allocSize,
6575 VkDeviceSize allocAlignment,
6576 VmaSuballocationType allocType,
6577 bool canMakeOtherLost,
6578 uint32_t strategy,
6579 VmaAllocationRequest* pAllocationRequest);
6580 };
6581
6582 /*
6583 - GetSize() is the original size of allocated memory block.
6584 - m_UsableSize is this size aligned down to a power of two.
6585 All allocations and calculations happen relative to m_UsableSize.
6586 - GetUnusableSize() is the difference between them.
6587 It is repoted as separate, unused range, not available for allocations.
6588
6589 Node at level 0 has size = m_UsableSize.
6590 Each next level contains nodes with size 2 times smaller than current level.
6591 m_LevelCount is the maximum number of levels to use in the current object.
6592 */
6593 class VmaBlockMetadata_Buddy : public VmaBlockMetadata
6594 {
6595 VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
6596 public:
6597 VmaBlockMetadata_Buddy(VmaAllocator hAllocator);
6598 virtual ~VmaBlockMetadata_Buddy();
6599 virtual void Init(VkDeviceSize size);
6600
6601 virtual bool Validate() const;
6602 virtual size_t GetAllocationCount() const { return m_AllocationCount; }
6603 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); }
6604 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
6605 virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; }
6606
6607 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6608 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6609
6610 #if VMA_STATS_STRING_ENABLED
6611 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6612 #endif
6613
6614 virtual bool CreateAllocationRequest(
6615 uint32_t currentFrameIndex,
6616 uint32_t frameInUseCount,
6617 VkDeviceSize bufferImageGranularity,
6618 VkDeviceSize allocSize,
6619 VkDeviceSize allocAlignment,
6620 bool upperAddress,
6621 VmaSuballocationType allocType,
6622 bool canMakeOtherLost,
6623 uint32_t strategy,
6624 VmaAllocationRequest* pAllocationRequest);
6625
6626 virtual bool MakeRequestedAllocationsLost(
6627 uint32_t currentFrameIndex,
6628 uint32_t frameInUseCount,
6629 VmaAllocationRequest* pAllocationRequest);
6630
6631 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6632
6633 virtual VkResult CheckCorruption(const void* pBlockData) { return VK_ERROR_FEATURE_NOT_PRESENT; }
6634
6635 virtual void Alloc(
6636 const VmaAllocationRequest& request,
6637 VmaSuballocationType type,
6638 VkDeviceSize allocSize,
6639 VmaAllocation hAllocation);
6640
6641 virtual void Free(const VmaAllocation allocation) { FreeAtOffset(allocation, allocation->GetOffset()); }
6642 virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VMA_NULL, offset); }
6643
6644 private:
6645 static const VkDeviceSize MIN_NODE_SIZE = 32;
6646 static const size_t MAX_LEVELS = 30;
6647
6648 struct ValidationContext
6649 {
6650 size_t calculatedAllocationCount;
6651 size_t calculatedFreeCount;
6652 VkDeviceSize calculatedSumFreeSize;
6653
6654 ValidationContext() :
6655 calculatedAllocationCount(0),
6656 calculatedFreeCount(0),
6657 calculatedSumFreeSize(0) { }
6658 };
6659
6660 struct Node
6661 {
6662 VkDeviceSize offset;
6663 enum TYPE
6664 {
6665 TYPE_FREE,
6666 TYPE_ALLOCATION,
6667 TYPE_SPLIT,
6668 TYPE_COUNT
6669 } type;
6670 Node* parent;
6671 Node* buddy;
6672
6673 union
6674 {
6675 struct
6676 {
6677 Node* prev;
6678 Node* next;
6679 } free;
6680 struct
6681 {
6682 VmaAllocation alloc;
6683 } allocation;
6684 struct
6685 {
6686 Node* leftChild;
6687 } split;
6688 };
6689 };
6690
6691 // Size of the memory block aligned down to a power of two.
6692 VkDeviceSize m_UsableSize;
6693 uint32_t m_LevelCount;
6694
6695 Node* m_Root;
6696 struct {
6697 Node* front;
6698 Node* back;
6699 } m_FreeList[MAX_LEVELS];
6700 // Number of nodes in the tree with type == TYPE_ALLOCATION.
6701 size_t m_AllocationCount;
6702 // Number of nodes in the tree with type == TYPE_FREE.
6703 size_t m_FreeCount;
6704 // This includes space wasted due to internal fragmentation. Doesn't include unusable size.
6705 VkDeviceSize m_SumFreeSize;
6706
6707 VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
6708 void DeleteNode(Node* node);
6709 bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
6710 uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
6711 inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
6712 // Alloc passed just for validation. Can be null.
6713 void FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset);
6714 void CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const;
6715 // Adds node to the front of FreeList at given level.
6716 // node->type must be FREE.
6717 // node->free.prev, next can be undefined.
6718 void AddToFreeListFront(uint32_t level, Node* node);
6719 // Removes node from FreeList at given level.
6720 // node->type must be FREE.
6721 // node->free.prev, next stay untouched.
6722 void RemoveFromFreeList(uint32_t level, Node* node);
6723
6724 #if VMA_STATS_STRING_ENABLED
6725 void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
6726 #endif
6727 };
6728
6729 /*
6730 Represents a single block of device memory (`VkDeviceMemory`) with all the
6731 data about its regions (aka suballocations, #VmaAllocation), assigned and free.
6732
6733 Thread-safety: This class must be externally synchronized.
6734 */
6735 class VmaDeviceMemoryBlock
6736 {
6737 VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
6738 public:
6739 VmaBlockMetadata* m_pMetadata;
6740
6741 VmaDeviceMemoryBlock(VmaAllocator hAllocator);
6742
6743 ~VmaDeviceMemoryBlock()
6744 {
6745 VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
6746 VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
6747 }
6748
6749 // Always call after construction.
6750 void Init(
6751 VmaAllocator hAllocator,
6752 VmaPool hParentPool,
6753 uint32_t newMemoryTypeIndex,
6754 VkDeviceMemory newMemory,
6755 VkDeviceSize newSize,
6756 uint32_t id,
6757 uint32_t algorithm);
6758 // Always call before destruction.
6759 void Destroy(VmaAllocator allocator);
6760
6761 VmaPool GetParentPool() const { return m_hParentPool; }
6762 VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
6763 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
6764 uint32_t GetId() const { return m_Id; }
6765 void* GetMappedData() const { return m_pMappedData; }
6766
6767 // Validates all data structures inside this object. If not valid, returns false.
6768 bool Validate() const;
6769
6770 VkResult CheckCorruption(VmaAllocator hAllocator);
6771
6772 // ppData can be null.
6773 VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
6774 void Unmap(VmaAllocator hAllocator, uint32_t count);
6775
6776 VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
6777 VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
6778
6779 VkResult BindBufferMemory(
6780 const VmaAllocator hAllocator,
6781 const VmaAllocation hAllocation,
6782 VkDeviceSize allocationLocalOffset,
6783 VkBuffer hBuffer,
6784 const void* pNext);
6785 VkResult BindImageMemory(
6786 const VmaAllocator hAllocator,
6787 const VmaAllocation hAllocation,
6788 VkDeviceSize allocationLocalOffset,
6789 VkImage hImage,
6790 const void* pNext);
6791
6792 private:
6793 VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
6794 uint32_t m_MemoryTypeIndex;
6795 uint32_t m_Id;
6796 VkDeviceMemory m_hMemory;
6797
6798 /*
6799 Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
6800 Also protects m_MapCount, m_pMappedData.
6801 Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
6802 */
6803 VMA_MUTEX m_Mutex;
6804 uint32_t m_MapCount;
6805 void* m_pMappedData;
6806 };
6807
6808 struct VmaPointerLess
6809 {
6810 bool operator()(const void* lhs, const void* rhs) const
6811 {
6812 return lhs < rhs;
6813 }
6814 };
6815
6816 struct VmaDefragmentationMove
6817 {
6818 size_t srcBlockIndex;
6819 size_t dstBlockIndex;
6820 VkDeviceSize srcOffset;
6821 VkDeviceSize dstOffset;
6822 VkDeviceSize size;
6823 VmaAllocation hAllocation;
6824 VmaDeviceMemoryBlock* pSrcBlock;
6825 VmaDeviceMemoryBlock* pDstBlock;
6826 };
6827
6828 class VmaDefragmentationAlgorithm;
6829
6830 /*
6831 Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
6832 Vulkan memory type.
6833
6834 Synchronized internally with a mutex.
6835 */
6836 struct VmaBlockVector
6837 {
6838 VMA_CLASS_NO_COPY(VmaBlockVector)
6839 public:
6840 VmaBlockVector(
6841 VmaAllocator hAllocator,
6842 VmaPool hParentPool,
6843 uint32_t memoryTypeIndex,
6844 VkDeviceSize preferredBlockSize,
6845 size_t minBlockCount,
6846 size_t maxBlockCount,
6847 VkDeviceSize bufferImageGranularity,
6848 uint32_t frameInUseCount,
6849 bool explicitBlockSize,
6850 uint32_t algorithm);
6851 ~VmaBlockVector();
6852
6853 VkResult CreateMinBlocks();
6854
6855 VmaAllocator GetAllocator() const { return m_hAllocator; }
6856 VmaPool GetParentPool() const { return m_hParentPool; }
6857 bool IsCustomPool() const { return m_hParentPool != VMA_NULL; }
6858 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
6859 VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
6860 VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
6861 uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
6862 uint32_t GetAlgorithm() const { return m_Algorithm; }
6863
6864 void GetPoolStats(VmaPoolStats* pStats);
6865
6866 bool IsEmpty();
6867 bool IsCorruptionDetectionEnabled() const;
6868
6869 VkResult Allocate(
6870 uint32_t currentFrameIndex,
6871 VkDeviceSize size,
6872 VkDeviceSize alignment,
6873 const VmaAllocationCreateInfo& createInfo,
6874 VmaSuballocationType suballocType,
6875 size_t allocationCount,
6876 VmaAllocation* pAllocations);
6877
6878 void Free(const VmaAllocation hAllocation);
6879
6880 // Adds statistics of this BlockVector to pStats.
6881 void AddStats(VmaStats* pStats);
6882
6883 #if VMA_STATS_STRING_ENABLED
6884 void PrintDetailedMap(class VmaJsonWriter& json);
6885 #endif
6886
6887 void MakePoolAllocationsLost(
6888 uint32_t currentFrameIndex,
6889 size_t* pLostAllocationCount);
6890 VkResult CheckCorruption();
6891
6892 // Saves results in pCtx->res.
6893 void Defragment(
6894 class VmaBlockVectorDefragmentationContext* pCtx,
6895 VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags,
6896 VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
6897 VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
6898 VkCommandBuffer commandBuffer);
6899 void DefragmentationEnd(
6900 class VmaBlockVectorDefragmentationContext* pCtx,
6901 uint32_t flags,
6902 VmaDefragmentationStats* pStats);
6903
6904 uint32_t ProcessDefragmentations(
6905 class VmaBlockVectorDefragmentationContext* pCtx,
6906 VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves);
6907
6908 void CommitDefragmentations(
6909 class VmaBlockVectorDefragmentationContext* pCtx,
6910 VmaDefragmentationStats* pStats);
6911
6912 ////////////////////////////////////////////////////////////////////////////////
6913 // To be used only while the m_Mutex is locked. Used during defragmentation.
6914
6915 size_t GetBlockCount() const { return m_Blocks.size(); }
6916 VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
6917 size_t CalcAllocationCount() const;
6918 bool IsBufferImageGranularityConflictPossible() const;
6919
6920 private:
6921 friend class VmaDefragmentationAlgorithm_Generic;
6922
6923 const VmaAllocator m_hAllocator;
6924 const VmaPool m_hParentPool;
6925 const uint32_t m_MemoryTypeIndex;
6926 const VkDeviceSize m_PreferredBlockSize;
6927 const size_t m_MinBlockCount;
6928 const size_t m_MaxBlockCount;
6929 const VkDeviceSize m_BufferImageGranularity;
6930 const uint32_t m_FrameInUseCount;
6931 const bool m_ExplicitBlockSize;
6932 const uint32_t m_Algorithm;
6933 VMA_RW_MUTEX m_Mutex;
6934
6935 /* There can be at most one allocation that is completely empty (except when minBlockCount > 0) -
6936 a hysteresis to avoid pessimistic case of alternating creation and destruction of a VkDeviceMemory. */
6937 bool m_HasEmptyBlock;
6938 // Incrementally sorted by sumFreeSize, ascending.
6939 VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
6940 uint32_t m_NextBlockId;
6941
6942 VkDeviceSize CalcMaxBlockSize() const;
6943
6944 // Finds and removes given block from vector.
6945 void Remove(VmaDeviceMemoryBlock* pBlock);
6946
6947 // Performs single step in sorting m_Blocks. They may not be fully sorted
6948 // after this call.
6949 void IncrementallySortBlocks();
6950
6951 VkResult AllocatePage(
6952 uint32_t currentFrameIndex,
6953 VkDeviceSize size,
6954 VkDeviceSize alignment,
6955 const VmaAllocationCreateInfo& createInfo,
6956 VmaSuballocationType suballocType,
6957 VmaAllocation* pAllocation);
6958
6959 // To be used only without CAN_MAKE_OTHER_LOST flag.
6960 VkResult AllocateFromBlock(
6961 VmaDeviceMemoryBlock* pBlock,
6962 uint32_t currentFrameIndex,
6963 VkDeviceSize size,
6964 VkDeviceSize alignment,
6965 VmaAllocationCreateFlags allocFlags,
6966 void* pUserData,
6967 VmaSuballocationType suballocType,
6968 uint32_t strategy,
6969 VmaAllocation* pAllocation);
6970
6971 VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
6972
6973 // Saves result to pCtx->res.
6974 void ApplyDefragmentationMovesCpu(
6975 class VmaBlockVectorDefragmentationContext* pDefragCtx,
6976 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
6977 // Saves result to pCtx->res.
6978 void ApplyDefragmentationMovesGpu(
6979 class VmaBlockVectorDefragmentationContext* pDefragCtx,
6980 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6981 VkCommandBuffer commandBuffer);
6982
6983 /*
6984 Used during defragmentation. pDefragmentationStats is optional. It's in/out
6985 - updated with new data.
6986 */
6987 void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
6988
6989 void UpdateHasEmptyBlock();
6990 };
6991
6992 struct VmaPool_T
6993 {
6994 VMA_CLASS_NO_COPY(VmaPool_T)
6995 public:
6996 VmaBlockVector m_BlockVector;
6997
6998 VmaPool_T(
6999 VmaAllocator hAllocator,
7000 const VmaPoolCreateInfo& createInfo,
7001 VkDeviceSize preferredBlockSize);
7002 ~VmaPool_T();
7003
7004 uint32_t GetId() const { return m_Id; }
7005 void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
7006
7007 const char* GetName() const { return m_Name; }
7008 void SetName(const char* pName);
7009
7010 #if VMA_STATS_STRING_ENABLED
7011 //void PrintDetailedMap(class VmaStringBuilder& sb);
7012 #endif
7013
7014 private:
7015 uint32_t m_Id;
7016 char* m_Name;
7017 };
7018
7019 /*
7020 Performs defragmentation:
7021
7022 - Updates `pBlockVector->m_pMetadata`.
7023 - Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
7024 - Does not move actual data, only returns requested moves as `moves`.
7025 */
7026 class VmaDefragmentationAlgorithm
7027 {
7028 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
7029 public:
7030 VmaDefragmentationAlgorithm(
7031 VmaAllocator hAllocator,
7032 VmaBlockVector* pBlockVector,
7033 uint32_t currentFrameIndex) :
7034 m_hAllocator(hAllocator),
7035 m_pBlockVector(pBlockVector),
7036 m_CurrentFrameIndex(currentFrameIndex)
7037 {
7038 }
7039 virtual ~VmaDefragmentationAlgorithm()
7040 {
7041 }
7042
7043 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
7044 virtual void AddAll() = 0;
7045
7046 virtual VkResult Defragment(
7047 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7048 VkDeviceSize maxBytesToMove,
7049 uint32_t maxAllocationsToMove,
7050 VmaDefragmentationFlags flags) = 0;
7051
7052 virtual VkDeviceSize GetBytesMoved() const = 0;
7053 virtual uint32_t GetAllocationsMoved() const = 0;
7054
7055 protected:
7056 VmaAllocator const m_hAllocator;
7057 VmaBlockVector* const m_pBlockVector;
7058 const uint32_t m_CurrentFrameIndex;
7059
7060 struct AllocationInfo
7061 {
7062 VmaAllocation m_hAllocation;
7063 VkBool32* m_pChanged;
7064
7065 AllocationInfo() :
7066 m_hAllocation(VK_NULL_HANDLE),
7067 m_pChanged(VMA_NULL)
7068 {
7069 }
7070 AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
7071 m_hAllocation(hAlloc),
7072 m_pChanged(pChanged)
7073 {
7074 }
7075 };
7076 };
7077
7078 class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
7079 {
7080 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
7081 public:
7082 VmaDefragmentationAlgorithm_Generic(
7083 VmaAllocator hAllocator,
7084 VmaBlockVector* pBlockVector,
7085 uint32_t currentFrameIndex,
7086 bool overlappingMoveSupported);
7087 virtual ~VmaDefragmentationAlgorithm_Generic();
7088
7089 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
7090 virtual void AddAll() { m_AllAllocations = true; }
7091
7092 virtual VkResult Defragment(
7093 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7094 VkDeviceSize maxBytesToMove,
7095 uint32_t maxAllocationsToMove,
7096 VmaDefragmentationFlags flags);
7097
7098 virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
7099 virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
7100
7101 private:
7102 uint32_t m_AllocationCount;
7103 bool m_AllAllocations;
7104
7105 VkDeviceSize m_BytesMoved;
7106 uint32_t m_AllocationsMoved;
7107
7108 struct AllocationInfoSizeGreater
7109 {
7110 bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
7111 {
7112 return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
7113 }
7114 };
7115
7116 struct AllocationInfoOffsetGreater
7117 {
7118 bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
7119 {
7120 return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
7121 }
7122 };
7123
7124 struct BlockInfo
7125 {
7126 size_t m_OriginalBlockIndex;
7127 VmaDeviceMemoryBlock* m_pBlock;
7128 bool m_HasNonMovableAllocations;
7129 VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
7130
7131 BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
7132 m_OriginalBlockIndex(SIZE_MAX),
7133 m_pBlock(VMA_NULL),
7134 m_HasNonMovableAllocations(true),
7135 m_Allocations(pAllocationCallbacks)
7136 {
7137 }
7138
7139 void CalcHasNonMovableAllocations()
7140 {
7141 const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
7142 const size_t defragmentAllocCount = m_Allocations.size();
7143 m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
7144 }
7145
7146 void SortAllocationsBySizeDescending()
7147 {
7148 VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
7149 }
7150
7151 void SortAllocationsByOffsetDescending()
7152 {
7153 VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
7154 }
7155 };
7156
7157 struct BlockPointerLess
7158 {
7159 bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
7160 {
7161 return pLhsBlockInfo->m_pBlock < pRhsBlock;
7162 }
7163 bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
7164 {
7165 return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
7166 }
7167 };
7168
7169 // 1. Blocks with some non-movable allocations go first.
7170 // 2. Blocks with smaller sumFreeSize go first.
7171 struct BlockInfoCompareMoveDestination
7172 {
7173 bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
7174 {
7175 if (pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
7176 {
7177 return true;
7178 }
7179 if (!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
7180 {
7181 return false;
7182 }
7183 if (pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
7184 {
7185 return true;
7186 }
7187 return false;
7188 }
7189 };
7190
7191 typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
7192 BlockInfoVector m_Blocks;
7193
7194 VkResult DefragmentRound(
7195 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7196 VkDeviceSize maxBytesToMove,
7197 uint32_t maxAllocationsToMove,
7198 bool freeOldAllocations);
7199
7200 size_t CalcBlocksWithNonMovableCount() const;
7201
7202 static bool MoveMakesSense(
7203 size_t dstBlockIndex, VkDeviceSize dstOffset,
7204 size_t srcBlockIndex, VkDeviceSize srcOffset);
7205 };
7206
7207 class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
7208 {
7209 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
7210 public:
7211 VmaDefragmentationAlgorithm_Fast(
7212 VmaAllocator hAllocator,
7213 VmaBlockVector* pBlockVector,
7214 uint32_t currentFrameIndex,
7215 bool overlappingMoveSupported);
7216 virtual ~VmaDefragmentationAlgorithm_Fast();
7217
7218 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { ++m_AllocationCount; }
7219 virtual void AddAll() { m_AllAllocations = true; }
7220
7221 virtual VkResult Defragment(
7222 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7223 VkDeviceSize maxBytesToMove,
7224 uint32_t maxAllocationsToMove,
7225 VmaDefragmentationFlags flags);
7226
7227 virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
7228 virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
7229
7230 private:
7231 struct BlockInfo
7232 {
7233 size_t origBlockIndex;
7234 };
7235
7236 class FreeSpaceDatabase
7237 {
7238 public:
7239 FreeSpaceDatabase()
7240 {
7241 FreeSpace s = {};
7242 s.blockInfoIndex = SIZE_MAX;
7243 for (size_t i = 0; i < MAX_COUNT; ++i)
7244 {
7245 m_FreeSpaces[i] = s;
7246 }
7247 }
7248
7249 void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
7250 {
7251 if (size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7252 {
7253 return;
7254 }
7255
7256 // Find first invalid or the smallest structure.
7257 size_t bestIndex = SIZE_MAX;
7258 for (size_t i = 0; i < MAX_COUNT; ++i)
7259 {
7260 // Empty structure.
7261 if (m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
7262 {
7263 bestIndex = i;
7264 break;
7265 }
7266 if (m_FreeSpaces[i].size < size &&
7267 (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
7268 {
7269 bestIndex = i;
7270 }
7271 }
7272
7273 if (bestIndex != SIZE_MAX)
7274 {
7275 m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
7276 m_FreeSpaces[bestIndex].offset = offset;
7277 m_FreeSpaces[bestIndex].size = size;
7278 }
7279 }
7280
7281 bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
7282 size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
7283 {
7284 size_t bestIndex = SIZE_MAX;
7285 VkDeviceSize bestFreeSpaceAfter = 0;
7286 for (size_t i = 0; i < MAX_COUNT; ++i)
7287 {
7288 // Structure is valid.
7289 if (m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
7290 {
7291 const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment);
7292 // Allocation fits into this structure.
7293 if (dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
7294 {
7295 const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
7296 (dstOffset + size);
7297 if (bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
7298 {
7299 bestIndex = i;
7300 bestFreeSpaceAfter = freeSpaceAfter;
7301 }
7302 }
7303 }
7304 }
7305
7306 if (bestIndex != SIZE_MAX)
7307 {
7308 outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
7309 outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment);
7310
7311 if (bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7312 {
7313 // Leave this structure for remaining empty space.
7314 const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
7315 m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
7316 m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
7317 }
7318 else
7319 {
7320 // This structure becomes invalid.
7321 m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX;
7322 }
7323
7324 return true;
7325 }
7326
7327 return false;
7328 }
7329
7330 private:
7331 static const size_t MAX_COUNT = 4;
7332
7333 struct FreeSpace
7334 {
7335 size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
7336 VkDeviceSize offset;
7337 VkDeviceSize size;
7338 } m_FreeSpaces[MAX_COUNT];
7339 };
7340
7341 const bool m_OverlappingMoveSupported;
7342
7343 uint32_t m_AllocationCount;
7344 bool m_AllAllocations;
7345
7346 VkDeviceSize m_BytesMoved;
7347 uint32_t m_AllocationsMoved;
7348
7349 VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
7350
7351 void PreprocessMetadata();
7352 void PostprocessMetadata();
7353 void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
7354 };
7355
7356 struct VmaBlockDefragmentationContext
7357 {
7358 enum BLOCK_FLAG
7359 {
7360 BLOCK_FLAG_USED = 0x00000001,
7361 };
7362 uint32_t flags;
7363 VkBuffer hBuffer;
7364 };
7365
7366 class VmaBlockVectorDefragmentationContext
7367 {
7368 VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
7369 public:
7370 VkResult res;
7371 bool mutexLocked;
7372 VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
7373 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > defragmentationMoves;
7374 uint32_t defragmentationMovesProcessed;
7375 uint32_t defragmentationMovesCommitted;
7376 bool hasDefragmentationPlan;
7377
7378 VmaBlockVectorDefragmentationContext(
7379 VmaAllocator hAllocator,
7380 VmaPool hCustomPool, // Optional.
7381 VmaBlockVector* pBlockVector,
7382 uint32_t currFrameIndex);
7383 ~VmaBlockVectorDefragmentationContext();
7384
7385 VmaPool GetCustomPool() const { return m_hCustomPool; }
7386 VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
7387 VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
7388
7389 void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
7390 void AddAll() { m_AllAllocations = true; }
7391
7392 void Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags);
7393
7394 private:
7395 const VmaAllocator m_hAllocator;
7396 // Null if not from custom pool.
7397 const VmaPool m_hCustomPool;
7398 // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
7399 VmaBlockVector* const m_pBlockVector;
7400 const uint32_t m_CurrFrameIndex;
7401 // Owner of this object.
7402 VmaDefragmentationAlgorithm* m_pAlgorithm;
7403
7404 struct AllocInfo
7405 {
7406 VmaAllocation hAlloc;
7407 VkBool32* pChanged;
7408 };
7409 // Used between constructor and Begin.
7410 VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
7411 bool m_AllAllocations;
7412 };
7413
7414 struct VmaDefragmentationContext_T
7415 {
7416 private:
7417 VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
7418 public:
7419 VmaDefragmentationContext_T(
7420 VmaAllocator hAllocator,
7421 uint32_t currFrameIndex,
7422 uint32_t flags,
7423 VmaDefragmentationStats* pStats);
7424 ~VmaDefragmentationContext_T();
7425
7426 void AddPools(uint32_t poolCount, const VmaPool* pPools);
7427 void AddAllocations(
7428 uint32_t allocationCount,
7429 const VmaAllocation* pAllocations,
7430 VkBool32* pAllocationsChanged);
7431
7432 /*
7433 Returns:
7434 - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
7435 - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
7436 - Negative value if error occured and object can be destroyed immediately.
7437 */
7438 VkResult Defragment(
7439 VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
7440 VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
7441 VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags);
7442
7443 VkResult DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo);
7444 VkResult DefragmentPassEnd();
7445
7446 private:
7447 const VmaAllocator m_hAllocator;
7448 const uint32_t m_CurrFrameIndex;
7449 const uint32_t m_Flags;
7450 VmaDefragmentationStats* const m_pStats;
7451
7452 VkDeviceSize m_MaxCpuBytesToMove;
7453 uint32_t m_MaxCpuAllocationsToMove;
7454 VkDeviceSize m_MaxGpuBytesToMove;
7455 uint32_t m_MaxGpuAllocationsToMove;
7456
7457 // Owner of these objects.
7458 VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
7459 // Owner of these objects.
7460 VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
7461 };
7462
7463 #if VMA_RECORDING_ENABLED
7464
7465 class VmaRecorder
7466 {
7467 public:
7468 VmaRecorder();
7469 VkResult Init(const VmaRecordSettings& settings, bool useMutex);
7470 void WriteConfiguration(
7471 const VkPhysicalDeviceProperties& devProps,
7472 const VkPhysicalDeviceMemoryProperties& memProps,
7473 uint32_t vulkanApiVersion,
7474 bool dedicatedAllocationExtensionEnabled,
7475 bool bindMemory2ExtensionEnabled,
7476 bool memoryBudgetExtensionEnabled,
7477 bool deviceCoherentMemoryExtensionEnabled);
7478 ~VmaRecorder();
7479
7480 void RecordCreateAllocator(uint32_t frameIndex);
7481 void RecordDestroyAllocator(uint32_t frameIndex);
7482 void RecordCreatePool(uint32_t frameIndex,
7483 const VmaPoolCreateInfo& createInfo,
7484 VmaPool pool);
7485 void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
7486 void RecordAllocateMemory(uint32_t frameIndex,
7487 const VkMemoryRequirements& vkMemReq,
7488 const VmaAllocationCreateInfo& createInfo,
7489 VmaAllocation allocation);
7490 void RecordAllocateMemoryPages(uint32_t frameIndex,
7491 const VkMemoryRequirements& vkMemReq,
7492 const VmaAllocationCreateInfo& createInfo,
7493 uint64_t allocationCount,
7494 const VmaAllocation* pAllocations);
7495 void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
7496 const VkMemoryRequirements& vkMemReq,
7497 bool requiresDedicatedAllocation,
7498 bool prefersDedicatedAllocation,
7499 const VmaAllocationCreateInfo& createInfo,
7500 VmaAllocation allocation);
7501 void RecordAllocateMemoryForImage(uint32_t frameIndex,
7502 const VkMemoryRequirements& vkMemReq,
7503 bool requiresDedicatedAllocation,
7504 bool prefersDedicatedAllocation,
7505 const VmaAllocationCreateInfo& createInfo,
7506 VmaAllocation allocation);
7507 void RecordFreeMemory(uint32_t frameIndex,
7508 VmaAllocation allocation);
7509 void RecordFreeMemoryPages(uint32_t frameIndex,
7510 uint64_t allocationCount,
7511 const VmaAllocation* pAllocations);
7512 void RecordSetAllocationUserData(uint32_t frameIndex,
7513 VmaAllocation allocation,
7514 const void* pUserData);
7515 void RecordCreateLostAllocation(uint32_t frameIndex,
7516 VmaAllocation allocation);
7517 void RecordMapMemory(uint32_t frameIndex,
7518 VmaAllocation allocation);
7519 void RecordUnmapMemory(uint32_t frameIndex,
7520 VmaAllocation allocation);
7521 void RecordFlushAllocation(uint32_t frameIndex,
7522 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
7523 void RecordInvalidateAllocation(uint32_t frameIndex,
7524 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
7525 void RecordCreateBuffer(uint32_t frameIndex,
7526 const VkBufferCreateInfo& bufCreateInfo,
7527 const VmaAllocationCreateInfo& allocCreateInfo,
7528 VmaAllocation allocation);
7529 void RecordCreateImage(uint32_t frameIndex,
7530 const VkImageCreateInfo& imageCreateInfo,
7531 const VmaAllocationCreateInfo& allocCreateInfo,
7532 VmaAllocation allocation);
7533 void RecordDestroyBuffer(uint32_t frameIndex,
7534 VmaAllocation allocation);
7535 void RecordDestroyImage(uint32_t frameIndex,
7536 VmaAllocation allocation);
7537 void RecordTouchAllocation(uint32_t frameIndex,
7538 VmaAllocation allocation);
7539 void RecordGetAllocationInfo(uint32_t frameIndex,
7540 VmaAllocation allocation);
7541 void RecordMakePoolAllocationsLost(uint32_t frameIndex,
7542 VmaPool pool);
7543 void RecordDefragmentationBegin(uint32_t frameIndex,
7544 const VmaDefragmentationInfo2& info,
7545 VmaDefragmentationContext ctx);
7546 void RecordDefragmentationEnd(uint32_t frameIndex,
7547 VmaDefragmentationContext ctx);
7548 void RecordSetPoolName(uint32_t frameIndex,
7549 VmaPool pool,
7550 const char* name);
7551
7552 private:
7553 struct CallParams
7554 {
7555 uint32_t threadId;
7556 double time;
7557 };
7558
7559 class UserDataString
7560 {
7561 public:
7562 UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
7563 const char* GetString() const { return m_Str; }
7564
7565 private:
7566 char m_PtrStr[17];
7567 const char* m_Str;
7568 };
7569
7570 bool m_UseMutex;
7571 VmaRecordFlags m_Flags;
7572 FILE* m_File;
7573 VMA_MUTEX m_FileMutex;
7574 std::chrono::time_point<std::chrono::high_resolution_clock> m_RecordingStartTime;
7575
7576 void GetBasicParams(CallParams& outParams);
7577
7578 // T must be a pointer type, e.g. VmaAllocation, VmaPool.
7579 template<typename T>
7580 void PrintPointerList(uint64_t count, const T* pItems)
7581 {
7582 if (count)
7583 {
7584 fprintf(m_File, "%p", pItems[0]);
7585 for (uint64_t i = 1; i < count; ++i)
7586 {
7587 fprintf(m_File, " %p", pItems[i]);
7588 }
7589 }
7590 }
7591
7592 void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
7593 void Flush();
7594 };
7595
7596 #endif // #if VMA_RECORDING_ENABLED
7597
7598 /*
7599 Thread-safe wrapper over VmaPoolAllocator free list, for allocation of VmaAllocation_T objects.
7600 */
7601 class VmaAllocationObjectAllocator
7602 {
7603 VMA_CLASS_NO_COPY(VmaAllocationObjectAllocator)
7604 public:
7605 VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks);
7606
7607 template<typename... Types> VmaAllocation Allocate(Types... args);
7608 void Free(VmaAllocation hAlloc);
7609
7610 private:
7611 VMA_MUTEX m_Mutex;
7612 VmaPoolAllocator<VmaAllocation_T> m_Allocator;
7613 };
7614
7615 struct VmaCurrentBudgetData
7616 {
7617 VMA_ATOMIC_UINT64 m_BlockBytes[VK_MAX_MEMORY_HEAPS];
7618 VMA_ATOMIC_UINT64 m_AllocationBytes[VK_MAX_MEMORY_HEAPS];
7619
7620 #if VMA_MEMORY_BUDGET
7621 VMA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch;
7622 VMA_RW_MUTEX m_BudgetMutex;
7623 uint64_t m_VulkanUsage[VK_MAX_MEMORY_HEAPS];
7624 uint64_t m_VulkanBudget[VK_MAX_MEMORY_HEAPS];
7625 uint64_t m_BlockBytesAtBudgetFetch[VK_MAX_MEMORY_HEAPS];
7626 #endif // #if VMA_MEMORY_BUDGET
7627
7628 VmaCurrentBudgetData()
7629 {
7630 for (uint32_t heapIndex = 0; heapIndex < VK_MAX_MEMORY_HEAPS; ++heapIndex)
7631 {
7632 m_BlockBytes[heapIndex] = 0;
7633 m_AllocationBytes[heapIndex] = 0;
7634 #if VMA_MEMORY_BUDGET
7635 m_VulkanUsage[heapIndex] = 0;
7636 m_VulkanBudget[heapIndex] = 0;
7637 m_BlockBytesAtBudgetFetch[heapIndex] = 0;
7638 #endif
7639 }
7640
7641 #if VMA_MEMORY_BUDGET
7642 m_OperationsSinceBudgetFetch = 0;
7643 #endif
7644 }
7645
7646 void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
7647 {
7648 m_AllocationBytes[heapIndex] += allocationSize;
7649 #if VMA_MEMORY_BUDGET
7650 ++m_OperationsSinceBudgetFetch;
7651 #endif
7652 }
7653
7654 void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
7655 {
7656 VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize); // DELME
7657 m_AllocationBytes[heapIndex] -= allocationSize;
7658 #if VMA_MEMORY_BUDGET
7659 ++m_OperationsSinceBudgetFetch;
7660 #endif
7661 }
7662 };
7663
7664 // Main allocator object.
7665 struct VmaAllocator_T
7666 {
7667 VMA_CLASS_NO_COPY(VmaAllocator_T)
7668 public:
7669 bool m_UseMutex;
7670 uint32_t m_VulkanApiVersion;
7671 bool m_UseKhrDedicatedAllocation; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
7672 bool m_UseKhrBindMemory2; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
7673 bool m_UseExtMemoryBudget;
7674 bool m_UseAmdDeviceCoherentMemory;
7675 bool m_UseKhrBufferDeviceAddress;
7676 VkDevice m_hDevice;
7677 VkInstance m_hInstance;
7678 bool m_AllocationCallbacksSpecified;
7679 VkAllocationCallbacks m_AllocationCallbacks;
7680 VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
7681 VmaAllocationObjectAllocator m_AllocationObjectAllocator;
7682
7683 // Each bit (1 << i) is set if HeapSizeLimit is enabled for that heap, so cannot allocate more than the heap size.
7684 uint32_t m_HeapSizeLimitMask;
7685
7686 VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
7687 VkPhysicalDeviceMemoryProperties m_MemProps;
7688
7689 // Default pools.
7690 VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
7691
7692 // Each vector is sorted by memory (handle value).
7693 typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
7694 AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES];
7695 VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
7696
7697 VmaCurrentBudgetData m_Budget;
7698
7699 VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
7700 VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
7701 ~VmaAllocator_T();
7702
7703 const VkAllocationCallbacks* GetAllocationCallbacks() const
7704 {
7705 return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
7706 }
7707 const VmaVulkanFunctions& GetVulkanFunctions() const
7708 {
7709 return m_VulkanFunctions;
7710 }
7711
7712 VkPhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; }
7713
7714 VkDeviceSize GetBufferImageGranularity() const
7715 {
7716 return VMA_MAX(
7717 static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
7718 m_PhysicalDeviceProperties.limits.bufferImageGranularity);
7719 }
7720
7721 uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
7722 uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
7723
7724 uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
7725 {
7726 VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
7727 return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
7728 }
7729 // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
7730 bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
7731 {
7732 return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
7733 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
7734 }
7735 // Minimum alignment for all allocations in specific memory type.
7736 VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
7737 {
7738 return IsMemoryTypeNonCoherent(memTypeIndex) ?
7739 VMA_MAX((VkDeviceSize)VMA_DEBUG_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
7740 (VkDeviceSize)VMA_DEBUG_ALIGNMENT;
7741 }
7742
7743 bool IsIntegratedGpu() const
7744 {
7745 return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
7746 }
7747
7748 uint32_t GetGlobalMemoryTypeBits() const { return m_GlobalMemoryTypeBits; }
7749
7750 #if VMA_RECORDING_ENABLED
7751 VmaRecorder* GetRecorder() const { return m_pRecorder; }
7752 #endif
7753
7754 void GetBufferMemoryRequirements(
7755 VkBuffer hBuffer,
7756 VkMemoryRequirements& memReq,
7757 bool& requiresDedicatedAllocation,
7758 bool& prefersDedicatedAllocation) const;
7759 void GetImageMemoryRequirements(
7760 VkImage hImage,
7761 VkMemoryRequirements& memReq,
7762 bool& requiresDedicatedAllocation,
7763 bool& prefersDedicatedAllocation) const;
7764
7765 // Main allocation function.
7766 VkResult AllocateMemory(
7767 const VkMemoryRequirements& vkMemReq,
7768 bool requiresDedicatedAllocation,
7769 bool prefersDedicatedAllocation,
7770 VkBuffer dedicatedBuffer,
7771 VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown.
7772 VkImage dedicatedImage,
7773 const VmaAllocationCreateInfo& createInfo,
7774 VmaSuballocationType suballocType,
7775 size_t allocationCount,
7776 VmaAllocation* pAllocations);
7777
7778 // Main deallocation function.
7779 void FreeMemory(
7780 size_t allocationCount,
7781 const VmaAllocation* pAllocations);
7782
7783 VkResult ResizeAllocation(
7784 const VmaAllocation alloc,
7785 VkDeviceSize newSize);
7786
7787 void CalculateStats(VmaStats* pStats);
7788
7789 void GetBudget(
7790 VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount);
7791
7792 #if VMA_STATS_STRING_ENABLED
7793 void PrintDetailedMap(class VmaJsonWriter& json);
7794 #endif
7795
7796 VkResult DefragmentationBegin(
7797 const VmaDefragmentationInfo2& info,
7798 VmaDefragmentationStats* pStats,
7799 VmaDefragmentationContext* pContext);
7800 VkResult DefragmentationEnd(
7801 VmaDefragmentationContext context);
7802
7803 VkResult DefragmentationPassBegin(
7804 VmaDefragmentationPassInfo* pInfo,
7805 VmaDefragmentationContext context);
7806 VkResult DefragmentationPassEnd(
7807 VmaDefragmentationContext context);
7808
7809 void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
7810 bool TouchAllocation(VmaAllocation hAllocation);
7811
7812 VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
7813 void DestroyPool(VmaPool pool);
7814 void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
7815
7816 void SetCurrentFrameIndex(uint32_t frameIndex);
7817 uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
7818
7819 void MakePoolAllocationsLost(
7820 VmaPool hPool,
7821 size_t* pLostAllocationCount);
7822 VkResult CheckPoolCorruption(VmaPool hPool);
7823 VkResult CheckCorruption(uint32_t memoryTypeBits);
7824
7825 void CreateLostAllocation(VmaAllocation* pAllocation);
7826
7827 // Call to Vulkan function vkAllocateMemory with accompanying bookkeeping.
7828 VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
7829 // Call to Vulkan function vkFreeMemory with accompanying bookkeeping.
7830 void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
7831 // Call to Vulkan function vkBindBufferMemory or vkBindBufferMemory2KHR.
7832 VkResult BindVulkanBuffer(
7833 VkDeviceMemory memory,
7834 VkDeviceSize memoryOffset,
7835 VkBuffer buffer,
7836 const void* pNext);
7837 // Call to Vulkan function vkBindImageMemory or vkBindImageMemory2KHR.
7838 VkResult BindVulkanImage(
7839 VkDeviceMemory memory,
7840 VkDeviceSize memoryOffset,
7841 VkImage image,
7842 const void* pNext);
7843
7844 VkResult Map(VmaAllocation hAllocation, void** ppData);
7845 void Unmap(VmaAllocation hAllocation);
7846
7847 VkResult BindBufferMemory(
7848 VmaAllocation hAllocation,
7849 VkDeviceSize allocationLocalOffset,
7850 VkBuffer hBuffer,
7851 const void* pNext);
7852 VkResult BindImageMemory(
7853 VmaAllocation hAllocation,
7854 VkDeviceSize allocationLocalOffset,
7855 VkImage hImage,
7856 const void* pNext);
7857
7858 VkResult FlushOrInvalidateAllocation(
7859 VmaAllocation hAllocation,
7860 VkDeviceSize offset, VkDeviceSize size,
7861 VMA_CACHE_OPERATION op);
7862 VkResult FlushOrInvalidateAllocations(
7863 uint32_t allocationCount,
7864 const VmaAllocation* allocations,
7865 const VkDeviceSize* offsets, const VkDeviceSize* sizes,
7866 VMA_CACHE_OPERATION op);
7867
7868 void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
7869
7870 /*
7871 Returns bit mask of memory types that can support defragmentation on GPU as
7872 they support creation of required buffer for copy operations.
7873 */
7874 uint32_t GetGpuDefragmentationMemoryTypeBits();
7875
7876 private:
7877 VkDeviceSize m_PreferredLargeHeapBlockSize;
7878
7879 VkPhysicalDevice m_PhysicalDevice;
7880 VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
7881 VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized.
7882
7883 VMA_RW_MUTEX m_PoolsMutex;
7884 // Protected by m_PoolsMutex. Sorted by pointer value.
7885 VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools;
7886 uint32_t m_NextPoolId;
7887
7888 VmaVulkanFunctions m_VulkanFunctions;
7889
7890 // Global bit mask AND-ed with any memoryTypeBits to disallow certain memory types.
7891 uint32_t m_GlobalMemoryTypeBits;
7892
7893 #if VMA_RECORDING_ENABLED
7894 VmaRecorder* m_pRecorder;
7895 #endif
7896
7897 void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
7898
7899 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
7900 void ImportVulkanFunctions_Static();
7901 #endif
7902
7903 void ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions);
7904
7905 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
7906 void ImportVulkanFunctions_Dynamic();
7907 #endif
7908
7909 void ValidateVulkanFunctions();
7910
7911 VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
7912
7913 VkResult AllocateMemoryOfType(
7914 VkDeviceSize size,
7915 VkDeviceSize alignment,
7916 bool dedicatedAllocation,
7917 VkBuffer dedicatedBuffer,
7918 VkBufferUsageFlags dedicatedBufferUsage,
7919 VkImage dedicatedImage,
7920 const VmaAllocationCreateInfo& createInfo,
7921 uint32_t memTypeIndex,
7922 VmaSuballocationType suballocType,
7923 size_t allocationCount,
7924 VmaAllocation* pAllocations);
7925
7926 // Helper function only to be used inside AllocateDedicatedMemory.
7927 VkResult AllocateDedicatedMemoryPage(
7928 VkDeviceSize size,
7929 VmaSuballocationType suballocType,
7930 uint32_t memTypeIndex,
7931 const VkMemoryAllocateInfo& allocInfo,
7932 bool map,
7933 bool isUserDataString,
7934 void* pUserData,
7935 VmaAllocation* pAllocation);
7936
7937 // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
7938 VkResult AllocateDedicatedMemory(
7939 VkDeviceSize size,
7940 VmaSuballocationType suballocType,
7941 uint32_t memTypeIndex,
7942 bool withinBudget,
7943 bool map,
7944 bool isUserDataString,
7945 void* pUserData,
7946 VkBuffer dedicatedBuffer,
7947 VkBufferUsageFlags dedicatedBufferUsage,
7948 VkImage dedicatedImage,
7949 size_t allocationCount,
7950 VmaAllocation* pAllocations);
7951
7952 void FreeDedicatedMemory(const VmaAllocation allocation);
7953
7954 /*
7955 Calculates and returns bit mask of memory types that can support defragmentation
7956 on GPU as they support creation of required buffer for copy operations.
7957 */
7958 uint32_t CalculateGpuDefragmentationMemoryTypeBits() const;
7959
7960 uint32_t CalculateGlobalMemoryTypeBits() const;
7961
7962 bool GetFlushOrInvalidateRange(
7963 VmaAllocation allocation,
7964 VkDeviceSize offset, VkDeviceSize size,
7965 VkMappedMemoryRange& outRange) const;
7966
7967 #if VMA_MEMORY_BUDGET
7968 void UpdateVulkanBudget();
7969 #endif // #if VMA_MEMORY_BUDGET
7970 };
7971
7972 ////////////////////////////////////////////////////////////////////////////////
7973 // Memory allocation #2 after VmaAllocator_T definition
7974
7975 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
7976 {
7977 return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
7978 }
7979
7980 static void VmaFree(VmaAllocator hAllocator, void* ptr)
7981 {
7982 VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
7983 }
7984
7985 template<typename T>
7986 static T* VmaAllocate(VmaAllocator hAllocator)
7987 {
7988 return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
7989 }
7990
7991 template<typename T>
7992 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
7993 {
7994 return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
7995 }
7996
7997 template<typename T>
7998 static void vma_delete(VmaAllocator hAllocator, T* ptr)
7999 {
8000 if (ptr != VMA_NULL)
8001 {
8002 ptr->~T();
8003 VmaFree(hAllocator, ptr);
8004 }
8005 }
8006
8007 template<typename T>
8008 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
8009 {
8010 if (ptr != VMA_NULL)
8011 {
8012 for (size_t i = count; i--; )
8013 ptr[i].~T();
8014 VmaFree(hAllocator, ptr);
8015 }
8016 }
8017
8018 ////////////////////////////////////////////////////////////////////////////////
8019 // VmaStringBuilder
8020
8021 #if VMA_STATS_STRING_ENABLED
8022
8023 class VmaStringBuilder
8024 {
8025 public:
8026 VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
8027 size_t GetLength() const { return m_Data.size(); }
8028 const char* GetData() const { return m_Data.data(); }
8029
8030 void Add(char ch) { m_Data.push_back(ch); }
8031 void Add(const char* pStr);
8032 void AddNewLine() { Add('\n'); }
8033 void AddNumber(uint32_t num);
8034 void AddNumber(uint64_t num);
8035 void AddPointer(const void* ptr);
8036
8037 private:
8038 VmaVector< char, VmaStlAllocator<char> > m_Data;
8039 };
8040
8041 void VmaStringBuilder::Add(const char* pStr)
8042 {
8043 const size_t strLen = strlen(pStr);
8044 if (strLen > 0)
8045 {
8046 const size_t oldCount = m_Data.size();
8047 m_Data.resize(oldCount + strLen);
8048 memcpy(m_Data.data() + oldCount, pStr, strLen);
8049 }
8050 }
8051
8052 void VmaStringBuilder::AddNumber(uint32_t num)
8053 {
8054 char buf[11];
8055 buf[10] = '\0';
8056 char* p = &buf[10];
8057 do
8058 {
8059 *--p = '0' + (num % 10);
8060 num /= 10;
8061 } while (num);
8062 Add(p);
8063 }
8064
8065 void VmaStringBuilder::AddNumber(uint64_t num)
8066 {
8067 char buf[21];
8068 buf[20] = '\0';
8069 char* p = &buf[20];
8070 do
8071 {
8072 *--p = '0' + (num % 10);
8073 num /= 10;
8074 } while (num);
8075 Add(p);
8076 }
8077
8078 void VmaStringBuilder::AddPointer(const void* ptr)
8079 {
8080 char buf[21];
8081 VmaPtrToStr(buf, sizeof(buf), ptr);
8082 Add(buf);
8083 }
8084
8085 #endif // #if VMA_STATS_STRING_ENABLED
8086
8087 ////////////////////////////////////////////////////////////////////////////////
8088 // VmaJsonWriter
8089
8090 #if VMA_STATS_STRING_ENABLED
8091
8092 class VmaJsonWriter
8093 {
8094 VMA_CLASS_NO_COPY(VmaJsonWriter)
8095 public:
8096 VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
8097 ~VmaJsonWriter();
8098
8099 void BeginObject(bool singleLine = false);
8100 void EndObject();
8101
8102 void BeginArray(bool singleLine = false);
8103 void EndArray();
8104
8105 void WriteString(const char* pStr);
8106 void BeginString(const char* pStr = VMA_NULL);
8107 void ContinueString(const char* pStr);
8108 void ContinueString(uint32_t n);
8109 void ContinueString(uint64_t n);
8110 void ContinueString_Pointer(const void* ptr);
8111 void EndString(const char* pStr = VMA_NULL);
8112
8113 void WriteNumber(uint32_t n);
8114 void WriteNumber(uint64_t n);
8115 void WriteBool(bool b);
8116 void WriteNull();
8117
8118 private:
8119 static const char* const INDENT;
8120
8121 enum COLLECTION_TYPE
8122 {
8123 COLLECTION_TYPE_OBJECT,
8124 COLLECTION_TYPE_ARRAY,
8125 };
8126 struct StackItem
8127 {
8128 COLLECTION_TYPE type;
8129 uint32_t valueCount;
8130 bool singleLineMode;
8131 };
8132
8133 VmaStringBuilder& m_SB;
8134 VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
8135 bool m_InsideString;
8136
8137 void BeginValue(bool isString);
8138 void WriteIndent(bool oneLess = false);
8139 };
8140
8141 const char* const VmaJsonWriter::INDENT = " ";
8142
8143 VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
8144 m_SB(sb),
8145 m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
8146 m_InsideString(false)
8147 {
8148 }
8149
8150 VmaJsonWriter::~VmaJsonWriter()
8151 {
8152 VMA_ASSERT(!m_InsideString);
8153 VMA_ASSERT(m_Stack.empty());
8154 }
8155
8156 void VmaJsonWriter::BeginObject(bool singleLine)
8157 {
8158 VMA_ASSERT(!m_InsideString);
8159
8160 BeginValue(false);
8161 m_SB.Add('{');
8162
8163 StackItem item;
8164 item.type = COLLECTION_TYPE_OBJECT;
8165 item.valueCount = 0;
8166 item.singleLineMode = singleLine;
8167 m_Stack.push_back(item);
8168 }
8169
8170 void VmaJsonWriter::EndObject()
8171 {
8172 VMA_ASSERT(!m_InsideString);
8173
8174 WriteIndent(true);
8175 m_SB.Add('}');
8176
8177 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
8178 m_Stack.pop_back();
8179 }
8180
8181 void VmaJsonWriter::BeginArray(bool singleLine)
8182 {
8183 VMA_ASSERT(!m_InsideString);
8184
8185 BeginValue(false);
8186 m_SB.Add('[');
8187
8188 StackItem item;
8189 item.type = COLLECTION_TYPE_ARRAY;
8190 item.valueCount = 0;
8191 item.singleLineMode = singleLine;
8192 m_Stack.push_back(item);
8193 }
8194
8195 void VmaJsonWriter::EndArray()
8196 {
8197 VMA_ASSERT(!m_InsideString);
8198
8199 WriteIndent(true);
8200 m_SB.Add(']');
8201
8202 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
8203 m_Stack.pop_back();
8204 }
8205
8206 void VmaJsonWriter::WriteString(const char* pStr)
8207 {
8208 BeginString(pStr);
8209 EndString();
8210 }
8211
8212 void VmaJsonWriter::BeginString(const char* pStr)
8213 {
8214 VMA_ASSERT(!m_InsideString);
8215
8216 BeginValue(true);
8217 m_SB.Add('"');
8218 m_InsideString = true;
8219 if (pStr != VMA_NULL && pStr[0] != '\0')
8220 {
8221 ContinueString(pStr);
8222 }
8223 }
8224
8225 void VmaJsonWriter::ContinueString(const char* pStr)
8226 {
8227 VMA_ASSERT(m_InsideString);
8228
8229 const size_t strLen = strlen(pStr);
8230 for (size_t i = 0; i < strLen; ++i)
8231 {
8232 char ch = pStr[i];
8233 if (ch == '\\')
8234 {
8235 m_SB.Add("\\\\");
8236 }
8237 else if (ch == '"')
8238 {
8239 m_SB.Add("\\\"");
8240 }
8241 else if (ch >= 32)
8242 {
8243 m_SB.Add(ch);
8244 }
8245 else switch (ch)
8246 {
8247 case '\b':
8248 m_SB.Add("\\b");
8249 break;
8250 case '\f':
8251 m_SB.Add("\\f");
8252 break;
8253 case '\n':
8254 m_SB.Add("\\n");
8255 break;
8256 case '\r':
8257 m_SB.Add("\\r");
8258 break;
8259 case '\t':
8260 m_SB.Add("\\t");
8261 break;
8262 default:
8263 VMA_ASSERT(0 && "Character not currently supported.");
8264 break;
8265 }
8266 }
8267 }
8268
8269 void VmaJsonWriter::ContinueString(uint32_t n)
8270 {
8271 VMA_ASSERT(m_InsideString);
8272 m_SB.AddNumber(n);
8273 }
8274
8275 void VmaJsonWriter::ContinueString(uint64_t n)
8276 {
8277 VMA_ASSERT(m_InsideString);
8278 m_SB.AddNumber(n);
8279 }
8280
8281 void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
8282 {
8283 VMA_ASSERT(m_InsideString);
8284 m_SB.AddPointer(ptr);
8285 }
8286
8287 void VmaJsonWriter::EndString(const char* pStr)
8288 {
8289 VMA_ASSERT(m_InsideString);
8290 if (pStr != VMA_NULL && pStr[0] != '\0')
8291 {
8292 ContinueString(pStr);
8293 }
8294 m_SB.Add('"');
8295 m_InsideString = false;
8296 }
8297
8298 void VmaJsonWriter::WriteNumber(uint32_t n)
8299 {
8300 VMA_ASSERT(!m_InsideString);
8301 BeginValue(false);
8302 m_SB.AddNumber(n);
8303 }
8304
8305 void VmaJsonWriter::WriteNumber(uint64_t n)
8306 {
8307 VMA_ASSERT(!m_InsideString);
8308 BeginValue(false);
8309 m_SB.AddNumber(n);
8310 }
8311
8312 void VmaJsonWriter::WriteBool(bool b)
8313 {
8314 VMA_ASSERT(!m_InsideString);
8315 BeginValue(false);
8316 m_SB.Add(b ? "true" : "false");
8317 }
8318
8319 void VmaJsonWriter::WriteNull()
8320 {
8321 VMA_ASSERT(!m_InsideString);
8322 BeginValue(false);
8323 m_SB.Add("null");
8324 }
8325
8326 void VmaJsonWriter::BeginValue(bool isString)
8327 {
8328 if (!m_Stack.empty())
8329 {
8330 StackItem& currItem = m_Stack.back();
8331 if (currItem.type == COLLECTION_TYPE_OBJECT &&
8332 currItem.valueCount % 2 == 0)
8333 {
8334 VMA_ASSERT(isString);
8335 }
8336
8337 if (currItem.type == COLLECTION_TYPE_OBJECT &&
8338 currItem.valueCount % 2 != 0)
8339 {
8340 m_SB.Add(": ");
8341 }
8342 else if (currItem.valueCount > 0)
8343 {
8344 m_SB.Add(", ");
8345 WriteIndent();
8346 }
8347 else
8348 {
8349 WriteIndent();
8350 }
8351 ++currItem.valueCount;
8352 }
8353 }
8354
8355 void VmaJsonWriter::WriteIndent(bool oneLess)
8356 {
8357 if (!m_Stack.empty() && !m_Stack.back().singleLineMode)
8358 {
8359 m_SB.AddNewLine();
8360
8361 size_t count = m_Stack.size();
8362 if (count > 0 && oneLess)
8363 {
8364 --count;
8365 }
8366 for (size_t i = 0; i < count; ++i)
8367 {
8368 m_SB.Add(INDENT);
8369 }
8370 }
8371 }
8372
8373 #endif // #if VMA_STATS_STRING_ENABLED
8374
8375 ////////////////////////////////////////////////////////////////////////////////
8376
8377 void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
8378 {
8379 if (IsUserDataString())
8380 {
8381 VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
8382
8383 FreeUserDataString(hAllocator);
8384
8385 if (pUserData != VMA_NULL)
8386 {
8387 m_pUserData = VmaCreateStringCopy(hAllocator->GetAllocationCallbacks(), (const char*)pUserData);
8388 }
8389 }
8390 else
8391 {
8392 m_pUserData = pUserData;
8393 }
8394 }
8395
8396 void VmaAllocation_T::ChangeBlockAllocation(
8397 VmaAllocator hAllocator,
8398 VmaDeviceMemoryBlock* block,
8399 VkDeviceSize offset)
8400 {
8401 VMA_ASSERT(block != VMA_NULL);
8402 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
8403
8404 // Move mapping reference counter from old block to new block.
8405 if (block != m_BlockAllocation.m_Block)
8406 {
8407 uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
8408 if (IsPersistentMap())
8409 ++mapRefCount;
8410 m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
8411 block->Map(hAllocator, mapRefCount, VMA_NULL);
8412 }
8413
8414 m_BlockAllocation.m_Block = block;
8415 m_BlockAllocation.m_Offset = offset;
8416 }
8417
8418 void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
8419 {
8420 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
8421 m_BlockAllocation.m_Offset = newOffset;
8422 }
8423
8424 VkDeviceSize VmaAllocation_T::GetOffset() const
8425 {
8426 switch (m_Type)
8427 {
8428 case ALLOCATION_TYPE_BLOCK:
8429 return m_BlockAllocation.m_Offset;
8430 case ALLOCATION_TYPE_DEDICATED:
8431 return 0;
8432 default:
8433 VMA_ASSERT(0);
8434 return 0;
8435 }
8436 }
8437
8438 VkDeviceMemory VmaAllocation_T::GetMemory() const
8439 {
8440 switch (m_Type)
8441 {
8442 case ALLOCATION_TYPE_BLOCK:
8443 return m_BlockAllocation.m_Block->GetDeviceMemory();
8444 case ALLOCATION_TYPE_DEDICATED:
8445 return m_DedicatedAllocation.m_hMemory;
8446 default:
8447 VMA_ASSERT(0);
8448 return VK_NULL_HANDLE;
8449 }
8450 }
8451
8452 void* VmaAllocation_T::GetMappedData() const
8453 {
8454 switch (m_Type)
8455 {
8456 case ALLOCATION_TYPE_BLOCK:
8457 if (m_MapCount != 0)
8458 {
8459 void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
8460 VMA_ASSERT(pBlockData != VMA_NULL);
8461 return (char*)pBlockData + m_BlockAllocation.m_Offset;
8462 }
8463 else
8464 {
8465 return VMA_NULL;
8466 }
8467 break;
8468 case ALLOCATION_TYPE_DEDICATED:
8469 VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
8470 return m_DedicatedAllocation.m_pMappedData;
8471 default:
8472 VMA_ASSERT(0);
8473 return VMA_NULL;
8474 }
8475 }
8476
8477 bool VmaAllocation_T::CanBecomeLost() const
8478 {
8479 switch (m_Type)
8480 {
8481 case ALLOCATION_TYPE_BLOCK:
8482 return m_BlockAllocation.m_CanBecomeLost;
8483 case ALLOCATION_TYPE_DEDICATED:
8484 return false;
8485 default:
8486 VMA_ASSERT(0);
8487 return false;
8488 }
8489 }
8490
8491 bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
8492 {
8493 VMA_ASSERT(CanBecomeLost());
8494
8495 /*
8496 Warning: This is a carefully designed algorithm.
8497 Do not modify unless you really know what you're doing :)
8498 */
8499 uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
8500 for (;;)
8501 {
8502 if (localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
8503 {
8504 VMA_ASSERT(0);
8505 return false;
8506 }
8507 else if (localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
8508 {
8509 return false;
8510 }
8511 else // Last use time earlier than current time.
8512 {
8513 if (CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
8514 {
8515 // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
8516 // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
8517 return true;
8518 }
8519 }
8520 }
8521 }
8522
8523 #if VMA_STATS_STRING_ENABLED
8524
8525 // Correspond to values of enum VmaSuballocationType.
8526 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
8527 "FREE",
8528 "UNKNOWN",
8529 "BUFFER",
8530 "IMAGE_UNKNOWN",
8531 "IMAGE_LINEAR",
8532 "IMAGE_OPTIMAL",
8533 };
8534
8535 void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
8536 {
8537 json.WriteString("Type");
8538 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
8539
8540 json.WriteString("Size");
8541 json.WriteNumber(m_Size);
8542
8543 if (m_pUserData != VMA_NULL)
8544 {
8545 json.WriteString("UserData");
8546 if (IsUserDataString())
8547 {
8548 json.WriteString((const char*)m_pUserData);
8549 }
8550 else
8551 {
8552 json.BeginString();
8553 json.ContinueString_Pointer(m_pUserData);
8554 json.EndString();
8555 }
8556 }
8557
8558 json.WriteString("CreationFrameIndex");
8559 json.WriteNumber(m_CreationFrameIndex);
8560
8561 json.WriteString("LastUseFrameIndex");
8562 json.WriteNumber(GetLastUseFrameIndex());
8563
8564 if (m_BufferImageUsage != 0)
8565 {
8566 json.WriteString("Usage");
8567 json.WriteNumber(m_BufferImageUsage);
8568 }
8569 }
8570
8571 #endif
8572
8573 void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
8574 {
8575 VMA_ASSERT(IsUserDataString());
8576 VmaFreeString(hAllocator->GetAllocationCallbacks(), (char*)m_pUserData);
8577 m_pUserData = VMA_NULL;
8578 }
8579
8580 void VmaAllocation_T::BlockAllocMap()
8581 {
8582 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
8583
8584 if ((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
8585 {
8586 ++m_MapCount;
8587 }
8588 else
8589 {
8590 VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
8591 }
8592 }
8593
8594 void VmaAllocation_T::BlockAllocUnmap()
8595 {
8596 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
8597
8598 if ((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
8599 {
8600 --m_MapCount;
8601 }
8602 else
8603 {
8604 VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
8605 }
8606 }
8607
8608 VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
8609 {
8610 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
8611
8612 if (m_MapCount != 0)
8613 {
8614 if ((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
8615 {
8616 VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
8617 *ppData = m_DedicatedAllocation.m_pMappedData;
8618 ++m_MapCount;
8619 return VK_SUCCESS;
8620 }
8621 else
8622 {
8623 VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
8624 return VK_ERROR_MEMORY_MAP_FAILED;
8625 }
8626 }
8627 else
8628 {
8629 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
8630 hAllocator->m_hDevice,
8631 m_DedicatedAllocation.m_hMemory,
8632 0, // offset
8633 VK_WHOLE_SIZE,
8634 0, // flags
8635 ppData);
8636 if (result == VK_SUCCESS)
8637 {
8638 m_DedicatedAllocation.m_pMappedData = *ppData;
8639 m_MapCount = 1;
8640 }
8641 return result;
8642 }
8643 }
8644
8645 void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
8646 {
8647 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
8648
8649 if ((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
8650 {
8651 --m_MapCount;
8652 if (m_MapCount == 0)
8653 {
8654 m_DedicatedAllocation.m_pMappedData = VMA_NULL;
8655 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
8656 hAllocator->m_hDevice,
8657 m_DedicatedAllocation.m_hMemory);
8658 }
8659 }
8660 else
8661 {
8662 VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
8663 }
8664 }
8665
8666 #if VMA_STATS_STRING_ENABLED
8667
8668 static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
8669 {
8670 json.BeginObject();
8671
8672 json.WriteString("Blocks");
8673 json.WriteNumber(stat.blockCount);
8674
8675 json.WriteString("Allocations");
8676 json.WriteNumber(stat.allocationCount);
8677
8678 json.WriteString("UnusedRanges");
8679 json.WriteNumber(stat.unusedRangeCount);
8680
8681 json.WriteString("UsedBytes");
8682 json.WriteNumber(stat.usedBytes);
8683
8684 json.WriteString("UnusedBytes");
8685 json.WriteNumber(stat.unusedBytes);
8686
8687 if (stat.allocationCount > 1)
8688 {
8689 json.WriteString("AllocationSize");
8690 json.BeginObject(true);
8691 json.WriteString("Min");
8692 json.WriteNumber(stat.allocationSizeMin);
8693 json.WriteString("Avg");
8694 json.WriteNumber(stat.allocationSizeAvg);
8695 json.WriteString("Max");
8696 json.WriteNumber(stat.allocationSizeMax);
8697 json.EndObject();
8698 }
8699
8700 if (stat.unusedRangeCount > 1)
8701 {
8702 json.WriteString("UnusedRangeSize");
8703 json.BeginObject(true);
8704 json.WriteString("Min");
8705 json.WriteNumber(stat.unusedRangeSizeMin);
8706 json.WriteString("Avg");
8707 json.WriteNumber(stat.unusedRangeSizeAvg);
8708 json.WriteString("Max");
8709 json.WriteNumber(stat.unusedRangeSizeMax);
8710 json.EndObject();
8711 }
8712
8713 json.EndObject();
8714 }
8715
8716 #endif // #if VMA_STATS_STRING_ENABLED
8717
8718 struct VmaSuballocationItemSizeLess
8719 {
8720 bool operator()(
8721 const VmaSuballocationList::iterator lhs,
8722 const VmaSuballocationList::iterator rhs) const
8723 {
8724 return lhs->size < rhs->size;
8725 }
8726 bool operator()(
8727 const VmaSuballocationList::iterator lhs,
8728 VkDeviceSize rhsSize) const
8729 {
8730 return lhs->size < rhsSize;
8731 }
8732 };
8733
8734
8735 ////////////////////////////////////////////////////////////////////////////////
8736 // class VmaBlockMetadata
8737
8738 VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
8739 m_Size(0),
8740 m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks())
8741 {
8742 }
8743
8744 #if VMA_STATS_STRING_ENABLED
8745
8746 void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
8747 VkDeviceSize unusedBytes,
8748 size_t allocationCount,
8749 size_t unusedRangeCount) const
8750 {
8751 json.BeginObject();
8752
8753 json.WriteString("TotalBytes");
8754 json.WriteNumber(GetSize());
8755
8756 json.WriteString("UnusedBytes");
8757 json.WriteNumber(unusedBytes);
8758
8759 json.WriteString("Allocations");
8760 json.WriteNumber((uint64_t)allocationCount);
8761
8762 json.WriteString("UnusedRanges");
8763 json.WriteNumber((uint64_t)unusedRangeCount);
8764
8765 json.WriteString("Suballocations");
8766 json.BeginArray();
8767 }
8768
8769 void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
8770 VkDeviceSize offset,
8771 VmaAllocation hAllocation) const
8772 {
8773 json.BeginObject(true);
8774
8775 json.WriteString("Offset");
8776 json.WriteNumber(offset);
8777
8778 hAllocation->PrintParameters(json);
8779
8780 json.EndObject();
8781 }
8782
8783 void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
8784 VkDeviceSize offset,
8785 VkDeviceSize size) const
8786 {
8787 json.BeginObject(true);
8788
8789 json.WriteString("Offset");
8790 json.WriteNumber(offset);
8791
8792 json.WriteString("Type");
8793 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
8794
8795 json.WriteString("Size");
8796 json.WriteNumber(size);
8797
8798 json.EndObject();
8799 }
8800
8801 void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
8802 {
8803 json.EndArray();
8804 json.EndObject();
8805 }
8806
8807 #endif // #if VMA_STATS_STRING_ENABLED
8808
8809 ////////////////////////////////////////////////////////////////////////////////
8810 // class VmaBlockMetadata_Generic
8811
8812 VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) :
8813 VmaBlockMetadata(hAllocator),
8814 m_FreeCount(0),
8815 m_SumFreeSize(0),
8816 m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8817 m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
8818 {
8819 }
8820
8821 VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
8822 {
8823 }
8824
8825 void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
8826 {
8827 VmaBlockMetadata::Init(size);
8828
8829 m_FreeCount = 1;
8830 m_SumFreeSize = size;
8831
8832 VmaSuballocation suballoc = {};
8833 suballoc.offset = 0;
8834 suballoc.size = size;
8835 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8836 suballoc.hAllocation = VK_NULL_HANDLE;
8837
8838 VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
8839 m_Suballocations.push_back(suballoc);
8840 VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
8841 --suballocItem;
8842 m_FreeSuballocationsBySize.push_back(suballocItem);
8843 }
8844
8845 bool VmaBlockMetadata_Generic::Validate() const
8846 {
8847 VMA_VALIDATE(!m_Suballocations.empty());
8848
8849 // Expected offset of new suballocation as calculated from previous ones.
8850 VkDeviceSize calculatedOffset = 0;
8851 // Expected number of free suballocations as calculated from traversing their list.
8852 uint32_t calculatedFreeCount = 0;
8853 // Expected sum size of free suballocations as calculated from traversing their list.
8854 VkDeviceSize calculatedSumFreeSize = 0;
8855 // Expected number of free suballocations that should be registered in
8856 // m_FreeSuballocationsBySize calculated from traversing their list.
8857 size_t freeSuballocationsToRegister = 0;
8858 // True if previous visited suballocation was free.
8859 bool prevFree = false;
8860
8861 for (VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
8862 suballocItem != m_Suballocations.cend();
8863 ++suballocItem)
8864 {
8865 const VmaSuballocation& subAlloc = *suballocItem;
8866
8867 // Actual offset of this suballocation doesn't match expected one.
8868 VMA_VALIDATE(subAlloc.offset == calculatedOffset);
8869
8870 const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
8871 // Two adjacent free suballocations are invalid. They should be merged.
8872 VMA_VALIDATE(!prevFree || !currFree);
8873
8874 VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE));
8875
8876 if (currFree)
8877 {
8878 calculatedSumFreeSize += subAlloc.size;
8879 ++calculatedFreeCount;
8880 if (subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8881 {
8882 ++freeSuballocationsToRegister;
8883 }
8884
8885 // Margin required between allocations - every free space must be at least that large.
8886 VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);
8887 }
8888 else
8889 {
8890 VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset);
8891 VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size);
8892
8893 // Margin required between allocations - previous allocation must be free.
8894 VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);
8895 }
8896
8897 calculatedOffset += subAlloc.size;
8898 prevFree = currFree;
8899 }
8900
8901 // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
8902 // match expected one.
8903 VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
8904
8905 VkDeviceSize lastSize = 0;
8906 for (size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
8907 {
8908 VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
8909
8910 // Only free suballocations can be registered in m_FreeSuballocationsBySize.
8911 VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
8912 // They must be sorted by size ascending.
8913 VMA_VALIDATE(suballocItem->size >= lastSize);
8914
8915 lastSize = suballocItem->size;
8916 }
8917
8918 // Check if totals match calculacted values.
8919 VMA_VALIDATE(ValidateFreeSuballocationList());
8920 VMA_VALIDATE(calculatedOffset == GetSize());
8921 VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
8922 VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
8923
8924 return true;
8925 }
8926
8927 VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
8928 {
8929 if (!m_FreeSuballocationsBySize.empty())
8930 {
8931 return m_FreeSuballocationsBySize.back()->size;
8932 }
8933 else
8934 {
8935 return 0;
8936 }
8937 }
8938
8939 bool VmaBlockMetadata_Generic::IsEmpty() const
8940 {
8941 return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
8942 }
8943
8944 void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
8945 {
8946 outInfo.blockCount = 1;
8947
8948 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
8949 outInfo.allocationCount = rangeCount - m_FreeCount;
8950 outInfo.unusedRangeCount = m_FreeCount;
8951
8952 outInfo.unusedBytes = m_SumFreeSize;
8953 outInfo.usedBytes = GetSize() - outInfo.unusedBytes;
8954
8955 outInfo.allocationSizeMin = UINT64_MAX;
8956 outInfo.allocationSizeMax = 0;
8957 outInfo.unusedRangeSizeMin = UINT64_MAX;
8958 outInfo.unusedRangeSizeMax = 0;
8959
8960 for (VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
8961 suballocItem != m_Suballocations.cend();
8962 ++suballocItem)
8963 {
8964 const VmaSuballocation& suballoc = *suballocItem;
8965 if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
8966 {
8967 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
8968 outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
8969 }
8970 else
8971 {
8972 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
8973 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
8974 }
8975 }
8976 }
8977
8978 void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
8979 {
8980 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
8981
8982 inoutStats.size += GetSize();
8983 inoutStats.unusedSize += m_SumFreeSize;
8984 inoutStats.allocationCount += rangeCount - m_FreeCount;
8985 inoutStats.unusedRangeCount += m_FreeCount;
8986 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
8987 }
8988
8989 #if VMA_STATS_STRING_ENABLED
8990
8991 void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
8992 {
8993 PrintDetailedMap_Begin(json,
8994 m_SumFreeSize, // unusedBytes
8995 m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
8996 m_FreeCount); // unusedRangeCount
8997
8998 size_t i = 0;
8999 for (VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
9000 suballocItem != m_Suballocations.cend();
9001 ++suballocItem, ++i)
9002 {
9003 if (suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9004 {
9005 PrintDetailedMap_UnusedRange(json, suballocItem->offset, suballocItem->size);
9006 }
9007 else
9008 {
9009 PrintDetailedMap_Allocation(json, suballocItem->offset, suballocItem->hAllocation);
9010 }
9011 }
9012
9013 PrintDetailedMap_End(json);
9014 }
9015
9016 #endif // #if VMA_STATS_STRING_ENABLED
9017
9018 bool VmaBlockMetadata_Generic::CreateAllocationRequest(
9019 uint32_t currentFrameIndex,
9020 uint32_t frameInUseCount,
9021 VkDeviceSize bufferImageGranularity,
9022 VkDeviceSize allocSize,
9023 VkDeviceSize allocAlignment,
9024 bool upperAddress,
9025 VmaSuballocationType allocType,
9026 bool canMakeOtherLost,
9027 uint32_t strategy,
9028 VmaAllocationRequest* pAllocationRequest)
9029 {
9030 VMA_ASSERT(allocSize > 0);
9031 VMA_ASSERT(!upperAddress);
9032 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9033 VMA_ASSERT(pAllocationRequest != VMA_NULL);
9034 VMA_HEAVY_ASSERT(Validate());
9035
9036 pAllocationRequest->type = VmaAllocationRequestType::Normal;
9037
9038 // There is not enough total free space in this block to fullfill the request: Early return.
9039 if (canMakeOtherLost == false &&
9040 m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN)
9041 {
9042 return false;
9043 }
9044
9045 // New algorithm, efficiently searching freeSuballocationsBySize.
9046 const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
9047 if (freeSuballocCount > 0)
9048 {
9049 if (strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
9050 {
9051 // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN.
9052 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
9053 m_FreeSuballocationsBySize.data(),
9054 m_FreeSuballocationsBySize.data() + freeSuballocCount,
9055 allocSize + 2 * VMA_DEBUG_MARGIN,
9056 VmaSuballocationItemSizeLess());
9057 size_t index = it - m_FreeSuballocationsBySize.data();
9058 for (; index < freeSuballocCount; ++index)
9059 {
9060 if (CheckAllocation(
9061 currentFrameIndex,
9062 frameInUseCount,
9063 bufferImageGranularity,
9064 allocSize,
9065 allocAlignment,
9066 allocType,
9067 m_FreeSuballocationsBySize[index],
9068 false, // canMakeOtherLost
9069 &pAllocationRequest->offset,
9070 &pAllocationRequest->itemsToMakeLostCount,
9071 &pAllocationRequest->sumFreeSize,
9072 &pAllocationRequest->sumItemSize))
9073 {
9074 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
9075 return true;
9076 }
9077 }
9078 }
9079 else if (strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
9080 {
9081 for (VmaSuballocationList::iterator it = m_Suballocations.begin();
9082 it != m_Suballocations.end();
9083 ++it)
9084 {
9085 if (it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
9086 currentFrameIndex,
9087 frameInUseCount,
9088 bufferImageGranularity,
9089 allocSize,
9090 allocAlignment,
9091 allocType,
9092 it,
9093 false, // canMakeOtherLost
9094 &pAllocationRequest->offset,
9095 &pAllocationRequest->itemsToMakeLostCount,
9096 &pAllocationRequest->sumFreeSize,
9097 &pAllocationRequest->sumItemSize))
9098 {
9099 pAllocationRequest->item = it;
9100 return true;
9101 }
9102 }
9103 }
9104 else // WORST_FIT, FIRST_FIT
9105 {
9106 // Search staring from biggest suballocations.
9107 for (size_t index = freeSuballocCount; index--; )
9108 {
9109 if (CheckAllocation(
9110 currentFrameIndex,
9111 frameInUseCount,
9112 bufferImageGranularity,
9113 allocSize,
9114 allocAlignment,
9115 allocType,
9116 m_FreeSuballocationsBySize[index],
9117 false, // canMakeOtherLost
9118 &pAllocationRequest->offset,
9119 &pAllocationRequest->itemsToMakeLostCount,
9120 &pAllocationRequest->sumFreeSize,
9121 &pAllocationRequest->sumItemSize))
9122 {
9123 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
9124 return true;
9125 }
9126 }
9127 }
9128 }
9129
9130 if (canMakeOtherLost)
9131 {
9132 // Brute-force algorithm. TODO: Come up with something better.
9133
9134 bool found = false;
9135 VmaAllocationRequest tmpAllocRequest = {};
9136 tmpAllocRequest.type = VmaAllocationRequestType::Normal;
9137 for (VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
9138 suballocIt != m_Suballocations.end();
9139 ++suballocIt)
9140 {
9141 if (suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
9142 suballocIt->hAllocation->CanBecomeLost())
9143 {
9144 if (CheckAllocation(
9145 currentFrameIndex,
9146 frameInUseCount,
9147 bufferImageGranularity,
9148 allocSize,
9149 allocAlignment,
9150 allocType,
9151 suballocIt,
9152 canMakeOtherLost,
9153 &tmpAllocRequest.offset,
9154 &tmpAllocRequest.itemsToMakeLostCount,
9155 &tmpAllocRequest.sumFreeSize,
9156 &tmpAllocRequest.sumItemSize))
9157 {
9158 if (strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
9159 {
9160 *pAllocationRequest = tmpAllocRequest;
9161 pAllocationRequest->item = suballocIt;
9162 break;
9163 }
9164 if (!found || tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost())
9165 {
9166 *pAllocationRequest = tmpAllocRequest;
9167 pAllocationRequest->item = suballocIt;
9168 found = true;
9169 }
9170 }
9171 }
9172 }
9173
9174 return found;
9175 }
9176
9177 return false;
9178 }
9179
9180 bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
9181 uint32_t currentFrameIndex,
9182 uint32_t frameInUseCount,
9183 VmaAllocationRequest* pAllocationRequest)
9184 {
9185 VMA_ASSERT(pAllocationRequest && pAllocationRequest->type == VmaAllocationRequestType::Normal);
9186
9187 while (pAllocationRequest->itemsToMakeLostCount > 0)
9188 {
9189 if (pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
9190 {
9191 ++pAllocationRequest->item;
9192 }
9193 VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
9194 VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
9195 VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
9196 if (pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
9197 {
9198 pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
9199 --pAllocationRequest->itemsToMakeLostCount;
9200 }
9201 else
9202 {
9203 return false;
9204 }
9205 }
9206
9207 VMA_HEAVY_ASSERT(Validate());
9208 VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
9209 VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
9210
9211 return true;
9212 }
9213
9214 uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
9215 {
9216 uint32_t lostAllocationCount = 0;
9217 for (VmaSuballocationList::iterator it = m_Suballocations.begin();
9218 it != m_Suballocations.end();
9219 ++it)
9220 {
9221 if (it->type != VMA_SUBALLOCATION_TYPE_FREE &&
9222 it->hAllocation->CanBecomeLost() &&
9223 it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
9224 {
9225 it = FreeSuballocation(it);
9226 ++lostAllocationCount;
9227 }
9228 }
9229 return lostAllocationCount;
9230 }
9231
9232 VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
9233 {
9234 for (VmaSuballocationList::iterator it = m_Suballocations.begin();
9235 it != m_Suballocations.end();
9236 ++it)
9237 {
9238 if (it->type != VMA_SUBALLOCATION_TYPE_FREE)
9239 {
9240 if (!VmaValidateMagicValue(pBlockData, it->offset - VMA_DEBUG_MARGIN))
9241 {
9242 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
9243 return VK_ERROR_VALIDATION_FAILED_EXT;
9244 }
9245 if (!VmaValidateMagicValue(pBlockData, it->offset + it->size))
9246 {
9247 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
9248 return VK_ERROR_VALIDATION_FAILED_EXT;
9249 }
9250 }
9251 }
9252
9253 return VK_SUCCESS;
9254 }
9255
9256 void VmaBlockMetadata_Generic::Alloc(
9257 const VmaAllocationRequest& request,
9258 VmaSuballocationType type,
9259 VkDeviceSize allocSize,
9260 VmaAllocation hAllocation)
9261 {
9262 VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
9263 VMA_ASSERT(request.item != m_Suballocations.end());
9264 VmaSuballocation& suballoc = *request.item;
9265 // Given suballocation is a free block.
9266 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9267 // Given offset is inside this suballocation.
9268 VMA_ASSERT(request.offset >= suballoc.offset);
9269 const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
9270 VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
9271 const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
9272
9273 // Unregister this free suballocation from m_FreeSuballocationsBySize and update
9274 // it to become used.
9275 UnregisterFreeSuballocation(request.item);
9276
9277 suballoc.offset = request.offset;
9278 suballoc.size = allocSize;
9279 suballoc.type = type;
9280 suballoc.hAllocation = hAllocation;
9281
9282 // If there are any free bytes remaining at the end, insert new free suballocation after current one.
9283 if (paddingEnd)
9284 {
9285 VmaSuballocation paddingSuballoc = {};
9286 paddingSuballoc.offset = request.offset + allocSize;
9287 paddingSuballoc.size = paddingEnd;
9288 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9289 VmaSuballocationList::iterator next = request.item;
9290 ++next;
9291 const VmaSuballocationList::iterator paddingEndItem =
9292 m_Suballocations.insert(next, paddingSuballoc);
9293 RegisterFreeSuballocation(paddingEndItem);
9294 }
9295
9296 // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
9297 if (paddingBegin)
9298 {
9299 VmaSuballocation paddingSuballoc = {};
9300 paddingSuballoc.offset = request.offset - paddingBegin;
9301 paddingSuballoc.size = paddingBegin;
9302 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9303 const VmaSuballocationList::iterator paddingBeginItem =
9304 m_Suballocations.insert(request.item, paddingSuballoc);
9305 RegisterFreeSuballocation(paddingBeginItem);
9306 }
9307
9308 // Update totals.
9309 m_FreeCount = m_FreeCount - 1;
9310 if (paddingBegin > 0)
9311 {
9312 ++m_FreeCount;
9313 }
9314 if (paddingEnd > 0)
9315 {
9316 ++m_FreeCount;
9317 }
9318 m_SumFreeSize -= allocSize;
9319 }
9320
9321 void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation)
9322 {
9323 for (VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
9324 suballocItem != m_Suballocations.end();
9325 ++suballocItem)
9326 {
9327 VmaSuballocation& suballoc = *suballocItem;
9328 if (suballoc.hAllocation == allocation)
9329 {
9330 FreeSuballocation(suballocItem);
9331 VMA_HEAVY_ASSERT(Validate());
9332 return;
9333 }
9334 }
9335 VMA_ASSERT(0 && "Not found!");
9336 }
9337
9338 void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
9339 {
9340 for (VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
9341 suballocItem != m_Suballocations.end();
9342 ++suballocItem)
9343 {
9344 VmaSuballocation& suballoc = *suballocItem;
9345 if (suballoc.offset == offset)
9346 {
9347 FreeSuballocation(suballocItem);
9348 return;
9349 }
9350 }
9351 VMA_ASSERT(0 && "Not found!");
9352 }
9353
9354 bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
9355 {
9356 VkDeviceSize lastSize = 0;
9357 for (size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
9358 {
9359 const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
9360
9361 VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
9362 VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
9363 VMA_VALIDATE(it->size >= lastSize);
9364 lastSize = it->size;
9365 }
9366 return true;
9367 }
9368
9369 bool VmaBlockMetadata_Generic::CheckAllocation(
9370 uint32_t currentFrameIndex,
9371 uint32_t frameInUseCount,
9372 VkDeviceSize bufferImageGranularity,
9373 VkDeviceSize allocSize,
9374 VkDeviceSize allocAlignment,
9375 VmaSuballocationType allocType,
9376 VmaSuballocationList::const_iterator suballocItem,
9377 bool canMakeOtherLost,
9378 VkDeviceSize* pOffset,
9379 size_t* itemsToMakeLostCount,
9380 VkDeviceSize* pSumFreeSize,
9381 VkDeviceSize* pSumItemSize) const
9382 {
9383 VMA_ASSERT(allocSize > 0);
9384 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9385 VMA_ASSERT(suballocItem != m_Suballocations.cend());
9386 VMA_ASSERT(pOffset != VMA_NULL);
9387
9388 *itemsToMakeLostCount = 0;
9389 *pSumFreeSize = 0;
9390 *pSumItemSize = 0;
9391
9392 if (canMakeOtherLost)
9393 {
9394 if (suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9395 {
9396 *pSumFreeSize = suballocItem->size;
9397 }
9398 else
9399 {
9400 if (suballocItem->hAllocation->CanBecomeLost() &&
9401 suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9402 {
9403 ++* itemsToMakeLostCount;
9404 *pSumItemSize = suballocItem->size;
9405 }
9406 else
9407 {
9408 return false;
9409 }
9410 }
9411
9412 // Remaining size is too small for this request: Early return.
9413 if (GetSize() - suballocItem->offset < allocSize)
9414 {
9415 return false;
9416 }
9417
9418 // Start from offset equal to beginning of this suballocation.
9419 *pOffset = suballocItem->offset;
9420
9421 // Apply VMA_DEBUG_MARGIN at the beginning.
9422 if (VMA_DEBUG_MARGIN > 0)
9423 {
9424 *pOffset += VMA_DEBUG_MARGIN;
9425 }
9426
9427 // Apply alignment.
9428 *pOffset = VmaAlignUp(*pOffset, allocAlignment);
9429
9430 // Check previous suballocations for BufferImageGranularity conflicts.
9431 // Make bigger alignment if necessary.
9432 if (bufferImageGranularity > 1)
9433 {
9434 bool bufferImageGranularityConflict = false;
9435 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
9436 while (prevSuballocItem != m_Suballocations.cbegin())
9437 {
9438 --prevSuballocItem;
9439 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
9440 if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
9441 {
9442 if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9443 {
9444 bufferImageGranularityConflict = true;
9445 break;
9446 }
9447 }
9448 else
9449 // Already on previous page.
9450 break;
9451 }
9452 if (bufferImageGranularityConflict)
9453 {
9454 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
9455 }
9456 }
9457
9458 // Now that we have final *pOffset, check if we are past suballocItem.
9459 // If yes, return false - this function should be called for another suballocItem as starting point.
9460 if (*pOffset >= suballocItem->offset + suballocItem->size)
9461 {
9462 return false;
9463 }
9464
9465 // Calculate padding at the beginning based on current offset.
9466 const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
9467
9468 // Calculate required margin at the end.
9469 const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
9470
9471 const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
9472 // Another early return check.
9473 if (suballocItem->offset + totalSize > GetSize())
9474 {
9475 return false;
9476 }
9477
9478 // Advance lastSuballocItem until desired size is reached.
9479 // Update itemsToMakeLostCount.
9480 VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
9481 if (totalSize > suballocItem->size)
9482 {
9483 VkDeviceSize remainingSize = totalSize - suballocItem->size;
9484 while (remainingSize > 0)
9485 {
9486 ++lastSuballocItem;
9487 if (lastSuballocItem == m_Suballocations.cend())
9488 {
9489 return false;
9490 }
9491 if (lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9492 {
9493 *pSumFreeSize += lastSuballocItem->size;
9494 }
9495 else
9496 {
9497 VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
9498 if (lastSuballocItem->hAllocation->CanBecomeLost() &&
9499 lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9500 {
9501 ++* itemsToMakeLostCount;
9502 *pSumItemSize += lastSuballocItem->size;
9503 }
9504 else
9505 {
9506 return false;
9507 }
9508 }
9509 remainingSize = (lastSuballocItem->size < remainingSize) ?
9510 remainingSize - lastSuballocItem->size : 0;
9511 }
9512 }
9513
9514 // Check next suballocations for BufferImageGranularity conflicts.
9515 // If conflict exists, we must mark more allocations lost or fail.
9516 if (bufferImageGranularity > 1)
9517 {
9518 VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
9519 ++nextSuballocItem;
9520 while (nextSuballocItem != m_Suballocations.cend())
9521 {
9522 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
9523 if (VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9524 {
9525 if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9526 {
9527 VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
9528 if (nextSuballoc.hAllocation->CanBecomeLost() &&
9529 nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9530 {
9531 ++* itemsToMakeLostCount;
9532 }
9533 else
9534 {
9535 return false;
9536 }
9537 }
9538 }
9539 else
9540 {
9541 // Already on next page.
9542 break;
9543 }
9544 ++nextSuballocItem;
9545 }
9546 }
9547 }
9548 else
9549 {
9550 const VmaSuballocation& suballoc = *suballocItem;
9551 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9552
9553 *pSumFreeSize = suballoc.size;
9554
9555 // Size of this suballocation is too small for this request: Early return.
9556 if (suballoc.size < allocSize)
9557 {
9558 return false;
9559 }
9560
9561 // Start from offset equal to beginning of this suballocation.
9562 *pOffset = suballoc.offset;
9563
9564 // Apply VMA_DEBUG_MARGIN at the beginning.
9565 if (VMA_DEBUG_MARGIN > 0)
9566 {
9567 *pOffset += VMA_DEBUG_MARGIN;
9568 }
9569
9570 // Apply alignment.
9571 *pOffset = VmaAlignUp(*pOffset, allocAlignment);
9572
9573 // Check previous suballocations for BufferImageGranularity conflicts.
9574 // Make bigger alignment if necessary.
9575 if (bufferImageGranularity > 1)
9576 {
9577 bool bufferImageGranularityConflict = false;
9578 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
9579 while (prevSuballocItem != m_Suballocations.cbegin())
9580 {
9581 --prevSuballocItem;
9582 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
9583 if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
9584 {
9585 if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9586 {
9587 bufferImageGranularityConflict = true;
9588 break;
9589 }
9590 }
9591 else
9592 // Already on previous page.
9593 break;
9594 }
9595 if (bufferImageGranularityConflict)
9596 {
9597 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
9598 }
9599 }
9600
9601 // Calculate padding at the beginning based on current offset.
9602 const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
9603
9604 // Calculate required margin at the end.
9605 const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
9606
9607 // Fail if requested size plus margin before and after is bigger than size of this suballocation.
9608 if (paddingBegin + allocSize + requiredEndMargin > suballoc.size)
9609 {
9610 return false;
9611 }
9612
9613 // Check next suballocations for BufferImageGranularity conflicts.
9614 // If conflict exists, allocation cannot be made here.
9615 if (bufferImageGranularity > 1)
9616 {
9617 VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
9618 ++nextSuballocItem;
9619 while (nextSuballocItem != m_Suballocations.cend())
9620 {
9621 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
9622 if (VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9623 {
9624 if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9625 {
9626 return false;
9627 }
9628 }
9629 else
9630 {
9631 // Already on next page.
9632 break;
9633 }
9634 ++nextSuballocItem;
9635 }
9636 }
9637 }
9638
9639 // All tests passed: Success. pOffset is already filled.
9640 return true;
9641 }
9642
9643 void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
9644 {
9645 VMA_ASSERT(item != m_Suballocations.end());
9646 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9647
9648 VmaSuballocationList::iterator nextItem = item;
9649 ++nextItem;
9650 VMA_ASSERT(nextItem != m_Suballocations.end());
9651 VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
9652
9653 item->size += nextItem->size;
9654 --m_FreeCount;
9655 m_Suballocations.erase(nextItem);
9656 }
9657
9658 VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
9659 {
9660 // Change this suballocation to be marked as free.
9661 VmaSuballocation& suballoc = *suballocItem;
9662 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9663 suballoc.hAllocation = VK_NULL_HANDLE;
9664
9665 // Update totals.
9666 ++m_FreeCount;
9667 m_SumFreeSize += suballoc.size;
9668
9669 // Merge with previous and/or next suballocation if it's also free.
9670 bool mergeWithNext = false;
9671 bool mergeWithPrev = false;
9672
9673 VmaSuballocationList::iterator nextItem = suballocItem;
9674 ++nextItem;
9675 if ((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
9676 {
9677 mergeWithNext = true;
9678 }
9679
9680 VmaSuballocationList::iterator prevItem = suballocItem;
9681 if (suballocItem != m_Suballocations.begin())
9682 {
9683 --prevItem;
9684 if (prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9685 {
9686 mergeWithPrev = true;
9687 }
9688 }
9689
9690 if (mergeWithNext)
9691 {
9692 UnregisterFreeSuballocation(nextItem);
9693 MergeFreeWithNext(suballocItem);
9694 }
9695
9696 if (mergeWithPrev)
9697 {
9698 UnregisterFreeSuballocation(prevItem);
9699 MergeFreeWithNext(prevItem);
9700 RegisterFreeSuballocation(prevItem);
9701 return prevItem;
9702 }
9703 else
9704 {
9705 RegisterFreeSuballocation(suballocItem);
9706 return suballocItem;
9707 }
9708 }
9709
9710 void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
9711 {
9712 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9713 VMA_ASSERT(item->size > 0);
9714
9715 // You may want to enable this validation at the beginning or at the end of
9716 // this function, depending on what do you want to check.
9717 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9718
9719 if (item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
9720 {
9721 if (m_FreeSuballocationsBySize.empty())
9722 {
9723 m_FreeSuballocationsBySize.push_back(item);
9724 }
9725 else
9726 {
9727 VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
9728 }
9729 }
9730
9731 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9732 }
9733
9734
9735 void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
9736 {
9737 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9738 VMA_ASSERT(item->size > 0);
9739
9740 // You may want to enable this validation at the beginning or at the end of
9741 // this function, depending on what do you want to check.
9742 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9743
9744 if (item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
9745 {
9746 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
9747 m_FreeSuballocationsBySize.data(),
9748 m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
9749 item,
9750 VmaSuballocationItemSizeLess());
9751 for (size_t index = it - m_FreeSuballocationsBySize.data();
9752 index < m_FreeSuballocationsBySize.size();
9753 ++index)
9754 {
9755 if (m_FreeSuballocationsBySize[index] == item)
9756 {
9757 VmaVectorRemove(m_FreeSuballocationsBySize, index);
9758 return;
9759 }
9760 VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
9761 }
9762 VMA_ASSERT(0 && "Not found.");
9763 }
9764
9765 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9766 }
9767
9768 bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
9769 VkDeviceSize bufferImageGranularity,
9770 VmaSuballocationType& inOutPrevSuballocType) const
9771 {
9772 if (bufferImageGranularity == 1 || IsEmpty())
9773 {
9774 return false;
9775 }
9776
9777 VkDeviceSize minAlignment = VK_WHOLE_SIZE;
9778 bool typeConflictFound = false;
9779 for (VmaSuballocationList::const_iterator it = m_Suballocations.cbegin();
9780 it != m_Suballocations.cend();
9781 ++it)
9782 {
9783 const VmaSuballocationType suballocType = it->type;
9784 if (suballocType != VMA_SUBALLOCATION_TYPE_FREE)
9785 {
9786 minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment());
9787 if (VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))
9788 {
9789 typeConflictFound = true;
9790 }
9791 inOutPrevSuballocType = suballocType;
9792 }
9793 }
9794
9795 return typeConflictFound || minAlignment >= bufferImageGranularity;
9796 }
9797
9798 ////////////////////////////////////////////////////////////////////////////////
9799 // class VmaBlockMetadata_Linear
9800
9801 VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) :
9802 VmaBlockMetadata(hAllocator),
9803 m_SumFreeSize(0),
9804 m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
9805 m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
9806 m_1stVectorIndex(0),
9807 m_2ndVectorMode(SECOND_VECTOR_EMPTY),
9808 m_1stNullItemsBeginCount(0),
9809 m_1stNullItemsMiddleCount(0),
9810 m_2ndNullItemsCount(0)
9811 {
9812 }
9813
9814 VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
9815 {
9816 }
9817
9818 void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
9819 {
9820 VmaBlockMetadata::Init(size);
9821 m_SumFreeSize = size;
9822 }
9823
9824 bool VmaBlockMetadata_Linear::Validate() const
9825 {
9826 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9827 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9828
9829 VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
9830 VMA_VALIDATE(!suballocations1st.empty() ||
9831 suballocations2nd.empty() ||
9832 m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
9833
9834 if (!suballocations1st.empty())
9835 {
9836 // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
9837 VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE);
9838 // Null item at the end should be just pop_back().
9839 VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE);
9840 }
9841 if (!suballocations2nd.empty())
9842 {
9843 // Null item at the end should be just pop_back().
9844 VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE);
9845 }
9846
9847 VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
9848 VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
9849
9850 VkDeviceSize sumUsedSize = 0;
9851 const size_t suballoc1stCount = suballocations1st.size();
9852 VkDeviceSize offset = VMA_DEBUG_MARGIN;
9853
9854 if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9855 {
9856 const size_t suballoc2ndCount = suballocations2nd.size();
9857 size_t nullItem2ndCount = 0;
9858 for (size_t i = 0; i < suballoc2ndCount; ++i)
9859 {
9860 const VmaSuballocation& suballoc = suballocations2nd[i];
9861 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9862
9863 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
9864 VMA_VALIDATE(suballoc.offset >= offset);
9865
9866 if (!currFree)
9867 {
9868 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
9869 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
9870 sumUsedSize += suballoc.size;
9871 }
9872 else
9873 {
9874 ++nullItem2ndCount;
9875 }
9876
9877 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
9878 }
9879
9880 VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
9881 }
9882
9883 for (size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
9884 {
9885 const VmaSuballocation& suballoc = suballocations1st[i];
9886 VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
9887 suballoc.hAllocation == VK_NULL_HANDLE);
9888 }
9889
9890 size_t nullItem1stCount = m_1stNullItemsBeginCount;
9891
9892 for (size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
9893 {
9894 const VmaSuballocation& suballoc = suballocations1st[i];
9895 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9896
9897 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
9898 VMA_VALIDATE(suballoc.offset >= offset);
9899 VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
9900
9901 if (!currFree)
9902 {
9903 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
9904 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
9905 sumUsedSize += suballoc.size;
9906 }
9907 else
9908 {
9909 ++nullItem1stCount;
9910 }
9911
9912 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
9913 }
9914 VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
9915
9916 if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9917 {
9918 const size_t suballoc2ndCount = suballocations2nd.size();
9919 size_t nullItem2ndCount = 0;
9920 for (size_t i = suballoc2ndCount; i--; )
9921 {
9922 const VmaSuballocation& suballoc = suballocations2nd[i];
9923 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9924
9925 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
9926 VMA_VALIDATE(suballoc.offset >= offset);
9927
9928 if (!currFree)
9929 {
9930 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
9931 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
9932 sumUsedSize += suballoc.size;
9933 }
9934 else
9935 {
9936 ++nullItem2ndCount;
9937 }
9938
9939 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
9940 }
9941
9942 VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
9943 }
9944
9945 VMA_VALIDATE(offset <= GetSize());
9946 VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
9947
9948 return true;
9949 }
9950
9951 size_t VmaBlockMetadata_Linear::GetAllocationCount() const
9952 {
9953 return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) +
9954 AccessSuballocations2nd().size() - m_2ndNullItemsCount;
9955 }
9956
9957 VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
9958 {
9959 const VkDeviceSize size = GetSize();
9960
9961 /*
9962 We don't consider gaps inside allocation vectors with freed allocations because
9963 they are not suitable for reuse in linear allocator. We consider only space that
9964 is available for new allocations.
9965 */
9966 if (IsEmpty())
9967 {
9968 return size;
9969 }
9970
9971 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9972
9973 switch (m_2ndVectorMode)
9974 {
9975 case SECOND_VECTOR_EMPTY:
9976 /*
9977 Available space is after end of 1st, as well as before beginning of 1st (which
9978 whould make it a ring buffer).
9979 */
9980 {
9981 const size_t suballocations1stCount = suballocations1st.size();
9982 VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
9983 const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
9984 const VmaSuballocation& lastSuballoc = suballocations1st[suballocations1stCount - 1];
9985 return VMA_MAX(
9986 firstSuballoc.offset,
9987 size - (lastSuballoc.offset + lastSuballoc.size));
9988 }
9989 break;
9990
9991 case SECOND_VECTOR_RING_BUFFER:
9992 /*
9993 Available space is only between end of 2nd and beginning of 1st.
9994 */
9995 {
9996 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9997 const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
9998 const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
9999 return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
10000 }
10001 break;
10002
10003 case SECOND_VECTOR_DOUBLE_STACK:
10004 /*
10005 Available space is only between end of 1st and top of 2nd.
10006 */
10007 {
10008 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10009 const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
10010 const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
10011 return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
10012 }
10013 break;
10014
10015 default:
10016 VMA_ASSERT(0);
10017 return 0;
10018 }
10019 }
10020
10021 void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
10022 {
10023 const VkDeviceSize size = GetSize();
10024 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10025 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10026 const size_t suballoc1stCount = suballocations1st.size();
10027 const size_t suballoc2ndCount = suballocations2nd.size();
10028
10029 outInfo.blockCount = 1;
10030 outInfo.allocationCount = (uint32_t)GetAllocationCount();
10031 outInfo.unusedRangeCount = 0;
10032 outInfo.usedBytes = 0;
10033 outInfo.allocationSizeMin = UINT64_MAX;
10034 outInfo.allocationSizeMax = 0;
10035 outInfo.unusedRangeSizeMin = UINT64_MAX;
10036 outInfo.unusedRangeSizeMax = 0;
10037
10038 VkDeviceSize lastOffset = 0;
10039
10040 if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10041 {
10042 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10043 size_t nextAlloc2ndIndex = 0;
10044 while (lastOffset < freeSpace2ndTo1stEnd)
10045 {
10046 // Find next non-null allocation or move nextAllocIndex to the end.
10047 while (nextAlloc2ndIndex < suballoc2ndCount &&
10048 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10049 {
10050 ++nextAlloc2ndIndex;
10051 }
10052
10053 // Found non-null allocation.
10054 if (nextAlloc2ndIndex < suballoc2ndCount)
10055 {
10056 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10057
10058 // 1. Process free space before this allocation.
10059 if (lastOffset < suballoc.offset)
10060 {
10061 // There is free space from lastOffset to suballoc.offset.
10062 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10063 ++outInfo.unusedRangeCount;
10064 outInfo.unusedBytes += unusedRangeSize;
10065 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10066 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10067 }
10068
10069 // 2. Process this allocation.
10070 // There is allocation with suballoc.offset, suballoc.size.
10071 outInfo.usedBytes += suballoc.size;
10072 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10073 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10074
10075 // 3. Prepare for next iteration.
10076 lastOffset = suballoc.offset + suballoc.size;
10077 ++nextAlloc2ndIndex;
10078 }
10079 // We are at the end.
10080 else
10081 {
10082 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10083 if (lastOffset < freeSpace2ndTo1stEnd)
10084 {
10085 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10086 ++outInfo.unusedRangeCount;
10087 outInfo.unusedBytes += unusedRangeSize;
10088 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10089 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10090 }
10091
10092 // End of loop.
10093 lastOffset = freeSpace2ndTo1stEnd;
10094 }
10095 }
10096 }
10097
10098 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10099 const VkDeviceSize freeSpace1stTo2ndEnd =
10100 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10101 while (lastOffset < freeSpace1stTo2ndEnd)
10102 {
10103 // Find next non-null allocation or move nextAllocIndex to the end.
10104 while (nextAlloc1stIndex < suballoc1stCount &&
10105 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10106 {
10107 ++nextAlloc1stIndex;
10108 }
10109
10110 // Found non-null allocation.
10111 if (nextAlloc1stIndex < suballoc1stCount)
10112 {
10113 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10114
10115 // 1. Process free space before this allocation.
10116 if (lastOffset < suballoc.offset)
10117 {
10118 // There is free space from lastOffset to suballoc.offset.
10119 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10120 ++outInfo.unusedRangeCount;
10121 outInfo.unusedBytes += unusedRangeSize;
10122 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10123 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10124 }
10125
10126 // 2. Process this allocation.
10127 // There is allocation with suballoc.offset, suballoc.size.
10128 outInfo.usedBytes += suballoc.size;
10129 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10130 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10131
10132 // 3. Prepare for next iteration.
10133 lastOffset = suballoc.offset + suballoc.size;
10134 ++nextAlloc1stIndex;
10135 }
10136 // We are at the end.
10137 else
10138 {
10139 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10140 if (lastOffset < freeSpace1stTo2ndEnd)
10141 {
10142 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10143 ++outInfo.unusedRangeCount;
10144 outInfo.unusedBytes += unusedRangeSize;
10145 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10146 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10147 }
10148
10149 // End of loop.
10150 lastOffset = freeSpace1stTo2ndEnd;
10151 }
10152 }
10153
10154 if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10155 {
10156 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10157 while (lastOffset < size)
10158 {
10159 // Find next non-null allocation or move nextAllocIndex to the end.
10160 while (nextAlloc2ndIndex != SIZE_MAX &&
10161 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10162 {
10163 --nextAlloc2ndIndex;
10164 }
10165
10166 // Found non-null allocation.
10167 if (nextAlloc2ndIndex != SIZE_MAX)
10168 {
10169 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10170
10171 // 1. Process free space before this allocation.
10172 if (lastOffset < suballoc.offset)
10173 {
10174 // There is free space from lastOffset to suballoc.offset.
10175 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10176 ++outInfo.unusedRangeCount;
10177 outInfo.unusedBytes += unusedRangeSize;
10178 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10179 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10180 }
10181
10182 // 2. Process this allocation.
10183 // There is allocation with suballoc.offset, suballoc.size.
10184 outInfo.usedBytes += suballoc.size;
10185 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10186 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10187
10188 // 3. Prepare for next iteration.
10189 lastOffset = suballoc.offset + suballoc.size;
10190 --nextAlloc2ndIndex;
10191 }
10192 // We are at the end.
10193 else
10194 {
10195 // There is free space from lastOffset to size.
10196 if (lastOffset < size)
10197 {
10198 const VkDeviceSize unusedRangeSize = size - lastOffset;
10199 ++outInfo.unusedRangeCount;
10200 outInfo.unusedBytes += unusedRangeSize;
10201 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10202 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10203 }
10204
10205 // End of loop.
10206 lastOffset = size;
10207 }
10208 }
10209 }
10210
10211 outInfo.unusedBytes = size - outInfo.usedBytes;
10212 }
10213
10214 void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
10215 {
10216 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10217 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10218 const VkDeviceSize size = GetSize();
10219 const size_t suballoc1stCount = suballocations1st.size();
10220 const size_t suballoc2ndCount = suballocations2nd.size();
10221
10222 inoutStats.size += size;
10223
10224 VkDeviceSize lastOffset = 0;
10225
10226 if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10227 {
10228 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10229 size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
10230 while (lastOffset < freeSpace2ndTo1stEnd)
10231 {
10232 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10233 while (nextAlloc2ndIndex < suballoc2ndCount &&
10234 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10235 {
10236 ++nextAlloc2ndIndex;
10237 }
10238
10239 // Found non-null allocation.
10240 if (nextAlloc2ndIndex < suballoc2ndCount)
10241 {
10242 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10243
10244 // 1. Process free space before this allocation.
10245 if (lastOffset < suballoc.offset)
10246 {
10247 // There is free space from lastOffset to suballoc.offset.
10248 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10249 inoutStats.unusedSize += unusedRangeSize;
10250 ++inoutStats.unusedRangeCount;
10251 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10252 }
10253
10254 // 2. Process this allocation.
10255 // There is allocation with suballoc.offset, suballoc.size.
10256 ++inoutStats.allocationCount;
10257
10258 // 3. Prepare for next iteration.
10259 lastOffset = suballoc.offset + suballoc.size;
10260 ++nextAlloc2ndIndex;
10261 }
10262 // We are at the end.
10263 else
10264 {
10265 if (lastOffset < freeSpace2ndTo1stEnd)
10266 {
10267 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10268 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10269 inoutStats.unusedSize += unusedRangeSize;
10270 ++inoutStats.unusedRangeCount;
10271 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10272 }
10273
10274 // End of loop.
10275 lastOffset = freeSpace2ndTo1stEnd;
10276 }
10277 }
10278 }
10279
10280 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10281 const VkDeviceSize freeSpace1stTo2ndEnd =
10282 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10283 while (lastOffset < freeSpace1stTo2ndEnd)
10284 {
10285 // Find next non-null allocation or move nextAllocIndex to the end.
10286 while (nextAlloc1stIndex < suballoc1stCount &&
10287 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10288 {
10289 ++nextAlloc1stIndex;
10290 }
10291
10292 // Found non-null allocation.
10293 if (nextAlloc1stIndex < suballoc1stCount)
10294 {
10295 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10296
10297 // 1. Process free space before this allocation.
10298 if (lastOffset < suballoc.offset)
10299 {
10300 // There is free space from lastOffset to suballoc.offset.
10301 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10302 inoutStats.unusedSize += unusedRangeSize;
10303 ++inoutStats.unusedRangeCount;
10304 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10305 }
10306
10307 // 2. Process this allocation.
10308 // There is allocation with suballoc.offset, suballoc.size.
10309 ++inoutStats.allocationCount;
10310
10311 // 3. Prepare for next iteration.
10312 lastOffset = suballoc.offset + suballoc.size;
10313 ++nextAlloc1stIndex;
10314 }
10315 // We are at the end.
10316 else
10317 {
10318 if (lastOffset < freeSpace1stTo2ndEnd)
10319 {
10320 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10321 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10322 inoutStats.unusedSize += unusedRangeSize;
10323 ++inoutStats.unusedRangeCount;
10324 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10325 }
10326
10327 // End of loop.
10328 lastOffset = freeSpace1stTo2ndEnd;
10329 }
10330 }
10331
10332 if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10333 {
10334 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10335 while (lastOffset < size)
10336 {
10337 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10338 while (nextAlloc2ndIndex != SIZE_MAX &&
10339 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10340 {
10341 --nextAlloc2ndIndex;
10342 }
10343
10344 // Found non-null allocation.
10345 if (nextAlloc2ndIndex != SIZE_MAX)
10346 {
10347 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10348
10349 // 1. Process free space before this allocation.
10350 if (lastOffset < suballoc.offset)
10351 {
10352 // There is free space from lastOffset to suballoc.offset.
10353 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10354 inoutStats.unusedSize += unusedRangeSize;
10355 ++inoutStats.unusedRangeCount;
10356 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10357 }
10358
10359 // 2. Process this allocation.
10360 // There is allocation with suballoc.offset, suballoc.size.
10361 ++inoutStats.allocationCount;
10362
10363 // 3. Prepare for next iteration.
10364 lastOffset = suballoc.offset + suballoc.size;
10365 --nextAlloc2ndIndex;
10366 }
10367 // We are at the end.
10368 else
10369 {
10370 if (lastOffset < size)
10371 {
10372 // There is free space from lastOffset to size.
10373 const VkDeviceSize unusedRangeSize = size - lastOffset;
10374 inoutStats.unusedSize += unusedRangeSize;
10375 ++inoutStats.unusedRangeCount;
10376 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10377 }
10378
10379 // End of loop.
10380 lastOffset = size;
10381 }
10382 }
10383 }
10384 }
10385
10386 #if VMA_STATS_STRING_ENABLED
10387 void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
10388 {
10389 const VkDeviceSize size = GetSize();
10390 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10391 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10392 const size_t suballoc1stCount = suballocations1st.size();
10393 const size_t suballoc2ndCount = suballocations2nd.size();
10394
10395 // FIRST PASS
10396
10397 size_t unusedRangeCount = 0;
10398 VkDeviceSize usedBytes = 0;
10399
10400 VkDeviceSize lastOffset = 0;
10401
10402 size_t alloc2ndCount = 0;
10403 if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10404 {
10405 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10406 size_t nextAlloc2ndIndex = 0;
10407 while (lastOffset < freeSpace2ndTo1stEnd)
10408 {
10409 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10410 while (nextAlloc2ndIndex < suballoc2ndCount &&
10411 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10412 {
10413 ++nextAlloc2ndIndex;
10414 }
10415
10416 // Found non-null allocation.
10417 if (nextAlloc2ndIndex < suballoc2ndCount)
10418 {
10419 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10420
10421 // 1. Process free space before this allocation.
10422 if (lastOffset < suballoc.offset)
10423 {
10424 // There is free space from lastOffset to suballoc.offset.
10425 ++unusedRangeCount;
10426 }
10427
10428 // 2. Process this allocation.
10429 // There is allocation with suballoc.offset, suballoc.size.
10430 ++alloc2ndCount;
10431 usedBytes += suballoc.size;
10432
10433 // 3. Prepare for next iteration.
10434 lastOffset = suballoc.offset + suballoc.size;
10435 ++nextAlloc2ndIndex;
10436 }
10437 // We are at the end.
10438 else
10439 {
10440 if (lastOffset < freeSpace2ndTo1stEnd)
10441 {
10442 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10443 ++unusedRangeCount;
10444 }
10445
10446 // End of loop.
10447 lastOffset = freeSpace2ndTo1stEnd;
10448 }
10449 }
10450 }
10451
10452 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10453 size_t alloc1stCount = 0;
10454 const VkDeviceSize freeSpace1stTo2ndEnd =
10455 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10456 while (lastOffset < freeSpace1stTo2ndEnd)
10457 {
10458 // Find next non-null allocation or move nextAllocIndex to the end.
10459 while (nextAlloc1stIndex < suballoc1stCount &&
10460 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10461 {
10462 ++nextAlloc1stIndex;
10463 }
10464
10465 // Found non-null allocation.
10466 if (nextAlloc1stIndex < suballoc1stCount)
10467 {
10468 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10469
10470 // 1. Process free space before this allocation.
10471 if (lastOffset < suballoc.offset)
10472 {
10473 // There is free space from lastOffset to suballoc.offset.
10474 ++unusedRangeCount;
10475 }
10476
10477 // 2. Process this allocation.
10478 // There is allocation with suballoc.offset, suballoc.size.
10479 ++alloc1stCount;
10480 usedBytes += suballoc.size;
10481
10482 // 3. Prepare for next iteration.
10483 lastOffset = suballoc.offset + suballoc.size;
10484 ++nextAlloc1stIndex;
10485 }
10486 // We are at the end.
10487 else
10488 {
10489 if (lastOffset < size)
10490 {
10491 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10492 ++unusedRangeCount;
10493 }
10494
10495 // End of loop.
10496 lastOffset = freeSpace1stTo2ndEnd;
10497 }
10498 }
10499
10500 if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10501 {
10502 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10503 while (lastOffset < size)
10504 {
10505 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10506 while (nextAlloc2ndIndex != SIZE_MAX &&
10507 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10508 {
10509 --nextAlloc2ndIndex;
10510 }
10511
10512 // Found non-null allocation.
10513 if (nextAlloc2ndIndex != SIZE_MAX)
10514 {
10515 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10516
10517 // 1. Process free space before this allocation.
10518 if (lastOffset < suballoc.offset)
10519 {
10520 // There is free space from lastOffset to suballoc.offset.
10521 ++unusedRangeCount;
10522 }
10523
10524 // 2. Process this allocation.
10525 // There is allocation with suballoc.offset, suballoc.size.
10526 ++alloc2ndCount;
10527 usedBytes += suballoc.size;
10528
10529 // 3. Prepare for next iteration.
10530 lastOffset = suballoc.offset + suballoc.size;
10531 --nextAlloc2ndIndex;
10532 }
10533 // We are at the end.
10534 else
10535 {
10536 if (lastOffset < size)
10537 {
10538 // There is free space from lastOffset to size.
10539 ++unusedRangeCount;
10540 }
10541
10542 // End of loop.
10543 lastOffset = size;
10544 }
10545 }
10546 }
10547
10548 const VkDeviceSize unusedBytes = size - usedBytes;
10549 PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
10550
10551 // SECOND PASS
10552 lastOffset = 0;
10553
10554 if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10555 {
10556 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10557 size_t nextAlloc2ndIndex = 0;
10558 while (lastOffset < freeSpace2ndTo1stEnd)
10559 {
10560 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10561 while (nextAlloc2ndIndex < suballoc2ndCount &&
10562 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10563 {
10564 ++nextAlloc2ndIndex;
10565 }
10566
10567 // Found non-null allocation.
10568 if (nextAlloc2ndIndex < suballoc2ndCount)
10569 {
10570 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10571
10572 // 1. Process free space before this allocation.
10573 if (lastOffset < suballoc.offset)
10574 {
10575 // There is free space from lastOffset to suballoc.offset.
10576 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10577 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10578 }
10579
10580 // 2. Process this allocation.
10581 // There is allocation with suballoc.offset, suballoc.size.
10582 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10583
10584 // 3. Prepare for next iteration.
10585 lastOffset = suballoc.offset + suballoc.size;
10586 ++nextAlloc2ndIndex;
10587 }
10588 // We are at the end.
10589 else
10590 {
10591 if (lastOffset < freeSpace2ndTo1stEnd)
10592 {
10593 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10594 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10595 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10596 }
10597
10598 // End of loop.
10599 lastOffset = freeSpace2ndTo1stEnd;
10600 }
10601 }
10602 }
10603
10604 nextAlloc1stIndex = m_1stNullItemsBeginCount;
10605 while (lastOffset < freeSpace1stTo2ndEnd)
10606 {
10607 // Find next non-null allocation or move nextAllocIndex to the end.
10608 while (nextAlloc1stIndex < suballoc1stCount &&
10609 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10610 {
10611 ++nextAlloc1stIndex;
10612 }
10613
10614 // Found non-null allocation.
10615 if (nextAlloc1stIndex < suballoc1stCount)
10616 {
10617 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10618
10619 // 1. Process free space before this allocation.
10620 if (lastOffset < suballoc.offset)
10621 {
10622 // There is free space from lastOffset to suballoc.offset.
10623 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10624 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10625 }
10626
10627 // 2. Process this allocation.
10628 // There is allocation with suballoc.offset, suballoc.size.
10629 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10630
10631 // 3. Prepare for next iteration.
10632 lastOffset = suballoc.offset + suballoc.size;
10633 ++nextAlloc1stIndex;
10634 }
10635 // We are at the end.
10636 else
10637 {
10638 if (lastOffset < freeSpace1stTo2ndEnd)
10639 {
10640 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10641 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10642 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10643 }
10644
10645 // End of loop.
10646 lastOffset = freeSpace1stTo2ndEnd;
10647 }
10648 }
10649
10650 if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10651 {
10652 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10653 while (lastOffset < size)
10654 {
10655 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10656 while (nextAlloc2ndIndex != SIZE_MAX &&
10657 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10658 {
10659 --nextAlloc2ndIndex;
10660 }
10661
10662 // Found non-null allocation.
10663 if (nextAlloc2ndIndex != SIZE_MAX)
10664 {
10665 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10666
10667 // 1. Process free space before this allocation.
10668 if (lastOffset < suballoc.offset)
10669 {
10670 // There is free space from lastOffset to suballoc.offset.
10671 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10672 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10673 }
10674
10675 // 2. Process this allocation.
10676 // There is allocation with suballoc.offset, suballoc.size.
10677 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10678
10679 // 3. Prepare for next iteration.
10680 lastOffset = suballoc.offset + suballoc.size;
10681 --nextAlloc2ndIndex;
10682 }
10683 // We are at the end.
10684 else
10685 {
10686 if (lastOffset < size)
10687 {
10688 // There is free space from lastOffset to size.
10689 const VkDeviceSize unusedRangeSize = size - lastOffset;
10690 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10691 }
10692
10693 // End of loop.
10694 lastOffset = size;
10695 }
10696 }
10697 }
10698
10699 PrintDetailedMap_End(json);
10700 }
10701 #endif // #if VMA_STATS_STRING_ENABLED
10702
10703 bool VmaBlockMetadata_Linear::CreateAllocationRequest(
10704 uint32_t currentFrameIndex,
10705 uint32_t frameInUseCount,
10706 VkDeviceSize bufferImageGranularity,
10707 VkDeviceSize allocSize,
10708 VkDeviceSize allocAlignment,
10709 bool upperAddress,
10710 VmaSuballocationType allocType,
10711 bool canMakeOtherLost,
10712 uint32_t strategy,
10713 VmaAllocationRequest* pAllocationRequest)
10714 {
10715 VMA_ASSERT(allocSize > 0);
10716 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
10717 VMA_ASSERT(pAllocationRequest != VMA_NULL);
10718 VMA_HEAVY_ASSERT(Validate());
10719 return upperAddress ?
10720 CreateAllocationRequest_UpperAddress(
10721 currentFrameIndex, frameInUseCount, bufferImageGranularity,
10722 allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest) :
10723 CreateAllocationRequest_LowerAddress(
10724 currentFrameIndex, frameInUseCount, bufferImageGranularity,
10725 allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest);
10726 }
10727
10728 bool VmaBlockMetadata_Linear::CreateAllocationRequest_UpperAddress(
10729 uint32_t currentFrameIndex,
10730 uint32_t frameInUseCount,
10731 VkDeviceSize bufferImageGranularity,
10732 VkDeviceSize allocSize,
10733 VkDeviceSize allocAlignment,
10734 VmaSuballocationType allocType,
10735 bool canMakeOtherLost,
10736 uint32_t strategy,
10737 VmaAllocationRequest* pAllocationRequest)
10738 {
10739 const VkDeviceSize size = GetSize();
10740 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10741 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10742
10743 if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10744 {
10745 VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
10746 return false;
10747 }
10748
10749 // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
10750 if (allocSize > size)
10751 {
10752 return false;
10753 }
10754 VkDeviceSize resultBaseOffset = size - allocSize;
10755 if (!suballocations2nd.empty())
10756 {
10757 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
10758 resultBaseOffset = lastSuballoc.offset - allocSize;
10759 if (allocSize > lastSuballoc.offset)
10760 {
10761 return false;
10762 }
10763 }
10764
10765 // Start from offset equal to end of free space.
10766 VkDeviceSize resultOffset = resultBaseOffset;
10767
10768 // Apply VMA_DEBUG_MARGIN at the end.
10769 if (VMA_DEBUG_MARGIN > 0)
10770 {
10771 if (resultOffset < VMA_DEBUG_MARGIN)
10772 {
10773 return false;
10774 }
10775 resultOffset -= VMA_DEBUG_MARGIN;
10776 }
10777
10778 // Apply alignment.
10779 resultOffset = VmaAlignDown(resultOffset, allocAlignment);
10780
10781 // Check next suballocations from 2nd for BufferImageGranularity conflicts.
10782 // Make bigger alignment if necessary.
10783 if (bufferImageGranularity > 1 && !suballocations2nd.empty())
10784 {
10785 bool bufferImageGranularityConflict = false;
10786 for (size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
10787 {
10788 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
10789 if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
10790 {
10791 if (VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
10792 {
10793 bufferImageGranularityConflict = true;
10794 break;
10795 }
10796 }
10797 else
10798 // Already on previous page.
10799 break;
10800 }
10801 if (bufferImageGranularityConflict)
10802 {
10803 resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
10804 }
10805 }
10806
10807 // There is enough free space.
10808 const VkDeviceSize endOf1st = !suballocations1st.empty() ?
10809 suballocations1st.back().offset + suballocations1st.back().size :
10810 0;
10811 if (endOf1st + VMA_DEBUG_MARGIN <= resultOffset)
10812 {
10813 // Check previous suballocations for BufferImageGranularity conflicts.
10814 // If conflict exists, allocation cannot be made here.
10815 if (bufferImageGranularity > 1)
10816 {
10817 for (size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
10818 {
10819 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
10820 if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
10821 {
10822 if (VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
10823 {
10824 return false;
10825 }
10826 }
10827 else
10828 {
10829 // Already on next page.
10830 break;
10831 }
10832 }
10833 }
10834
10835 // All tests passed: Success.
10836 pAllocationRequest->offset = resultOffset;
10837 pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
10838 pAllocationRequest->sumItemSize = 0;
10839 // pAllocationRequest->item unused.
10840 pAllocationRequest->itemsToMakeLostCount = 0;
10841 pAllocationRequest->type = VmaAllocationRequestType::UpperAddress;
10842 return true;
10843 }
10844
10845 return false;
10846 }
10847
10848 bool VmaBlockMetadata_Linear::CreateAllocationRequest_LowerAddress(
10849 uint32_t currentFrameIndex,
10850 uint32_t frameInUseCount,
10851 VkDeviceSize bufferImageGranularity,
10852 VkDeviceSize allocSize,
10853 VkDeviceSize allocAlignment,
10854 VmaSuballocationType allocType,
10855 bool canMakeOtherLost,
10856 uint32_t strategy,
10857 VmaAllocationRequest* pAllocationRequest)
10858 {
10859 const VkDeviceSize size = GetSize();
10860 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10861 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10862
10863 if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10864 {
10865 // Try to allocate at the end of 1st vector.
10866
10867 VkDeviceSize resultBaseOffset = 0;
10868 if (!suballocations1st.empty())
10869 {
10870 const VmaSuballocation& lastSuballoc = suballocations1st.back();
10871 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
10872 }
10873
10874 // Start from offset equal to beginning of free space.
10875 VkDeviceSize resultOffset = resultBaseOffset;
10876
10877 // Apply VMA_DEBUG_MARGIN at the beginning.
10878 if (VMA_DEBUG_MARGIN > 0)
10879 {
10880 resultOffset += VMA_DEBUG_MARGIN;
10881 }
10882
10883 // Apply alignment.
10884 resultOffset = VmaAlignUp(resultOffset, allocAlignment);
10885
10886 // Check previous suballocations for BufferImageGranularity conflicts.
10887 // Make bigger alignment if necessary.
10888 if (bufferImageGranularity > 1 && !suballocations1st.empty())
10889 {
10890 bool bufferImageGranularityConflict = false;
10891 for (size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
10892 {
10893 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
10894 if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
10895 {
10896 if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
10897 {
10898 bufferImageGranularityConflict = true;
10899 break;
10900 }
10901 }
10902 else
10903 // Already on previous page.
10904 break;
10905 }
10906 if (bufferImageGranularityConflict)
10907 {
10908 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
10909 }
10910 }
10911
10912 const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
10913 suballocations2nd.back().offset : size;
10914
10915 // There is enough free space at the end after alignment.
10916 if (resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd)
10917 {
10918 // Check next suballocations for BufferImageGranularity conflicts.
10919 // If conflict exists, allocation cannot be made here.
10920 if (bufferImageGranularity > 1 && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10921 {
10922 for (size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
10923 {
10924 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
10925 if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
10926 {
10927 if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
10928 {
10929 return false;
10930 }
10931 }
10932 else
10933 {
10934 // Already on previous page.
10935 break;
10936 }
10937 }
10938 }
10939
10940 // All tests passed: Success.
10941 pAllocationRequest->offset = resultOffset;
10942 pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
10943 pAllocationRequest->sumItemSize = 0;
10944 // pAllocationRequest->item, customData unused.
10945 pAllocationRequest->type = VmaAllocationRequestType::EndOf1st;
10946 pAllocationRequest->itemsToMakeLostCount = 0;
10947 return true;
10948 }
10949 }
10950
10951 // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
10952 // beginning of 1st vector as the end of free space.
10953 if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10954 {
10955 VMA_ASSERT(!suballocations1st.empty());
10956
10957 VkDeviceSize resultBaseOffset = 0;
10958 if (!suballocations2nd.empty())
10959 {
10960 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
10961 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
10962 }
10963
10964 // Start from offset equal to beginning of free space.
10965 VkDeviceSize resultOffset = resultBaseOffset;
10966
10967 // Apply VMA_DEBUG_MARGIN at the beginning.
10968 if (VMA_DEBUG_MARGIN > 0)
10969 {
10970 resultOffset += VMA_DEBUG_MARGIN;
10971 }
10972
10973 // Apply alignment.
10974 resultOffset = VmaAlignUp(resultOffset, allocAlignment);
10975
10976 // Check previous suballocations for BufferImageGranularity conflicts.
10977 // Make bigger alignment if necessary.
10978 if (bufferImageGranularity > 1 && !suballocations2nd.empty())
10979 {
10980 bool bufferImageGranularityConflict = false;
10981 for (size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
10982 {
10983 const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
10984 if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
10985 {
10986 if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
10987 {
10988 bufferImageGranularityConflict = true;
10989 break;
10990 }
10991 }
10992 else
10993 // Already on previous page.
10994 break;
10995 }
10996 if (bufferImageGranularityConflict)
10997 {
10998 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
10999 }
11000 }
11001
11002 pAllocationRequest->itemsToMakeLostCount = 0;
11003 pAllocationRequest->sumItemSize = 0;
11004 size_t index1st = m_1stNullItemsBeginCount;
11005
11006 if (canMakeOtherLost)
11007 {
11008 while (index1st < suballocations1st.size() &&
11009 resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset)
11010 {
11011 // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
11012 const VmaSuballocation& suballoc = suballocations1st[index1st];
11013 if (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
11014 {
11015 // No problem.
11016 }
11017 else
11018 {
11019 VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
11020 if (suballoc.hAllocation->CanBecomeLost() &&
11021 suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
11022 {
11023 ++pAllocationRequest->itemsToMakeLostCount;
11024 pAllocationRequest->sumItemSize += suballoc.size;
11025 }
11026 else
11027 {
11028 return false;
11029 }
11030 }
11031 ++index1st;
11032 }
11033
11034 // Check next suballocations for BufferImageGranularity conflicts.
11035 // If conflict exists, we must mark more allocations lost or fail.
11036 if (bufferImageGranularity > 1)
11037 {
11038 while (index1st < suballocations1st.size())
11039 {
11040 const VmaSuballocation& suballoc = suballocations1st[index1st];
11041 if (VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))
11042 {
11043 if (suballoc.hAllocation != VK_NULL_HANDLE)
11044 {
11045 // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
11046 if (suballoc.hAllocation->CanBecomeLost() &&
11047 suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
11048 {
11049 ++pAllocationRequest->itemsToMakeLostCount;
11050 pAllocationRequest->sumItemSize += suballoc.size;
11051 }
11052 else
11053 {
11054 return false;
11055 }
11056 }
11057 }
11058 else
11059 {
11060 // Already on next page.
11061 break;
11062 }
11063 ++index1st;
11064 }
11065 }
11066
11067 // Special case: There is not enough room at the end for this allocation, even after making all from the 1st lost.
11068 if (index1st == suballocations1st.size() &&
11069 resultOffset + allocSize + VMA_DEBUG_MARGIN > size)
11070 {
11071 // TODO: This is a known bug that it's not yet implemented and the allocation is failing.
11072 VMA_DEBUG_LOG("Unsupported special case in custom pool with linear allocation algorithm used as ring buffer with allocations that can be lost.");
11073 }
11074 }
11075
11076 // There is enough free space at the end after alignment.
11077 if ((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= size) ||
11078 (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset))
11079 {
11080 // Check next suballocations for BufferImageGranularity conflicts.
11081 // If conflict exists, allocation cannot be made here.
11082 if (bufferImageGranularity > 1)
11083 {
11084 for (size_t nextSuballocIndex = index1st;
11085 nextSuballocIndex < suballocations1st.size();
11086 nextSuballocIndex++)
11087 {
11088 const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
11089 if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
11090 {
11091 if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
11092 {
11093 return false;
11094 }
11095 }
11096 else
11097 {
11098 // Already on next page.
11099 break;
11100 }
11101 }
11102 }
11103
11104 // All tests passed: Success.
11105 pAllocationRequest->offset = resultOffset;
11106 pAllocationRequest->sumFreeSize =
11107 (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size)
11108 - resultBaseOffset
11109 - pAllocationRequest->sumItemSize;
11110 pAllocationRequest->type = VmaAllocationRequestType::EndOf2nd;
11111 // pAllocationRequest->item, customData unused.
11112 return true;
11113 }
11114 }
11115
11116 return false;
11117 }
11118
11119 bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
11120 uint32_t currentFrameIndex,
11121 uint32_t frameInUseCount,
11122 VmaAllocationRequest* pAllocationRequest)
11123 {
11124 if (pAllocationRequest->itemsToMakeLostCount == 0)
11125 {
11126 return true;
11127 }
11128
11129 VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
11130
11131 // We always start from 1st.
11132 SuballocationVectorType* suballocations = &AccessSuballocations1st();
11133 size_t index = m_1stNullItemsBeginCount;
11134 size_t madeLostCount = 0;
11135 while (madeLostCount < pAllocationRequest->itemsToMakeLostCount)
11136 {
11137 if (index == suballocations->size())
11138 {
11139 index = 0;
11140 // If we get to the end of 1st, we wrap around to beginning of 2nd of 1st.
11141 if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11142 {
11143 suballocations = &AccessSuballocations2nd();
11144 }
11145 // else: m_2ndVectorMode == SECOND_VECTOR_EMPTY:
11146 // suballocations continues pointing at AccessSuballocations1st().
11147 VMA_ASSERT(!suballocations->empty());
11148 }
11149 VmaSuballocation& suballoc = (*suballocations)[index];
11150 if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11151 {
11152 VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
11153 VMA_ASSERT(suballoc.hAllocation->CanBecomeLost());
11154 if (suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11155 {
11156 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11157 suballoc.hAllocation = VK_NULL_HANDLE;
11158 m_SumFreeSize += suballoc.size;
11159 if (suballocations == &AccessSuballocations1st())
11160 {
11161 ++m_1stNullItemsMiddleCount;
11162 }
11163 else
11164 {
11165 ++m_2ndNullItemsCount;
11166 }
11167 ++madeLostCount;
11168 }
11169 else
11170 {
11171 return false;
11172 }
11173 }
11174 ++index;
11175 }
11176
11177 CleanupAfterFree();
11178 //VMA_HEAVY_ASSERT(Validate()); // Already called by ClanupAfterFree().
11179
11180 return true;
11181 }
11182
11183 uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
11184 {
11185 uint32_t lostAllocationCount = 0;
11186
11187 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11188 for (size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
11189 {
11190 VmaSuballocation& suballoc = suballocations1st[i];
11191 if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
11192 suballoc.hAllocation->CanBecomeLost() &&
11193 suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11194 {
11195 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11196 suballoc.hAllocation = VK_NULL_HANDLE;
11197 ++m_1stNullItemsMiddleCount;
11198 m_SumFreeSize += suballoc.size;
11199 ++lostAllocationCount;
11200 }
11201 }
11202
11203 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11204 for (size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
11205 {
11206 VmaSuballocation& suballoc = suballocations2nd[i];
11207 if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
11208 suballoc.hAllocation->CanBecomeLost() &&
11209 suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11210 {
11211 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11212 suballoc.hAllocation = VK_NULL_HANDLE;
11213 ++m_2ndNullItemsCount;
11214 m_SumFreeSize += suballoc.size;
11215 ++lostAllocationCount;
11216 }
11217 }
11218
11219 if (lostAllocationCount)
11220 {
11221 CleanupAfterFree();
11222 }
11223
11224 return lostAllocationCount;
11225 }
11226
11227 VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
11228 {
11229 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11230 for (size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
11231 {
11232 const VmaSuballocation& suballoc = suballocations1st[i];
11233 if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11234 {
11235 if (!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
11236 {
11237 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
11238 return VK_ERROR_VALIDATION_FAILED_EXT;
11239 }
11240 if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
11241 {
11242 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
11243 return VK_ERROR_VALIDATION_FAILED_EXT;
11244 }
11245 }
11246 }
11247
11248 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11249 for (size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
11250 {
11251 const VmaSuballocation& suballoc = suballocations2nd[i];
11252 if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11253 {
11254 if (!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
11255 {
11256 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
11257 return VK_ERROR_VALIDATION_FAILED_EXT;
11258 }
11259 if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
11260 {
11261 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
11262 return VK_ERROR_VALIDATION_FAILED_EXT;
11263 }
11264 }
11265 }
11266
11267 return VK_SUCCESS;
11268 }
11269
11270 void VmaBlockMetadata_Linear::Alloc(
11271 const VmaAllocationRequest& request,
11272 VmaSuballocationType type,
11273 VkDeviceSize allocSize,
11274 VmaAllocation hAllocation)
11275 {
11276 const VmaSuballocation newSuballoc = { request.offset, allocSize, hAllocation, type };
11277
11278 switch (request.type)
11279 {
11280 case VmaAllocationRequestType::UpperAddress:
11281 {
11282 VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
11283 "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
11284 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11285 suballocations2nd.push_back(newSuballoc);
11286 m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
11287 }
11288 break;
11289 case VmaAllocationRequestType::EndOf1st:
11290 {
11291 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11292
11293 VMA_ASSERT(suballocations1st.empty() ||
11294 request.offset >= suballocations1st.back().offset + suballocations1st.back().size);
11295 // Check if it fits before the end of the block.
11296 VMA_ASSERT(request.offset + allocSize <= GetSize());
11297
11298 suballocations1st.push_back(newSuballoc);
11299 }
11300 break;
11301 case VmaAllocationRequestType::EndOf2nd:
11302 {
11303 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11304 // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
11305 VMA_ASSERT(!suballocations1st.empty() &&
11306 request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset);
11307 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11308
11309 switch (m_2ndVectorMode)
11310 {
11311 case SECOND_VECTOR_EMPTY:
11312 // First allocation from second part ring buffer.
11313 VMA_ASSERT(suballocations2nd.empty());
11314 m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
11315 break;
11316 case SECOND_VECTOR_RING_BUFFER:
11317 // 2-part ring buffer is already started.
11318 VMA_ASSERT(!suballocations2nd.empty());
11319 break;
11320 case SECOND_VECTOR_DOUBLE_STACK:
11321 VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
11322 break;
11323 default:
11324 VMA_ASSERT(0);
11325 }
11326
11327 suballocations2nd.push_back(newSuballoc);
11328 }
11329 break;
11330 default:
11331 VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
11332 }
11333
11334 m_SumFreeSize -= newSuballoc.size;
11335 }
11336
11337 void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation)
11338 {
11339 FreeAtOffset(allocation->GetOffset());
11340 }
11341
11342 void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
11343 {
11344 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11345 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11346
11347 if (!suballocations1st.empty())
11348 {
11349 // First allocation: Mark it as next empty at the beginning.
11350 VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
11351 if (firstSuballoc.offset == offset)
11352 {
11353 firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11354 firstSuballoc.hAllocation = VK_NULL_HANDLE;
11355 m_SumFreeSize += firstSuballoc.size;
11356 ++m_1stNullItemsBeginCount;
11357 CleanupAfterFree();
11358 return;
11359 }
11360 }
11361
11362 // Last allocation in 2-part ring buffer or top of upper stack (same logic).
11363 if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
11364 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11365 {
11366 VmaSuballocation& lastSuballoc = suballocations2nd.back();
11367 if (lastSuballoc.offset == offset)
11368 {
11369 m_SumFreeSize += lastSuballoc.size;
11370 suballocations2nd.pop_back();
11371 CleanupAfterFree();
11372 return;
11373 }
11374 }
11375 // Last allocation in 1st vector.
11376 else if (m_2ndVectorMode == SECOND_VECTOR_EMPTY)
11377 {
11378 VmaSuballocation& lastSuballoc = suballocations1st.back();
11379 if (lastSuballoc.offset == offset)
11380 {
11381 m_SumFreeSize += lastSuballoc.size;
11382 suballocations1st.pop_back();
11383 CleanupAfterFree();
11384 return;
11385 }
11386 }
11387
11388 // Item from the middle of 1st vector.
11389 {
11390 VmaSuballocation refSuballoc;
11391 refSuballoc.offset = offset;
11392 // Rest of members stays uninitialized intentionally for better performance.
11393 SuballocationVectorType::iterator it = VmaBinaryFindSorted(
11394 suballocations1st.begin() + m_1stNullItemsBeginCount,
11395 suballocations1st.end(),
11396 refSuballoc,
11397 VmaSuballocationOffsetLess());
11398 if (it != suballocations1st.end())
11399 {
11400 it->type = VMA_SUBALLOCATION_TYPE_FREE;
11401 it->hAllocation = VK_NULL_HANDLE;
11402 ++m_1stNullItemsMiddleCount;
11403 m_SumFreeSize += it->size;
11404 CleanupAfterFree();
11405 return;
11406 }
11407 }
11408
11409 if (m_2ndVectorMode != SECOND_VECTOR_EMPTY)
11410 {
11411 // Item from the middle of 2nd vector.
11412 VmaSuballocation refSuballoc;
11413 refSuballoc.offset = offset;
11414 // Rest of members stays uninitialized intentionally for better performance.
11415 SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
11416 VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :
11417 VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());
11418 if (it != suballocations2nd.end())
11419 {
11420 it->type = VMA_SUBALLOCATION_TYPE_FREE;
11421 it->hAllocation = VK_NULL_HANDLE;
11422 ++m_2ndNullItemsCount;
11423 m_SumFreeSize += it->size;
11424 CleanupAfterFree();
11425 return;
11426 }
11427 }
11428
11429 VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
11430 }
11431
11432 bool VmaBlockMetadata_Linear::ShouldCompact1st() const
11433 {
11434 const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
11435 const size_t suballocCount = AccessSuballocations1st().size();
11436 return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
11437 }
11438
11439 void VmaBlockMetadata_Linear::CleanupAfterFree()
11440 {
11441 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11442 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11443
11444 if (IsEmpty())
11445 {
11446 suballocations1st.clear();
11447 suballocations2nd.clear();
11448 m_1stNullItemsBeginCount = 0;
11449 m_1stNullItemsMiddleCount = 0;
11450 m_2ndNullItemsCount = 0;
11451 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11452 }
11453 else
11454 {
11455 const size_t suballoc1stCount = suballocations1st.size();
11456 const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
11457 VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
11458
11459 // Find more null items at the beginning of 1st vector.
11460 while (m_1stNullItemsBeginCount < suballoc1stCount &&
11461 suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
11462 {
11463 ++m_1stNullItemsBeginCount;
11464 --m_1stNullItemsMiddleCount;
11465 }
11466
11467 // Find more null items at the end of 1st vector.
11468 while (m_1stNullItemsMiddleCount > 0 &&
11469 suballocations1st.back().hAllocation == VK_NULL_HANDLE)
11470 {
11471 --m_1stNullItemsMiddleCount;
11472 suballocations1st.pop_back();
11473 }
11474
11475 // Find more null items at the end of 2nd vector.
11476 while (m_2ndNullItemsCount > 0 &&
11477 suballocations2nd.back().hAllocation == VK_NULL_HANDLE)
11478 {
11479 --m_2ndNullItemsCount;
11480 suballocations2nd.pop_back();
11481 }
11482
11483 // Find more null items at the beginning of 2nd vector.
11484 while (m_2ndNullItemsCount > 0 &&
11485 suballocations2nd[0].hAllocation == VK_NULL_HANDLE)
11486 {
11487 --m_2ndNullItemsCount;
11488 VmaVectorRemove(suballocations2nd, 0);
11489 }
11490
11491 if (ShouldCompact1st())
11492 {
11493 const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
11494 size_t srcIndex = m_1stNullItemsBeginCount;
11495 for (size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
11496 {
11497 while (suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE)
11498 {
11499 ++srcIndex;
11500 }
11501 if (dstIndex != srcIndex)
11502 {
11503 suballocations1st[dstIndex] = suballocations1st[srcIndex];
11504 }
11505 ++srcIndex;
11506 }
11507 suballocations1st.resize(nonNullItemCount);
11508 m_1stNullItemsBeginCount = 0;
11509 m_1stNullItemsMiddleCount = 0;
11510 }
11511
11512 // 2nd vector became empty.
11513 if (suballocations2nd.empty())
11514 {
11515 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11516 }
11517
11518 // 1st vector became empty.
11519 if (suballocations1st.size() - m_1stNullItemsBeginCount == 0)
11520 {
11521 suballocations1st.clear();
11522 m_1stNullItemsBeginCount = 0;
11523
11524 if (!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11525 {
11526 // Swap 1st with 2nd. Now 2nd is empty.
11527 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11528 m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
11529 while (m_1stNullItemsBeginCount < suballocations2nd.size() &&
11530 suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
11531 {
11532 ++m_1stNullItemsBeginCount;
11533 --m_1stNullItemsMiddleCount;
11534 }
11535 m_2ndNullItemsCount = 0;
11536 m_1stVectorIndex ^= 1;
11537 }
11538 }
11539 }
11540
11541 VMA_HEAVY_ASSERT(Validate());
11542 }
11543
11544
11545 ////////////////////////////////////////////////////////////////////////////////
11546 // class VmaBlockMetadata_Buddy
11547
11548 VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :
11549 VmaBlockMetadata(hAllocator),
11550 m_Root(VMA_NULL),
11551 m_AllocationCount(0),
11552 m_FreeCount(1),
11553 m_SumFreeSize(0)
11554 {
11555 memset(m_FreeList, 0, sizeof(m_FreeList));
11556 }
11557
11558 VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
11559 {
11560 DeleteNode(m_Root);
11561 }
11562
11563 void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
11564 {
11565 VmaBlockMetadata::Init(size);
11566
11567 m_UsableSize = VmaPrevPow2(size);
11568 m_SumFreeSize = m_UsableSize;
11569
11570 // Calculate m_LevelCount.
11571 m_LevelCount = 1;
11572 while (m_LevelCount < MAX_LEVELS &&
11573 LevelToNodeSize(m_LevelCount) >= MIN_NODE_SIZE)
11574 {
11575 ++m_LevelCount;
11576 }
11577
11578 Node* rootNode = vma_new(GetAllocationCallbacks(), Node)();
11579 rootNode->offset = 0;
11580 rootNode->type = Node::TYPE_FREE;
11581 rootNode->parent = VMA_NULL;
11582 rootNode->buddy = VMA_NULL;
11583
11584 m_Root = rootNode;
11585 AddToFreeListFront(0, rootNode);
11586 }
11587
11588 bool VmaBlockMetadata_Buddy::Validate() const
11589 {
11590 // Validate tree.
11591 ValidationContext ctx;
11592 if (!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
11593 {
11594 VMA_VALIDATE(false && "ValidateNode failed.");
11595 }
11596 VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
11597 VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
11598
11599 // Validate free node lists.
11600 for (uint32_t level = 0; level < m_LevelCount; ++level)
11601 {
11602 VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
11603 m_FreeList[level].front->free.prev == VMA_NULL);
11604
11605 for (Node* node = m_FreeList[level].front;
11606 node != VMA_NULL;
11607 node = node->free.next)
11608 {
11609 VMA_VALIDATE(node->type == Node::TYPE_FREE);
11610
11611 if (node->free.next == VMA_NULL)
11612 {
11613 VMA_VALIDATE(m_FreeList[level].back == node);
11614 }
11615 else
11616 {
11617 VMA_VALIDATE(node->free.next->free.prev == node);
11618 }
11619 }
11620 }
11621
11622 // Validate that free lists ar higher levels are empty.
11623 for (uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
11624 {
11625 VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
11626 }
11627
11628 return true;
11629 }
11630
11631 VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
11632 {
11633 for (uint32_t level = 0; level < m_LevelCount; ++level)
11634 {
11635 if (m_FreeList[level].front != VMA_NULL)
11636 {
11637 return LevelToNodeSize(level);
11638 }
11639 }
11640 return 0;
11641 }
11642
11643 void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
11644 {
11645 const VkDeviceSize unusableSize = GetUnusableSize();
11646
11647 outInfo.blockCount = 1;
11648
11649 outInfo.allocationCount = outInfo.unusedRangeCount = 0;
11650 outInfo.usedBytes = outInfo.unusedBytes = 0;
11651
11652 outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
11653 outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
11654 outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused.
11655
11656 CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0));
11657
11658 if (unusableSize > 0)
11659 {
11660 ++outInfo.unusedRangeCount;
11661 outInfo.unusedBytes += unusableSize;
11662 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize);
11663 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize);
11664 }
11665 }
11666
11667 void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
11668 {
11669 const VkDeviceSize unusableSize = GetUnusableSize();
11670
11671 inoutStats.size += GetSize();
11672 inoutStats.unusedSize += m_SumFreeSize + unusableSize;
11673 inoutStats.allocationCount += m_AllocationCount;
11674 inoutStats.unusedRangeCount += m_FreeCount;
11675 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
11676
11677 if (unusableSize > 0)
11678 {
11679 ++inoutStats.unusedRangeCount;
11680 // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
11681 }
11682 }
11683
11684 #if VMA_STATS_STRING_ENABLED
11685
11686 void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
11687 {
11688 // TODO optimize
11689 VmaStatInfo stat;
11690 CalcAllocationStatInfo(stat);
11691
11692 PrintDetailedMap_Begin(
11693 json,
11694 stat.unusedBytes,
11695 stat.allocationCount,
11696 stat.unusedRangeCount);
11697
11698 PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
11699
11700 const VkDeviceSize unusableSize = GetUnusableSize();
11701 if (unusableSize > 0)
11702 {
11703 PrintDetailedMap_UnusedRange(json,
11704 m_UsableSize, // offset
11705 unusableSize); // size
11706 }
11707
11708 PrintDetailedMap_End(json);
11709 }
11710
11711 #endif // #if VMA_STATS_STRING_ENABLED
11712
11713 bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
11714 uint32_t currentFrameIndex,
11715 uint32_t frameInUseCount,
11716 VkDeviceSize bufferImageGranularity,
11717 VkDeviceSize allocSize,
11718 VkDeviceSize allocAlignment,
11719 bool upperAddress,
11720 VmaSuballocationType allocType,
11721 bool canMakeOtherLost,
11722 uint32_t strategy,
11723 VmaAllocationRequest* pAllocationRequest)
11724 {
11725 VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
11726
11727 // Simple way to respect bufferImageGranularity. May be optimized some day.
11728 // Whenever it might be an OPTIMAL image...
11729 if (allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
11730 allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
11731 allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
11732 {
11733 allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity);
11734 allocSize = VMA_MAX(allocSize, bufferImageGranularity);
11735 }
11736
11737 if (allocSize > m_UsableSize)
11738 {
11739 return false;
11740 }
11741
11742 const uint32_t targetLevel = AllocSizeToLevel(allocSize);
11743 for (uint32_t level = targetLevel + 1; level--; )
11744 {
11745 for (Node* freeNode = m_FreeList[level].front;
11746 freeNode != VMA_NULL;
11747 freeNode = freeNode->free.next)
11748 {
11749 if (freeNode->offset % allocAlignment == 0)
11750 {
11751 pAllocationRequest->type = VmaAllocationRequestType::Normal;
11752 pAllocationRequest->offset = freeNode->offset;
11753 pAllocationRequest->sumFreeSize = LevelToNodeSize(level);
11754 pAllocationRequest->sumItemSize = 0;
11755 pAllocationRequest->itemsToMakeLostCount = 0;
11756 pAllocationRequest->customData = (void*)(uintptr_t)level;
11757 return true;
11758 }
11759 }
11760 }
11761
11762 return false;
11763 }
11764
11765 bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
11766 uint32_t currentFrameIndex,
11767 uint32_t frameInUseCount,
11768 VmaAllocationRequest* pAllocationRequest)
11769 {
11770 /*
11771 Lost allocations are not supported in buddy allocator at the moment.
11772 Support might be added in the future.
11773 */
11774 return pAllocationRequest->itemsToMakeLostCount == 0;
11775 }
11776
11777 uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
11778 {
11779 /*
11780 Lost allocations are not supported in buddy allocator at the moment.
11781 Support might be added in the future.
11782 */
11783 return 0;
11784 }
11785
11786 void VmaBlockMetadata_Buddy::Alloc(
11787 const VmaAllocationRequest& request,
11788 VmaSuballocationType type,
11789 VkDeviceSize allocSize,
11790 VmaAllocation hAllocation)
11791 {
11792 VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
11793
11794 const uint32_t targetLevel = AllocSizeToLevel(allocSize);
11795 uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
11796
11797 Node* currNode = m_FreeList[currLevel].front;
11798 VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
11799 while (currNode->offset != request.offset)
11800 {
11801 currNode = currNode->free.next;
11802 VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
11803 }
11804
11805 // Go down, splitting free nodes.
11806 while (currLevel < targetLevel)
11807 {
11808 // currNode is already first free node at currLevel.
11809 // Remove it from list of free nodes at this currLevel.
11810 RemoveFromFreeList(currLevel, currNode);
11811
11812 const uint32_t childrenLevel = currLevel + 1;
11813
11814 // Create two free sub-nodes.
11815 Node* leftChild = vma_new(GetAllocationCallbacks(), Node)();
11816 Node* rightChild = vma_new(GetAllocationCallbacks(), Node)();
11817
11818 leftChild->offset = currNode->offset;
11819 leftChild->type = Node::TYPE_FREE;
11820 leftChild->parent = currNode;
11821 leftChild->buddy = rightChild;
11822
11823 rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
11824 rightChild->type = Node::TYPE_FREE;
11825 rightChild->parent = currNode;
11826 rightChild->buddy = leftChild;
11827
11828 // Convert current currNode to split type.
11829 currNode->type = Node::TYPE_SPLIT;
11830 currNode->split.leftChild = leftChild;
11831
11832 // Add child nodes to free list. Order is important!
11833 AddToFreeListFront(childrenLevel, rightChild);
11834 AddToFreeListFront(childrenLevel, leftChild);
11835
11836 ++m_FreeCount;
11837 //m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2.
11838 ++currLevel;
11839 currNode = m_FreeList[currLevel].front;
11840
11841 /*
11842 We can be sure that currNode, as left child of node previously split,
11843 also fullfills the alignment requirement.
11844 */
11845 }
11846
11847 // Remove from free list.
11848 VMA_ASSERT(currLevel == targetLevel &&
11849 currNode != VMA_NULL &&
11850 currNode->type == Node::TYPE_FREE);
11851 RemoveFromFreeList(currLevel, currNode);
11852
11853 // Convert to allocation node.
11854 currNode->type = Node::TYPE_ALLOCATION;
11855 currNode->allocation.alloc = hAllocation;
11856
11857 ++m_AllocationCount;
11858 --m_FreeCount;
11859 m_SumFreeSize -= allocSize;
11860 }
11861
11862 void VmaBlockMetadata_Buddy::DeleteNode(Node* node)
11863 {
11864 if (node->type == Node::TYPE_SPLIT)
11865 {
11866 DeleteNode(node->split.leftChild->buddy);
11867 DeleteNode(node->split.leftChild);
11868 }
11869
11870 vma_delete(GetAllocationCallbacks(), node);
11871 }
11872
11873 bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
11874 {
11875 VMA_VALIDATE(level < m_LevelCount);
11876 VMA_VALIDATE(curr->parent == parent);
11877 VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
11878 VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
11879 switch (curr->type)
11880 {
11881 case Node::TYPE_FREE:
11882 // curr->free.prev, next are validated separately.
11883 ctx.calculatedSumFreeSize += levelNodeSize;
11884 ++ctx.calculatedFreeCount;
11885 break;
11886 case Node::TYPE_ALLOCATION:
11887 ++ctx.calculatedAllocationCount;
11888 ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize();
11889 VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);
11890 break;
11891 case Node::TYPE_SPLIT:
11892 {
11893 const uint32_t childrenLevel = level + 1;
11894 const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2;
11895 const Node* const leftChild = curr->split.leftChild;
11896 VMA_VALIDATE(leftChild != VMA_NULL);
11897 VMA_VALIDATE(leftChild->offset == curr->offset);
11898 if (!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
11899 {
11900 VMA_VALIDATE(false && "ValidateNode for left child failed.");
11901 }
11902 const Node* const rightChild = leftChild->buddy;
11903 VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
11904 if (!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
11905 {
11906 VMA_VALIDATE(false && "ValidateNode for right child failed.");
11907 }
11908 }
11909 break;
11910 default:
11911 return false;
11912 }
11913
11914 return true;
11915 }
11916
11917 uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
11918 {
11919 // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
11920 uint32_t level = 0;
11921 VkDeviceSize currLevelNodeSize = m_UsableSize;
11922 VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
11923 while (allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
11924 {
11925 ++level;
11926 currLevelNodeSize = nextLevelNodeSize;
11927 nextLevelNodeSize = currLevelNodeSize >> 1;
11928 }
11929 return level;
11930 }
11931
11932 void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset)
11933 {
11934 // Find node and level.
11935 Node* node = m_Root;
11936 VkDeviceSize nodeOffset = 0;
11937 uint32_t level = 0;
11938 VkDeviceSize levelNodeSize = LevelToNodeSize(0);
11939 while (node->type == Node::TYPE_SPLIT)
11940 {
11941 const VkDeviceSize nextLevelSize = levelNodeSize >> 1;
11942 if (offset < nodeOffset + nextLevelSize)
11943 {
11944 node = node->split.leftChild;
11945 }
11946 else
11947 {
11948 node = node->split.leftChild->buddy;
11949 nodeOffset += nextLevelSize;
11950 }
11951 ++level;
11952 levelNodeSize = nextLevelSize;
11953 }
11954
11955 VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
11956 VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc);
11957
11958 ++m_FreeCount;
11959 --m_AllocationCount;
11960 m_SumFreeSize += alloc->GetSize();
11961
11962 node->type = Node::TYPE_FREE;
11963
11964 // Join free nodes if possible.
11965 while (level > 0 && node->buddy->type == Node::TYPE_FREE)
11966 {
11967 RemoveFromFreeList(level, node->buddy);
11968 Node* const parent = node->parent;
11969
11970 vma_delete(GetAllocationCallbacks(), node->buddy);
11971 vma_delete(GetAllocationCallbacks(), node);
11972 parent->type = Node::TYPE_FREE;
11973
11974 node = parent;
11975 --level;
11976 //m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2.
11977 --m_FreeCount;
11978 }
11979
11980 AddToFreeListFront(level, node);
11981 }
11982
11983 void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const
11984 {
11985 switch (node->type)
11986 {
11987 case Node::TYPE_FREE:
11988 ++outInfo.unusedRangeCount;
11989 outInfo.unusedBytes += levelNodeSize;
11990 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, levelNodeSize);
11991 outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize);
11992 break;
11993 case Node::TYPE_ALLOCATION:
11994 {
11995 const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
11996 ++outInfo.allocationCount;
11997 outInfo.usedBytes += allocSize;
11998 outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize);
11999 outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize);
12000
12001 const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize;
12002 if (unusedRangeSize > 0)
12003 {
12004 ++outInfo.unusedRangeCount;
12005 outInfo.unusedBytes += unusedRangeSize;
12006 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize);
12007 outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize);
12008 }
12009 }
12010 break;
12011 case Node::TYPE_SPLIT:
12012 {
12013 const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
12014 const Node* const leftChild = node->split.leftChild;
12015 CalcAllocationStatInfoNode(outInfo, leftChild, childrenNodeSize);
12016 const Node* const rightChild = leftChild->buddy;
12017 CalcAllocationStatInfoNode(outInfo, rightChild, childrenNodeSize);
12018 }
12019 break;
12020 default:
12021 VMA_ASSERT(0);
12022 }
12023 }
12024
12025 void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
12026 {
12027 VMA_ASSERT(node->type == Node::TYPE_FREE);
12028
12029 // List is empty.
12030 Node* const frontNode = m_FreeList[level].front;
12031 if (frontNode == VMA_NULL)
12032 {
12033 VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
12034 node->free.prev = node->free.next = VMA_NULL;
12035 m_FreeList[level].front = m_FreeList[level].back = node;
12036 }
12037 else
12038 {
12039 VMA_ASSERT(frontNode->free.prev == VMA_NULL);
12040 node->free.prev = VMA_NULL;
12041 node->free.next = frontNode;
12042 frontNode->free.prev = node;
12043 m_FreeList[level].front = node;
12044 }
12045 }
12046
12047 void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
12048 {
12049 VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
12050
12051 // It is at the front.
12052 if (node->free.prev == VMA_NULL)
12053 {
12054 VMA_ASSERT(m_FreeList[level].front == node);
12055 m_FreeList[level].front = node->free.next;
12056 }
12057 else
12058 {
12059 Node* const prevFreeNode = node->free.prev;
12060 VMA_ASSERT(prevFreeNode->free.next == node);
12061 prevFreeNode->free.next = node->free.next;
12062 }
12063
12064 // It is at the back.
12065 if (node->free.next == VMA_NULL)
12066 {
12067 VMA_ASSERT(m_FreeList[level].back == node);
12068 m_FreeList[level].back = node->free.prev;
12069 }
12070 else
12071 {
12072 Node* const nextFreeNode = node->free.next;
12073 VMA_ASSERT(nextFreeNode->free.prev == node);
12074 nextFreeNode->free.prev = node->free.prev;
12075 }
12076 }
12077
12078 #if VMA_STATS_STRING_ENABLED
12079 void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
12080 {
12081 switch (node->type)
12082 {
12083 case Node::TYPE_FREE:
12084 PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
12085 break;
12086 case Node::TYPE_ALLOCATION:
12087 {
12088 PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);
12089 const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
12090 if (allocSize < levelNodeSize)
12091 {
12092 PrintDetailedMap_UnusedRange(json, node->offset + allocSize, levelNodeSize - allocSize);
12093 }
12094 }
12095 break;
12096 case Node::TYPE_SPLIT:
12097 {
12098 const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
12099 const Node* const leftChild = node->split.leftChild;
12100 PrintDetailedMapNode(json, leftChild, childrenNodeSize);
12101 const Node* const rightChild = leftChild->buddy;
12102 PrintDetailedMapNode(json, rightChild, childrenNodeSize);
12103 }
12104 break;
12105 default:
12106 VMA_ASSERT(0);
12107 }
12108 }
12109 #endif // #if VMA_STATS_STRING_ENABLED
12110
12111
12112 ////////////////////////////////////////////////////////////////////////////////
12113 // class VmaDeviceMemoryBlock
12114
12115 VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) :
12116 m_pMetadata(VMA_NULL),
12117 m_MemoryTypeIndex(UINT32_MAX),
12118 m_Id(0),
12119 m_hMemory(VK_NULL_HANDLE),
12120 m_MapCount(0),
12121 m_pMappedData(VMA_NULL)
12122 {
12123 }
12124
12125 void VmaDeviceMemoryBlock::Init(
12126 VmaAllocator hAllocator,
12127 VmaPool hParentPool,
12128 uint32_t newMemoryTypeIndex,
12129 VkDeviceMemory newMemory,
12130 VkDeviceSize newSize,
12131 uint32_t id,
12132 uint32_t algorithm)
12133 {
12134 VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
12135
12136 m_hParentPool = hParentPool;
12137 m_MemoryTypeIndex = newMemoryTypeIndex;
12138 m_Id = id;
12139 m_hMemory = newMemory;
12140
12141 switch (algorithm)
12142 {
12143 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
12144 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator);
12145 break;
12146 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
12147 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator);
12148 break;
12149 default:
12150 VMA_ASSERT(0);
12151 // Fall-through.
12152 case 0:
12153 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator);
12154 }
12155 m_pMetadata->Init(newSize);
12156 }
12157
12158 void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
12159 {
12160 // This is the most important assert in the entire library.
12161 // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
12162 VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
12163
12164 VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
12165 allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
12166 m_hMemory = VK_NULL_HANDLE;
12167
12168 vma_delete(allocator, m_pMetadata);
12169 m_pMetadata = VMA_NULL;
12170 }
12171
12172 bool VmaDeviceMemoryBlock::Validate() const
12173 {
12174 VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
12175 (m_pMetadata->GetSize() != 0));
12176
12177 return m_pMetadata->Validate();
12178 }
12179
12180 VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
12181 {
12182 void* pData = nullptr;
12183 VkResult res = Map(hAllocator, 1, &pData);
12184 if (res != VK_SUCCESS)
12185 {
12186 return res;
12187 }
12188
12189 res = m_pMetadata->CheckCorruption(pData);
12190
12191 Unmap(hAllocator, 1);
12192
12193 return res;
12194 }
12195
12196 VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
12197 {
12198 if (count == 0)
12199 {
12200 return VK_SUCCESS;
12201 }
12202
12203 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12204 if (m_MapCount != 0)
12205 {
12206 m_MapCount += count;
12207 VMA_ASSERT(m_pMappedData != VMA_NULL);
12208 if (ppData != VMA_NULL)
12209 {
12210 *ppData = m_pMappedData;
12211 }
12212 return VK_SUCCESS;
12213 }
12214 else
12215 {
12216 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
12217 hAllocator->m_hDevice,
12218 m_hMemory,
12219 0, // offset
12220 VK_WHOLE_SIZE,
12221 0, // flags
12222 &m_pMappedData);
12223 if (result == VK_SUCCESS)
12224 {
12225 if (ppData != VMA_NULL)
12226 {
12227 *ppData = m_pMappedData;
12228 }
12229 m_MapCount = count;
12230 }
12231 return result;
12232 }
12233 }
12234
12235 void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
12236 {
12237 if (count == 0)
12238 {
12239 return;
12240 }
12241
12242 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12243 if (m_MapCount >= count)
12244 {
12245 m_MapCount -= count;
12246 if (m_MapCount == 0)
12247 {
12248 m_pMappedData = VMA_NULL;
12249 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
12250 }
12251 }
12252 else
12253 {
12254 VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
12255 }
12256 }
12257
12258 VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
12259 {
12260 VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
12261 VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
12262
12263 void* pData;
12264 VkResult res = Map(hAllocator, 1, &pData);
12265 if (res != VK_SUCCESS)
12266 {
12267 return res;
12268 }
12269
12270 VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN);
12271 VmaWriteMagicValue(pData, allocOffset + allocSize);
12272
12273 Unmap(hAllocator, 1);
12274
12275 return VK_SUCCESS;
12276 }
12277
12278 VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
12279 {
12280 VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
12281 VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
12282
12283 void* pData;
12284 VkResult res = Map(hAllocator, 1, &pData);
12285 if (res != VK_SUCCESS)
12286 {
12287 return res;
12288 }
12289
12290 if (!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN))
12291 {
12292 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
12293 }
12294 else if (!VmaValidateMagicValue(pData, allocOffset + allocSize))
12295 {
12296 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
12297 }
12298
12299 Unmap(hAllocator, 1);
12300
12301 return VK_SUCCESS;
12302 }
12303
12304 VkResult VmaDeviceMemoryBlock::BindBufferMemory(
12305 const VmaAllocator hAllocator,
12306 const VmaAllocation hAllocation,
12307 VkDeviceSize allocationLocalOffset,
12308 VkBuffer hBuffer,
12309 const void* pNext)
12310 {
12311 VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
12312 hAllocation->GetBlock() == this);
12313 VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
12314 "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
12315 const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
12316 // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
12317 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12318 return hAllocator->BindVulkanBuffer(m_hMemory, memoryOffset, hBuffer, pNext);
12319 }
12320
12321 VkResult VmaDeviceMemoryBlock::BindImageMemory(
12322 const VmaAllocator hAllocator,
12323 const VmaAllocation hAllocation,
12324 VkDeviceSize allocationLocalOffset,
12325 VkImage hImage,
12326 const void* pNext)
12327 {
12328 VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
12329 hAllocation->GetBlock() == this);
12330 VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
12331 "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
12332 const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
12333 // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
12334 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12335 return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext);
12336 }
12337
12338 static void InitStatInfo(VmaStatInfo& outInfo)
12339 {
12340 memset(&outInfo, 0, sizeof(outInfo));
12341 outInfo.allocationSizeMin = UINT64_MAX;
12342 outInfo.unusedRangeSizeMin = UINT64_MAX;
12343 }
12344
12345 // Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
12346 static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
12347 {
12348 inoutInfo.blockCount += srcInfo.blockCount;
12349 inoutInfo.allocationCount += srcInfo.allocationCount;
12350 inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
12351 inoutInfo.usedBytes += srcInfo.usedBytes;
12352 inoutInfo.unusedBytes += srcInfo.unusedBytes;
12353 inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
12354 inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
12355 inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
12356 inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
12357 }
12358
12359 static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
12360 {
12361 inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
12362 VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
12363 inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
12364 VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
12365 }
12366
12367 VmaPool_T::VmaPool_T(
12368 VmaAllocator hAllocator,
12369 const VmaPoolCreateInfo& createInfo,
12370 VkDeviceSize preferredBlockSize) :
12371 m_BlockVector(
12372 hAllocator,
12373 this, // hParentPool
12374 createInfo.memoryTypeIndex,
12375 createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
12376 createInfo.minBlockCount,
12377 createInfo.maxBlockCount,
12378 (createInfo.flags& VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
12379 createInfo.frameInUseCount,
12380 createInfo.blockSize != 0, // explicitBlockSize
12381 createInfo.flags& VMA_POOL_CREATE_ALGORITHM_MASK), // algorithm
12382 m_Id(0),
12383 m_Name(VMA_NULL)
12384 {
12385 }
12386
12387 VmaPool_T::~VmaPool_T()
12388 {
12389 }
12390
12391 void VmaPool_T::SetName(const char* pName)
12392 {
12393 const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks();
12394 VmaFreeString(allocs, m_Name);
12395
12396 if (pName != VMA_NULL)
12397 {
12398 m_Name = VmaCreateStringCopy(allocs, pName);
12399 }
12400 else
12401 {
12402 m_Name = VMA_NULL;
12403 }
12404 }
12405
12406 #if VMA_STATS_STRING_ENABLED
12407
12408 #endif // #if VMA_STATS_STRING_ENABLED
12409
12410 VmaBlockVector::VmaBlockVector(
12411 VmaAllocator hAllocator,
12412 VmaPool hParentPool,
12413 uint32_t memoryTypeIndex,
12414 VkDeviceSize preferredBlockSize,
12415 size_t minBlockCount,
12416 size_t maxBlockCount,
12417 VkDeviceSize bufferImageGranularity,
12418 uint32_t frameInUseCount,
12419 bool explicitBlockSize,
12420 uint32_t algorithm) :
12421 m_hAllocator(hAllocator),
12422 m_hParentPool(hParentPool),
12423 m_MemoryTypeIndex(memoryTypeIndex),
12424 m_PreferredBlockSize(preferredBlockSize),
12425 m_MinBlockCount(minBlockCount),
12426 m_MaxBlockCount(maxBlockCount),
12427 m_BufferImageGranularity(bufferImageGranularity),
12428 m_FrameInUseCount(frameInUseCount),
12429 m_ExplicitBlockSize(explicitBlockSize),
12430 m_Algorithm(algorithm),
12431 m_HasEmptyBlock(false),
12432 m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
12433 m_NextBlockId(0)
12434 {
12435 }
12436
12437 VmaBlockVector::~VmaBlockVector()
12438 {
12439 for (size_t i = m_Blocks.size(); i--; )
12440 {
12441 m_Blocks[i]->Destroy(m_hAllocator);
12442 vma_delete(m_hAllocator, m_Blocks[i]);
12443 }
12444 }
12445
12446 VkResult VmaBlockVector::CreateMinBlocks()
12447 {
12448 for (size_t i = 0; i < m_MinBlockCount; ++i)
12449 {
12450 VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
12451 if (res != VK_SUCCESS)
12452 {
12453 return res;
12454 }
12455 }
12456 return VK_SUCCESS;
12457 }
12458
12459 void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
12460 {
12461 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12462
12463 const size_t blockCount = m_Blocks.size();
12464
12465 pStats->size = 0;
12466 pStats->unusedSize = 0;
12467 pStats->allocationCount = 0;
12468 pStats->unusedRangeCount = 0;
12469 pStats->unusedRangeSizeMax = 0;
12470 pStats->blockCount = blockCount;
12471
12472 for (uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12473 {
12474 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12475 VMA_ASSERT(pBlock);
12476 VMA_HEAVY_ASSERT(pBlock->Validate());
12477 pBlock->m_pMetadata->AddPoolStats(*pStats);
12478 }
12479 }
12480
12481 bool VmaBlockVector::IsEmpty()
12482 {
12483 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12484 return m_Blocks.empty();
12485 }
12486
12487 bool VmaBlockVector::IsCorruptionDetectionEnabled() const
12488 {
12489 const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
12490 return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
12491 (VMA_DEBUG_MARGIN > 0) &&
12492 (m_Algorithm == 0 || m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) &&
12493 (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
12494 }
12495
12496 static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
12497
12498 VkResult VmaBlockVector::Allocate(
12499 uint32_t currentFrameIndex,
12500 VkDeviceSize size,
12501 VkDeviceSize alignment,
12502 const VmaAllocationCreateInfo& createInfo,
12503 VmaSuballocationType suballocType,
12504 size_t allocationCount,
12505 VmaAllocation* pAllocations)
12506 {
12507 size_t allocIndex;
12508 VkResult res = VK_SUCCESS;
12509
12510 if (IsCorruptionDetectionEnabled())
12511 {
12512 size = VmaAlignUp<VkDeviceSize>(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12513 alignment = VmaAlignUp<VkDeviceSize>(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12514 }
12515
12516 {
12517 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12518 for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
12519 {
12520 res = AllocatePage(
12521 currentFrameIndex,
12522 size,
12523 alignment,
12524 createInfo,
12525 suballocType,
12526 pAllocations + allocIndex);
12527 if (res != VK_SUCCESS)
12528 {
12529 break;
12530 }
12531 }
12532 }
12533
12534 if (res != VK_SUCCESS)
12535 {
12536 // Free all already created allocations.
12537 while (allocIndex--)
12538 {
12539 Free(pAllocations[allocIndex]);
12540 }
12541 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
12542 }
12543
12544 return res;
12545 }
12546
12547 VkResult VmaBlockVector::AllocatePage(
12548 uint32_t currentFrameIndex,
12549 VkDeviceSize size,
12550 VkDeviceSize alignment,
12551 const VmaAllocationCreateInfo& createInfo,
12552 VmaSuballocationType suballocType,
12553 VmaAllocation* pAllocation)
12554 {
12555 const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
12556 bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
12557 const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
12558 const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
12559
12560 VkDeviceSize freeMemory;
12561 {
12562 const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
12563 VmaBudget heapBudget = {};
12564 m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
12565 freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0;
12566 }
12567
12568 const bool canFallbackToDedicated = !IsCustomPool();
12569 const bool canCreateNewBlock =
12570 ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
12571 (m_Blocks.size() < m_MaxBlockCount) &&
12572 (freeMemory >= size || !canFallbackToDedicated);
12573 uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
12574
12575 // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
12576 // Which in turn is available only when maxBlockCount = 1.
12577 if (m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
12578 {
12579 canMakeOtherLost = false;
12580 }
12581
12582 // Upper address can only be used with linear allocator and within single memory block.
12583 if (isUpperAddress &&
12584 (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
12585 {
12586 return VK_ERROR_FEATURE_NOT_PRESENT;
12587 }
12588
12589 // Validate strategy.
12590 switch (strategy)
12591 {
12592 case 0:
12593 strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
12594 break;
12595 case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
12596 case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
12597 case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
12598 break;
12599 default:
12600 return VK_ERROR_FEATURE_NOT_PRESENT;
12601 }
12602
12603 // Early reject: requested allocation size is larger that maximum block size for this block vector.
12604 if (size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
12605 {
12606 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12607 }
12608
12609 /*
12610 Under certain condition, this whole section can be skipped for optimization, so
12611 we move on directly to trying to allocate with canMakeOtherLost. That's the case
12612 e.g. for custom pools with linear algorithm.
12613 */
12614 if (!canMakeOtherLost || canCreateNewBlock)
12615 {
12616 // 1. Search existing allocations. Try to allocate without making other allocations lost.
12617 VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
12618 allocFlagsCopy &= ~VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
12619
12620 if (m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
12621 {
12622 // Use only last block.
12623 if (!m_Blocks.empty())
12624 {
12625 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
12626 VMA_ASSERT(pCurrBlock);
12627 VkResult res = AllocateFromBlock(
12628 pCurrBlock,
12629 currentFrameIndex,
12630 size,
12631 alignment,
12632 allocFlagsCopy,
12633 createInfo.pUserData,
12634 suballocType,
12635 strategy,
12636 pAllocation);
12637 if (res == VK_SUCCESS)
12638 {
12639 VMA_DEBUG_LOG(" Returned from last block #%u", pCurrBlock->GetId());
12640 return VK_SUCCESS;
12641 }
12642 }
12643 }
12644 else
12645 {
12646 if (strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
12647 {
12648 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12649 for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12650 {
12651 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12652 VMA_ASSERT(pCurrBlock);
12653 VkResult res = AllocateFromBlock(
12654 pCurrBlock,
12655 currentFrameIndex,
12656 size,
12657 alignment,
12658 allocFlagsCopy,
12659 createInfo.pUserData,
12660 suballocType,
12661 strategy,
12662 pAllocation);
12663 if (res == VK_SUCCESS)
12664 {
12665 VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
12666 return VK_SUCCESS;
12667 }
12668 }
12669 }
12670 else // WORST_FIT, FIRST_FIT
12671 {
12672 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
12673 for (size_t blockIndex = m_Blocks.size(); blockIndex--; )
12674 {
12675 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12676 VMA_ASSERT(pCurrBlock);
12677 VkResult res = AllocateFromBlock(
12678 pCurrBlock,
12679 currentFrameIndex,
12680 size,
12681 alignment,
12682 allocFlagsCopy,
12683 createInfo.pUserData,
12684 suballocType,
12685 strategy,
12686 pAllocation);
12687 if (res == VK_SUCCESS)
12688 {
12689 VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
12690 return VK_SUCCESS;
12691 }
12692 }
12693 }
12694 }
12695
12696 // 2. Try to create new block.
12697 if (canCreateNewBlock)
12698 {
12699 // Calculate optimal size for new block.
12700 VkDeviceSize newBlockSize = m_PreferredBlockSize;
12701 uint32_t newBlockSizeShift = 0;
12702 const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
12703
12704 if (!m_ExplicitBlockSize)
12705 {
12706 // Allocate 1/8, 1/4, 1/2 as first blocks.
12707 const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
12708 for (uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
12709 {
12710 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12711 if (smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
12712 {
12713 newBlockSize = smallerNewBlockSize;
12714 ++newBlockSizeShift;
12715 }
12716 else
12717 {
12718 break;
12719 }
12720 }
12721 }
12722
12723 size_t newBlockIndex = 0;
12724 VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12725 CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12726 // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
12727 if (!m_ExplicitBlockSize)
12728 {
12729 while (res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
12730 {
12731 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12732 if (smallerNewBlockSize >= size)
12733 {
12734 newBlockSize = smallerNewBlockSize;
12735 ++newBlockSizeShift;
12736 res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12737 CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12738 }
12739 else
12740 {
12741 break;
12742 }
12743 }
12744 }
12745
12746 if (res == VK_SUCCESS)
12747 {
12748 VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
12749 VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
12750
12751 res = AllocateFromBlock(
12752 pBlock,
12753 currentFrameIndex,
12754 size,
12755 alignment,
12756 allocFlagsCopy,
12757 createInfo.pUserData,
12758 suballocType,
12759 strategy,
12760 pAllocation);
12761 if (res == VK_SUCCESS)
12762 {
12763 VMA_DEBUG_LOG(" Created new block #%u Size=%llu", pBlock->GetId(), newBlockSize);
12764 return VK_SUCCESS;
12765 }
12766 else
12767 {
12768 // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
12769 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12770 }
12771 }
12772 }
12773 }
12774
12775 // 3. Try to allocate from existing blocks with making other allocations lost.
12776 if (canMakeOtherLost)
12777 {
12778 uint32_t tryIndex = 0;
12779 for (; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
12780 {
12781 VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
12782 VmaAllocationRequest bestRequest = {};
12783 VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
12784
12785 // 1. Search existing allocations.
12786 if (strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
12787 {
12788 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12789 for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12790 {
12791 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12792 VMA_ASSERT(pCurrBlock);
12793 VmaAllocationRequest currRequest = {};
12794 if (pCurrBlock->m_pMetadata->CreateAllocationRequest(
12795 currentFrameIndex,
12796 m_FrameInUseCount,
12797 m_BufferImageGranularity,
12798 size,
12799 alignment,
12800 (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
12801 suballocType,
12802 canMakeOtherLost,
12803 strategy,
12804 &currRequest))
12805 {
12806 const VkDeviceSize currRequestCost = currRequest.CalcCost();
12807 if (pBestRequestBlock == VMA_NULL ||
12808 currRequestCost < bestRequestCost)
12809 {
12810 pBestRequestBlock = pCurrBlock;
12811 bestRequest = currRequest;
12812 bestRequestCost = currRequestCost;
12813
12814 if (bestRequestCost == 0)
12815 {
12816 break;
12817 }
12818 }
12819 }
12820 }
12821 }
12822 else // WORST_FIT, FIRST_FIT
12823 {
12824 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
12825 for (size_t blockIndex = m_Blocks.size(); blockIndex--; )
12826 {
12827 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12828 VMA_ASSERT(pCurrBlock);
12829 VmaAllocationRequest currRequest = {};
12830 if (pCurrBlock->m_pMetadata->CreateAllocationRequest(
12831 currentFrameIndex,
12832 m_FrameInUseCount,
12833 m_BufferImageGranularity,
12834 size,
12835 alignment,
12836 (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
12837 suballocType,
12838 canMakeOtherLost,
12839 strategy,
12840 &currRequest))
12841 {
12842 const VkDeviceSize currRequestCost = currRequest.CalcCost();
12843 if (pBestRequestBlock == VMA_NULL ||
12844 currRequestCost < bestRequestCost ||
12845 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
12846 {
12847 pBestRequestBlock = pCurrBlock;
12848 bestRequest = currRequest;
12849 bestRequestCost = currRequestCost;
12850
12851 if (bestRequestCost == 0 ||
12852 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
12853 {
12854 break;
12855 }
12856 }
12857 }
12858 }
12859 }
12860
12861 if (pBestRequestBlock != VMA_NULL)
12862 {
12863 if (mapped)
12864 {
12865 VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
12866 if (res != VK_SUCCESS)
12867 {
12868 return res;
12869 }
12870 }
12871
12872 if (pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
12873 currentFrameIndex,
12874 m_FrameInUseCount,
12875 &bestRequest))
12876 {
12877 // Allocate from this pBlock.
12878 *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
12879 pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, *pAllocation);
12880 UpdateHasEmptyBlock();
12881 (*pAllocation)->InitBlockAllocation(
12882 pBestRequestBlock,
12883 bestRequest.offset,
12884 alignment,
12885 size,
12886 m_MemoryTypeIndex,
12887 suballocType,
12888 mapped,
12889 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
12890 VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
12891 VMA_DEBUG_LOG(" Returned from existing block");
12892 (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
12893 m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
12894 if (VMA_DEBUG_INITIALIZE_ALLOCATIONS)
12895 {
12896 m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
12897 }
12898 if (IsCorruptionDetectionEnabled())
12899 {
12900 VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
12901 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
12902 }
12903 return VK_SUCCESS;
12904 }
12905 // else: Some allocations must have been touched while we are here. Next try.
12906 }
12907 else
12908 {
12909 // Could not find place in any of the blocks - break outer loop.
12910 break;
12911 }
12912 }
12913 /* Maximum number of tries exceeded - a very unlike event when many other
12914 threads are simultaneously touching allocations making it impossible to make
12915 lost at the same time as we try to allocate. */
12916 if (tryIndex == VMA_ALLOCATION_TRY_COUNT)
12917 {
12918 return VK_ERROR_TOO_MANY_OBJECTS;
12919 }
12920 }
12921
12922 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12923 }
12924
12925 void VmaBlockVector::Free(
12926 const VmaAllocation hAllocation)
12927 {
12928 VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
12929
12930 bool budgetExceeded = false;
12931 {
12932 const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
12933 VmaBudget heapBudget = {};
12934 m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
12935 budgetExceeded = heapBudget.usage >= heapBudget.budget;
12936 }
12937
12938 // Scope for lock.
12939 {
12940 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12941
12942 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
12943
12944 if (IsCorruptionDetectionEnabled())
12945 {
12946 VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
12947 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
12948 }
12949
12950 if (hAllocation->IsPersistentMap())
12951 {
12952 pBlock->Unmap(m_hAllocator, 1);
12953 }
12954
12955 pBlock->m_pMetadata->Free(hAllocation);
12956 VMA_HEAVY_ASSERT(pBlock->Validate());
12957
12958 VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
12959
12960 const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount;
12961 // pBlock became empty after this deallocation.
12962 if (pBlock->m_pMetadata->IsEmpty())
12963 {
12964 // Already has empty block. We don't want to have two, so delete this one.
12965 if ((m_HasEmptyBlock || budgetExceeded) && canDeleteBlock)
12966 {
12967 pBlockToDelete = pBlock;
12968 Remove(pBlock);
12969 }
12970 // else: We now have an empty block - leave it.
12971 }
12972 // pBlock didn't become empty, but we have another empty block - find and free that one.
12973 // (This is optional, heuristics.)
12974 else if (m_HasEmptyBlock && canDeleteBlock)
12975 {
12976 VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
12977 if (pLastBlock->m_pMetadata->IsEmpty())
12978 {
12979 pBlockToDelete = pLastBlock;
12980 m_Blocks.pop_back();
12981 }
12982 }
12983
12984 UpdateHasEmptyBlock();
12985 IncrementallySortBlocks();
12986 }
12987
12988 // Destruction of a free block. Deferred until this point, outside of mutex
12989 // lock, for performance reason.
12990 if (pBlockToDelete != VMA_NULL)
12991 {
12992 VMA_DEBUG_LOG(" Deleted empty block");
12993 pBlockToDelete->Destroy(m_hAllocator);
12994 vma_delete(m_hAllocator, pBlockToDelete);
12995 }
12996 }
12997
12998 VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
12999 {
13000 VkDeviceSize result = 0;
13001 for (size_t i = m_Blocks.size(); i--; )
13002 {
13003 result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
13004 if (result >= m_PreferredBlockSize)
13005 {
13006 break;
13007 }
13008 }
13009 return result;
13010 }
13011
13012 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
13013 {
13014 for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13015 {
13016 if (m_Blocks[blockIndex] == pBlock)
13017 {
13018 VmaVectorRemove(m_Blocks, blockIndex);
13019 return;
13020 }
13021 }
13022 VMA_ASSERT(0);
13023 }
13024
13025 void VmaBlockVector::IncrementallySortBlocks()
13026 {
13027 if (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
13028 {
13029 // Bubble sort only until first swap.
13030 for (size_t i = 1; i < m_Blocks.size(); ++i)
13031 {
13032 if (m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
13033 {
13034 VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
13035 return;
13036 }
13037 }
13038 }
13039 }
13040
13041 VkResult VmaBlockVector::AllocateFromBlock(
13042 VmaDeviceMemoryBlock* pBlock,
13043 uint32_t currentFrameIndex,
13044 VkDeviceSize size,
13045 VkDeviceSize alignment,
13046 VmaAllocationCreateFlags allocFlags,
13047 void* pUserData,
13048 VmaSuballocationType suballocType,
13049 uint32_t strategy,
13050 VmaAllocation* pAllocation)
13051 {
13052 VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
13053 const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
13054 const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
13055 const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
13056
13057 VmaAllocationRequest currRequest = {};
13058 if (pBlock->m_pMetadata->CreateAllocationRequest(
13059 currentFrameIndex,
13060 m_FrameInUseCount,
13061 m_BufferImageGranularity,
13062 size,
13063 alignment,
13064 isUpperAddress,
13065 suballocType,
13066 false, // canMakeOtherLost
13067 strategy,
13068 &currRequest))
13069 {
13070 // Allocate from pCurrBlock.
13071 VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
13072
13073 if (mapped)
13074 {
13075 VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
13076 if (res != VK_SUCCESS)
13077 {
13078 return res;
13079 }
13080 }
13081
13082 *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
13083 pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, *pAllocation);
13084 UpdateHasEmptyBlock();
13085 (*pAllocation)->InitBlockAllocation(
13086 pBlock,
13087 currRequest.offset,
13088 alignment,
13089 size,
13090 m_MemoryTypeIndex,
13091 suballocType,
13092 mapped,
13093 (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
13094 VMA_HEAVY_ASSERT(pBlock->Validate());
13095 (*pAllocation)->SetUserData(m_hAllocator, pUserData);
13096 m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
13097 if (VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13098 {
13099 m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13100 }
13101 if (IsCorruptionDetectionEnabled())
13102 {
13103 VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
13104 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
13105 }
13106 return VK_SUCCESS;
13107 }
13108 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13109 }
13110
13111 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
13112 {
13113 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
13114 allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
13115 allocInfo.allocationSize = blockSize;
13116
13117 #if VMA_BUFFER_DEVICE_ADDRESS
13118 // Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature.
13119 VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
13120 if (m_hAllocator->m_UseKhrBufferDeviceAddress)
13121 {
13122 allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
13123 VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
13124 }
13125 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
13126
13127 VkDeviceMemory mem = VK_NULL_HANDLE;
13128 VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
13129 if (res < 0)
13130 {
13131 return res;
13132 }
13133
13134 // New VkDeviceMemory successfully created.
13135
13136 // Create new Allocation for it.
13137 VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
13138 pBlock->Init(
13139 m_hAllocator,
13140 m_hParentPool,
13141 m_MemoryTypeIndex,
13142 mem,
13143 allocInfo.allocationSize,
13144 m_NextBlockId++,
13145 m_Algorithm);
13146
13147 m_Blocks.push_back(pBlock);
13148 if (pNewBlockIndex != VMA_NULL)
13149 {
13150 *pNewBlockIndex = m_Blocks.size() - 1;
13151 }
13152
13153 return VK_SUCCESS;
13154 }
13155
13156 void VmaBlockVector::ApplyDefragmentationMovesCpu(
13157 class VmaBlockVectorDefragmentationContext* pDefragCtx,
13158 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
13159 {
13160 const size_t blockCount = m_Blocks.size();
13161 const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
13162
13163 enum BLOCK_FLAG
13164 {
13165 BLOCK_FLAG_USED = 0x00000001,
13166 BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
13167 };
13168
13169 struct BlockInfo
13170 {
13171 uint32_t flags;
13172 void* pMappedData;
13173 };
13174 VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
13175 blockInfo(blockCount, BlockInfo(), VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
13176 memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
13177
13178 // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13179 const size_t moveCount = moves.size();
13180 for (size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13181 {
13182 const VmaDefragmentationMove& move = moves[moveIndex];
13183 blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
13184 blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
13185 }
13186
13187 VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13188
13189 // Go over all blocks. Get mapped pointer or map if necessary.
13190 for (size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13191 {
13192 BlockInfo& currBlockInfo = blockInfo[blockIndex];
13193 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13194 if ((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
13195 {
13196 currBlockInfo.pMappedData = pBlock->GetMappedData();
13197 // It is not originally mapped - map it.
13198 if (currBlockInfo.pMappedData == VMA_NULL)
13199 {
13200 pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
13201 if (pDefragCtx->res == VK_SUCCESS)
13202 {
13203 currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
13204 }
13205 }
13206 }
13207 }
13208
13209 // Go over all moves. Do actual data transfer.
13210 if (pDefragCtx->res == VK_SUCCESS)
13211 {
13212 const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
13213 VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
13214
13215 for (size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13216 {
13217 const VmaDefragmentationMove& move = moves[moveIndex];
13218
13219 const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
13220 const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
13221
13222 VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
13223
13224 // Invalidate source.
13225 if (isNonCoherent)
13226 {
13227 VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
13228 memRange.memory = pSrcBlock->GetDeviceMemory();
13229 memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
13230 memRange.size = VMA_MIN(
13231 VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
13232 pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
13233 (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13234 }
13235
13236 // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
13237 memmove(
13238 reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
13239 reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
13240 static_cast<size_t>(move.size));
13241
13242 if (IsCorruptionDetectionEnabled())
13243 {
13244 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
13245 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
13246 }
13247
13248 // Flush destination.
13249 if (isNonCoherent)
13250 {
13251 VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
13252 memRange.memory = pDstBlock->GetDeviceMemory();
13253 memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
13254 memRange.size = VMA_MIN(
13255 VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
13256 pDstBlock->m_pMetadata->GetSize() - memRange.offset);
13257 (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13258 }
13259 }
13260 }
13261
13262 // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
13263 // Regardless of pCtx->res == VK_SUCCESS.
13264 for (size_t blockIndex = blockCount; blockIndex--; )
13265 {
13266 const BlockInfo& currBlockInfo = blockInfo[blockIndex];
13267 if ((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
13268 {
13269 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13270 pBlock->Unmap(m_hAllocator, 1);
13271 }
13272 }
13273 }
13274
13275 void VmaBlockVector::ApplyDefragmentationMovesGpu(
13276 class VmaBlockVectorDefragmentationContext* pDefragCtx,
13277 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13278 VkCommandBuffer commandBuffer)
13279 {
13280 const size_t blockCount = m_Blocks.size();
13281
13282 pDefragCtx->blockContexts.resize(blockCount);
13283 memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext));
13284
13285 // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13286 const size_t moveCount = moves.size();
13287 for (size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13288 {
13289 const VmaDefragmentationMove& move = moves[moveIndex];
13290
13291 //if(move.type == VMA_ALLOCATION_TYPE_UNKNOWN)
13292 {
13293 // Old school move still require us to map the whole block
13294 pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13295 pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13296 }
13297 }
13298
13299 VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13300
13301 // Go over all blocks. Create and bind buffer for whole block if necessary.
13302 {
13303 VkBufferCreateInfo bufCreateInfo;
13304 VmaFillGpuDefragmentationBufferCreateInfo(bufCreateInfo);
13305
13306 for (size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13307 {
13308 VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
13309 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13310 if ((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
13311 {
13312 bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
13313 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
13314 m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
13315 if (pDefragCtx->res == VK_SUCCESS)
13316 {
13317 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
13318 m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
13319 }
13320 }
13321 }
13322 }
13323
13324 // Go over all moves. Post data transfer commands to command buffer.
13325 if (pDefragCtx->res == VK_SUCCESS)
13326 {
13327 for (size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13328 {
13329 const VmaDefragmentationMove& move = moves[moveIndex];
13330
13331 const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
13332 const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
13333
13334 VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
13335
13336 VkBufferCopy region = {
13337 move.srcOffset,
13338 move.dstOffset,
13339 move.size };
13340 (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
13341 commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, &region);
13342 }
13343 }
13344
13345 // Save buffers to defrag context for later destruction.
13346 if (pDefragCtx->res == VK_SUCCESS && moveCount > 0)
13347 {
13348 pDefragCtx->res = VK_NOT_READY;
13349 }
13350 }
13351
13352 void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
13353 {
13354 for (size_t blockIndex = m_Blocks.size(); blockIndex--; )
13355 {
13356 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13357 if (pBlock->m_pMetadata->IsEmpty())
13358 {
13359 if (m_Blocks.size() > m_MinBlockCount)
13360 {
13361 if (pDefragmentationStats != VMA_NULL)
13362 {
13363 ++pDefragmentationStats->deviceMemoryBlocksFreed;
13364 pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
13365 }
13366
13367 VmaVectorRemove(m_Blocks, blockIndex);
13368 pBlock->Destroy(m_hAllocator);
13369 vma_delete(m_hAllocator, pBlock);
13370 }
13371 else
13372 {
13373 break;
13374 }
13375 }
13376 }
13377 UpdateHasEmptyBlock();
13378 }
13379
13380 void VmaBlockVector::UpdateHasEmptyBlock()
13381 {
13382 m_HasEmptyBlock = false;
13383 for (size_t index = 0, count = m_Blocks.size(); index < count; ++index)
13384 {
13385 VmaDeviceMemoryBlock* const pBlock = m_Blocks[index];
13386 if (pBlock->m_pMetadata->IsEmpty())
13387 {
13388 m_HasEmptyBlock = true;
13389 break;
13390 }
13391 }
13392 }
13393
13394 #if VMA_STATS_STRING_ENABLED
13395
13396 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
13397 {
13398 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13399
13400 json.BeginObject();
13401
13402 if (IsCustomPool())
13403 {
13404 const char* poolName = m_hParentPool->GetName();
13405 if (poolName != VMA_NULL && poolName[0] != '\0')
13406 {
13407 json.WriteString("Name");
13408 json.WriteString(poolName);
13409 }
13410
13411 json.WriteString("MemoryTypeIndex");
13412 json.WriteNumber(m_MemoryTypeIndex);
13413
13414 json.WriteString("BlockSize");
13415 json.WriteNumber(m_PreferredBlockSize);
13416
13417 json.WriteString("BlockCount");
13418 json.BeginObject(true);
13419 if (m_MinBlockCount > 0)
13420 {
13421 json.WriteString("Min");
13422 json.WriteNumber((uint64_t)m_MinBlockCount);
13423 }
13424 if (m_MaxBlockCount < SIZE_MAX)
13425 {
13426 json.WriteString("Max");
13427 json.WriteNumber((uint64_t)m_MaxBlockCount);
13428 }
13429 json.WriteString("Cur");
13430 json.WriteNumber((uint64_t)m_Blocks.size());
13431 json.EndObject();
13432
13433 if (m_FrameInUseCount > 0)
13434 {
13435 json.WriteString("FrameInUseCount");
13436 json.WriteNumber(m_FrameInUseCount);
13437 }
13438
13439 if (m_Algorithm != 0)
13440 {
13441 json.WriteString("Algorithm");
13442 json.WriteString(VmaAlgorithmToStr(m_Algorithm));
13443 }
13444 }
13445 else
13446 {
13447 json.WriteString("PreferredBlockSize");
13448 json.WriteNumber(m_PreferredBlockSize);
13449 }
13450
13451 json.WriteString("Blocks");
13452 json.BeginObject();
13453 for (size_t i = 0; i < m_Blocks.size(); ++i)
13454 {
13455 json.BeginString();
13456 json.ContinueString(m_Blocks[i]->GetId());
13457 json.EndString();
13458
13459 m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
13460 }
13461 json.EndObject();
13462
13463 json.EndObject();
13464 }
13465
13466 #endif // #if VMA_STATS_STRING_ENABLED
13467
13468 void VmaBlockVector::Defragment(
13469 class VmaBlockVectorDefragmentationContext* pCtx,
13470 VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags,
13471 VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
13472 VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
13473 VkCommandBuffer commandBuffer)
13474 {
13475 pCtx->res = VK_SUCCESS;
13476
13477 const VkMemoryPropertyFlags memPropFlags =
13478 m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
13479 const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
13480
13481 const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
13482 isHostVisible;
13483 const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
13484 !IsCorruptionDetectionEnabled() &&
13485 ((1u << m_MemoryTypeIndex) & m_hAllocator->GetGpuDefragmentationMemoryTypeBits()) != 0;
13486
13487 // There are options to defragment this memory type.
13488 if (canDefragmentOnCpu || canDefragmentOnGpu)
13489 {
13490 bool defragmentOnGpu;
13491 // There is only one option to defragment this memory type.
13492 if (canDefragmentOnGpu != canDefragmentOnCpu)
13493 {
13494 defragmentOnGpu = canDefragmentOnGpu;
13495 }
13496 // Both options are available: Heuristics to choose the best one.
13497 else
13498 {
13499 defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
13500 m_hAllocator->IsIntegratedGpu();
13501 }
13502
13503 bool overlappingMoveSupported = !defragmentOnGpu;
13504
13505 if (m_hAllocator->m_UseMutex)
13506 {
13507 if (flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
13508 {
13509 if (!m_Mutex.TryLockWrite())
13510 {
13511 pCtx->res = VK_ERROR_INITIALIZATION_FAILED;
13512 return;
13513 }
13514 }
13515 else
13516 {
13517 m_Mutex.LockWrite();
13518 pCtx->mutexLocked = true;
13519 }
13520 }
13521
13522 pCtx->Begin(overlappingMoveSupported, flags);
13523
13524 // Defragment.
13525
13526 const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
13527 const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
13528 pCtx->res = pCtx->GetAlgorithm()->Defragment(pCtx->defragmentationMoves, maxBytesToMove, maxAllocationsToMove, flags);
13529
13530 // Accumulate statistics.
13531 if (pStats != VMA_NULL)
13532 {
13533 const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
13534 const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
13535 pStats->bytesMoved += bytesMoved;
13536 pStats->allocationsMoved += allocationsMoved;
13537 VMA_ASSERT(bytesMoved <= maxBytesToMove);
13538 VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
13539 if (defragmentOnGpu)
13540 {
13541 maxGpuBytesToMove -= bytesMoved;
13542 maxGpuAllocationsToMove -= allocationsMoved;
13543 }
13544 else
13545 {
13546 maxCpuBytesToMove -= bytesMoved;
13547 maxCpuAllocationsToMove -= allocationsMoved;
13548 }
13549 }
13550
13551 if (flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
13552 {
13553 if (m_hAllocator->m_UseMutex)
13554 m_Mutex.UnlockWrite();
13555
13556 if (pCtx->res >= VK_SUCCESS && !pCtx->defragmentationMoves.empty())
13557 pCtx->res = VK_NOT_READY;
13558
13559 return;
13560 }
13561
13562 if (pCtx->res >= VK_SUCCESS)
13563 {
13564 if (defragmentOnGpu)
13565 {
13566 ApplyDefragmentationMovesGpu(pCtx, pCtx->defragmentationMoves, commandBuffer);
13567 }
13568 else
13569 {
13570 ApplyDefragmentationMovesCpu(pCtx, pCtx->defragmentationMoves);
13571 }
13572 }
13573 }
13574 }
13575
13576 void VmaBlockVector::DefragmentationEnd(
13577 class VmaBlockVectorDefragmentationContext* pCtx,
13578 uint32_t flags,
13579 VmaDefragmentationStats* pStats)
13580 {
13581 if (flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL && m_hAllocator->m_UseMutex)
13582 {
13583 VMA_ASSERT(pCtx->mutexLocked == false);
13584
13585 // Incremental defragmentation doesn't hold the lock, so when we enter here we don't actually have any
13586 // lock protecting us. Since we mutate state here, we have to take the lock out now
13587 m_Mutex.LockWrite();
13588 pCtx->mutexLocked = true;
13589 }
13590
13591 // If the mutex isn't locked we didn't do any work and there is nothing to delete.
13592 if (pCtx->mutexLocked || !m_hAllocator->m_UseMutex)
13593 {
13594 // Destroy buffers.
13595 for (size_t blockIndex = pCtx->blockContexts.size(); blockIndex--;)
13596 {
13597 VmaBlockDefragmentationContext& blockCtx = pCtx->blockContexts[blockIndex];
13598 if (blockCtx.hBuffer)
13599 {
13600 (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
13601 }
13602 }
13603
13604 if (pCtx->res >= VK_SUCCESS)
13605 {
13606 FreeEmptyBlocks(pStats);
13607 }
13608 }
13609
13610 if (pCtx->mutexLocked)
13611 {
13612 VMA_ASSERT(m_hAllocator->m_UseMutex);
13613 m_Mutex.UnlockWrite();
13614 }
13615 }
13616
13617 uint32_t VmaBlockVector::ProcessDefragmentations(
13618 class VmaBlockVectorDefragmentationContext* pCtx,
13619 VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves)
13620 {
13621 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13622
13623 const uint32_t moveCount = std::min(uint32_t(pCtx->defragmentationMoves.size()) - pCtx->defragmentationMovesProcessed, maxMoves);
13624
13625 for (uint32_t i = 0; i < moveCount; ++i)
13626 {
13627 VmaDefragmentationMove& move = pCtx->defragmentationMoves[pCtx->defragmentationMovesProcessed + i];
13628
13629 pMove->allocation = move.hAllocation;
13630 pMove->memory = move.pDstBlock->GetDeviceMemory();
13631 pMove->offset = move.dstOffset;
13632
13633 ++pMove;
13634 }
13635
13636 pCtx->defragmentationMovesProcessed += moveCount;
13637
13638 return moveCount;
13639 }
13640
13641 void VmaBlockVector::CommitDefragmentations(
13642 class VmaBlockVectorDefragmentationContext* pCtx,
13643 VmaDefragmentationStats* pStats)
13644 {
13645 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13646
13647 for (uint32_t i = pCtx->defragmentationMovesCommitted; i < pCtx->defragmentationMovesProcessed; ++i)
13648 {
13649 const VmaDefragmentationMove& move = pCtx->defragmentationMoves[i];
13650
13651 move.pSrcBlock->m_pMetadata->FreeAtOffset(move.srcOffset);
13652 move.hAllocation->ChangeBlockAllocation(m_hAllocator, move.pDstBlock, move.dstOffset);
13653 }
13654
13655 pCtx->defragmentationMovesCommitted = pCtx->defragmentationMovesProcessed;
13656 FreeEmptyBlocks(pStats);
13657 }
13658
13659 size_t VmaBlockVector::CalcAllocationCount() const
13660 {
13661 size_t result = 0;
13662 for (size_t i = 0; i < m_Blocks.size(); ++i)
13663 {
13664 result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
13665 }
13666 return result;
13667 }
13668
13669 bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
13670 {
13671 if (m_BufferImageGranularity == 1)
13672 {
13673 return false;
13674 }
13675 VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
13676 for (size_t i = 0, count = m_Blocks.size(); i < count; ++i)
13677 {
13678 VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
13679 VMA_ASSERT(m_Algorithm == 0);
13680 VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
13681 if (pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
13682 {
13683 return true;
13684 }
13685 }
13686 return false;
13687 }
13688
13689 void VmaBlockVector::MakePoolAllocationsLost(
13690 uint32_t currentFrameIndex,
13691 size_t* pLostAllocationCount)
13692 {
13693 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13694 size_t lostAllocationCount = 0;
13695 for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13696 {
13697 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13698 VMA_ASSERT(pBlock);
13699 lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
13700 }
13701 if (pLostAllocationCount != VMA_NULL)
13702 {
13703 *pLostAllocationCount = lostAllocationCount;
13704 }
13705 }
13706
13707 VkResult VmaBlockVector::CheckCorruption()
13708 {
13709 if (!IsCorruptionDetectionEnabled())
13710 {
13711 return VK_ERROR_FEATURE_NOT_PRESENT;
13712 }
13713
13714 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13715 for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13716 {
13717 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13718 VMA_ASSERT(pBlock);
13719 VkResult res = pBlock->CheckCorruption(m_hAllocator);
13720 if (res != VK_SUCCESS)
13721 {
13722 return res;
13723 }
13724 }
13725 return VK_SUCCESS;
13726 }
13727
13728 void VmaBlockVector::AddStats(VmaStats* pStats)
13729 {
13730 const uint32_t memTypeIndex = m_MemoryTypeIndex;
13731 const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
13732
13733 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13734
13735 for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13736 {
13737 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13738 VMA_ASSERT(pBlock);
13739 VMA_HEAVY_ASSERT(pBlock->Validate());
13740 VmaStatInfo allocationStatInfo;
13741 pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
13742 VmaAddStatInfo(pStats->total, allocationStatInfo);
13743 VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
13744 VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
13745 }
13746 }
13747
13748 ////////////////////////////////////////////////////////////////////////////////
13749 // VmaDefragmentationAlgorithm_Generic members definition
13750
13751 VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
13752 VmaAllocator hAllocator,
13753 VmaBlockVector* pBlockVector,
13754 uint32_t currentFrameIndex,
13755 bool overlappingMoveSupported) :
13756 VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
13757 m_AllocationCount(0),
13758 m_AllAllocations(false),
13759 m_BytesMoved(0),
13760 m_AllocationsMoved(0),
13761 m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
13762 {
13763 // Create block info for each block.
13764 const size_t blockCount = m_pBlockVector->m_Blocks.size();
13765 for (size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13766 {
13767 BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
13768 pBlockInfo->m_OriginalBlockIndex = blockIndex;
13769 pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
13770 m_Blocks.push_back(pBlockInfo);
13771 }
13772
13773 // Sort them by m_pBlock pointer value.
13774 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
13775 }
13776
13777 VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
13778 {
13779 for (size_t i = m_Blocks.size(); i--; )
13780 {
13781 vma_delete(m_hAllocator, m_Blocks[i]);
13782 }
13783 }
13784
13785 void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
13786 {
13787 // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
13788 if (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
13789 {
13790 VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
13791 BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
13792 if (it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
13793 {
13794 AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
13795 (*it)->m_Allocations.push_back(allocInfo);
13796 }
13797 else
13798 {
13799 VMA_ASSERT(0);
13800 }
13801
13802 ++m_AllocationCount;
13803 }
13804 }
13805
13806 VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
13807 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13808 VkDeviceSize maxBytesToMove,
13809 uint32_t maxAllocationsToMove,
13810 bool freeOldAllocations)
13811 {
13812 if (m_Blocks.empty())
13813 {
13814 return VK_SUCCESS;
13815 }
13816
13817 // This is a choice based on research.
13818 // Option 1:
13819 uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
13820 // Option 2:
13821 //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
13822 // Option 3:
13823 //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
13824
13825 size_t srcBlockMinIndex = 0;
13826 // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
13827 /*
13828 if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
13829 {
13830 const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
13831 if(blocksWithNonMovableCount > 0)
13832 {
13833 srcBlockMinIndex = blocksWithNonMovableCount - 1;
13834 }
13835 }
13836 */
13837
13838 size_t srcBlockIndex = m_Blocks.size() - 1;
13839 size_t srcAllocIndex = SIZE_MAX;
13840 for (;;)
13841 {
13842 // 1. Find next allocation to move.
13843 // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
13844 // 1.2. Then start from last to first m_Allocations.
13845 while (srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
13846 {
13847 if (m_Blocks[srcBlockIndex]->m_Allocations.empty())
13848 {
13849 // Finished: no more allocations to process.
13850 if (srcBlockIndex == srcBlockMinIndex)
13851 {
13852 return VK_SUCCESS;
13853 }
13854 else
13855 {
13856 --srcBlockIndex;
13857 srcAllocIndex = SIZE_MAX;
13858 }
13859 }
13860 else
13861 {
13862 srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
13863 }
13864 }
13865
13866 BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
13867 AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
13868
13869 const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
13870 const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
13871 const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
13872 const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
13873
13874 // 2. Try to find new place for this allocation in preceding or current block.
13875 for (size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
13876 {
13877 BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
13878 VmaAllocationRequest dstAllocRequest;
13879 if (pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
13880 m_CurrentFrameIndex,
13881 m_pBlockVector->GetFrameInUseCount(),
13882 m_pBlockVector->GetBufferImageGranularity(),
13883 size,
13884 alignment,
13885 false, // upperAddress
13886 suballocType,
13887 false, // canMakeOtherLost
13888 strategy,
13889 &dstAllocRequest) &&
13890 MoveMakesSense(
13891 dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
13892 {
13893 VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
13894
13895 // Reached limit on number of allocations or bytes to move.
13896 if ((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
13897 (m_BytesMoved + size > maxBytesToMove))
13898 {
13899 return VK_SUCCESS;
13900 }
13901
13902 VmaDefragmentationMove move = {};
13903 move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
13904 move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
13905 move.srcOffset = srcOffset;
13906 move.dstOffset = dstAllocRequest.offset;
13907 move.size = size;
13908 move.hAllocation = allocInfo.m_hAllocation;
13909 move.pSrcBlock = pSrcBlockInfo->m_pBlock;
13910 move.pDstBlock = pDstBlockInfo->m_pBlock;
13911
13912 moves.push_back(move);
13913
13914 pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
13915 dstAllocRequest,
13916 suballocType,
13917 size,
13918 allocInfo.m_hAllocation);
13919
13920 if (freeOldAllocations)
13921 {
13922 pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
13923 allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
13924 }
13925
13926 if (allocInfo.m_pChanged != VMA_NULL)
13927 {
13928 *allocInfo.m_pChanged = VK_TRUE;
13929 }
13930
13931 ++m_AllocationsMoved;
13932 m_BytesMoved += size;
13933
13934 VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
13935
13936 break;
13937 }
13938 }
13939
13940 // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
13941
13942 if (srcAllocIndex > 0)
13943 {
13944 --srcAllocIndex;
13945 }
13946 else
13947 {
13948 if (srcBlockIndex > 0)
13949 {
13950 --srcBlockIndex;
13951 srcAllocIndex = SIZE_MAX;
13952 }
13953 else
13954 {
13955 return VK_SUCCESS;
13956 }
13957 }
13958 }
13959 }
13960
13961 size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
13962 {
13963 size_t result = 0;
13964 for (size_t i = 0; i < m_Blocks.size(); ++i)
13965 {
13966 if (m_Blocks[i]->m_HasNonMovableAllocations)
13967 {
13968 ++result;
13969 }
13970 }
13971 return result;
13972 }
13973
13974 VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
13975 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13976 VkDeviceSize maxBytesToMove,
13977 uint32_t maxAllocationsToMove,
13978 VmaDefragmentationFlags flags)
13979 {
13980 if (!m_AllAllocations && m_AllocationCount == 0)
13981 {
13982 return VK_SUCCESS;
13983 }
13984
13985 const size_t blockCount = m_Blocks.size();
13986 for (size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13987 {
13988 BlockInfo* pBlockInfo = m_Blocks[blockIndex];
13989
13990 if (m_AllAllocations)
13991 {
13992 VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
13993 for (VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
13994 it != pMetadata->m_Suballocations.end();
13995 ++it)
13996 {
13997 if (it->type != VMA_SUBALLOCATION_TYPE_FREE)
13998 {
13999 AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
14000 pBlockInfo->m_Allocations.push_back(allocInfo);
14001 }
14002 }
14003 }
14004
14005 pBlockInfo->CalcHasNonMovableAllocations();
14006
14007 // This is a choice based on research.
14008 // Option 1:
14009 pBlockInfo->SortAllocationsByOffsetDescending();
14010 // Option 2:
14011 //pBlockInfo->SortAllocationsBySizeDescending();
14012 }
14013
14014 // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
14015 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
14016
14017 // This is a choice based on research.
14018 const uint32_t roundCount = 2;
14019
14020 // Execute defragmentation rounds (the main part).
14021 VkResult result = VK_SUCCESS;
14022 for (uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
14023 {
14024 result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove, !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL));
14025 }
14026
14027 return result;
14028 }
14029
14030 bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
14031 size_t dstBlockIndex, VkDeviceSize dstOffset,
14032 size_t srcBlockIndex, VkDeviceSize srcOffset)
14033 {
14034 if (dstBlockIndex < srcBlockIndex)
14035 {
14036 return true;
14037 }
14038 if (dstBlockIndex > srcBlockIndex)
14039 {
14040 return false;
14041 }
14042 if (dstOffset < srcOffset)
14043 {
14044 return true;
14045 }
14046 return false;
14047 }
14048
14049 ////////////////////////////////////////////////////////////////////////////////
14050 // VmaDefragmentationAlgorithm_Fast
14051
14052 VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
14053 VmaAllocator hAllocator,
14054 VmaBlockVector* pBlockVector,
14055 uint32_t currentFrameIndex,
14056 bool overlappingMoveSupported) :
14057 VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
14058 m_OverlappingMoveSupported(overlappingMoveSupported),
14059 m_AllocationCount(0),
14060 m_AllAllocations(false),
14061 m_BytesMoved(0),
14062 m_AllocationsMoved(0),
14063 m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
14064 {
14065 VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
14066
14067 }
14068
14069 VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
14070 {
14071 }
14072
14073 VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
14074 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14075 VkDeviceSize maxBytesToMove,
14076 uint32_t maxAllocationsToMove,
14077 VmaDefragmentationFlags flags)
14078 {
14079 VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
14080
14081 const size_t blockCount = m_pBlockVector->GetBlockCount();
14082 if (blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
14083 {
14084 return VK_SUCCESS;
14085 }
14086
14087 PreprocessMetadata();
14088
14089 // Sort blocks in order from most destination.
14090
14091 m_BlockInfos.resize(blockCount);
14092 for (size_t i = 0; i < blockCount; ++i)
14093 {
14094 m_BlockInfos[i].origBlockIndex = i;
14095 }
14096
14097 VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
14098 return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
14099 m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
14100 });
14101
14102 // THE MAIN ALGORITHM
14103
14104 FreeSpaceDatabase freeSpaceDb;
14105
14106 size_t dstBlockInfoIndex = 0;
14107 size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14108 VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14109 VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14110 VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
14111 VkDeviceSize dstOffset = 0;
14112
14113 bool end = false;
14114 for (size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
14115 {
14116 const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
14117 VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
14118 VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
14119 for (VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
14120 !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
14121 {
14122 VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
14123 const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
14124 const VkDeviceSize srcAllocSize = srcSuballocIt->size;
14125 if (m_AllocationsMoved == maxAllocationsToMove ||
14126 m_BytesMoved + srcAllocSize > maxBytesToMove)
14127 {
14128 end = true;
14129 break;
14130 }
14131 const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
14132
14133 VmaDefragmentationMove move = {};
14134 // Try to place it in one of free spaces from the database.
14135 size_t freeSpaceInfoIndex;
14136 VkDeviceSize dstAllocOffset;
14137 if (freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
14138 freeSpaceInfoIndex, dstAllocOffset))
14139 {
14140 size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
14141 VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
14142 VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
14143
14144 // Same block
14145 if (freeSpaceInfoIndex == srcBlockInfoIndex)
14146 {
14147 VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14148
14149 // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14150
14151 VmaSuballocation suballoc = *srcSuballocIt;
14152 suballoc.offset = dstAllocOffset;
14153 suballoc.hAllocation->ChangeOffset(dstAllocOffset);
14154 m_BytesMoved += srcAllocSize;
14155 ++m_AllocationsMoved;
14156
14157 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14158 ++nextSuballocIt;
14159 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14160 srcSuballocIt = nextSuballocIt;
14161
14162 InsertSuballoc(pFreeSpaceMetadata, suballoc);
14163
14164 move.srcBlockIndex = srcOrigBlockIndex;
14165 move.dstBlockIndex = freeSpaceOrigBlockIndex;
14166 move.srcOffset = srcAllocOffset;
14167 move.dstOffset = dstAllocOffset;
14168 move.size = srcAllocSize;
14169
14170 moves.push_back(move);
14171 }
14172 // Different block
14173 else
14174 {
14175 // MOVE OPTION 2: Move the allocation to a different block.
14176
14177 VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
14178
14179 VmaSuballocation suballoc = *srcSuballocIt;
14180 suballoc.offset = dstAllocOffset;
14181 suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
14182 m_BytesMoved += srcAllocSize;
14183 ++m_AllocationsMoved;
14184
14185 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14186 ++nextSuballocIt;
14187 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14188 srcSuballocIt = nextSuballocIt;
14189
14190 InsertSuballoc(pFreeSpaceMetadata, suballoc);
14191
14192 move.srcBlockIndex = srcOrigBlockIndex;
14193 move.dstBlockIndex = freeSpaceOrigBlockIndex;
14194 move.srcOffset = srcAllocOffset;
14195 move.dstOffset = dstAllocOffset;
14196 move.size = srcAllocSize;
14197
14198 moves.push_back(move);
14199 }
14200 }
14201 else
14202 {
14203 dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
14204
14205 // If the allocation doesn't fit before the end of dstBlock, forward to next block.
14206 while (dstBlockInfoIndex < srcBlockInfoIndex &&
14207 dstAllocOffset + srcAllocSize > dstBlockSize)
14208 {
14209 // But before that, register remaining free space at the end of dst block.
14210 freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
14211
14212 ++dstBlockInfoIndex;
14213 dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14214 pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14215 pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14216 dstBlockSize = pDstMetadata->GetSize();
14217 dstOffset = 0;
14218 dstAllocOffset = 0;
14219 }
14220
14221 // Same block
14222 if (dstBlockInfoIndex == srcBlockInfoIndex)
14223 {
14224 VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14225
14226 const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
14227
14228 bool skipOver = overlap;
14229 if (overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
14230 {
14231 // If destination and source place overlap, skip if it would move it
14232 // by only < 1/64 of its size.
14233 skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
14234 }
14235
14236 if (skipOver)
14237 {
14238 freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
14239
14240 dstOffset = srcAllocOffset + srcAllocSize;
14241 ++srcSuballocIt;
14242 }
14243 // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14244 else
14245 {
14246 srcSuballocIt->offset = dstAllocOffset;
14247 srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);
14248 dstOffset = dstAllocOffset + srcAllocSize;
14249 m_BytesMoved += srcAllocSize;
14250 ++m_AllocationsMoved;
14251 ++srcSuballocIt;
14252
14253 move.srcBlockIndex = srcOrigBlockIndex;
14254 move.dstBlockIndex = dstOrigBlockIndex;
14255 move.srcOffset = srcAllocOffset;
14256 move.dstOffset = dstAllocOffset;
14257 move.size = srcAllocSize;
14258
14259 moves.push_back(move);
14260 }
14261 }
14262 // Different block
14263 else
14264 {
14265 // MOVE OPTION 2: Move the allocation to a different block.
14266
14267 VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
14268 VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
14269
14270 VmaSuballocation suballoc = *srcSuballocIt;
14271 suballoc.offset = dstAllocOffset;
14272 suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
14273 dstOffset = dstAllocOffset + srcAllocSize;
14274 m_BytesMoved += srcAllocSize;
14275 ++m_AllocationsMoved;
14276
14277 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14278 ++nextSuballocIt;
14279 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14280 srcSuballocIt = nextSuballocIt;
14281
14282 pDstMetadata->m_Suballocations.push_back(suballoc);
14283
14284 move.srcBlockIndex = srcOrigBlockIndex;
14285 move.dstBlockIndex = dstOrigBlockIndex;
14286 move.srcOffset = srcAllocOffset;
14287 move.dstOffset = dstAllocOffset;
14288 move.size = srcAllocSize;
14289
14290 moves.push_back(move);
14291 }
14292 }
14293 }
14294 }
14295
14296 m_BlockInfos.clear();
14297
14298 PostprocessMetadata();
14299
14300 return VK_SUCCESS;
14301 }
14302
14303 void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
14304 {
14305 const size_t blockCount = m_pBlockVector->GetBlockCount();
14306 for (size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14307 {
14308 VmaBlockMetadata_Generic* const pMetadata =
14309 (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14310 pMetadata->m_FreeCount = 0;
14311 pMetadata->m_SumFreeSize = pMetadata->GetSize();
14312 pMetadata->m_FreeSuballocationsBySize.clear();
14313 for (VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14314 it != pMetadata->m_Suballocations.end(); )
14315 {
14316 if (it->type == VMA_SUBALLOCATION_TYPE_FREE)
14317 {
14318 VmaSuballocationList::iterator nextIt = it;
14319 ++nextIt;
14320 pMetadata->m_Suballocations.erase(it);
14321 it = nextIt;
14322 }
14323 else
14324 {
14325 ++it;
14326 }
14327 }
14328 }
14329 }
14330
14331 void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
14332 {
14333 const size_t blockCount = m_pBlockVector->GetBlockCount();
14334 for (size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14335 {
14336 VmaBlockMetadata_Generic* const pMetadata =
14337 (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14338 const VkDeviceSize blockSize = pMetadata->GetSize();
14339
14340 // No allocations in this block - entire area is free.
14341 if (pMetadata->m_Suballocations.empty())
14342 {
14343 pMetadata->m_FreeCount = 1;
14344 //pMetadata->m_SumFreeSize is already set to blockSize.
14345 VmaSuballocation suballoc = {
14346 0, // offset
14347 blockSize, // size
14348 VMA_NULL, // hAllocation
14349 VMA_SUBALLOCATION_TYPE_FREE };
14350 pMetadata->m_Suballocations.push_back(suballoc);
14351 pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
14352 }
14353 // There are some allocations in this block.
14354 else
14355 {
14356 VkDeviceSize offset = 0;
14357 VmaSuballocationList::iterator it;
14358 for (it = pMetadata->m_Suballocations.begin();
14359 it != pMetadata->m_Suballocations.end();
14360 ++it)
14361 {
14362 VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
14363 VMA_ASSERT(it->offset >= offset);
14364
14365 // Need to insert preceding free space.
14366 if (it->offset > offset)
14367 {
14368 ++pMetadata->m_FreeCount;
14369 const VkDeviceSize freeSize = it->offset - offset;
14370 VmaSuballocation suballoc = {
14371 offset, // offset
14372 freeSize, // size
14373 VMA_NULL, // hAllocation
14374 VMA_SUBALLOCATION_TYPE_FREE };
14375 VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14376 if (freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14377 {
14378 pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
14379 }
14380 }
14381
14382 pMetadata->m_SumFreeSize -= it->size;
14383 offset = it->offset + it->size;
14384 }
14385
14386 // Need to insert trailing free space.
14387 if (offset < blockSize)
14388 {
14389 ++pMetadata->m_FreeCount;
14390 const VkDeviceSize freeSize = blockSize - offset;
14391 VmaSuballocation suballoc = {
14392 offset, // offset
14393 freeSize, // size
14394 VMA_NULL, // hAllocation
14395 VMA_SUBALLOCATION_TYPE_FREE };
14396 VMA_ASSERT(it == pMetadata->m_Suballocations.end());
14397 VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14398 if (freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14399 {
14400 pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
14401 }
14402 }
14403
14404 VMA_SORT(
14405 pMetadata->m_FreeSuballocationsBySize.begin(),
14406 pMetadata->m_FreeSuballocationsBySize.end(),
14407 VmaSuballocationItemSizeLess());
14408 }
14409
14410 VMA_HEAVY_ASSERT(pMetadata->Validate());
14411 }
14412 }
14413
14414 void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
14415 {
14416 // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
14417 VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14418 while (it != pMetadata->m_Suballocations.end())
14419 {
14420 if (it->offset < suballoc.offset)
14421 {
14422 ++it;
14423 }
14424 }
14425 pMetadata->m_Suballocations.insert(it, suballoc);
14426 }
14427
14428 ////////////////////////////////////////////////////////////////////////////////
14429 // VmaBlockVectorDefragmentationContext
14430
14431 VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
14432 VmaAllocator hAllocator,
14433 VmaPool hCustomPool,
14434 VmaBlockVector* pBlockVector,
14435 uint32_t currFrameIndex) :
14436 res(VK_SUCCESS),
14437 mutexLocked(false),
14438 blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
14439 defragmentationMoves(VmaStlAllocator<VmaDefragmentationMove>(hAllocator->GetAllocationCallbacks())),
14440 defragmentationMovesProcessed(0),
14441 defragmentationMovesCommitted(0),
14442 hasDefragmentationPlan(0),
14443 m_hAllocator(hAllocator),
14444 m_hCustomPool(hCustomPool),
14445 m_pBlockVector(pBlockVector),
14446 m_CurrFrameIndex(currFrameIndex),
14447 m_pAlgorithm(VMA_NULL),
14448 m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
14449 m_AllAllocations(false)
14450 {
14451 }
14452
14453 VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
14454 {
14455 vma_delete(m_hAllocator, m_pAlgorithm);
14456 }
14457
14458 void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
14459 {
14460 AllocInfo info = { hAlloc, pChanged };
14461 m_Allocations.push_back(info);
14462 }
14463
14464 void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags)
14465 {
14466 const bool allAllocations = m_AllAllocations ||
14467 m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
14468
14469 /********************************
14470 HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
14471 ********************************/
14472
14473 /*
14474 Fast algorithm is supported only when certain criteria are met:
14475 - VMA_DEBUG_MARGIN is 0.
14476 - All allocations in this block vector are moveable.
14477 - There is no possibility of image/buffer granularity conflict.
14478 - The defragmentation is not incremental
14479 */
14480 if (VMA_DEBUG_MARGIN == 0 &&
14481 allAllocations &&
14482 !m_pBlockVector->IsBufferImageGranularityConflictPossible() &&
14483 !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL))
14484 {
14485 m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
14486 m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14487 }
14488 else
14489 {
14490 m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
14491 m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14492 }
14493
14494 if (allAllocations)
14495 {
14496 m_pAlgorithm->AddAll();
14497 }
14498 else
14499 {
14500 for (size_t i = 0, count = m_Allocations.size(); i < count; ++i)
14501 {
14502 m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
14503 }
14504 }
14505 }
14506
14507 ////////////////////////////////////////////////////////////////////////////////
14508 // VmaDefragmentationContext
14509
14510 VmaDefragmentationContext_T::VmaDefragmentationContext_T(
14511 VmaAllocator hAllocator,
14512 uint32_t currFrameIndex,
14513 uint32_t flags,
14514 VmaDefragmentationStats* pStats) :
14515 m_hAllocator(hAllocator),
14516 m_CurrFrameIndex(currFrameIndex),
14517 m_Flags(flags),
14518 m_pStats(pStats),
14519 m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
14520 {
14521 memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
14522 }
14523
14524 VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
14525 {
14526 for (size_t i = m_CustomPoolContexts.size(); i--; )
14527 {
14528 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
14529 pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
14530 vma_delete(m_hAllocator, pBlockVectorCtx);
14531 }
14532 for (size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
14533 {
14534 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
14535 if (pBlockVectorCtx)
14536 {
14537 pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
14538 vma_delete(m_hAllocator, pBlockVectorCtx);
14539 }
14540 }
14541 }
14542
14543 void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, const VmaPool* pPools)
14544 {
14545 for (uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
14546 {
14547 VmaPool pool = pPools[poolIndex];
14548 VMA_ASSERT(pool);
14549 // Pools with algorithm other than default are not defragmented.
14550 if (pool->m_BlockVector.GetAlgorithm() == 0)
14551 {
14552 VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
14553
14554 for (size_t i = m_CustomPoolContexts.size(); i--; )
14555 {
14556 if (m_CustomPoolContexts[i]->GetCustomPool() == pool)
14557 {
14558 pBlockVectorDefragCtx = m_CustomPoolContexts[i];
14559 break;
14560 }
14561 }
14562
14563 if (!pBlockVectorDefragCtx)
14564 {
14565 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14566 m_hAllocator,
14567 pool,
14568 &pool->m_BlockVector,
14569 m_CurrFrameIndex);
14570 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
14571 }
14572
14573 pBlockVectorDefragCtx->AddAll();
14574 }
14575 }
14576 }
14577
14578 void VmaDefragmentationContext_T::AddAllocations(
14579 uint32_t allocationCount,
14580 const VmaAllocation* pAllocations,
14581 VkBool32* pAllocationsChanged)
14582 {
14583 // Dispatch pAllocations among defragmentators. Create them when necessary.
14584 for (uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14585 {
14586 const VmaAllocation hAlloc = pAllocations[allocIndex];
14587 VMA_ASSERT(hAlloc);
14588 // DedicatedAlloc cannot be defragmented.
14589 if ((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
14590 // Lost allocation cannot be defragmented.
14591 (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
14592 {
14593 VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
14594
14595 const VmaPool hAllocPool = hAlloc->GetBlock()->GetParentPool();
14596 // This allocation belongs to custom pool.
14597 if (hAllocPool != VK_NULL_HANDLE)
14598 {
14599 // Pools with algorithm other than default are not defragmented.
14600 if (hAllocPool->m_BlockVector.GetAlgorithm() == 0)
14601 {
14602 for (size_t i = m_CustomPoolContexts.size(); i--; )
14603 {
14604 if (m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
14605 {
14606 pBlockVectorDefragCtx = m_CustomPoolContexts[i];
14607 break;
14608 }
14609 }
14610 if (!pBlockVectorDefragCtx)
14611 {
14612 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14613 m_hAllocator,
14614 hAllocPool,
14615 &hAllocPool->m_BlockVector,
14616 m_CurrFrameIndex);
14617 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
14618 }
14619 }
14620 }
14621 // This allocation belongs to default pool.
14622 else
14623 {
14624 const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
14625 pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
14626 if (!pBlockVectorDefragCtx)
14627 {
14628 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14629 m_hAllocator,
14630 VMA_NULL, // hCustomPool
14631 m_hAllocator->m_pBlockVectors[memTypeIndex],
14632 m_CurrFrameIndex);
14633 m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
14634 }
14635 }
14636
14637 if (pBlockVectorDefragCtx)
14638 {
14639 VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
14640 &pAllocationsChanged[allocIndex] : VMA_NULL;
14641 pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
14642 }
14643 }
14644 }
14645 }
14646
14647 VkResult VmaDefragmentationContext_T::Defragment(
14648 VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
14649 VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
14650 VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags)
14651 {
14652 if (pStats)
14653 {
14654 memset(pStats, 0, sizeof(VmaDefragmentationStats));
14655 }
14656
14657 if (flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
14658 {
14659 // For incremental defragmetnations, we just earmark how much we can move
14660 // The real meat is in the defragmentation steps
14661 m_MaxCpuBytesToMove = maxCpuBytesToMove;
14662 m_MaxCpuAllocationsToMove = maxCpuAllocationsToMove;
14663
14664 m_MaxGpuBytesToMove = maxGpuBytesToMove;
14665 m_MaxGpuAllocationsToMove = maxGpuAllocationsToMove;
14666
14667 if (m_MaxCpuBytesToMove == 0 && m_MaxCpuAllocationsToMove == 0 &&
14668 m_MaxGpuBytesToMove == 0 && m_MaxGpuAllocationsToMove == 0)
14669 return VK_SUCCESS;
14670
14671 return VK_NOT_READY;
14672 }
14673
14674 if (commandBuffer == VK_NULL_HANDLE)
14675 {
14676 maxGpuBytesToMove = 0;
14677 maxGpuAllocationsToMove = 0;
14678 }
14679
14680 VkResult res = VK_SUCCESS;
14681
14682 // Process default pools.
14683 for (uint32_t memTypeIndex = 0;
14684 memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
14685 ++memTypeIndex)
14686 {
14687 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14688 if (pBlockVectorCtx)
14689 {
14690 VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14691 pBlockVectorCtx->GetBlockVector()->Defragment(
14692 pBlockVectorCtx,
14693 pStats, flags,
14694 maxCpuBytesToMove, maxCpuAllocationsToMove,
14695 maxGpuBytesToMove, maxGpuAllocationsToMove,
14696 commandBuffer);
14697 if (pBlockVectorCtx->res != VK_SUCCESS)
14698 {
14699 res = pBlockVectorCtx->res;
14700 }
14701 }
14702 }
14703
14704 // Process custom pools.
14705 for (size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14706 customCtxIndex < customCtxCount && res >= VK_SUCCESS;
14707 ++customCtxIndex)
14708 {
14709 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14710 VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14711 pBlockVectorCtx->GetBlockVector()->Defragment(
14712 pBlockVectorCtx,
14713 pStats, flags,
14714 maxCpuBytesToMove, maxCpuAllocationsToMove,
14715 maxGpuBytesToMove, maxGpuAllocationsToMove,
14716 commandBuffer);
14717 if (pBlockVectorCtx->res != VK_SUCCESS)
14718 {
14719 res = pBlockVectorCtx->res;
14720 }
14721 }
14722
14723 return res;
14724 }
14725
14726 VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo)
14727 {
14728 VmaDefragmentationPassMoveInfo* pCurrentMove = pInfo->pMoves;
14729 uint32_t movesLeft = pInfo->moveCount;
14730
14731 // Process default pools.
14732 for (uint32_t memTypeIndex = 0;
14733 memTypeIndex < m_hAllocator->GetMemoryTypeCount();
14734 ++memTypeIndex)
14735 {
14736 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14737 if (pBlockVectorCtx)
14738 {
14739 VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14740
14741 if (!pBlockVectorCtx->hasDefragmentationPlan)
14742 {
14743 pBlockVectorCtx->GetBlockVector()->Defragment(
14744 pBlockVectorCtx,
14745 m_pStats, m_Flags,
14746 m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
14747 m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
14748 VK_NULL_HANDLE);
14749
14750 if (pBlockVectorCtx->res < VK_SUCCESS)
14751 continue;
14752
14753 pBlockVectorCtx->hasDefragmentationPlan = true;
14754 }
14755
14756 const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
14757 pBlockVectorCtx,
14758 pCurrentMove, movesLeft);
14759
14760 movesLeft -= processed;
14761 pCurrentMove += processed;
14762 }
14763 }
14764
14765 // Process custom pools.
14766 for (size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14767 customCtxIndex < customCtxCount;
14768 ++customCtxIndex)
14769 {
14770 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14771 VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14772
14773 if (!pBlockVectorCtx->hasDefragmentationPlan)
14774 {
14775 pBlockVectorCtx->GetBlockVector()->Defragment(
14776 pBlockVectorCtx,
14777 m_pStats, m_Flags,
14778 m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
14779 m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
14780 VK_NULL_HANDLE);
14781
14782 if (pBlockVectorCtx->res < VK_SUCCESS)
14783 continue;
14784
14785 pBlockVectorCtx->hasDefragmentationPlan = true;
14786 }
14787
14788 const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
14789 pBlockVectorCtx,
14790 pCurrentMove, movesLeft);
14791
14792 movesLeft -= processed;
14793 pCurrentMove += processed;
14794 }
14795
14796 pInfo->moveCount = pInfo->moveCount - movesLeft;
14797
14798 return VK_SUCCESS;
14799 }
14800 VkResult VmaDefragmentationContext_T::DefragmentPassEnd()
14801 {
14802 VkResult res = VK_SUCCESS;
14803
14804 // Process default pools.
14805 for (uint32_t memTypeIndex = 0;
14806 memTypeIndex < m_hAllocator->GetMemoryTypeCount();
14807 ++memTypeIndex)
14808 {
14809 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14810 if (pBlockVectorCtx)
14811 {
14812 VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14813
14814 if (!pBlockVectorCtx->hasDefragmentationPlan)
14815 {
14816 res = VK_NOT_READY;
14817 continue;
14818 }
14819
14820 pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
14821 pBlockVectorCtx, m_pStats);
14822
14823 if (pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
14824 res = VK_NOT_READY;
14825 }
14826 }
14827
14828 // Process custom pools.
14829 for (size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14830 customCtxIndex < customCtxCount;
14831 ++customCtxIndex)
14832 {
14833 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14834 VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14835
14836 if (!pBlockVectorCtx->hasDefragmentationPlan)
14837 {
14838 res = VK_NOT_READY;
14839 continue;
14840 }
14841
14842 pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
14843 pBlockVectorCtx, m_pStats);
14844
14845 if (pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
14846 res = VK_NOT_READY;
14847 }
14848
14849 return res;
14850 }
14851
14852 ////////////////////////////////////////////////////////////////////////////////
14853 // VmaRecorder
14854
14855 #if VMA_RECORDING_ENABLED
14856
14857 VmaRecorder::VmaRecorder() :
14858 m_UseMutex(true),
14859 m_Flags(0),
14860 m_File(VMA_NULL),
14861 m_RecordingStartTime(std::chrono::high_resolution_clock::now())
14862 {
14863 }
14864
14865 VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
14866 {
14867 m_UseMutex = useMutex;
14868 m_Flags = settings.flags;
14869
14870 #if defined(_WIN32)
14871 // Open file for writing.
14872 errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
14873
14874 if (err != 0)
14875 {
14876 return VK_ERROR_INITIALIZATION_FAILED;
14877 }
14878 #else
14879 // Open file for writing.
14880 m_File = fopen(settings.pFilePath, "wb");
14881
14882 if (m_File == 0)
14883 {
14884 return VK_ERROR_INITIALIZATION_FAILED;
14885 }
14886 #endif
14887
14888 // Write header.
14889 fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
14890 fprintf(m_File, "%s\n", "1,8");
14891
14892 return VK_SUCCESS;
14893 }
14894
14895 VmaRecorder::~VmaRecorder()
14896 {
14897 if (m_File != VMA_NULL)
14898 {
14899 fclose(m_File);
14900 }
14901 }
14902
14903 void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
14904 {
14905 CallParams callParams;
14906 GetBasicParams(callParams);
14907
14908 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14909 fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
14910 Flush();
14911 }
14912
14913 void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
14914 {
14915 CallParams callParams;
14916 GetBasicParams(callParams);
14917
14918 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14919 fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
14920 Flush();
14921 }
14922
14923 void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
14924 {
14925 CallParams callParams;
14926 GetBasicParams(callParams);
14927
14928 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14929 fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
14930 createInfo.memoryTypeIndex,
14931 createInfo.flags,
14932 createInfo.blockSize,
14933 (uint64_t)createInfo.minBlockCount,
14934 (uint64_t)createInfo.maxBlockCount,
14935 createInfo.frameInUseCount,
14936 pool);
14937 Flush();
14938 }
14939
14940 void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
14941 {
14942 CallParams callParams;
14943 GetBasicParams(callParams);
14944
14945 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14946 fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
14947 pool);
14948 Flush();
14949 }
14950
14951 void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
14952 const VkMemoryRequirements& vkMemReq,
14953 const VmaAllocationCreateInfo& createInfo,
14954 VmaAllocation allocation)
14955 {
14956 CallParams callParams;
14957 GetBasicParams(callParams);
14958
14959 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14960 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
14961 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
14962 vkMemReq.size,
14963 vkMemReq.alignment,
14964 vkMemReq.memoryTypeBits,
14965 createInfo.flags,
14966 createInfo.usage,
14967 createInfo.requiredFlags,
14968 createInfo.preferredFlags,
14969 createInfo.memoryTypeBits,
14970 createInfo.pool,
14971 allocation,
14972 userDataStr.GetString());
14973 Flush();
14974 }
14975
14976 void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
14977 const VkMemoryRequirements& vkMemReq,
14978 const VmaAllocationCreateInfo& createInfo,
14979 uint64_t allocationCount,
14980 const VmaAllocation* pAllocations)
14981 {
14982 CallParams callParams;
14983 GetBasicParams(callParams);
14984
14985 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14986 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
14987 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
14988 vkMemReq.size,
14989 vkMemReq.alignment,
14990 vkMemReq.memoryTypeBits,
14991 createInfo.flags,
14992 createInfo.usage,
14993 createInfo.requiredFlags,
14994 createInfo.preferredFlags,
14995 createInfo.memoryTypeBits,
14996 createInfo.pool);
14997 PrintPointerList(allocationCount, pAllocations);
14998 fprintf(m_File, ",%s\n", userDataStr.GetString());
14999 Flush();
15000 }
15001
15002 void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
15003 const VkMemoryRequirements& vkMemReq,
15004 bool requiresDedicatedAllocation,
15005 bool prefersDedicatedAllocation,
15006 const VmaAllocationCreateInfo& createInfo,
15007 VmaAllocation allocation)
15008 {
15009 CallParams callParams;
15010 GetBasicParams(callParams);
15011
15012 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15013 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15014 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,
15015 vkMemReq.size,
15016 vkMemReq.alignment,
15017 vkMemReq.memoryTypeBits,
15018 requiresDedicatedAllocation ? 1 : 0,
15019 prefersDedicatedAllocation ? 1 : 0,
15020 createInfo.flags,
15021 createInfo.usage,
15022 createInfo.requiredFlags,
15023 createInfo.preferredFlags,
15024 createInfo.memoryTypeBits,
15025 createInfo.pool,
15026 allocation,
15027 userDataStr.GetString());
15028 Flush();
15029 }
15030
15031 void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
15032 const VkMemoryRequirements& vkMemReq,
15033 bool requiresDedicatedAllocation,
15034 bool prefersDedicatedAllocation,
15035 const VmaAllocationCreateInfo& createInfo,
15036 VmaAllocation allocation)
15037 {
15038 CallParams callParams;
15039 GetBasicParams(callParams);
15040
15041 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15042 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15043 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,
15044 vkMemReq.size,
15045 vkMemReq.alignment,
15046 vkMemReq.memoryTypeBits,
15047 requiresDedicatedAllocation ? 1 : 0,
15048 prefersDedicatedAllocation ? 1 : 0,
15049 createInfo.flags,
15050 createInfo.usage,
15051 createInfo.requiredFlags,
15052 createInfo.preferredFlags,
15053 createInfo.memoryTypeBits,
15054 createInfo.pool,
15055 allocation,
15056 userDataStr.GetString());
15057 Flush();
15058 }
15059
15060 void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
15061 VmaAllocation allocation)
15062 {
15063 CallParams callParams;
15064 GetBasicParams(callParams);
15065
15066 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15067 fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15068 allocation);
15069 Flush();
15070 }
15071
15072 void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
15073 uint64_t allocationCount,
15074 const VmaAllocation* pAllocations)
15075 {
15076 CallParams callParams;
15077 GetBasicParams(callParams);
15078
15079 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15080 fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
15081 PrintPointerList(allocationCount, pAllocations);
15082 fprintf(m_File, "\n");
15083 Flush();
15084 }
15085
15086 void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
15087 VmaAllocation allocation,
15088 const void* pUserData)
15089 {
15090 CallParams callParams;
15091 GetBasicParams(callParams);
15092
15093 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15094 UserDataString userDataStr(
15095 allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
15096 pUserData);
15097 fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15098 allocation,
15099 userDataStr.GetString());
15100 Flush();
15101 }
15102
15103 void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
15104 VmaAllocation allocation)
15105 {
15106 CallParams callParams;
15107 GetBasicParams(callParams);
15108
15109 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15110 fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15111 allocation);
15112 Flush();
15113 }
15114
15115 void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
15116 VmaAllocation allocation)
15117 {
15118 CallParams callParams;
15119 GetBasicParams(callParams);
15120
15121 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15122 fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15123 allocation);
15124 Flush();
15125 }
15126
15127 void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
15128 VmaAllocation allocation)
15129 {
15130 CallParams callParams;
15131 GetBasicParams(callParams);
15132
15133 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15134 fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15135 allocation);
15136 Flush();
15137 }
15138
15139 void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
15140 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15141 {
15142 CallParams callParams;
15143 GetBasicParams(callParams);
15144
15145 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15146 fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15147 allocation,
15148 offset,
15149 size);
15150 Flush();
15151 }
15152
15153 void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
15154 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15155 {
15156 CallParams callParams;
15157 GetBasicParams(callParams);
15158
15159 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15160 fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15161 allocation,
15162 offset,
15163 size);
15164 Flush();
15165 }
15166
15167 void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
15168 const VkBufferCreateInfo& bufCreateInfo,
15169 const VmaAllocationCreateInfo& allocCreateInfo,
15170 VmaAllocation allocation)
15171 {
15172 CallParams callParams;
15173 GetBasicParams(callParams);
15174
15175 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15176 UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15177 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,
15178 bufCreateInfo.flags,
15179 bufCreateInfo.size,
15180 bufCreateInfo.usage,
15181 bufCreateInfo.sharingMode,
15182 allocCreateInfo.flags,
15183 allocCreateInfo.usage,
15184 allocCreateInfo.requiredFlags,
15185 allocCreateInfo.preferredFlags,
15186 allocCreateInfo.memoryTypeBits,
15187 allocCreateInfo.pool,
15188 allocation,
15189 userDataStr.GetString());
15190 Flush();
15191 }
15192
15193 void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
15194 const VkImageCreateInfo& imageCreateInfo,
15195 const VmaAllocationCreateInfo& allocCreateInfo,
15196 VmaAllocation allocation)
15197 {
15198 CallParams callParams;
15199 GetBasicParams(callParams);
15200
15201 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15202 UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15203 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,
15204 imageCreateInfo.flags,
15205 imageCreateInfo.imageType,
15206 imageCreateInfo.format,
15207 imageCreateInfo.extent.width,
15208 imageCreateInfo.extent.height,
15209 imageCreateInfo.extent.depth,
15210 imageCreateInfo.mipLevels,
15211 imageCreateInfo.arrayLayers,
15212 imageCreateInfo.samples,
15213 imageCreateInfo.tiling,
15214 imageCreateInfo.usage,
15215 imageCreateInfo.sharingMode,
15216 imageCreateInfo.initialLayout,
15217 allocCreateInfo.flags,
15218 allocCreateInfo.usage,
15219 allocCreateInfo.requiredFlags,
15220 allocCreateInfo.preferredFlags,
15221 allocCreateInfo.memoryTypeBits,
15222 allocCreateInfo.pool,
15223 allocation,
15224 userDataStr.GetString());
15225 Flush();
15226 }
15227
15228 void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
15229 VmaAllocation allocation)
15230 {
15231 CallParams callParams;
15232 GetBasicParams(callParams);
15233
15234 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15235 fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
15236 allocation);
15237 Flush();
15238 }
15239
15240 void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
15241 VmaAllocation allocation)
15242 {
15243 CallParams callParams;
15244 GetBasicParams(callParams);
15245
15246 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15247 fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
15248 allocation);
15249 Flush();
15250 }
15251
15252 void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
15253 VmaAllocation allocation)
15254 {
15255 CallParams callParams;
15256 GetBasicParams(callParams);
15257
15258 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15259 fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15260 allocation);
15261 Flush();
15262 }
15263
15264 void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
15265 VmaAllocation allocation)
15266 {
15267 CallParams callParams;
15268 GetBasicParams(callParams);
15269
15270 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15271 fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
15272 allocation);
15273 Flush();
15274 }
15275
15276 void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
15277 VmaPool pool)
15278 {
15279 CallParams callParams;
15280 GetBasicParams(callParams);
15281
15282 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15283 fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
15284 pool);
15285 Flush();
15286 }
15287
15288 void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
15289 const VmaDefragmentationInfo2& info,
15290 VmaDefragmentationContext ctx)
15291 {
15292 CallParams callParams;
15293 GetBasicParams(callParams);
15294
15295 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15296 fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
15297 info.flags);
15298 PrintPointerList(info.allocationCount, info.pAllocations);
15299 fprintf(m_File, ",");
15300 PrintPointerList(info.poolCount, info.pPools);
15301 fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
15302 info.maxCpuBytesToMove,
15303 info.maxCpuAllocationsToMove,
15304 info.maxGpuBytesToMove,
15305 info.maxGpuAllocationsToMove,
15306 info.commandBuffer,
15307 ctx);
15308 Flush();
15309 }
15310
15311 void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
15312 VmaDefragmentationContext ctx)
15313 {
15314 CallParams callParams;
15315 GetBasicParams(callParams);
15316
15317 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15318 fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
15319 ctx);
15320 Flush();
15321 }
15322
15323 void VmaRecorder::RecordSetPoolName(uint32_t frameIndex,
15324 VmaPool pool,
15325 const char* name)
15326 {
15327 CallParams callParams;
15328 GetBasicParams(callParams);
15329
15330 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15331 fprintf(m_File, "%u,%.3f,%u,vmaSetPoolName,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15332 pool, name != VMA_NULL ? name : "");
15333 Flush();
15334 }
15335
15336 VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
15337 {
15338 if (pUserData != VMA_NULL)
15339 {
15340 if ((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
15341 {
15342 m_Str = (const char*)pUserData;
15343 }
15344 else
15345 {
15346 // If VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is not specified, convert the string's memory address to a string and store it.
15347 snprintf(m_PtrStr, 17, "%p", pUserData);
15348 m_Str = m_PtrStr;
15349 }
15350 }
15351 else
15352 {
15353 m_Str = "";
15354 }
15355 }
15356
15357 void VmaRecorder::WriteConfiguration(
15358 const VkPhysicalDeviceProperties& devProps,
15359 const VkPhysicalDeviceMemoryProperties& memProps,
15360 uint32_t vulkanApiVersion,
15361 bool dedicatedAllocationExtensionEnabled,
15362 bool bindMemory2ExtensionEnabled,
15363 bool memoryBudgetExtensionEnabled,
15364 bool deviceCoherentMemoryExtensionEnabled)
15365 {
15366 fprintf(m_File, "Config,Begin\n");
15367
15368 fprintf(m_File, "VulkanApiVersion,%u,%u\n", VK_VERSION_MAJOR(vulkanApiVersion), VK_VERSION_MINOR(vulkanApiVersion));
15369
15370 fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
15371 fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
15372 fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
15373 fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
15374 fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
15375 fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
15376
15377 fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
15378 fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
15379 fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
15380
15381 fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
15382 for (uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
15383 {
15384 fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
15385 fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
15386 }
15387 fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
15388 for (uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
15389 {
15390 fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
15391 fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
15392 }
15393
15394 fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
15395 fprintf(m_File, "Extension,VK_KHR_bind_memory2,%u\n", bindMemory2ExtensionEnabled ? 1 : 0);
15396 fprintf(m_File, "Extension,VK_EXT_memory_budget,%u\n", memoryBudgetExtensionEnabled ? 1 : 0);
15397 fprintf(m_File, "Extension,VK_AMD_device_coherent_memory,%u\n", deviceCoherentMemoryExtensionEnabled ? 1 : 0);
15398
15399 fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
15400 fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);
15401 fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
15402 fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
15403 fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
15404 fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
15405 fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
15406 fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
15407 fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15408
15409 fprintf(m_File, "Config,End\n");
15410 }
15411
15412 void VmaRecorder::GetBasicParams(CallParams& outParams)
15413 {
15414 #if defined(_WIN32)
15415 outParams.threadId = GetCurrentThreadId();
15416 #else
15417 // Use C++11 features to get thread id and convert it to uint32_t.
15418 // There is room for optimization since sstream is quite slow.
15419 // Is there a better way to convert std::this_thread::get_id() to uint32_t?
15420 std::thread::id thread_id = std::this_thread::get_id();
15421 stringstream thread_id_to_string_converter;
15422 thread_id_to_string_converter << thread_id;
15423 string thread_id_as_string = thread_id_to_string_converter.str();
15424 outParams.threadId = static_cast<uint32_t>(std::stoi(thread_id_as_string.c_str()));
15425 #endif
15426
15427 auto current_time = std::chrono::high_resolution_clock::now();
15428
15429 outParams.time = std::chrono::duration<double, std::chrono::seconds::period>(current_time - m_RecordingStartTime).count();
15430 }
15431
15432 void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
15433 {
15434 if (count)
15435 {
15436 fprintf(m_File, "%p", pItems[0]);
15437 for (uint64_t i = 1; i < count; ++i)
15438 {
15439 fprintf(m_File, " %p", pItems[i]);
15440 }
15441 }
15442 }
15443
15444 void VmaRecorder::Flush()
15445 {
15446 if ((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
15447 {
15448 fflush(m_File);
15449 }
15450 }
15451
15452 #endif // #if VMA_RECORDING_ENABLED
15453
15454 ////////////////////////////////////////////////////////////////////////////////
15455 // VmaAllocationObjectAllocator
15456
15457 VmaAllocationObjectAllocator::VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks) :
15458 m_Allocator(pAllocationCallbacks, 1024)
15459 {
15460 }
15461
15462 template<typename... Types> VmaAllocation VmaAllocationObjectAllocator::Allocate(Types... args)
15463 {
15464 VmaMutexLock mutexLock(m_Mutex);
15465 return m_Allocator.Alloc<Types...>(std::forward<Types>(args)...);
15466 }
15467
15468 void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc)
15469 {
15470 VmaMutexLock mutexLock(m_Mutex);
15471 m_Allocator.Free(hAlloc);
15472 }
15473
15474 ////////////////////////////////////////////////////////////////////////////////
15475 // VmaAllocator_T
15476
15477 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
15478 m_UseMutex((pCreateInfo->flags& VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
15479 m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0),
15480 m_UseKhrDedicatedAllocation((pCreateInfo->flags& VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
15481 m_UseKhrBindMemory2((pCreateInfo->flags& VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0),
15482 m_UseExtMemoryBudget((pCreateInfo->flags& VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0),
15483 m_UseAmdDeviceCoherentMemory((pCreateInfo->flags& VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0),
15484 m_UseKhrBufferDeviceAddress((pCreateInfo->flags& VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0),
15485 m_hDevice(pCreateInfo->device),
15486 m_hInstance(pCreateInfo->instance),
15487 m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
15488 m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
15489 *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
15490 m_AllocationObjectAllocator(&m_AllocationCallbacks),
15491 m_HeapSizeLimitMask(0),
15492 m_PreferredLargeHeapBlockSize(0),
15493 m_PhysicalDevice(pCreateInfo->physicalDevice),
15494 m_CurrentFrameIndex(0),
15495 m_GpuDefragmentationMemoryTypeBits(UINT32_MAX),
15496 m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks())),
15497 m_NextPoolId(0),
15498 m_GlobalMemoryTypeBits(UINT32_MAX)
15499 #if VMA_RECORDING_ENABLED
15500 , m_pRecorder(VMA_NULL)
15501 #endif
15502 {
15503 if (m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15504 {
15505 m_UseKhrDedicatedAllocation = false;
15506 m_UseKhrBindMemory2 = false;
15507 }
15508
15509 if (VMA_DEBUG_DETECT_CORRUPTION)
15510 {
15511 // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
15512 VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
15513 }
15514
15515 VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance);
15516
15517 if (m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
15518 {
15519 #if !(VMA_DEDICATED_ALLOCATION)
15520 if ((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
15521 {
15522 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
15523 }
15524 #endif
15525 #if !(VMA_BIND_MEMORY2)
15526 if ((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0)
15527 {
15528 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros.");
15529 }
15530 #endif
15531 }
15532 #if !(VMA_MEMORY_BUDGET)
15533 if ((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0)
15534 {
15535 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros.");
15536 }
15537 #endif
15538 #if !(VMA_BUFFER_DEVICE_ADDRESS)
15539 if (m_UseKhrBufferDeviceAddress)
15540 {
15541 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.");
15542 }
15543 #endif
15544 #if VMA_VULKAN_VERSION < 1002000
15545 if (m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0))
15546 {
15547 VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros.");
15548 }
15549 #endif
15550 #if VMA_VULKAN_VERSION < 1001000
15551 if (m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15552 {
15553 VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros.");
15554 }
15555 #endif
15556
15557 memset(&m_DeviceMemoryCallbacks, 0, sizeof(m_DeviceMemoryCallbacks));
15558 memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
15559 memset(&m_MemProps, 0, sizeof(m_MemProps));
15560
15561 memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
15562 memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations));
15563 memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions));
15564
15565 if (pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
15566 {
15567 m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData;
15568 m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
15569 m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
15570 }
15571
15572 ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
15573
15574 (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
15575 (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
15576
15577 VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT));
15578 VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
15579 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
15580 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
15581
15582 m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
15583 pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15584
15585 m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits();
15586
15587 if (pCreateInfo->pHeapSizeLimit != VMA_NULL)
15588 {
15589 for (uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
15590 {
15591 const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
15592 if (limit != VK_WHOLE_SIZE)
15593 {
15594 m_HeapSizeLimitMask |= 1u << heapIndex;
15595 if (limit < m_MemProps.memoryHeaps[heapIndex].size)
15596 {
15597 m_MemProps.memoryHeaps[heapIndex].size = limit;
15598 }
15599 }
15600 }
15601 }
15602
15603 for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15604 {
15605 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
15606
15607 m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
15608 this,
15609 VK_NULL_HANDLE, // hParentPool
15610 memTypeIndex,
15611 preferredBlockSize,
15612 0,
15613 SIZE_MAX,
15614 GetBufferImageGranularity(),
15615 pCreateInfo->frameInUseCount,
15616 false, // explicitBlockSize
15617 false); // linearAlgorithm
15618 // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
15619 // becase minBlockCount is 0.
15620 m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
15621
15622 }
15623 }
15624
15625 VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
15626 {
15627 VkResult res = VK_SUCCESS;
15628
15629 if (pCreateInfo->pRecordSettings != VMA_NULL &&
15630 !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
15631 {
15632 #if VMA_RECORDING_ENABLED
15633 m_pRecorder = vma_new(this, VmaRecorder)();
15634 res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
15635 if (res != VK_SUCCESS)
15636 {
15637 return res;
15638 }
15639 m_pRecorder->WriteConfiguration(
15640 m_PhysicalDeviceProperties,
15641 m_MemProps,
15642 m_VulkanApiVersion,
15643 m_UseKhrDedicatedAllocation,
15644 m_UseKhrBindMemory2,
15645 m_UseExtMemoryBudget,
15646 m_UseAmdDeviceCoherentMemory);
15647 m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
15648 #else
15649 VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
15650 return VK_ERROR_FEATURE_NOT_PRESENT;
15651 #endif
15652 }
15653
15654 #if VMA_MEMORY_BUDGET
15655 if (m_UseExtMemoryBudget)
15656 {
15657 UpdateVulkanBudget();
15658 }
15659 #endif // #if VMA_MEMORY_BUDGET
15660
15661 return res;
15662 }
15663
15664 VmaAllocator_T::~VmaAllocator_T()
15665 {
15666 #if VMA_RECORDING_ENABLED
15667 if (m_pRecorder != VMA_NULL)
15668 {
15669 m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
15670 vma_delete(this, m_pRecorder);
15671 }
15672 #endif
15673
15674 VMA_ASSERT(m_Pools.empty());
15675
15676 for (size_t i = GetMemoryTypeCount(); i--; )
15677 {
15678 if (m_pDedicatedAllocations[i] != VMA_NULL && !m_pDedicatedAllocations[i]->empty())
15679 {
15680 VMA_ASSERT(0 && "Unfreed dedicated allocations found.");
15681 }
15682
15683 vma_delete(this, m_pDedicatedAllocations[i]);
15684 vma_delete(this, m_pBlockVectors[i]);
15685 }
15686 }
15687
15688 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
15689 {
15690 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15691 ImportVulkanFunctions_Static();
15692 #endif
15693
15694 if (pVulkanFunctions != VMA_NULL)
15695 {
15696 ImportVulkanFunctions_Custom(pVulkanFunctions);
15697 }
15698
15699 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
15700 ImportVulkanFunctions_Dynamic();
15701 #endif
15702
15703 ValidateVulkanFunctions();
15704 }
15705
15706 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15707
15708 void VmaAllocator_T::ImportVulkanFunctions_Static()
15709 {
15710 // Vulkan 1.0
15711 m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties;
15712 m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties;
15713 m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
15714 m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory;
15715 m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory;
15716 m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory;
15717 m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges;
15718 m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges;
15719 m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory;
15720 m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory;
15721 m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements;
15722 m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements;
15723 m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer;
15724 m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer;
15725 m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage;
15726 m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage;
15727 m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer;
15728
15729 // Vulkan 1.1
15730 #if VMA_VULKAN_VERSION >= 1001000
15731 if (m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15732 {
15733 m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2;
15734 m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2;
15735 m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2;
15736 m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2;
15737 m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2;
15738 }
15739 #endif
15740 }
15741
15742 #endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15743
15744 void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions)
15745 {
15746 VMA_ASSERT(pVulkanFunctions != VMA_NULL);
15747
15748 #define VMA_COPY_IF_NOT_NULL(funcName) \
15749 if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
15750
15751 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
15752 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
15753 VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
15754 VMA_COPY_IF_NOT_NULL(vkFreeMemory);
15755 VMA_COPY_IF_NOT_NULL(vkMapMemory);
15756 VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
15757 VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
15758 VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
15759 VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
15760 VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
15761 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
15762 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
15763 VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
15764 VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
15765 VMA_COPY_IF_NOT_NULL(vkCreateImage);
15766 VMA_COPY_IF_NOT_NULL(vkDestroyImage);
15767 VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
15768
15769 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15770 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
15771 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
15772 #endif
15773
15774 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
15775 VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR);
15776 VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR);
15777 #endif
15778
15779 #if VMA_MEMORY_BUDGET
15780 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR);
15781 #endif
15782
15783 #undef VMA_COPY_IF_NOT_NULL
15784 }
15785
15786 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
15787
15788 void VmaAllocator_T::ImportVulkanFunctions_Dynamic()
15789 {
15790 #define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \
15791 if(m_VulkanFunctions.memberName == VMA_NULL) \
15792 m_VulkanFunctions.memberName = \
15793 (functionPointerType)vkGetInstanceProcAddr(m_hInstance, functionNameString);
15794 #define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \
15795 if(m_VulkanFunctions.memberName == VMA_NULL) \
15796 m_VulkanFunctions.memberName = \
15797 (functionPointerType)vkGetDeviceProcAddr(m_hDevice, functionNameString);
15798
15799 VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");
15800 VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties");
15801 VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory");
15802 VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory");
15803 VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory");
15804 VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory");
15805 VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges");
15806 VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges");
15807 VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory");
15808 VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory");
15809 VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements");
15810 VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements");
15811 VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer");
15812 VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer");
15813 VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage");
15814 VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage");
15815 VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer");
15816
15817 #if VMA_VULKAN_VERSION >= 1001000
15818 if (m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15819 {
15820 VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2, "vkGetBufferMemoryRequirements2");
15821 VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2, "vkGetImageMemoryRequirements2");
15822 VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2, "vkBindBufferMemory2");
15823 VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2, "vkBindImageMemory2");
15824 VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2, "vkGetPhysicalDeviceMemoryProperties2");
15825 }
15826 #endif
15827
15828 #if VMA_DEDICATED_ALLOCATION
15829 if (m_UseKhrDedicatedAllocation)
15830 {
15831 VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR");
15832 VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR");
15833 }
15834 #endif
15835
15836 #if VMA_BIND_MEMORY2
15837 if (m_UseKhrBindMemory2)
15838 {
15839 VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR");
15840 VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR");
15841 }
15842 #endif // #if VMA_BIND_MEMORY2
15843
15844 #if VMA_MEMORY_BUDGET
15845 if (m_UseExtMemoryBudget)
15846 {
15847 VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");
15848 }
15849 #endif // #if VMA_MEMORY_BUDGET
15850
15851 #undef VMA_FETCH_DEVICE_FUNC
15852 #undef VMA_FETCH_INSTANCE_FUNC
15853 }
15854
15855 #endif // #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
15856
15857 void VmaAllocator_T::ValidateVulkanFunctions()
15858 {
15859 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
15860 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
15861 VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
15862 VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
15863 VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
15864 VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
15865 VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
15866 VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
15867 VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
15868 VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
15869 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
15870 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
15871 VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
15872 VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
15873 VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
15874 VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
15875 VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
15876
15877 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15878 if (m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation)
15879 {
15880 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
15881 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
15882 }
15883 #endif
15884
15885 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
15886 if (m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2)
15887 {
15888 VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL);
15889 VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL);
15890 }
15891 #endif
15892
15893 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
15894 if (m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15895 {
15896 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);
15897 }
15898 #endif
15899 }
15900
15901 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
15902 {
15903 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
15904 const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
15905 const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
15906 return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32);
15907 }
15908
15909 VkResult VmaAllocator_T::AllocateMemoryOfType(
15910 VkDeviceSize size,
15911 VkDeviceSize alignment,
15912 bool dedicatedAllocation,
15913 VkBuffer dedicatedBuffer,
15914 VkBufferUsageFlags dedicatedBufferUsage,
15915 VkImage dedicatedImage,
15916 const VmaAllocationCreateInfo& createInfo,
15917 uint32_t memTypeIndex,
15918 VmaSuballocationType suballocType,
15919 size_t allocationCount,
15920 VmaAllocation* pAllocations)
15921 {
15922 VMA_ASSERT(pAllocations != VMA_NULL);
15923 VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size);
15924
15925 VmaAllocationCreateInfo finalCreateInfo = createInfo;
15926
15927 // If memory type is not HOST_VISIBLE, disable MAPPED.
15928 if ((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
15929 (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15930 {
15931 finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
15932 }
15933 // If memory is lazily allocated, it should be always dedicated.
15934 if (finalCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED)
15935 {
15936 finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
15937 }
15938
15939 VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
15940 VMA_ASSERT(blockVector);
15941
15942 const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
15943 bool preferDedicatedMemory =
15944 VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
15945 dedicatedAllocation ||
15946 // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
15947 size > preferredBlockSize / 2;
15948
15949 if (preferDedicatedMemory &&
15950 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
15951 finalCreateInfo.pool == VK_NULL_HANDLE)
15952 {
15953 finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
15954 }
15955
15956 if ((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
15957 {
15958 if ((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
15959 {
15960 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15961 }
15962 else
15963 {
15964 return AllocateDedicatedMemory(
15965 size,
15966 suballocType,
15967 memTypeIndex,
15968 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
15969 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
15970 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
15971 finalCreateInfo.pUserData,
15972 dedicatedBuffer,
15973 dedicatedBufferUsage,
15974 dedicatedImage,
15975 allocationCount,
15976 pAllocations);
15977 }
15978 }
15979 else
15980 {
15981 VkResult res = blockVector->Allocate(
15982 m_CurrentFrameIndex.load(),
15983 size,
15984 alignment,
15985 finalCreateInfo,
15986 suballocType,
15987 allocationCount,
15988 pAllocations);
15989 if (res == VK_SUCCESS)
15990 {
15991 return res;
15992 }
15993
15994 // 5. Try dedicated memory.
15995 if ((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
15996 {
15997 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15998 }
15999 else
16000 {
16001 res = AllocateDedicatedMemory(
16002 size,
16003 suballocType,
16004 memTypeIndex,
16005 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
16006 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
16007 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
16008 finalCreateInfo.pUserData,
16009 dedicatedBuffer,
16010 dedicatedBufferUsage,
16011 dedicatedImage,
16012 allocationCount,
16013 pAllocations);
16014 if (res == VK_SUCCESS)
16015 {
16016 // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
16017 VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
16018 return VK_SUCCESS;
16019 }
16020 else
16021 {
16022 // Everything failed: Return error code.
16023 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
16024 return res;
16025 }
16026 }
16027 }
16028 }
16029
16030 VkResult VmaAllocator_T::AllocateDedicatedMemory(
16031 VkDeviceSize size,
16032 VmaSuballocationType suballocType,
16033 uint32_t memTypeIndex,
16034 bool withinBudget,
16035 bool map,
16036 bool isUserDataString,
16037 void* pUserData,
16038 VkBuffer dedicatedBuffer,
16039 VkBufferUsageFlags dedicatedBufferUsage,
16040 VkImage dedicatedImage,
16041 size_t allocationCount,
16042 VmaAllocation* pAllocations)
16043 {
16044 VMA_ASSERT(allocationCount > 0 && pAllocations);
16045
16046 if (withinBudget)
16047 {
16048 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16049 VmaBudget heapBudget = {};
16050 GetBudget(&heapBudget, heapIndex, 1);
16051 if (heapBudget.usage + size * allocationCount > heapBudget.budget)
16052 {
16053 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16054 }
16055 }
16056
16057 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
16058 allocInfo.memoryTypeIndex = memTypeIndex;
16059 allocInfo.allocationSize = size;
16060
16061 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16062 VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
16063 if (m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16064 {
16065 if (dedicatedBuffer != VK_NULL_HANDLE)
16066 {
16067 VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
16068 dedicatedAllocInfo.buffer = dedicatedBuffer;
16069 VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16070 }
16071 else if (dedicatedImage != VK_NULL_HANDLE)
16072 {
16073 dedicatedAllocInfo.image = dedicatedImage;
16074 VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16075 }
16076 }
16077 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16078
16079 #if VMA_BUFFER_DEVICE_ADDRESS
16080 VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
16081 if (m_UseKhrBufferDeviceAddress)
16082 {
16083 bool canContainBufferWithDeviceAddress = true;
16084 if (dedicatedBuffer != VK_NULL_HANDLE)
16085 {
16086 canContainBufferWithDeviceAddress = dedicatedBufferUsage == UINT32_MAX || // Usage flags unknown
16087 (dedicatedBufferUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) != 0;
16088 }
16089 else if (dedicatedImage != VK_NULL_HANDLE)
16090 {
16091 canContainBufferWithDeviceAddress = false;
16092 }
16093 if (canContainBufferWithDeviceAddress)
16094 {
16095 allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
16096 VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
16097 }
16098 }
16099 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
16100
16101 size_t allocIndex;
16102 VkResult res = VK_SUCCESS;
16103 for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16104 {
16105 res = AllocateDedicatedMemoryPage(
16106 size,
16107 suballocType,
16108 memTypeIndex,
16109 allocInfo,
16110 map,
16111 isUserDataString,
16112 pUserData,
16113 pAllocations + allocIndex);
16114 if (res != VK_SUCCESS)
16115 {
16116 break;
16117 }
16118 }
16119
16120 if (res == VK_SUCCESS)
16121 {
16122 // Register them in m_pDedicatedAllocations.
16123 {
16124 VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16125 AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
16126 VMA_ASSERT(pDedicatedAllocations);
16127 for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16128 {
16129 VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, pAllocations[allocIndex]);
16130 }
16131 }
16132
16133 VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
16134 }
16135 else
16136 {
16137 // Free all already created allocations.
16138 while (allocIndex--)
16139 {
16140 VmaAllocation currAlloc = pAllocations[allocIndex];
16141 VkDeviceMemory hMemory = currAlloc->GetMemory();
16142
16143 /*
16144 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
16145 before vkFreeMemory.
16146
16147 if(currAlloc->GetMappedData() != VMA_NULL)
16148 {
16149 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
16150 }
16151 */
16152
16153 FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
16154 m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize());
16155 currAlloc->SetUserData(this, VMA_NULL);
16156 m_AllocationObjectAllocator.Free(currAlloc);
16157 }
16158
16159 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16160 }
16161
16162 return res;
16163 }
16164
16165 VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
16166 VkDeviceSize size,
16167 VmaSuballocationType suballocType,
16168 uint32_t memTypeIndex,
16169 const VkMemoryAllocateInfo& allocInfo,
16170 bool map,
16171 bool isUserDataString,
16172 void* pUserData,
16173 VmaAllocation* pAllocation)
16174 {
16175 VkDeviceMemory hMemory = VK_NULL_HANDLE;
16176 VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
16177 if (res < 0)
16178 {
16179 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
16180 return res;
16181 }
16182
16183 void* pMappedData = VMA_NULL;
16184 if (map)
16185 {
16186 res = (*m_VulkanFunctions.vkMapMemory)(
16187 m_hDevice,
16188 hMemory,
16189 0,
16190 VK_WHOLE_SIZE,
16191 0,
16192 &pMappedData);
16193 if (res < 0)
16194 {
16195 VMA_DEBUG_LOG(" vkMapMemory FAILED");
16196 FreeVulkanMemory(memTypeIndex, size, hMemory);
16197 return res;
16198 }
16199 }
16200
16201 *pAllocation = m_AllocationObjectAllocator.Allocate(m_CurrentFrameIndex.load(), isUserDataString);
16202 (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
16203 (*pAllocation)->SetUserData(this, pUserData);
16204 m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size);
16205 if (VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16206 {
16207 FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
16208 }
16209
16210 return VK_SUCCESS;
16211 }
16212
16213 void VmaAllocator_T::GetBufferMemoryRequirements(
16214 VkBuffer hBuffer,
16215 VkMemoryRequirements& memReq,
16216 bool& requiresDedicatedAllocation,
16217 bool& prefersDedicatedAllocation) const
16218 {
16219 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16220 if (m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16221 {
16222 VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
16223 memReqInfo.buffer = hBuffer;
16224
16225 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16226
16227 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16228 VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16229
16230 (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16231
16232 memReq = memReq2.memoryRequirements;
16233 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16234 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
16235 }
16236 else
16237 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16238 {
16239 (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
16240 requiresDedicatedAllocation = false;
16241 prefersDedicatedAllocation = false;
16242 }
16243 }
16244
16245 void VmaAllocator_T::GetImageMemoryRequirements(
16246 VkImage hImage,
16247 VkMemoryRequirements& memReq,
16248 bool& requiresDedicatedAllocation,
16249 bool& prefersDedicatedAllocation) const
16250 {
16251 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16252 if (m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16253 {
16254 VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
16255 memReqInfo.image = hImage;
16256
16257 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16258
16259 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16260 VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16261
16262 (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16263
16264 memReq = memReq2.memoryRequirements;
16265 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16266 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
16267 }
16268 else
16269 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16270 {
16271 (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
16272 requiresDedicatedAllocation = false;
16273 prefersDedicatedAllocation = false;
16274 }
16275 }
16276
16277 VkResult VmaAllocator_T::AllocateMemory(
16278 const VkMemoryRequirements& vkMemReq,
16279 bool requiresDedicatedAllocation,
16280 bool prefersDedicatedAllocation,
16281 VkBuffer dedicatedBuffer,
16282 VkBufferUsageFlags dedicatedBufferUsage,
16283 VkImage dedicatedImage,
16284 const VmaAllocationCreateInfo& createInfo,
16285 VmaSuballocationType suballocType,
16286 size_t allocationCount,
16287 VmaAllocation* pAllocations)
16288 {
16289 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16290
16291 VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
16292
16293 if (vkMemReq.size == 0)
16294 {
16295 return VK_ERROR_VALIDATION_FAILED_EXT;
16296 }
16297 if ((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
16298 (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16299 {
16300 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
16301 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16302 }
16303 if ((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16304 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
16305 {
16306 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
16307 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16308 }
16309 if (requiresDedicatedAllocation)
16310 {
16311 if ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16312 {
16313 VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
16314 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16315 }
16316 if (createInfo.pool != VK_NULL_HANDLE)
16317 {
16318 VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
16319 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16320 }
16321 }
16322 if ((createInfo.pool != VK_NULL_HANDLE) &&
16323 ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
16324 {
16325 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
16326 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16327 }
16328
16329 if (createInfo.pool != VK_NULL_HANDLE)
16330 {
16331 const VkDeviceSize alignmentForPool = VMA_MAX(
16332 vkMemReq.alignment,
16333 GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex()));
16334
16335 VmaAllocationCreateInfo createInfoForPool = createInfo;
16336 // If memory type is not HOST_VISIBLE, disable MAPPED.
16337 if ((createInfoForPool.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16338 (m_MemProps.memoryTypes[createInfo.pool->m_BlockVector.GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
16339 {
16340 createInfoForPool.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
16341 }
16342
16343 return createInfo.pool->m_BlockVector.Allocate(
16344 m_CurrentFrameIndex.load(),
16345 vkMemReq.size,
16346 alignmentForPool,
16347 createInfoForPool,
16348 suballocType,
16349 allocationCount,
16350 pAllocations);
16351 }
16352 else
16353 {
16354 // Bit mask of memory Vulkan types acceptable for this allocation.
16355 uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
16356 uint32_t memTypeIndex = UINT32_MAX;
16357 VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16358 if (res == VK_SUCCESS)
16359 {
16360 VkDeviceSize alignmentForMemType = VMA_MAX(
16361 vkMemReq.alignment,
16362 GetMemoryTypeMinAlignment(memTypeIndex));
16363
16364 res = AllocateMemoryOfType(
16365 vkMemReq.size,
16366 alignmentForMemType,
16367 requiresDedicatedAllocation || prefersDedicatedAllocation,
16368 dedicatedBuffer,
16369 dedicatedBufferUsage,
16370 dedicatedImage,
16371 createInfo,
16372 memTypeIndex,
16373 suballocType,
16374 allocationCount,
16375 pAllocations);
16376 // Succeeded on first try.
16377 if (res == VK_SUCCESS)
16378 {
16379 return res;
16380 }
16381 // Allocation from this memory type failed. Try other compatible memory types.
16382 else
16383 {
16384 for (;;)
16385 {
16386 // Remove old memTypeIndex from list of possibilities.
16387 memoryTypeBits &= ~(1u << memTypeIndex);
16388 // Find alternative memTypeIndex.
16389 res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16390 if (res == VK_SUCCESS)
16391 {
16392 alignmentForMemType = VMA_MAX(
16393 vkMemReq.alignment,
16394 GetMemoryTypeMinAlignment(memTypeIndex));
16395
16396 res = AllocateMemoryOfType(
16397 vkMemReq.size,
16398 alignmentForMemType,
16399 requiresDedicatedAllocation || prefersDedicatedAllocation,
16400 dedicatedBuffer,
16401 dedicatedBufferUsage,
16402 dedicatedImage,
16403 createInfo,
16404 memTypeIndex,
16405 suballocType,
16406 allocationCount,
16407 pAllocations);
16408 // Allocation from this alternative memory type succeeded.
16409 if (res == VK_SUCCESS)
16410 {
16411 return res;
16412 }
16413 // else: Allocation from this memory type failed. Try next one - next loop iteration.
16414 }
16415 // No other matching memory type index could be found.
16416 else
16417 {
16418 // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
16419 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16420 }
16421 }
16422 }
16423 }
16424 // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
16425 else
16426 return res;
16427 }
16428 }
16429
16430 void VmaAllocator_T::FreeMemory(
16431 size_t allocationCount,
16432 const VmaAllocation* pAllocations)
16433 {
16434 VMA_ASSERT(pAllocations);
16435
16436 for (size_t allocIndex = allocationCount; allocIndex--; )
16437 {
16438 VmaAllocation allocation = pAllocations[allocIndex];
16439
16440 if (allocation != VK_NULL_HANDLE)
16441 {
16442 if (TouchAllocation(allocation))
16443 {
16444 if (VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16445 {
16446 FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
16447 }
16448
16449 switch (allocation->GetType())
16450 {
16451 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16452 {
16453 VmaBlockVector* pBlockVector = VMA_NULL;
16454 VmaPool hPool = allocation->GetBlock()->GetParentPool();
16455 if (hPool != VK_NULL_HANDLE)
16456 {
16457 pBlockVector = &hPool->m_BlockVector;
16458 }
16459 else
16460 {
16461 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16462 pBlockVector = m_pBlockVectors[memTypeIndex];
16463 }
16464 pBlockVector->Free(allocation);
16465 }
16466 break;
16467 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
16468 FreeDedicatedMemory(allocation);
16469 break;
16470 default:
16471 VMA_ASSERT(0);
16472 }
16473 }
16474
16475 // Do this regardless of whether the allocation is lost. Lost allocations still account to Budget.AllocationBytes.
16476 m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
16477 allocation->SetUserData(this, VMA_NULL);
16478 m_AllocationObjectAllocator.Free(allocation);
16479 }
16480 }
16481 }
16482
16483 VkResult VmaAllocator_T::ResizeAllocation(
16484 const VmaAllocation alloc,
16485 VkDeviceSize newSize)
16486 {
16487 // This function is deprecated and so it does nothing. It's left for backward compatibility.
16488 if (newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST)
16489 {
16490 return VK_ERROR_VALIDATION_FAILED_EXT;
16491 }
16492 if (newSize == alloc->GetSize())
16493 {
16494 return VK_SUCCESS;
16495 }
16496 return VK_ERROR_OUT_OF_POOL_MEMORY;
16497 }
16498
16499 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
16500 {
16501 // Initialize.
16502 InitStatInfo(pStats->total);
16503 for (size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
16504 InitStatInfo(pStats->memoryType[i]);
16505 for (size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
16506 InitStatInfo(pStats->memoryHeap[i]);
16507
16508 // Process default pools.
16509 for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16510 {
16511 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
16512 VMA_ASSERT(pBlockVector);
16513 pBlockVector->AddStats(pStats);
16514 }
16515
16516 // Process custom pools.
16517 {
16518 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
16519 for (size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
16520 {
16521 m_Pools[poolIndex]->m_BlockVector.AddStats(pStats);
16522 }
16523 }
16524
16525 // Process dedicated allocations.
16526 for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16527 {
16528 const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16529 VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16530 AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
16531 VMA_ASSERT(pDedicatedAllocVector);
16532 for (size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
16533 {
16534 VmaStatInfo allocationStatInfo;
16535 (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo);
16536 VmaAddStatInfo(pStats->total, allocationStatInfo);
16537 VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
16538 VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
16539 }
16540 }
16541
16542 // Postprocess.
16543 VmaPostprocessCalcStatInfo(pStats->total);
16544 for (size_t i = 0; i < GetMemoryTypeCount(); ++i)
16545 VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
16546 for (size_t i = 0; i < GetMemoryHeapCount(); ++i)
16547 VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
16548 }
16549
16550 void VmaAllocator_T::GetBudget(VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount)
16551 {
16552 #if VMA_MEMORY_BUDGET
16553 if (m_UseExtMemoryBudget)
16554 {
16555 if (m_Budget.m_OperationsSinceBudgetFetch < 30)
16556 {
16557 VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex);
16558 for (uint32_t i = 0; i < heapCount; ++i, ++outBudget)
16559 {
16560 const uint32_t heapIndex = firstHeap + i;
16561
16562 outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
16563 outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
16564
16565 if (m_Budget.m_VulkanUsage[heapIndex] + outBudget->blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex])
16566 {
16567 outBudget->usage = m_Budget.m_VulkanUsage[heapIndex] +
16568 outBudget->blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
16569 }
16570 else
16571 {
16572 outBudget->usage = 0;
16573 }
16574
16575 // Have to take MIN with heap size because explicit HeapSizeLimit is included in it.
16576 outBudget->budget = VMA_MIN(
16577 m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size);
16578 }
16579 }
16580 else
16581 {
16582 UpdateVulkanBudget(); // Outside of mutex lock
16583 GetBudget(outBudget, firstHeap, heapCount); // Recursion
16584 }
16585 }
16586 else
16587 #endif
16588 {
16589 for (uint32_t i = 0; i < heapCount; ++i, ++outBudget)
16590 {
16591 const uint32_t heapIndex = firstHeap + i;
16592
16593 outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
16594 outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
16595
16596 outBudget->usage = outBudget->blockBytes;
16597 outBudget->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
16598 }
16599 }
16600 }
16601
16602 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
16603
16604 VkResult VmaAllocator_T::DefragmentationBegin(
16605 const VmaDefragmentationInfo2& info,
16606 VmaDefragmentationStats* pStats,
16607 VmaDefragmentationContext* pContext)
16608 {
16609 if (info.pAllocationsChanged != VMA_NULL)
16610 {
16611 memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
16612 }
16613
16614 *pContext = vma_new(this, VmaDefragmentationContext_T)(
16615 this, m_CurrentFrameIndex.load(), info.flags, pStats);
16616
16617 (*pContext)->AddPools(info.poolCount, info.pPools);
16618 (*pContext)->AddAllocations(
16619 info.allocationCount, info.pAllocations, info.pAllocationsChanged);
16620
16621 VkResult res = (*pContext)->Defragment(
16622 info.maxCpuBytesToMove, info.maxCpuAllocationsToMove,
16623 info.maxGpuBytesToMove, info.maxGpuAllocationsToMove,
16624 info.commandBuffer, pStats, info.flags);
16625
16626 if (res != VK_NOT_READY)
16627 {
16628 vma_delete(this, *pContext);
16629 *pContext = VMA_NULL;
16630 }
16631
16632 return res;
16633 }
16634
16635 VkResult VmaAllocator_T::DefragmentationEnd(
16636 VmaDefragmentationContext context)
16637 {
16638 vma_delete(this, context);
16639 return VK_SUCCESS;
16640 }
16641
16642 VkResult VmaAllocator_T::DefragmentationPassBegin(
16643 VmaDefragmentationPassInfo* pInfo,
16644 VmaDefragmentationContext context)
16645 {
16646 return context->DefragmentPassBegin(pInfo);
16647 }
16648 VkResult VmaAllocator_T::DefragmentationPassEnd(
16649 VmaDefragmentationContext context)
16650 {
16651 return context->DefragmentPassEnd();
16652
16653 }
16654
16655 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
16656 {
16657 if (hAllocation->CanBecomeLost())
16658 {
16659 /*
16660 Warning: This is a carefully designed algorithm.
16661 Do not modify unless you really know what you're doing :)
16662 */
16663 const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
16664 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
16665 for (;;)
16666 {
16667 if (localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
16668 {
16669 pAllocationInfo->memoryType = UINT32_MAX;
16670 pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
16671 pAllocationInfo->offset = 0;
16672 pAllocationInfo->size = hAllocation->GetSize();
16673 pAllocationInfo->pMappedData = VMA_NULL;
16674 pAllocationInfo->pUserData = hAllocation->GetUserData();
16675 return;
16676 }
16677 else if (localLastUseFrameIndex == localCurrFrameIndex)
16678 {
16679 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
16680 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
16681 pAllocationInfo->offset = hAllocation->GetOffset();
16682 pAllocationInfo->size = hAllocation->GetSize();
16683 pAllocationInfo->pMappedData = VMA_NULL;
16684 pAllocationInfo->pUserData = hAllocation->GetUserData();
16685 return;
16686 }
16687 else // Last use time earlier than current time.
16688 {
16689 if (hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
16690 {
16691 localLastUseFrameIndex = localCurrFrameIndex;
16692 }
16693 }
16694 }
16695 }
16696 else
16697 {
16698 #if VMA_STATS_STRING_ENABLED
16699 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
16700 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
16701 for (;;)
16702 {
16703 VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
16704 if (localLastUseFrameIndex == localCurrFrameIndex)
16705 {
16706 break;
16707 }
16708 else // Last use time earlier than current time.
16709 {
16710 if (hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
16711 {
16712 localLastUseFrameIndex = localCurrFrameIndex;
16713 }
16714 }
16715 }
16716 #endif
16717
16718 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
16719 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
16720 pAllocationInfo->offset = hAllocation->GetOffset();
16721 pAllocationInfo->size = hAllocation->GetSize();
16722 pAllocationInfo->pMappedData = hAllocation->GetMappedData();
16723 pAllocationInfo->pUserData = hAllocation->GetUserData();
16724 }
16725 }
16726
16727 bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
16728 {
16729 // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
16730 if (hAllocation->CanBecomeLost())
16731 {
16732 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
16733 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
16734 for (;;)
16735 {
16736 if (localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
16737 {
16738 return false;
16739 }
16740 else if (localLastUseFrameIndex == localCurrFrameIndex)
16741 {
16742 return true;
16743 }
16744 else // Last use time earlier than current time.
16745 {
16746 if (hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
16747 {
16748 localLastUseFrameIndex = localCurrFrameIndex;
16749 }
16750 }
16751 }
16752 }
16753 else
16754 {
16755 #if VMA_STATS_STRING_ENABLED
16756 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
16757 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
16758 for (;;)
16759 {
16760 VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
16761 if (localLastUseFrameIndex == localCurrFrameIndex)
16762 {
16763 break;
16764 }
16765 else // Last use time earlier than current time.
16766 {
16767 if (hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
16768 {
16769 localLastUseFrameIndex = localCurrFrameIndex;
16770 }
16771 }
16772 }
16773 #endif
16774
16775 return true;
16776 }
16777 }
16778
16779 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
16780 {
16781 VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
16782
16783 VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
16784
16785 if (newCreateInfo.maxBlockCount == 0)
16786 {
16787 newCreateInfo.maxBlockCount = SIZE_MAX;
16788 }
16789 if (newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
16790 {
16791 return VK_ERROR_INITIALIZATION_FAILED;
16792 }
16793 // Memory type index out of range or forbidden.
16794 if (pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() ||
16795 ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0)
16796 {
16797 return VK_ERROR_FEATURE_NOT_PRESENT;
16798 }
16799
16800 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
16801
16802 *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
16803
16804 VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
16805 if (res != VK_SUCCESS)
16806 {
16807 vma_delete(this, *pPool);
16808 *pPool = VMA_NULL;
16809 return res;
16810 }
16811
16812 // Add to m_Pools.
16813 {
16814 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
16815 (*pPool)->SetId(m_NextPoolId++);
16816 VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool);
16817 }
16818
16819 return VK_SUCCESS;
16820 }
16821
16822 void VmaAllocator_T::DestroyPool(VmaPool pool)
16823 {
16824 // Remove from m_Pools.
16825 {
16826 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
16827 bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
16828 VMA_ASSERT(success && "Pool not found in Allocator.");
16829 }
16830
16831 vma_delete(this, pool);
16832 }
16833
16834 void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
16835 {
16836 pool->m_BlockVector.GetPoolStats(pPoolStats);
16837 }
16838
16839 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
16840 {
16841 m_CurrentFrameIndex.store(frameIndex);
16842
16843 #if VMA_MEMORY_BUDGET
16844 if (m_UseExtMemoryBudget)
16845 {
16846 UpdateVulkanBudget();
16847 }
16848 #endif // #if VMA_MEMORY_BUDGET
16849 }
16850
16851 void VmaAllocator_T::MakePoolAllocationsLost(
16852 VmaPool hPool,
16853 size_t* pLostAllocationCount)
16854 {
16855 hPool->m_BlockVector.MakePoolAllocationsLost(
16856 m_CurrentFrameIndex.load(),
16857 pLostAllocationCount);
16858 }
16859
16860 VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
16861 {
16862 return hPool->m_BlockVector.CheckCorruption();
16863 }
16864
16865 VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
16866 {
16867 VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
16868
16869 // Process default pools.
16870 for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16871 {
16872 if (((1u << memTypeIndex) & memoryTypeBits) != 0)
16873 {
16874 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
16875 VMA_ASSERT(pBlockVector);
16876 VkResult localRes = pBlockVector->CheckCorruption();
16877 switch (localRes)
16878 {
16879 case VK_ERROR_FEATURE_NOT_PRESENT:
16880 break;
16881 case VK_SUCCESS:
16882 finalRes = VK_SUCCESS;
16883 break;
16884 default:
16885 return localRes;
16886 }
16887 }
16888 }
16889
16890 // Process custom pools.
16891 {
16892 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
16893 for (size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
16894 {
16895 if (((1u << m_Pools[poolIndex]->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
16896 {
16897 VkResult localRes = m_Pools[poolIndex]->m_BlockVector.CheckCorruption();
16898 switch (localRes)
16899 {
16900 case VK_ERROR_FEATURE_NOT_PRESENT:
16901 break;
16902 case VK_SUCCESS:
16903 finalRes = VK_SUCCESS;
16904 break;
16905 default:
16906 return localRes;
16907 }
16908 }
16909 }
16910 }
16911
16912 return finalRes;
16913 }
16914
16915 void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
16916 {
16917 *pAllocation = m_AllocationObjectAllocator.Allocate(VMA_FRAME_INDEX_LOST, false);
16918 (*pAllocation)->InitLost();
16919 }
16920
16921 VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
16922 {
16923 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
16924
16925 // HeapSizeLimit is in effect for this heap.
16926 if ((m_HeapSizeLimitMask & (1u << heapIndex)) != 0)
16927 {
16928 const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
16929 VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex];
16930 for (;;)
16931 {
16932 const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize;
16933 if (blockBytesAfterAllocation > heapSize)
16934 {
16935 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16936 }
16937 if (m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation))
16938 {
16939 break;
16940 }
16941 }
16942 }
16943 else
16944 {
16945 m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize;
16946 }
16947
16948 // VULKAN CALL vkAllocateMemory.
16949 VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
16950
16951 if (res == VK_SUCCESS)
16952 {
16953 #if VMA_MEMORY_BUDGET
16954 ++m_Budget.m_OperationsSinceBudgetFetch;
16955 #endif
16956
16957 // Informative callback.
16958 if (m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
16959 {
16960 (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData);
16961 }
16962 }
16963 else
16964 {
16965 m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize;
16966 }
16967
16968 return res;
16969 }
16970
16971 void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
16972 {
16973 // Informative callback.
16974 if (m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
16975 {
16976 (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData);
16977 }
16978
16979 // VULKAN CALL vkFreeMemory.
16980 (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
16981
16982 m_Budget.m_BlockBytes[MemoryTypeIndexToHeapIndex(memoryType)] -= size;
16983 }
16984
16985 VkResult VmaAllocator_T::BindVulkanBuffer(
16986 VkDeviceMemory memory,
16987 VkDeviceSize memoryOffset,
16988 VkBuffer buffer,
16989 const void* pNext)
16990 {
16991 if (pNext != VMA_NULL)
16992 {
16993 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
16994 if ((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
16995 m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL)
16996 {
16997 VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR };
16998 bindBufferMemoryInfo.pNext = pNext;
16999 bindBufferMemoryInfo.buffer = buffer;
17000 bindBufferMemoryInfo.memory = memory;
17001 bindBufferMemoryInfo.memoryOffset = memoryOffset;
17002 return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17003 }
17004 else
17005 #endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17006 {
17007 return VK_ERROR_EXTENSION_NOT_PRESENT;
17008 }
17009 }
17010 else
17011 {
17012 return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset);
17013 }
17014 }
17015
17016 VkResult VmaAllocator_T::BindVulkanImage(
17017 VkDeviceMemory memory,
17018 VkDeviceSize memoryOffset,
17019 VkImage image,
17020 const void* pNext)
17021 {
17022 if (pNext != VMA_NULL)
17023 {
17024 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17025 if ((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
17026 m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL)
17027 {
17028 VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR };
17029 bindBufferMemoryInfo.pNext = pNext;
17030 bindBufferMemoryInfo.image = image;
17031 bindBufferMemoryInfo.memory = memory;
17032 bindBufferMemoryInfo.memoryOffset = memoryOffset;
17033 return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17034 }
17035 else
17036 #endif // #if VMA_BIND_MEMORY2
17037 {
17038 return VK_ERROR_EXTENSION_NOT_PRESENT;
17039 }
17040 }
17041 else
17042 {
17043 return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset);
17044 }
17045 }
17046
17047 VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
17048 {
17049 if (hAllocation->CanBecomeLost())
17050 {
17051 return VK_ERROR_MEMORY_MAP_FAILED;
17052 }
17053
17054 switch (hAllocation->GetType())
17055 {
17056 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17057 {
17058 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17059 char* pBytes = VMA_NULL;
17060 VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
17061 if (res == VK_SUCCESS)
17062 {
17063 *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
17064 hAllocation->BlockAllocMap();
17065 }
17066 return res;
17067 }
17068 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17069 return hAllocation->DedicatedAllocMap(this, ppData);
17070 default:
17071 VMA_ASSERT(0);
17072 return VK_ERROR_MEMORY_MAP_FAILED;
17073 }
17074 }
17075
17076 void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
17077 {
17078 switch (hAllocation->GetType())
17079 {
17080 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17081 {
17082 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17083 hAllocation->BlockAllocUnmap();
17084 pBlock->Unmap(this, 1);
17085 }
17086 break;
17087 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17088 hAllocation->DedicatedAllocUnmap(this);
17089 break;
17090 default:
17091 VMA_ASSERT(0);
17092 }
17093 }
17094
17095 VkResult VmaAllocator_T::BindBufferMemory(
17096 VmaAllocation hAllocation,
17097 VkDeviceSize allocationLocalOffset,
17098 VkBuffer hBuffer,
17099 const void* pNext)
17100 {
17101 VkResult res = VK_SUCCESS;
17102 switch (hAllocation->GetType())
17103 {
17104 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17105 res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext);
17106 break;
17107 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17108 {
17109 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17110 VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
17111 res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext);
17112 break;
17113 }
17114 default:
17115 VMA_ASSERT(0);
17116 }
17117 return res;
17118 }
17119
17120 VkResult VmaAllocator_T::BindImageMemory(
17121 VmaAllocation hAllocation,
17122 VkDeviceSize allocationLocalOffset,
17123 VkImage hImage,
17124 const void* pNext)
17125 {
17126 VkResult res = VK_SUCCESS;
17127 switch (hAllocation->GetType())
17128 {
17129 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17130 res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext);
17131 break;
17132 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17133 {
17134 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
17135 VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
17136 res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext);
17137 break;
17138 }
17139 default:
17140 VMA_ASSERT(0);
17141 }
17142 return res;
17143 }
17144
17145 VkResult VmaAllocator_T::FlushOrInvalidateAllocation(
17146 VmaAllocation hAllocation,
17147 VkDeviceSize offset, VkDeviceSize size,
17148 VMA_CACHE_OPERATION op)
17149 {
17150 VkResult res = VK_SUCCESS;
17151
17152 VkMappedMemoryRange memRange = {};
17153 if (GetFlushOrInvalidateRange(hAllocation, offset, size, memRange))
17154 {
17155 switch (op)
17156 {
17157 case VMA_CACHE_FLUSH:
17158 res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
17159 break;
17160 case VMA_CACHE_INVALIDATE:
17161 res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
17162 break;
17163 default:
17164 VMA_ASSERT(0);
17165 }
17166 }
17167 // else: Just ignore this call.
17168 return res;
17169 }
17170
17171 VkResult VmaAllocator_T::FlushOrInvalidateAllocations(
17172 uint32_t allocationCount,
17173 const VmaAllocation* allocations,
17174 const VkDeviceSize* offsets, const VkDeviceSize* sizes,
17175 VMA_CACHE_OPERATION op)
17176 {
17177 typedef VmaStlAllocator<VkMappedMemoryRange> RangeAllocator;
17178 typedef VmaSmallVector<VkMappedMemoryRange, RangeAllocator, 16> RangeVector;
17179 RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks()));
17180
17181 for (uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
17182 {
17183 const VmaAllocation alloc = allocations[allocIndex];
17184 const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0;
17185 const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE;
17186 VkMappedMemoryRange newRange;
17187 if (GetFlushOrInvalidateRange(alloc, offset, size, newRange))
17188 {
17189 ranges.push_back(newRange);
17190 }
17191 }
17192
17193 VkResult res = VK_SUCCESS;
17194 if (!ranges.empty())
17195 {
17196 switch (op)
17197 {
17198 case VMA_CACHE_FLUSH:
17199 res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17200 break;
17201 case VMA_CACHE_INVALIDATE:
17202 res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17203 break;
17204 default:
17205 VMA_ASSERT(0);
17206 }
17207 }
17208 // else: Just ignore this call.
17209 return res;
17210 }
17211
17212 void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
17213 {
17214 VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
17215
17216 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17217 {
17218 VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17219 AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
17220 VMA_ASSERT(pDedicatedAllocations);
17221 bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
17222 VMA_ASSERT(success);
17223 }
17224
17225 VkDeviceMemory hMemory = allocation->GetMemory();
17226
17227 /*
17228 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
17229 before vkFreeMemory.
17230
17231 if(allocation->GetMappedData() != VMA_NULL)
17232 {
17233 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
17234 }
17235 */
17236
17237 FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
17238
17239 VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
17240 }
17241
17242 uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const
17243 {
17244 VkBufferCreateInfo dummyBufCreateInfo;
17245 VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo);
17246
17247 uint32_t memoryTypeBits = 0;
17248
17249 // Create buffer.
17250 VkBuffer buf = VK_NULL_HANDLE;
17251 VkResult res = (*GetVulkanFunctions().vkCreateBuffer)(
17252 m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf);
17253 if (res == VK_SUCCESS)
17254 {
17255 // Query for supported memory types.
17256 VkMemoryRequirements memReq;
17257 (*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq);
17258 memoryTypeBits = memReq.memoryTypeBits;
17259
17260 // Destroy buffer.
17261 (*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks());
17262 }
17263
17264 return memoryTypeBits;
17265 }
17266
17267 uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const
17268 {
17269 // Make sure memory information is already fetched.
17270 VMA_ASSERT(GetMemoryTypeCount() > 0);
17271
17272 uint32_t memoryTypeBits = UINT32_MAX;
17273
17274 if (!m_UseAmdDeviceCoherentMemory)
17275 {
17276 // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD.
17277 for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17278 {
17279 if ((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
17280 {
17281 memoryTypeBits &= ~(1u << memTypeIndex);
17282 }
17283 }
17284 }
17285
17286 return memoryTypeBits;
17287 }
17288
17289 bool VmaAllocator_T::GetFlushOrInvalidateRange(
17290 VmaAllocation allocation,
17291 VkDeviceSize offset, VkDeviceSize size,
17292 VkMappedMemoryRange& outRange) const
17293 {
17294 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17295 if (size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
17296 {
17297 const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
17298 const VkDeviceSize allocationSize = allocation->GetSize();
17299 VMA_ASSERT(offset <= allocationSize);
17300
17301 outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
17302 outRange.pNext = VMA_NULL;
17303 outRange.memory = allocation->GetMemory();
17304
17305 switch (allocation->GetType())
17306 {
17307 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17308 outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17309 if (size == VK_WHOLE_SIZE)
17310 {
17311 outRange.size = allocationSize - outRange.offset;
17312 }
17313 else
17314 {
17315 VMA_ASSERT(offset + size <= allocationSize);
17316 outRange.size = VMA_MIN(
17317 VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize),
17318 allocationSize - outRange.offset);
17319 }
17320 break;
17321 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17322 {
17323 // 1. Still within this allocation.
17324 outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17325 if (size == VK_WHOLE_SIZE)
17326 {
17327 size = allocationSize - offset;
17328 }
17329 else
17330 {
17331 VMA_ASSERT(offset + size <= allocationSize);
17332 }
17333 outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize);
17334
17335 // 2. Adjust to whole block.
17336 const VkDeviceSize allocationOffset = allocation->GetOffset();
17337 VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
17338 const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize();
17339 outRange.offset += allocationOffset;
17340 outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset);
17341
17342 break;
17343 }
17344 default:
17345 VMA_ASSERT(0);
17346 }
17347 return true;
17348 }
17349 return false;
17350 }
17351
17352 #if VMA_MEMORY_BUDGET
17353
17354 void VmaAllocator_T::UpdateVulkanBudget()
17355 {
17356 VMA_ASSERT(m_UseExtMemoryBudget);
17357
17358 VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR };
17359
17360 VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };
17361 VmaPnextChainPushFront(&memProps, &budgetProps);
17362
17363 GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps);
17364
17365 {
17366 VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex);
17367
17368 for (uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
17369 {
17370 m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex];
17371 m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex];
17372 m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load();
17373
17374 // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size.
17375 if (m_Budget.m_VulkanBudget[heapIndex] == 0)
17376 {
17377 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
17378 }
17379 else if (m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size)
17380 {
17381 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size;
17382 }
17383 if (m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0)
17384 {
17385 m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
17386 }
17387 }
17388 m_Budget.m_OperationsSinceBudgetFetch = 0;
17389 }
17390 }
17391
17392 #endif // #if VMA_MEMORY_BUDGET
17393
17394 void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
17395 {
17396 if (VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
17397 !hAllocation->CanBecomeLost() &&
17398 (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
17399 {
17400 void* pData = VMA_NULL;
17401 VkResult res = Map(hAllocation, &pData);
17402 if (res == VK_SUCCESS)
17403 {
17404 memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
17405 FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
17406 Unmap(hAllocation);
17407 }
17408 else
17409 {
17410 VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
17411 }
17412 }
17413 }
17414
17415 uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits()
17416 {
17417 uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load();
17418 if (memoryTypeBits == UINT32_MAX)
17419 {
17420 memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits();
17421 m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits);
17422 }
17423 return memoryTypeBits;
17424 }
17425
17426 #if VMA_STATS_STRING_ENABLED
17427
17428 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
17429 {
17430 bool dedicatedAllocationsStarted = false;
17431 for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17432 {
17433 VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17434 AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
17435 VMA_ASSERT(pDedicatedAllocVector);
17436 if (pDedicatedAllocVector->empty() == false)
17437 {
17438 if (dedicatedAllocationsStarted == false)
17439 {
17440 dedicatedAllocationsStarted = true;
17441 json.WriteString("DedicatedAllocations");
17442 json.BeginObject();
17443 }
17444
17445 json.BeginString("Type ");
17446 json.ContinueString(memTypeIndex);
17447 json.EndString();
17448
17449 json.BeginArray();
17450
17451 for (size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
17452 {
17453 json.BeginObject(true);
17454 const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
17455 hAlloc->PrintParameters(json);
17456 json.EndObject();
17457 }
17458
17459 json.EndArray();
17460 }
17461 }
17462 if (dedicatedAllocationsStarted)
17463 {
17464 json.EndObject();
17465 }
17466
17467 {
17468 bool allocationsStarted = false;
17469 for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17470 {
17471 if (m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
17472 {
17473 if (allocationsStarted == false)
17474 {
17475 allocationsStarted = true;
17476 json.WriteString("DefaultPools");
17477 json.BeginObject();
17478 }
17479
17480 json.BeginString("Type ");
17481 json.ContinueString(memTypeIndex);
17482 json.EndString();
17483
17484 m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
17485 }
17486 }
17487 if (allocationsStarted)
17488 {
17489 json.EndObject();
17490 }
17491 }
17492
17493 // Custom pools
17494 {
17495 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
17496 const size_t poolCount = m_Pools.size();
17497 if (poolCount > 0)
17498 {
17499 json.WriteString("Pools");
17500 json.BeginObject();
17501 for (size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
17502 {
17503 json.BeginString();
17504 json.ContinueString(m_Pools[poolIndex]->GetId());
17505 json.EndString();
17506
17507 m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
17508 }
17509 json.EndObject();
17510 }
17511 }
17512 }
17513
17514 #endif // #if VMA_STATS_STRING_ENABLED
17515
17516 ////////////////////////////////////////////////////////////////////////////////
17517 // Public interface
17518
17519 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
17520 const VmaAllocatorCreateInfo* pCreateInfo,
17521 VmaAllocator* pAllocator)
17522 {
17523 VMA_ASSERT(pCreateInfo && pAllocator);
17524 VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 ||
17525 (VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 2));
17526 VMA_DEBUG_LOG("vmaCreateAllocator");
17527 *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
17528 return (*pAllocator)->Init(pCreateInfo);
17529 }
17530
17531 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
17532 VmaAllocator allocator)
17533 {
17534 if (allocator != VK_NULL_HANDLE)
17535 {
17536 VMA_DEBUG_LOG("vmaDestroyAllocator");
17537 VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
17538 vma_delete(&allocationCallbacks, allocator);
17539 }
17540 }
17541
17542 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo)
17543 {
17544 VMA_ASSERT(allocator && pAllocatorInfo);
17545 pAllocatorInfo->instance = allocator->m_hInstance;
17546 pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice();
17547 pAllocatorInfo->device = allocator->m_hDevice;
17548 }
17549
17550 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
17551 VmaAllocator allocator,
17552 const VkPhysicalDeviceProperties** ppPhysicalDeviceProperties)
17553 {
17554 VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
17555 *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
17556 }
17557
17558 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
17559 VmaAllocator allocator,
17560 const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
17561 {
17562 VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
17563 *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
17564 }
17565
17566 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
17567 VmaAllocator allocator,
17568 uint32_t memoryTypeIndex,
17569 VkMemoryPropertyFlags* pFlags)
17570 {
17571 VMA_ASSERT(allocator && pFlags);
17572 VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
17573 *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
17574 }
17575
17576 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
17577 VmaAllocator allocator,
17578 uint32_t frameIndex)
17579 {
17580 VMA_ASSERT(allocator);
17581 VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
17582
17583 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17584
17585 allocator->SetCurrentFrameIndex(frameIndex);
17586 }
17587
17588 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
17589 VmaAllocator allocator,
17590 VmaStats* pStats)
17591 {
17592 VMA_ASSERT(allocator && pStats);
17593 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17594 allocator->CalculateStats(pStats);
17595 }
17596
17597 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
17598 VmaAllocator allocator,
17599 VmaBudget* pBudget)
17600 {
17601 VMA_ASSERT(allocator && pBudget);
17602 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17603 allocator->GetBudget(pBudget, 0, allocator->GetMemoryHeapCount());
17604 }
17605
17606 #if VMA_STATS_STRING_ENABLED
17607
17608 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
17609 VmaAllocator allocator,
17610 char** ppStatsString,
17611 VkBool32 detailedMap)
17612 {
17613 VMA_ASSERT(allocator && ppStatsString);
17614 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17615
17616 VmaStringBuilder sb(allocator);
17617 {
17618 VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
17619 json.BeginObject();
17620
17621 VmaBudget budget[VK_MAX_MEMORY_HEAPS];
17622 allocator->GetBudget(budget, 0, allocator->GetMemoryHeapCount());
17623
17624 VmaStats stats;
17625 allocator->CalculateStats(&stats);
17626
17627 json.WriteString("Total");
17628 VmaPrintStatInfo(json, stats.total);
17629
17630 for (uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
17631 {
17632 json.BeginString("Heap ");
17633 json.ContinueString(heapIndex);
17634 json.EndString();
17635 json.BeginObject();
17636
17637 json.WriteString("Size");
17638 json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
17639
17640 json.WriteString("Flags");
17641 json.BeginArray(true);
17642 if ((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
17643 {
17644 json.WriteString("DEVICE_LOCAL");
17645 }
17646 json.EndArray();
17647
17648 json.WriteString("Budget");
17649 json.BeginObject();
17650 {
17651 json.WriteString("BlockBytes");
17652 json.WriteNumber(budget[heapIndex].blockBytes);
17653 json.WriteString("AllocationBytes");
17654 json.WriteNumber(budget[heapIndex].allocationBytes);
17655 json.WriteString("Usage");
17656 json.WriteNumber(budget[heapIndex].usage);
17657 json.WriteString("Budget");
17658 json.WriteNumber(budget[heapIndex].budget);
17659 }
17660 json.EndObject();
17661
17662 if (stats.memoryHeap[heapIndex].blockCount > 0)
17663 {
17664 json.WriteString("Stats");
17665 VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
17666 }
17667
17668 for (uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
17669 {
17670 if (allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
17671 {
17672 json.BeginString("Type ");
17673 json.ContinueString(typeIndex);
17674 json.EndString();
17675
17676 json.BeginObject();
17677
17678 json.WriteString("Flags");
17679 json.BeginArray(true);
17680 VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
17681 if ((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
17682 {
17683 json.WriteString("DEVICE_LOCAL");
17684 }
17685 if ((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
17686 {
17687 json.WriteString("HOST_VISIBLE");
17688 }
17689 if ((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
17690 {
17691 json.WriteString("HOST_COHERENT");
17692 }
17693 if ((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
17694 {
17695 json.WriteString("HOST_CACHED");
17696 }
17697 if ((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
17698 {
17699 json.WriteString("LAZILY_ALLOCATED");
17700 }
17701 if ((flags & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0)
17702 {
17703 json.WriteString(" PROTECTED");
17704 }
17705 if ((flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
17706 {
17707 json.WriteString(" DEVICE_COHERENT");
17708 }
17709 if ((flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY) != 0)
17710 {
17711 json.WriteString(" DEVICE_UNCACHED");
17712 }
17713 json.EndArray();
17714
17715 if (stats.memoryType[typeIndex].blockCount > 0)
17716 {
17717 json.WriteString("Stats");
17718 VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
17719 }
17720
17721 json.EndObject();
17722 }
17723 }
17724
17725 json.EndObject();
17726 }
17727 if (detailedMap == VK_TRUE)
17728 {
17729 allocator->PrintDetailedMap(json);
17730 }
17731
17732 json.EndObject();
17733 }
17734
17735 const size_t len = sb.GetLength();
17736 char* const pChars = vma_new_array(allocator, char, len + 1);
17737 if (len > 0)
17738 {
17739 memcpy(pChars, sb.GetData(), len);
17740 }
17741 pChars[len] = '\0';
17742 *ppStatsString = pChars;
17743 }
17744
17745 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
17746 VmaAllocator allocator,
17747 char* pStatsString)
17748 {
17749 if (pStatsString != VMA_NULL)
17750 {
17751 VMA_ASSERT(allocator);
17752 size_t len = strlen(pStatsString);
17753 vma_delete_array(allocator, pStatsString, len + 1);
17754 }
17755 }
17756
17757 #endif // #if VMA_STATS_STRING_ENABLED
17758
17759 /*
17760 This function is not protected by any mutex because it just reads immutable data.
17761 */
17762 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
17763 VmaAllocator allocator,
17764 uint32_t memoryTypeBits,
17765 const VmaAllocationCreateInfo* pAllocationCreateInfo,
17766 uint32_t* pMemoryTypeIndex)
17767 {
17768 VMA_ASSERT(allocator != VK_NULL_HANDLE);
17769 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
17770 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
17771
17772 memoryTypeBits &= allocator->GetGlobalMemoryTypeBits();
17773
17774 if (pAllocationCreateInfo->memoryTypeBits != 0)
17775 {
17776 memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
17777 }
17778
17779 uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
17780 uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
17781 uint32_t notPreferredFlags = 0;
17782
17783 // Convert usage to requiredFlags and preferredFlags.
17784 switch (pAllocationCreateInfo->usage)
17785 {
17786 case VMA_MEMORY_USAGE_UNKNOWN:
17787 break;
17788 case VMA_MEMORY_USAGE_GPU_ONLY:
17789 if (!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
17790 {
17791 preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
17792 }
17793 break;
17794 case VMA_MEMORY_USAGE_CPU_ONLY:
17795 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
17796 break;
17797 case VMA_MEMORY_USAGE_CPU_TO_GPU:
17798 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
17799 if (!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
17800 {
17801 preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
17802 }
17803 break;
17804 case VMA_MEMORY_USAGE_GPU_TO_CPU:
17805 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
17806 preferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
17807 break;
17808 case VMA_MEMORY_USAGE_CPU_COPY:
17809 notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
17810 break;
17811 case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED:
17812 requiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
17813 break;
17814 default:
17815 VMA_ASSERT(0);
17816 break;
17817 }
17818
17819 // Avoid DEVICE_COHERENT unless explicitly requested.
17820 if (((pAllocationCreateInfo->requiredFlags | pAllocationCreateInfo->preferredFlags) &
17821 (VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0)
17822 {
17823 notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY;
17824 }
17825
17826 *pMemoryTypeIndex = UINT32_MAX;
17827 uint32_t minCost = UINT32_MAX;
17828 for (uint32_t memTypeIndex = 0, memTypeBit = 1;
17829 memTypeIndex < allocator->GetMemoryTypeCount();
17830 ++memTypeIndex, memTypeBit <<= 1)
17831 {
17832 // This memory type is acceptable according to memoryTypeBits bitmask.
17833 if ((memTypeBit & memoryTypeBits) != 0)
17834 {
17835 const VkMemoryPropertyFlags currFlags =
17836 allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
17837 // This memory type contains requiredFlags.
17838 if ((requiredFlags & ~currFlags) == 0)
17839 {
17840 // Calculate cost as number of bits from preferredFlags not present in this memory type.
17841 uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags) +
17842 VmaCountBitsSet(currFlags & notPreferredFlags);
17843 // Remember memory type with lowest cost.
17844 if (currCost < minCost)
17845 {
17846 *pMemoryTypeIndex = memTypeIndex;
17847 if (currCost == 0)
17848 {
17849 return VK_SUCCESS;
17850 }
17851 minCost = currCost;
17852 }
17853 }
17854 }
17855 }
17856 return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
17857 }
17858
17859 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
17860 VmaAllocator allocator,
17861 const VkBufferCreateInfo* pBufferCreateInfo,
17862 const VmaAllocationCreateInfo* pAllocationCreateInfo,
17863 uint32_t* pMemoryTypeIndex)
17864 {
17865 VMA_ASSERT(allocator != VK_NULL_HANDLE);
17866 VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
17867 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
17868 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
17869
17870 const VkDevice hDev = allocator->m_hDevice;
17871 VkBuffer hBuffer = VK_NULL_HANDLE;
17872 VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
17873 hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
17874 if (res == VK_SUCCESS)
17875 {
17876 VkMemoryRequirements memReq = {};
17877 allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
17878 hDev, hBuffer, &memReq);
17879
17880 res = vmaFindMemoryTypeIndex(
17881 allocator,
17882 memReq.memoryTypeBits,
17883 pAllocationCreateInfo,
17884 pMemoryTypeIndex);
17885
17886 allocator->GetVulkanFunctions().vkDestroyBuffer(
17887 hDev, hBuffer, allocator->GetAllocationCallbacks());
17888 }
17889 return res;
17890 }
17891
17892 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
17893 VmaAllocator allocator,
17894 const VkImageCreateInfo* pImageCreateInfo,
17895 const VmaAllocationCreateInfo* pAllocationCreateInfo,
17896 uint32_t* pMemoryTypeIndex)
17897 {
17898 VMA_ASSERT(allocator != VK_NULL_HANDLE);
17899 VMA_ASSERT(pImageCreateInfo != VMA_NULL);
17900 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
17901 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
17902
17903 const VkDevice hDev = allocator->m_hDevice;
17904 VkImage hImage = VK_NULL_HANDLE;
17905 VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
17906 hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
17907 if (res == VK_SUCCESS)
17908 {
17909 VkMemoryRequirements memReq = {};
17910 allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
17911 hDev, hImage, &memReq);
17912
17913 res = vmaFindMemoryTypeIndex(
17914 allocator,
17915 memReq.memoryTypeBits,
17916 pAllocationCreateInfo,
17917 pMemoryTypeIndex);
17918
17919 allocator->GetVulkanFunctions().vkDestroyImage(
17920 hDev, hImage, allocator->GetAllocationCallbacks());
17921 }
17922 return res;
17923 }
17924
17925 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
17926 VmaAllocator allocator,
17927 const VmaPoolCreateInfo* pCreateInfo,
17928 VmaPool* pPool)
17929 {
17930 VMA_ASSERT(allocator && pCreateInfo && pPool);
17931
17932 VMA_DEBUG_LOG("vmaCreatePool");
17933
17934 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17935
17936 VkResult res = allocator->CreatePool(pCreateInfo, pPool);
17937
17938 #if VMA_RECORDING_ENABLED
17939 if (allocator->GetRecorder() != VMA_NULL)
17940 {
17941 allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
17942 }
17943 #endif
17944
17945 return res;
17946 }
17947
17948 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
17949 VmaAllocator allocator,
17950 VmaPool pool)
17951 {
17952 VMA_ASSERT(allocator);
17953
17954 if (pool == VK_NULL_HANDLE)
17955 {
17956 return;
17957 }
17958
17959 VMA_DEBUG_LOG("vmaDestroyPool");
17960
17961 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17962
17963 #if VMA_RECORDING_ENABLED
17964 if (allocator->GetRecorder() != VMA_NULL)
17965 {
17966 allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
17967 }
17968 #endif
17969
17970 allocator->DestroyPool(pool);
17971 }
17972
17973 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
17974 VmaAllocator allocator,
17975 VmaPool pool,
17976 VmaPoolStats* pPoolStats)
17977 {
17978 VMA_ASSERT(allocator && pool && pPoolStats);
17979
17980 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17981
17982 allocator->GetPoolStats(pool, pPoolStats);
17983 }
17984
17985 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
17986 VmaAllocator allocator,
17987 VmaPool pool,
17988 size_t* pLostAllocationCount)
17989 {
17990 VMA_ASSERT(allocator && pool);
17991
17992 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17993
17994 #if VMA_RECORDING_ENABLED
17995 if (allocator->GetRecorder() != VMA_NULL)
17996 {
17997 allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
17998 }
17999 #endif
18000
18001 allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
18002 }
18003
18004 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
18005 {
18006 VMA_ASSERT(allocator && pool);
18007
18008 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18009
18010 VMA_DEBUG_LOG("vmaCheckPoolCorruption");
18011
18012 return allocator->CheckPoolCorruption(pool);
18013 }
18014
18015 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
18016 VmaAllocator allocator,
18017 VmaPool pool,
18018 const char** ppName)
18019 {
18020 VMA_ASSERT(allocator && pool && ppName);
18021
18022 VMA_DEBUG_LOG("vmaGetPoolName");
18023
18024 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18025
18026 * ppName = pool->GetName();
18027 }
18028
18029 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
18030 VmaAllocator allocator,
18031 VmaPool pool,
18032 const char* pName)
18033 {
18034 VMA_ASSERT(allocator && pool);
18035
18036 VMA_DEBUG_LOG("vmaSetPoolName");
18037
18038 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18039
18040 pool->SetName(pName);
18041
18042 #if VMA_RECORDING_ENABLED
18043 if (allocator->GetRecorder() != VMA_NULL)
18044 {
18045 allocator->GetRecorder()->RecordSetPoolName(allocator->GetCurrentFrameIndex(), pool, pName);
18046 }
18047 #endif
18048 }
18049
18050 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
18051 VmaAllocator allocator,
18052 const VkMemoryRequirements* pVkMemoryRequirements,
18053 const VmaAllocationCreateInfo* pCreateInfo,
18054 VmaAllocation* pAllocation,
18055 VmaAllocationInfo* pAllocationInfo)
18056 {
18057 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
18058
18059 VMA_DEBUG_LOG("vmaAllocateMemory");
18060
18061 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18062
18063 VkResult result = allocator->AllocateMemory(
18064 *pVkMemoryRequirements,
18065 false, // requiresDedicatedAllocation
18066 false, // prefersDedicatedAllocation
18067 VK_NULL_HANDLE, // dedicatedBuffer
18068 UINT32_MAX, // dedicatedBufferUsage
18069 VK_NULL_HANDLE, // dedicatedImage
18070 *pCreateInfo,
18071 VMA_SUBALLOCATION_TYPE_UNKNOWN,
18072 1, // allocationCount
18073 pAllocation);
18074
18075 #if VMA_RECORDING_ENABLED
18076 if (allocator->GetRecorder() != VMA_NULL)
18077 {
18078 allocator->GetRecorder()->RecordAllocateMemory(
18079 allocator->GetCurrentFrameIndex(),
18080 *pVkMemoryRequirements,
18081 *pCreateInfo,
18082 *pAllocation);
18083 }
18084 #endif
18085
18086 if (pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18087 {
18088 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18089 }
18090
18091 return result;
18092 }
18093
18094 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
18095 VmaAllocator allocator,
18096 const VkMemoryRequirements* pVkMemoryRequirements,
18097 const VmaAllocationCreateInfo* pCreateInfo,
18098 size_t allocationCount,
18099 VmaAllocation* pAllocations,
18100 VmaAllocationInfo* pAllocationInfo)
18101 {
18102 if (allocationCount == 0)
18103 {
18104 return VK_SUCCESS;
18105 }
18106
18107 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
18108
18109 VMA_DEBUG_LOG("vmaAllocateMemoryPages");
18110
18111 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18112
18113 VkResult result = allocator->AllocateMemory(
18114 *pVkMemoryRequirements,
18115 false, // requiresDedicatedAllocation
18116 false, // prefersDedicatedAllocation
18117 VK_NULL_HANDLE, // dedicatedBuffer
18118 UINT32_MAX, // dedicatedBufferUsage
18119 VK_NULL_HANDLE, // dedicatedImage
18120 *pCreateInfo,
18121 VMA_SUBALLOCATION_TYPE_UNKNOWN,
18122 allocationCount,
18123 pAllocations);
18124
18125 #if VMA_RECORDING_ENABLED
18126 if (allocator->GetRecorder() != VMA_NULL)
18127 {
18128 allocator->GetRecorder()->RecordAllocateMemoryPages(
18129 allocator->GetCurrentFrameIndex(),
18130 *pVkMemoryRequirements,
18131 *pCreateInfo,
18132 (uint64_t)allocationCount,
18133 pAllocations);
18134 }
18135 #endif
18136
18137 if (pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18138 {
18139 for (size_t i = 0; i < allocationCount; ++i)
18140 {
18141 allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
18142 }
18143 }
18144
18145 return result;
18146 }
18147
18148 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
18149 VmaAllocator allocator,
18150 VkBuffer buffer,
18151 const VmaAllocationCreateInfo* pCreateInfo,
18152 VmaAllocation* pAllocation,
18153 VmaAllocationInfo* pAllocationInfo)
18154 {
18155 VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18156
18157 VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
18158
18159 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18160
18161 VkMemoryRequirements vkMemReq = {};
18162 bool requiresDedicatedAllocation = false;
18163 bool prefersDedicatedAllocation = false;
18164 allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
18165 requiresDedicatedAllocation,
18166 prefersDedicatedAllocation);
18167
18168 VkResult result = allocator->AllocateMemory(
18169 vkMemReq,
18170 requiresDedicatedAllocation,
18171 prefersDedicatedAllocation,
18172 buffer, // dedicatedBuffer
18173 UINT32_MAX, // dedicatedBufferUsage
18174 VK_NULL_HANDLE, // dedicatedImage
18175 *pCreateInfo,
18176 VMA_SUBALLOCATION_TYPE_BUFFER,
18177 1, // allocationCount
18178 pAllocation);
18179
18180 #if VMA_RECORDING_ENABLED
18181 if (allocator->GetRecorder() != VMA_NULL)
18182 {
18183 allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
18184 allocator->GetCurrentFrameIndex(),
18185 vkMemReq,
18186 requiresDedicatedAllocation,
18187 prefersDedicatedAllocation,
18188 *pCreateInfo,
18189 *pAllocation);
18190 }
18191 #endif
18192
18193 if (pAllocationInfo && result == VK_SUCCESS)
18194 {
18195 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18196 }
18197
18198 return result;
18199 }
18200
18201 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
18202 VmaAllocator allocator,
18203 VkImage image,
18204 const VmaAllocationCreateInfo* pCreateInfo,
18205 VmaAllocation* pAllocation,
18206 VmaAllocationInfo* pAllocationInfo)
18207 {
18208 VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18209
18210 VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
18211
18212 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18213
18214 VkMemoryRequirements vkMemReq = {};
18215 bool requiresDedicatedAllocation = false;
18216 bool prefersDedicatedAllocation = false;
18217 allocator->GetImageMemoryRequirements(image, vkMemReq,
18218 requiresDedicatedAllocation, prefersDedicatedAllocation);
18219
18220 VkResult result = allocator->AllocateMemory(
18221 vkMemReq,
18222 requiresDedicatedAllocation,
18223 prefersDedicatedAllocation,
18224 VK_NULL_HANDLE, // dedicatedBuffer
18225 UINT32_MAX, // dedicatedBufferUsage
18226 image, // dedicatedImage
18227 *pCreateInfo,
18228 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
18229 1, // allocationCount
18230 pAllocation);
18231
18232 #if VMA_RECORDING_ENABLED
18233 if (allocator->GetRecorder() != VMA_NULL)
18234 {
18235 allocator->GetRecorder()->RecordAllocateMemoryForImage(
18236 allocator->GetCurrentFrameIndex(),
18237 vkMemReq,
18238 requiresDedicatedAllocation,
18239 prefersDedicatedAllocation,
18240 *pCreateInfo,
18241 *pAllocation);
18242 }
18243 #endif
18244
18245 if (pAllocationInfo && result == VK_SUCCESS)
18246 {
18247 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18248 }
18249
18250 return result;
18251 }
18252
18253 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
18254 VmaAllocator allocator,
18255 VmaAllocation allocation)
18256 {
18257 VMA_ASSERT(allocator);
18258
18259 if (allocation == VK_NULL_HANDLE)
18260 {
18261 return;
18262 }
18263
18264 VMA_DEBUG_LOG("vmaFreeMemory");
18265
18266 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18267
18268 #if VMA_RECORDING_ENABLED
18269 if (allocator->GetRecorder() != VMA_NULL)
18270 {
18271 allocator->GetRecorder()->RecordFreeMemory(
18272 allocator->GetCurrentFrameIndex(),
18273 allocation);
18274 }
18275 #endif
18276
18277 allocator->FreeMemory(
18278 1, // allocationCount
18279 &allocation);
18280 }
18281
18282 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
18283 VmaAllocator allocator,
18284 size_t allocationCount,
18285 const VmaAllocation* pAllocations)
18286 {
18287 if (allocationCount == 0)
18288 {
18289 return;
18290 }
18291
18292 VMA_ASSERT(allocator);
18293
18294 VMA_DEBUG_LOG("vmaFreeMemoryPages");
18295
18296 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18297
18298 #if VMA_RECORDING_ENABLED
18299 if (allocator->GetRecorder() != VMA_NULL)
18300 {
18301 allocator->GetRecorder()->RecordFreeMemoryPages(
18302 allocator->GetCurrentFrameIndex(),
18303 (uint64_t)allocationCount,
18304 pAllocations);
18305 }
18306 #endif
18307
18308 allocator->FreeMemory(allocationCount, pAllocations);
18309 }
18310
18311 VMA_CALL_PRE VkResult VMA_CALL_POST vmaResizeAllocation(
18312 VmaAllocator allocator,
18313 VmaAllocation allocation,
18314 VkDeviceSize newSize)
18315 {
18316 VMA_ASSERT(allocator && allocation);
18317
18318 VMA_DEBUG_LOG("vmaResizeAllocation");
18319
18320 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18321
18322 return allocator->ResizeAllocation(allocation, newSize);
18323 }
18324
18325 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
18326 VmaAllocator allocator,
18327 VmaAllocation allocation,
18328 VmaAllocationInfo* pAllocationInfo)
18329 {
18330 VMA_ASSERT(allocator && allocation && pAllocationInfo);
18331
18332 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18333
18334 #if VMA_RECORDING_ENABLED
18335 if (allocator->GetRecorder() != VMA_NULL)
18336 {
18337 allocator->GetRecorder()->RecordGetAllocationInfo(
18338 allocator->GetCurrentFrameIndex(),
18339 allocation);
18340 }
18341 #endif
18342
18343 allocator->GetAllocationInfo(allocation, pAllocationInfo);
18344 }
18345
18346 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
18347 VmaAllocator allocator,
18348 VmaAllocation allocation)
18349 {
18350 VMA_ASSERT(allocator && allocation);
18351
18352 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18353
18354 #if VMA_RECORDING_ENABLED
18355 if (allocator->GetRecorder() != VMA_NULL)
18356 {
18357 allocator->GetRecorder()->RecordTouchAllocation(
18358 allocator->GetCurrentFrameIndex(),
18359 allocation);
18360 }
18361 #endif
18362
18363 return allocator->TouchAllocation(allocation);
18364 }
18365
18366 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
18367 VmaAllocator allocator,
18368 VmaAllocation allocation,
18369 void* pUserData)
18370 {
18371 VMA_ASSERT(allocator && allocation);
18372
18373 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18374
18375 allocation->SetUserData(allocator, pUserData);
18376
18377 #if VMA_RECORDING_ENABLED
18378 if (allocator->GetRecorder() != VMA_NULL)
18379 {
18380 allocator->GetRecorder()->RecordSetAllocationUserData(
18381 allocator->GetCurrentFrameIndex(),
18382 allocation,
18383 pUserData);
18384 }
18385 #endif
18386 }
18387
18388 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
18389 VmaAllocator allocator,
18390 VmaAllocation* pAllocation)
18391 {
18392 VMA_ASSERT(allocator && pAllocation);
18393
18394 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18395
18396 allocator->CreateLostAllocation(pAllocation);
18397
18398 #if VMA_RECORDING_ENABLED
18399 if (allocator->GetRecorder() != VMA_NULL)
18400 {
18401 allocator->GetRecorder()->RecordCreateLostAllocation(
18402 allocator->GetCurrentFrameIndex(),
18403 *pAllocation);
18404 }
18405 #endif
18406 }
18407
18408 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
18409 VmaAllocator allocator,
18410 VmaAllocation allocation,
18411 void** ppData)
18412 {
18413 VMA_ASSERT(allocator && allocation && ppData);
18414
18415 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18416
18417 VkResult res = allocator->Map(allocation, ppData);
18418
18419 #if VMA_RECORDING_ENABLED
18420 if (allocator->GetRecorder() != VMA_NULL)
18421 {
18422 allocator->GetRecorder()->RecordMapMemory(
18423 allocator->GetCurrentFrameIndex(),
18424 allocation);
18425 }
18426 #endif
18427
18428 return res;
18429 }
18430
18431 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
18432 VmaAllocator allocator,
18433 VmaAllocation allocation)
18434 {
18435 VMA_ASSERT(allocator && allocation);
18436
18437 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18438
18439 #if VMA_RECORDING_ENABLED
18440 if (allocator->GetRecorder() != VMA_NULL)
18441 {
18442 allocator->GetRecorder()->RecordUnmapMemory(
18443 allocator->GetCurrentFrameIndex(),
18444 allocation);
18445 }
18446 #endif
18447
18448 allocator->Unmap(allocation);
18449 }
18450
18451 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18452 {
18453 VMA_ASSERT(allocator && allocation);
18454
18455 VMA_DEBUG_LOG("vmaFlushAllocation");
18456
18457 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18458
18459 const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
18460
18461 #if VMA_RECORDING_ENABLED
18462 if (allocator->GetRecorder() != VMA_NULL)
18463 {
18464 allocator->GetRecorder()->RecordFlushAllocation(
18465 allocator->GetCurrentFrameIndex(),
18466 allocation, offset, size);
18467 }
18468 #endif
18469
18470 return res;
18471 }
18472
18473 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18474 {
18475 VMA_ASSERT(allocator && allocation);
18476
18477 VMA_DEBUG_LOG("vmaInvalidateAllocation");
18478
18479 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18480
18481 const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
18482
18483 #if VMA_RECORDING_ENABLED
18484 if (allocator->GetRecorder() != VMA_NULL)
18485 {
18486 allocator->GetRecorder()->RecordInvalidateAllocation(
18487 allocator->GetCurrentFrameIndex(),
18488 allocation, offset, size);
18489 }
18490 #endif
18491
18492 return res;
18493 }
18494
18495 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
18496 VmaAllocator allocator,
18497 uint32_t allocationCount,
18498 const VmaAllocation* allocations,
18499 const VkDeviceSize* offsets,
18500 const VkDeviceSize* sizes)
18501 {
18502 VMA_ASSERT(allocator);
18503
18504 if (allocationCount == 0)
18505 {
18506 return VK_SUCCESS;
18507 }
18508
18509 VMA_ASSERT(allocations);
18510
18511 VMA_DEBUG_LOG("vmaFlushAllocations");
18512
18513 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18514
18515 const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH);
18516
18517 #if VMA_RECORDING_ENABLED
18518 if (allocator->GetRecorder() != VMA_NULL)
18519 {
18520 //TODO
18521 }
18522 #endif
18523
18524 return res;
18525 }
18526
18527 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
18528 VmaAllocator allocator,
18529 uint32_t allocationCount,
18530 const VmaAllocation* allocations,
18531 const VkDeviceSize* offsets,
18532 const VkDeviceSize* sizes)
18533 {
18534 VMA_ASSERT(allocator);
18535
18536 if (allocationCount == 0)
18537 {
18538 return VK_SUCCESS;
18539 }
18540
18541 VMA_ASSERT(allocations);
18542
18543 VMA_DEBUG_LOG("vmaInvalidateAllocations");
18544
18545 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18546
18547 const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE);
18548
18549 #if VMA_RECORDING_ENABLED
18550 if (allocator->GetRecorder() != VMA_NULL)
18551 {
18552 //TODO
18553 }
18554 #endif
18555
18556 return res;
18557 }
18558
18559 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
18560 {
18561 VMA_ASSERT(allocator);
18562
18563 VMA_DEBUG_LOG("vmaCheckCorruption");
18564
18565 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18566
18567 return allocator->CheckCorruption(memoryTypeBits);
18568 }
18569
18570 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
18571 VmaAllocator allocator,
18572 const VmaAllocation* pAllocations,
18573 size_t allocationCount,
18574 VkBool32* pAllocationsChanged,
18575 const VmaDefragmentationInfo* pDefragmentationInfo,
18576 VmaDefragmentationStats* pDefragmentationStats)
18577 {
18578 // Deprecated interface, reimplemented using new one.
18579
18580 VmaDefragmentationInfo2 info2 = {};
18581 info2.allocationCount = (uint32_t)allocationCount;
18582 info2.pAllocations = pAllocations;
18583 info2.pAllocationsChanged = pAllocationsChanged;
18584 if (pDefragmentationInfo != VMA_NULL)
18585 {
18586 info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
18587 info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
18588 }
18589 else
18590 {
18591 info2.maxCpuAllocationsToMove = UINT32_MAX;
18592 info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
18593 }
18594 // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
18595
18596 VmaDefragmentationContext ctx;
18597 VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
18598 if (res == VK_NOT_READY)
18599 {
18600 res = vmaDefragmentationEnd(allocator, ctx);
18601 }
18602 return res;
18603 }
18604
18605 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
18606 VmaAllocator allocator,
18607 const VmaDefragmentationInfo2* pInfo,
18608 VmaDefragmentationStats* pStats,
18609 VmaDefragmentationContext* pContext)
18610 {
18611 VMA_ASSERT(allocator && pInfo && pContext);
18612
18613 // Degenerate case: Nothing to defragment.
18614 if (pInfo->allocationCount == 0 && pInfo->poolCount == 0)
18615 {
18616 return VK_SUCCESS;
18617 }
18618
18619 VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
18620 VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
18621 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
18622 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
18623
18624 VMA_DEBUG_LOG("vmaDefragmentationBegin");
18625
18626 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18627
18628 VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
18629
18630 #if VMA_RECORDING_ENABLED
18631 if (allocator->GetRecorder() != VMA_NULL)
18632 {
18633 allocator->GetRecorder()->RecordDefragmentationBegin(
18634 allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
18635 }
18636 #endif
18637
18638 return res;
18639 }
18640
18641 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
18642 VmaAllocator allocator,
18643 VmaDefragmentationContext context)
18644 {
18645 VMA_ASSERT(allocator);
18646
18647 VMA_DEBUG_LOG("vmaDefragmentationEnd");
18648
18649 if (context != VK_NULL_HANDLE)
18650 {
18651 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18652
18653 #if VMA_RECORDING_ENABLED
18654 if (allocator->GetRecorder() != VMA_NULL)
18655 {
18656 allocator->GetRecorder()->RecordDefragmentationEnd(
18657 allocator->GetCurrentFrameIndex(), context);
18658 }
18659 #endif
18660
18661 return allocator->DefragmentationEnd(context);
18662 }
18663 else
18664 {
18665 return VK_SUCCESS;
18666 }
18667 }
18668
18669 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
18670 VmaAllocator allocator,
18671 VmaDefragmentationContext context,
18672 VmaDefragmentationPassInfo* pInfo
18673 )
18674 {
18675 VMA_ASSERT(allocator);
18676 VMA_ASSERT(pInfo);
18677
18678 VMA_DEBUG_LOG("vmaBeginDefragmentationPass");
18679
18680 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18681
18682 if (context == VK_NULL_HANDLE)
18683 {
18684 pInfo->moveCount = 0;
18685 return VK_SUCCESS;
18686 }
18687
18688 return allocator->DefragmentationPassBegin(pInfo, context);
18689 }
18690 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
18691 VmaAllocator allocator,
18692 VmaDefragmentationContext context)
18693 {
18694 VMA_ASSERT(allocator);
18695
18696 VMA_DEBUG_LOG("vmaEndDefragmentationPass");
18697 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18698
18699 if (context == VK_NULL_HANDLE)
18700 return VK_SUCCESS;
18701
18702 return allocator->DefragmentationPassEnd(context);
18703 }
18704
18705 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
18706 VmaAllocator allocator,
18707 VmaAllocation allocation,
18708 VkBuffer buffer)
18709 {
18710 VMA_ASSERT(allocator && allocation && buffer);
18711
18712 VMA_DEBUG_LOG("vmaBindBufferMemory");
18713
18714 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18715
18716 return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL);
18717 }
18718
18719 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
18720 VmaAllocator allocator,
18721 VmaAllocation allocation,
18722 VkDeviceSize allocationLocalOffset,
18723 VkBuffer buffer,
18724 const void* pNext)
18725 {
18726 VMA_ASSERT(allocator && allocation && buffer);
18727
18728 VMA_DEBUG_LOG("vmaBindBufferMemory2");
18729
18730 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18731
18732 return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext);
18733 }
18734
18735 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
18736 VmaAllocator allocator,
18737 VmaAllocation allocation,
18738 VkImage image)
18739 {
18740 VMA_ASSERT(allocator && allocation && image);
18741
18742 VMA_DEBUG_LOG("vmaBindImageMemory");
18743
18744 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18745
18746 return allocator->BindImageMemory(allocation, 0, image, VMA_NULL);
18747 }
18748
18749 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
18750 VmaAllocator allocator,
18751 VmaAllocation allocation,
18752 VkDeviceSize allocationLocalOffset,
18753 VkImage image,
18754 const void* pNext)
18755 {
18756 VMA_ASSERT(allocator && allocation && image);
18757
18758 VMA_DEBUG_LOG("vmaBindImageMemory2");
18759
18760 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18761
18762 return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext);
18763 }
18764
18765 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
18766 VmaAllocator allocator,
18767 const VkBufferCreateInfo* pBufferCreateInfo,
18768 const VmaAllocationCreateInfo* pAllocationCreateInfo,
18769 VkBuffer* pBuffer,
18770 VmaAllocation* pAllocation,
18771 VmaAllocationInfo* pAllocationInfo)
18772 {
18773 VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
18774
18775 if (pBufferCreateInfo->size == 0)
18776 {
18777 return VK_ERROR_VALIDATION_FAILED_EXT;
18778 }
18779 if ((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
18780 !allocator->m_UseKhrBufferDeviceAddress)
18781 {
18782 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.");
18783 return VK_ERROR_VALIDATION_FAILED_EXT;
18784 }
18785
18786 VMA_DEBUG_LOG("vmaCreateBuffer");
18787
18788 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18789
18790 * pBuffer = VK_NULL_HANDLE;
18791 *pAllocation = VK_NULL_HANDLE;
18792
18793 // 1. Create VkBuffer.
18794 VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
18795 allocator->m_hDevice,
18796 pBufferCreateInfo,
18797 allocator->GetAllocationCallbacks(),
18798 pBuffer);
18799 if (res >= 0)
18800 {
18801 // 2. vkGetBufferMemoryRequirements.
18802 VkMemoryRequirements vkMemReq = {};
18803 bool requiresDedicatedAllocation = false;
18804 bool prefersDedicatedAllocation = false;
18805 allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
18806 requiresDedicatedAllocation, prefersDedicatedAllocation);
18807
18808 // 3. Allocate memory using allocator.
18809 res = allocator->AllocateMemory(
18810 vkMemReq,
18811 requiresDedicatedAllocation,
18812 prefersDedicatedAllocation,
18813 *pBuffer, // dedicatedBuffer
18814 pBufferCreateInfo->usage, // dedicatedBufferUsage
18815 VK_NULL_HANDLE, // dedicatedImage
18816 *pAllocationCreateInfo,
18817 VMA_SUBALLOCATION_TYPE_BUFFER,
18818 1, // allocationCount
18819 pAllocation);
18820
18821 #if VMA_RECORDING_ENABLED
18822 if (allocator->GetRecorder() != VMA_NULL)
18823 {
18824 allocator->GetRecorder()->RecordCreateBuffer(
18825 allocator->GetCurrentFrameIndex(),
18826 *pBufferCreateInfo,
18827 *pAllocationCreateInfo,
18828 *pAllocation);
18829 }
18830 #endif
18831
18832 if (res >= 0)
18833 {
18834 // 3. Bind buffer with memory.
18835 if ((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
18836 {
18837 res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
18838 }
18839 if (res >= 0)
18840 {
18841 // All steps succeeded.
18842 #if VMA_STATS_STRING_ENABLED
18843 (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
18844 #endif
18845 if (pAllocationInfo != VMA_NULL)
18846 {
18847 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18848 }
18849
18850 return VK_SUCCESS;
18851 }
18852 allocator->FreeMemory(
18853 1, // allocationCount
18854 pAllocation);
18855 *pAllocation = VK_NULL_HANDLE;
18856 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
18857 *pBuffer = VK_NULL_HANDLE;
18858 return res;
18859 }
18860 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
18861 *pBuffer = VK_NULL_HANDLE;
18862 return res;
18863 }
18864 return res;
18865 }
18866
18867 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
18868 VmaAllocator allocator,
18869 VkBuffer buffer,
18870 VmaAllocation allocation)
18871 {
18872 VMA_ASSERT(allocator);
18873
18874 if (buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
18875 {
18876 return;
18877 }
18878
18879 VMA_DEBUG_LOG("vmaDestroyBuffer");
18880
18881 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18882
18883 #if VMA_RECORDING_ENABLED
18884 if (allocator->GetRecorder() != VMA_NULL)
18885 {
18886 allocator->GetRecorder()->RecordDestroyBuffer(
18887 allocator->GetCurrentFrameIndex(),
18888 allocation);
18889 }
18890 #endif
18891
18892 if (buffer != VK_NULL_HANDLE)
18893 {
18894 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
18895 }
18896
18897 if (allocation != VK_NULL_HANDLE)
18898 {
18899 allocator->FreeMemory(
18900 1, // allocationCount
18901 &allocation);
18902 }
18903 }
18904
18905 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
18906 VmaAllocator allocator,
18907 const VkImageCreateInfo* pImageCreateInfo,
18908 const VmaAllocationCreateInfo* pAllocationCreateInfo,
18909 VkImage* pImage,
18910 VmaAllocation* pAllocation,
18911 VmaAllocationInfo* pAllocationInfo)
18912 {
18913 VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
18914
18915 if (pImageCreateInfo->extent.width == 0 ||
18916 pImageCreateInfo->extent.height == 0 ||
18917 pImageCreateInfo->extent.depth == 0 ||
18918 pImageCreateInfo->mipLevels == 0 ||
18919 pImageCreateInfo->arrayLayers == 0)
18920 {
18921 return VK_ERROR_VALIDATION_FAILED_EXT;
18922 }
18923
18924 VMA_DEBUG_LOG("vmaCreateImage");
18925
18926 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18927
18928 * pImage = VK_NULL_HANDLE;
18929 *pAllocation = VK_NULL_HANDLE;
18930
18931 // 1. Create VkImage.
18932 VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
18933 allocator->m_hDevice,
18934 pImageCreateInfo,
18935 allocator->GetAllocationCallbacks(),
18936 pImage);
18937 if (res >= 0)
18938 {
18939 VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
18940 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
18941 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
18942
18943 // 2. Allocate memory using allocator.
18944 VkMemoryRequirements vkMemReq = {};
18945 bool requiresDedicatedAllocation = false;
18946 bool prefersDedicatedAllocation = false;
18947 allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
18948 requiresDedicatedAllocation, prefersDedicatedAllocation);
18949
18950 res = allocator->AllocateMemory(
18951 vkMemReq,
18952 requiresDedicatedAllocation,
18953 prefersDedicatedAllocation,
18954 VK_NULL_HANDLE, // dedicatedBuffer
18955 UINT32_MAX, // dedicatedBufferUsage
18956 *pImage, // dedicatedImage
18957 *pAllocationCreateInfo,
18958 suballocType,
18959 1, // allocationCount
18960 pAllocation);
18961
18962 #if VMA_RECORDING_ENABLED
18963 if (allocator->GetRecorder() != VMA_NULL)
18964 {
18965 allocator->GetRecorder()->RecordCreateImage(
18966 allocator->GetCurrentFrameIndex(),
18967 *pImageCreateInfo,
18968 *pAllocationCreateInfo,
18969 *pAllocation);
18970 }
18971 #endif
18972
18973 if (res >= 0)
18974 {
18975 // 3. Bind image with memory.
18976 if ((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
18977 {
18978 res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);
18979 }
18980 if (res >= 0)
18981 {
18982 // All steps succeeded.
18983 #if VMA_STATS_STRING_ENABLED
18984 (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
18985 #endif
18986 if (pAllocationInfo != VMA_NULL)
18987 {
18988 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18989 }
18990
18991 return VK_SUCCESS;
18992 }
18993 allocator->FreeMemory(
18994 1, // allocationCount
18995 pAllocation);
18996 *pAllocation = VK_NULL_HANDLE;
18997 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
18998 *pImage = VK_NULL_HANDLE;
18999 return res;
19000 }
19001 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
19002 *pImage = VK_NULL_HANDLE;
19003 return res;
19004 }
19005 return res;
19006 }
19007
19008 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
19009 VmaAllocator allocator,
19010 VkImage image,
19011 VmaAllocation allocation)
19012 {
19013 VMA_ASSERT(allocator);
19014
19015 if (image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
19016 {
19017 return;
19018 }
19019
19020 VMA_DEBUG_LOG("vmaDestroyImage");
19021
19022 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19023
19024 #if VMA_RECORDING_ENABLED
19025 if (allocator->GetRecorder() != VMA_NULL)
19026 {
19027 allocator->GetRecorder()->RecordDestroyImage(
19028 allocator->GetCurrentFrameIndex(),
19029 allocation);
19030 }
19031 #endif
19032
19033 if (image != VK_NULL_HANDLE)
19034 {
19035 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
19036 }
19037 if (allocation != VK_NULL_HANDLE)
19038 {
19039 allocator->FreeMemory(
19040 1, // allocationCount
19041 &allocation);
19042 }
19043 }
19044
19045 #endif // #ifdef VMA_IMPLEMENTATION
19046